How can I change this simple cell formula to a script that will be apply to the sheet :
=if(D3="Devis";B3+8;(if(D3<>"Devis";"")))
Where :
C column contains the formula
"Devis" is a name presents in the D column
B column contains Dates
Thanks a lot
First you need something that will trigger the script. So you'll need something like a simple onEdit() function, or a change trigger.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Sheet1');
var valueOfCellEdited = e.value;
Logger.log('valueOfCellEdited: ' + valueOfCellEdited);
var columnOfCellEdited = e.range.getColumn();
Logger.log('columnOfCellEdited: ' + columnOfCellEdited);
var rowOfCellEdited = e.range.getRow();
Logger.log('rowOfCellEdited: ' + rowOfCellEdited);
if (columnOfCellEdited === 3) {
var valueToGet = sh.getRange(rowOfCellEdited, 4);
var dateToGet = "";
if (valueOfCellEdited === "Devis") { //Date to write is already set to an empty string, so no "Else" needed
var dateInColB = sh.getRange(rowOfCellEdited, 2).getValue();
Logger.log('dateInColB: ' + dateInColB);
Logger.log('typeof dateInColB: ' + typeof dateInColB);
dateToGet = new Date(dateInColB);
var dateInMilliseconds = dateToGet.valueOf();
Logger.log('dateInMilliseconds: ' + dateInMilliseconds);
//60 seconds in a minute = 60 * 1000 = 60000 milliseconds in a minute.
//60 minutes in a hour = 60,000 * 60 = 3,600,000
//24 hours in day 3,600,000 * 24 = 86,400,000
var datePlusEight = dateInMilliseconds + (8 * 86400000);
Logger.log('datePlusEight: ' + datePlusEight);
var newDateIs = new Date(datePlusEight);
Logger.log('newDateIs: ' + newDateIs);
Logger.log('newDateIs: ' + newDateIs.toString());
sh.getRange(rowOfCellEdited, 4).setValue(newDateIs);
};
};
};
Comment out the Logger.log() statements for use. To test, edit a cell in column C, with a date right next to it in the same row, but in column B.
In the spreadsheet, under the Tools menu, choose Script Editor, copy the code and paste it into the script editor.
Related
I'm running a script which automatically generates values for the A and B columns, and then I want to copy the values generated through the E-I columns (columns C and D run formulas to print out values of E-I), and copy them into J-N (the initial values reload every time I open the sheet, so I'd like to just copy their value instead of using =E2 for example). This is what I'm working with so far:
function daily() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Instagram");
var date = Utilities.formatDate(new Date(), "GMT", "YYYY-MM-DD");
var aValues = sh.getRange("A1:A").getValues();
var aLast = aValues.filter(String).length;
// Assuming A and B have same last row, no need for B
// If different, need to calculate separately
var bValues = sh.getRange("B1:B").getValues();
var bLast = bValues.filter(String).length;
// If A and B are the same, use setValues
sh.getRange(aLast + 1, 1, 1, 2).setValues([[date,'microdosehq']]);
var sheet = SpreadsheetApp.getActiveSheet();
var balance = sheet.getRange("E1:E").getValues();
var nextRow = getFirstEmptyRow('J');
// Record current balance and timestamp at end of columns B & C
sheet.getRange(nextRow, 10, 1, 1).setValues([[balance]]);
}
// From https://stackoverflow.com/a/9102463/1677912
function getFirstEmptyRow(columnLetter) {
columnLetter = columnLetter || 'A';
var rangeA1 = columnLetter + ':' + columnLetter;
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange(rangeA1);
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
return (ct+1); // +1 for compatibility with spreadsheet functions
}
But it's always returning the value of E1 into the next available J cell, and right now I'm not focused on all the columns, just column E. I've added an image here to show what I'd like to accomplish at the end of the day.
Any extra support would be much appreciated!
I have simplified your code due to having problems during testing.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Instagram");
function daily() {
var date = Utilities.formatDate(new Date(), "GMT", "YYYY-MM-DD");
// Created a function that returns last row of column
// Add 1 to get first row of column which does not have a value
var aNext = getLastRow('A') + 1;
sh.getRange(aNext, 1, 1, 2).setValues([[date, 'microdosehq']]);
// Assuming E-I columns have the same number of rows
// Get last row of I which does have a value
var iLast = getLastRow('I');
// Excluded to get the header, if needed, change to E1
var balance = sh.getRange("E2:I" + iLast).getDisplayValues();
// Get first row of J which does not have a value
var jNext = getLastRow('J') + 1;
// Range of J-N starting from first blank J row
var jNRange = "J" + jNext + ":N" + (jNext + balance.length - 1);
// Copy values to J-N
sh.getRange(jNRange).setValues(balance);
}
function getLastRow(column){
var range = column + "1:" + column;
var values = sh.getRange(range).getValues();
return values.filter(String).length;
}
In short, the code above copies the rows under E-I, and are being appended under J-N, like what your snippet here shows:
var balance = sheet.getRange("E1:E").getValues();
var nextRow = getFirstEmptyRow('J');
// Although the line below doesn't append
sheet.getRange(nextRow, 10, 1, 1).setValues([[balance]]);
I removed everything that were unrelated to the question to show a clear answer, so please re-add them if needed.
Sample Data:
1st run:
2nd run:
If I misunderstood your question and this was not the result you want, I apologize. Feel free to clarify below if this was not the result you want.
I have the similar issue as described at How to make sure a daily time trigger runs?.
I have a specific script in one of the Google sheets with a daily trigger (time-driven, should trigger every morning, set up through interface, not programmatically). But the script doesn't execute every day. I can see this in the execution report, where there're just successful executions and no failed ones. I can also see if the script executed by checking a cell in the sheet which gets updated with the execution timestamp when the script runs. And I've set up an immediate notification for the failed executions in the trigger settings.
In my specific case, the script should ran every day from Nov 9 - Nov 13, but it ran just on Nov 9, Nov 10, Nov 12. And I didn't get any notification about the failed execution.
The script itself doesn't use any API, it's pretty basic: reading data in one sheet, doing some calculation and writing to another sheet (talking about the sheets in single Google Sheet file).
If I run the main function manually, it always works.
I'd be very glad to get some ideas what could be wrong. Thanks.
EDIT: Code sample (main function and prototype for Array.includes)
function main(){
var date = new Date();
//var date = new Date(2019, 9, 1); // year, month (zero-indexed!!!), day
//var date = new Date(date.getYear(), date.getMonth()-3); // testing
var currentDay = Utilities.formatDate(date, "CET", "d");
Logger.log('currentDate: ' + Utilities.formatDate(date, "CET", "YYYY-MM-dd HH:mm:ss.S") + ' | currentDay: ' + currentDay);
if (currentDay == 1) {
Logger.log('currentDay is 1st of the month');
date = new Date(date.getYear(), date.getMonth() - 1);
var newCurrentDay = Utilities.formatDate(date, "CET", "d");
}
var monthToCheck = Utilities.formatDate(date, "CET", "MMMM").toUpperCase();
var yearToCheck = Utilities.formatDate(date, "CET", "YYYY");
Logger.log('dateToCheck: ' + Utilities.formatDate(date, "CET", "YYYY-MM-dd HH:mm:ss.S") + ' | monthToCheck: ' + monthToCheck + ' | yearToCheck: ' + yearToCheck);
var firstProjectRow = 7; // first row with the project data
var firstProjectCol = 1; // first column with project data - should contain Tool IDs
var numOfProjectRows = 999; // num of project rows to check (counted from and including var firstProjectRow)
var numOfProjectCols = 21; // num of project columns to check (counted from and including var firstProjectCol the last one contains number of hours for the last service)
var firstProjectHoursCol = 7; // first column with data about project hours (usually PM hours)
// ************* DO NOT EDIT BELOW THIS LINE ************* //
//return;
var indexedFirstProjectHoursCol = firstProjectHoursCol - 1;
var ss = SpreadsheetApp.getActiveSpreadsheet();
//var sheet = ss.getSheets()[3];
var sheetName = monthToCheck + ' ' + yearToCheck;
var sheet = ss.getSheetByName(sheetName);
Logger.log('sheet: ' + sheetName);
var range = sheet.getRange(firstProjectRow, firstProjectCol, numOfProjectRows, numOfProjectCols); // getRange(row, column, numRows, numColumns)
var rangeValues = range.getValues();
//Logger.log('rangeValues: "' + rangeValues);
var toolData = new Array();
var toolIds = new Array();
var toolHours = new Array();
//return;
for (var row in rangeValues) {
Logger.log('row: "' + row);
var clientId = rangeValues[row][0];
var projectId = rangeValues[row][1];
var hoursSum = 0;
// we have Tool ID so it's OK to proceed
if (clientId != "" && projectId != "") {
var clientProjectId = clientId + "-" + projectId;
for (var col in rangeValues[row]) {
var cellValue = rangeValues[row][col];
//Logger.log('col: ' + col + ' value: ' + value);
// get hours sum
if (col >= indexedFirstProjectHoursCol)
hoursSum += typeof cellValue == 'number' ? cellValue : 0;
}
//Logger.log('hoursSum: [' + hoursSum + ']');
var record = {id: clientProjectId, hours: hoursSum};
Logger.log("Data: " + record.id + " : " + record.hours);
// don't yet have a record of clientId-projectId
if (!toolIds.includes(clientProjectId)) {
toolData.push(record);
}
else {
recordIdx = toolIds.indexOf(clientProjectId);
toolData[recordIdx].hours += hoursSum;
}
toolIds = [];
toolHours = [];
toolData.forEach(function(item) {
toolIds.push(item.id);
toolHours.push(item.hours);
});
}
//Logger.log(toolData);
//Logger.log('ROW DONE!');
}
Logger.log('ROWS DONE!');
Logger.log('toolData.length: ' + toolData.length);
toolData.forEach(function(item) {
Logger.log('toolData: ' + item.id + " : " + item.hours);
});
Logger.log('DONE!!!');
// fill the table in the sheet with assigned number of hours
fillTheSheet(sheetName, toolData);
}
Apps Script triggers have always been a bit finicky. But of late they have been far more unreliable than usual (there have been several reports of spurious triggers and other maladies).
In this case, you can avoid using them altogether by leveraging an external service such as cron-jobs.org.
You'll have to refactor your app script project and deploy it as a public Web App with a doPost(e) function. You'd then pass the Web App's url to the external service as a web-hook endpoint that is invoked daily.
I'm trying to realize email notification at google spreadsheet, when sheet have no changes.
I'm tried to find smth opposite of onEdit() function.
I have email script, it fired by the trigger once at day. It's working nice.
function sendEmailAlert2() {
var ss = SpreadsheetApp.openById("MyID");
var sheetname = ss.getSheetByName("Sheet1");
var Toemail = 'email#gmail.com';
var subject = 'New Entry -' + ss.getName();
var body = 'Your file has a new entry in - ' + sheetname + ' Updated by - ' +
' check file- ' + ss.getUrl();
MailApp.sendEmail(Toemail,subject, body);
}
I need condition to this script when my sheet was no edited on this day.
Maybe someone have any idea?
You can achieve this by implementing a onEdit function which inserts a new time stamp into the sheet every time the file gets edited. And then, in your sendEmailAlert2() function, you can implement an if condition checking if the current day is equal to the day of last time stamp (i.e. the day of the last edit). If there was no edit today, the script email will be sent.
function onEdit(e) {
SpreadsheetApp.getActive().getActiveSheet()
.getRange('A1')
.setValue(new Date());
}
function sendEmailAlert2() {
var ss = SpreadsheetApp.openById("MyID");
var sheetname = ss.getSheetByName("Sheet1").getName();
var Toemail = 'email#gmail.com';
var subject = 'New Entry -' + ss.getName();
var body = 'Your file has a new entry in - ' + sheetname + ' Updated by - ' +
' check file- ' + ss.getUrl();
var NoEditSubject = 'No new Entry in' + ss.getName();
var NoEditBody = 'Your file has no new entry in - ' + sheetname + ss.getUrl();
if(sheetname.getRange('A1').getValue().getDay()==new Date().getDay())
{
MailApp.sendEmail(Toemail,subject, body);
}
else
{
MailApp.sendEmail(Toemail,NoEditSubject, NoEditBody);
}
}
On my side I would recommend you to use the DriveApp service and check if date Last Update is higher than 24h.
function getAlertedIfNoChange(){
var id = 'ID_SHEET';
var file = DriveApp.getFolderById(id);
var date = file.getLastUpdated();
var lastUpdated = date.getTime();
var today = (new Date()).getTime();
var delta = (24 * 60 * 60 * 1000)- (10*60*1000)
if((today - lastUpdated) > delta){
//Fire alert
Logger.log('No update during the last 24h')
}
}
For the 'delta' you have to set it to 24h in milliseconds and by experience I recommend you to remove some minutes, because when you set a trigger to run each day it will not fire at the exact same time. By removing some minutes to the 24h you are sure it will fire an alert.
You program a trigger to run each day and it will be ok.
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);}
I am looking for help with a script that posts that a specific sheet has been edited rather than when any sheet in the document is modified.
Here is what I currently have:
function onEdit() {
var d = new Date();
var h = d.getHours();
var min = d.getMinutes();
var sec = d.getSeconds();
var h_str = h;
var min_str = min;
var sec_str = sec;
// format time nicely
if (h < 10)
h_str = '0' + h;
if (min < 10)
min_str = '0' + min;
if (sec < 10)
sec_str = '0' + sec;
// create the formatted time string
var time_str = h_str + ':' + min_str + ':' + sec_str;
// create the message
var s = 'Roster last modified on: ' + d.toDateString() + ' # ' + time_str;
// change the range
SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Infernal Dawn').getRange("A1").setValue(s);}
This works perfectly fine to only update the sheet "Infernal Dawn" with the modification date, yet it also updates when ANY sheet is modified.
I found that there were 2 different ways of doing this (one using the gid). The only problem is that my skills in coding are extremely limited and I couldn't figure out how to integrate it into the above code. Any help would be appreciated!
The function onEdit has an optional argument that remembers which cell\range\sheet was edited. The structure of this argument is described here.
function onEdit(e)
{ var d=new Date();
var s=d.toLocaleString();
if(e.source.getActiveSheet().getName()=="TheSheetThatWeWishToTrace")
{ SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Infernal Dawn').getRange("A1").setValue(s);}
}