Admin tools

Settings  ›  Tools

One-shot maintenance jobs. These are safe to run multiple times — each tool is idempotent (it skips records that are already up to date) and won't overwrite data the admin has explicitly set.

Seed profile chips on the Job Board pipeline

Adds the default colored panel-profile chips (Snap 1.0", Snap 1.5", Lock variants, Standing Seam, G-Rib) to the ops_pipelines/main document so the kanban + job-detail pages can render the second chip on each card. Skips silently if profiles are already configured. Run this once after upgrading to v0.1.3.

Upgrade profiles to siding + work-type scoping

For pipelines created before profile chips became work-type-aware (v0.1.5 and earlier). This:
(1) Adds workTypes: ['re-roof','new-construction'] to the existing roofing profiles (Snap, Lock, Standing Seam, G-Rib) so they only show on roofing jobs.
(2) Adds three new siding profiles — Board and Batten, Cottage Panel, G-Rib Siding — scoped to the siding work type.
Existing custom profiles are left exactly as-is. Idempotent — safe to click again.

Backfill panel profile on imported jobs

Scans every job, and for any job missing panelProfile and roofType, copies those values from the yutzybid snapshot stored in the job's private/main sub-document. After this runs, the colored profile chip will appear on every imported job that had a recognizable profile in yutzybid. Jobs without a snapshot (manually added, or older imports that didn't capture one) are left untouched.

Re-pull attachments from yutzybid

Fixes two problems in one pass:
(a) Jobs with copyFailed attachment records (auto-import couldn't copy the PDFs server-side because of an IAM gap on the Cloud Functions runtime). This tool re-copies them from the browser using your admin credentials, then replaces the failure record with a successful one.
(b) Jobs that came over with no PDFs at all because the source estimate hadn't generated them yet at import time (e.g. Holcomb). For each yutzybid-imported job, looks up the live source estimate and copies any estimatePdfStoragePath / financialReportStoragePath / attachments[] that aren't already on the ops job.
Idempotent — files already present (matched by source path) are skipped.

Canonicalize ops_jobs.scheduledWeekStart

One-shot data migration. The v5.6.153 fix made the kanban robust to viewer timezone differences by storing scheduledWeekStart as a noon-UTC instant (instead of local-midnight Monday). Existing jobs were written under the old format. This tool rewrites each one to the new canonical value so the kanban can use exact matching everywhere. Safe to re-run — jobs already in canonical form are skipped.

Seed estimate aggregates

Reads every estimates and siding-estimates doc (excluding contractor-deleted ones), sums counts and totals, and writes the result to meta/estimateAggregates. Run this once after the Cloud Function trigger is deployed (firebase deploy --only functions from firebase-functions/). After this seeds, the aggregate doc is maintained automatically by the per-doc trigger — no further manual runs needed. Re-running this tool re-computes from scratch and overwrites whatever the trigger has accumulated, so only run again if you suspect drift.

Backfill ops_jobs.archived

Scans every ops_jobs doc and writes archived: false on any that don't have the field set. Pre-requisite for narrowing the active-jobs and kanban listeners from full-collection reads to where('archived','==',false) — which currently can't ship without this backfill because legacy imports don't have the field and the strict where-clause would silently drop them. Idempotent — safe to click twice.

Audit sold → kanban pairing

Scans every estimate (both roofing and siding) where sold AND soldAcknowledged are true and reports any that aren't currently rendering on the kanban. Three failure categories surfaced separately:

  • Never imported — opsImported is false. Use /seamless-ops/admin-import.html to re-import manually.
  • Orphan reference — opsImported=true but the ops_jobs/{opsJobId} doc no longer exists. Auto-import claim succeeded but the doc was later deleted or never finished writing. Re-import via admin-import.html.
  • Ghost (archived) — ops_jobs doc exists but archived is true. The Backfill button above only writes archived:false on docs missing the field; manually-archived docs need to be unarchived in Firestore or via job-detail.

Fill in missing job day counts

Every sold job lands on the calendar with a suggested number of work days, worked out from the size of the roof — steeper and more difficult squares count for more, and tear-off adds on top. Older jobs imported before that existed have no day count, which leaves their calendar bars a single day. This fills those in with the same calculation new jobs get. The numbers behind it live under Settings › Jobs & Job Board › How many days a job gets. Jobs that already have a day count are left alone, so it is safe to click twice.

Backfill crew job scope

Crew can no longer open estimates, because an estimate shows the sell price and the markups. Instead, each job now carries a price-free copy of the scope — roof type, panel profile, gauge, colour, squares, pitch, trim runs, accessories, tear-off and crew notes — which is what the job detail page, the Load Job checklist, the coil demand views and the report variance table read for a crew member. New and edited estimates build that copy automatically. Jobs that already existed do not have one yet, so until you run this once, crew see a thinner scope panel on older jobs. Safe to click twice — it skips anything already up to date.

Move supplier invoices out of crew reach

Supplier invoices matched to a job before you start Job Cost Analysis used to sit on the job record itself. Crew can read job records, so the amount you paid and a working link to the invoice PDF were reachable — not shown on any screen, but present in the data. New pairings already go somewhere only managers and admins can read. This moves the ones already saved. Safe to click twice; it skips jobs already moved.

Remove price PDFs from crew-readable job folders

When a sold estimate became a job, the estimate PDF and the internal Financial Breakdown were copied into the job's file folder. Crew can open files in that folder, so those two documents — including your cost build-up — were reachable there. New jobs now point at the original, which only managers and admins can open. This repoints the ones already saved and deletes the copies. Nothing is lost: the original stays where it always was. Safe to click twice.

Migrate order statuses to 3 levels

v5.6.200 collapsed the order-status list from six entries down to three: Not Ordered Yet / Ordered / Ready. The kanban and detail-page UI already render legacy values mapped on the fly, but until this runs the underlying field on each ops_jobs doc still carries the old id (e.g. material-ordered). This tool walks every job and rewrites: material-ordered & material-in → ordered; on-hold → not-ordered; done → ready. Already-new values are skipped.

Migrate PIN material to subcollection (Path B)

v5.6.161 moved contractor auth material off the parent contractor doc into contractors/{id}/private/auth (admin/server- only). This tool walks every existing contractor doc and copies pinHash / pinSalt / legacy pin into the subcollection, then sets pinSet: true on the parent. Does NOT delete the parent fields yet — run the Audit tool below first to verify completeness, then the Purge tool to clear them. Idempotent.

Audit PIN migration (Path B)

Read-only audit. Walks every contractor doc and reports any that either (a) have PIN material on the parent doc but no subcollection doc, or (b) have a subcollection doc that's missing a hash. Should return clean before running the Purge tool. Safe to re-run.

Purge legacy PIN fields from parent (Path B)

Final step. Deletes pin, pinHash, and pinSalt from every contractor doc whose subcollection auth material exists. After this runs, the parent contractor doc carries no PIN material at all — only the pinSet: true flag. Run only after the Audit returns clean. Idempotent.

Audit unauthorized admin grants

Lists everyone who has admin rights on their contractor record but isn’t on the built-in admin list (today that’s aaron@seamlessarthur.com). Run it whenever you want to confirm nobody has given themselves admin. If a name turns up that you don’t recognise, check the audit log for what they did, then take the rights away on the People page. This tool only reads — it changes nothing.

Seed leaderboard aggregates

Reads every estimates + siding-estimates doc, tallies per-company counts, and writes them to meta/leaderboardAggregates. Run once after deploying the new Cloud Functions in v5.6.184; subsequent estimate writes are maintained automatically. Idempotent — re-running just rebuilds the doc from current data.

Backfill hasPendingCorrection flag

For every ops_timeEntries doc that still carries a correctionRequest object but no hasPendingCorrection: true flag, set the flag so the daily checkPendingCorrections Cloud Function can find them via a where() query instead of a 90-day full scan. Idempotent; safe to re-run.

Failed attachment diagnostic

Scans every job for attachments whose copy from yutzybid failed. Groups them by job and shows the exact error message + the original yutzybid storage path each one was trying to copy from. Use this to figure out WHY a PDF didn't come over — common causes are the source file having been deleted in yutzybid, a malformed legacy URL, or a bucket-permission hiccup. Read-only.