feat: exclude fixed-salary managers from attendance pickers

This commit is contained in:
Konrad du Plessis 2026-05-15 19:36:33 +02:00
parent 482f88bb10
commit 65df9f817e
3 changed files with 45 additions and 5 deletions

View File

@ -105,13 +105,14 @@ class AttendanceLogForm(forms.ModelForm):
supervised_teams = Team.objects.filter(supervisor=self.user, active=True)
self.fields['workers'].queryset = Worker.objects.filter(
active=True,
teams__in=supervised_teams
).distinct()
teams__in=supervised_teams,
).exclude(pay_type='fixed').distinct()
# Only show teams this supervisor manages
self.fields['team'].queryset = supervised_teams
else:
# Admins see everything
self.fields['workers'].queryset = Worker.objects.filter(active=True)
self.fields['workers'].queryset = Worker.objects.filter(
active=True).exclude(pay_type='fixed')
self.fields['project'].queryset = Project.objects.filter(active=True)
self.fields['team'].queryset = Team.objects.filter(active=True)

View File

@ -3395,3 +3395,40 @@ class ManagerSalariedPayTypeRegistrationTests(TestCase):
def test_salary_is_additive(self):
from core.views import ADDITIVE_TYPES
self.assertIn('Salary', ADDITIVE_TYPES)
# === MANAGER / SALARIED PAY - attendance picker exclusion ===
# A pay_type='fixed' manager must be impossible to add to a WorkLog.
# They are excluded at the attendance picker + supporting maps so
# they can never be selected for / auto-checked into a WorkLog or
# counted in the attendance cost estimate.
class ManagerSalariedAttendanceExclusionTests(TestCase):
def setUp(self):
self.admin = User.objects.create_user('msa_admin', password='x', is_staff=True)
self.sup = User.objects.create_user('msa_sup', password='x')
self.daily = Worker.objects.create(
name='Daily Del', id_number='MSA-D', monthly_salary=Decimal('6000'))
self.mgr = Worker.objects.create(
name='Mgr Mo', id_number='MSA-M', monthly_salary=Decimal('40000'),
pay_type='fixed')
self.team = Team.objects.create(name='MSA Team', supervisor=self.sup)
self.team.workers.add(self.daily, self.mgr)
def test_attendance_admin_picker_excludes_fixed(self):
from core.forms import AttendanceLogForm
qs = AttendanceLogForm(user=self.admin).fields['workers'].queryset
self.assertIn(self.daily, qs)
self.assertNotIn(self.mgr, qs)
def test_attendance_supervisor_picker_excludes_fixed(self):
from core.forms import AttendanceLogForm
qs = AttendanceLogForm(user=self.sup).fields['workers'].queryset
self.assertIn(self.daily, qs)
self.assertNotIn(self.mgr, qs)
def test_team_workers_map_excludes_fixed(self):
from core.views import _build_team_workers_map
m = _build_team_workers_map(self.admin)
ids = m.get(self.team.id) or m.get(str(self.team.id)) or []
self.assertIn(self.daily.id, ids)
self.assertNotIn(self.mgr.id, ids)

View File

@ -586,7 +586,8 @@ def _build_team_workers_map(user):
teams_qs = Team.objects.filter(active=True).prefetch_related(
Prefetch(
'workers',
queryset=Worker.objects.filter(active=True),
# Managers (pay_type='fixed') never go on a WorkLog.
queryset=Worker.objects.filter(active=True).exclude(pay_type='fixed'),
to_attr='active_workers_cached',
)
)
@ -789,7 +790,8 @@ def attendance_log(request):
# (admins only — supervisors don't see the cost card)
worker_rates = {}
if is_admin(user):
for w in Worker.objects.filter(active=True):
# Managers (pay_type='fixed') never go on a WorkLog.
for w in Worker.objects.filter(active=True).exclude(pay_type='fixed'):
worker_rates[str(w.id)] = str(w.daily_rate)
# Build team→workers mapping so the JS can auto-check workers when a