Script triggered by new row appearing in sheet - google-apps-script

I am using a snapData script sourced externally and shown below:
function snapData() {
// get current sheet and tabs
var ss = SpreadsheetApp.getActiveSpreadsheet(); var current = ss.getSheetByName('Fees (management)'); var database = ss.getSheetByName('Fees (data)');
// count rows to snap var current_rows = current.getLastRow();
var database_rows = database.getLastRow() + 1;
var database_rows_new = current_rows + database_rows - 2;
var rows_new = current.getRange("A2:F" + current_rows).getValues();
// snap rows, can run this on a trigger to be timed
database.getRange("A" + database_rows + ":F" + database_rows_new).setValues(rows_new);
}
At the moment, it is triggered by pressing the 'Run' button, however I would like to have the script triggered by a new row appearing on another tab.
Help with this would be appreciated. Thanks!

"I would like to have the script triggered by a new row appearing on another tab."
By using the installable trigger onChange, it is possible to trigger the script as planned. source is an Event Object returned by onChange, though it is not included in the documnentation.
var sheetname = src.getActiveSheet().getSheetName(): get the sheet name
var ctype = e.changeType;: get the change type
if (sheetname === "target" && ctype === "INSERT_ROW"){: test if the type was inserting a row on "target"
function so5899606501(e) {
// return all the event objects
Logger.log(JSON.stringify(e));
// get the source
var src = e.source;
var spreadsheetname = src.getName();
var sheetname = src.getActiveSheet().getSheetName()
Logger.log("spreadsheet = "+spreadsheetname+", sheet name = "+sheetname);
var currentcell = src.getCurrentCell();
Logger.log("the current cell = "+currentcell.getA1Notation());
// get the change type
var ctype = e.changeType;
Logger.log("the change type is "+ctype);
// test for the type of change
if (ctype == "INSERT_ROW"){
Logger.log("A row was just inserted");
}
else
{
Logger.log("the change type wasnt a new row, it was "+ctype)
}
//test for the sheet where the change took place
if (sheetname == "target"){
Logger.log("the change took place on the sheet named 'target'");
}
else
{
Logger.log("the change took place on the sheet named" +sheetname+".")
}
// test for the change type AND the sheet name
if (sheetname == "target" && ctype == "INSERT_ROW"){
Logger.log("Eureka!. A new row was inserted on the sheet named 'target. Lets do stuff");
}
else{
Logger.log("Sigh. It either wasn't a new row AND/OR it wasn't on the sheet named 'target'. Either way, we can ignore it.");
}
}

Related

Auto Refreshing ImportXML Google Sheets

I have a sheet that imports data from a site, there are about 10 imports all slightly different. I want a way that I could update that every five minutes or so. I know there are scrips to do it but when I tried to use one it would just put ?update at the end and mess up the entire import. I can show the script I am using. It is from 4 years ago and maybe it is outdated. Any help would be appreciated.
Also just having a script that changes the = to nothing then adds it back again or something would work I guess.
Here is the script I am using
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
var id = "Sheets ID";
var ss = SpreadsheetApp.openById(id);
var sheet = ss.getSheetByName("Sheet Name");
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var content = "";
var now = new Date();
var time = now.getTime();
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
var re2 = /((\?|&)(update=[0-9]*))/gi;
var re3 = /(",)/gi;
for (var row=0; row<formulas.length; row++) {
for (var col=0; col<formulas[0].length; col++) {
content = formulas[row][col];
if (content != "") {
var match = content.search(re);
if (match !== -1 ) {
// import function is used in this cell
var updatedContent = content.toString().replace(re2,"$2update=" + time);
if (updatedContent == content) {
// No querystring exists yet in url
updatedContent = content.toString().replace(re3,"?update=" + time + "$1");
}
// Update url in formula with querystring param
sheet.getRange(row+1, col+1).setFormula(updatedContent);
}
}
}
}
// Done refresh; release the lock.
lock.releaseLock();
// Show last updated time on sheet somewhere
sheet.getRange(12,2).setValue("Rates were last updated at " + now.toLocaleTimeString())
}
You can write a script that retrieves the formula from the specified cell and sets it back to the same cell on a timer
For this you need:
Retrieve the formula with getFormula()
Set the formula back into the same cell with getFormula()
Bind an installable time-driven trigger to your function specifying the desired interval
Sample:
function setMeOnTrigger() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var cell = sheet.getRange("C1");
cell.setFormula(cell.getFormula())
}

How to freeze a range of cells when a checkbox is clicked?

Screenshot for the sheet:
I just need someone to help me write simple code to freeze a range of cells when a certain checkbox is clicked.
I would like it so that when I click on the 'Complete' checkbox, all of the ones above it cannot be edited or changed anymore. Vise Versa when the 'Complete' checkbox is unchecked the ones above are editable. That simple.
The purpose of the sheet is to take attendance for a class. When I am done taking the attendance I don't want to be able to change it anymore (or risk clicking on the wrong checkbox). That's why the complete button is there.
Can anyone write the code for me, please?
(Freeze or seal or protect)
This code is not working (I am a beginner so sorry)
function onEdit() {
var sheet = SpreadsheetApp.getActive();;
var completedRow = sheet.getDataRange();
for (i = 2; i < 18; i++){
var isComplete = source.getRange(countRow, i).getValue();
if (isComplete === true){
source.getRange(2, i, countRow-1).protect();
}
}
}
Your code reflects the basic logic, though there are some syntax flaws. Hopefully this answer will help you understand and adapt that syntax.
The code doesn't doesn't take advantage of the Event Objects that are available to onEdit(e), which include the row, column and value of the edited cell. It's not compulsory to use the Event objects, but they certainly make life easier.
countRow isn't defined; and because you are working with a spreadsheet of finite length (20 rows); it is probably unnecessary. But it is a sensible idea to allow for bigger spreadsheets. Maybe something like var countRow = sheet.getLastRow(); would be a good alternative Doc Ref.
isComplete - we know that this is always on row 20; we also know that it will have a value of "true" or "false". So, you don't need a loop to define this row.
At some stage, you may want to "unprotect" a column; say at the start of a new term or year; so it's likely that checking row 20 for a value of "false" could be useful.
Your goal can probably be achieved in many ways. The following should be considered as just one option.
The main function is setup in an onEdit(e) simple trigger.
I also setup a custom menu (using onOpen) that gives you access to view all the protected columns, and to remove protection if you need to.
I've also left some Logger.log statements in the code that may enable you to check the value of certain fields at key stages of the code.
All-in-all, this code follows the same logic as your code, but with some more detail.
One last thing, this code is designed to work on a specific sheet by virtue of var sheet = ss.getSheetByName(sheetname); but you could just as easily change this to var sheet = SpreadsheetApp.getActiveSheet(); to make it work on multiple sheets in your spreadsheet.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
// set variable for last column
//Logger.log(JSON.stringify(e))
// set variables for edited cells,
var edittedRow = e.range.rowStart;
var edittedColumn = e.range.columnStart;
var newValue = e.value;
var headerrange = sheet.getRange(1, edittedColumn);
var headervalue = headerrange.getDisplayValue();
//Logger.log("DEBUG: The header range is "+headerrange.getA1Notation()+", and the value is "+headervalue);
// test if edit row =20, and the checkbox was ticked
if (edittedRow === 20 && newValue === "TRUE") {
//Logger.log("DEBUG: The 'ON' leg applies");
//Logger.log("DEBUG: edittedRow = "+edittedRow+", Editted column = "+edittedColumn+", and value = "+newValue);
// define the range to protect
var protectRangeOn = sheet.getRange(1, edittedColumn, 19, 1);
// protect the range - warning only.
protectRangeOn.protect().setDescription(headervalue)
.setWarningOnly(true);
//Logger.log("DEBUG1: protection set for "+protectRangeOn.getA1Notation());
}
//test if edit row=20, and the checkbox was unticked
if (edittedRow === 20 && newValue === "FALSE") {
//Logger.log("DEBUG: The 'OFF' leg applies");
//Logger.log("DEBUG: edittedRow = "+edittedRow+", Editted column = "+edittedColumn+", and value = "+newValue);
// define the range to unprotect
var protectRangeOff = sheet.getRange(1, edittedColumn, 19, 1);
var protections = sheet.getProtections(SpreadsheetApp
.ProtectionType.RANGE)
for (var i = 0; i < protections.length; i++) {
Logger.log("protections range name = " + protections[i]
.getDescription() + " - Header value = " + headervalue);
if (protections[i].getDescription() === headervalue) {
//Logger.log("DEBUG: OFF matches")
protections[i].remove();
}
}
//Logger.log("DEBUG2: protection unset for "+protectRangeOff.getA1Notation());
}
}
// Add a custom menu to the active spreadsheet to access Utilities
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Protection Utilities')
.addItem('Show all protections', 'uigetprotections')
.addItem('Remove all protections', 'removeallprotections')
.addToUi();
}
function removeallprotections() {
// remove all protections
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
var protections = ss.getProtections(SpreadsheetApp.ProtectionType
.RANGE);
Logger.log(protections);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
Logger.log(protection.getEditors())
if (protection.canEdit()) {
protection.remove();
}
}
// Display confirmation dialog
var ui = SpreadsheetApp.getUi();
var response = ui.alert('REMOVE ALL PROTECTION',
'Confirmed: Removed all protections', ui.ButtonSet.OK);
}
function uigetprotections() {
// generate a list of all RANGE protections
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
var protections = ss.getProtections(SpreadsheetApp.ProtectionType
.RANGE);
//Logger.log(protections);
var ui = SpreadsheetApp.getUi();
var protectioninfo = "";
if (protections.length != 0) {
for (var p = 0; p < protections.length; p++) {
//Logger.log("DEBUG: Date = "+protections[p].getDescription()+", Range = "+protections[p].getRange().getA1Notation());
protectioninfo = protectioninfo + "Date: " + protections[p]
.getDescription() + ", Range = " + protections[p].getRange()
.getA1Notation() + "\n";
}
var response = ui.alert('SHOW ALL PROTECTIONS', protectioninfo, ui
.ButtonSet.OK);
} else {
var response = ui.alert('SHOW ALL PROTECTIONS',
"There were no protected ranges", ui.ButtonSet.OK);
}
}

Send email when cell is edited using email in parallel cell

I have read a lot of onEdit and Triggers script but still I cannot achieve what I want to achieve, I code a lot in excel VBA and google sheet is very different. So the thing is based on my screenshot, what I want is to send an email once the cell contains "Approved", "denied", "In progress" and the email address must be based on the parallel of the edited cell. I'm dying to get this work done.
The code is based on the internet but I cannot tweak it based on my data/sheet.
You can add a custom function to a dropdown menu in the Spreadsheets UI with the following script. This will allow you to circumvent the onEdit() restriction that doesn't allow one to use the MailApp class, but it is at the cost of having users manually call the script instead of the script running automatically.
Here the user will select "Send E-Mail" from the dropdown menu, and it will prompt him/her for the Primary Key via an input prompt modal. The row of the corresponding key will be identified and an e-mail sent out after status is automatically changed to "approved". This script assumes that the spreadsheet contains at least four columns with header rows "Primary Key", "Description", "Email", and "Status" in any order.
Please note: this code was tested successfully. Please update lines 20 and 21 by replacing the square brackets and text contained therein that defines sheetURL and workSheetName variables.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Send E-Mail', 'sendEmail')
.addToUi();
}
function sendEmail(){
// Display a dialog box with a title, message, input field, and "OK" and "Cancel" buttons. The
// user can also close the dialog by clicking the close button in its title bar.
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Pop-Up Prompt', 'Please enter primary key:', ui.ButtonSet.OK_CANCEL);
// Process the user's response.
if (response.getSelectedButton() == ui.Button.OK) {
Logger.log('The user entered the following primary key:', response.getResponseText());
// Map the header rows in order that column position is not hard-coded.
var sheetURL = '[ENTER YOUR SHEET URL HERE]';
var workSheetName = '[ENTER YOUR WORKSHEET NAME HERE]';
var sheet = SpreadsheetApp.openByUrl(sheetURL).getSheetByName(workSheetName);
var lastColumn = sheet.getLastColumn();
var headerRange = sheet.getRange(1, 1, 1, lastColumn);
var headers = headerRange.getValues();
for (var i=1; i<headers[0].length+1; i++) {
switch (headers[0][i-1]){
case "Primary Key":
var primaryKeyIndex = i;
break;
case "Description":
var descriptionIndex = i;
break;
case "Email":
var emailIndex = i;
break;
case "Status":
var statusIndex = i;
break;
}
}
// Header rows mapped.
// Search for row corresponding to primary key.
var primaryKey = response.getResponseText();
var keyRow = findInColumn(columnToLetter(primaryKeyIndex), primaryKey);
if (keyRow == -1){
ui.alert('Primary Key "'+ primaryKey + '" not found.');
} else {
ui.alert('Primary Key "'+ primaryKey + '" found at row: ' +keyRow+ '.');
sheet.getRange(keyRow, statusIndex).setValue("Approved");
//Prepare Email
var subject = "test";
var email = sheet.getRange(keyRow, emailIndex).getValue();
var body = "Hi, \n\n Your entry with primary key " + primaryKey + " is now approved.";
MailApp.sendEmail(email, subject, body);
}
} else if (response.getSelectedButton() == ui.Button.CANCEL) {
Logger.log('The user clicked cancel.');
} else {
Logger.log('The user clicked the close button in the dialog\'s title bar.');
}
}
// Helper function to find corresponding row to data in column.
function findInColumn(column, data) {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var column = sheet.getRange(column + ":" + column); // like A:A
var values = column.getValues();
var row = 0;
while ( String(values[row]) && String(values[row][0]) !== String(data) ) {
row++;
}
if (String(values[row][0]) === String(data))
return row+1;
else
return -1;
}
// Helper function to convert Column Number to Column Letter
function columnToLetter(column){
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
I'd suggest that you not use the onEdit trigger for sending email. I think it's over used by many users. If you are, you will have to go with the installable triggers. This is an example email solution that looks pretty clean that came in yesterday.
You can use most of this code below. Modify the email portions to suit your needs.
This code checks for sheet name to be 'Form Responses' and edited column header to be 'Status' as from pics given above.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var aSheet = ss.getActiveSheet();
// check sheet name
if (aSheet.getName() != 'Form Responses') return;
var range = ss.getActiveRange();
var row = range.getRow();
var col = range.getColumn();
// Logger.log(col);
var headers = aSheet.getRange(1,1,1,aSheet.getLastColumn()).getValues()[0];
// Logger.log(headers[col-1]);
// check column header
if (headers[col-1] != 'Status') return;
var value = range.getValue();
var values = ["approved", "denied", "in progress"]; // values to check for
// check values
if (values.indexOf(value.toString().toLowerCase()) == -1) return;
// Logger.log(row);
var rowValues = aSheet.getRange(row, 1, 1, aSheet.getLastColumn()).getValues()[0];
Logger.log(rowValues);
// change as necessary
var recipient = rowValues[1]; // email is in 2nd column
var body = 'Email body'; // create body
var subject = 'Test'; // set subject
// send email
MailApp.sendEmail(recipient, subject, body);
}

Automatically replace a text value in Google Sheets

I have set up an email to Google Sheets automation, where contents from a specific email automagically become new rows in a Google sheet. But one of the values is a full address (which is always one of only two addresses), and I would like it to be changed automatically to just the place name. So "address No.1" to "Place Name No.1", and "address No.2" to "Place Name No.2".
The text is always in column E, and added to a new row.
Here is what I tried to use:
function onOpen() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("e1:e10");
var to_replace = "full long address";
var replace_with = "place name";
replaceInSheet(sheet,range, to_replace, replace_with);
}
function replaceInSheet(sheet, range, to_replace, replace_with) {
//Confirm
var ui = SpreadsheetApp.getUi();
var spread = SpreadsheetApp.getActiveSpreadsheet();
var result = ui.alert(
"Will update " + to_replace + " to " + replace_with + " ",
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
spread.toast("Will update " + to_replace + " to " + replace_with + " ", "ALERT");
var data = range.getValues();
var oldValue="";
var newValue="";
var cellsChanged = 0;
for (var row=0; row<data.length; row++) {
for (var item=0; item<data[row].length; item++) {
oldValue = data[row][item];
newValue = data[row][item].replace(to_replace, replace_with);
if (oldValue!=newValue)
{
cellsChanged++;
data[row][item] = newValue;
}
}
}
range.setValues(data);
spread.toast(cellsChanged + " cells changed", "STATUS");
}
else {
// User clicked "No" or X in the title bar.
spread.toast("No action taken", "ABANDONED");
}
}
The OP's original code was most elegant (sourced from webapps) but over complicated for the task, and also not adapted to the replacing two addresses.
The following code is essentially a cut-down version of the OP original. A number of Logger.log() statements have been kept which will allow the OP (if &/or when necessary) to test values at different stages of the code.
The code logic is straightforward.
1) the addresses to be found and replaced are described as variables (var address01find and var address01replace, rinse and repeat for address02). The OP can edit this according to their taste.
2) get the last row in column E.
3) get the values for column E.
4) iterate through the values, row-by-row, testing for a value equal to either address01find or address02find. If the value is found, replace the value with address01replace and address02replace respectively.
5) after the loop, setValues for the entire data range. Most field values won't change, but those fields that were modified during the loop, will be updated to the revised value.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var MenuEntries = [{
name: "Replace addresses",
functionName: "replaceaddresss"
}];
ss.addMenu("Address Update", MenuEntries);
};
function replaceaddresss() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Logger.log("DEBUG: sheet name: "+sheet.getSheetName());//DEBUG
//Logger.log("DEBUG: range: "+range.getA1Notation());//DEBUG
var Evals = ss.getRange("E1:E").getValues();
var Elast = Evals.filter(String).length;
//Logger.log("DEBUG: last row in E: "+Elast);//DEBUG
var range = sheet.getRange(1, 5, Elast)
//Logger.log("DEBUG: range: "+range.getA1Notation());//DEBUG
var columnValues = range.getValues();
var address01find = "Full Address 123, Tokyo, Japan";
var address01replace = "Place Name No.1";
var address02find = "Short Address 123, Tokyo, Japan";
var address02replace = "Place Name No.2";
//Logger.log("DEBUG: address #1: find: "+address01find+", replace with: "+address01replace);//DEBUG
//Logger.log("DEBUG: address #2: find: "+address02find+", replace with: "+address02replace);//DEBUG
for (i = 0; i < Elast; i++) {
if (columnValues[i][0] === address01find) {
columnValues[i][0] = address01replace;
}
if (columnValues[i][0] === address02find) {
columnValues[i][0] = address02replace;
}
};
range.setValues(columnValues);
}
Before and after
UPDATE for OnEdit
This update looks at the scenario where data (i.e. the street address in Column E) is created by a script.
The code is essentially the same except that the event range is known from onEdit, and it is only necessary to get the values, evaluate the addresses, and update the data IF the address value was changed.
Note: this script requires that it be created as an Installable OnEdit Trigger.
function oneditemailupdate(e) {
// set up spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// setup triggerdetector
var trigger = 0;
// User variables
// identify the column to watch
var addresscol = 5; // = Column E - this is the column to watch
// Setup the old and new addresses
var address01find = "Full Address 123, Tokyo, Japan";
var address01replace = "Place Name No.1";
var address02find = "Short Address 123, Tokyo, Japan";
var address02replace = "Place Name No.2";
// list onedit values for debug reporting and other use
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e.oldValue
};
//Logger.log("DEBUG: AuthMode: "+debug_e.authMode);// DEBUG
//Logger.log("DEBUG: Range: "+debug_e.range);// DEBUG
//Logger.log("DEBUG: Source: "+debug_e.source);// DEBUG
//Logger.log("DEBUG: User: "+debug_e.user);// DEBUG
//Logger.log("DEBUG: user email"+debug_e.user.getEmail());// DEBUG
//Logger.log("DEBUG: Value: "+debug_e.value);// DEBUG
//Logger.log("DEBUG: Old value: "+debug_e.oldValue);// DEBUG
//Logger.log("DEBUG: AuthMode: "+debug_e.authMode+", Range: "+debug_e.range+", source: "+debug_e.source+", user: "+debug_e.user+", value: "+debug_e.value+", old value: "+debug_e.oldValue);//DEBUG
// get the values for the range that was edited (e.range)
var emailvalues = e.range.getValues();
// evaluate email address value#1 and change value if necessary
if (emailvalues[0][4] === address01find) {
emailvalues[0][4] = address01replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address01");//DEBUG
}
// evaluate email address value#2 and change value if necessary
if (emailvalues[0][4] === address02find) {
emailvalues[0][4] = address02replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address02");//DEBUG
}
// if trigger value has been set to one, then update the values
// Logger.log("DEBUG: Trigger value is "+trigger);//DEBUG
if (trigger == 1) {
e.range.setValues(emailvalues);
}
}
UPDATE - FORM SUBMIT
Mea culpa. I managed to ignore the fact that the OP said that the spreadsheet was an email response sheet. Script executions and API requests do not cause triggers to run. So onEdit (whether as a Simple or Installable trigger) will not work.
The script needs to be installed as an Installable Script
The event type = "On form Submit"
This will return 'Form Submit' event data when the event object is called.
Note: the sheet name is now a variable. Otherwise the code is almost (but not quite) identical to the previous onEdit version.
function addressupdate(e) {
// set up spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formresponsesheet = "Form Responses 2";
var sheet = ss.getSheetByName(formresponsesheet);
// setup changetriggerdetector
var trigger = 0;
// User variables
// identify the column to watch
var addresscol = 5; // = Column E - this is the column to watch
// Setup the old and new addresses
var address01find = "Full Address 123, Tokyo, Japan";
var address01replace = "Place Name No.1";
var address02find = "Short Address 123, Tokyo, Japan";
var address02replace = "Place Name No.2";
// list onedit values for debug reporting and other use
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
namedValues: e.namedValues,
triggerUid: e.triggerUid,
values: e.values
};
//Logger.log("DEBUG: AuthMode: "+debug_e.authMode);// DEBUG
//Logger.log("DEBUG: Range: "+debug_e.range);// DEBUG
//Logger.log("DEBUG: named ValuesSource: "+debug_e.namedValues);// DEBUG
//Logger.log("DEBUG: triggerUid: "+debug_e.triggerUid);// DEBUG
//Logger.log("DEBUG: values: "+debug_e.values);// DEBUG
// get the values for the range that was created (e.range)
var emailvalues = e.range.getValues();
// evaluate email address value#1 and change value if necessary
if (emailvalues[0][4] === address01find) {
emailvalues[0][4] = address01replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address01");//DEBUG
}
// evaluate email address value#2 and change value if necessary
if (emailvalues[0][4] === address02find) {
emailvalues[0][4] = address02replace;
// set the trigger so that the range value can be updated
trigger = 1;
//Logger.log("DEBUG: Updated address02");//DEBUG
}
// if trigger value has been set to one, then update the values
// Logger.log("DEBUG: Trigger value is "+trigger);//DEBUG
if (trigger == 1) {
e.range.setValues(emailvalues);
}
}

Cell reference out of range, google sheets

I'm getting this error while running this sheet.
Cell reference out of range (line 81, file "genreportSE")
I don't know why it says it's 'out of range'.
I tried to used 'copyvalues'. I saw a script where you can't really "print" a range, but you can create another spreadsheet, copy that range, then print that sheet and delete it.
How should I accomplish this?
function genreportSE() { // This function let us read the value of a cell from a sheet and change the value of another cell in a different sheet
var ss = SpreadsheetApp.getActive(); //ss stands for spreadsheet, this is the active spreadsheet
var clientsheet = ss.getSheetByName('Clientes SE');
var gensheet = ss.getSheetByName('Generador SE');
var clienttable = clientsheet.getDataRange();
var numberofservices = clienttable.getNumRows(); //The number of services in the Clientes sheet
var error1;
var error2;
var rangetocheck1;
var rangetocheck2;
var client;
var clientname;
var i=0;
var reportswitherrors = []; //Array for faulty reports
var email ='jvaldez#galt.mx';
var subject = "Reporte de producción y consumo - " + (new Date()).toString();
var body = "TEXT" ;
for (i=0;i<=2;i++){
gensheet.getRange('B2').setValue(clientsheet.getRange(i+2,1).getValue()); //This will change the cell "B2" in "Generador SE" to the current service number for the report generation
Utilities.sleep(3000); //A timer to let the importdata function get the data from the SE server in miliseconds
client = gensheet.getRange('B4').getValue;
clientname = String(client);
rangetocheck1 = gensheet.getRange('B8:C14').getValues(); //Data range that could present calculation errors ********
rangetocheck2 = gensheet.getRange('H8:H14').getValues(); //Data range that could present calculation errors ********
if(String(rangetocheck1).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck1
error1 = false;
} else {
error1 = true;
};
if(String(rangetocheck2).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck2
error2 = false;
} else{
error2 = true;
};
if(error1||error2){
reportswitherrors.push(clientsheet.getRange(i+2,1).getValue()); //This appends the current service number to the faulty services array
} else {
// Convert individual worksheets to PDF
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
newSpreadsheet.getSheetByName('Sheet1').activate();
var newsheet = newSpreadsheet.getSheetByName('Sheet1');
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)
var pdf = DriveApp.getFileById(newSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Weekly Status.pdf',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(email, subject, body, {attachments:[attach]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}
};
Logger.log(reportswitherrors);
}
It appears that you've got your row & column dimensions flipped between function calls. (Because Google decided to be inconsistent with the order of them...)
This line calls create(name, rows, columns):
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
You've created a spreadsheet with 15 rows and 60 columns.
A bit further along, probably on line 81, copyValuesToRange(sheet, column, columnEnd, row, rowEnd) gets invoked:
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)