251 Commits

Author SHA1 Message Date
Konrad du Plessis
fb19655a1d docs: design for Pay Salary dashboard quick action
Konrad-approved: a home-dashboard admin Quick Actions tile that
deep-links /payroll/?action=pay-salary and auto-clicks the existing
paySalaryBtn (then strips the param). Reuses all existing machinery;
no view/model/URL change. Rides the same paused-bundle HARD STOP.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:52:58 +02:00
Konrad du Plessis
b397cdf46c docs: note Salary auto-scope picker (rides paused bundle)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:31:28 +02:00
Konrad du Plessis
bf3b63fc6b docs: clarify no synthetic change-event needed in Salary sync block
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:27:26 +02:00
Konrad du Plessis
31ee9e2e3c feat: type=Salary auto-scopes Add-Adjustment picker to managers
When type=Salary: set pay-type filter to Managers-only, hide daily
rows, and untick any selected daily worker so a Salary can never
silently target a daily worker. Re-applied on the Pay-Salary open
path (the show.bs.modal reset clears it first). Pure JS; verified by
manual checklist; suite stays 207/207.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:20:46 +02:00
Konrad du Plessis
0c705129f6 docs: TDD plan for Salary auto-scope picker (2 tasks, HARD STOP)
2 small tasks: (1) toggleProjectField() Salary sync + _paySalaryOpen
re-apply, (2) docs. JS-only — manual-checklist verified, suite stays
207/207. Nothing pushed until Konrad's local verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:14:06 +02:00
Konrad du Plessis
8f443faebc docs: design for Salary auto-scope picker (filter + auto-untick)
Konrad-approved: when Add-Adjustment type=Salary, auto-set the pay-type
filter to Managers-only, hide daily rows, and untick any selected daily
worker so a Salary can never silently target a daily worker. Pure JS,
hooks the toggleProjectField() chokepoint. Rides the same HARD STOP.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:12:03 +02:00
Konrad du Plessis
0d77d7228d docs: note managers pay-type filter (rides with paused Manager/Salaried)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 14:05:34 +02:00
Konrad du Plessis
18ec393c0a fix: reset Add-Adjustment pay-type filter on every modal open
Prevents a pre-checked quick-adjust worker from opening hidden behind a
stale 'Managers only'/'Daily only' filter. Display-only; no data impact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 14:01:34 +02:00
Konrad du Plessis
3a18ea008a feat: 'Managers only' client-side filter on Add-Adjustment picker
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:55:41 +02:00
Konrad du Plessis
fb8952a323 feat: pay-type dropdown on /workers/ filter row
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:49:04 +02:00
Konrad du Plessis
d949a01550 refine: document ?pay_type= param + add unknown-value regression test
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:45:24 +02:00
Konrad du Plessis
a442658430 feat: ?pay_type= filter on /workers/ (managers/daily, display-only)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:40:40 +02:00
Konrad du Plessis
45871225e1 docs: TDD plan for Managers pay-type filter (4 tasks, HARD STOP)
4 bite-sized TDD tasks: (1) worker_list ?pay_type= view+tests,
(2) /workers/ dropdown, (3) Add-Adjustment modal data-pay-type +
client-side toggle, (4) docs. ~205/205 expected. Nothing pushed until
Konrad's local verification — rides with paused Manager/Salaried.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:17:39 +02:00
Konrad du Plessis
4aac2c1cf2 docs: design for Managers pay-type filter (Approach A, display-only)
Konrad-approved design for a display-only ?pay_type= filter on /workers/
and a "Managers only" toggle on the Add-Adjustment modal picker. No
model/migration/URL changes; rides with the paused Manager/Salaried
feature's HARD STOP (nothing pushed until local verification).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:13:30 +02:00
Konrad du Plessis
4d06b83e30 docs: correct Salary verification step (manual amount entry, not auto-filled)
Final whole-feature review flagged the design doc's verification
checklist step 4 over-promised an auto-filled amount. Manual entry is
intentional; corrected so Konrad's local verification expectations match
actual behaviour. Docs-only, local-only — feature still NOT pushed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 22:00:26 +02:00
Konrad du Plessis
61e1f1492c docs: document Manager/Salaried pay; park feature pending Konrad local verify + 2 follow-ups 2026-05-15 21:45:12 +02:00
Konrad du Plessis
268a050397 polish: Salary multi-adjustment payslip-layout guard test; tighten list test; a11y badge contrast 2026-05-15 21:34:12 +02:00
Konrad du Plessis
862766f9b5 feat: pay_type UI (form/list/detail), Salary modal+entry, badge, clean Salary payslip
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 21:14:27 +02:00
Konrad du Plessis
d6f12e7dd1 polish: document team-filter Salary scoping; stricter report regression assertion 2026-05-15 20:52:30 +02:00
Konrad du Plessis
65b10e74ec feat: per-project Management/Salaried Cost report line + regression & netting guards 2026-05-15 20:35:11 +02:00
Konrad du Plessis
255ec82cef feat: add_adjustment Salary branch (project-required, pay-now/pending) 2026-05-15 20:15:30 +02:00
Konrad du Plessis
86b0cb9dd6 test: strengthen Task 4 absence-exclusion tests (parity + behavioral POST guard) 2026-05-15 20:05:15 +02:00
Konrad du Plessis
5fa3efcf64 feat: exclude fixed-salary managers from absence pickers 2026-05-15 19:55:42 +02:00
Konrad du Plessis
0f45d64eea fix: close inline team-map manager-exclusion gap + add cost-rate exclusion test 2026-05-15 19:46:34 +02:00
Konrad du Plessis
65df9f817e feat: exclude fixed-salary managers from attendance pickers 2026-05-15 19:36:33 +02:00
Konrad du Plessis
482f88bb10 fix: add missing 0017 AlterField migration for Salary choice; correct plan premise 2026-05-15 19:28:58 +02:00
Konrad du Plessis
b3a8147a60 feat: register 'Salary' PayrollAdjustment type as additive 2026-05-15 19:20:04 +02:00
Konrad du Plessis
3c471691f3 feat: add Worker.pay_type discriminator + is_salaried property 2026-05-15 19:08:39 +02:00
Konrad du Plessis
4dadb7cf23 docs: add Manager / Salaried Pay implementation plan (8 TDD tasks)
Task-by-task TDD plan: Worker.pay_type + migration, Salary additive
type, attendance/absence picker exclusions, add_adjustment Salary
branch, per-project salaried-cost report line + byte-for-byte
daily-numbers regression guard, UI, docs. Ends with a HARD STOP
before any push for Konrad's local verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:03:26 +02:00
Konrad du Plessis
325c59d4a1 docs: add Manager / Salaried Pay design (Approach A, approved)
Models a manager as a Worker with a pay_type discriminator, reusing the
existing loan/adjustment/payslip/payroll pipeline. New 'Salary'
adjustment type, project-attributed; managers excluded from
attendance/absence pickers so daily-worker math is provably untouched.
HARD STOP after implementation for local verification before any push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:57:53 +02:00
Konrad du Plessis
9713b89ede docs: park post-attendance-flow-v2 (designed+planned, execution paused)
Konrad paused execution. parked-work.md now has a "⏸ Paused —
ready to execute" section pointing at the design (110545b) + plan
(29c36be) commits, with the resume instruction and the hard-stop
constraint. All 3 commits are local-only on ai-dev — nothing
pushed until Konrad verifies the flow locally.
2026-05-15 13:11:15 +02:00
Konrad du Plessis
29c36bede7 docs: post-attendance flow v2 implementation plan
4 small TDD tasks (~120 LOC): display rename, attendance 3-button
branch, Site Journal save+absences button, docs. Reuses Round C
next_action pattern. HARD STOP after Task 4 — local verification
by Konrad before any push (UX change to a daily-use path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:05:48 +02:00
Konrad du Plessis
110545b11e docs: post-attendance flow v2 design
Replaces the forced post-attendance SiteReport redirect with 3
explicit buttons (Log Work → dashboard / + Site Journal / +
Absences) + a parallel "Save Site Journal + Add Absences" on the
journal page. Renames the user-facing "Site Report" → "Site
Journal" (display-only, Path-A; frees "Journal" for the parked
voice feature). Reuses the Round C next_action POST mechanism —
no model/migration/URL changes. NOT to be deployed until Konrad
verifies locally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:02:18 +02:00
Konrad du Plessis
d7015b9210 docs: move journal/voice work to explicit backburner track
Konrad's decision (15 May 2026): Phase A.2 (manual JournalEntry
UI) + Phase B (Letterly inbound webhook) are too complex to
interleave with normal app work — they'll be built and tested
offline on a separate track, not in ai-dev.

Verified there is NOTHING to remove or bypass: zero JournalEntry
model/views/urls/templates, zero Letterly/webhook/@csrf_exempt
code anywhere on ai-dev, latest migration is 0015. The working
app was already 100% clean of journal/voice code — these features
never left the design-doc stage.

Doc changes:
- parked-work.md: "Blocked on Konrad's input" section replaced
  with "🧊 Backburner — separate offline track", with an explicit
  "do NOT start in ai-dev" warning and the nothing-to-remove
  verification recorded.
- CLAUDE.md breadcrumb: reframed from "parked pending Q5/Q7
  answers" (implies ready-to-go once answered) to "deliberately
  deferred to offline track — do not pick up as normal feature
  work".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 11:53:04 +02:00
Konrad du Plessis
5162db966a docs: production fully deployed at 1d224bc + capture deploy-ordering lesson
Production caught up — all 14 pending commits live after a
second service restart (the first restart ran before the code
reached the target commit; DEBUG=False's cached template loader
held the old templates until restarted again).

CLAUDE.md:
- 'What's mid-flight' breadcrumb: no longer says pending deploy;
  now states production is at 1d224bc and fully live.
- Flatlogic Deployment section: new '⚠ DEPLOY ORDERING' bullet
  documenting that production runs DEBUG=False → cached template
  loader → restart MUST come after the pull, and template-only
  changes still need a restart (unlike DEBUG=True local dev).
  Includes the symptom ('git log shows right commit but page
  looks old') and the fix (restart again).
- Bumped the {# #} bit-us count + added a grep sanity-check
  one-liner (from the prior commit, retained).

parked-work.md: 'Pending pull-and-restart' section replaced with
'Production status — fully caught up'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 11:29:26 +02:00
Konrad du Plessis
1d224bc01b fix(templates): convert 8 broken multi-line {# #} comments + clarify cryptic sublines
CLAUDE.md gotcha #1 strikes again — the dashboard audit pass added
7 multi-line {# ... #} comment blocks across index.html, report.html,
and pdf/report_pdf.html. All rendered as literal text on the live
pages (Konrad screenshotted them). Also caught an old one in
admin/base_site.html that was technically broken syntax but
non-rendering (outside any block). All 8 converted to
{% comment %}{% endcomment %}.

CLAUDE.md updated:
- Bumped the bit-us count (4 → confirmed 4 + 5 + 7 across three
  features). Added a grep-one-liner sanity check that finds broken
  multi-line {# blocks across all templates so future passes can
  spot-check before committing.

Cryptic hero-card sublines on /report/ clarified (Konrad asked
what they mean):
- "as of 08:13" → "Live total at 08:13 today · for <scope>" with
  hover tooltip explaining the snapshot semantics.
- "Company Avg / Working Day" / "/ Month" labels renamed to
  "Avg Labour Cost / Working Day" / "/ Month". Sublines simplified
  to "Lifetime average across all crews" / "Daily figure × 30.44
  days". Both gain hover tooltips that explain the math and the
  "current pay rates" basis.

Pure template + docs change. 173/173 tests still passing
(no test changes — these are cosmetic fixes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:23:20 +02:00
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
Konrad du Plessis
c02edce0b9 docs(CLAUDE.md): daily_rate semantics + dual-code-path note
Documents two related foot-guns that bit recent audits:

- `Worker.daily_rate` is computed LIVE from `monthly_salary`; it's
  never snapshotted onto a WorkLog row. So historical cost totals
  inflate retroactively when a worker gets a raise. The new
  "at current pay rates" subline on the report hero cards
  (commit 4186603) is the visible half of this convention. Future
  audits should read this note before deciding "this can't be right".

- Two code paths compute the same formula: Python property and a
  SQL `Sum(F('workers__monthly_salary') / Decimal('20'))`. They
  produce identical results in normal use but could drift by 1
  cent on edge-case rounding. `CompanyCostVelocitySQLAggregateTests`
  is the regression test that would catch a real divergence.

Findings 2 + 11.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:11:19 +02:00
Konrad du Plessis
8f81e5ab94 refactor(report): signed adjustment amounts + filter-attribution caveat
Three related changes to the executive payroll report:

1. Adjustment Summary table and Worker Breakdown table now render
   deductive types (Deductions, Loan Repayment, Advance Repayment)
   as "-R 500.00" in muted red. Before, they showed the same way as
   bonuses — which read as "everyone gets richer" when a deduction
   was actually shrinking net pay. New context keys:
   - `adjustment_totals[i]['sign']` and `['is_deductive']`
   - `active_adj_headers` (list of {label, is_deductive}) replaces
     the parallel `active_adj_labels`/`active_adj_types` lists for
     templates. The originals are still emitted for any external
     consumer.
   - `worker_breakdown[i]['adj_values']` now contains
     {'amount', 'is_deductive'} dicts instead of bare Decimals.
   Templates updated: report.html + pdf/report_pdf.html.

2. "Total Paid Out" hero card on /report/ now shows a small asterisk
   + tooltip when project/team filters are active, explaining that
   a PayrollRecord touching the filtered scope is summed at its
   FULL amount — not just the project-attributable portion. Cheap
   label approach; the proper per-project attribution would need
   proportional splitting across each record's work_logs (deferred).
   New context key `total_paid_filter_caveat: bool`.

3. (No code change — Finding 6 was already satisfied by commit 1's
   `outstanding_by_project_sorted` rewrite, but the regression test
   protects the sort order going forward.)

Findings 3, 4, 6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:10:36 +02:00
Konrad du Plessis
4186603bcb ux(report,dashboard): clearer labels for paid/outstanding/avg
Pure-template label cleanups on /report/ — no math changes, just
clearer wording for the non-developer reader. Plus one consistency
fix on the payroll dashboard.

- "Outstanding Now" hero card now shows a "scoped to ..." subline
  when project/team filters are active (so it's not read as a
  company-wide figure when it's actually scoped). Finding 5.
- "Paid This Period" hero card subline adds "includes adjustments"
  to head off confusion vs the day-rate-only Labour Cost tables.
  Finding 10.
- "FoxFitt Avg / Day" + "FoxFitt Avg / Month" renamed to
  "Company Avg / Working Day" / "Company Avg / Month", with a
  subline that calls out the "at current pay rates" caveat
  (a worker's daily_rate is computed live from monthly_salary,
  so retroactive raises inflate historical totals). Findings 2 + 15.
- "Labour Cost by Project" + "Labour Cost by Team" tables: header
  renamed to "Day-Rate Cost" with a tooltip clarifying it excludes
  adjustments. Finding 10.
- Worker Breakdown table: footnote explaining that "Days" and
  "Total Paid" can disagree within a single period when a worker
  is paid for previous-period work. Finding 9.
- Payroll dashboard chart data: dropped the `worker__active=True`
  pre-filter on the per-worker breakdown queries so the SQL matches
  `recent_payments_total` (which has no active filter). The outer
  loop still iterates active workers only — this is a SQL-side
  consistency fix, not a behaviour change. Finding 18.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:04:55 +02:00
Konrad du Plessis
3ef6db71c9 refactor(report): drop dead year_projects context + SQL cost velocity
Two unrelated cleanups in `_build_report_context` and the helper next
to it.

- Removed `year_projects`, `year_teams`, and `current_year` from the
  report context dict. No template ever rendered them — they were
  added 2026-04 as part of an executive-report design that never
  shipped that section. Each render fired 2 extra GROUP BY queries
  for nothing.

- `_company_cost_velocity` no longer loops every (work_log × worker)
  pair in Python. Single SQL aggregate (`Sum(monthly_salary / 20)`)
  instead — one round-trip regardless of dataset size. Old behaviour
  loaded the entire WorkLog table + M2M into memory for the hero KPI
  card. Regression test (`test_sql_aggregate_matches_python_loop`)
  uses the old Python loop as the expected oracle.

Findings 14 + 16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:01:48 +02:00
Konrad du Plessis
e797a71b94 fix(dashboard,report): timezone.localdate + off-by-one date windows
Two related foot-guns on the admin dashboard and payroll dashboard:

1. Every `timezone.now().date()` call returned the date in UTC, not
   in Africa/Johannesburg. Between 22:00 and midnight SAST that's the
   NEXT calendar day — so the "today" the dashboard thought it was
   could be ahead of what the user sees on the clock. Now uses
   `timezone.localdate()` which respects `settings.TIME_ZONE`.
   Same fix for `datetime.date.today()` calls — those used the
   server's system clock, which on the production VM is set to UTC.

2. "Absences (last 7 days)" and "Paid (Last 60 Days)" both subtracted
   the FULL window length and combined it with `>=`, producing N+1
   inclusive days. E.g. `today - timedelta(days=7)` with `date__gte`
   spans 8 calendar days, not 7. Now subtract N-1 so the windows are
   exactly N days. Regression test: DateWindowOffByOneTests.

Findings 12 + 13.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:59:12 +02:00
Konrad du Plessis
2e6b78d28a fix(dashboard): align outstanding totals + project-name dedupe
The home dashboard and payroll dashboard used to disagree on
"outstanding payments" because the home version included inactive
workers' unpaid wages while the payroll dashboard's per-worker loop
only iterated active workers. Symptom was the same field showing two
different R-amounts depending on which page you opened first.

Also fixes the Outstanding-by-Project card silently merging two
projects when they share a name (it was keyed by project_name).

- `_compute_outstanding` now defaults to active workers only.
  Pass `include_inactive_workers=True` to surface deactivated-worker
  liabilities (rare; usually means a forgotten payment).
- Output is keyed by project_id (with name as data) so two projects
  with identical names stay as separate rows.
- New `outstanding_by_project_sorted` list — pre-sorted by amount
  desc — replaces the dict iteration in templates.
- "Active Loans" card on the home dashboard renamed to
  "Active Loans & Advances" so the label matches its data (which
  already summed both loan_types).
- Regression tests: ComputeOutstandingActiveScopeTests +
  ComputeOutstandingProjectIdKeyingTests.

Findings 1, 7/17, 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:55:49 +02:00
Konrad du Plessis
18c75b2bce fix(dashboard): 'Paid This Month' actually uses calendar month
The dashboard card labeled 'Paid This Month' was summing the
last 60 days of PayrollRecords — identical to the payroll
dashboard's 'Paid (60D)' card. Misleading at best, wrong at
worst when explaining the dashboard to a non-developer.

Now filters by date__year + date__month (current calendar month
only). Added 3 regression tests: excludes 45-day-old payment,
includes 1st-of-month payment, returns 0 cleanly when nothing
paid yet this month.

Found during Konrad's 15 May audit of dashboard numbers.
2026-05-15 01:46:47 +02:00
Konrad du Plessis
9bd0e8541d docs: capture absence polish-pass landing + refresh deploy counter
CLAUDE.md breadcrumb: 'subsequent UX polish' → '6 subsequent
commits' so the next session can see at a glance how much is
pending pull-and-restart on production.

parked-work.md:
- 'Small polish follow-ups' section: 7 items cleared on 15 May;
  section header retained as a landing pad for future cleanups.
- 'Pending pull-and-restart' table: added rows for 70fa085
  (day-name in modal) and d1d3e15 (polish pass) so the deploy
  checklist is current.
- 'Recently shipped' grew two new entries at the top: the polish
  pass and the day-name modal change.
2026-05-15 01:11:13 +02:00
Konrad du Plessis
d1d3e15444 chore(absences): 7 polish follow-ups from code review
Small cleanups tracked in docs/plans/parked-work.md:

1. Delete dead AbsenceQuickForm class — Round C replaced the per-row
   ✗ modal paradigm with the "Submit + Log Absences" button, but the
   form class never got wired up. No view, URL, template, or test
   ever referenced it.
2. Single-query team_workers_map via shared _build_team_workers_map
   helper. Previously fired one SELECT per team because .filter(
   active=True) on a prefetched M2M bypasses the prefetch cache.
   Now uses Prefetch(to_attr='active_workers_cached'). Both
   attendance_log() and absence_log() use the same helper.
3. absence_list permission check now uses _user_can_log_absences
   instead of duplicating the same `is_admin OR supervised_teams`
   logic inline.
4. Drop misleading var(--badge-neutral-bg, …) wrapper in custom.css —
   the variable isn't declared so the fallback always wins. Use the
   hex directly.
5. conflicting_worklogs() N+1 → single query: was firing one SELECT
   per (worker, date) pair (25 queries on a 5×5 form). Now 2 queries
   total via .filter(date__in=…, workers__in=…) + Python-side pair
   set check.
6. Extract _apply_absence_filters helper — absence_list and
   absence_export_csv were duplicating the same 7-param filter block
   (with a TODO comment to factor it out). Now structurally enforced
   in one place; list view keeps the raw param read-back for
   template-context dropdown preselection.
7. Replace style="color: var(--badge-bonus-bg)" with class="text-success"
   on the paid-check icon in site_report_detail.html — same WCAG
   contrast bug we fixed on the absence templates (background colour
   used as foreground).

All 157 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:09:44 +02:00
Konrad du Plessis
70fa085886 ux(history): show day name in Work Log Payroll modal header
date in the modal header was "2026-05-15" — now reads
"Friday, 15 May 2026". Server-side strftime on the already-loaded
log.date — zero DB / compute overhead. Touches only the modal's
date field; other date fields in the JSON (paid_date,
pay_period_*) still use the ISO format because they don't render
into a human-facing header.
2026-05-15 00:57:38 +02:00
Konrad du Plessis
bde6f24bb1 docs: refresh CLAUDE.md + parked-work for 15 May session wins
CLAUDE.md changes:
- 'What's mid-flight' breadcrumb updated: SiteReport/Absences
  migrations are LIVE on prod; only the 4 latest UX commits await
  a pull-and-restart (no migration / collectstatic needed).
- URL Routes table entries for /workers/ and /history/ now document
  the new ?team= filter (and team=none for unassigned / no-team cases).
- Worker Management UI inline description mentions the team filter
  + Absences tab on the worker detail page.
- Two new Coding Style gotchas captured: (1) Bootstrap dropdowns
  inside .card elements get clipped by sibling cards — fix is to
  lift the wrapping card with position:relative + z-index;
  (2) JS reading from data-worker-id was unreliable on production —
  read input[name="workers"][value] directly (the attendance-form
  pattern that's been working for years).

parked-work.md changes:
- Pending-deploy section rewritten: the BIG deploy (migrations +
  collectstatic) is DONE; only 4 small commits await a pull +
  service restart.
- 'Recently shipped' grew two new entries: the team filters on
  /workers/ + /history/, and the two absences UX polish fixes.
- Updated timestamp to 15 May 2026.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 00:50:47 +02:00
Konrad du Plessis
398a5b21ab feat(history): add Team filter to /history/ page (and CSV export)
Mirrors the team filter just added to /workers/. WorkLog.team is a
nullable FK, so the filter accepts:
- empty   → all logs (default)
- digit   → logs tagged with that team
- 'none'  → logs with no team set (ad-hoc attendance)

Filter row reflowed to col-md-3 col-lg-2 so all four selects fit on
a single row on wide screens; mobile stacks them. CSV export link
now passes &team=… through. Supervisors only see teams they
supervise in the dropdown.

4 regression tests covering filter narrowing, no-team match,
empty=show-all, and filter_params round-trip for the List/Calendar
toggle links.
2026-05-15 00:47:15 +02:00
Konrad du Plessis
4b57cffb77 feat(workers): add team filter to /workers/ page
New ?team=<id> URL param narrows the worker list to that team's
members via the Team.workers M2M. ?team=none filters to workers
not assigned to any team. Default (empty) still shows all
matching workers across all teams.

UI: new "Team" dropdown in the filter row, between Search and
Status. Lists active teams alphabetically. Layout reflowed to
col-md-4 / col-md-3 / col-md-3 / col-md-2.

Konrad's checkpoint feedback: "in the worker page - can i have a
filter for teams so i can easely see who is in what team".

4 regression tests covering no-filter, by-team, no-team, and
dropdown options.
2026-05-15 00:34:35 +02:00