Insert timestamp when value copy/paste or inserts/edits whole row in google sheets, only insert to blank cell

  • I'm trying to insert a timestamp into a cell in "timestamp" column that has the same row index as the edited or pasted value in "status" column . Also, I only want to insert a timestamp in an empty cell in "timestamp" column, while the skip cell already has a timestamp.

  • Issue: When I add or remove columns, the positions of the "timestamp" and "status" columns change, causing the code to fail. I'm seeking for a solution to make this code work properly when the "timestamp" and "status" columns' index change. I'm trying to use column header in code instead of column's index.

Also I want to convert timestamp to number like yymmddHHmmss*1000 + seri number column "No." Exp: timestamp 22:01:20 14:08:05 and seri number column "No." is 678 then the value I want to insert into column "id" is 2201201408050678.

function insert_timestamp(e) {
  //get range
  var range = e.range;
  var sheet = range.getSheet();
  // get header
  var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
  var editcol = headers[0].indexOf("Status") + 1;
  var timestampcol = headers[0].indexOf("Timestamp") + 1;

  if ((range.columnStart == editcol || range.columnEnd == editcol) && sheet.getSheetName() == 'CONTENT.ORDER') {
    var values = range.offset(0, -1, range.rowEnd - range.rowStart + 1, 2).getDisplayValues();
    var rangeList = values.reduce((ar, [b, c], i) => {
      if (b == "" &&  c =="Approved") ar.push("b" + (i + range.rowStart));
      return ar;
    }, []);
    sheet.getRangeList(rangeList).setValue(new Date()).setNumberFormat("yy:MM:dd HH:mm:ss");
  }
}

Here is my try:

function onEdit(e) {
  var sheet = e.range.getSheet();
  if (sheet.getSheetName() != 'CONTENT.ORDER') return;

  var col = e.range.columnStart;
  var col_header = sheet.getRange(1,col).getValue();
  if (col_header != 'Status') return;
  if (e.value != 'Approved') return;

  var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
  var timestamp_col = headers.indexOf('Timestamp') + 1;
  var num_col = headers.indexOf('No.') + 1;
  var id_col = headers.indexOf('ID') + 1;

  var row = e.range.rowStart;
  var timestamp_cell = sheet.getRange(row, timestamp_col);
  if (timestamp_cell.getValue() !== '') return;

  var tz = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone();
  var timestamp = Utilities.formatDate(new Date(), tz, 'yy-MM-dd HH:mm:ss');
  timestamp_cell.setValue(timestamp).setNumberFormat('yy:MM:dd HH:mm:ss');

  var num = sheet.getRange(row,num_col).getValue().toString().padStart(4,'0');
  var id = timestamp.replace(/\D/g,'') + num;
  var id_cell = sheet.getRange(row,id_col);
  id_cell.setValue(id);
}

Update

Here is the variant of the function that handles a range of selected (adjoined) cells:

function onEdit(e) {
  var sheet = e.range.getSheet();
  if (sheet.getSheetName() != 'CONTENT.ORDER') return;

  var col = e.range.columnStart;
  var col_header = sheet.getRange(1,col).getValue();
  if (col_header != 'Status') return;

  var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
  var timestamp_col = headers.indexOf('Timestamp') + 1;
  var num_col = headers.indexOf('No.') + 1;
  var id_col = headers.indexOf('ID') + 1;

  var row_start = e.range.rowStart;
  var row_end = e.range.rowEnd;
  if (sheet.getRange(row_start,col).getValue() != 'Approved') return;

  var tz = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone();
  var timestamp = Utilities.formatDate(new Date(), tz, 'yy-MM-dd HH:mm:ss');

  for (let row = row_start; row <= row_end; row++) {

    var timestamp_cell = sheet.getRange(row, timestamp_col);
    if (timestamp_cell.getValue() !== '') continue;
    timestamp_cell.setValue(timestamp).setNumberFormat('yy:MM:dd HH:mm:ss');

    var num = sheet.getRange(row,num_col).getValue().toString().padStart(4,'0');
    var id = timestamp.replace(/\D/g,'') + num;
    var id_cell = sheet.getRange(row,id_col);

    id_cell.setValue(id);
  }
}

Basically it just repeats the original function for every selected cell. It does the job, but not too fast. It can be improved if you need to deal with too many rows.