Cannot find function getMonth in object 33463 - google-apps-script

I have created a script to send emails to a specific people with Birthday Reminder. This use to work till day before yesterday. I don't know why am I getting this error that Cannot find function getMonth, Can anyone tell where is the mistake
function emailAlert() {
// Short circuit if email notice is set to "No". This causes an error and the script stops.
if (turnOnEmailNotice.toLowerCase() == "no")
{
Logger.log("The Email Notification is NOT turned ON. System will Exit.");
exit
}
//Get the total number of filled row in the sheet.
var currentRowAT = 2;
var currentCellValueAT = "start";
while (currentCellValueAT != ""){
if (currentCellValueAT = birthdaysSheet.getRange("G" + currentRowAT).getValue() != ""){
currentRowAT = currentRowAT +1;
}
}
var birthdaysSheetLastRow = currentRowAT - 1;
// Get today's Date (with Month, Date and Year)
var today = new Date();
var todayMth = today.getMonth()+1;
var todayDate = today.getDate();
var todayYear = today.getFullYear();
// Check sheet cell for match to alertDate, k is the current row number being checked. Starting at 2 as the row #1 is for the headers
for (k=2; k < birthdaysSheetLastRow + 1; k++)
{
var targetBday = new Date();
targetBday = birthdaysSheet.getRange("P" + k).getValue();
// If Birthday is not speicified, continue with the next row
if (targetBday == ""){continue};
var unadjTargetBday = new Date();
var unadjTargetBdayMth = targetBday.getMonth()+1;
var unadjTargetBdayDate = targetBday.getDate();
var unadjTargetBdayYear = targetBday.getFullYear();
var unadjTargetBday = targetBday;
targetBday.setDate(targetBday.getDate()-daysInAdvance); // Calculating how many days in advance you want to trigger the notification. This is set in Settings Tab.
var targetBdayMth = targetBday.getMonth()+1;
var targetBdayDate = targetBday.getDate();
if (targetBdayMth + " " + targetBdayDate == todayMth + " " + todayDate)
{
var targetBirthDateYearsOld = (today.getYear() - unadjTargetBday.getYear())-1900;
prepareAndSendEmail(k, targetBirthDateYearsOld);
}
}
}

getValue will return the type string/date/number depending on the cell type in the spreadsheet (see menu Format -> Number). To be sure, just always convert to Date Type. This is the right way to convert it:
var targetBday = new Date(birthdaysSheet.getRange("P" + k).getValue());

Short answer
Check that the cell value is a valid date for Google Sheet.
Explanation
From https://developers.google.com/apps-script/reference/spreadsheet/range#getvalue (emphasis mine)
getValue()
Returns the value of the top-left cell in the range. The value may be
of type Number, Boolean, Date, or String depending on the value of the
cell. Empty cells will return an empty string.
To avoid this kind of problems, you may include some sort of data validation. You could use build-in features like conditional formatting, data validation, or something like a on edit script together with try / catch or a redundant validation in your emailAlert script.

Related

Google Apps Script: how to copy paste ranges based on formulas?

I have a model in Google Sheets that is set up with one column per day. It contains both actuals and forecasts, and every day I need to roll forward formulas to replace forecasts with actuals. I can't roll forward the whole column, only a segment of it (there are reference numbers above and below that shouldn't be changed).
I have tried to write a script to do this for me every day, but I don't know how to make getRange reference a dynamic range. This is my attempt:
function rollColumn() {
var ss2 = SpreadsheetApp.openById('<ID redacted>');
ss2.getRange("=index(Model!$7:$7,,match(today()-2,Model!$4:$4,0)):index(Model!$168:$168,,match(today()-2,Model!$4:$4,0))").copyTo(ss2.getRange("=index(Model!$7:$7,,match(today()-1,Model!$4:$4,0)):index(Model!$168:$168,,match(today()-1,Model!$4:$4,0))"))
};
The INDEX formulas work insofar as they reference the relevant ranges (I have tested them in the spreadsheet). But clearly getRange doesn't accept formulas as an input. It also seems that Google Sheets doesn't allow for a named range to be created with formulas (which is how I would solve this in Excel).
Can someone help me recreate this functionality with GAS?
This is the closest existing question I've found on Stack Overflow, but I haven't been able to make it work:
Google Apps Script performing Index & Match function between two separate Google Sheets
Thank you!
You should add {contentsOnly:false} parameter to your code. something like this:
TemplateSheet.getRange("S2:T2").copyTo(DestSheet.getRange("S2:T"+LRow2+""), {contentsOnly:false});
Getting a date from column's title, then pasting formulas to the row to the right:
// note: we assume that sheet is disposed as in the following document: https://docs.google.com/spreadsheets/d/1BU2rhAZGOLYgzgSAdEz4fJkxEcPRpwl_TZ1SR5F0y08/edit?ts=5a32fcc5#gid=0
function find_3formulas() {
var sheet = SpreadsheetApp.getActiveSheet(),
leftTitle, // this variable will stay unused because we do not need a vertical index
topTitle = todayMinus_xDays(2),
topTitlesRange = sheet.getRange("G3:T3"),
leftTitlesRange = sheet.getRange("A4:A8"); // this range will stay unused.
var coor = findCoordinates(leftTitlesRange, leftTitle, topTitlesRange, topTitle);
if (coor.row == null || coor.column == null) {
sheet.getRange("M12:M14").setFormula('="NULL: please check logs"');
return;
}
var rowAxis = 4 + coor.row;
var colAxis = 8 + coor.column;
var fromRange = sheet.getRange(rowAxis, colAxis, 3, 1);
var toRange = sheet.getRange(rowAxis, colAxis + 1, 3, 1);
Logger.log(fromRange.getA1Notation())
Logger.log(toRange.getA1Notation());
var threeFormulas = fromRange.getFormulas();
toRange.setFormulas(threeFormulas)
}
// unused in current script!
function findCoordinates(leftTitlesRange, leftTitle, topTitlesRange, topTitle) {
var formattedDate,
row = 0,
column = 0;
if (leftTitle) {
row = findRow(leftTitlesRange, leftTitle);
}
if (topTitle) {
column = findColumn(topTitlesRange, topTitle);
}
var array = {row:row, column:column}
return array;
}
// unused in current script!
function findRow(range, valueToSearch) {
var colRows = range.getValues();
for (i = 0; i < colRows.length; i++) {
if (valueToSearch == colRows[i][0]) {return i;}
}
// however, if found nothing:
Logger.log("the value " + valueToSearch + " could not be found in row titles");
return null;
}
// assumes that column titles are dates, therefore of type object.
function findColumn(range, valueToSearch) {
var colTitles = range.getValues();
for (i = 0; i < colTitles[0].length; i++) {
if (typeof colTitles[0][i] == "object") {
formattedDate = Utilities.formatDate(colTitles[0][i], "GMT", "yyyy-MM-dd")
};
if (valueToSearch === formattedDate) {return i;}
}
// however, if found nothing:
Logger.log("today's date, " + valueToSearch + ", could not be found in column titles");
return null;
}
// substracts 2 days from today, then returns the result in string format.
function todayMinus_xDays(x) {
var d = new Date();
d = new Date(d - x * 24 * 60 * 60 * 1000);
d = Utilities.formatDate(d, "GMT", "yyyy-MM-dd");
return d;
}

Using Google Sheets, how do I run a script every hour and populate each cell in a column?

I'm using google sheets and have never run a "cron job" here.
To give credit where it's due, I found this awesome code to help me access the speed insights api here: https://statsravingmad.com/measure/page-speed-insights/
function speed(url,device,filter_third_party_resources,http_secure) {
url = url || 'www.statsravingmad.com';
strategy = 'desktop' || device;
filter_third_party_resources = 'true' || filter_third_party_resources;
http_secure = 'false' || http_secure ;
switch (http_secure) {
case 'false':
http_protocol = 'http://';
break;
case 'true':
http_protocol = 'https://';
break;
}
var key = 'api-key';
var api = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=' + http_protocol + url
+ '&filter_third_party_resources=' + filter_third_party_resources +
'&strategy=' + strategy + '&key=' + key;
var response = UrlFetchApp.fetch(api, {muteHttpExceptions: true });
var result = JSON.parse(response.getContentText());
score = result.ruleGroups.SPEED.score;
return(score);
}
So I have this code in a function that is triggered every hour for my particular test sites in my google sheet.
But, the data is only filling one cell per site, the cell that the formula is assigned to.
When using google sheets, how can I modify this in order to have it fill a new cell in a column every hour? Do I modify this code, do I have to set up another function, or is there an option to fill the cells down a column?
This function can be modified to write, say, to column C of Sheet1. Here is how it would end, instead of return(score) (there is no need to return anything if the value is written to the spreadsheet directly; the function would not be invoked from the spreadsheet, but from a trigger).
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var values = sheet.getRange("C:C").getValues(); // using column C
var lastRow = 1;
for (var i = 0; i < values.length; i++) {
if (values[i][0] != "") {
lastRow = i + 1;
}
}
sheet.getRange(lastRow + 1, 3).setValue(score); // column = 3 because C
Here the loop finds the last row in column C that has data, and the values of score is placed under it.

Form submission handler creates duplicate Google Calendar events

I modified code from this question to suit my needs. I use Google Forms that fill into Google Sheets, and then the script inserts an event into Google Calendar. I have installed a "form submit" trigger to execute the script. The issue I have is that my code starts with the data in the first row of the sheet, adding the same events every time a new form response is added.
Here is my version:
//push new events to calendar
function pushToCalendar() {
//spreadsheet variables
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2,1,lastRow,16);
var values = range.getValues();
//calendar variables
var defaultCalendar = CalendarApp.getDefaultCalendar()
var numValues = 0;
for (var i = 0; i < values.length; i++) {
//check to see if Start DateTime and End DateTime are filled out
if ((values[i][3]) && (values[i][4])) {
//check if it's been entered before
if (values[i][6] != 'y') {
//create event https://developers.google.com/apps-script/class_calendarapp#createEvent
var newEventTitle = values[i][1] + ' - ' + values[i][2];
var startDay = Utilities.formatDate(new Date(values[i][3]), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var endDay = Utilities.formatDate(new Date(values[i][4]), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var newEvent = defaultCalendar.createEvent(newEventTitle, new Date(startDay), new Date(endDay), {location: values[i][2]});
//get ID
var newEventId = newEvent.getId();
//mark as entered, enter ID
sheet.getRange(i + 2, 6).setValue('y');
sheet.getRange(i + 2, 7).setValue(newEventId);
}
}
numValues++;
}
}
Found the solution by myself: on row 20 i changed the code
if (values[i][6] != 'y') {
to
if (!values[i][6]) {
No need to write that 'y' sign. I also changed
sheet.getRange(i+2,6).setValue('y');
to
sheet.getRange(i+2,6).setValue('');
And no duplicates anymore. The code works perfect!
The issue with the original code is that you are reading the wrong column - JavaScript arrays are 0-base, while the spreadsheet columns are 1-base. Column 6 (from getRange(i + 2, 6) is in the array index 5, i.e. values[i][5], not values[i][6]. So you were comparing the newEventId with the string 'y', which was never going to be the same.
As your solution indicates, removing the condition that values[i][6] (aka column 7, where your script writes the created event's ID) is not equal to 'y' (a condition that was always true), and instead testing for any value will appropriately guard the event creation code. Given the presence of the event ID column, the column in which 'y' was written is entirely unnecessary.
If you remove that column from your form response sheet, the code guard would then be:
//check to see if Start DateTime and End DateTime are filled out
if ((values[i][3]) && (values[i][4])) {
//check if it's been entered before, by looking in Column F:
var existingEventId = values[i][5]; // Will be "" (falsy) if not yet added
if (!existingEventId) {
...
// Log the event's ID so we don't make a duplicate:
sheet.getRange(i + 2, 6).setValue(newEventId);
...

Google Apps Script- Increment column each day- Google Spreadsheet

I need the apps script to scan each column each day on incremental basis. If it scans one column today say A column, next day the next column B should be scanned.
To trigger the function, daily timer is used for a specific time in a day.
The below code scans all the columns at a time.
The problem here is, the apps script is triggered daily one time. So it starts from beginning. How to store which column it had scanned yesterday and increment it for today.
function AutoSend() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var list= new Array();
for(var j= 2; j<=29;j++){ // for Column
for(var i=5;i<=23;i++){ // for row
var value = sheet.getRange(i, j).getValue();
if(value !== "OFF"){ if(value !== "L") { if(value !== "COMP OFF") {
var EmpName= sheet.getRange(i,1).getValue();
list.push(EmpName);
}}}
}
}
Logger.log(list);
var subject= "sub";
var message = list;
MailApp.sendEmail('example#gmail.com',subject, message);
}
You'll probably need to store data in the document properties.
PropertiesService.getDocumentProperties().setProperty(key, value);
You may want to use the date as a string for the key, and the column number as the value.
var theColumn = 9; //To Do - get the correct column number
var todaysDate = new Date().toString();
Logger.log('todaysDate: ' + todaysDate);
todaysDate = todaysDate.slice(0,15); //Remove time off the end
Logger.log('todaysDate: ' + todaysDate);
PropertiesService.getDocumentProperties().setProperty(todaysDate, theColumn);
var theColumnForTheDate = PropertiesService.getDocumentProperties()
.getProperty(todaysDate);
Logger.log('theColumnForTheDate: ' + theColumnForTheDate);

"Row Last Modified" Script Produces Wrong Month

This code has been working well for me, but today (when I modified a cell) I noticed that the script is producing a date that is one month behind the actual date (i.e., today is 6/5/2013, but the script produced 5/5/2013). Can anyone see what might be causing this problem?
// * based on the script "insert last modified date" by blisterpeanuts#gmail.com *
// Update cell with the the last modified time of any (single) cell in that row, excluding row 1 and column 1
function onEdit() {
var d = new Date();
// format date nicely
var month_str = d.getMonth();
var day_str = d.getUTCDate();
var year_str = d.getYear().toString().substring(2);
// create the formatted time and date strings
var date_str = month_str + '/' + day_str + '/' + year_str;
// create the message (change this to whatever wording you prefer)
// note also that rather than all the above, you could just use d.toString()
// I didn't because I didn't want it printing the timezone.
var s = date_str;
var active_range = SpreadsheetApp.getActiveRange();
if (active_range.getHeight() == 1 && active_range.getWidth() == 1 && active_range.getRow != 1 && active_range.getColumn() != 1) {
var update_row = active_range.getRow().toString();
var update_cell = "AF" + update_row;
SpreadsheetApp.getActiveSheet().getRange(update_cell).setValue(s);
}
}
I don't think this has ever worked correctly for you. The documentation for Date.getMonth() says:
The value returned by getMonth is an integer between 0 and 11. 0
corresponds to January, 1 to February, and so on.
You need to increment the month by one.
var month_str = d.getMonth() + 1;
(Also the variable name month_str is misleading, it isn't a string, getMonth() returns an integer)