Time stamp a cell when multiple columns are updated - spreadsheet

I have been checking out multiple codes on trying to update my Google Spreadsheet but have been unsuccessful when trying to do this with multiple cells. On my spreadsheet I have multiple tabs and when I update a row in column 2,3 or 4, I would like it to enter the date in column 5.
Thank you for your help.

Step 1.
In the Google Spreadsheet, click on "Script editor..." under the "Tools" menu.
Step 2.
Remove any sample script that might be in there and paste the following ...
// Sets the targetColumn on the edited row to the current date if the
// edited column in within columnBounds.
// Note: This will only handle single cell editing.
// Columns that need to be monitored for changes. Use CAPITAL letters.
var monitoredColumns = ['B', 'C', 'D'];
// Colum that will receive the date.
var targetColumn = 'E'
// To avoid adding the date in the title row, we need to consider the starting row.
var startingRow = 4
// onEdit() is a reserved function name that will be called every time the sheet will be edited.
function onEdit(e) {
var range = e.range;
// Row of the edited cell.
var row = range.getRow();
// Column of the edited cell.
var col = String.fromCharCode(64 + range.getColumn());
if (row < startingRow) {
// None of the monitored rows have been edited.
return;
}
if (monitoredColumns.indexOf(col) < 0) {
// Column B, C or D (2, 3 or 4) was not modified.
// Do not proceed any further.
return;
}
// Current spreadsheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// Date cell.
var dateCell = sheet.getRange(targetColumn + range.getRow());
// Set it to the current date.
dateCell.setValue(new Date());
}
Step 3
Adjust the values of monitoredColumns, targetColumn and startingRow
Step 4
Start entering some content in the cells.

Related

How do I use variables to avoid having to create a set of these for all of the columns in my sheet?

I'm trying to get my sheet to automatically recalculate a set of dates within a schedule, in both directions, when a cell is changed.
The code works fine, but I need to add a bunch more columns and I'd really rather not copy/paste/find/replace a load more times. I'm fairly certain I can do this with variables (just looking up the column identifier and feeding that into the code somehow), but I don't know-how.
functJon onEdJt(e) {
var sh = e.source.getActJveSheet();
Jf(sh.getName() === 'Date Calculator' && e.range.getA1NotatJon() === 'C9'
)
{
sh.getRange("C10").setFormula("=WORKDAY(C9,+$C$3)");
sh.getRange("C11").setFormula("=WORKDAY(C10,+10)");
sh.getRange("C12").setFormula("=WORKDAY(C11,+$C$4)");
sh.getRange("C13").setFormula("=WORKDAY(C12,+$C$3)");
sh.getRange("C14").setFormula("=WORKDAY(C13,+10)");
sh.getRange("C15").setFormula("=WORKDAY(C14,+1)");
sh.getRange("C16").setFormula("=WORKDAY(C15,+$C$5)");
}
else Jf (sh.getName() === 'Date Calculator' && e.range.getA1NotatJon()
=== 'C10' )
{
sh.getRange("C9").setFormula("=WORKDAY(C10,-$C$3)");
sh.getRange("C11").setFormula("=WORKDAY(C10,+10)");
sh.getRange("C12").setFormula("=WORKDAY(C11,+$C$4)");
sh.getRange("C13").setFormula("=WORKDAY(C12,+$C$3)");
sh.getRange("C14").setFormula("=WORKDAY(C13,+10)");
sh.getRange("C15").setFormula("=WORKDAY(C14,+1)");
sh.getRange("C16").setFormula("=WORKDAY(C15,+$C$5)");
Ideally the code should then just "work" for any number of columns in the sheet, so I don't need to add more code if I add more columns.
Update
Here's an example of what I'm trying (but it's not working) - attempting to check that the active cell is in row 9 of a specific column before then running the "set.Formula" functions:
function onEdit(e) {
var sh = e.source.getActiveSheet();
var col = e.source.getActiveSheet().getRange().getColumn();
var row = e.source.getActiveSheet().getRange().getRow();
if(sh.getName() === 'Date Calculator' && e.getRange('9',col) )
Event Objects
Even though the code was written as onEdit(e), you didn't take advantage of the Event Objects.
In this answer, the code returns the new value of the edited cell and also the range. The range is then used to work out the row, column and sheet name and these is used for validation as well as for building the ranges and the setFormula
Variables
The code includes variables for the valid range of columns that can be used for data entry (Column C to Column H), and respective input rows (rows 9 and 10). These are expressed as values, but they could just as easily be written into the spreadsheet as assumptions and the values obtained in the code by using getValue.
The absolute cell references used in the setFormula are partly variable (column reference) and part hard-coded (the respective rows-3,4 and 5). If desired, the rows could be variable as well.
Efficiency
There is just one if statement containing one version of the code to build setFormula.
This is achieved by designing the if statement:
1. if the sheet = "Date Calculator" AND
2. if the editColumn is between the valid ColumnStart and ColumnEnd values (Column C to H) AND
3. if the editRow is between the valid Row values (rows 9 or 10) AND
4. if the edited value isn't a blank (length != 0).
The last condition ("edited value is blank") ensures that if cell contents are been deleted (and/or have no value), then the code won't proceed.
Convert column number to letter
I used a routine written by #AdamL found at Convert column index into corresponding column letter; this converts a column number into a letter. It's used to build the "targetcolumn" address in Workdays. It's valid for the letters A-Z; there's a version for letters beyond Z.
Cleanup
If data is entered into row 10 of a given column, then any value in row 9 (of the same column) needs to be deleted. The code does this and also deletes any pre-existing formula dates in the rows below so there is no confusion about the dates derived by the data entry.
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Date Calculator";
var sheet = ss.getSheetByName(sheetname);
// get the event source data
var editedCell = e.range;
var editRow = editedCell.getRow();
var editCol = editedCell.getColumn();
var eValue = e.value;
var editedSheet = editedCell.getSheet().getName();
//Logger.log("DEBUG: the cell = "+editedCell.getA1Notation()+", the column = "+editCol+", the row is "+editRow+", the value is "+eValue+", the edited sheet is "+editedSheet);
// create some variables for column and row range
var columnStart = 3; // Column C
var columnEnd = 8; // Column H
var rowOption1 = 9; // row 9
var rowOption2 = 10 // row 10
// create some variables for target cells
var absolutecolumn = "C";
//var absoluterow1 = 3; // not used
//var absoluterow2 = 4; // not used
//var absoluterow3 = 5; // not used
// test for valid edit in row option 1 // Row 9
if(editedSheet === sheetname && columnEnd >=editCol && editCol>=columnStart && rowOption2>=editRow && editRow>=rowOption1 && eValue.length !=0 ){
//Logger.log("DEBUG: You got the right sheet, the edit is in the right range of columns and the edited row was = "+rowOption1);
if (editRow == rowOption2){
// clear row 9
sheet.getRange((+editRow-1),editCol).clear();
}
// clear following 8 rows of data
sheet.getRange((+editRow+1),editCol,8).clear();
// set the targetcolumn as a letter
var targetcolumn = columnToLetter(editCol);
// set formula for row+1
sheet.getRange((+editRow+1),editCol).setFormula("=WORKDAY("+targetcolumn+editRow+",$"+absolutecolumn+"$3)"); //
// set formula row +2
sheet.getRange((+editRow+2),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+1)+",+10)");
// set formula row +3
sheet.getRange((+editRow+3),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+2)+",$"+absolutecolumn+"$4)");
// set formula row +4
sheet.getRange((+editRow+4),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+3)+",$"+absolutecolumn+"$3)");
// set formula row + 5
sheet.getRange((+editRow+5),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+4)+",+10)");
// set formula row + 6
sheet.getRange((+editRow+6),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+5)+",+1)");
// set formula row + 7
sheet.getRange((+editRow+7),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+6)+",$"+absolutecolumn+"$5)");
// change the background to show entry in rowoption1
sheet.getRange(editRow,editCol).setBackground("yellow");
sheet.getRange((+editRow+1),editCol).setBackground("white");
}
}
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
Screenshot

Increment String Value Google Sheet

Using the following code I cannot increment a value in google sheets to be plus one.
function incrementCellValuesByOne() {
// Increments the values in all the cells in the active range (i.e., selected cells).
// Numbers increase by one, text strings get a "1" appended.
// Cells that contain a formula are ignored.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeRange = ss.getActiveRange();
var cell, cellValue, cellFormula;
// iterate through all cells in the active range
for (var cellRow = 1; cellRow <= activeRange.getHeight(); cellRow++) {
for (var cellColumn = 1; cellColumn <= activeRange.getWidth(); cellColumn++) {
cell = activeRange.getCell(cellRow, cellColumn);
cellFormula = cell.getFormula();
// if not a formula, increment numbers by one, or add "1" to text strings
// if the leftmost character is "=", it contains a formula and is ignored
// otherwise, the cell contains a constant and is safe to increment
// does not work correctly with cells that start with '=
if (cellFormula[0] != "=") {
cellValue = cell.getValue();
cellValue =+cellValue
cell.setValue(cellValue + 1);
}
}
}
}
For example "personalDataDOB_3" needs to become "personalDaTaDOB_4" I'm looking for a fast way to do this as right now I need to replace the value by typing.
You want to modify "personalDataDOB_3" of a certain cell to "personalDaTaDOB_4". If my understanding is correct, how about this modification?
Modification points :
When the retrieved "personalDataDOB_3" is converted to the number using cellValue =+cellValue, NaN is returned. So even if 1 was added, the result is NaN.
If the format of strings you want to modify is always "personalDataDOB_#", how about separating the string by _?
In order to reflect above points, please modify your script as follows.
From :
if (cellFormula[0] != "=") {
cellValue = cell.getValue();
cellValue =+cellValue
cell.setValue(cellValue + 1);
}
To :
if (cellFormula[0] != "=") {
cellValue = cell.getValue();
var temp = cellValue.split("_"); // Added
temp[1] = Number(temp[1]) + 1; // Added
cell.setValue(temp.join("_")); // Modified
}
Note :
If the format of strings you want to modify is always changed, please tell me.
If I misunderstand your question, I'm sorry.

My google scripts code does not update a cell (N5) thats keeps track of sales made

I've written a code on google scripts that should update a cell on a sheet if another cell is updated with the phrase "vendio" (sold in Spanish). After it reads that "vendio" has been entered, it finds the price of the cell 4 rows over and adds it to the cell that is keeping track of total sales.
function TotalSales() {
/*
#OnlyCurrentDoc
*/
//Get Current Google Sheet, Get Active Sheet Tab.
//finds current active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//find current active cell
var cell = ss.getActiveCell();
//find row number of active cell
var row = cell.getRow();
//defines a variable to the cell adjacent to where "vendio" is
var H_cell = ss.getRange(row,8);
//gets value of Column H cell adjacent to where "vendio" is
var H_value = H_cell.getValue();
//find the range of N5 cell
var N5_cell = ss.getRange("N5");
//Gets value inside N5 Range
var N5_value = N5_cell.getValue();
if(cell.getValue() == "vendio") { //cell.getValue() gets value in the active cell
sum_value = H_value + N5_value; // sums value of adjacent cell to vendio and value of an cell thats keeping count
N5_cell.setValue(sum_value); //sets N5 value to the sum of N5 and H(row)
} else {
N5_cell.setValue(N5_value);
};
}
function onEdit(){
TotalSales();
};
Nevermind I edited my code and changed the function onEdit to match the name of the function my code is running inside. I've left it on here for anyone else to see.

Need a more efficient solution than looping

I am building a spreadsheet that tracks work in progress as it moves through steps of a manufacturing process.
Each step of the process has a column with the total parts moved to each stage. To the left of this column is a column for number of parts moved to the stage (parts move through a few at a time).
My scrpit then takes the values in the "add" column, adds them to the "total" column, then reset the "add" column to "".
Here's the code:
function addColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// ss is now the spreadsheet the script is associated with
var sheet = ss.getSheets()[0]; // sheets are counted starting from 0
// sheet is the first worksheet in the spreadsheet
for (var i=4; i<500; i++ ) {
if(sheet.getRange(i,1).getValue()>0){ //Only run if order number not empty
//Breakout Column
var add = sheet.getRange(i,6);
var total = sheet.getRange(i,7);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CNC Column
var add = sheet.getRange(i,8);
var total = sheet.getRange(i,9);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CutSand Column
var add = sheet.getRange(i,10);
var total = sheet.getRange(i,11);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Lasered Column
var add = sheet.getRange(i,12);
var total = sheet.getRange(i,13);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//To Finishing Column
var add = sheet.getRange(i,14);
var total = sheet.getRange(i,15);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
// Defective Column
var add = sheet.getRange(i,17);
var total = sheet.getRange(i,18);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Etsy Column
var add = sheet.getRange(i,20);
var total = sheet.getRange(i,21);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
}
if(sheet.getRange(i,4).getValue()<1){i=500} //Once you find a blank order exit the loop
}
}
My code as written does accomplish this; it does exactly what I need. The problem is that since the code is accessing the spreadsheet on each loop it takes almost a full second per cell to run, and with 7 steps per order it can take minutes at a time to run through with lots of orders...
This is a pretty simple mathematical task, so there has to be a more efficient way of doing it, I just haven't been able to find the right keywords to describe what I need to do.
I am quite happy to learn whatever needs to be done, just need to know what direction to head.
Thanks in advance!
I would suggest to do something like this: (not tested)
function addColumns() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // Refers to the first worksheet in the spreadsheet
var data = sheet.getDataRange().getValues(); // Acquires all values of the sheet
for (var i = 3; i < data.length; i++) { // Loop over every row
if (data[i][0].length > 0) { // Check if first column has a value
// Breakout
sheet.getRange(i+1,7).setValue(parseFloat(data[i][6]) + parseFloat(data[i][5]));
sheet.getRange(i+1,6).clear();
// Repeat code above for other columns
}
}
}
This code acquires all the data from the sheet instead of looping over a fixed amount of 500 rows. Assuming that your data starts at row 4, I've implemented this in the code above as well.
Variable data acquires all the data at one moment instead of trying to fetch values of every range (cell) all the time. I expect that this will save your script quite some time.
Because we acquire the data at once, the script sees the value as a string. Before we calculate the new value of the total column, we parse the value as a float (a number with decimals).
The code above is not tested as I don't have a sheet ready in the same format as you do but I think the logic is clear and if it doesn't work I suppose you should be able to adjust it to work for your sheet.

Problems with insert a Timestamp

I have to insert a link with a sheet with the basic's of my original sheet.
=> At the original sheet there is a importrange which insert the data. In the next tab a query take the data - now there should be appear a timestamp when the data in col B (in the Sheet at the link) is updated, but only at the first time the col change from an empty col to a filled col.
I searching at the internet, but I didn't find a helpful answer. On the one hand the most of the Scripts I found didn't work in general or doing a little bit. As a example:
This script worked:
function onEdit(event)
{
var timezone = "GMT-5";
var timestamp_format = "MM-dd-yyyy";
var updatedColName = "Bid Responses";
var sheet = event.source.getSheetByName('Overview - Working (Hidden)');
var actRng = event.source.getActiveRange();
var editColumn = actRng.getColum();
var index = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(timeStampCoName);
var updateCol = headers[0].indexOf(updateColName); updateCol = updateCol+1;
if (dateCol > -1 && index > 1 && editColumn == updateCol) {
var cell = sheet.getRange(index, dateCol + 1);
var date = Utilities.formatDate(new Date(), timezone, timestamp_format);
cell.setValue(date);
}
}
But the timestamp was not inserted at the row where the col data change, it appear in a completely different row.
Can someone help me to write a Script that do exactly what I want?
Unru,
an onEdit trigger will always require a manual edit to the spreadsheet. In other words: the script runs when a user changes a value in a spreadsheet.A recalculation of a formula does NOT fire an onEdit script.
More info: here