I am trying to create a small laptop rental system in the Google Sheets which sends email depending on checkbox which is selected (checked).
Two emails which are sent are: 1. Approved and 2. Overdue.
Problem that I have with my attempt is that multiple emails are sent even to the users which already have received the approval email when approved checkbox is selected and that no user is receiving any overdue email when overdue checkbox is selected. Also I have a Trigger set up with On edit. Its been wrecking my head for a while now, an help, pointers are greatly appreciated.
function onCheckboxEdit(e) {
var source = e.source;
var sheet = source.getActiveSheet();
var range = e.range;
var row = range.getRow();
var column = range.getColumn();
console.log("column:: ", column);
var targetRange = sheet.getRange(row, 1, 1, 17);
var targetValues = targetRange.getValues();
console.log("targetRange:: ", targetValues);
var student = targetValues[0][2];
var recipient = targetValues[0][1];
var checkboxValue = targetValues[0][9];
var checkboxValue2 = targetValues[0][17];
var subject = ("Laptop Loan Approved");
var body = ("Hello "+ student +", \n\nWe are happy to confirm that your laptop loan application has been approved.");
var subject2 = ("Laptop Loan");
var body2 = ("Hello "+ student +", \n\nPlease disregard last e-mail sent by us, it was mistakenly sent.");
var subject3 = ("Overdue Laptop Loan");
var body3 = ("Hello "+ student +", \n\nThe laptop you have been loaned is due to be returned.");
if(column = 10 && checkboxValue == true) {
console.log("chekbox marked true")
MailApp.sendEmail(recipient, subject, body)
} else if (column = 10 && checkboxValue == false) {
console.log("chekbox marked false")
MailApp.sendEmail(recipient, subject2, body2)
} else {
console.log("No clue")
}
if(column = 18 && checkboxValue2 == true) {
console.log("chekbox marked true")
MailApp.sendEmail(recipient, subject3, body3)
} else if (column = 18 && checkboxValue2 == false) {
console.log("chekbox marked false")
MailApp.sendEmail(recipient, subject3, body2)
} else {
console.log("No clue")
}
}
if(column = 10 .........)
should be ...
if(column == 10 .........)
or even ...
if(column === 10 .........)
Thanks to JtrM we have an answer!
rrays are zero indexed. Rows and columns are not. var targetRange = sheet.getRange(row, 1, 1, 17); Change the 17 to 18 –
JtrM
Also adding extra (=) eg: Before column = 10 After column == 10 etc...
function onCheckboxEdit(e) {
var source = e.source;
var sheet = source.getActiveSheet();
var range = e.range;
var row = range.getRow();
var column = range.getColumn();
console.log("column:: ", column);
var targetRange = sheet.getRange(row, 1, 1, 18); // <- 17 changed to 18
var targetValues = targetRange.getValues();
console.log("targetRange:: ", targetValues);
var student = targetValues[0][2];
var recipient = targetValues[0][1];
var checkboxValue = targetValues[0][9];
var checkboxValue2 = targetValues[0][17];
var subject = ("Laptop Loan Approved");
var body = ("Hello "+ student +", \n\nWe are happy to confirm that your laptop loan application has been approved.");
var subject2 = ("Laptop Loan");
var body2 = ("Hello "+ student +", \n\nPlease disregard last e-mail sent by us, it was mistakenly sent.");
var subject3 = ("Overdue Laptop Loan");
var body3 = ("Hello "+ student +", \n\nThe laptop you have been loaned is due to be returned.");
if(column == 10 && checkboxValue == true) {
console.log("chekbox marked true")
MailApp.sendEmail(recipient, subject, body)
} else if (column == 10 && checkboxValue == false) {
console.log("chekbox marked false")
MailApp.sendEmail(recipient, subject2, body2)
} else if(column == 18 && checkboxValue2 == true) {
console.log("chekbox marked true")
MailApp.sendEmail(recipient, subject3, body3)
} else if (column == 18 && checkboxValue2 == false) {
console.log("chekbox marked false")
MailApp.sendEmail(recipient, subject3, body2)
} else {
console.log("No clue")
}
}
Related
The script works when you change 1st and 2second column when you edit each cell on it's own. But when you past a row in it. It only change the 1st column
function onEdit(e) {
var range = e.range;
var column = range.getColumn();
var value = range.getValue();
var sheet = range.getSheet();
var sheetName = sheet.getName();
//Logger.log(value);
//Logger.log(Date(value).getMonth());
Logger.log(range.getColumn());
switch (sheetName) {
case "sheetnameeee":
if(column == 1||column == 2)
{
range.getCell(1, 1).setValue(dateChange(value)).setNumberFormat("yyyy-MM-dd");
range.getCell(1, 2).setValue(dateChange(value)).setNumberFormat("yyyy-MM-dd");
};
break;
}
Input :
Start date | Eind date
okt 15, 2018 | okt 21, 2018
Copy and past:
Start date | Eind date
2018-10-15 | okt 21, 2018
But it need todo :
Start date | Eind date
2018-10-15 | 2018-10-21
This is my solution. It's not great but it works..
switch (sheetName) {
case "Sheetnameeeee":
var ss = SpreadsheetApp.getActiveSpreadsheet();
var test222 = ss.getSheetName();
var startRow = 1
var rowRange = ss.getRange("A:B");
var rowLength = getRange.getLastRow();
var RangeValues = getRange.getValues();
for (var i=startRow; i < rowLength; i++) {
if(typeof RangeValues[i][0] === 'string')
{
RangeValues[i][0] = dateChange(RangeValues[i][0]);
};
if(typeof RangeValues[i][1] === 'string')
{
RangeValues[i][1] = dateChange(RangeValues[i][1]);
};
}
rowRange.setValues(RangeValues);
rowRange.setNumberFormats([["yyyy-MM-dd","yyyy-MM-dd"]]);
I'm not sure about your logic. It seems that getCell(1,1) and getCell(1,2) would end up with the same value. However, this may work for you with some minor name modifications assuming dateChange returns a Date.
function onEdit(e) {
try {
if( e.range.getSheet().getName() === "Sheet4" ) {
var range = null;
if( e.range.getColumn() === 1 ) {
range = e.range.offset(0,0,1,2);
}
else if( e.range.getColumn() === 2 ) {
range = e.range.offset(0,-1,1,2);
}
var values = range.getValues();
values[0][0] = dateChange(values[0][0]);
values[0][1] = dateChange(values[0][1]);
range.setValues(values);
range.setNumberFormats([["yyyy-MM-dd","yyyy-MM-dd"]]);
}
}
catch(err) {
Logger.log(err);
}
}
This function sends email reminders based on a few conditions. One of the things I need to check for is that the Visit ID (which is in column 11 in the "email log" sheet) exists in a separate sheet ("DATA", stored in the enrollmentData variable). How do I search this array and return the ID to match in the IF statement below?
function sendReminders() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var sheet = ss.getSheetByName("Email Log");
var rows = sheet.getLastRow()-1;
var startRow = 2; // First row of data to process
var numRows = rows; // Number of rows to process
var now = new Date();
// Fetch the range of cells
var dataRange = sheet.getRange(startRow, 1, numRows, 22)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
var today = Utilities.formatDate(new Date(), "GMT-5", "m/d/yyyy")
var reminderSent = "Reminder Sent";
//get email body and subject
var bodyTem = ss.getSheetByName("Email Templates").getRange('b8').getValues();
var subject = ss.getSheetByName("Email Templates").getRange('d13').getValues();
//get enrollments data to search for visit ID
var enrollmentData = ss.getSheetByName("DATA").getRange('H:H').getValues();
for (var i = 0; i < data.length; i++) {
var row = data[i];
//set conditions
var sendReminder = row[18];
var reminderDate = row[19];
var reminderStatus = row[20];
var visitID = row[11]
//need condition to look for visit ID to not include already cancelled. Search enrollmentData for visitID and return as foundID for conditional below
if (sendReminder == "Yes" && reminderStatus != reminderSent && reminderDate >= today && visitID == foundID) {
//assemble email
var studentEmail = row[13];
var firstName = row[12];
var instructor = row[0];
var body1 = bodyTem.replace(/*name*/gi,firstName);
var body2 = body1.replace(/*instructorFull*/gi,instructor);
MailApp.sendEmail(studentEmail, subject, body2);
//need to write in that the reminder email was sent.
sheet.getRange(startRow + i, 20).setValue(reminderSent);
sheet.getRange(startRow + i, 21).setValue(now);
};
};
};
You want to search the array
var enrollmentData = ss.getSheetByName("DATA").getRange('H:H').getValues();
The method getValues always returns a double array: in this case, it's of the form [[1], [2], [3],..] since each row has one element. I usually flatten this:
var enrollmentDataFlat = enrollmentData.map(function(row) {return row[0];});
Now enrollmentDataFlat is like [1, 2, 3, ..] so indexOf will work as usual:
if (enrollmentDataFlat.indexOf(visitID) != -1) {
// it's there
}
else {
// it's not there
}
I have the below script that is running but marks the "Email_Sent" column for every row (all 1000), I would like it to only send an email if Column A, B or C has an entry. (This is copied from the first sheet if someone marks a Yes in a specific column) and only if the Sent_Email column is blank as well.
function sendEmails()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Email"));
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getDataRange();
var data = dataRange.getValues();
for (var i = 1; i < data.length; ++i)
{
var rowData = data[i];
var emailAddress = rowData[1];
var recipient = rowData[0];
var message1 = rowData[3];
var message2 = rowData[4];
var message3 = rowData[5];
var message4 = rowData[6];
var message5 = rowData[7];
var emailSent = rowData[9];
var message = 'Hi ' + recipient + ',\n\n' + message1 + ' ' + message2 +
',\n\n' + message3 + ',\n' + message4 + ',\n' + message5;
var subject = 'Medical Questionairre Check';
if (emailSent != 'EMAIL_SENT' && MailApp.getRemainingDailyQuota()>0 &&
emailAddress && subject && message)
{
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(i+1, 9).setValue('EMAIL_SENT');// Make sure the cell is
updated right away in case the script is interrupted
}
}
}
Here is the link to the spreadsheet.
https://docs.google.com/spreadsheets/d/1SOPiXhU3KWHpHyZEiDUSk8m5qzO5CyALWio6doQvJ4Q/edit?usp=sharing
A Simple Fix
Just had to change emailSent = rowData[25] rather than rowData[9]
function sendEmails()
{
var ss=SpreadsheetApp.getActive();
var sheet=ss.getSheetByName('Sheet2');
var dataRange=sheet.getDataRange();
var data=dataRange.getValues();
for (var i=1;i<data.length;++i)
{
var rowData = data[i];
var emailAddress = rowData[1];
var recipient = rowData[0];
var message1 = rowData[3];
var message2 = rowData[4];
var message3 = rowData[5];
var message4 = rowData[6];
var message5 = rowData[7];
var emailSent = rowData[25];//Change from 9 to 25
var message = 'Hi ' + recipient + ',\n\n' + message1 + ' ' + message2 + ',\n\n' + message3 + ',\n' + message4 + ',\n' + message5;
var subject = 'Medical Questionairre Check';
var rdq=MailApp.getRemainingDailyQuota();
if (!emailSent && rdq>0 && emailAddress && subject && message)
{
MailApp.sendEmail(emailAddress, subject, message)
//Logger.log('emailAddress: %s subject: %s message: %s emailSent: %s',emailAddress,subject,message,emailSent);
sheet.getRange(i+1, 26).setValue('EMAIL_SENT');
}
}
}
The spreadsheet after execution:
I have a sheet in my Google spreadsheet that contains 5 cells, the first 3 contains only words while the last 2 contains time, specifically a timestamp.
cell 1 = data
cell 2 = data
cell 3 = data
cell 4 = time start
cell 5 = time ended
Now, what I want is when cell 1 is supplied with data, a timestamp will automatically appear in cell 4. And when cell 2 and cell 3 is supplied with data, a timestamp will be the new value for cell 5.
My friend give me a code, that should pasted in Script editor:
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
Logger.log(row);
}
};
And
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Read Data",
functionName : "readRows"
}];
spreadsheet.addMenu("Script Center Menu", entries);
};
function timestamp() {
return new Date()
}
and this code is pasted in =IF(B6="","",timestamp(B6))cell 4 and this one =IF(D6="","",timestamp(C6&B6)) is on cell 5. in his example tracker its working. But when i copied it to mine, the output in cell 4 and cell 5 is the Date today and not the time.
can anyone help me? why does it output the date and not the time?
You can refer this tutorial, if this helps.
In the script code, change
var timestamp_format = "MM-dd-yyyy"; // Timestamp Format.
to
var timestamp_format = "MM-dd-yyyy hh:mm:ss"; // Timestamp Format.
This should probably help you.
I just came across this problem and I modified the code provided by Internet Geeks.
Their code works by updating a specified column, the timestamp is inserted in the same row in another specified column.
What I changed is that I separated the date and the time, because the timestamp is a string, not a date format. My way is useful for generating graphs.
It works by specifying the column to track for changes, and then creating an upDate and upTime columns for the date and time respectively.
function onEdit(event) {
var timezone = "GMT+1";
var date_format = "MM/dd/yyyy";
var time_format = "hh:mm";
var updateColName = "Резултат";
var DateColName = "upDate";
var TimeColName = "upTime";
var sheet = event.source.getActiveSheet(); // All sheets
// var sheet = event.source.getSheetByName('Test'); //Name of the sheet where you want to run this script.
var actRng = event.source.getActiveRange();
var editColumn = actRng.getColumn();
var index = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(DateColName);
var timeCol = headers[0].indexOf(TimeColName);
var updateCol = headers[0].indexOf(updateColName);
updateCol = updateCol + 1;
if (dateCol > -1 && timeCol > -1 && index > 1 && editColumn == updateCol) {
// only timestamp if 'Last Updated' header exists, but not in the header row itself!
var cellDate = sheet.getRange(index, dateCol + 1);
var cellTime = sheet.getRange(index, timeCol + 1);
var date = Utilities.formatDate(new Date(), timezone, date_format);
var time = Utilities.formatDate(new Date(), timezone, time_format);
cellDate.setValue(date);
cellTime.setValue(time);
}
}
Hope this helps people.
Updated and simpler code
function onEdit(e) {
var sh = e.source.getActiveSheet();
var sheets = ['Sheet1']; // Which sheets to run the code.
// Columns with the data to be tracked. 1 = A, 2 = B...
var ind = [1, 2, 3].indexOf(e.range.columnStart);
// Which columns to have the timestamp, related to the data cells.
// Data in 1 (A) will have the timestamp in 4 (D)
var stampCols = [4, 5, 6]
if(sheets.indexOf(sh.getName()) == -1 || ind == -1) return;
// Insert/Update the timestamp.
var timestampCell = sh.getRange(e.range.rowStart, stampCols[ind]);
timestampCell.setValue(typeof e.value == 'object' ? null : new Date());
}
I made a slightly different version, based also on the code from Internet Geeks
In order to support multiple named sheets, and because Google Sheets Script doesn't currently support Array.prototype.includes(), I included the polyfill mentioned here
Also, in my version, the timestamp marks the date of creation of that row's cell, not the date of the last update as in the other scripts provided here.
function onEdit(event) {
var sheetNames = [
'Pounds £',
'Euros €'
]
var sheet = event.source.getActiveSheet();
if (sheetNames.includes(sheet.getName())){
var timezone = "GMT";
var dateFormat = "MM/dd/yyyy";
var updateColName = "Paid for ...";
var dateColName = "Date";
var actRng = sheet.getActiveRange();
var editColumn = actRng.getColumn();
var rowIndex = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(dateColName) + 1;
var updateCol = headers[0].indexOf(updateColName) + 1;
var dateCell = sheet.getRange(rowIndex, dateCol);
if (dateCol > 0 && rowIndex > 1 && editColumn == updateCol && dateCell.isBlank())
{
dateCell.setValue(Utilities.formatDate(new Date(), timezone, dateFormat));
}
}
}
// https://stackoverflow.com/a/51774307/349169
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
if (sameValueZero(o[k], searchElement)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
});
}
I have three scripts that are in a google docs spreadsheet. In this spreadsheet, in column H (or column 8), if I type an "x", the script changes it into that days date. After a few days, every date in column H has changed from a date to just a number. The numbers look like this: 40492, 40494, 40511. I am not sure what is causing this. Maybe it's something that is wrong in my script. I've pasted them below. Any ideas?
function onEdit(e) {
var colorA = "yellow";
var colorB = "#dddddd";
var colorC = "#dddddd";
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Purchase Orders");
var range = e.source.getActiveRange();
var sheetName = SpreadsheetApp.getActiveSheet().getName();
if (sheetName == "Purchase Orders") {
// 3 is column C
if (range.getColumn() == 3 && range.getValue() != "") {
sheet.insertRowAfter(range.getRow());
var r = range.getRow() + 1;
sheet.getRange("A" + r + ":H" + r).setBackgroundColor(colorC);
}
}
var col = e.source.getActiveRange().getColumn();
if(col == 3 || col == 8) {
var rows = sheet.getMaxRows();
//column C
var rangeC = sheet.getRange("C1:C"+rows);
var valuesC = rangeC.getValues();
//column H range
var rangeH = sheet.getRange("H1:H"+rows);
var colorH = rangeH.getBackgroundColors();
var valuesH = rangeH.getValues();
//iterate over each row in column C and H
//then change color
for (var row = 0; row < valuesC.length; row++) {
//check for columnC and column H
var hRow = colorH[row];
if (valuesC[row][0] != "" && valuesH[row][0] == "") {
hRow[0] = colorA;
} else if (valuesH[row][0] != "") {
hRow[0] = colorB;
}
}
sheet.getRange("H1:H" + rows).setBackgroundColors(colorH);
}
}
And this one
function onEdit(e) {
var ss = e.source.getActiveSheet();
var r = e.source.getActiveRange();
// 1 is A, 2 is B, ... 8 is H
if (r.getColumn() == 8 && r.getValue() == "x") {
r.setValue(Utilities.formatDate(new Date(), "MST", "yyyy-MM-dd"));
}
}
And this last one
ss = SpreadsheetApp.getActiveSpreadsheet();
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "New PO", functionName: "NewPO"}];
ss.addMenu("New PO", menuEntries);
}
function NewPO() {
SpreadsheetApp.getActiveSheet().insertRowsBefore(1,6);
// Adjust this range accordingly, these are the cells that will be
// copied. Format is getRange(startRow, startCol, numRows, numCols)
ss.getSheetByName("PO Form").getRange(1, 1, 6, 8)
.copyTo(SpreadsheetApp.getActiveSheet().getRange(1, 1, 6, 8));
}
In OnEdit, you probably want to set the format for that cell as well. setNumberFormat(numberFormat) appears to be the function you are after.
http://code.google.com/googleapps/appsscript/class_range.html#setNumberFormat