misc: update future income computation with periodic invoicing (#326)

This commit is contained in:
Benjamin Dauvergne 2021-11-25 18:26:08 +01:00
parent b29508c479
commit 0979cdaf80
2 changed files with 79 additions and 10 deletions

View File

@ -151,13 +151,22 @@ class Contrat(models.Model):
pourcentage_facture.short_description = 'Pourcentage facturé'
@property
def montant_prestations(self):
return Decimal(sum(p.montant() for p in self.prestations.all()))
def montant(self):
"""
Montant total d'un contrat, y compris les prestations
optionnelles. Si pas de prestation définie, montant des factures émises.
"""
if self.prestations.exists():
return Decimal(sum(p.montant() for p in self.prestations.all()))
montant = self.montant_prestations
if self.periodicite and self.periodicite_debut:
return montant * self.periodicite_nombre_d_echeances()
else:
return montant
else:
return self.montant_facture()
@ -171,12 +180,26 @@ class Contrat(models.Model):
today_year = datetime.date.today().year
montant_par_annee = defaultdict(Decimal)
adjust = 0
for year, fraction in self.percentage_per_year:
amount = self.montant() * fraction
if year > today_year:
montant_par_annee[year] += amount
else:
adjust += amount
if not self.periodicite:
for year, fraction in self.percentage_per_year: # pylint: disable=not-an-iterable
amount = self.montant() * fraction
if year > today_year:
montant_par_annee[year] += amount
else:
adjust += amount
else:
# calcule le montant par periode en sommant le montant des
# prestations puis impute à chaque année ce même montant pour
# chaque échéance arrivant dans cette année, pour les montants de
# l'année en cours et des précédentes, tout mettre dans l'année
# courante, ce sera diminué du total des factures déjà faites (même
# comportement que dans le cas classique)
montant = self.montant_prestations
for _, debut, _ in self.periodicite_echeances(limit=20):
if debut.year > today_year:
montant_par_annee[debut.year] += montant
else:
adjust += montant
for f in self.factures.all():
if f.proforma:
continue
@ -234,9 +257,11 @@ class Contrat(models.Model):
return dates
def periodicite_nombre_d_echeances(self):
if not self.periodicite_fin:
return '*'
return len(list(self.periodicite_echeances(limit=1000)))
if self.periodicite_fin:
return len(list(self.periodicite_echeances(limit=1000)))
else:
# compter les échéances passées + 1
return len(list(self.periodicite_echeances(limit=1)))
def periodicite_echeances(self, until=None, limit=3):
i = 1

View File

@ -56,3 +56,47 @@ def test_contrat_montant_par_annee_percentage_per_year(db, django_user_model, fr
facture.lignes.create(prix_unitaire_ht=1000, quantite=1, order=1)
assert sorted(list(contrat.montant_par_annee().items())) == [(2021, Decimal(5500)), (2022, Decimal(2500))]
def test_contrat_montant_par_annee_recurrent(db, django_user_model, freezer):
freezer.move_to('2021-06-01')
client = Client.objects.create(nom=str(uuid.uuid4()))
user = django_user_model.objects.all()[0]
contrat = Contrat.objects.create(
client=client,
creator=user,
intitule=str(uuid.uuid4()),
periodicite='semestrielle',
periodicite_debut=date(2020, 7, 1),
periodicite_fin=date(2023, 7, 1),
)
contrat.clean()
for i in range(2):
contrat.prestations.create(intitule=str(i), quantite=1, prix_unitaire_ht=1000)
facture = Facture.objects.create(
proforma=False,
client=client,
contrat=contrat,
emission=date(2020, 1, 1),
echeance=date(2020, 1, 16),
creator=user,
)
facture.lignes.create(prix_unitaire_ht=1000, quantite=1, order=1)
facture = Facture.objects.create(
proforma=False,
client=client,
contrat=contrat,
emission=date(2021, 1, 1),
echeance=date(2021, 1, 16),
creator=user,
)
facture.lignes.create(prix_unitaire_ht=1000, quantite=1, order=1)
assert contrat.montant() == Decimal(1000) * 2 * 6
assert sorted(list(contrat.montant_par_annee().items())) == [
(2021, Decimal(4000)),
(2022, Decimal(4000)),
(2023, Decimal(2000)),
]