nk20/apps/treasury/models.py

210 lines
5.1 KiB
Python
Raw Normal View History

2020-03-20 23:30:49 +00:00
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
2020-03-22 17:27:22 +00:00
2020-03-22 16:29:31 +00:00
from django.core.exceptions import ValidationError
2020-03-20 23:30:49 +00:00
from django.db import models
2020-03-22 16:29:31 +00:00
from django.db.models import Q
2020-03-20 23:30:49 +00:00
from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial, SpecialTransaction
2020-03-20 23:30:49 +00:00
2020-03-22 00:22:27 +00:00
class Invoice(models.Model):
"""
2020-03-24 19:22:15 +00:00
An invoice model that can generates a true invoice.
"""
2020-03-20 23:30:49 +00:00
id = models.PositiveIntegerField(
primary_key=True,
2020-03-22 00:22:27 +00:00
verbose_name=_("Invoice identifier"),
2020-03-20 23:30:49 +00:00
)
2020-03-20 23:41:55 +00:00
bde = models.CharField(
max_length=32,
default='Saperlistpopette.png',
choices=(
('Saperlistpopette.png', 'Saper[list]popette'),
('Finalist.png', 'Fina[list]'),
('Listorique.png', '[List]orique'),
('Satellist.png', 'Satel[list]'),
('Monopolist.png', 'Monopo[list]'),
('Kataclist.png', 'Katac[list]'),
),
verbose_name=_("BDE"),
)
2020-03-22 14:24:54 +00:00
object = models.CharField(
2020-03-20 23:30:49 +00:00
max_length=255,
2020-03-22 14:24:54 +00:00
verbose_name=_("Object"),
2020-03-20 23:30:49 +00:00
)
description = models.TextField(
verbose_name=_("Description")
)
name = models.CharField(
max_length=255,
verbose_name=_("Name"),
)
address = models.TextField(
verbose_name=_("Address"),
)
date = models.DateField(
auto_now_add=True,
verbose_name=_("Place"),
)
acquitted = models.BooleanField(
verbose_name=_("Acquitted"),
)
2020-04-06 08:58:16 +00:00
class Meta:
verbose_name = _("invoice")
verbose_name_plural = _("invoices")
2020-03-20 23:30:49 +00:00
class Product(models.Model):
"""
2020-03-24 19:22:15 +00:00
Product that appears on an invoice.
"""
2020-03-22 00:22:27 +00:00
invoice = models.ForeignKey(
Invoice,
2020-03-20 23:30:49 +00:00
on_delete=models.PROTECT,
)
designation = models.CharField(
max_length=255,
verbose_name=_("Designation"),
)
quantity = models.PositiveIntegerField(
verbose_name=_("Quantity")
)
amount = models.IntegerField(
2020-03-20 23:30:49 +00:00
verbose_name=_("Unit price")
)
2020-03-21 15:49:18 +00:00
@property
def amount_euros(self):
return self.amount / 100
2020-03-20 23:30:49 +00:00
@property
def total(self):
return self.quantity * self.amount
2020-03-21 15:49:18 +00:00
@property
def total_euros(self):
return self.total / 100
2020-03-22 16:29:31 +00:00
2020-04-06 08:58:16 +00:00
class Meta:
verbose_name = _("product")
verbose_name_plural = _("products")
2020-03-22 16:29:31 +00:00
2020-03-24 16:06:50 +00:00
class RemittanceType(models.Model):
"""
Store what kind of remittances can be stored.
"""
note = models.OneToOneField(
NoteSpecial,
on_delete=models.CASCADE,
)
def __str__(self):
return str(self.note)
2020-04-06 08:58:16 +00:00
class Meta:
verbose_name = _("remittance type")
verbose_name_plural = _("remittance types")
2020-03-24 16:06:50 +00:00
2020-03-22 16:29:31 +00:00
class Remittance(models.Model):
"""
Treasurers want to regroup checks or bank transfers in bank remittances.
"""
2020-03-22 17:27:22 +00:00
date = models.DateTimeField(
2020-03-22 16:29:31 +00:00
auto_now_add=True,
verbose_name=_("Date"),
)
2020-03-24 16:06:50 +00:00
remittance_type = models.ForeignKey(
RemittanceType,
2020-03-22 16:29:31 +00:00
on_delete=models.PROTECT,
verbose_name=_("Type"),
)
comment = models.CharField(
max_length=255,
verbose_name=_("Comment"),
)
2020-03-22 17:27:22 +00:00
closed = models.BooleanField(
default=False,
verbose_name=_("Closed"),
)
2020-04-06 08:58:16 +00:00
class Meta:
verbose_name = _("remittance")
verbose_name_plural = _("remittances")
@property
def transactions(self):
2020-03-24 19:22:15 +00:00
"""
:return: Transactions linked to this remittance.
"""
if not self.pk:
return SpecialTransaction.objects.none()
return SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self)
2020-03-23 22:42:37 +00:00
def count(self):
2020-03-24 19:22:15 +00:00
"""
Linked transactions count.
"""
return self.transactions.count()
2020-03-22 16:29:31 +00:00
@property
def amount(self):
2020-03-24 19:22:15 +00:00
"""
Total amount of the remittance.
"""
return sum(transaction.total for transaction in self.transactions.all())
2020-03-22 16:29:31 +00:00
2020-03-24 19:22:15 +00:00
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
# Check if all transactions have the right type.
2020-03-24 16:06:50 +00:00
if self.transactions.filter(~Q(source=self.remittance_type.note)).exists():
2020-03-22 16:29:31 +00:00
raise ValidationError("All transactions in a remittance must have the same type")
2020-03-24 16:06:50 +00:00
return super().save(force_insert, force_update, using, update_fields)
2020-03-23 22:42:37 +00:00
def __str__(self):
return _("Remittance #{:d}: {}").format(self.id, self.comment, )
class SpecialTransactionProxy(models.Model):
"""
In order to keep modularity, we don't that the Note app depends on the treasury app.
That's why we create a proxy in this app, to link special transactions and remittances.
If it isn't very clean, that makes what we want.
"""
transaction = models.OneToOneField(
SpecialTransaction,
on_delete=models.CASCADE,
)
remittance = models.ForeignKey(
Remittance,
on_delete=models.PROTECT,
null=True,
verbose_name=_("Remittance"),
)
2020-04-06 08:58:16 +00:00
class Meta:
verbose_name = _("special transaction proxy")
verbose_name_plural = _("special transaction proxies")