diff --git a/CLAUDE.md b/CLAUDE.md index 62ce8d7..a50c50e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,14 +11,17 @@ Konrad verified 17 May 2026). `origin/ai-dev` HEAD `80d96d7` == prod (the only delta over the functional tip `4c25011` is doc breadcrumbs). Migrations `0016`/`0017` applied; `static/css/custom.css` collected. -**🔧 In progress — local only, NOT pushed:** removal of the -"Log Today's Work" / **SiteReport** feature (Konrad wants to rethink -it from scratch separately — work mix is shifting). Designed + -planned; executes under a HARD STOP (destructive migration -`0018_delete_sitereport` on the daily-use attendance path) — nothing -reaches origin until Konrad verifies locally. See the SiteReport -entry in `docs/plans/parked-work.md` + the capture doc -`docs/plans/2026-05-17-site-report-removed-capture.md`. +**🔧 In progress — local only, NOT pushed (HARD STOP):** removal of +the "Log Today's Work" / **SiteReport** feature (Konrad wants to +rethink it from scratch separately — work mix is shifting). +**Implemented locally — Tasks 1-3 complete:** model/table/UI/routes +deleted, migration `0018_delete_sitereport` drops `core_sitereport`, +post-attendance flow now returns to the dashboard, suite **193 OK**. +Still un-pushed and under a HARD STOP — nothing reaches origin until +Konrad verifies locally (destructive migration on the daily-use +attendance path). Design knowledge preserved for a future rebuild in +the capture doc `docs/plans/2026-05-17-site-report-removed-capture.md`; +see also the parked rebuild entry in `docs/plans/parked-work.md`. **🧊 Backburner — do NOT start in `ai-dev`:** Phase A.2 (manual JournalEntry UI) and Phase B (Letterly inbound webhook) are @@ -94,7 +97,6 @@ staticfiles/ — Collected static assets (Bootstrap, admin) — NOT in git ( - **ExpenseReceipt / ExpenseLineItem** — business expense records with VAT handling - **WorkerCertificate** — per-worker certifications (Skills, PDP, First Aid, Medical, Work at Height) with `valid_until` expiry and optional document upload. Unique per (worker, cert_type). Has `is_expired` and `expires_soon` (≤30 days) properties. - **WorkerWarning** — disciplinary records per worker with severity (verbal/written/final), reason, optional document. Ordered -date. -- **SiteReport** — optional 1:1 with `WorkLog`, captures what was DONE on site that day: weather, temperature_min/max (°C, IntegerField), free-form `notes`, and a flexible `metrics` JSONField with shape `{'counts': {key: int}, 'checks': {key: bool}}`. The metric KEYS live in `core/site_report_schema.py` (NOT in the model) — see the "SiteReport metric schema" section below for the rationale + how to add a new metric without a migration. Reverse accessor: `work_log.site_report` (1:1, raises DoesNotExist when absent — wrap with try/except or use `WorkLog.objects.filter(site_report__isnull=False)`). - **Absence** — per-worker dated record of a day not worked. 8 reason choices (Sick, Family Responsibility, Annual Leave, Personal/Unpaid Leave, Injury on Duty, Suspension, Absconded, Other). Optional `project` FK (SET_NULL). `is_paid` boolean (default False) — when ticked, the save flow auto-creates a Bonus PayrollAdjustment via `_sync_absence_payroll_adjustment(absence)` helper, inheriting the absence's project for cost-attribution. Linked via OneToOneField (`payroll_adjustment`). Unique per (worker, date) at DB layer. Permission scoping: admin (all) or supervisor (workers in their teams). ### Schema name-drifts to remember @@ -172,45 +174,16 @@ the generated migration. Precedent `0012`; this feature added `0017` - Loans have automated repayment deductions during payroll processing - Cascading deletes use SET_NULL for supervisors/teams to preserve historical data -## SiteReport metric schema (Apr 2026) — flexible JSON, single Python source +## SiteReport / "Log Today's Work" — REMOVED 17 May 2026 -**Why this pattern exists:** `SiteReport.metrics` is a `JSONField` with -shape `{'counts': {key: int}, 'checks': {key: bool}}`. The KEYS aren't -modelled as columns — they live in a single Python file -`core/site_report_schema.py`. To add a new metric (e.g. `cables_pulled`), -append one dict to `COUNT_METRICS` or `CHECK_METRICS` and redeploy. **No -database migration required.** The form auto-renders the new field; old -reports without that key just show 0 / unchecked. - -**Why that's safe:** -- The form (`SiteReportForm` in `core/forms.py`) iterates the schema - lists at `__init__` time and builds dynamic `IntegerField` / - `BooleanField` per metric. Form state ↔ JSON blob via `save()`. -- Old data is never touched. Removing a metric from the schema means - the form stops rendering it; the JSON still contains the historic - value. `label_for(key)` falls back to the snake_case key when a - retired metric is shown on the detail page. -- The metric LABEL is rendered via `label_for(key)` (in - `core/site_report_schema.py`) so renaming a label is also a one-line - edit (and **doesn't break old data** because the key is unchanged). - -**What to NEVER do:** rename a metric `key` that already has data — -historic reports will silently lose their value for that metric. If a -key MUST change, write a one-off Django data migration that walks -every `SiteReport` and renames the JSON key. - -**Where the metric KEYS show up:** -- `core/site_report_schema.py` — the source of truth (COUNT_METRICS, CHECK_METRICS, helpers) -- `core/forms.py::SiteReportForm.__init__` — reads the lists, builds dynamic form fields -- `core/forms.py::SiteReportForm.save` — serializes form data into the JSON blob -- `core/templates/core/site_report_edit.html` — iterates `count_field_pairs` / `check_field_pairs` from the view -- `core/templates/core/site_report_detail.html` — iterates `counts_display` / `checks_display` from the view - -**The two-step flow:** after `attendance_log` POST creates one or more -WorkLogs, the view redirects to `site_report_edit` for the most recent -log. The form has a "Skip" link to home — site reports are entirely -optional. WorkLogs without a SiteReport are completely valid historic -rows; they just don't show progress data on `/history/`. +**SiteReport / "Log Today's Work" — REMOVED 17 May 2026.** Feature +deleted (model/table/UI/routes; migration `0018_delete_sitereport`). +To be rebuilt from scratch later — see +`docs/plans/2026-05-17-site-report-removed-capture.md`, which preserves +the schema-as-Python design (flexible `metrics` JSONField, single +Python source of truth, no-migration metric adds) for the future +rebuild. Nothing site-report-related exists in the working app; the +post-attendance flow now simply returns to the dashboard. ## Absence-to-PayrollAdjustment cascade (May 2026) @@ -237,7 +210,7 @@ Edit / delete cascades: The "Submit + Log Absences" button on `/attendance/log/` lets admins jump from logging attendance straight to `/absences/log/` pre-filled with the same date, team, and project. Uses `next_action=log_absences` -POST param; default Submit keeps the existing SiteReport redirect. +POST param; default Submit returns to the dashboard. Permission scoping helper: `_absence_user_queryset(user)` in `core/views.py` is the single authority for "which absences can this user see/touch". Admin @@ -429,8 +402,6 @@ numbers on hot pages. | `/attendance/log/` | `attendance_log` | Log daily work with date range support | | `/history/` | `work_history` | Work logs table with filters. Query params: `?worker=`, `?project=`, `?team=` (digit = WorkLog.team FK match; `none` = logs with no team set), `?status=paid|unpaid`. CSV export at `/history/export/` honors the same filters. | | `/history/export/` | `export_work_log_csv` | Download filtered logs as CSV | -| `/site-report//edit/` | `site_report_edit` | Create-or-update the optional SiteReport for a WorkLog (auto-redirected here after `/attendance/log/` POST) | -| `/site-report//` | `site_report_detail` | Read-only view of the SiteReport (404 if none — use the edit URL to create) | | `/absences/log/` | `absence_log` | Admin/supervisor: log absences (date range, multi-worker). Reads `date`, `team`, `project` GET params for prefill from /attendance/log/'s "Submit + Log Absences" shortcut. | | `/absences/log/confirm/` | `absence_log_confirm` | Yellow conflict-warning page; per-row Remove-from-WorkLog checkboxes; reads pending data from session. | | `/absences/` | `absence_list` | Filtered list with pagination. Multi-select reason filter (`?reason=sick&reason=iod` etc.). Direct `project_id` filter. | diff --git a/docs/plans/parked-work.md b/docs/plans/parked-work.md index 72398d9..b327bcb 100644 --- a/docs/plans/parked-work.md +++ b/docs/plans/parked-work.md @@ -9,29 +9,22 @@ ## ⏸ Paused — ready to execute (not started, not pushed) -### Post-Attendance Flow v2 +### Site-progress logging — rebuild from scratch (parked) -**Status:** Brainstormed + designed + planned, **approved by Konrad**, -execution paused (Konrad's call, 15 May 2026). Design doc -`docs/plans/2026-05-15-post-attendance-flow-v2-design.md` (local -commit `110545b`), plan `docs/plans/2026-05-15-post-attendance-flow-v2-plan.md` -(local commit `29c36be`). **These 2 commits are local-only on -`ai-dev`, NOT pushed to origin** — Konrad wants nothing reaching the -working app until he's verified the flow locally. +The SiteReport / "Log Today's Work" feature was **removed 17 May +2026** (Konrad's call — work mix shifting, scope uncertain). The +post-attendance flow now simply returns to the dashboard. A future +rebuild starts fresh — see +`docs/plans/2026-05-17-site-report-removed-capture.md` (preserves the +schema-as-Python pattern, recovery pointers, and the now-superseded +`2026-05-15-post-attendance-flow-v2-*` prior thinking, which stays on +disk). -**What it does:** Replaces the forced post-attendance SiteReport -redirect with 3 explicit buttons (Log Work → dashboard / + Site -Journal / + Absences) + a "Save Site Journal + Add Absences" button -on the journal page. Renames user-facing "Site Report" → "Site -Journal" (display-only, Path-A — frees "Journal" for the parked -voice feature, which must now be called "Voice Notes"). - -**To resume:** new or same session → "execute the -post-attendance-flow-v2 plan". 4 tiny TDD tasks (~120 LOC), reuses -the proven Round C `next_action` pattern. **HARD STOP after Task 4 -— Konrad runs the manual verification checklist (in the design doc) -locally and explicitly approves BEFORE any push.** No migrations / -collectstatic — pure template + view change. +**Removal status (local only, HARD STOP — not pushed):** Tasks 1-3 +done — model/table/UI/routes deleted, migration +`0018_delete_sitereport` drops `core_sitereport`, suite **193 OK**. +Un-pushed pending Konrad's local verification (destructive migration +on the daily-use attendance path). --- @@ -121,19 +114,28 @@ the 36-commit push `d7015b9..4c25011`. investigate a possible date/locale or response-rendering nondeterminism in that test. **NOT introduced by this feature.** -> Note: the **Post-Attendance Flow v2** paused entry above is STILL -> paused / unchanged — this Manager/Salaried entry does not affect -> it. +> Note: the **Site-progress logging — rebuild from scratch** parked +> entry above is unaffected by this Manager/Salaried entry (it was +> superseded separately by the 17 May 2026 SiteReport removal). --- ## Production status — ✅ fully caught up (15 May 2026) Production (`https://foxlog.flatlogic.app/`) is deployed at -`origin/ai-dev` HEAD **`1d224bc`**. Everything is live: SiteReport -+ Absences feature, all UX polish, the team filters, the +`origin/ai-dev` HEAD **`1d224bc`**. Everything is live: the original +SiteReport + Absences feature, all UX polish, the team filters, the dashboard/report number audit (18 findings), and the -template-comment + subline-clarity fixes. +template-comment + subline-clarity fixes. (The 17 May Manager/ +Salaried bundle has since shipped too — see the "Shipped & live" +section above.) + +**In-progress local-only work (HARD STOP — not pushed):** the +SiteReport / "Log Today's Work" removal — Tasks 1-3 done, suite +**193 OK**, migration `0018_delete_sitereport` drops the table. +Un-pushed pending Konrad's local verify; see the "Site-progress +logging — rebuild from scratch (parked)" entry above + the capture +doc `docs/plans/2026-05-17-site-report-removed-capture.md`. **Deploy-ordering lesson learned (now in CLAUDE.md):** production runs `DEBUG=False` → Django's cached template loader holds compiled @@ -202,16 +204,13 @@ revisits. ## Defaulted (not blocking — flag if you disagree) -From Q9, Q4 of the Site Work Logging brainstorm: - -- **Q9 — photos on site reports.** Deferred to v2 of SiteReport. - Construction supervisors often want to attach a phone photo of - "plinths cast today" but it adds ~50 LOC + storage handling + - thumbnail rendering. v1 ships without. -- **Q4 — per-project metric templates.** Same metric set for all - projects in v1. If/when a non-solar-farm project lands and the - metrics diverge wildly, we add a `MetricTemplate` model. YAGNI - for now — `core/site_report_schema.py` is one Python file edit. +The Q9 (photos on site reports) and Q4 (per-project metric +templates) defaults from the Site Work Logging brainstorm are now +**obsolete** — the SiteReport feature was removed 17 May 2026. Both +were open design considerations for that feature; they are captured +(along with the rest of the design rationale) in +`docs/plans/2026-05-17-site-report-removed-capture.md` for whoever +rebuilds site-progress logging from scratch. ---