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 |
|
||||
| `/toggle/<model>/<id>/` | `toggle_active` | Admin: AJAX toggle active status |
|
||||
| `/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/price-overtime/` | `price_overtime` | Admin: AJAX price unpriced OT entries |
|
||||
| `/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)
|
||||
|
||||
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