Script copies row data to another sheet, is only partially working - google-apps-script

I have a script to move a row of data in an employee schedule spreadsheet (thanks to help from the Sheets reddit) it is working but has stopped finding one part of the data.
Spreadsheet here: https://docs.google.com/spreadsheets/d/10yLJ_NyFasGhlRt2LsXO2CC804Zf5JLvwUisgytQOhM/edit?usp=sharing
Once the Finished column 63 is set to "YES" it copies the data (only from the required columns), locates the employee's name based on whose week is marked "x" and moves them to the Finished Sheet.
The code used to work fully but now it isn't finding "x" or grabbing the employee's name.
I've had to update the column numbers in the script a few times as we've gotten more employees, it's possible I've broken it doing that.
(Any blank columns do have data in my real spreadsheet I've just left them blank here as it's not relevant to the script)
Would really appreciate any advice! Thank you!
[Full code in spreadsheet]
if (ecol == 63 && ssh.getName() == 'SCHEDULE' && rng.getValue() == 'YES') { //if Schedule!63 is YES
var data = []; //output data
var rowN = rng.getRow();
var rowV = ssh.getRange(rowN,1,1,63).getDisplayValues();
var row = rowV[0]; //get edited row
var colX = row.indexOf('x')+1; //find X
var offset = colX - ((colX-2) % 5 ) // offset to the first column
var emp = ssh.getRange(1,offset).getValue(); //get employee name
var dd = Utilities.formatDate(new Date(), "GMT+10", "dd/MM/yyyy") //generate date finished
data.push(row[1],row[2],row[3],dd,row[5],emp); //get row
dsh.appendRow(data); //move row
ssh.deleteRow(e.range.rowStart); //delete from schedule
}
}

I believe this var offset = colX - ((colX-2) % 5 ) // offset to the first column
should be this var offset = colX - ((colX+2) % 5 ) // offset to the first column
This is what's causing you to not find employees because the employees are in a merge group of cells which always puts it's value in the left most cell for horizontal merging.

Related

append row to a specific column?

function dailyprofit() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Matt");
var profit = SpreadsheetApp.getActiveSheet().getRange('O2').getValue();
sheet.appendRow([,,,profit ]);
}
Hello, i'm attempting to use appendrow to put my variable into the last blank cell of row D, however appendRow does the entre row rather than just column D.
i would like the "profit" to be appended to row 362 rather than row 363 -> see example here
Thanks!
You can use this sample code to get the last row of Column D:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Matt");
var profit = SpreadsheetApp.getActiveSheet().getRange('O2').getValue();
var colValues = sheet.getRange("D1:D").getValues();
var count = colValues.filter(String).length
sheet.getRange(count+1,4).setValue(profit);
What it does?
Get all the column D values using sheet.getRange("D1:D").getValues()
Filter the array with string values and get its length/count. Empty cells will not be counted.
Increment the column D count by 1 and use Sheet.getRange(row,column) to get the cell below the non-empty cell in Column D. Use Range.setValue(value) to set the cell value.
Sample Output:
NOTE:
This will only work assuming you don't have an empty row in Column D.
If you have empty rows in your Column D, you might need to add few more offsets when incrementing the count.
Example:
I have 2 empty header rows (rows 1 and 2). Therefore, the offset of row count should be +3.
Sample Code:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Matt");
var profit = SpreadsheetApp.getActiveSheet().getRange('O2').getValue();
var colValues = sheet.getRange("D1:D").getValues();
var count = colValues.filter(String).length
sheet.getRange(count+3,4).setValue(profit);

Why is GAS inputting a invalid range into a data validation?

function DYNAMIC(){
var s = SpreadsheetApp.getActiveSpreadsheet(), ss = s.getSheetByName("Snippet Gen"),
dd = s.getSheetByName("dv snip"), column = 1, lastc = dd.getLastColumn(),
lastr = dd.getLastRow(), time = ss.getRange(2,1), name = ss.getRange(4,1),
time_v = time.getValue(), tp = dd.getRange(1,1,1,lastc),
nm = dd.getRange(2,column,lastr,1), match = dd.getRange(1,column).getValue();
var rule_tp = SpreadsheetApp.newDataValidation().requireValueInRange(tp,false).build();
time.setDataValidation(rule_tp);
for(; column < lastc; column++){
if(match = time_v){
var rule_nm = SpreadsheetApp.newDataValidation().requireValueInRange(nm,false).build();
name.setDataValidation(rule_nm);
}
}
}
Sheet 1: Snippet Gen
Sheet 2: dv snip
The result is the correct DV in A2 but a continuous Loading… DV in A4. And then when I go into the DV panel, the range is set as 'dv snip'!$A$2:$A$8 which is an invalid range. How do I set the range correctly?
if(match = time_v){ is an assignment not a comparison try this instead if(match == time_v){
You're most probably getting an invalid range because of this method:
nm = dd.getRange(2,column,lastr,1);
Please notice that the method getRange(row, column, numRows, numColumns) takes the number of rows as the third parameter, not the index of the last row in the range.
Because of this, here you are getting a range that:
Starts at row 2.
Its number of rows equals the index of the last row.
With these settings, the last row in this range doesn't exist. Therefore, you get an invalid range.
To fix this, you would have to substract the first row index to the third parameter in the method. One option would define the parameters and the method in this way:
var firstRow = 2;
var column = 1;
var numRows = dd.getLastRow() - firstRow + 1;
var numCols = dd.getLastColumn() - column + 1;
var nm = dd.getRange(firstRow, column, numRows, numCols);
Also, as Cooper said, you should use a comparison operator, not an assignment one in here:
if(match == time_v){
Reference:
getRange(row, column, numRows, numColumns)
Comparison operator
I hope this is of any help.

How to find undefined or empty cells in a row with google script?

so as the title says I would like to find the empty or undefined cells in a row while iterating row until my lastrow. But it says "TypeError: Cannot read property "0" from undefined." Since it cant read undefined values and check it. You can see my code below. Thanks!
for (var row = startRow; row <= endRow; row++) {
var rangeA = "A" + row;
var rangeB = "H" + row;
var range = rangeA + ":" + rangeB;
var values = sheet1.getRange(range).getValues(); // get all data in one call
for ( var ct = 0; ct <= 7; ct++) {
if (values[ct][0]) = "")
runloop = false;
}
Cause:
Arrays are indexed by rows first and then columns. For example, A1:H1 contains 1 row and 8 columns or 1 inner array and 8 elements in that 1 inner array[[A1,B1,C1,D1,E1,F1,G1,H1]]. Therefore, values[1] will be undefined( since there's only 1 inner array whose index is 0). The script is looping through inner array instead of looping through elements of the inner array.
Solution:
Loop through the elements instead
Snippet:
if (values[0][ct] === "")

Prevent MySQL query from timing out in Apps Script

Our client's online shop uses Opencart, for marketing purposes they wanted to sync Opencart orders automatically with Google Sheets. I modified a script in google sheets to extract customer orders from the past year (based on https://www.transpacific-software.com/blog/opencart-to-google-docs-pull-and-sync-data-auto-through-scripting and https://gist.github.com/pradeepbheron/e6129c9fd9bc74e814d0)
The SQL query is:
SELECT order_id, firstname AS first_name, lastname AS last_name,
email, date_added AS order_date, CONCAT("£", FORMAT(total,2))
order_value, payment_address_1 AS billing_address_1,
payment_address_2 AS billing_address_2, payment_city AS
billing_city, payment_postcode AS billing_postcode,
payment_country AS billing_country, items_purchased FROM
(
SELECT order_id, firstname, lastname, email, date_added,
total, payment_address_1, payment_address_2, payment_city,
payment_postcode, payment_country
FROM ocbw_order
GROUP BY order_id
) AS A
JOIN (
SELECT order_id AS product_order_id, GROUP_CONCAT(name
SEPARATOR ", ") AS items_purchased
FROM ocbw_order_product
GROUP BY product_order_id
) AS B
ON A.order_id=B.product_order_id
WHERE date_added >= DATE_SUB(NOW(),INTERVAL 1 YEAR)
AND firstname != ''
It runs fine in phpMyAdmin but Google Script Editor generates an "Exceeded maximum execution time" error.
It looks like there are 7357 rows (exported from myPhpAdmin). Is there a better way to write the query? Also I am trying to rename the column headers but can only two works, i.e:
GROUP_CONCAT(name SEPARATOR ", ") AS items_purchased
and
CONCAT("£", FORMAT(total,2)) order_value
Any thoughts
QUICK UPDATE: Fri Nov 9 11:54:03 2018
1) As request by #ThisGuyHasTwoThumbs, here is a screenshot of the explain table result
2) I looked into the Best Practices doc mentioned by #bcperth. I tried to rewrite the google sheets script but ran into issues.
Here is the amended script.
function p1MySQLFetchData() {
// Change it as per your database credentials
var conn =
Jdbc.getConnection('jdbc:mysql://[dbHostIp]:3306/[dbName]',
'[dbUsername]', '[dbPassword]');
var stmt = conn.createStatement();
var start = new Date(); // Get script starting time
//change table name as per your database structure
var rs = stmt.executeQuery('[sqlQuery]');
// It sets the limit of the
// maximum nuber of rows in a ResultSet object
// Returns the currently active spreadsheet
var doc = SpreadsheetApp.getActiveSpreadsheet();
var cell = doc.getRange('a1');
var row = 0;
// Mysql table column name count.
var getCount = rs.getMetaData().getColumnCount();
// Create array to hold mysql data
var tempArray = [];
// get row and column count for later
var colCount = getCount;
// ATTEMPT TO GET ROW COUNT 1
//rs.last();
//var rowCount = rs.getRow();
//rs.beforeFirst(); // resets rs cursor
//Logger.log(RowCount);
// DOESN'T WORK! result => ReferenceError: "RowCount" is not
// defined. (line 28, file "Code")
// ATTEMPT TO GET ROW COUNT 2
//var rsCount = stmt.executeQuery('SELECT COUNT(*) AS rowcount FROM
//[sqlQuery]);
// It sets the limit of the maximum number of rows in a ResultSet
// object
//rsCount.next();
//var rowCount = rsCount.getString("rowcount");
//Logger.log(rowCount);
// DOESN'T WORK! result => 0
// Build TempArray using MySQL data
for (var i = 0; i < getCount; i++){
tempArray[0][i] = rs.getMetaData().getColumnName(i+1);
// DOESNT WORK => ERROR
// TypeError: Cannot set property "0.0" of undefined to "order_id".
// (line 39, file "Code")
//Logger.log(rs.getMetaData().getColumnName(i+1));
}
var row = 1;
while (rs.next()) {
for (var col = 0; col < rs.getMetaData().getColumnCount();
col++) {
tempArray[row][col] = rs.getString(col + 1);
//Logger.log(rs.getString(col + 1));
}
row++;
}
// BELOW DOESNT AS I CANT GET A ROW COUNT (SEE ABOVE)
// Fill Spreadsheet from tempArray
//for (var row = 0; row < rowCount; row++) {
//for (var col = 0; col < colCount; col++) {
//cell.offset(row, col).setValue(tempArray[row][col + 1]);
//}
// }
rs.close();
stmt.close();
conn.close();
var end = new Date(); // Get script ending time
Logger.log('Time elapsed: ' + (end.getTime() - start.getTime()));
// To generate script log. To view log click on View -> Logs.
}
But as you can see from the comments, I get loads of errors. Am not sure what to do next.
UPDATE Fri Nov 30 15:26:14 2018
In answer to #Tedinoz comment below.
PhpMyAdmin generates 6862 results and the query took 13.3074 seconds.
When I ran the script in Googles Script Editor, it took around 2 minutes 30 to complete and only pulls 6348 records (92.5%). The records stop after 3rd October 2018.
Regarding your suggestions:
1) I tried running a modified script, setting the INTERVAL to:
1 MONTH => I get no results in Google (it should be 529)
2 MONTH => I get 14 results (should be 1029)
3 MONTH => I get 299 results (should be 1669).
They all took about 4-7 second in myPhpAdmin vs 5 - 20 seconds for Google Script Editor
2) Do you mean exporting a csv from phpMyAdmin and importing to Google Sheets? Well I did that and it works fine.
Another thing I have noticed is that the order_id's in Google Sheets don't match that from phpMyAdmin. Weird.

How to remove duplicate rows while keeping only first row based on duplicate data in B & C columns. Rows to be checked sequentially

I have a google spreadsheet that is populated with end of day portfolio data in my 'portfolio history' sheet, so I require to delete rows where there is duplicate data in column B & C while keeping the first row with the oldest data.(eg duplicate data '123' in column B row 19,20,21 & '4567' column C row 19,20,21. I only require first entry in this case for row 19,rows 20, 21 to be deleted).The rows need to be checked sequentially for duplicates in column B & C. How would I do this? Thanks!!!
Image https://photos.app.goo.gl/7Ufowk4r7K9Gdpsw1
Try this:
function removeDupesInBAndC(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('portfolio history');
var rg=sh.getDataRange();
var vA=rg.getValues();
var dupesA=[];
var delA=[];
for(var i=0;i<vA.length;i++){
var s=vA[i][1].toString() + vA[i][2].toString();
var idx=dupesA.indexOf(s);
if(idx>-1){
delA.push(idx);
}else{
dupesA.push(s);
}
}
for(var i=delA.length-1;i>-1;i--){
sh.deleteRow(i+1);
}
}