OnEdit trigger fired simultaneously causing wrong calculation of last row - google-apps-script

I'm new to Google App Script and trying to create a queue-like spreadsheet.
The current design is that: in sheet A, there's a column of checkboxs, and if any one of them is checked, the entire row would be moved to the end of another sheet, say sheet B.
The way I calculate the end of the sheet B is as followed:
(I read from a different stackoverflow post)
var Avals = dest.getRange("B:B").getValues();
var Alast = Avals.filter(String).length;
var lastRow = String(Alast + 1);
The issue I'm having here is that when I check two checkboxes quickly one by one, they are mapped to the same lastRow in sheet B, because the second checkbox triggers the onEdit trigger before the first one has moved the entire row to the bottom of the sheet B.
Does anyone know how to solve this issue?
Right now, the workaround is since it's a shared google sheet, I asked my colleagues not to click the checkbox column at the same time.

This seems to be working for what little testing I've done
function onEdit(e) {
LockService.getScriptLock().tryLock(2000)
const sh = e.range.getSheet();
if(sh.getName() == "Sheet0" && e.range.columnStart == 1 && e.range.rowStart > 1 && e.value == "TRUE") {
let tsh = e.source.getSheetByName("Sheet1");
let vs = sh.getRange(e.range.rowStart, 1, 1,sh.getLastColumn()).getValues();
tsh.getRange(tsh.getLastRow() + 1, 1 , vs.length, vs[0].length).setValues(vs);
}
LockService.getScriptLock().releaseLock();
}

Related

Google Sheets: Append then Delete rows based on Tickbox condition

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.

Moving a row from one sheet to another sheet?

Ok I am completely new to all of this so understand I know nothing.
I am working in google spreadsheet and I want when I mark a job Done in column I it moves the row to another spreadsheet. I have to sheets set up just don’t know how to make work.
I don’t know what script to use or how to import it the right way.
Can someone help?
I have tired a few script I found but get confused on where to put what information for it to work.
Move to Destination when marked done:
function onEdit(e) {
const jobdoneColumn = 1;
const sh = e.range.getSheet();
if(sh.getName() == "your sheet name" && e.range.columnStart == 1 && e.value == "Done") {
let vs = sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues();
let dsh = e.source.getSheetByName("Destination Name");
dsh.getRange(dsh.getLastRow() + 1, 1,vs.length,vs[0].length ).setValues(vs);
sh.deleteRow(e.range.rowStart);
}
}

how do I move a row from one tab in a google sheet to another in the same sheet

I am super new (as in this is my first stab at anything code related) to this...
I've read a bunch of similar questions and I have watched several videos related to the specific thing I am trying to do. I thought it would be simple, but it keeps failing and I cannot figure out why!
I am trying to set it up so that when a checkbox in the row is checked (which currently automatically happens when a value is 0 in another cell), that entire row gets moved to bottom of the list in a different sheet tab within the same spreadsheet and deleted from the first sheet tab.
I know about filtering and every other simpler option, but for what we are doing, it won't work right. It needs to move.
The error I keep getting says: Error Message
TypeError: Cannot read property 'columnStart' of undefined
at onEdit(Move Row When Paid:4:51)
Here is what I have (pic also attached): Code
function onEdit(e) {
const src = e.source.getActiveSheet();
const r = e.range;
if (src.getName() != "Open Invoices" || r.range.columnStart != 11 || r.range.rowStart == 1) return;
const dest = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Closed Invoices");
src.getRange(r.rowStart,1,1,12).moveTo(dest.getRange(dest.getLastRow()+1,1,1,12));
src.deleteRow(r.rowStart);
}
I can link the spreadsheet if needed! All help is appreciated for this noob :)
Try it this way:
function onEdit(e) {
//e.source.toast("Entry")
const sh = e.range.getSheet();
if (sh.getName() == "Open Invoices" && e.range.columnStart == 11 && e.range.rowStart > 4 && e.value == "TRUE") {
//e.source.toast('Flag1');
const dest = e.source.getSheetByName("Closed Invoices");
sh.getRange(e.range.rowStart, 1, 1, 12).moveTo(dest.getRange(dest.getLastRow() + 1, 1));
sh.deleteRow(e.range.rowStart);
}
}
Here's was my sheet looks like:
I don't have your headers so it does not work below line 5

Re-triggering onEdit for each row when pasting multiple rows

I'll start by saying that I'm a complete novice but I spent a few hours manipulating a bit of code I found online to fit a sheet I'm designing. I'm attempting to create a script that will generate a dynamic dropdown to the right of a cell when a value is selected from an existing dropdown.
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Lists")
var r = event.range;
if(r.getColumn() == 3 && r.getRow() > 1 && ss.getName() == "Main"){
r.offset(0, 1).clearContent().clearDataValidations();
var types = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var typeIndex = types[0].indexOf(r.getValue()) + 1;
if(typeIndex != 0) {
var validationRange = datass.getRange(2, typeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
r.offset(0, 1).setDataValidation(validationRule);
}
}
}
Here's my process so far:
I created the initial script with var r = ss.getActiveCell(), which worked for a single cell at a time, but I need it to work when multiple rows are pasted. Version 1
I edited to var r = event.range, which successfully applied the script to all rows that were pasted. However, the script only generated the list for the top cell that I pasted and created that same list for all pasted cells. Version 2
What I need is for onEdit to essentially re-trigger for each row when data is pasted in. Goal Version
Rather than re-triggering onEdit make it to be able to handle both cases. For this, you could take advantage of Range.getRows(), Range.getColumns() as well of edit event object properties like (in this answer e is the event object):
e.range.rowStart
e.range.rowEnd
e.range.columnStart
e.range.columnEnd
If a single cell was edited, rowStart and rowEnd will have the same value, the same for columnStart and columnEnd.
i.e. you might replace
r.offset(0, 1).clearContent().clearDataValidations();
by (remember, I'm using e instead of event for the event object)
r.offset(0, 1, e.range.getRows()).clearContent().clearDataValidations();
Related
Google script onEdit in all pasted cells
How to customize onEdit() script to work on all rows modified when a list of values is pasted from the clipboard?

I am wanting to run a function in script apps that automatically looks for a word in a column and automatically archives that row to another tab

I want to automatically have a row archive to another tab if column D contains the word Deprovisioned. I am not manually editing the cell, I am copy and pasting from a CSV export so the columns come pre-filled with information. I tried the below script, but received an error saying undefined. I am not sure if this has something to do with the fact that I am not manually editing anything on the sheet?
Below is the error I get
var s = event.source.getActiveSheet(); undefined.
Below is the script I am using
function onEDIT(event) {
// assumes source data in sheet named Needed
// target sheet of move to named Acquired
// test column with yes/no is col 4 or D
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "Copy of Zoominfo Usage" && r.getColumn() == 4 &&
r.getValue() == "DEPROVISIONED") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("ZOOMINFO ARCHIVE");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Copy of Zoominfo Usage" && e.range.columnStart == 4 && e.value == "DEPROVISIONED") {
let tsh = e.source.getSheetByName("ZOOMINFO ARCHIVE");
let tgt = tsh.getRange(tsh.getLastRow() + 1, 1);
sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).moveTo(tgt);
sh.deleteRow(e.range.rowStart);
}
}
If you are trying to use simple edit trigger instead of onEDIT you should use onEdit.
An edit trigger, simple or installable will be triggered only when a user uses the Google Sheets UI to edit a cell or range. A paste will trigger the script but the script is not prepared to manage pasting multiple cells
r.getValue() returns only the value of top-left cell
r.getRow() returns only the row of the top row.
It might work when pasting a single row.
It will not work when calling the script from the script editor, as the function requires the event parameter, which is provide automatically when the function is called by the trigger but not when it's called form the script editor.
Please bear in in mind that the active sheet and active range will be the range that has being edited by the user using the Google Sheets UI. When running the function from the script editor, if the Google Apps Script project is opened from the bounded spreadsheet they will be the sheet / range that are active for the user running the function, but if Google Apps Script project were opened from other means like the https://script.google.com then the the methods calling the active sheet / range might return the first sheet / first cell or throw an error.
References
https://developers.google.com/apps-script/guides/triggers
Related
Is it possible to trigger onEdit function on formula
How can I test a trigger function in GAS?