Skip to content
// generated · Sheets

Move a row to a “Done” sheet when column E says Done.

I keep a tracker and want finished rows to leave the active list and pile up on their own tab.

“When I mark a row Done in column E, move that row to a separate sheet called Done.”

The script

copy · paste · trigger
moveDoneRows.gs
Apps Script
// Made with bulldo.gs · describe an automation, get working Apps Script.
// Fork or fix this one → https://bulldo.gs/g/move-done-rows-to-archive-sheet

// Installable onEdit trigger. When column E of a row reads "Done", copy the
// whole row to a sheet named "Done", then delete it from the source sheet.
function onEdit(e) {
  const range = e.range;
  if (range.getColumn() !== 5) return;          // only column E
  if (range.getValue() !== 'Done') return;      // only the "Done" value

  const sheet = range.getSheet();
  const done = sheet.getParent().getSheetByName('Done');
  const row = sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).getValues()[0];

  done.appendRow(row);
  sheet.deleteRow(range.getRow());
}

Made with bulldo.gs. Fork it to change a field, a trigger, or the edge cases — describe the change and Gnaw rewrites it.

Walkthrough

How it works

The two guards drop edits we do not care about: anything outside column E, and any value other than the exact string "Done". That keeps the trigger cheap on every keystroke.

It reads the full row with getRange(row, 1, 1, getLastColumn()), appends it to the Done sheet, then deletes the source row. These are separate operations, not an atomic move: a failure between them can leave a duplicate. Concurrent edits can also move row positions; this minimal example needs coordination and recovery before unattended use.

A bound simple onEdit trigger can modify its own spreadsheet. Installable triggers are needed for services that require additional authorization; moving between tabs of this same spreadsheet does not by itself require one.

Set it up

Create a tab named exactly "Done". Paste the function, then add an installable trigger: Triggers → add → event source: from spreadsheet, event type: on edit. Approve the Sheets scope on the first edit.

Need a different version?

Describe your sheet or inbox and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

3 questions
What if the "Done" sheet does not exist?
getSheetByName returns null and appendRow throws. Create the tab first, or add `if (!done) return;` to fail quietly.
Can I trigger on a checkbox instead of the word "Done"?
Yes. A checked box is the boolean true, so change the value check to `if (range.getValue() !== true) return;`.
Deleting the row shifts everything up — is that a problem?
No, because the handler runs per edit and reads the row position from the event each time. If you mark several rows at once, mark them top-to-bottom or switch to a batched time-trigger version.