Skip to content

SOP: Replace Database Sheet Data from Generated TSVs

Use this procedure whenever you need to seed or replace the contents of a tenant's database Google Sheet — for example when setting up a demo environment, resetting test data, or bootstrapping a new tenant from the dummy data generator.


Prerequisites

  • Node.js installed
  • Access to the target Google Sheet (database sheet, not the reporting sheet)
  • VS Code or any editor that can open TSV files and select all text

Step 1 — Generate the TSV files

bash
cd generate-dummy-data
node generate-v2.js

This writes one TSV per database tab into generate-dummy-data/. Each run overwrites the previous output. The manifest (MANIFEST.json) logs row counts for verification.


Step 2 — Import each TSV into its sheet tab

For each file in the table below, in order:

  1. Open the TSV in VS Code
  2. Select All (Ctrl+A / Cmd+A)
  3. Copy (Ctrl+C / Cmd+C)
  4. In the Google Sheet, click the matching tab
  5. Click cell A1
  6. Paste (Ctrl+V / Cmd+V)
  7. If Google Sheets prompts "Split text to columns" — click No (the paste already handles tab-separated values correctly)

File → Tab mapping (import in this order)

TSV fileSheet tabWhy this order
disciplines.tsv08. Disciplinesreferenced by users
clients.tsv04. Clientsreferenced by projects
users.tsv03. Usersreferenced by projects, budgets, entries
tasks.tsv05. Tasksreferenced by project_tasks, entries
projects.tsv02. Projectsreferenced by everything below
project_tasks.tsv06. Project_Tasksdepends on projects + tasks
time_entries.tsv01. Time Entriesdepends on projects + users + tasks
budgets.tsv07. Budgetsdepends on projects + users
rate_history.tsv13. Rate_Historydepends on users
pending_pto.tsv10. Pending_PTOdepends on users
unresolved_items.tsv11. Unresolved_Itemsdepends on projects + users

Tabs not in this list (09. Conversations, 12. Company_Calendar, 14. Liabilities) are not generated — leave them with their existing header row only, or clear them manually if resetting.


Step 3 — Verify

After pasting, spot-check each tab:

  • Row 1 is the header row (column names), not data
  • Row count roughly matches MANIFEST.json
  • No blank rows between the header and first data row
  • The last row has data, not a trailing blank line

Quick count: in Google Sheets, =COUNTA(A:A)-1 in any empty cell gives the data row count (subtract 1 for the header).


Gotchas

Do not paste as "values only". The default paste (Ctrl+V) is correct. "Paste special → values only" strips the tab delimiters and collapses everything into column A.

Clear existing data first if rows have shifted. If the sheet already has data and you're replacing it, select all existing content below the header (rows 2 onward) and delete those rows before pasting. Otherwise old rows below the new data remain.

Header row must stay. The bot reads sheets by positional column index — it does not look up column names. The header in row 1 is for human reference only, but it must remain in place so row 2 is the first data row.

Google Sheets may auto-format dates. If date columns (e.g. date in Time Entries) display as numbers after paste, select the column and set the format to Date (Format → Number → Date).

The reporting sheet is unaffected. This process only touches the database sheet. The reporting sheet pulls data via IMPORTRANGE and will reflect the new data automatically once the import is complete (allow a minute for IMPORTRANGE to refresh).


Re-running

The generator is fully idempotent — running it again produces a fresh consistent dataset. IDs are stable within a single run but will differ across runs (they are regenerated from 1 each time). Do not mix TSVs from different runs.