feat: ?pay_type= filter on /workers/ (managers/daily, display-only)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Konrad du Plessis 2026-05-16 13:40:40 +02:00
parent 45871225e1
commit a442658430
2 changed files with 55 additions and 0 deletions

View File

@ -3239,6 +3239,50 @@ class WorkerListTeamFilterTests(TestCase):
self.assertContains(resp, 'Bravo Team')
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'], '')
class WorkHistoryTeamFilterTests(TestCase):
"""The /history/ page accepts ?team=<id> to narrow to logs tagged
with that team, ?team=none for logs with no team set, and empty

View File

@ -1620,6 +1620,7 @@ def worker_list(request):
q = (request.GET.get('q') or '').strip()
status = request.GET.get('status') or 'active'
team_filter = (request.GET.get('team') or '').strip()
pay_type_filter = (request.GET.get('pay_type') or '').strip()
workers = Worker.objects.all()
if status == 'active':
@ -1642,6 +1643,15 @@ def worker_list(request):
elif team_filter.isdigit():
workers = workers.filter(teams__id=int(team_filter))
# === 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)
# Annotate days worked (distinct WorkLog dates) — shown in the table.
# `.distinct()` is also needed to avoid duplicate Worker rows when
# the team filter joins through the M2M (a worker on multiple teams
@ -1658,6 +1668,7 @@ def worker_list(request):
'q': q,
'status': status,
'team_filter': team_filter,
'pay_type_filter': pay_type_filter,
'teams_for_filter': teams_for_filter,
'total_count': workers.count(),
}