38686-vm/docs/plans/parked-work.md
Konrad du Plessis 652168fe88 docs: capture dashboard/report audit pass landing
CLAUDE.md breadcrumb: '6 subsequent commits' → '13 subsequent
commits' with a one-paragraph summary of what's pending pull +
restart on production. Points at parked-work.md for the full
commit table.

parked-work.md:
- 'Pending pull-and-restart' table: added 18c75b2 (calendar
  month) and the 2e6b78d→c02edce audit-pass range as a single
  collapsed entry.
- 'Recently shipped' grew a detailed entry for the 18-finding
  audit pass at the top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:15:53 +02:00

10 KiB
Raw Blame History

Parked / deferred work

Updated 15 May 2026. A small index of features that are designed, half-built, blocked on input, or pending an operator step. When a fresh session opens, glance here first to see what's already on the workbench.


⚠ Pending pull-and-restart on production

Status: The big absences/SiteReport deploy (migrations 0013/0014/0015 + collectstatic + service restart) was completed on 14 May 2026 — /history/ no longer crashes, /absences/* is reachable, badge CSS is collected. ✓

Since that deploy, four small commits have shipped to origin/ai-dev that are NOT yet on production:

Commit What it does
4368e53 Fixes the absence-form team filter (workers were all disappearing when a team was selected — read input.value instead of data-attr)
02c6d4d Fixes the Reasons multi-checkbox dropdown stacking-context bug on /absences/ (lifted the filter card above the table card)
4b57cff Adds a Team filter to /workers/
398a5b2 Adds a Team filter to /history/ (and CSV export)
70fa085 Shows day name in Work Log Payroll modal header ("Friday, 15 May 2026")
d1d3e15 7 absence-feature polish follow-ups (dead code removal, N+1 fixes, DRY helpers, WCAG icon contrast)
18c75b2 Dashboard "Paid This Month" now actually uses calendar month (was 60-day rolling)
2e6b78dc02edce Dashboard + report numbers audit fixes — 18 findings, 6 commits: outstanding-total alignment, project-name dedupe, timezone.localdate(), off-by-one date windows, dead context removal, SQL-aggregate cost velocity, clearer labels, signed adjustment amounts, daily_rate documentation

To deploy these:

  • No migrations needed (pure template + view changes; no schema changes).
  • No collectstatic needed (no new CSS or JS files).
  • Just: ask Gemini in Flatlogic to git pull origin ai-dev and then sudo systemctl restart django-dev.service.

That's the whole sequence. Production should pick up all four improvements instantly. Rollback is git reset --hard <previous SHA> on the VM if anything looks wrong (no data migrations were involved, so rollback is safe).


Blocked on Konrad's input

Phase A.2 — Manual JournalEntry web UI

Status: Designed in the Site Work Logging brainstorm. Model class drafted in the design doc (not yet in core/models.py). Not yet built.

Blocked on: Q7 from the original brainstorm — "Who is Vi?" The answer drives whether JournalEntry needs a recipient or audience field (i.e. is this a public log, or does it have specific addressees like a client). Default if unanswered: no recipient field — entries are all-admin-readable.

Scope when unblocked: ~1 hour of work. Manual entry form at /journal/new/, list at /journal/, edit at /journal/<id>/edit/. Admin-only views. Lives on ai-dev (no webhook involvement).

Phase B — Letterly inbound webhook (integrations branch)

Status: Designed in the Site Work Logging brainstorm. Belongs on the integrations branch, not ai-dev.

Blocked on: Q5 from the original brainstorm — Konrad needs to share a sample Letterly webhook payload. Need to know:

  • Does it include the recording user (so we can map to a Django User)?
  • Does it include project / location metadata, or just the transcript?
  • Does it include an audio URL for link-back?
  • Does Letterly support custom fields (so a deep-link from /site-report/<id>/edit/ could embed a work_log_id that the webhook reads back)?

Scope when unblocked: ~3-4 hours including tests + one-off Letterly account setup. Adds a @csrf_exempt view at /webhooks/letterly/, JournalEntry row creation, HMAC body verification, shared-secret URL token.


Small polish follow-ups from the Absences feature

All 7 cleared on 15 May 2026 (commit d1d3e15). Kept the section header in place because more polish items will accumulate from future code reviews — when they do, append here. The shipped items are summarized under "Recently shipped" below.


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.

Recently shipped (for context, so a fresh session knows what just landed)

  • Dashboard + report numbers audit (commits 18c75b2c02edce, 15 May 2026): Konrad spotted that the home dashboard's "Paid This Month" actually showed the last 60 days. A thorough audit found 17 more issues across the home dashboard, payroll dashboard, and report. All 18 fixed in 7 commits: calendar-month math, outstanding totals aligned between dashboards, Project.name dedupe (was silently merging same-named projects), timezone.localdate() replacing timezone.now().date() (SAST drift fix), off-by-one on "last 7 / 60 days" windows, dead year_projects/year_teams context removed, _company_cost_velocity rewritten as single SQL aggregate, signed/coloured adjustment amounts on report tables (Bonus vs Deduction now visually distinct), "Outstanding Now" hero card subline now shows scope when filters active, dual daily_rate code paths documented in CLAUDE.md. 160 → 173 tests (+13 regression).
  • Absence-feature polish pass (commit d1d3e15, 15 May 2026): Seven small cleanups in one focused commit. (1) Deleted unused AbsenceQuickForm (Round C made it obsolete). (2) Extracted _build_team_workers_map(user) helper using Prefetch(..., to_attr=...), fixing N+1 on both attendance_log and absence_log (one query instead of one-per-team). (3) absence_list now calls the existing _user_can_log_absences helper. (4) Removed undefined var(--badge-neutral-bg) CSS wrapper — fallback hex was always winning anyway. (5) conflicting_worklogs() rewritten as a single query with Python-side pair-set filtering — 25-query → 2-query for a 5×5 form. (6) Extracted _apply_absence_filters(qs, request) shared by absence_list + absence_export_csv so adding a filter param now touches one place, not two. (7) site_report_detail.html paid-check icon uses text-success instead of background-color variable as foreground (WCAG contrast fix). 157/157 tests still green.
  • Day name in Work Log Payroll modal header (commit 70fa085, 15 May 2026): The modal opened from /history/ now shows "Friday, 15 May 2026" instead of "2026-05-15". Pure server-side strftime on the already-loaded log.date — zero overhead.
  • Team filters on /workers/ and /history/ (commits 4b57cff, 398a5b2, 15 May 2026): Both pages now have a Team dropdown in their filter row. /workers/?team=<id> filters by Team.workers M2M membership (who's CURRENTLY on the team); /history/?team=<id> filters by WorkLog.team FK (logs TAGGED with the team when created — different semantics, intentional). Both accept team=none for the "no team assigned" / "ad-hoc work log" case. CSV export at /history/export/ honours the same param. +8 regression tests.
  • Absences UX polish (commits 4368e53, 02c6d4d, 15 May 2026): Two production-found bugs fixed. (1) Absence-form team filter was hiding ALL workers when a team was selected — switched the JS to read <input name="workers">[value] directly instead of going through data-worker-id (proven attendance-form pattern). (2) Reasons multi-checkbox dropdown on /absences/ was rendering behind the table — lifted the filter card with position: relative; z-index: 10 so the whole card sits above its sibling table card in the stacking order. See the Coding Style section in CLAUDE.md for both gotchas.
  • Worker Absences feature (commits bf6f0a527fe05e, 14 May 2026): Complete absence-tracking system. 8 reason choices, optional project FK (auto-attributes paid-absence Bonus adjustments to the project), date-range logging with weekend toggles, supervisor scoping (admin sees all; supervisors see their teams). Standalone /absences/log/ form + "Submit + Log Absences" shortcut on attendance form. List/edit/delete + CSV export. Worker-detail "Absences" tab with YTD totals. Dashboard alert card "X absent in last 7 days". Bidirectional cascade with PayrollAdjustment via _sync_absence_payroll_adjustment helper (single-chokepoint design, transaction.atomic-wrapped). 12 commits pushed to origin/ai-dev. Test count: 85 → 149 (+64 tests). Migrations 0013_add_site_report, 0014_add_absence, 0015_absence_project are LIVE on production as of late 14 May 2026.
  • Phase A.1 — SiteReport (commit 864ae72, 14 May 2026): Model, migration 0013_add_site_report.py, form, two-step flow from attendance log, 16 new tests. CLAUDE.md updated with model summary + site_report_schema.py pattern section.
  • Pastel soft-fill payroll action buttons (commit 6c6ade9, 24 Apr 2026): unified Worker Lookup / Batch Pay / Add Adjustment / Price Overtime treatment.
  • Path A display-only rename (UX Polish Pass, Apr 2026): "New Loan" / "Advance Payment" / "Advance Repayment" display as "Loan" / "Advance" / "Advance Repaid" — but DB values are unchanged. See "UI-vs-DB naming drift" section in CLAUDE.md before writing any filter/comparison on PayrollAdjustment.type.

Original Site Work Logging brainstorm

The full Q1-Q13 question list and design rationale lives in ~/.claude/plans/prancy-painting-brook.md (local — deliberately not committed to the repo, since the parts that matter have been absorbed into CLAUDE.md and this file). If Konrad answers Q5 or Q7 later, refer back to that file for the original framing.