I am trying to create a script that triggers a notification whenever a value is changed in a specific column of a Google Spreadsheet. Ideally, I would like to trigger notifications to other people, each based on changes to specific columns.
I have a test spreadsheet here:
https://docs.google.com/spreadsheets/d/1V4X1FNtYKbXhha84MzeU8kI57ck246WfvSluHlsP1eo/edit?usp=sharing
And have found a script for a custom notification elsewhere in the answers on SO. I took that and tweaked it until I got this:
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = ss.getActiveCell().getA1Notation();
var row = sheet.getActiveRange().getRow();
var column = sheet.getActiveRange().getColumn();
var colLetter = columnToLetter(column);
var cellvalue = ss.getActiveCell().getValue().toString();
var recipients = "email#domain.org";
var message = '';
if(cell.indexOf('G')!=-1){
message = sheet.getRange('F'+ sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'Update to Notification TEST Sheet';
var body = 'Sheet ' +sheet.getName() + ' has been updated. Visit ' + ss.getUrl() + ' View the changes in row ' + row + ', column ' +colLetter+ '.';
MailApp.sendEmail(recipients, subject, body);
};
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;
}
function letterToColumn(letter)
{
var column = 0, length = letter.length;
for (var i = 0; i < length; i++)
{
column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
}
return column;
};
Once you change the receipient email, the script runs, but sends a notification for any change in any cell, not for cells with a specific column.
Can anyone help me get it to do what I am looking for?
TIA!
Imagine your trigger event is, onEdit(e)
function onEdit(e)
{
var range = e.range;
var column = range.getColumn();
if(column == `your expected column number`)
{
// call your send notification function
sendNotification();
}
}
You can read about triggers more. Event Objects
Related
Absolute noob here !
Background:
am trying to create a Google Sheet which I can update for a series of events and
create Google Calendar events based on those entries
so far, am successful in creating calendar events and also updating back the last column of the sheet with the EventID (iCalUID) - thanks to other stackoverflow posts
am also successful in not creating Duplicates by checking if the EventID (iCalUID) is already present in the last column - thanks again to other stackoverflow posts
But... have another requirement, where am failing:
need to mark an existing event as 'Cancelled' in one of the columns in the sheet and
if this is 'true' then look-up the EventID (iCalUID) from the corresponding last cell (of that row which has a 'Cancelled' entry) and
delete that particular event from the calendar
also, calendar events should NOT be created again as long as that cell remains/retains the word 'Cancelled'.
the "var check1 = row[23]; //Booked/Blocked/Cancelled" in below script was just added to bring in this logic that I wanted, but am kind of unable to proceed
Relevant screen-shot of the sheet
Code that I used so far as below:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Sync to Calendar')
.addItem('Sync Now', 'sync')
.addToUi();
}
function sync() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var calendar = CalendarApp.getCalendarById('myemailid#gmail.com');
var startRow = 2; // First row from which data should process > 2 exempts my header row
var numRows = sheet.getLastRow(); // Number of rows to process
var numColumns = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, numRows-1, numColumns);
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var name = row[1]; //Name of Guest
var place = row[4]; //Add2
var room = row[9]; //Room Number
var inDate = new Date(row[10]); //Check-In Date
var outDate = new Date(row[11]); //Check-Out Date
var check1 = row[23]; //Booked/Blocked/Cancelled
var check2 = row[24]; //Event created and EventID (iCalUID) populated
if (check2 == "") {
var currentCell = sheet.getRange(startRow + i, numColumns);
var event = calendar.createEvent(room, inDate, outDate, {
description: 'Booked by: ' + name + ' / ' + place + '\nFrom: ' + inDate + '\nTo: ' + outDate
});
var eventId = event.getId();
currentCell.setValue(eventId);
}
}
}
I believe your goal is as follows.
You want to check the columns "X" and "Y".
When the column "X" is not Cancelled and the column "Y" is empty, you want to create a new event.
When the column "X" is Cancelled and the column "Y" is not empty, you want to delete the existing event.
When the column "X" is Cancelled, you don't want to create a new event.
In this case, how about the following modification?
Modified script:
In this script, in order to check whether the event has already been deleted, Calendar API is used. So please enable Calendar API at Advanced Google services.
function sync() {
var calendarId = 'myemailid#gmail.com'; // Please set your calendar ID.
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var calendar = CalendarApp.getCalendarById(calendarId);
var startRow = 2; // First row from which data should process > 2 exempts my header row
var numRows = sheet.getLastRow(); // Number of rows to process
var numColumns = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, numRows - 1, numColumns);
var data = dataRange.getValues();
var done = "Done"; // It seems that this is not used.
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var name = row[1]; //Name of Guest
var place = row[4]; //Add2
var room = row[9]; //Room Number
var inDate = new Date(row[10]); //Check-In Date
var outDate = new Date(row[11]); //Check-Out Date
var check1 = row[23]; //Booked/Blocked/Cancelled
var check2 = row[24]; //Event created and EventID (iCalUID) populated
// I modified below script.
if (check1 != "Cancelled" && check2 == "") {
var currentCell = sheet.getRange(startRow + i, numColumns);
var event = calendar.createEvent(room, inDate, outDate, {
description: 'Booked by: ' + name + ' / ' + place + '\nFrom: ' + inDate + '\nTo: ' + outDate
});
var eventId = event.getId();
currentCell.setValue(eventId);
} else if (check1 == "Cancelled" && check2 != "") {
var status = Calendar.Events.get(calendarId, check2.split("#")[0]).status;
if (status != "cancelled") {
calendar.getEventById(check2).deleteEvent();
}
}
}
}
Reference:
Events: get
I have a Google Spreadsheet with 4 columns including Products, Salesperson, Category and Status. What I am trying to reach is whenever a user choose Yes option on Status column and if that product category is also G, then sending an email using the MailApp.
The e-mail should include the product value as well.
So far, I was able to sending an email. But I've really confused about the offset concept here and was not able to send the product in the email
The spreadsheet: https://docs.google.com/spreadsheets/d/1wVr0SGryNNvorVdDZEY1E6UDgh25_A5A2LhN1UNbIHE/edit?usp=sharing
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var sheetName = sheet.getName();
var r = sheet.getActiveCell();
var cell = sheet.getActiveCell().getA1Notation();
if(ss.getActiveCell().getColumn() !== 5) return;//
var row = sheet.getActiveRange().getRow();
var cellvalue = sheet.getActiveCell().getValue().toString();
var prod = sheet.getRange().offset(0, -2).getValues();
var cat = cellvalue.offset(0, -1).getValues();
var recipients = "email#email.com";
var message = '';
if(cellvalue === 'Yes' && cat === 'G') {
message = cell + ' in Sheet ' + sheetName + ' was changed to YES.';
var subject = 'Cell Changed to YES';
var body = message + ss.getUrl() + ' to view the changes' + prod;
MailApp.sendEmail(recipients, subject, body);
}
}
It's probably easier to use the object passed with the onEdit event instead of manipulating active cells and offsets.
This event object gives you the new value, so you can check if it is the one that you want ('Yes'). It also gives you the range that was edited, so you can use it to check if the change was in the correct column ('D') and to get the row that was modified. Once you have the row, you can use it to get the values of the other columns ('Products' and 'Cat') in that row.
Something like this should work:
function onEdit(event) {
const statusColumnNumber = 4; // Indices of rows and columns start from 1, so column D is 4.
if (event.range.getColumn() === statusColumnNumber && event.value === 'Yes') {
const sheet = event.range.getSheet();
const rowValues = sheet.getRange(event.range.getRow(), 1, 1, sheet.getLastColumn()).getValues().flat();
const categoryColumnIndex = 2; // Indices of JavaScript arrays start from 0, so column C is in position 2 of the array.
if (rowValues[categoryColumnIndex] === 'G') {
const prodColumnIndex = 0;
const prodValue = rowValues[prodColumnIndex];
const recipients = "email#email.com";
const subject = 'Cell Changed to YES';
const message = event.range.getA1Notation() + ' in Sheet ' + sheet.getName() + ' was changed to YES. '
const body = message + '\n' + sheet.getParent().getUrl() + ' to view the changes for ' + prodValue;
MailApp.sendEmail(recipients, subject, body);
}
}
}
As mentioned in the comments of your question, since you use a service that needs permissions (MailApp), you'll need to create an installable trigger that calls this function.
I want to check if there is any blank cells in a given range and trying to use below code for that. Problem is Range is not certain and subject to change with every iteration.
I tried something like getRange('A'+ row : 'H'+ row) but its in wrong syntax. Can someone help me with this issue ? Thanks!
var sheet1 = spreadsheet.getSheetByName('Red'); // Get worksheet
var endRow = sheet1.getLastRow();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // Get current active spreadsheet.
var sheet2 = spreadsheet.getSheetByName('Template');
var runloop = true;
var startRow = 3;
for (var row = startRow; row <= endRow; row++) {
var sheet_name = sheet1.getRange("A" + row).getValue(); // Get the JD number for the file name.
var range = sheet1.getRange('A' + row: 'H' + row);
if (range.isBlank()) {
# Dome Something here
}
}
You can concatenate the A1Notation before passing it to getRange()
Example:
function myFunction() {
for (var row = 1; row <= 5; row++) {
var rangeA = "A" + row;
var rangeB = "H" + row;
var range = rangeA + ":" + rangeB;
Logger.log(range);
// Get the JD number for the file name.
var sheet_name = SpreadsheetApp.getActiveSheet().getRange(rangeA).getValue();
var range = SpreadsheetApp.getActiveSheet().getRange(range);
if (range.isBlank()) {
Logger.log(range.getA1Notation() + " is blank!.");
}
}
}
Output log from the example:
You can try Utilities.formatString
var range = sheet1.getRange(Utilities.formatString('A%s:H%s', row, row));
For V8 runtime
const range = sheet1.getRange(`A${row}:H${row}`);
Just started my adventure with apps-script and im already stuck.
I'm trying to write script that triggers when any cell in my table is edited , check if that cell is in specific range ( entire column "O"), then gets cell row and sends mail to person bounded with that row.
As long as I got first and last part, I'm having trouble with checking if that range contains cell :
var cell = get.ActiveCell();
var range = ss.getRange("O:O");
However there are few similar columns with similar values, and i want to check this one only so far i got something like that
var ss = getActiveSpreadsheet();
var sheet = ss.getSheetByName("zamówienia");
var cell = get.ActiveCell();
var range =ss.getRange("O:O");
while (i != range.lenght){
if (cell != range[i]) {
i++;
}
else {
break;
return 1;
}
}
You should be using the onEdit() simple trigger, and then you can use the associated event object.
Example:
// This will show a pop up in your spreadsheet whenever you edit a cell in Column O of any sheet
function onEdit(e) {
var columnO = 15;
if (e.range.getColumn() === columnO) {
Browser.msgBox("Column O");
}
}
Got it like that if anybody's trying to sort out same problem. Added on edit simple trigger
function sprawdzenie(){
var ss = SpreadsheetApp.getActive();
var activeRow = ss.getActiveCell().getRow();
var activeCol = ss.getActiveCell().getColumn();
if (activeCol === 13){
var klient = SpreadsheetApp.getActiveSheet().getRange("c"+activeRow).getValue();
var data_wys = SpreadsheetApp.getActiveSheet().getRange("H"+activeRow).getValue();
var numer_oferty = SpreadsheetApp.getActiveSheet().getRange("E"+activeRow).getValue();
var kolor = SpreadsheetApp.getActiveSheet().getRange("B"+activeRow).getValue();
var prowadzacy = (SpreadsheetApp.getActiveSheet().getRange('AL'+activeRow).getValue() );
var zejscie = SpreadsheetApp.getActiveSheet().getRange("M"+activeRow).getValue();
var temat = ('Zejście z produkcji ' + numer_oferty + ' wysłanej dnia ' + data_wys);
var wiadomosc = (' Oferta o numerze :' + numer_oferty + ' ' + klient + ' w kolorze : ' + kolor + ' zeszła z dniem : ' + zejscie);
MailApp.sendEmail(prowadzacy, temat, wiadomosc);}
How do I tell cell C2 to set the date when cells A2 or B2 have been updated?
Further, how do I trigger the sending of my email function when A2 or B2 have been updated?
My issue is that onEdit fires anytime the document is edited at all, but I only want an action if a specific range is edited.
For the sending of emails, I've found and edited a script that almost works, but it sends me email notifications whenever any cell in the document is changed rather than just when column G is changed. Any suggestions?
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = ss.getActiveCell().getA1Notation();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
var recipients = "me#email.com";
var message = 'Cell value has been changed';
if(cell.indexOf('G')!=-1){
message = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'Update to '+sheet.getName();
var body = sheet.getName() + ' has been updated. Visit ' + ss.getUrl() + ' to view the changes on row: «' + row + '». New comment: «' + cellvalue + '». For message: «' + message + '»';
MailApp.sendEmail(recipients, subject, body);
};
Does this have to do with my onEdit() function being off?
For anyone that needs the final scripts
I ended up splitting this up in two separate functions. Here are the finished scripts.
The first one is the email notifications
/* This function send an email when a specified range is edited
* The spreadsheets triggers must be set to onEdit for the function
*/
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Get Active cell
var mycell = ss.getActiveSelection();
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
//Define Notification Details
var recipients = ENTEREMAILHERE;
var subject = "Update to "+ss.getName();
var body = ss.getName() + "has been updated. Visit " + ss.getUrl() + " to view the changes.";
//Check to see if column is A or B to trigger
if (cellcol == EDITMECOLUMN)
{
//check for row to trigger
if (cellrow == EDITMEROW)
{
//Send the Email
MailApp.sendEmail(recipients, subject, body);
}
//End sendNotification
}
}
And here is the one for time stamps
/* This function saves the date in a cell
* every time a specific row or column is edited
* The spreadsheets triggers must be set to onEdit for the function
*/
function setDate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Get Active cell
var mycell = ss.getActiveSelection();
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
//Check to see if column is A or B to trigger
if (cellcol == EDITMECOLUMN)
{
//check for row to trigger
if (cellrow == EDITMEROW)
{
//Find cell and set date in a defined cell
var celldate = sheet.getRange(EDITMEROW, EDITMECOLUMN);
celldate.setValue(new Date());
//end set date
}
}
}
This should work or at least give you the idea as I do something very similar. I usually set the trigger for this to be onEdit()
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Get Active cell
var mycell = ss.getActiveSelection();
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
//Check to see if column is A or B to trigger email
if (cellcol == 1 || cellcol ==2)
{
//check for row to trigger email
if (cellrow ==2)
{
//Find cell and set date
var celldate = sheet.getRange(2, 3);
celldate.setValue(new Date());
//end set date
var cellvalue = mycell.getValue().toString();
var recipients = "me#email.com";
var message = 'Cell value has been changed';
if(cell.indexOf('G')!=-1){
message = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'Update to '+sheet.getName();
var body = sheet.getName() + ' has been updated. Visit ' + ss.getUrl() + ' to view the changes on row: «' + row + '». New comment: «' + cellvalue + '». For message: «' + message + '»';
MailApp.sendEmail(recipients, subject, body);
}
}
}
Your onEdit(event) needs to check the event.range and make decisions based on that.
See Understanding Events.
Here's an answer to a similar question.