docs(adjustments): Shipped block on design doc + CLAUDE.md URL routes
Captures the 11-task implementation, 5 deviations (biggest: the CP1 pivot from Choices.js chip-multiselect to popover-checkbox filter UX after Konrad flagged the chip pattern as intrusive), 14 new adjustments-tab tests, and total code churn (~+1400 lines). CLAUDE.md URL Routes table gains two rows so future sessions surface /payroll/?status=adjustments and the bulk-delete endpoint. Feature ready for final whole-feature code review + batched push.
This commit is contained in:
parent
9bb9ede300
commit
269d86259a
@ -179,6 +179,8 @@ USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2
|
|||||||
| `/projects/report/csv/` | `project_batch_report_csv` | Admin: project batch report as CSV download |
|
| `/projects/report/csv/` | `project_batch_report_csv` | Admin: project batch report as CSV download |
|
||||||
| `/toggle/<model>/<id>/` | `toggle_active` | Admin: AJAX toggle active status |
|
| `/toggle/<model>/<id>/` | `toggle_active` | Admin: AJAX toggle active status |
|
||||||
| `/payroll/` | `payroll_dashboard` | Admin: pending payments, loans, charts |
|
| `/payroll/` | `payroll_dashboard` | Admin: pending payments, loans, charts |
|
||||||
|
| `/payroll/?status=adjustments` | `payroll_dashboard` | Admin: browse ALL payroll adjustments (filter by type, worker, team, status, date; group-by type/worker; bulk-delete unpaid; row actions open existing modals) |
|
||||||
|
| `/payroll/adjustments/bulk-delete/` | `bulk_delete_adjustments` | Admin: POST-only; delete multiple unpaid adjustments in one shot via fetch() with X-CSRFToken cookie |
|
||||||
| `/payroll/pay/<worker_id>/` | `process_payment` | Admin: process payment (atomic) |
|
| `/payroll/pay/<worker_id>/` | `process_payment` | Admin: process payment (atomic) |
|
||||||
| `/payroll/price-overtime/` | `price_overtime` | Admin: AJAX price unpriced OT entries |
|
| `/payroll/price-overtime/` | `price_overtime` | Admin: AJAX price unpriced OT entries |
|
||||||
| `/payroll/adjustment/add/` | `add_adjustment` | Admin: create adjustment |
|
| `/payroll/adjustment/add/` | `add_adjustment` | Admin: create adjustment |
|
||||||
|
|||||||
@ -555,3 +555,115 @@ Hand off to `superpowers:writing-plans`. Two design docs exist today:
|
|||||||
- `docs/plans/2026-04-23-adjustments-tab-design.md` (this doc — Feature 2)
|
- `docs/plans/2026-04-23-adjustments-tab-design.md` (this doc — Feature 2)
|
||||||
|
|
||||||
Recommended sequence: **Feature 1 first** (smaller, ~5-6 tasks; Choices.js patterns learned here can lift into Feature 2). Ship Feature 1, validate on production, then Feature 2's plan + implementation. Both design docs stay local until their respective implementations ship; then push everything together.
|
Recommended sequence: **Feature 1 first** (smaller, ~5-6 tasks; Choices.js patterns learned here can lift into Feature 2). Ship Feature 1, validate on production, then Feature 2's plan + implementation. Both design docs stay local until their respective implementations ship; then push everything together.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19. Shipped — 2026-04-23
|
||||||
|
|
||||||
|
Implementation complete. 11 tasks + 1 hard-pause checkpoint + 1 round of
|
||||||
|
Konrad feedback fixes. 65 tests passing (up from 47 pre-feature).
|
||||||
|
|
||||||
|
### Commit map
|
||||||
|
|
||||||
|
| Task | Commits | Scope |
|
||||||
|
|------|---------|-------|
|
||||||
|
| 1 | `97d8a69` | `type_slug` template filter (+ tests) |
|
||||||
|
| 2 | `a20a025` | CSS badge palette + foundational styles |
|
||||||
|
| 3 | `10d381e`, `89f109a` | Backend filter branch + stats; strengthened subquery test |
|
||||||
|
| 4 | `b450bd3`, `06b3315` | Tab markup + filter bar + flat table; pagination / a11y / N+1 fixes |
|
||||||
|
| 4* | `e088192`, `4c1cdb6` | Two multi-line `{# #}` comment hotfixes — see Deviations #2 |
|
||||||
|
| CP1 A | `b59eb31` | Row actions → modals + project link → History tab |
|
||||||
|
| CP1 B | `4f15e4b` | **Replaced Choices.js chip-multiselect with popover-checkbox filter UX** — see Deviations #1 |
|
||||||
|
| 5 | `0862805`, `e5d06f9` | Group-by type/worker + toggle + colour-accented headers; chevron + ordering polish |
|
||||||
|
| 6 | `03f177e`, `5f2e6d8`, `4c3e90f` | Bulk-delete endpoint; id-collision fix; **cascade logic fix** — see Deviations #3 |
|
||||||
|
| 7 | `6905703` | Team → Workers cross-filter |
|
||||||
|
| 8 | `c851b49` | Date picker single/range toggle + preset buttons |
|
||||||
|
| 9 | `7b71048` | Sortable column headers with URL state |
|
||||||
|
| 10 | `9bb9ede` | Empty-state card with recovery CTAs |
|
||||||
|
|
||||||
|
### Deviations from the original design
|
||||||
|
|
||||||
|
1. **Choices.js chip-multiselect → popover-checkbox filters.** The original
|
||||||
|
design (§3) specified Choices.js for Type/Workers/Teams multi-selects —
|
||||||
|
the same pattern used in the report page's retired modal. At Checkpoint 1
|
||||||
|
Konrad flagged that the chip-style rendering was intrusive once multiple
|
||||||
|
options were selected, dominating the filter bar. We replaced the
|
||||||
|
Choices.js widgets with pill-buttons that open popovers containing a
|
||||||
|
scrollable checkbox list + search + Select All / Invert / Clear. Reuses
|
||||||
|
Feature 1's `.filter-pill` / `.filter-popover` CSS vocabulary.
|
||||||
|
Implemented in `4f15e4b`.
|
||||||
|
|
||||||
|
2. **Multi-line `{# ... #}` comment bug, twice.** Django's `{# #}` comment
|
||||||
|
syntax is single-line only — multi-line blocks need
|
||||||
|
`{% comment %}...{% endcomment %}`. We shipped the bug in the Task 4
|
||||||
|
row partial (`e088192` fixed it) and then AGAIN in the Fix-A worker
|
||||||
|
cell (`4c1cdb6` fixed it). Both shipped into production-looking
|
||||||
|
renders, not caught by automated tests. Lesson: add a repo-wide
|
||||||
|
grep guard or a Django linter for this class of template bug.
|
||||||
|
|
||||||
|
3. **Bulk-delete cascade gap.** The original Task 6 spec's reference
|
||||||
|
implementation (`PayrollAdjustment.objects.filter(...).delete()`)
|
||||||
|
silently orphaned linked `Loan` rows and `priced_workers` M2M entries
|
||||||
|
when bulk-deleting adjustments of type "New Loan", "Advance Payment",
|
||||||
|
or "Overtime". The single-row `delete_adjustment` view had 30+ lines
|
||||||
|
of cascade logic the bulk view didn't use. Code review caught it.
|
||||||
|
Fix: extracted `_delete_adjustment_with_cascade(adj)` helper and
|
||||||
|
delegated both views to it — ensuring bulk and single-row have
|
||||||
|
identical semantics. Also added a 'has_paid_repayments' skip reason
|
||||||
|
in the JSON response so the UI can indicate why some rows were kept.
|
||||||
|
Implemented in `4c3e90f`.
|
||||||
|
|
||||||
|
4. **Row actions → modals (CP1 Fix A).** The original design §4 said row
|
||||||
|
actions "match the rest of the dashboard — NO expandable rows". We
|
||||||
|
interpreted this as table-to-page navigation (Worker name → `/workers/<id>/`,
|
||||||
|
View Payslip → `/payroll/payslip/<pk>/`). At CP1 Konrad clarified he
|
||||||
|
wanted in-place MODALS matching the Pending tab: worker name opens
|
||||||
|
`#workerLookupModal`, paid-row eye icon opens `#previewPayslipModal`,
|
||||||
|
project name goes to `/projects/<id>/#history` (History tab active).
|
||||||
|
Implemented in `b59eb31`; tiny tab-activation helper in
|
||||||
|
`projects/detail.html` picks up the URL hash.
|
||||||
|
|
||||||
|
5. **id collision.** Task 4 added `id="adjSelectAll"` to the table
|
||||||
|
header checkbox, but the Add Adjustment modal already used that id
|
||||||
|
for its Select-All anchor. `document.getElementById` returns only
|
||||||
|
the first match, so the modal's handler silently bound to the table
|
||||||
|
checkbox. Renamed the table's to `#adjTableSelectAll` in `5f2e6d8`.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
Added 14 tests in `AdjustmentsTabTests`:
|
||||||
|
|
||||||
|
- `test_admin_sees_adjustments_tab` — 200 + active_tab set
|
||||||
|
- `test_supervisor_forbidden` — non-admin redirected
|
||||||
|
- `test_type_multi_filter` — union on multi-value param (uses adj_total_count)
|
||||||
|
- `test_worker_multi_filter` — worker filter
|
||||||
|
- `test_team_filter_uses_subquery_no_inflation` — proves the subquery
|
||||||
|
pattern with 2 teams × 2 workers × 3 adjustments (naive would return 6)
|
||||||
|
- `test_status_filter_unpaid` — payroll_record__isnull filter
|
||||||
|
- `test_date_range_filter` — date__gte/lte
|
||||||
|
- `test_stats_scoped_to_filtered_set` — counts + sums respect filter
|
||||||
|
- `test_group_by_type` — buckets + net_sum + descending-magnitude ordering
|
||||||
|
- `test_group_by_worker` — buckets by worker_id
|
||||||
|
- `test_bulk_delete_only_affects_unpaid` — paid row survives
|
||||||
|
- `test_bulk_delete_requires_admin` — 403 for supervisors
|
||||||
|
- `test_bulk_delete_cascades_new_loan` — Loan + unpaid repayments gone too
|
||||||
|
- `test_bulk_delete_skips_loan_with_paid_repayments` — refuses, reports reason
|
||||||
|
- `test_team_worker_pairs_json_context_key` — raw Python list shape (not double-encoded)
|
||||||
|
|
||||||
|
Also extended existing tests:
|
||||||
|
- `test_group_by_type` gained a descending-magnitude ordering assertion
|
||||||
|
- `TypeSlugFilterTests` has 3 tests for the new template filter
|
||||||
|
|
||||||
|
### Net code churn
|
||||||
|
|
||||||
|
- `core/views.py`: ~+200 lines (filter branch + 2 helpers + bulk-delete view)
|
||||||
|
- `core/templates/core/payroll_dashboard.html`: ~+450 lines (tab + filter bar + popover markup + table + JS modules)
|
||||||
|
- `core/templates/core/_adjustment_row.html`: new file, ~120 lines
|
||||||
|
- `core/templatetags/format_tags.py`: ~+35 lines (`type_slug`, `money_abs`, `url_replace`)
|
||||||
|
- `static/css/custom.css`: ~+220 lines (badge palette + layout skeleton + popover extensions + colour-accented group headers + chevron rotation)
|
||||||
|
- `core/tests.py`: ~+380 lines (14 new adjustments tests + 3 type_slug tests)
|
||||||
|
- `core/urls.py`: +1 route
|
||||||
|
- Total: ~+1,400 lines added, ~-100 replaced/removed.
|
||||||
|
|
||||||
|
(Original estimate: ~960 lines. Actual: +44% — mostly from the popover-
|
||||||
|
checkbox filter rewrite, the bulk-delete cascade, and the cross-filter JS.)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user