diff --git a/core/migrations/0016_worker_pay_type.py b/core/migrations/0016_worker_pay_type.py new file mode 100644 index 0000000..79a9787 --- /dev/null +++ b/core/migrations/0016_worker_pay_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-05-15 17:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0015_absence_project'), + ] + + operations = [ + migrations.AddField( + model_name='worker', + name='pay_type', + field=models.CharField(choices=[('daily', 'Daily-rated'), ('fixed', 'Fixed salary')], default='daily', help_text='Daily-rated workers are logged per day. Fixed-salary staff (managers) are paid a set monthly amount and are never added to a work log.', max_length=10), + ), + ] diff --git a/core/models.py b/core/models.py index 0d5be01..78f468d 100644 --- a/core/models.py +++ b/core/models.py @@ -72,6 +72,22 @@ class Worker(models.Model): notes = models.TextField(blank=True) active = models.BooleanField(default=True) + # === PAY TYPE === + # 'daily' = normal field worker, paid per logged work day (daily_rate). + # 'fixed' = manager / salaried staff: paid a fixed monthly amount via a + # 'Salary' PayrollAdjustment, never logged on a WorkLog. See + # CLAUDE.md "Manager / Salaried pay" + the Path-A naming note. + PAY_TYPE_CHOICES = [ + ('daily', 'Daily-rated'), + ('fixed', 'Fixed salary'), + ] + pay_type = models.CharField( + max_length=10, choices=PAY_TYPE_CHOICES, default='daily', + help_text='Daily-rated workers are logged per day. Fixed-salary ' + 'staff (managers) are paid a set monthly amount and are ' + 'never added to a work log.', + ) + # === SIZING === # Clothing and boot sizes for PPE (personal protective equipment) ordering shoe_size = models.CharField(max_length=20, blank=True) @@ -94,6 +110,11 @@ class Worker(models.Model): # monthly salary divided by 20 working days return (self.monthly_salary / Decimal('20.00')).quantize(Decimal('0.01')) + @property + def is_salaried(self): + # True for managers / fixed-salary staff (pay_type='fixed'). + return self.pay_type == 'fixed' + def __str__(self): return self.name diff --git a/core/tests.py b/core/tests.py index d8bfd62..2c013fa 100644 --- a/core/tests.py +++ b/core/tests.py @@ -3364,3 +3364,22 @@ class DashboardPaidThisMonthTests(TestCase): """No payments in the current month → zero (not None).""" resp = self.client.get('/') self.assertEqual(resp.context['paid_this_month'], Decimal('0.00')) + + +class ManagerSalariedPayModelTests(TestCase): + """Worker.pay_type discriminator + is_salaried convenience property.""" + + def test_pay_type_defaults_to_daily(self): + w = Worker.objects.create( + name='Daily Dan', id_number='PT-D1', monthly_salary=Decimal('6000.00'), + ) + self.assertEqual(w.pay_type, 'daily') + self.assertFalse(w.is_salaried) + + def test_pay_type_fixed_is_salaried(self): + m = Worker.objects.create( + name='Manager Fitz', id_number='PT-M1', + monthly_salary=Decimal('40000.00'), pay_type='fixed', + ) + self.assertEqual(m.pay_type, 'fixed') + self.assertTrue(m.is_salaried)