1π
β
Just in case you want a second opinion. You can clean up your models, there are many redundant fields in Payroll
such as payroll_gross_value
is the same as the method get_gross_value_based_on_hours
. An option is to use a model property.
Also, you are also unnecessarily overriding the save method with redundant conditions by replacing the field with itself. Lastly, you can also rename your models fields to improve code readability.
models.py
PERCENTAGE_VALIDATOR = [MinValueValidator(0), MaxValueValidator(100)]
class Taxes(models.Model):
name = models.CharField(max_length=100)
percentage = models.DecimalField(
max_digits=5,
decimal_places=2,
validators=PERCENTAGE_VALIDATOR
)
class Meta:
verbose_name = 'Tax'
verbose_name_plural = 'Taxes'
def __str__(self):
return f'{self.name} - {self.percentage}%'
class FixedCosts(models.Model):
name = models.CharField(max_length=100)
value = models.DecimalField(
max_digits=10,
decimal_places=2,
blank=True,
null=True
)
class Meta:
verbose_name = 'Fixed Cost'
verbose_name_plural = 'Fixed Costs'
def __str__(self):
return f'{self.name} - {self.value} β¬'
class Payroll(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date = models.DateField()
hourly_rate = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
worked_hours = models.IntegerField()
taxes = models.ManyToManyField(Taxes, blank=True, related_name="payroll_taxes")
fixed_costs = models.ManyToManyField(FixedCosts, blank=True, related_name="payroll_fixed_costs")
send = models.BooleanField(default=False)
send_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return f'Payroll for {self.user.username}'
@property
def gross_value(self):
return self.hourly_rate * self.worked_hours
@property
def net_value(self):
taxes = self.taxes.all()
fixed_costs = self.fixed_costs.all()
net_value = self.gross_value
for tax in taxes:
net_value -= (self.gross_value * tax.percentage) / 100
for cost in fixed_costs:
net_value -= cost.value
return net_value
tests.py (A simple test case)
class PayrollTestCase(TestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(username='test', password='test123')
self.payroll = Payroll.objects.create(
user=self.user,
date=datetime.datetime.now(),
worked_hours=160,
hourly_rate=decimal.Decimal(10)
)
def test_payroll_net_value_property(self):
fixed_cost1 = FixedCosts.objects.create(
name='Fixed Cost One',
value=decimal.Decimal(100)
)
fixed_cost2 = FixedCosts.objects.create(
name='Fixed Cost Two',
value=decimal.Decimal(50)
)
tax1 = Taxes.objects.create(name='Tax One', percentage=10)
tax2 = Taxes.objects.create(name='Tax Two', percentage=20)
self.payroll.fixed_costs.add(fixed_cost1)
self.payroll.fixed_costs.add(fixed_cost2)
self.payroll.taxes.add(tax1)
self.payroll.taxes.add(tax2)
self.assertEqual(self.payroll.net_value, 970)
def test_payroll_gross_value(self):
self.assertEqual(self.payroll.gross_value, 1600)
π€Niko
0π
I figured it out on my own,
@receiver(m2m_changed, sender=Payroll.payroll_taxes.through)
@receiver(m2m_changed, sender=Payroll.payroll_fixed_costs.through)
def calculate_net_value(sender, instance, **kwargs):
gross_value = instance.payroll_gross_value
taxes_total = instance.payroll_taxes.all().aggregate(Sum('tax_percentage'))['tax_percentage__sum'] or 0
taxes_total = Decimal(taxes_total / 100)
fixed_costs_total = instance.payroll_fixed_costs.all().aggregate(Sum('value'))['value__sum'] or 0
instance.payroll_net_value = gross_value - (gross_value * taxes_total)\
- fixed_costs_total
instance.save()
π€Bartosz
- [Answered ]-Django Python: Install multiple versions of the same package within a virtualenv
- [Answered ]-Django 1.6.5 with Celery 3.1 for Periodic Tasks
- [Answered ]-Login to a Django site using a POST request
- [Answered ]-Django:adding more than one value in model field
- [Answered ]-IndexError: string index out of range β Django β Why?
Source:stackexchange.com