I am trying to fix a code I found online. My goal is that once the Summary tab, column I is edited with the drop-down "approved" for the sheet to send an email to the person on the name in column D.
The email is found in the range tab though. This is what I have so far...
var admin_email='taniapeche#gmail.com';
function triggerOnEdit(e)
{
sendEmailOnApproval(e);
}
function checkStatusIsApproved(e)
{ var sheet = SpreadsheetApp.getActive().getSheetByName('Summary');
var range = e.range;
if(range.getColumn() <= 9 &&
range.getLastColumn() >=9 )
{
var edited_row = range.getRow();
var status = SpreadsheetApp.getActiveSheet().getSheetName('Summary').getRange(edited_row,9).getValue();
if(status == 'Approved')
{
return edited_row;
}
}
return 0;
}
function sendEmailOnApproval(e)
{ var sheet = SpreadsheetApp.getActive().getSheetByName('Range');
var approved_row = checkStatusIsApproved(e);
if(approved_row <= 0)
{
return;
}
sendEmailByRow(approved_row);
}
function sendEmailByRow(row)
{
var values = SpreadsheetApp.getActiveSheet().getSheetName('Range').getRange(row,1,row,5).getValues();
var row_values = values[0];
var mail = composeApprovedEmail(row_values);
//SpreadsheetApp.getUi().alert(" subject is "+mail.subject+"\n message "+mail.message);
MailApp.sendEmail(admin_email,mail.subject,mail.message);
}
function composeApprovedEmail(row_values)
{
var first_name = row_values[1];
var last_name = row_values[2];
var email = row_values[3];
var message = "The following mileage is approved: "+first_name+" "+last_name+
" email "+email;
var subject = "Mileage approved "+first_name+" "+last_name
return({message:message,subject:subject});
}
This is how to sheet looks:
https://docs.google.com/spreadsheets/d/1lWORvuwAHducEIiL-VVidJ-wjujE344udPbWCZpE1kw/edit?usp=sharing
Thanks for the help :)
First of all, because you want the script to send an email (an action which requires your authorization), you have to install the edit trigger, either manually or programmatically. If you do it programmatically, you can install the trigger by running this function once:
function createTriggerOnEdit(e) {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("sendEmailOnApproval")
.forSpreadsheet(ss)
.onEdit()
.create();
}
As a result of this, the function sendEmailOnApproval will fire every time the spreadsheet is edited. This function could be something along the following lines (check inline comments for detailed explanation):
function sendEmailOnApproval(e) {
// Get the edited range and sheet using the event object:
var range = e.range;
var editedSheet = range.getSheet();
var textToSearch = "Approved"; // Set which value will cause the email to be sent
// Check that edited cell is in column I, its value is "Approved" and its sheet is "Summary":
if (range.getColumn() === 9 && range.getValue() === textToSearch &&
editedSheet.getName() === "Summary") {
var rowIndex = range.getRow(); // Get index of the edited row
var name = editedSheet.getRange(rowIndex, 4).getValue(); // Get corresponding name in column D
var rangeValues = e.source.getSheetByName("Range").getDataRange().getValues(); // Get values in sheet "Range"
// Iterate through the rows in "Range", looking for the name retrieved from sheet "Summary"
for (var i = 0; i < rangeValues.length; i++) {
var rowValues = rangeValues[i];
if (name === rowValues[0]) { // Check if name matches the one in column D from "Summary"
var mail = composeApprovedEmail(rowValues); // Compose email (your function)
MailApp.sendEmail(admin_email, mail.subject, mail.message); // Send email
return; // End execution so that the script stops iterating through the rows in "Range"
}
}
}
}
Notes:
The function composeApprovedEmail is called in this sample. It's the same as the one you provided. The rest of functions you provided are not used.
Reference:
Installable Triggers
Event Objects: Edit
Related
I have sheet Transactions with the main data, and sheet Report (Active) with the name of the person and into which the metrics that will be calculated from the Transaction main data will be fed. First step is to retrieve the data for that person (cell B6 in the Report) with another condition based on a different column.
var report = SpreadsheetApp.getActiveSpreadsheet();
var trsheet = report.getSheetByName("Transactions");
var areport = SpreadsheetApp.getActiveSheet();
var agent = areport.getRange('B6').getValue();
var trdata = trsheet.getDataRange().getValues();
var tdata = trdata.filter(function (row) {
return row[29] == agent && row[5] == 'Closed' ;
});
This produces no errors, but an empty array for tdata. How do I correctly add the second condition to the filter function?
The code looks fine. It might not work because of the two following reasons:
1. You might be comparing different data types. For example agent might
be a type of string in the Transactions sheet and integer in your
active sheet.
It would be a good idea to try to use getDisplayValues() instead:
var report = SpreadsheetApp.getActiveSpreadsheet();
var trsheet = report.getSheetByName("Transactions");
var areport = SpreadsheetApp.getActiveSheet();
var agent = areport.getRange('B6').getDisplayValue();
var trdata = trsheet.getDataRange().getDisplayValues();
var tdata = trdata.filter(function (row) {
return row[29] === agent && row[5] === 'Closed' ;
});
2. Make also sure that in the sheet Transactions:
column F row[5] contains the keyword you are looking for : 'Closed',
column AD row[29] contains the value of the variable you are looking for : var agent.
I'm relatively new to javascript and definitely new to google scripting.
I have a tab where raw data entry happens. I have another tab where that data is sorted, and an approval date is written next to that line later. I want that date to reflect back to the first tab in the proper line, and I also want to make sure that it stays on the proper line in the second tab (even after new lines are added and sorted in).
Attached is an example sheet with only a few lines. In reality I have more columns but they are irrelevant here.
https://docs.google.com/spreadsheets/d/14dh0IW7vO8c2OLc2O8WE-Ptuh3MJfLWTESVMA_DpOv0/edit?usp=sharing
My second tab uses the sheets SORT function. When A date is typed though, it creates an error. I was going to account for this by using onEdit() function and writing the date into the correct line in the first tab, then clearing the date column in the second tab (to avoid the error and keep it aligned).
''javascript
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet2 = ss.getSheetByName("check released");
var sheet1 = ss.getSheetByName("Log");
var job2 = sheet2.getRange("A2:A1000").getValues();
var job1 = sheet1.getRange("A2:A1000").getValues();
var draw2 = sheet2.getRange("B2:B1000").getValues();
var draw1 = sheet1.getRange("B2:B1000").getValues();
var inv2 = sheet2.getRange("C2:C1000").getValues();
var inv1 = sheet1.getRange("C2:C1000").getValues();
var dates2 = sheet2.getRange("D2:D1000");
var dates_values = sheet2.getRange("D2:D1000").getValues();
var dates1 = sheet1.getRange("D2:D1000");
var indices = []
var to_paste = []
for(var i = 0; i < job2.length; i++) {
var job2_Value = job2[i][0];
var draw2_Value = draw2[i][0];
var inv2_Value = inv2[i][0];
for(var j=0; j<job2.length; j++) {
var job1_Value = job1[j][0];
var draw1_Value = draw1[j][0];
var inv1_Value = inv1[j][0];
if((job2_Value != "") && (job1_Value == job2_Value) && (draw1_Value === draw2_Value) && (inv1_Value === inv2_Value)) {
indices.push([i]);
}
}
}
for(k in indices) {
to_paste.push([dates2[k]])
}
Logger.log(to_paste)
dates1.setValues(to_paste)
dates2.clearContent()
Logger.log("Cleared on check released page")
};
'''
This is the code that I've written, but it doesn't work and I don't know why. I also don't know where to find the console log.
You have a sheet "check released" that takes values from "sheet "Log". Your aim is enter a "release date" on "check released" and have that updated to "Log", and then reflected in "check released". The approach you took was to edit the actual linked "release date" value on "check released". Not surprisingly this wasn't successful because it was merely taking date from "Log".
In the following code:
I have used a helper column on "check released" where the date can be entered.
The onEdit(e) script detects the new value, finds the equivalent row on "Log".
The script updates the "Date" column on "Log" - at this point, the values on "check released" are automatically updated.
Then the script deletes the value in the helper column.
Matching from "Log" to "check released"
The matching of rows from "check released" to "Log" relies on several elements:
No record has a truly unique identifier.
However, the values of Job, Draw and Invoice numbers, when each is converted to a string and concatenated, generate a unique value that can be used to compare values from "Log" to "check released".
The values on "Log" are processed in a loop to create a 1D array of concatenated values.
Event objects provide the edited Row on "check released"; and the Job, Draw and Invoice values in that row are concatenated.
Using the Javascript indexOf method, the script 'finds' the row on "Log" that matches the unique concatenated value from the edited row.
Other items to note:
the actual size of the data range on "Log" and "check released" is determined by using getlastRow()
getRange and getValues are run once each for "Log" and "check released"
Meaningful names for variables have been chosen to reflect their purpose; this assists in reading and understanding the code.
function onEdit(e) {
// 5824330602
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var logsheet = "Log";
var log = ss.getSheetByName(logsheet);
var checkrsheet = "check released";
var checkr = ss.getSheetByName(checkrsheet);
// get Log data
var logFR = 2;
var logLR = log.getLastRow();
var logLC = 4;
var logRange = log.getRange(logFR,1,logLR-logFR+1,logLC);
// Logger.log(logRange.getA1Notation());// DEBUG
var logValues = logRange.getValues();
// get check released data
var checkrRange = checkr.getRange(logFR,1,logLR-logFR+1,logLC);
//Logger.log(checkrRange.getA1Notation());
var checkrValues = checkrRange.getValues();
// build array of uniquelogitems
var logitems=[];
for (var i=0; i<logValues.length; i++) {
var logjob = logValues[i][0].toString();
var logdraw = logValues[i][1].toString();
var loginv = logValues[i][2].toString();
var logid = logjob+logdraw+loginv;
//Logger.log("DEBUG: LOG: Job= "+logjob);
//Logger.log("DEBUG: LOG: draw= "+logdraw);
//Logger.log("DEBUG: LOG: inv= "+loginv);
//Logger.log("DEBUG: LOG: concat= "+logid);
var logid = logjob+logdraw+loginv;
logitems.push(logid);
}
//Logger.log(logitems); //DEBUG
// get the event objects
var editedRow = e.range.getRow();
var editedCol = e.range.getColumn();
var editedSheet = e.range.getSheet().getSheetName();
var editedValue = e.value;
// Logger.log("DEBUG: row = "+editedRow+", column = "+editedCol+", Sheet = "+editedSheet)
// apply logic to test whether this edit should be processed
// Column 5 ("E") is the helper column
if(editedRow >= logFR && editedRow <=logLR && editedCol === 5 && editedSheet === checkrsheet){
// the edit is in Column E (Date), between the first and last rows of data, on the "check released" sheet
//Logger.log("DEBUG: edit is OK. edit row = "+editedRow+". Keep processing");
var checkrjob = checkrValues[editedRow-2][0].toString();
var checkrdraw = checkrValues[editedRow-2][1].toString();
var checkrinv = checkrValues[editedRow-2][2].toString();
var checkritem = checkrjob+checkrdraw+checkrinv;
//Logger.log("DEBUG: Checkr: job="+checkrjob+", draw= "+checkrdraw+", inv = "+checkrinv+", Item = "+checkritem);
var match = logitems.indexOf(checkritem);
//Logger.log("DEBUG: Matching row = "+match);
// get the existing date
var existingdate = logValues[+match+1][3];
var cell = log.getRange(+match+2,4);
//Logger.log("DEBUG: the update cell = "+cell.getA1Notation())
// date field is a date, so update new date
cell.setValue(editedValue);
cell.setNumberFormat('mm/dd/yy');
e.range.clearContent();
//Logger.log("DEBUG: updated date on Log")
}
else{
// the edit didn't meet the rule
//Logger.log("DEBUG: edit did NOT meet criteria. Do not proceed")
}
};
Column E - Helper Column
I want onFormSubmit(e) to be my main function trigger and within that I want onEdit(e) to be nested. Basically, no matter, the trigger will run onFormSubmit but it will do others within the onEdit if there is any edit, if there isn't then it will do something else.
I can't see to understand and make it work.
My script triggers shows onFormSubmit as the only function and onEdit is not in the dropdown.
function onFormSubmit(e){
ScriptApp.newTrigger("onEdit").timeBased().after(60000).create();
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet().getRange('SpeedVSD');
var sheet = ss.getSheetByName("Responses 1");
var row = ss.range.getRow();
var col = ss.range.getColumn();
if (col >= ss.getColumn() && col <= ss.getLastColumn() && row >= ss.getRow() && row <= ss.getLastRow()){
console.log("You edited a Cell within Range");
}
}
edit: Managed to get my lastRow value. However, I am still looking to get a command that can get the lastRow value for all the columns instead of manually doing it.
edit: Using a FOR Loop helps with collating the values.
//This is to get the Last Row on Column 2 value.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheetByName('FIRST');
var row = sheets.getLastRow();
for(var i = 1; i <= sheets.getLastColumn(); i++){
var myID = sheets.getRange(row, i).getValue();
}
console.log("Row Number: "+row);
console.log("Content of last Row: "+myID);```
If you want the onEdit() to run always, you just create it as a separate function. then you can call it from the onFormSubmit(), like this:
function onFormSubmit(e){
//does something you need...
onEdit();
}
onEdit(e){
//do the onEdit code...
}
The only problem with this is that the event e for onFormSubmit() is different than the one for onEdit(), so working with events might not be the best idea. However, calling one function from the other would fun just like with any other function.
function onFormSubmit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheetByName('FIRST');
var row = sheets.getLastRow();
var myIDCol2 = 2;
var myIDCol3 = 3;
var myID2 = sheets.getRange(row, myIDCol2).getValue();
var myID3 = sheets.getRange(row, myIDCol3).getValue();
console.log("Speed Before Trigger Value: "+myID2);
console.log("Voltage Before Trigger Value: "+myID3);
ScriptApp.newTrigger("responsechange").timeBased().after(60000).create();
}
function responsechange(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheetByName('FIRST');
var row = sheets.getLastRow();
var myIDCol2 = 2;
var myIDCol3 = 3;
/*for(var i = 1; i <= sheets.getLastColumn(); i++){
console.log("Content of last Row: "+myID);
}*/
var myID2 = sheets.getRange(row, myIDCol2).getValue();
var myID3 = sheets.getRange(row, myIDCol3).getValue();
var template1 = HtmlService.createTemplateFromFile("speed1");
var template2 = HtmlService.createTemplateFromFile("voltage");
template1.speed1 = myID2;
template2.voltage = myID3;
console.log("Speed After Trigger Value: "+myID2);
console.log("Voltage After Trigger Value: "+myID3);
if((myID2 >=100) || (myID2 <= 50)){
MailApp.sendEmail("someone#gmail.com","Out of Range Notification Speed","",{htmlBody: template1.evaluate().getContent()});
}
if((myID3 >=100) || (myID3 <= 50)){
MailApp.sendEmail("someone#gmail.com","Out of Range Notification Voltage","",{htmlBody: template2.evaluate().getContent()});
}
}
With this, I managed make it work whereby on form submit, lets say the values are below 50 and above 100, it will trigger an email after the time-based trigger. I also tried within the time-based trigger, I edited the values to be within the range and it did not send an email. However, the only problems now is, if there are many triggers, it will stop the trigger by saying
This script has too many triggers. Triggers must be deleted from the script before more can be added.
But on the bright side, I managed to get the last value submitted to have it checked if it was edited or not.
I need to have an email sent to 3 people when column E is updated on a specific tab of the sheet with the value 'TRUE'. I have found a lot regarding how to cite the spreadsheet, but nothing on how to direct it to a particular tab... This sheet has two tabs.
Any advice is greatly appreciated! I have tried this: (PIPELINE is the tab name.)
function sendEmails() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PIPELINE");
var sheetNameToWatch = "PIPELINE";
var columnNumberToWatch = 5; // column A = 1, B = 2, etc.
var valueToWatch1 = "TRUE";
if(column[5] = 'TRUE')
//Define Notification Details
var recipients = "kessla.sloan#skysmb.com, email#email.com";
var subject = "TEST Update"+e.range.getSheet().getName();
var body = "google sheet update - TEST";
//Send the Email
GmailApp.sendEmail(recipients, subject, body);
}
Since you want to run the script every time there is an edit to the sheet, you should use the onEdit(e) trigger.
function onEdit(e){
sendEmails(e.range);
}
The param e of this trigger contains the range of the edit, so you can check easily the column and the sheet in your if.
function sendEmails(range) {
var sheetNameToWatch = "PIPELINE";
var columnNumberToWatch = 5; // column A = 1, B = 2, etc.
var valueToWatch1 = "TRUE";
//here you check if all the requirements are met
var rightSheet = range.getSheet().getName() == sheetNameToWatch;
//the == are used for comparaison, while = are used only for assignment.
var rightColumn = range.getColumn() == columnNumberToWatch;
var rightValue = range.getValue() == valueToWatch1;
// the && are "and" operators, so that the script run only of the 3 conditions are met.
if(rightSheet && rightColumn && rightvalue){ //don't forget the brackets here
//Define Notification Details
var recipients = "kessla.sloan#skysmb.com, email#email.com";
var subject = "TEST Update"+ range.getSheet().getName();
var body = "google sheet update - TEST";
//Send the Email
GmailApp.sendEmail(recipients, subject, body);
}
}
Hope you are all well.
I am trying to write this nice script for my office because my colleagues lack a bit of discipline.
This script should trigger if a column in a sheet is edited, preferably, but it's fine if it trigger on sheet edit or maybe even on spreadsheet edit. This script should look at the value in that column of that sheet and if any of them have the strings "On hold(i)","On hold(ii)" or "On hold(iii)" then the cell on another column (chosen by the user) on the same row should be overwritten with the string "TBC". I tried piecing this from google and below is what I got but since I am here that obviously doesn't work haha. Any help would be greatly appreciated !! :((
function OnEdit() {
var a=1;
var ss = SpreadsheetApp.getActiveSpreadsheet();
while ( a<200 ){
if ( ss.getSheetByName('Active Jobs').getRange(a,12) == "On hold (i)" ) {
ss.getSheetByName('Active Jobs').getRange(a,15).setValues("TBC");
a=a+1;
}
}
}
I read somewhere on google that naming the funciton OnEdit would make the function trigger if the spreadsheet was edited but it doesn't seem to work.
function OnEdit() {
var a=1;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var activeCell = activeSheet.getActiveCell();
//Check if the sheet is a JOb sheet and the cell us the status cell
if (activeSheet.getName().indexOf("Job ID") != -1 && activeCell.getRow() == 2 && activeCell.getColumn() == 15){
var switchValue = activeCell.getValue();
switch (switchValue){
case "On hold (i)":
case "On hold (ii)":
case "On hold (iii)":
case "To be assigned":
//Write date to active jobs sheet
addDateToActive("TBC");
break;
case "In progress":
var newDate = Browser.inputBox("Please enter report out date");
addDateToActive(newDate);
break;
default:
Browser.msgBox("GOTHERE");
}
}
}
function addDateToActive(input){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var activeCell = activeSheet.getActiveCell();
var jobid = activeSheet.getRange(2,1).getValue().toString();
var activeJobSheet = ss.getSheetByName("Active Jobs");
var activeJobs = activeJobSheet.getRange(1,1,activeJobSheet.getLastRow(),1).getValues();
activeJobs = ColumnToArray(activeJobs);
var jobrow = activeJobs.indexOf(jobid)+1;
if (jobrow == -1){
Browser.msgBox("Job Id not preent on Active Jobs sheet");
}else{
activeJobSheet.getRange(jobrow,15).setValue(input);
}
}
function ColumnToArray(columnData){
//Ensure that the data supplied is a column
if(columnData[0].length == 1){
//Convert column to array
var dataout = [];
for (var a = 0; a < columnData.length; a++){
dataout.push(columnData[a][0]);
}
}else{
throw new Error("Data Supplied to ColumnToArray was not a simple Column");
}
return dataout;
}