Google Sheets: Append then Delete rows based on Tickbox condition - google-apps-script

I'm attempting to create a spreadsheet to organise products ordered at my workplace.
When an order is received a team member would add the details to the sheet; when it is collected they'd fill out date and ID then tick the order complete. See Attached
What I want to happen next is that the row containing the complete details from that order is appended to a second page in the sheet and the original row is deleted.
I can't make sense of how to get this to run automatically when the box is checked; so far I have been compiling a script to run from a button press:
function runFiling() {
function moveRows() {
var ss = SpreadsheetApp.getActive();
var osh = ss.getSheetByName('Current');
var dsh = ss.getSheetByName('Collected');
var srg = osh.getDataRange('H2:H');//You might want to specify a more unique range. This just gets all of the data on the sheet
var svA = srg.getValues();
var d=0;//deleted row counter
for(var i=1;i<svA.length;i++) {
if(svA[i][7] =='TRUE') {
dsh.appendRow(svA[i]);//append entire row to Sheet2
osh.deleteRow(i-d+1);//accounts for the difference between length of array and number of remaining row.
d++;
}
}
}
}
However even this fails to Append or Delete anything although no errors are found/returned.
If anyone can suggest a way to fix the above or, preferably, how to make the script work when the box is ticked your help will be most appreciated.

Try it this way using an onEdit(e) function
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Current' && e.range.columnStart == 7 && e.range.rowStart > 1 && e.value == "TRUE") {
const dsh = ss.getSheetByName('Collected');
const vs = sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).getValues()
dsh.getRange(dsh.getLastRow() + 1, 1, vs.length, vs[0].length).setValues(vs);
sh.deleteRow(e.range.rowStart);
}
}
This will accomplish the task line by line as the check boxes are checked off by the user.

Related

Copying (and deleting) rows in Google Sheets based on dropdown (Data sourced from Google Survey)

I'm a newbie when it comes to any sort of coding, but I'm trying to figure out a way to fix my spreadsheet. I am working on a Google Sheet for a trucking company where data comes in via a Google Form (e.g. date, driver, amount, pickup, destinaiton, etc) and is a linked to a spreadsheet with responses recorded to a sheet called "Loadboard". Responses start on Row 5.
On "Loadboard", I created a dropdown column called "Paid" in Column R (18) as a place to mark when these orders are completed. The idea is that once "Yes" is selected it will be moved from the "Loadboard" sheet to the "Paid" sheet. I also created a "No" option as part of the dropdown in case we need to move from "Paid" back to "Loadboard" in case this dropdown is selected accidentally.
While I have an onedit function that works great, I'm having trouble figuring out how to convert this to work with data submitted via form (in particular how to write the new onformsubmit function and how to setup the form submission trigger)
Here's the function I'm using:
function onEdit(e) {
let r = e.range;
if (r.columnStart != 18 || r.rowStart == 1 || e.value == null) return;
const sh = SpreadsheetApp.getActive();
const valArray = ["No", "Yes"];
const destArray = ["Loadboard", "Paid"];
let dest = sh.getSheetByName(destArray[valArray.indexOf(e.value)]);
let src = sh.getActiveSheet();
if (dest.getName() == src.getName()) return;
src.getRange(r.rowStart, 1, 1, 18).moveTo(dest.getRange(dest.getLastRow() + 1, 1, 1, 18));
src.deleteRow(r.rowStart);
}
After searching for hours on the internet, this was the closest I got -
function formSubmission() {
var s = SpreadsheetApp.getActiveSheet();
var data = range.getValues();
var numCol = range.getLastColumn();
var row = s.getActiveRow;
var targetinfo = s.getRange(row, (18).getValue);
if (targetinfo() == "Yes") {
var targetSheet = ss.getSheetByName("Paid");
var targetrow = targetSheet.getLastrow() + 1);
var Targetcol = numCol();
targetSheet.getRange(targetrow, 1, 1, Targetcol).setValues(data);
}
}
I'm also using the following trigger setup:
Function to Run: OnEdit
Event Source: From Spreadsheet
Event Type: On Change
Unfortunately, neither seem to be working. I also just tried replacing the original onedit with "formsubmission" which did not work.
I would have to do the same thing, I would let a sheet with all the results untouched. I would make a namedRange with it and two sheets Paid and Loadboard with a request like =QUERY(myNamedRange, "select * where R='Yes'") so get a clean sheet with every "Paid" row.
About your code, a hint : I can't find any var row s.getActiveRow function and i it existed it would be s.getActiveRow() (parenthesis).

Check/Uncheck Boxes From a Dynamic Range Based On Condition

I have a to-do list with checkboxes for my employees in a spreadsheet with a dynamic number of rows that cannot exceed row 20. Because starting from row 20 another employee's to-do list is available.
I have a checkbox next to the name of the employee. Once I check/uncheck that checkbox, I want all below checkboxes to check or uncheck no matter how many rows are added or removed as long as it does not pass row number 20.
I have therefore named the range on row 20 so that even if it moves, apps script can locate it through named range. I have removed the named range and called it instead ('A20') in the script below to avoid confusion.
I thought a forloop could work but I must be doing something wrong because it is executing successfully but showing no results.
function Todolist() {
var spreadsheet = SpreadsheetApp.getActive();
var lianrange = spreadsheet.getRange('A20');
for (var i = 1 ; i<lianrange.length;i++){
if (spreadsheet.getCurrentCell().getValue() === 'check'){
spreadsheet.getRange[i].setValue('Y');
}
if (spreadsheet.getCurrentCell().getValue() === 'uncheck'){
spreadsheet.getRange[i].setValue('N');
}
}
};
Your help is much appreciated.
Check or uncheckall
function onEdit(e) {
//e.source.toast('Entry');
const sh = e.range.getSheet();
const rg = sh.getRange('stopRow');//stopRow is name of namedRange
const sr = rg.getRow();
//need to update name of sheet
if (sh.getName() == "Sheet0" && e.range.columnStart == 2 && e.range.rowStart < sr) {
//e.source.toast('Gate1');
const vs = sh.getRange(e.range.rowStart, e.range.columnStart, sr - e.range.rowStart + 1).getValues().flat();
const cv = sh.getRange(e.range.rowStart, e.range.columnStart).getValue();
let o = vs.map((e, i) => [cv])
sh.getRange(e.range.rowStart, e.range.columnStart, o.length).setValues(o);
}
}
demo:

Moving rows between sheets based on cell edit + blank row found by column lookup not full row

I have been working on this for a few days and have turned the internet and these forums upside down, but can't find the answer anywhere. I've learned some scripting along the way, but have determined that what I need to do is way beyond my limits. Any help at all or guidance would be SUPER well received!!
I'm setting up an order sheet system for my and my wife's handmade 'business' (project). Essentially I need to move order 1 (columns a-w) from 'ready to ship" when "done" is entered into column X, onto the next blank row on "sent", discounting the formulas in columns w-z. So when its checking for a blank row, it should only check columns a-w.
This means that once the order info has moved across onto the new sheet, we have extra functionality added in those columns (I hooked it up to send an email apologising, if an order will be late - hope not, but better to be prepared, with our postal service :) )
Sample sheet here.
I originally learned how to write a 'move row if value added in column x - and learned about triggers and how to call the sheets etc.
Then, because column x, y and z have formulas, I either end up deleting them when I move the whole row - not cool. Or I end up (if i move only columns a- w) having the new (moved) row, showing up in row 9 million, because it sees the formulas as not blank rows. Ive tried editing a 'find row based on column' script, that seemed to work okay, but then i tried to put the two together and thats when it blew up.
Can anyone tell me where i'm going wrong here?
function lastRowForColumn(sheet, column){
// Get the last row with data for the whole sheet.
var ss = spreadSheetApp.getActiveSpreadSheet();// this gets you the
active spreadsheet in which you are working
var sheet = ss.getSheetByName('Sent');
var r = event.source.getActiveRange();
var data = sheet.getRange(1, "A", numRows).getValues();
var numRows = sheet.getLastRow();
// Iterate backwards and find first non empty cell
for(var i = data.length - 1 ; i >= 0 ; i--){
if (data[i][0] != null && data[i][0] != ""){
return i + 1;
var ss = spreadSheetApp.getActiveSpreadSheet();// this gets you the
active spreadsheet in which you are working
var sheet = ss.getSheetByName('Sent');
var r = event.source.getActiveRange();
if(s.getName() == "Ready To Send" && r.getColumn() == 25 &&
r.getValue() == "Sent") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Sent");
var lastRow = lastRowForColumn();
var target = targetSheet.getRange(targetSheet, i, 1);
s.getRange(row, 1, 1, 10).moveTo(target);
s.deleteRow(row);
}
}
}
}
Try this:
function onEdit(e){
var rg=e.range;
var sh=rg.getSheet();
var name=sh.getName();
var col=rg.columnStart;
if(name!='Sheet To Send'){return;}
if(col==24 && e.value=='done') {
var ss=e.source;
var sheet=ss.getSheetByName('Sent');
var row=rg.rowStart;
var srg=sh.getRange(row,1,1,23);
var data=srg.getValues();
var drg=sheet.getRange(sheet.getLastRow()+1,1,1,23);
drg.setValues(data);
sh.deleteRow(row);
}
}
I think what you want is to use getDisplayValues. That solves both your (formulas aren't blank problem (make sure your formulas display blank when the row is unused) and also the it isn't moving my numbers problem).
Try:
moveMe = [];
rowArray = s.getRange(row,1,1,10).getDisplayValues();
moveMe.push(rowArray);
targetSheet.appendRow(moveMe);
s.deleteRow(row);
You might have to wrestle the appendRow a bit but it is the cleanest. If it doesn't work as is try skipping the push step and doing
targetSheet.appendRow(rowArray);
BETTER (edit):
You need to change first your data row to:
var data = sheet.getRange(1, "A", numRows).getDisplayValues();
and then later you can simply do:
targetSheet.appendRow(data[i]);
s.deleteRow(row);
This function takes an edit event and checks if the values edited was changed to 'Sent'. It then copies that row into the sent sheet in the same spreadsheet, maintaining the values.
function onEdit(e){
if(e.value != 'Sent') return false; //If not changed to 'Sent' then stop.
var range = e.range; //get the range the edit occured in.
if(range.getColumn() != 25) return false;//see if it was column number 25, ie. Column Y
var sheet = range.getSheet();
if(sheet.getName() != 'Ready To Send') return false;
var editedRow = sheet.getRange(range.getRow(), 1, 1, sheet.getMaxColumns()); //Select the row.
var sent = sheet.getParent().getSheetByName('Sent');
sent.appendRow(editedRow.getValues()[0]); //Copy row to Sent sheet.
sheet.deleteRow(range.getRow()); //Deleted the original row
}

pull data to a specific sheet within a document

I'm a new to Script editor on google spreadsheet and not much of a programmer.
I've been working with google docs for sometime now and it has become an important tool in my daily activity.
What I'm trying to do, is the following:
Seek a whole document (with severall sheet such as "1", "2", "3", and so on, corresponding to the number of days a month can hold) and if the column 7 shows a determined value (in my case, it will be RECEBER), pull all the data in that row and write onto a sheet created for this purpose.
What's happening is that I'm using the the event onEdit to trigger this function. At first sight, it would be ideal, but in my case, I copy a lot of data from other spreadsheets and the paste command does not trigger the onEdit event. Instead, I have to edit the cell manually in order to get that row copied onto the other sheet.
I could run it just once, once tha whole days of the month were filled and there were any changes left to do, but what I really want to do is to make it immediately, as soon as the content is inserted into the spreadsheet.
There's also another problem with my code, it has to be fit and adapted to all the other sheets, as the if clause only performs the full operation if the active sheet equals "1". Anyway, I believe there is a simple solution to this.
Here's the code found on the net that already took me halfway:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "1" && r.getColumn() == 7 && r.getValue() == "RECEBER") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("money");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
I'll appreciate all the help you can give.
Thanks in advance.
Diogo Sousa
-- Updated 12Oct --
I've changed the logics on my code, and as patt0 suggested, I run the script from the menu created. I've tried adapting the code, but I believe there's some section wrong. The script runs, but isn't writing anything at all on my target sheet.
here's the code:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "RECEBER", functionName: "RECEBER"} ];
ss.addMenu("Scripts", menuEntries);
}
function mustBeCopied(sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNumber = parseInt(sheetName);
if (sheetNumber <=31 && sheetNumber >=1)
return true;
return false;
}
function RECEBER() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = ss.getActiveRange();
if(mustBeCopied(s.getName()) && r.getColumn() == 7 && r.getValue() == "RECEBER") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("money");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
the function Mustbecopied, from what I believe, only sets the range of sheets (1 to 31) eligible;
the function RECEBER will determine if the value on the column 7 will satisfy the condition (RECEBER), so it can retrieve all the row information and move it to the target sheet.
Maybe the trouble is the active sheet thing.. can I us eit in my own advantage and apply the script to the selected sheet?
Also, if I could have both option (whether to apply it to the selected sheet or to the whole document), that wouuld be great and simplify my daily work a lot!
Your question is really multiple questions in one, and I will only deal with the second part, the first part has quite a number of dimensions (how to update summary sheets effectively), which we can discuss with another question maybe.
In order to determine if the data in a particular sheet needs to be copied, you could create a simple function that returns a boolean if the sheet satisfies the condition that it is a number between 1 and 31.
function mustBeCopied(sheetName) {
var sheetNumber = parseInt(sheetName);
if (sheetNumber <=31 && sheetNumber >=1)
return true;
return false;
}
Bear in mind that a sheet with the name "28.7" would satisfy the condition.
The first line of your onEdit() script would then look like this
if(mustBeCopied(s.getName()) && r.getColumn() == 7 && r.getValue() == "RECEBER")
Let me know if this works for you.
--- Oct 14 ---
As far as I can see, the issue that remains, is that when your function was running onEdit, the range (active range) was the cell that was just edited. So the range would be only one cell.
In order for the scrip to run, you would need to highlight each cell to be copied before selecting the menu, which would be tedious to say the least
Further if you have a defined range to be copied, which is the same on each page, we can copy that range every time, or iterate through each row in the column to look for "RECEBER" and get those rows copied.
Further We can try to update your function so that iterate through all the sheets is a many similar to this.
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i = 0; i < sheets.length; i::) {
var s = sheets[i];
if (mustBeCopied(s.getName()) {
var range = s.getRange(someRow, 7, someNumOfRows);
for (var j = 1; j <= someNumOfRows; j++) {
if (range.getCell(j,1).getValue() == "RECEBER") {
//YOUR COPY CODE
}
}
}
}
Let me know how this works.

Delete a row in Google Spreadsheets if value of cell in said row is 0 or blank

I'd like to be able to delete an entire row in a Google Spreadsheets if the value entered for say column "C" in that row is 0 or blank. Is there a simple script I could write to accomplish this?
Thanks!
I can suggest a simple solution without using a script !!
Lets say you want to delete rows with empty text in column C.
Sort the data (Data Menu -> Sort sheet by column C, A->Z) in the sheet w.r.t column C, so all your empty text rows will be available together.
Just select those rows all together and right-click -> delete rows.
Then you can re-sort your data according to the column you need.
Done.
function onEdit(e) {
//Logger.log(JSON.stringify(e));
//{"source":{},"range":{"rowStart":1,"rowEnd":1,"columnEnd":1,"columnStart":1},"value":"1","user":{"email":"","nickname":""},"authMode":{}}
try {
var ss = e.source; // Just pull the spreadsheet object from the one already being passed to onEdit
var s = ss.getActiveSheet();
// Conditions are by sheet and a single cell in a certain column
if (s.getName() == 'Sheet1' && // change to your own
e.range.columnStart == 3 && e.range.columnEnd == 3 && // only look at edits happening in col C which is 3
e.range.rowStart == e.range.rowEnd ) { // only look at single row edits which will equal a single cell
checkCellValue(e);
}
} catch (error) { Logger.log(error); }
};
function checkCellValue(e) {
if ( !e.value || e.value == 0) { // Delete if value is zero or empty
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}
This only looks at the value from a single cell edit now and not the values in the whole sheet.
I wrote this script to do the same thing for one of my Google spreadsheets. I wanted to be able to run the script after all the data was in the spreadsheet so I have the script adding a menu option to run the script.
/**
* Deletes rows in the active spreadsheet that contain 0 or
* a blank valuein column "C".
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[2] == 0 || row[2] == '') {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
};
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the readRows() function specified above.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Remove rows where column C is 0 or blank",
functionName : "readRows"
}];
sheet.addMenu("Script Center Menu", entries);
};
Test spreadsheet before:
Running script from menu:
After running script:
I was having a few problems with scripts so my workaround was to use the "Filter" tool.
Select all spreadsheet data
Click filter tool icon (looks like wine glass)
Click the newly available filter icon in the first cell of the column you wish to search.
Select "Filter By Condition" > Set the conditions (I was using "Text Contains" > "word")
This will leave the rows that contain the word your searching for and they can be deleted by bulk selecting them while holding the shift key > right click > delete rows.
This is what I managed to make work. You can see that I looped backwards through the sheet so that as a row was deleted the next row wouldn't be skipped. I hope this helps somebody.
function UpdateLog() {
var returnSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('RetLog');
var rowCount = returnSheet.getLastRow();
for (i = rowCount; i > 0; i--) {
var rrCell = 'G' + i;
var cell = returnSheet.getRange(rrCell).getValue();
if (cell > 0 ){
logSheet.
returnSheet.deleteRow(i);
}
}
}
quite simple request. Try this :
function try_It(){
deleteRow(2); //// choose col = 2 for column C
}
function deleteRow(col){ // col is the index of the column to check for 0 or empty
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n][col]!='' && data[n][col]!=0){ targetData.push(data[n])};
}
Logger.log(targetData);
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
EDIT : re-reading the question I'm not sure if the question is asking for a 'live' on Edit function or a function (like this above) to apply after data has been entered... It's not very clear to me... so feel free to be more accurate if necessary ;)
There is a simpler way:
Use filtering to only show the rows which you want to delete. For example, my column based on which I want to delete rows had categories on them, A, B, C. Through the filtering interface I selected only A and B, which I wanted to delete.
Select all rows and delete them. Doing this, in my example, effectively selected all A and B rows and deleted them; now my spreadsheet does not show any rows.
Turn off the filter. This unhides my C rows. Done!
There is a short way to solve that instead of a script.
Select entire data > Go to menu > click Data tab > select create filter > click on filter next to column header > pop-up will appear then check values you want to delete > click okay and copy the filtered data to a different sheet > FINISH
reading your question carefully, I came up with this solution:
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [{name: "Evaluate Column C", functionName: "deleteRow"}];
// add to menu
ss.addMenu("Check", menu);
}
function deleteRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get active/selected row
var activeRow = ss.getActiveRange().getRowIndex();
// get content column C
var columnC = ss.getRange("C"+activeRow).getValue();
// evaluate whether content is blank or 0 (null)
if (columnC == '' || columnC == 0) {
ss.deleteRow(parseInt(activeRow));
}
}
This script will create a menu upon file load and will enable you to delete a row, based on those criteria set in column C, or not.
This simple code did the job for me!
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // get active spreadsheet
var activeRow = ss.getActiveRange().getRowIndex(); // get active/selected row
var start=1;
var end=650;
var match='';
var match2=0; //Edit this according to your choice.
for (var i = start; i <= end; i++) {
var columnC = ss.getRange("C"+i).getValue();
if (columnC ==match || columnC ==match2){ ss.deleteRow(i); }
}
}
The below code was able to delete rows containing a date more than 50 days before today in a particular column G , move these row values to back up sheet and delete the rows from source sheet.
The code is better as it deletes the rows at one go rather than deleting one by one. Runs much faster.
It does not copy back values like some solutions suggested (by pushing into an array and copying back to sheet). If I follow that logic, I am losing formulas contained in these cells.
I run the function everyday in the night (scheduled) when no one is using the sheet.
function delete_old(){
//delete > 50 day old records and copy to backup
//run daily from owner login
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bill = ss.getSheetByName("Allotted");
var backss = SpreadsheetApp.openById("..."); //backup spreadsheet
var bill2 = backss.getSheetByName("Allotted");
var today=new Date();
//process allotted sheet (bills)
bill.getRange(1, 1, bill.getMaxRows(), bill.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 7, ascending: true});
var data = bill.getDataRange().getValues();
var delData = new Array();
for(n=data.length-1; n>1; n--){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){ //change the condition as per your situation
delData.push(data[n]);
}//if
}//for
//get first and last row no to be deleted
for(n=1;n<data.length; n++){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){
var strow=n+1 ; //first row
break
}//if
}//for
for(n=data.length-1; n>1; n--){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){
var ltrow=n+1 ; //last row
break
}//if
}//for
var bill2lr=bill2.getLastRow();
bill2.getRange((bill2lr+1),1,delData.length,delData[0].length).setValues(delData);
bill.deleteRows(strow, 1+ltrow-strow);
bill.getRange(1, 1, bill.getMaxRows(), bill.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 6, ascending: true}); //get back ordinal sorting order as per column F
}//function