Trigger bug when copying rows - google-apps-script

I have a function that copies rows from one sheet to another, which works when I run it manually:
function updateDataRange() {
var formSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lastFormRow = formSheet.getLastRow();
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DataRange");
dataSheet.clear();
for(var rowCounter = 1; rowCounter <= lastFormRow; rowCounter++) {
var sourceRange = formSheet.getRange('B'+rowCounter+':H'+rowCounter);
var insertRow = dataSheet.getLastRow() + 1;
var targetRange = dataSheet.getRange('A'+insertRow);
sourceRange.copyTo(targetRange);
}
}
However, when I run this via a trigger (e.g. function onEdit(e) { updateDateRange(); } ), there are gaps in the rows and some of the values will be left out. In this case, I know there is a "fix" by using rowCounter instead of insertRow to write the files instead of using getLastRow(), but one can easily see how this is a problem in another scenario.
So I guess my question is simple: Why isn't this working correctly when using a trigger?
Edit: To clarify, I have a 3rd sheet with some conditions/cells (i.e. conditions that influence which rows are to be copied) and changing them (like changing a different date) trigger the function.

Explanation:
You are using an onEdit trigger to capture sheet changes. But onEdit triggers work only when the user changes the value of a cell. Referencing the official documentation:
The onEdit(e) trigger runs automatically when a user changes the value
of any cell in a spreadsheet.
In your case, I think you are trying to execute this code when the Form Responses sheet is filled in with data by the form.
There are two kind of trigger functions that work with form submissions and they are both installable.
One is a google sheet event based trigger and the other one a form event based trigger.
Your goal is to execute some code from the google sheets side, so it makes sense to use the first one.
Modifications:
Change the name of the function from onEdit to a different name of your choice e.g. myFormSubmitTrigger.
Since the trigger is installable you need to "install" a onFormSubmit trigger for the myFormSubmitTrigger function. Here you can find some simple instructions on how to do that.

If your question is why doesn't your function work when the form makes changes to sheet Form Responses 1 then Marios has answered you question accept it and move on. If your trying to run your function when a user is making edits to sheet Form Responses 1 using a simple trigger then a possible explanation for not getting all of the rows completely copied is that simple trigger must complete in 30 seconds. If you continue to accept more data in Form 1 Responses then soon or later you will have problems but with this function it will be a lot later because it will run much faster than your code:
function updateDataRange() {
const sss=SpreadsheetApp.openById('ssid');
const ssh=e.source.getSheetByName('Form Responses 1');
const sr=2;
const svs=ssh.getRange(sr,2,sh.getLastRow()-ssr+1,7).getValues();
const dsh=e.source.getSheetByName('DataRange');
dsh.clear();
dsh.getRange(1,1,svs.length,svs[0].length).setValues(svs);
}
However, I would recommend that you never edit a form responses sheet. I would consider using the onFormSubmit trigger to capture all of the data to a second sheet that is available to edit. And it will have additional data automatically appended to it via dsh.appendRow(e.values). And so now you would no longer require an onEdit trigger because your data sheet is kept upto data with an onFormSubmit trigger and you may feel free to edit the datasheet any time you wish.
If neither of these questions suffices then I would recommend that you be more clear about what you are asking.

Related

Google Sheet Script not fully executing

I'm using a Google Form to trigger this script.
When I run the script with the Play button it works perfect.
When I let the onsubmit trigger run it, the check box populates fine but the setValue date does not.
I've also tried using setFormula but I get the same result.
function AddCheckBox_toSchoolLunchForm(F) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1")
var criteria = SpreadsheetApp.DataValidationCriteria.CHECKBOX;
var rule = SpreadsheetApp.newDataValidation().requireCheckbox().build();
var range = sheet.getRange(sheet.getLastRow(), 8);
range.clearContent();
range.setDataValidation(rule);
var FormulaCell = sheet.getRange(sheet.getLastRow(), 10);
FormulaCell.setFormula("=DATEVALUE(A" + (sheet.getLastRow()) + ")");
}
The end goal is to have column J populate with the shot formatted date from column A each time the form is submitted.
I need this format to run a countIfs on another sheet.
Another option would be to somehow embed a format tag into this CountIfs command so that they match.
=COUNTIFS('Form Responses 1'!J:J,A2,'Form Responses 1'!D:D,B2)
UPDATED ANSWER
If you have problems running a function on trigger while it works as intended without a trigger, check the following:
For heavy files / form data they can be a delay in populating the sheet with new form data.
To avoid conflicts, give the spreadsheet some time to populate the new row before retrieving sheet.getLastRow() or accessing data in the sheet.
You can dot is easily e.g. with Utilities.sleep().
Check that your trigger has been installed correctly. For this check that
the type of trigger is correct
the trigger is bound to the correct function
avoid conflicts by creating a new trigger when renaming the funciton.
Check that the problem is not due to trigger restrictions
This is a common error source when using simple triggers.
Check that account under whose authorization the trigger is running has permissions to edit the sheet /range. This is important to check when the trigger owner is a diferent person that the person who runs the script manualy.
Note that for setFormula() it is not necessary to incorporate the = into the formula
Note that DATEVALUE() will only return the expected result when the cell is formatted correctly as a date

Insert new row and move data based on cell text

Trying to automate a Google sheet to save time for a team member. I have zero scripting experience. I am trying to insert a row based on a "Yes" result from column K in a Google form submission sheet and then move data in cells L:P to the new row, all without messing up the query functions that are pulling this data.
Is this possible?
Appending selected data from a form submission
function onFormSubmit(e) {
var ss=SpreadsheetApp.openById('SpreadsheetID');
var sh=ss.getSheetByName('Sheet Name')
if(e.values[11]=="Yes") {
var nV=e.values.slice(11,16);
ss.appendRow(nV);
}
}
Since you said that you have zero scripting experience, I should warn you that you cannot run this function without supplying the event object and personally I would never append new rows to a linked sheet. I would append the rows to another sheet instead. There have been problems with the onFormSubmit trigger lately which causes spurious additional triggers as described here.

Trigger a script when a formula changes a cell value

I'm using a Google script then sends out an email when a certain column in a Google sheet is changed. The information in the cell is either inputted manually, or completes using a formula based on information in other cells.
The script works fine when information is manually entered, but not when the formula runs. I've read up on it and realise that a formula calculation doesn't count as an edit, so how do I get the script to run?
It's currently set up to trigger from the spreadsheet when there's an edit.
Below is the part of my script that covers the column/cell in question.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
if(sheet.getActiveRange().getColumn() == 12)
There's a lot more included in the script so I haven't copied everything onto here. Many thanks in advance.
There is no trigger that can run when a formula changes.
Try figure out, what conditions is your formula depends on:
if it is another cell, entered manually, then use those cell to trigger it's changes with onEdit
if the formula imports data from external source, use random or time functions, you'd better use onTime trigger.
if the formula uses importrange then go to the range you import and see the original range, return to step 1 → 2...

How to create a time based trigger for a google spread sheet created with apps script

I wonder if there is a way to create a time based trigger fir a google spread sheet created with google apps script.I googled a lot and no luck.what i am exactly looking for is,i created a google spread sheet and took the id of that spread sheet like this
var spreadSheet = SpreadsheetApp.create('Google Contacts');
var id = spreadSheet.getId();
PropertiesService.getScriptProperties().setProperty('ssId', id);
and then i was trying to create a trigger for this spread sheet like this
ScriptApp.newTrigger("myTrigger").timeBased().everyMinutes(1).create();
and myTrigger is this
function myTrigger(e) {
var range = e.range;
range.setNote('Last modified: ' + new Date());
}
I want to run this trigger ,ie,this time based trigger on my spread sheet created with the previous method.And now i don't find a way for this.
What i am looking for is, i want to run this time based trigger on the spread sheet with this id
var id = PropertiesService.getScriptProperties().getProperty('ssId');
I was able to create this trigger for this spread sheet with the code ScriptApp.newTrigger('myTrigger').forSpreadsheet(id).onEdit().create(); but this is not triggering on a time based ,rather than it is triigering on editof the above spread sheet.
Thanks.
The issue here is your trigger function myTrigger is coded to expect an onEdit event rather than an time based trigger event, as a result the script simply crashes when your time-based trigger calls it.
You can probably see details of this failure in the Execution Transcript of your Script Editor.
The reason is the reference to e.range, with an OnEdit trigger e.range is the edited range, but on a time-based trigger e.range is undefined. This is because the time-based trigger has nothing to do with any given range of cells.
Since your goal seems to be to set a note saying when a given range was last modified, I don't think you can easily achieve your final goal with a time-based trigger, why not simply use the onEdit trigger?
If you want to interact with the spreadsheet from your time based trigger, you will need to open the spreadsheet and get the range, then take your actions.
function myTrigger(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("A1:B1");
range.setNote('...')
You can see the different properties of event vs time-driven triggers listed in the tables here: https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events

Spreadsheet Email Trigger

I have Written Script on Google Spreadsheet to send Email when spreadsheet is modified or any Data is added. Email Trigger is working but whenever any data is entered in next Row it send Email to previous email address also.
Please suggest solution
The below is written script :
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1 , numRows,3) // Fetch the range of cells A2:B3
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[2]; // First column
var message = row[0] + "requested" + row [1]; // Second column
var subject = "Sending emails from a Spreadsheet";
MailApp.sendEmail(emailAddress, subject, message);
}
}
Your question is unclear... nowhere in the script I see something that reads which cell is actually modified... your target range is hardcoded on row 2 so the only row that can be processed is row 2 (and the mail can only be sent once)...
So can you :
explain how it should work
explain how it works now , especially what do you mean by 'previous email'
remove typos in your code (row[2] is not First column)
explain how you trigger this function : the name onEdit(e) suggest an onEdit trigger but simple triggers cannot send mail so I suppose you have set some other trigger.
explain why (e) in your function parameter and not using it ?
EDIT : thanks for the complement of information.
The script you suggest is not sufficient to achieve what you want. The idea here is to check if something in the sheet has been modified either by adding (or inserting) a row of data or (if I understood well) by editing any row in the sheet with a new value.
This is not really as simple as it looks at the first glance ;-)
What I would do it to take a 'snapshot' of the sheet and -based on a timer or onEdit - compare that snapshot to the sheet's current state.
There is more than one way to get that result, you could have a second sheet in your spreadsheet that no one could modify and that is a copy of the main sheet that you update after each modification/mail send. So before updating the script should look for any difference between the sheets and send a report to the corresponding email when a difference is found.
Another way to do that is to store the sheet data converted to a string in the script properties, the principle is the same but it's more 'invisible' for normal users accessing the spreadsheet.
You could also use scriptDb or your userproperties but the script properties is probably better suited (simpler) for this use case.
Tell us what you think/prefer and I (or someone else) could probably give you some code to start with.
It appears that you're using a shared spreadsheet to collect the add-user-requests, and trusting the requesters to fill in the information. In the detail document you shared, it further appears that requests are ADDED, but not EDITED. (That's an important simplifying distinction.)
I suggest that what you really need is to use a form for receiving that input. Using a form will create a "data table" within your spreadsheet, a set of columns that you must not mess with. (You can edit the contents, add and delete rows, but must not add or remove columns.) However, you CAN add columns to the spreadsheet outside of this table, which gives you a handy place to store state information about the status of individual requests.
Further, you can trigger your processing to run on form submit, rather than a simple "onEdit" - this gets away from the problem that ScampMichael pointed out. Alternatively, you can use an installable edit trigger, as described in this answer.
Try this sheet, and this form. Save yourself a copy, go into the script and remove the comments that are stopping emails from being sent, and try it out. There's a menu item in the spreadsheet that can kick off processing; just clear the "Request State" column to re-run it. You can open the form (and find its URL), and add more entries to experiment.
It's the core of a similar system that I've written, and contains a discreet state machine for processing the requests. My system has large amounts of very complex data in multiple spreadsheets, so it often gets pre-empted, then needs to run again. (I use a timed trigger for that.) That's why requests are handled through states. If you find that too complex, pull out only the parts you need.