mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-27 22:45:03 +00:00
Extends the #414 upload-and-parse work (does not rewrite it) with a per-row contractor, an optional stage, and a setup-completeness gate on the import page. New ADR-0019 records the design. Still writes nothing. CHANGE 1 — template + parser expected fields Final column order: Property identifier, Workstream, Contractor, Stage, Work order reference (optional), Forecast end date (optional). Contractor is required; Stage is optional (blank falls back to the workstream's first stage at commit). buildImportTemplateCsv, its worked example and the field docs are updated. The raw parser stays header-agnostic — required/optional is enforced in mapping (#415) and validation/commit (#417), not here — with a test proving a file missing the required Contractor column still parses. CHANGE 2 — setup-completeness gate (the substantive feature) src/lib/projects/import/readiness.ts: a pure computeImportReadiness(facts) plus a read-only loadImportReadiness(projectId) round trip. Rule: every project_workstream must have >=1 project_workstream_stage AND >=1 project_workstream_contractor, and the project must have >=1 workstream. Evidence requirements (#412) are advisory and excluded from the gate. The helper returns per-workstream detail (which are incomplete, and why) and is built to be reused by #413's final wizard step — noted in a comment. The import page loads readiness and, when not ready, renders an ImportSetupIncomplete state (lists the incomplete workstreams with the reason, links to project setup) instead of the drop zone — not a 404, not a silent empty zone. When ready, the drop zone renders as before. The gate applies on every entry: the route stays reachable standalone, and a comment notes #413 will also route the wizard into it (that wiring is out of scope here). CHANGE 3 — ADR-0019 Records: import is the final, setup-gated wizard step; contractor is supplied per row and must match a contractor already assigned to the workstream (contractor creation is out of scope — the existing "invite company to Ara" flow owns it); stage is optional per row with first-stage fallback. States the trade-off vs the replaced #417 design, which derived the contractor from the workstream's single assignee — abandoned because project_workstream_contractor is a collection, so one workstream's properties can be split across contractors, which a single-assignee lookup cannot express. Notes on the readiness query count(distinct project_workstream_stage.id) / (…contractor.id) over two left joins — the distinct is load-bearing because joining both children at once makes a stages x contractors cartesian product a plain count(*) would over-count. count(distinct) returns a bigint that node-postgres hands back as a string, so each count is coerced with Number(); a loader test drives a mocked db to prove that coercion. Production-DB safety No migrations, seeds or writes. loadImportReadiness is read-only SELECTs and is not executed here. Unit tests mock @/app/db/db (vi.mock) and open no connection; the pure readiness logic is tested with in-memory fixtures. Full suite: 575 passing. tsc and eslint clean. authz modules and src/app/projects/page.tsx untouched (other branches own them). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| adr | ||
| backend-asks | ||
| design | ||
| runbooks | ||
| wip | ||
| wireframes/ara-projects | ||