I've wrote the following code- on the homepage there is a cell (merged at the moment) where we'd put in the date. The end goal is to have the script send emails out when the current date meets 14 days later from the date in the cell.
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Email addresses")
var hpsheet = ss.getSheetByName("Main Page-Control Plan")
var startRow = 2; // Start at second row because the first row contains the data labels
var numRows = 2; // Put in here the number of rows you want to process
// Fetch the range of cells A2:B4
// Column A = Email Address, Column B = First Name
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[0]; // First column of selected data
var personName = row[1]
var currentDate = new Date(hpsheet.getRange("F6:F9").getValue());
var fourteenDaysLater = new Date(currentDate.getTime() + 14 * 24 * 60 * 60 * 1000);
console.log(fourteenDaysLater)
var subject = 'Recovery schedule is due';
var message ='Hi, ' + personName + '\n\n' + 'Recovery schedule is due. Please notify other members in this email chain if this has been done. '
if (fourteenDaysLater < new Date()) {
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
I've put a daily trigger on it to run the script between 6-7am and put the date in the cell as 09/02/23, I've changed the number of days to 1 in the fourteenDaysLater variable so I could see if it worked today but I didn't get emails for some reason.
Is there something wrong with the code?
I think the problem is when you parse the string to Date(), where the parsed date shows Sat Sep 16 00:00:00 GMT+12:00 2023 in my timezone.
To make sure it parses the correct format and timezone, try the code below:
var timezone = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
var dateString = "09/02/23"; //replace with your cell value
var format = "dd/MM/yy"; //date format to parse
var currentDate = Utilities.parseDate(dateString,timezone,format);
var fourteenDaysLater = new Date(currentDate.getTime() + 14 * 24 * 60 * 60 * 1000);
Related
This code works on Google Sheets as to:
Help users update today's date in a column, and effectively add a signature.
In a specific column, users type in any random jibberish (easy to do), and the sheet will then change this to today's date, and add their logged-in email address as a signature.
Ps the sheet does know who's currently logged in - I've set the sheet so users do have to be logged in and given access to the sheet
This is my code (https://imgur.com/8KSBbsB):
function onEdit(event)
{
var timezone = "UTC";
var timestamp_format = "d/MM/yy"; // Timestamp Format.
var updateColName = "[code]";
var timeStampColName = "[code]";
var nameColname = "[codename]";
var userName = Session.getEffectiveUser().getEmail();
var sheet = event.source.getSheetByName('Rank Sheet'); //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(timeStampColName);
var nameCol = headers [0].indexOf(nameColname); nameColname = nameColname+1;
var updateCol = headers[0].indexOf(updateColName); updateCol = updateCol+1;
if (dateCol > -1 && index > 1 && editColumn == updateCol) { // only timestamp if 'Last Updated' header exists, but not in the header row itself!
var cell = sheet.getRange(index, dateCol + 1);
var date = Utilities.formatDate(new Date(), timezone, timestamp_format);
cell.setValue(date + " " +userName)
var cell = sheet.getRange(index, nameColname + 1);
var nameCol = cell.setValue = (userName)
}
}
To do this you need to 'title' the columns [code], and [codename] (changeable).
Thank you very much to Cooper for advice given!!
Try this:
cell.setValue(date + '\n' + userName)
The "stock/average" sheet calculates the average purchase price. F is Products and H is the purchase price.
I need to calculate and save profit/loss of each sale in the E column after every sale. Like: the purchase price of the first product is 14200 (First image # stock/average, column H, second product) so 2800 should be put here. I can do it using vlookup or query but the purchase price gets changed with each purchase so I need to do it using the script and preserve the result.
Link to Spreadsheet:
Please help.
The code in this answer is presented in two parts:
Section One - Add this to your IF statement.
var thissheet = range.getSheet();
var edditedCol = range.columnStart;
var edittedRow = range.rowStart;
var productdetails = sheet.getRange(edittedRow,1,1,5).getValues();
var product = productdetails[0][0];
var qty = productdetails[0][1];
var sellprice = productdetails[0][2];
// Logger.log("DEBUG: product = "+product+", qty:"+qty+", sellprice="+sellprice);
var avg = getcost(product);
var net = +sellprice-(+avg*qty);
var netprofit = Math.round(net * 100) / 100
sheet.getRange(edittedRow,5).setValue(netprofit);
// Logger.log("DEBUG: net profit = "+netprofit);
This identifies the edited row and column, and get the values for the appropriate
- Product
- Qty
- Total sales value
- calls a separate function to return the average cost for the relevant product,
- calculates the net profit,
- updates the value in the "Profit" Column on the sheet.
Section Two - a sub-routine gets the average cost from the "stock/average" Sheet.
function getcost(product){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "stock/average";
var sheet = ss.getSheetByName(sheetname);
var Fvals = sheet.getRange("F5:F").getValues();
var Flast = Fvals.filter(String).length;
var sarange = sheet.getRange(5,6,Flast,7);
// Logger.log("DEBUG: sarange = "+sarange.getA1Notation());
var data = sarange.getValues();
// convert the 2D array to a 1D array for the "Product" column.
var colA = new Array()
for(i=0;i<data.length;++i){
colA.push(data[i][0]) ;// taking index 0 means I'll get column A of each row and put it in the new array
}
// find the posityion of the editted product on the "stack/average" sheet
var pos = colA.indexOf(product);
// get the average cost for this line.
var avgcost = data[pos][2];
// Logger.log("DEBUG: the average cost = "+avgcost);
return avgcost;
}
I have a code I use to send email reminders based on dates in a spreadsheet. Most of the sheets I use it for are simple and under 100 lines of data. In the past I have used individual variables for each cell but I am looking for a way to find all instances of a value in a column (D), in this case, it is a city code ("SEA"), and return the row number, which I then want to take and use in a var to make it act like a vlookup.
ex: var empname = ss.getRange("A"+ rownumber).getValue();
Which should return column A of whichever row has "SEA" in it.
The challenge then is to be able to go down the column and do the same for each row that has "SEA" in it.
This is a one-line version of what I have done in the past.
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var ss = spreadsheet.getSheetByName("Active");
var empnum = ss.getRange("A8").getValue();
var empfirstname = ss.getRange("C8").getValue();
var emplastname = ss.getRange("B8").getValue();
var emplocation = ss.getRange("D8").getValue();
var test = ss.getRange("H8").getValue();
if (test === -1 && emplocation === "SEA"){
var message = empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.' + '\n'
} else if (test === 0 && emplocation === "SEA"){
var message = empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.' + '\n'
} else if (test === 1) {
var message = "";
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
The H column will present a -1, 0 or 1 depending on the date in the G column to determine if someone is due to take a test or not. I then want to send an email with the Employee Number, First Name and Last Name for each employee at the SEA branch. This has worked for me in the past but I have 230+ lines of employees and don't want to create that many variables. My solution is to get the row number of each row with a "SEA" location and use that as a variable in a getRange call. Not sure exactly how that would work, or if it even would. If there is a better solution, I am amenable to that.
Requirement:
Send an email when a row contains "SEA" and column H matches -1 or 0.
Solution:
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Active');
//find all occurrences of "SEA" and push range to array
var search = "SEA";
var ranges = ss.createTextFinder(search).findAll();
var message = ''; //pre-define email body
//loop through each range
for (i = 0; i < ranges.length; i++) {
var row = ranges[i].getRow();
var lastCol = ss.getLastColumn();
var values = ss.getRange(row, 1, 1, lastCol).getValues(); //get all values for the row
var empnum = values[0][0]; //column A
var empfirstname = values[0][2]; //column C
var emplastname = values[0][1]; //column B
var emplocation = values[0][3]; //column D
var test = values[0][7]; //column H
if (test === -1) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.\n');
} else if (test === 0) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.\n');
}
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
if (message) { //make sure message is not blank
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
}
Explanation:
Okay, so I've made quite a few changes to your original script, if there's anything I've missed please let me know, I'm happy to explain it.
First of all, I've changed the script so that we're using textFinder, this is so that we can search through your sheet to find your desired pattern "SEA". This then pushes all of the ranges to an array that we are looping through in the for loop.
When looping through the ranges, we can grab all of the data in the row (e.g. values[0][0] is the data in column A). This is then pushed to var message to build the email body in the same format you specified in your question.
I've tried to keep the variable names alike to the ones in your original script to make it easier to understand which parts of the script are essentially the same as your original. Also there are comments throughout the script that should help you understand what it's doing at each point.
References:
textFinder Documentation
Update:
To scan only a certain column, try the code below instead. I've defined var range separately so that we can use textFinder on that range rather than the whole sheet.
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Active');
//define range to search
var lastRow = ss.getLastRow();
var range = ss.getRange(1, 4, lastRow); //range for column D
//find all occurrences of "SEA" in column D and push range to array
var search = "SEA"
var ranges = range.createTextFinder(search).findAll();
var message = '';
//loop through each range
for (i = 0; i < ranges.length; i++) {
var row = ranges[i].getRow();
var lastCol = ss.getLastColumn();
var values = ss.getRange(row, 1, 1, lastCol).getValues(); //get all values for the row
var empnum = values[0][0]; //column A
var empfirstname = values[0][2]; //column C
var emplastname = values[0][1]; //column B
var emplocation = values[0][3]; //column D
var test = values[0][7]; //column H
if (test === -1) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.\n');
} else if (test === 0) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.\n');
}
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
if (message) {
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
}
I have a google script that changes the event dates to the day of the year, so 8/26/15 is 237, and also changes today's date to it's day of the year. It then compares the event date day of the year against today's date day of the year. If the event date integer is smaller than today's date integer than it changes the event description in the google calendar to Event Closed. The problem is it only does this for the first event in the spreadsheet. It does not iterate through all of the events and change all of the event descriptions that are before today's date.
function compareDates(){
var sheet = SpreadsheetApp.getActive().getSheetByName("Events");
var data = sheet.getDataRange().getValues();
var headerRow = 1;
var i = 1;
for (i = 1; i < data.length; i += 1) {
var row = data[i];
var room = row[5];
var description = row[6];
var agegroup = row[7];
var registration = row[8];
var calname = row[14];
var registrants = row[17];
var id = sheet.getRange(headerRow + 1, 16).getValues();
var event = CalendarApp.getEventSeriesById(id);
var description1 = (description + '\n' + '\n<b>Room:</b> ' + room + '\n' + '\n<b>Event Type:</b> ' + calname + '\n' + '\n<b>Age Group:</b> ' + agegroup);
var today = parseInt(Utilities.formatDate(new Date(sheet.getRange("U2").getValues()), "EST", "D"));
var eventDate = parseInt(Utilities.formatDate(new Date(row[0]), "EST", "D"));
var testED = sheet.getRange(headerRow + i, 22).setValue(eventDate);//To see if var Today works.
var testT = sheet.getRange(headerRow + i, 23).setValue(today);//To see if var eventDate works.
if (today > eventDate && registration === 'Y') {
var descriptionHTMLEC = '\n <div id="registration_button" ><a style="text-align:right;color:white!important;text-decoration:bold;background-color:rgb(121,183,138);background-image:-webkit-linear-gradient(top,rgb(221,75,57),rgb(121,183,138nnn ));color:#ffffff;border:1px solid rgba(0,0,0,0);border-radius:2px;display:inline-block;font-size:12px;font-weight:bold;height:27px;line-height:27px;padding:0px 20px 0px 20px;text-align:center;text-transform:uppercase;white-space:nowrap;text-decoration:none;color:white" target="_blank">Event Closed</a>';
var descriptionRegistrationEC = (description1 + '\n' + '\n' + descriptionHTMLEC);
event.setDescription(descriptionRegistrationEC);
}
}
}
It seems you are modifying the same event on each iteration. The statement:
var id = sheet.getRange(headerRow + 1, 16).getValues();
Will assign the same value to id each time. Shouldn't it be:
var id = sheet.getRange(headerRow + i, 16).getValues();
For more readable code you can just the getTime() function on the date object.
var today = new Date(sheet.getRange("C2").getValues()).getTime();
var eventDate = new Date(row[0]).getTime();
I'd like to make a script that compares the date within a row to today's date and delete that row if today's date is paste the date row.
This is my current script:
function deleteRow1() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 1; // First row of data to process
var numRows = sheet.getLastRow()-1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 2, numRows);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i=0;i<data.length;i++) {
var row = data[i];
var date = new Date();
var sheetDate = new Date(row);
var Sdate = Utilities.formatDate(date,'GMT+0200','yyyy:MM:dd')
var SsheetDate = Utilities.formatDate(sheetDate,'GMT+0200', 'yyyy:MM:dd')
if (Sdate > SsheetDate){
sheet.deleteRow(i+2) //don't delete header
}
}
}
The ultimate goal is to compare dates in column C with today's date and to delete that particular row if today's date is greater.
Right now when I run the script, it deletes some rows, but not specifically the rows that are earlier than today's date.
The beginnings of this script was based off another StackOverflow post
when you start iteration from top to bottom deleting row in iteration alter the offset (actual row number) of next rows
above problem can be solved using start the iteration from bottom to top.
for (i=data.length-1;i>=0;i--)
here is the working code
function deleteRow1()
{
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow()-1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 2, numRows);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i=data.length-1;i>=0;i--)
{
var row = data[i];
var date = new Date();
var sheetDate = new Date(row);
var Sdate = Utilities.formatDate(date,'GMT+0200','yyyy:MM:dd')
var SsheetDate = Utilities.formatDate(sheetDate,'GMT+0200', 'yyyy:MM:dd')
if (Sdate.valueOf() > SsheetDate.valueOf())
{
sheet.deleteRow(i+2) //don't delete header
}
}
}