feat: add Worker.pay_type discriminator + is_salaried property
This commit is contained in:
parent
4dadb7cf23
commit
3c471691f3
18
core/migrations/0016_worker_pay_type.py
Normal file
18
core/migrations/0016_worker_pay_type.py
Normal file
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -72,6 +72,22 @@ class Worker(models.Model):
|
|||||||
notes = models.TextField(blank=True)
|
notes = models.TextField(blank=True)
|
||||||
active = models.BooleanField(default=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 ===
|
# === SIZING ===
|
||||||
# Clothing and boot sizes for PPE (personal protective equipment) ordering
|
# Clothing and boot sizes for PPE (personal protective equipment) ordering
|
||||||
shoe_size = models.CharField(max_length=20, blank=True)
|
shoe_size = models.CharField(max_length=20, blank=True)
|
||||||
@ -94,6 +110,11 @@ class Worker(models.Model):
|
|||||||
# monthly salary divided by 20 working days
|
# monthly salary divided by 20 working days
|
||||||
return (self.monthly_salary / Decimal('20.00')).quantize(Decimal('0.01'))
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|||||||
@ -3364,3 +3364,22 @@ class DashboardPaidThisMonthTests(TestCase):
|
|||||||
"""No payments in the current month → zero (not None)."""
|
"""No payments in the current month → zero (not None)."""
|
||||||
resp = self.client.get('/')
|
resp = self.client.get('/')
|
||||||
self.assertEqual(resp.context['paid_this_month'], Decimal('0.00'))
|
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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user