scrips to subtract value in one cell from another cell - google-apps-script

My current script adds a timestamp, i now want to add the function to subtract the value in cell 13 from that in cell 14 and put in into cell 12 at the same time it adds the date stamp
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
var value = e.range.getValue();
var sheet = e.source.getActiveSheet().getName();
var destination = e.source.getActiveSheet().getRange(row,11);
var destination2 = e.source.getActiveSheet().getRange(row,10);
var num1 = e.source.getActiveSheet().getRange(row,13).getValue;
var num2 = e.source.getActiveSheet().getRange(row,14).getValue;
if(col === 4 && row > 4 && sheet ==="Bayswater" && value === "Completed" && destination2.getValue() ==="" && destination.getValue() ===""){
e.source.getActiveSheet().getRange(row,11).setValue(new Date(new Date().setHours(0,0,0,0))).setNumberFormat('dd-MMM-yy');
e.source.getActiveSheet().getRange(row,10).setValue(new Date(new Date().setHours(0,0,0,0))).setNumberFormat('dd-MMM-yy');
e.source.getActiveSheet().getRange(row,12).setValue('=num1-num2')
}

Probably the last line should be like this:
e.source.getActiveSheet().getRange(row,12).setFormula('=(N' + row + '-M' + row + ')');
Or it was just a typo. You forgot to add () after getValue in the two lines:
var num1 = e.source.getActiveSheet().getRange(row,13).getValue(); // <-- here
var num2 = e.source.getActiveSheet().getRange(row,14).getValue(); // <-- here
If you add () you can use the last line this way:
e.source.getActiveSheet().getRange(row,12).setValue(num1-num2);

Related

Is there a way to prevent spreadsheet from updating the date to today's date in formula?

I'm trying to print the date when I change the value on a cell (status column). But spreadsheets update the date to today's date. I wish it could save the date from when the status was changed.
The code to print the date:
function TIMESTAMP() {
var today = new Date();
var date = Utilities.formatDate(today, 'GMT-3', 'dd/MM/yyyy');
return date;
}
In the spreadsheet, the column D is where I set the status and columns H to J print the date from when the status was changed.
The code for column H is the following:
=ARRAYFORMULA(IF(ROW(H:H)=1;"Logística";IF(ISBLANK(D:D);;IF((D:D>=3)*(D:D<6);TIMESTAMP();IF(D:D<1;"Aguardando Pagamento";IF(D:D=2;"Aguardando Etiqueta";IF(D:D=6;"Cancelado";"Aguardando Faturamento")))))))
The codes for columns I and J are similar to H. So, how to print the date from when the status in column D was changed and keep spreadsheet from updating to today's date?
Not sure but how about this:
function onEdit(e) {
const sh=e.range.getSheet();
if(sh.getName() == 'Sheet Name' && e.range.columnStart == 4 ){
let dt = Utilities.formatDate(new Date(), 'GMT-3', 'dd/MM/yyyy');
e.range.offset(0,4).setValue(dt)
}
}
You have to change the sheet name
Since you manually change the state on column D, you may use an onEdit(e) trigger to override the formula with the value that you want:
function onEdit(e) {
const { range } = e
const sheet = range.getSheet()
if(sheet.getName() === 'Set your sheet name here' && range.getColumn() <= 4 && range.getLastColumn() >= 4){
const firstRow = range.getRow()
const values = sheet
.getRange(range.getRow(), 4, range.getHeight(), 1)
.getValues()
.flat()
for (let i = 0; i < values.length; i++) {
const value = values[i]
if (!Number.isInteger(value) || value < 3 || value > 5) continue
const row = firstRow + i
const col = value - 3 + 8
const targetRange = sheet.getRange(row, col)
const todayStr = Utilities.formatDate(new Date(), 'GMT', 'dd/MM/yyyy')
targetRange.setValue(todayStr)
}
}
}
This code works even when setting multiple rows at the same time. Remember to set the name of your sheet and the timezone you are in.
References
Utilities.formatDate(date, timeZone, format) (Google Apps Script reference)
Class Range (Google Apps Script reference)

Google Sheet SetValu of cell which is 2 cells ahead

I am trying to set the value in the ebayFee cell once i enter a sold value in the sold cell.
Also, is this possible to make this function automatically so it runs every time on a new row when i enter a value to sold it automatically populate the rest for me.
function onEnter(num) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var row = ss.getActiveCell().getRow();
var ebayfee = ( num * 8 ) / 100;
var cell = ss.getRange('E' + row).setValue(ebayfee);
}
Try the following code, change the sheet name "Sheet1" as per your need:
function onEdit(e) {
let r = e.range;
if( r.getSheet().getName() === 'Sheet1' ) {
if( r.rowStart >= 2 && r.columnStart === 3 ) {
r.offset( 0, 2 ).setValue( r.getValue() * 8 / 100 );
}
}
};

Automatic date if i change a cell

i know that's might a simple function, but i can't solve it.
I want to write a simple onEdit function with the following conditions:
If anything in a row (3 - xxx) is changed or updated, i want the actuel date in a specific column.
For example:
if I changed cell B10, I want the actuel date in cell G10. Also for an update in cell E10.
My head
function onEdit() {
var s = SpreadsheetApp.getActiveSheet;
var range = s.getActiveRange()
My loop over all rows is:
for(var i = 1; i<= range.getNumRows() ; i++){
var r = range.getCell(i, 1)
if( s.getName() == "Sheet1" ) {
My if-statement:
if( r.getColumn() == 2 ) {
var nextCell = r.offset(0, 2);
if( nextCell.getValue() === '' ) {//is empty?
var time = new Date();
time = Utilities.formatDate(time, "GMT+01:00", "HH:mm:ss MM/dd/yy");
nextCell.setValue(time);
}
};
};
I'll be happy if someone can helpme ;-)
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
// Instead of getting activeCell we get the whole range
var range = s.getActiveRange()
//Use For loop to go through each row and add the time Stamp
for(var i = 1; i<= range.getNumRows() ; i++){
var r = range.getCell(i, 1) //Assumption here is that data is pasted in one column only
// IF that is not always the case, you will have to get the range over which the data was pasted and select column 2
if( s.getName() == "Gesamt" ) { //checks that we're on the correct sheet
//var r = s.getActiveCell(); calling it again doesnt change its the value
// If it is first time keyword added, we will add the current date to "Date Added" Column
// We will if it is the first time the "Keyword" column has been written
for (var c = 1; c<=26; c++){
if( r.getColumn() == c) {
var k = 26 - c; //every updated time to the column 26 in the same row
var nextCell = r.offset(0, k);
//if( nextCell.getValue() === '' ) {//is empty?
var time = new Date();
time = Utilities.formatDate(time, "GMT+01:00", "HH:mm:ss MM/dd/yy");
nextCell.setValue(time);
};
};
}
}
}
There is no reason to loop on an on-edit trigger, as you will automatically be in the correct row and column every time there is an edit:
function onEdit(e){
var sheet = SpreadsheetApp.getActiveSheet();
var range = e.range; //gets edited range
var column = range.getColumn();
if (column < 3){ //stops if column is too low
return;
}
if (column > 6){ //stops is column is too high
return;
}
//else puts date in same row in column G
var row = range.getRow();
var time = new Date();
var stringTime = Utilities.formatDate(time, "GMT+01:00", "HH:mm:ss MM/dd/yy");
sheet.getRange(row, 7).setValue(stringTime);
}

How to automatically add a timestamp in google spreadsheet

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;
}
});
}

Find LastRow of column C (when Col A and B have a different row size)?

How to find the last used cell of column C ?
Example: "Sheet1" : "Col A" and "Col B" have 1200 rows. And "Col C" has only 1 row.
## ColA ColB ColC
## 1 1 1
## 2 2 empty
## .. .. ..
## 1200 1200 empty
Here are my unsuccessful tests :
Function find_last_row_other_column() {
var ws_sheet =
var ws = SpreadsheetApp.openById("Dy...spreadsheet_id...4I")
var ws_sheet = ws1.getSheetByName("Sheet1");
var lastRow = ws_sheet.getRange("C").getLastRow();
var lastRow = ws_sheet.getRange("C:C").getLastRow();
var lastRow = ws_sheet.getRange(1,3,ws_sheet.getLastRow()); 1200 rows for colA! instead of row = 1 for col C.
}
Note: I can't use C1 because next time I use the function it will be C1200 or something else.
var lastRow = ws_sheet.getRange("C1").getLastRow();
I ask this because my next goal is to copy/paste the result of C1 into C2:C1200. Here is my test :
var lastRow = ws_sheet.getLastRow();
var target_range = ws_sheet.getRange(1,3,lastRow,1); //C1 until last row
var Formula_values = source_range.getValues();
target_range.setValues(Formula_values);
Thanks in advance ;)
ps: I have spend 2 hours on it. I have tried similar problems & their solutions already given on this website, but I can't happen to make them working. I am lost ! :
More efficient way too look up the last row in a specific column?
and Get last row of specific column function - best solution
As I mentioned in the comments above, this is the subject of the highest score post on StackOverFlow...
The original post returns the value of the last cell in a column but a (very) little modification makes it return the row index.
Original post :
Script:
function lastValue(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + lastRow).getValues();
for (; values[lastRow - 1] == "" && lastRow > 0; lastRow--) {}
return values[lastRow - 1];
}
modified to return index of the last used cell in a column :
function lastValue(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + lastRow).getValues();
for (; values[lastRow - 1] == "" && lastRow > 0; lastRow--) {}
return lastRow;
}
Here is the function to do it:
function lastRowInColumnLetter(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + (lastRow + 1)).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return lastRow + 1;
}
}
and you invoke it as =lastRowInColumnLetter("C").
And here are 3 more useful functions in this context:
function lastValueInColumnLetter(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + (lastRow + 1)).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return values[lastRow];
}
}
function lastValueInColumnNumber(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,lastRow + 1).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return values[lastRow];
}
}
function lastRowInColumnNumber(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,lastRow + 1).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return lastRow + 1;
}
}
These functions properly address empty columns, and also start counting backwards from the last row with content on the active sheet getLastRow(), and not from the last row on the sheet (with or without content) getMaxRows() as in the accepted answer.
If you don't have empty cells between your data, you can use this:
function last_Column_Row(){
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var Direction = SpreadsheetApp.Direction;
var xcol = 2;//e.g. for column 2 ("B"), to obtain its last row
var yrow = 8;//e.g. for row 8, to obtain its last column
var lastRow =sheet.getRange(1,xcol).getNextDataCell(Direction.DOWN).getRow();//last row of column 'xcol'
var lastCol =sheet.getRange(yrow,1).getNextDataCell(Direction.NEXT).getColumn();//last column of row 'yrow'
};
It gets the number of next empty cell-1 of a specific row or column (similar to Ctrl + 'arrow' in a sheet)
But If you have empty cells between your data, you can use this:
function last_Row_Column2()
{
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var Direction = SpreadsheetApp.Direction;
var maxR =sheet.getMaxRows();
var maxC = sheet.getMaxColumns();
var yrow = 8;//e.g. for row 8, to obtain its last column
var xcol = 2;//e.g. for column 2 ('B'), to obtain its last row
var valMaxR = sheet.getRange(maxR,xcol).getValue();//for the case that the last row has the last value
var valMaxC = sheet.getRange(yrow,maxC).getValue();//for the case that the last column has the last value
if(valMaxR !=''){var lastRow = maxR;}//if the last row in studied column is the last row of sheet
else{var lastRow =sheet.getRange(maxR,xcol).getNextDataCell(Direction.UP).getRow();}
if(valMaxC !=''){var lastCol = maxC;}//if the last column in studied row is the last column of sheet(e.g.'Z')
else{var lastCol =sheet.getRange(yrow,maxC).getNextDataCell(Direction.PREVIOUS).getColumn();}
};
[UPADTE} Please disregard this answer. User Serge's code instead. I was having a brain fart. His answer is magnitudes better in every way. That will teach me not to answer SO questions after you come back from a cocktail night... [/UPDATE]
The following function will log the last non-blank row number of column C. Note: if, for example, column C has a value in row 1 and row 200, with rows 2-199 blank, the function will return 200 as last non-blank row - it does not account for blank rows above last non-blank row.
function getLastNonBlankColCrow() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastNonBlankColCrow = 0;
for (var i=1, lenRows=sheet.getRange("C:C").getNumRows(); i<=lenRows; i++) {
if ( !sheet.getRange(i, 3).isBlank() ) { // 3 is 1-based index of column C
lastNonBlankColCrow = i;
}
}
Logger.log(lastNonBlankColCrow);
}