diff --git a/core/forms.py b/core/forms.py index fe7c0fa..338451a 100644 --- a/core/forms.py +++ b/core/forms.py @@ -660,7 +660,7 @@ class AbsenceLogForm(forms.ModelForm): help_text='Optional — narrows the worker list below.', ) workers = forms.ModelMultipleChoiceField( - queryset=Worker.objects.filter(active=True), + queryset=Worker.objects.filter(active=True).exclude(pay_type='fixed'), widget=forms.CheckboxSelectMultiple, ) @@ -699,7 +699,7 @@ class AbsenceLogForm(forms.ModelForm): active=True, teams__supervisor=user, teams__active=True, - ).distinct() + ).exclude(pay_type='fixed').distinct() ) # Project dropdown — only projects this supervisor is assigned to. # Mirrors the AttendanceLogForm supervisor scoping pattern. @@ -840,7 +840,7 @@ class AbsenceEditForm(forms.ModelForm): def __init__(self, *args, user=None, **kwargs): super().__init__(*args, **kwargs) # Default: every active worker. Admins (staff/superuser) keep this list. - self.fields['worker'].queryset = Worker.objects.filter(active=True) + self.fields['worker'].queryset = Worker.objects.filter(active=True).exclude(pay_type='fixed') # Project is optional — leave blank for non-project absences. self.fields['project'].required = False # Supervisor scope: when a non-admin opens the edit form, the worker @@ -853,7 +853,7 @@ class AbsenceEditForm(forms.ModelForm): active=True, teams__supervisor=user, teams__active=True, - ).distinct() + ).exclude(pay_type='fixed').distinct() self.fields['project'].queryset = Project.objects.filter( active=True, supervisors=user, ) diff --git a/core/tests.py b/core/tests.py index b199117..ac39d1b 100644 --- a/core/tests.py +++ b/core/tests.py @@ -3500,3 +3500,46 @@ class ManagerSalariedAttendanceExclusionTests(TestCase): ids = tw_map.get(str(self.team.id)) or tw_map.get(self.team.id) or [] self.assertIn(self.daily.id, ids) self.assertNotIn(self.mgr.id, ids) + + +# === MANAGER / SALARIED ABSENCE-PICKER EXCLUSION TESTS (Task 4) === +# A pay_type='fixed' manager must be impossible to mark absent — the +# AbsenceLogForm worker picker (admin default + supervisor branch) and +# the AbsenceEditForm worker picker (admin + supervisor branch) must +# exclude fixed-salary workers. (Managers stay selectable in payroll +# modals / team-edit — those are NOT touched by this task.) + +class ManagerSalariedAbsenceExclusionTests(TestCase): + def setUp(self): + self.admin = User.objects.create_user('msab_admin', password='x', is_staff=True) + self.sup = User.objects.create_user('msab_sup', password='x') + self.daily = Worker.objects.create( + name='Abs Daily', id_number='MSAB-D', monthly_salary=Decimal('6000')) + self.mgr = Worker.objects.create( + name='Abs Mgr', id_number='MSAB-M', monthly_salary=Decimal('40000'), + pay_type='fixed') + self.team = Team.objects.create(name='MSAB Team', supervisor=self.sup, active=True) + self.team.workers.add(self.daily, self.mgr) + + def test_absencelog_admin_excludes_fixed(self): + from core.forms import AbsenceLogForm + qs = AbsenceLogForm(user=self.admin).fields['workers'].queryset + self.assertIn(self.daily, qs) + self.assertNotIn(self.mgr, qs) + + def test_absencelog_supervisor_excludes_fixed(self): + from core.forms import AbsenceLogForm + qs = AbsenceLogForm(user=self.sup).fields['workers'].queryset + self.assertIn(self.daily, qs) + self.assertNotIn(self.mgr, qs) + + def test_absenceedit_admin_excludes_fixed(self): + from core.forms import AbsenceEditForm + qs = AbsenceEditForm(user=self.admin).fields['worker'].queryset + self.assertIn(self.daily, qs) + self.assertNotIn(self.mgr, qs) + + def test_absenceedit_supervisor_excludes_fixed(self): + from core.forms import AbsenceEditForm + qs = AbsenceEditForm(user=self.sup).fields['worker'].queryset + self.assertNotIn(self.mgr, qs)