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>
This commit is contained in:
parent
4aac2c1cf2
commit
45871225e1
482
docs/plans/2026-05-16-managers-paytype-filter-plan.md
Normal file
482
docs/plans/2026-05-16-managers-paytype-filter-plan.md
Normal file
@ -0,0 +1,482 @@
|
||||
# Managers Pay-Type Filter — Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task (in-session, fresh subagent + 2-stage review per task). Each subagent MUST use superpowers:test-driven-development.
|
||||
|
||||
**Goal:** Add a display-only `pay_type` filter so `Worker(pay_type='fixed')` managers are fast to find on `/workers/` and in the Add-Adjustment modal picker — no model/migration/URL/money-math changes.
|
||||
|
||||
**Architecture:** One new `?pay_type=` GET branch in `worker_list` mirroring the existing `?status=`/`?team=` pattern; one extra `<select>` in `workers/list.html`; a `data-pay-type` attribute + a small client-side show/hide `<select>` + JS handler in the Add-Adjustment modal (reusing the existing `.add-adj-worker` / team-quick-select pattern). Server-side worker querysets are NEVER narrowed for the modal (preserves the must-stay-payable invariant).
|
||||
|
||||
**Tech Stack:** Django 5.2.7, Python 3.13, SQLite local (`USE_SQLITE=true`), Bootstrap 5 templates, vanilla JS.
|
||||
|
||||
**Design doc:** `docs/plans/2026-05-16-managers-paytype-filter-design.md` (commit `4aac2c1`).
|
||||
|
||||
**Branch / baseline:** `ai-dev`, HEAD `4aac2c1`, **201/201 tests passing**. Builds on top of the **paused, un-pushed Manager/Salaried commits**.
|
||||
|
||||
**Test command (Git Bash, per CLAUDE.md):**
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2
|
||||
```
|
||||
Single class: append `.ClassName` → `core.tests.WorkerListPayTypeFilterTests`.
|
||||
|
||||
> ⛔ **HARD STOP after Task 4.** Do NOT `git push`, do NOT deploy. After all
|
||||
> 4 tasks pass locally, STOP and hand back to Konrad to run the manual
|
||||
> verification checklist in the design doc. Pushing happens only on
|
||||
> Konrad's explicit say-so, bundled with the rest of the paused
|
||||
> Manager/Salaried feature.
|
||||
|
||||
**Path-A reminder:** the DB value is `'fixed'` (used in querysets, the
|
||||
`data-pay-type` attribute, and `<option value>`); the user-facing label
|
||||
is "Managers (Salaried)". Never invert these.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: `/workers/` `?pay_type=` filter — view + tests
|
||||
|
||||
**Files:**
|
||||
- Modify: `core/views.py` — `worker_list` (currently lines 1606–1664)
|
||||
- Test: `core/tests.py` — new class `WorkerListPayTypeFilterTests`, inserted immediately before `class WorkHistoryTeamFilterTests` (currently line 3242)
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Insert this new class right before line 3242 (`class WorkHistoryTeamFilterTests(TestCase):`). It mirrors the existing `WorkerListTeamFilterTests` setup style (line 3183).
|
||||
|
||||
```python
|
||||
class WorkerListPayTypeFilterTests(TestCase):
|
||||
"""The /workers/ page accepts ?pay_type=fixed (managers only) and
|
||||
?pay_type=daily (daily workers only). No param = unchanged 'all
|
||||
pay types' behaviour. Display-only filter — no money math touched."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.admin = User.objects.create_user(
|
||||
username='ptadmin', password='pw', is_staff=True, is_superuser=True,
|
||||
)
|
||||
cls.daily = Worker.objects.create(
|
||||
name='Danny Daily', id_number='PT-D1',
|
||||
monthly_salary=Decimal('6000'), # pay_type defaults to 'daily'
|
||||
)
|
||||
cls.mgr = Worker.objects.create(
|
||||
name='Mary Manager', id_number='PT-M1',
|
||||
monthly_salary=Decimal('40000'), pay_type='fixed',
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.client.force_login(self.admin)
|
||||
|
||||
def test_pay_type_fixed_shows_only_managers(self):
|
||||
resp = self.client.get('/workers/?pay_type=fixed')
|
||||
names = [w.name for w in resp.context['workers']]
|
||||
self.assertIn('Mary Manager', names)
|
||||
self.assertNotIn('Danny Daily', names)
|
||||
self.assertEqual(resp.context['pay_type_filter'], 'fixed')
|
||||
|
||||
def test_pay_type_daily_shows_only_daily(self):
|
||||
resp = self.client.get('/workers/?pay_type=daily')
|
||||
names = [w.name for w in resp.context['workers']]
|
||||
self.assertIn('Danny Daily', names)
|
||||
self.assertNotIn('Mary Manager', names)
|
||||
|
||||
def test_no_pay_type_param_shows_both(self):
|
||||
# Regression: the default /workers/ behaviour must NOT change.
|
||||
resp = self.client.get('/workers/')
|
||||
names = [w.name for w in resp.context['workers']]
|
||||
self.assertIn('Danny Daily', names)
|
||||
self.assertIn('Mary Manager', names)
|
||||
self.assertEqual(resp.context['pay_type_filter'], '')
|
||||
```
|
||||
|
||||
**Step 2: Run the tests to verify they fail**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.WorkerListPayTypeFilterTests -v 2
|
||||
```
|
||||
Expected: FAIL — `KeyError: 'pay_type_filter'` (context key absent) and the `fixed`/`daily` filters not applied.
|
||||
|
||||
**Step 3: Implement the view change**
|
||||
|
||||
In `core/views.py::worker_list`:
|
||||
|
||||
(a) Read the param next to the existing `team_filter` line (currently line 1622):
|
||||
|
||||
```python
|
||||
team_filter = (request.GET.get('team') or '').strip()
|
||||
pay_type_filter = (request.GET.get('pay_type') or '').strip()
|
||||
```
|
||||
|
||||
(b) Add the filter branch immediately AFTER the `# === Team filter ===`
|
||||
block (right after the `elif team_filter.isdigit():` lines, currently
|
||||
~line 1643, BEFORE the `# Annotate days worked` comment):
|
||||
|
||||
```python
|
||||
# === Pay-type filter ===
|
||||
# Display-only narrowing by Worker.pay_type. 'fixed' = managers /
|
||||
# salaried staff; 'daily' = normal field workers. Any other value
|
||||
# (including absent) leaves the list unfiltered — the default view
|
||||
# is deliberately unchanged. DB value is 'fixed'/'daily' (Path-A;
|
||||
# the user-facing label is "Managers (Salaried)").
|
||||
if pay_type_filter in ('fixed', 'daily'):
|
||||
workers = workers.filter(pay_type=pay_type_filter)
|
||||
```
|
||||
|
||||
(c) Add the context key (in the `context = { ... }` dict, alongside
|
||||
`'team_filter': team_filter,`):
|
||||
|
||||
```python
|
||||
'pay_type_filter': pay_type_filter,
|
||||
```
|
||||
|
||||
**Step 4: Run the tests to verify they pass**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.WorkerListPayTypeFilterTests -v 2
|
||||
```
|
||||
Expected: PASS (3 tests, OK).
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add core/views.py core/tests.py
|
||||
git commit -m "feat: ?pay_type= filter on /workers/ (managers/daily, display-only)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: `/workers/` list template — filter dropdown
|
||||
|
||||
**Files:**
|
||||
- Modify: `core/templates/core/workers/list.html` — filter form (lines 35–63) + "Clear filters" condition (line 130)
|
||||
- Test: `core/tests.py` — add one method to `WorkerListPayTypeFilterTests`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Append this method to `WorkerListPayTypeFilterTests` (created in Task 1):
|
||||
|
||||
```python
|
||||
def test_list_renders_pay_type_dropdown_with_selection(self):
|
||||
# The filter row must render a pay-type <select> and mark the
|
||||
# active option selected so the dropdown reflects the URL.
|
||||
resp = self.client.get('/workers/?pay_type=fixed')
|
||||
self.assertContains(resp, 'name="pay_type"')
|
||||
self.assertContains(resp, 'Managers (Salaried)')
|
||||
# The 'fixed' option must be the selected one.
|
||||
self.assertContains(
|
||||
resp, '<option value="fixed" selected>Managers (Salaried)</option>',
|
||||
html=False,
|
||||
)
|
||||
```
|
||||
|
||||
**Step 2: Run to verify it fails**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.WorkerListPayTypeFilterTests.test_list_renders_pay_type_dropdown_with_selection -v 2
|
||||
```
|
||||
Expected: FAIL — `name="pay_type"` not found in response.
|
||||
|
||||
**Step 3: Implement the template change**
|
||||
|
||||
In `core/templates/core/workers/list.html`:
|
||||
|
||||
(a) The filter `<form>` row currently is `col-md-4` (search) + `col-md-3`
|
||||
(team) + `col-md-3` (status) + `col-md-2` (button) = 12. Rebalance to
|
||||
fit a 4th control. Change the column classes:
|
||||
- Search wrapper `col-md-4` → `col-md-3`
|
||||
- Team wrapper `col-md-3` → `col-md-3` (unchanged)
|
||||
- Status wrapper `col-md-3` → `col-md-2`
|
||||
- (new) Pay-type wrapper `col-md-2`
|
||||
- Button wrapper `col-md-2` → `col-md-2` (unchanged)
|
||||
(3+3+2+2+2 = 12)
|
||||
|
||||
So edit the Status wrapper from:
|
||||
```html
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small fw-semibold mb-1">Status</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="active" {% if status == 'active' %}selected{% endif %}>Active only</option>
|
||||
<option value="inactive" {% if status == 'inactive' %}selected{% endif %}>Inactive only</option>
|
||||
<option value="all" {% if status == 'all' %}selected{% endif %}>All workers</option>
|
||||
</select>
|
||||
</div>
|
||||
```
|
||||
to (note `col-md-3` → `col-md-2`, then a NEW pay-type block right after):
|
||||
```html
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Status</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="active" {% if status == 'active' %}selected{% endif %}>Active only</option>
|
||||
<option value="inactive" {% if status == 'inactive' %}selected{% endif %}>Inactive only</option>
|
||||
<option value="all" {% if status == 'all' %}selected{% endif %}>All workers</option>
|
||||
</select>
|
||||
</div>
|
||||
{# === Pay-type filter === #}
|
||||
{# DB values are 'daily'/'fixed' (Path-A); label is friendly. #}
|
||||
{# Empty value = 'All pay types' (unchanged default view). #}
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Pay type</label>
|
||||
<select name="pay_type" class="form-select">
|
||||
<option value="" {% if not pay_type_filter %}selected{% endif %}>All pay types</option>
|
||||
<option value="daily" {% if pay_type_filter == 'daily' %}selected{% endif %}>Daily workers</option>
|
||||
<option value="fixed" {% if pay_type_filter == 'fixed' %}selected{% endif %}>Managers (Salaried)</option>
|
||||
</select>
|
||||
</div>
|
||||
```
|
||||
Also change the Search wrapper `col-md-4` → `col-md-3` (one-word edit on
|
||||
line 36).
|
||||
|
||||
(b) Update the "Clear filters" visibility condition (currently line 130)
|
||||
from:
|
||||
```html
|
||||
{% if q or status != 'active' or team_filter %}<br><a href="{% url 'worker_list' %}">Clear filters</a>{% endif %}
|
||||
```
|
||||
to (add `or pay_type_filter`):
|
||||
```html
|
||||
{% if q or status != 'active' or team_filter or pay_type_filter %}<br><a href="{% url 'worker_list' %}">Clear filters</a>{% endif %}
|
||||
```
|
||||
|
||||
**Single-line `{# #}` check (CLAUDE.md gotcha):** the two `{# ... #}`
|
||||
comments added in (a) are each fully self-contained on one line — verify
|
||||
no multi-line `{#` was introduced:
|
||||
```bash
|
||||
grep -rn "^\s*{#" core/templates/core/workers/list.html | awk -F: '$0 !~ /#}/ {print}'
|
||||
```
|
||||
Expected: no output.
|
||||
|
||||
**Step 4: Run tests to verify pass**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.WorkerListPayTypeFilterTests -v 2
|
||||
```
|
||||
Expected: PASS (4 tests, OK).
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add core/templates/core/workers/list.html core/tests.py
|
||||
git commit -m "feat: pay-type dropdown on /workers/ filter row
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add-Adjustment modal — `data-pay-type` + "Managers only" toggle
|
||||
|
||||
**Files:**
|
||||
- Modify: `core/templates/core/payroll_dashboard.html` — worker rows (~line 1087), filter row (~line 1075–1084), JS (~after line 1999)
|
||||
- Test: `core/tests.py` — add one method to `ManagerSalariedPayUITests` (class at line 3727)
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Append this method to `ManagerSalariedPayUITests` (after
|
||||
`test_worker_list_shows_manager_label_for_fixed`, i.e. after current
|
||||
line 3759, before `def test_salary_immediate_payslip_has_no_zero_days_line`):
|
||||
|
||||
```python
|
||||
def test_add_adjustment_modal_has_pay_type_scaffolding(self):
|
||||
# The Add-Adjustment modal must (a) still include a manager in
|
||||
# the picker (the must-stay-payable invariant), (b) tag that
|
||||
# row with data-pay-type="fixed", and (c) render the client-side
|
||||
# "pay type" filter <select>. The toggle's runtime hide/show is
|
||||
# verified by Konrad's manual checklist (it's vanilla JS).
|
||||
mgr = Worker.objects.create(
|
||||
name='Modal Mgr', id_number='MM-1',
|
||||
monthly_salary=Decimal('40000'), pay_type='fixed')
|
||||
resp = self.client.get('/payroll/')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# (a) invariant: manager present in the modal picker queryset
|
||||
self.assertIn(mgr, resp.context['all_workers'])
|
||||
# (b) the row carries the DB pay_type value as a data attribute
|
||||
self.assertContains(resp, 'data-pay-type="fixed"')
|
||||
# (c) the client-side filter control exists
|
||||
self.assertContains(resp, 'id="addAdjPayTypeFilter"')
|
||||
```
|
||||
|
||||
**Step 2: Run to verify it fails**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.ManagerSalariedPayUITests.test_add_adjustment_modal_has_pay_type_scaffolding -v 2
|
||||
```
|
||||
Expected: FAIL — `data-pay-type="fixed"` / `id="addAdjPayTypeFilter"` not in response.
|
||||
|
||||
**Step 3: Implement the modal changes**
|
||||
|
||||
In `core/templates/core/payroll_dashboard.html`:
|
||||
|
||||
(a) Worker row — add `data-pay-type` to the `.form-check` wrapper.
|
||||
Currently (lines ~1086–1092):
|
||||
```html
|
||||
{% for w in all_workers %}
|
||||
<div class="form-check">
|
||||
<input class="form-check-input add-adj-worker" type="checkbox"
|
||||
name="workers" value="{{ w.id }}" id="addW{{ w.id }}">
|
||||
<label class="form-check-label" for="addW{{ w.id }}">{{ w.name }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
```
|
||||
Change the wrapper opening tag to:
|
||||
```html
|
||||
<div class="form-check" data-pay-type="{{ w.pay_type }}">
|
||||
```
|
||||
(only that one line changes; the `{{ w.pay_type }}` field is already on
|
||||
the `Worker` model — no view change).
|
||||
|
||||
(b) Filter row — add the pay-type `<select>`. Currently (lines
|
||||
~1075–1084):
|
||||
```html
|
||||
<div class="mb-2 d-flex flex-wrap gap-2 align-items-center">
|
||||
<select id="addAdjTeamSelect" class="form-select form-select-sm" style="max-width: 250px;">
|
||||
<option value="">Quick select by team...</option>
|
||||
{% for team in all_teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<a href="#" id="adjSelectAll" class="small text-primary text-decoration-none text-nowrap">Select All</a>
|
||||
<a href="#" id="adjDeselectAll" class="small text-muted text-decoration-none text-nowrap">Clear</a>
|
||||
</div>
|
||||
```
|
||||
Insert the new `<select>` immediately AFTER the `addAdjTeamSelect`
|
||||
`</select>` and BEFORE the `Select All` link:
|
||||
```html
|
||||
</select>
|
||||
<select id="addAdjPayTypeFilter" class="form-select form-select-sm" style="max-width: 170px;" aria-label="Filter picker by pay type">
|
||||
<option value="">All pay types</option>
|
||||
<option value="daily">Daily only</option>
|
||||
<option value="fixed">Managers only</option>
|
||||
</select>
|
||||
<a href="#" id="adjSelectAll" class="small text-primary text-decoration-none text-nowrap">Select All</a>
|
||||
```
|
||||
|
||||
(c) JS handler — add immediately AFTER the team quick-select handler
|
||||
(after the closing `}` of `if (addAdjTeamSelect) { ... }`, currently
|
||||
line 1999, before the `// === QUICK ADJUST BUTTON ===` comment at line
|
||||
2001):
|
||||
```javascript
|
||||
// Pay-type filter: show/hide picker rows by data-pay-type.
|
||||
// Display-only — selection state still lives on the checkboxes;
|
||||
// hidden rows keep whatever checked state they had. "All" (empty
|
||||
// value) reveals every row again. Same pattern as the pending-table
|
||||
// team/loan client-side filters.
|
||||
var addAdjPayTypeFilter = document.getElementById('addAdjPayTypeFilter');
|
||||
if (addAdjPayTypeFilter) {
|
||||
addAdjPayTypeFilter.addEventListener('change', function() {
|
||||
var want = this.value; // '', 'daily', or 'fixed'
|
||||
addAdjWorkerCheckboxes.forEach(function(cb) {
|
||||
var row = cb.closest('.form-check');
|
||||
if (!row) return;
|
||||
var rowType = row.getAttribute('data-pay-type') || '';
|
||||
row.style.display = (!want || rowType === want) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Single-line `{# #}` check** (no Django comments added here, but the
|
||||
JS `//` comments are fine — run the repo-wide guard anyway to be safe):
|
||||
```bash
|
||||
grep -rn "^\s*{#" core/templates/core/payroll_dashboard.html | awk -F: '$0 !~ /#}/ {print}'
|
||||
```
|
||||
Expected: no output.
|
||||
|
||||
**Step 4: Run tests to verify pass**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.ManagerSalariedPayUITests -v 2
|
||||
```
|
||||
Expected: PASS (all methods in the class, OK).
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add core/templates/core/payroll_dashboard.html core/tests.py
|
||||
git commit -m "feat: 'Managers only' client-side filter on Add-Adjustment picker
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Docs — parked-work + CLAUDE.md
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/plans/parked-work.md` — the paused Manager/Salaried entry
|
||||
- Modify: `CLAUDE.md` — one line under the "Manager / Salaried pay (May 2026)" section
|
||||
|
||||
**Step 1: Full regression run (no new test — docs only)**
|
||||
|
||||
```bash
|
||||
USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2
|
||||
```
|
||||
Expected: **205/205 OK** (201 baseline + 4 new). If any FAIL, STOP and
|
||||
fix before touching docs.
|
||||
|
||||
**Step 2: Update `docs/plans/parked-work.md`**
|
||||
|
||||
Find the paused Manager/Salaried Pay entry. Append a sentence noting the
|
||||
pay-type filter rides with it, e.g.:
|
||||
|
||||
> Also includes the display-only `?pay_type=` filter on `/workers/` +
|
||||
> the "Managers only" toggle on the Add-Adjustment modal (design
|
||||
> `2026-05-16-managers-paytype-filter-design.md`, plan
|
||||
> `2026-05-16-managers-paytype-filter-plan.md`). Same HARD STOP — all
|
||||
> un-pushed until Konrad's local verification.
|
||||
|
||||
(If no Manager/Salaried paused entry exists yet, add a short one under
|
||||
the "⏸ Paused — ready to execute" section following the existing
|
||||
entry's format.)
|
||||
|
||||
**Step 3: Update `CLAUDE.md`**
|
||||
|
||||
In the "Manager / Salaried pay (May 2026)" section, add one line:
|
||||
|
||||
> Finding them: `/workers/?pay_type=fixed` (display-only filter,
|
||||
> mirrors the status/team filters) and a "Managers only" client-side
|
||||
> toggle on the Add-Adjustment modal picker. Both are display-only —
|
||||
> the modal's `all_workers` queryset is NOT narrowed server-side
|
||||
> (preserves the must-stay-payable invariant).
|
||||
|
||||
**Step 4: Verify docs render sanely**
|
||||
|
||||
```bash
|
||||
grep -n "pay_type" docs/plans/parked-work.md CLAUDE.md
|
||||
```
|
||||
Expected: the new lines appear.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add docs/plans/parked-work.md CLAUDE.md
|
||||
git commit -m "docs: note managers pay-type filter (rides with paused Manager/Salaried)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⛔ HARD STOP — hand back to Konrad
|
||||
|
||||
After Task 4's commit:
|
||||
|
||||
1. Confirm `git status` is clean and `git log --oneline -5` shows the 4
|
||||
new commits on `ai-dev` on top of `4aac2c1`.
|
||||
2. Run the full suite one last time — expect **205/205 OK**.
|
||||
3. **Do NOT `git push`. Do NOT deploy.** Report the commit list + test
|
||||
count to Konrad and point him at the **Verification (manual, local)**
|
||||
section of `docs/plans/2026-05-16-managers-paytype-filter-design.md`.
|
||||
4. Pushing/deploying happens only when Konrad explicitly approves, and
|
||||
it goes out **bundled with the rest of the paused Manager/Salaried
|
||||
feature** (single push, his call).
|
||||
|
||||
## Notes
|
||||
|
||||
- **DRY/YAGNI:** the filter reuses the existing status/team filter
|
||||
pattern (view) and the existing `.add-adj-worker` + team-quick-select
|
||||
pattern (modal). No new abstraction, no helper — two call sites only.
|
||||
- **No migration:** `Worker.pay_type` already exists (migration
|
||||
`0016_worker_pay_type`). This plan adds zero migrations; if
|
||||
`makemigrations --check` ever flags something, STOP — something
|
||||
unintended changed.
|
||||
- **Why JS isn't unit-tested:** the show/hide is vanilla DOM mutation;
|
||||
the test asserts the *scaffolding* (data attribute + select element +
|
||||
server-side invariant). Runtime behaviour is on Konrad's manual
|
||||
checklist (design doc § Verification steps 4–6).
|
||||
Loading…
x
Reference in New Issue
Block a user