Recipes / Recipe 11

Automatically move rows to a 'Done' tab when the status changes

Keep your task, order or lead list short: rows marked Done move to an archive tab with the date, instantly.

Apps ScriptWorkflow Setup time: 5 minutes

You'll need: A tab with a status column (a dropdown works best).

Steps

  1. Add a dropdown to your status column (Insert → Dropdown) with values like Open, In progress, Done.
  2. Extensions → Apps Script, paste the script, and set the tab names and statusColumn (4 = column D).
  3. Save. Change any status to Done and the row moves to the Done tab (created automatically).
  4. Already have lots of done rows? Run archiveAllDone once to sweep them.
Code.gsApps Script
/**
 * Move a row to an archive tab as soon as its status is set to "Done".
 * Uses the simple onEdit trigger. Also includes archiveAllDone() to sweep existing rows.
 */
const ARCHIVE_CONFIG = {
  sourceSheet: 'Tasks',
  archiveSheet: 'Done',
  headerRows: 1,
  statusColumn: 4,                  // column D holds the status dropdown
  doneValues: ['done', 'complete'], // case-insensitive
  addArchivedAt: true               // append the date it was archived
};

function onEdit(e) {
  archiveOnEdit(e); // if you also use the timestamp recipe, call stampOnEdit(e) here too
}

function isDone(value) {
  return ARCHIVE_CONFIG.doneValues.indexOf(String(value).trim().toLowerCase()) !== -1;
}

function archiveTarget() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  return ss.getSheetByName(ARCHIVE_CONFIG.archiveSheet) || ss.insertSheet(ARCHIVE_CONFIG.archiveSheet);
}

function archiveOnEdit(e) {
  const cfg = ARCHIVE_CONFIG;
  if (!e || !e.range) return;
  const range = e.range;
  const sheet = range.getSheet();
  if (sheet.getName() !== cfg.sourceSheet) return;
  if (range.getNumRows() !== 1 || range.getNumColumns() !== 1) return; // single-cell edits only
  if (range.getColumn() !== cfg.statusColumn || range.getRow() <= cfg.headerRows) return;
  if (!isDone(range.getValue())) return;

  const row = range.getRow();
  const values = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues()[0];
  archiveTarget().appendRow(cfg.addArchivedAt ? values.concat([new Date()]) : values);
  sheet.deleteRow(row);
}

/** Pure function: returns 0-based indexes of rows whose status is done. */
function doneRowIndexes(rows, statusColumn) {
  return rows.reduce((acc, row, i) => (isDone(row[statusColumn - 1]) ? acc.concat([i]) : acc), []);
}

function archiveAllDone() {
  const cfg = ARCHIVE_CONFIG;
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(cfg.sourceSheet);
  const lastRow = sheet.getLastRow();
  if (lastRow <= cfg.headerRows) return;
  const rows = sheet.getRange(cfg.headerRows + 1, 1, lastRow - cfg.headerRows, sheet.getLastColumn()).getValues();
  const idx = doneRowIndexes(rows, cfg.statusColumn);
  const target = archiveTarget();
  const now = new Date();
  idx.forEach((i) => target.appendRow(cfg.addArchivedAt ? rows[i].concat([now]) : rows[i]));
  idx.slice().reverse().forEach((i) => sheet.deleteRow(i + cfg.headerRows + 1)); // bottom-up so row numbers stay valid
}

Good to know

Do it yourself

Recipe Club: $12/month

Prefer to set things up yourself? Join the Recipe Club and get access to a private GitHub repository of ready-made, tested automations for Sheets, Apps Script, Python and Node. Each one comes with setup notes and sample data.

  • A new recipe added every week
  • Everything in the free library, plus the longer, more complete versions
  • Use them in your own business as much as you like
  • Cancel anytime by email (or the Stripe customer portal once available)

Recipe Club opens soon: join the waitlist

You'll need a free GitHub account. After checkout, reply with your GitHub username and we'll send the repo invite, usually within one business day.

Or buy a one-time recipe pack ($19–$29)

  • Clean Data Pack $19
    Dedupe, cleaning, merging and normalizing scripts and formulas for messy exports, with a sample sheet.
    Coming soon
  • Forms & Inbox Pack $19
    Form auto-replies, request routing, a lead log and follow-up nudges, ready to paste into Apps Script.
    Coming soon
  • Money Ops Pack $29
    Invoice reminders, a monthly revenue dashboard with QUERY, a receipts log and nightly backups.
    Coming soon

Written with AI assistance. Script logic is unit-tested; formulas follow documented Google Sheets behavior. Always try new automations on a copy of your sheet first.