mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 11:23:10 +00:00
Merge branch 'fix-overlapping-invoices' into 'dev'
Fix Overlapping invoices. See merge request federez/re2o!476
This commit is contained in:
commit
9e87f796aa
3 changed files with 126 additions and 43 deletions
|
@ -292,15 +292,40 @@ class Facture(BaseInvoice):
|
||||||
return bool(self.get_subscription())
|
return bool(self.get_subscription())
|
||||||
|
|
||||||
def reorder_purchases(self):
|
def reorder_purchases(self):
|
||||||
date = self.date
|
date_adh = max(self.date, self.user.end_adhesion())
|
||||||
|
date_con = max(self.date, self.user.end_connexion())
|
||||||
for purchase in self.vente_set.all():
|
for purchase in self.vente_set.all():
|
||||||
if hasattr(purchase, "cotisation"):
|
if hasattr(purchase, "cotisation"):
|
||||||
cotisation = purchase.cotisation
|
cotisation = purchase.cotisation
|
||||||
cotisation.date_start = date
|
if cotisation.type_cotisation == 'Connexion':
|
||||||
date += relativedelta(
|
cotisation.date_start = date_con
|
||||||
months=(purchase.duration or 0) * purchase.number,
|
date_con += relativedelta(
|
||||||
days=(purchase.duration_days or 0) * purchase.number,
|
months=(purchase.duration or 0) * purchase.number,
|
||||||
)
|
days=(purchase.duration_days or 0) * purchase.number,
|
||||||
|
)
|
||||||
|
cotisation.date_end = date_con
|
||||||
|
elif cotisation.type_cotisation == 'Adhesion':
|
||||||
|
cotisation.date_start = date_adh
|
||||||
|
date_adh += relativedelta(
|
||||||
|
months=(purchase.duration or 0) * purchase.number,
|
||||||
|
days=(purchase.duration_days or 0) * purchase.number,
|
||||||
|
)
|
||||||
|
cotisation.date_end = date_adh
|
||||||
|
else: # it is assumed that adhesion is required for a connexion
|
||||||
|
date = min(date_adh, date_con)
|
||||||
|
cotisation.date_start = date
|
||||||
|
date_adh += relativedelta(
|
||||||
|
months=(purchase.duration or 0) * purchase.number,
|
||||||
|
days=(purchase.duration_days or 0) * purchase.number,
|
||||||
|
)
|
||||||
|
date_con += relativedelta(
|
||||||
|
months=(purchase.duration or 0) * purchase.number,
|
||||||
|
days=(purchase.duration_days or 0) * purchase.number,
|
||||||
|
)
|
||||||
|
date = max(date_adh, date_con)
|
||||||
|
cotisation.date_end = date
|
||||||
|
cotisation.save()
|
||||||
|
purchase.facture = self
|
||||||
purchase.save()
|
purchase.save()
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -488,9 +513,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
def create_cotis(self, date_start=False):
|
def create_cotis(self, date_start=False):
|
||||||
"""
|
"""
|
||||||
Update and create a 'cotisation' related object if there is a
|
Creates a cotisation without initializing the dates (start and end ar set to self.facture.facture.date) and without saving it. You should use Facture.reorder_purchases to set the right dates.
|
||||||
cotisation_type defined (which means the article sold represents
|
|
||||||
a cotisation)
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
invoice = self.facture.facture
|
invoice = self.facture.facture
|
||||||
|
@ -500,33 +523,16 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
cotisation = Cotisation(vente=self)
|
cotisation = Cotisation(vente=self)
|
||||||
cotisation.type_cotisation = self.type_cotisation
|
cotisation.type_cotisation = self.type_cotisation
|
||||||
if date_start:
|
if date_start:
|
||||||
end_cotisation = (
|
cotisation.date_start = date_start
|
||||||
Cotisation.objects.filter(
|
cotisation.date_end = cotisation.date_start + relativedelta(
|
||||||
vente__in=Vente.objects.filter(
|
months=(self.duration or 0) * self.number,
|
||||||
facture__in=Facture.objects.filter(
|
days=(self.duration_days or 0) * self.number,
|
||||||
user=invoice.user
|
|
||||||
).exclude(valid=False)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.filter(
|
|
||||||
Q(type_cotisation="All")
|
|
||||||
| Q(type_cotisation=self.type_cotisation)
|
|
||||||
)
|
|
||||||
.filter(date_start__lt=date_start)
|
|
||||||
.aggregate(Max("date_end"))["date_end__max"]
|
|
||||||
)
|
)
|
||||||
elif self.type_cotisation == "Adhesion":
|
self.save()
|
||||||
end_cotisation = invoice.user.end_adhesion()
|
cotisation.save()
|
||||||
else:
|
else:
|
||||||
end_cotisation = invoice.user.end_connexion()
|
cotisation.date_start = invoice.date
|
||||||
date_start = date_start or timezone.now()
|
cotisation.date_end = invoice.date
|
||||||
end_cotisation = end_cotisation or date_start
|
|
||||||
date_max = max(end_cotisation, date_start)
|
|
||||||
cotisation.date_start = date_max
|
|
||||||
cotisation.date_end = cotisation.date_start + relativedelta(
|
|
||||||
months=(self.duration or 0) * self.number,
|
|
||||||
days=(self.duration_days or 0) * self.number,
|
|
||||||
)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -10,7 +10,7 @@ from .models import Vente, Facture, Cotisation, Paiement
|
||||||
|
|
||||||
class VenteModelTests(TestCase):
|
class VenteModelTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.user = User.objects.create(pseudo="testUser", email="test@example.org")
|
self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org")
|
||||||
self.paiement = Paiement.objects.create(moyen="test payment")
|
self.paiement = Paiement.objects.create(moyen="test payment")
|
||||||
self.f = Facture.objects.create(
|
self.f = Facture.objects.create(
|
||||||
user=self.user, paiement=self.paiement, valid=True
|
user=self.user, paiement=self.paiement, valid=True
|
||||||
|
@ -30,6 +30,7 @@ class VenteModelTests(TestCase):
|
||||||
type_cotisation="All",
|
type_cotisation="All",
|
||||||
prix=0,
|
prix=0,
|
||||||
)
|
)
|
||||||
|
self.f.reorder_purchases()
|
||||||
self.assertAlmostEqual(
|
self.assertAlmostEqual(
|
||||||
self.user.end_connexion() - date,
|
self.user.end_connexion() - date,
|
||||||
datetime.timedelta(days=1),
|
datetime.timedelta(days=1),
|
||||||
|
@ -41,7 +42,7 @@ class VenteModelTests(TestCase):
|
||||||
It should be possible to have one day membership.
|
It should be possible to have one day membership.
|
||||||
"""
|
"""
|
||||||
date = timezone.now()
|
date = timezone.now()
|
||||||
purchase = Vente.objects.create(
|
Vente.objects.create(
|
||||||
facture=self.f,
|
facture=self.f,
|
||||||
number=1,
|
number=1,
|
||||||
name="Test purchase",
|
name="Test purchase",
|
||||||
|
@ -50,16 +51,19 @@ class VenteModelTests(TestCase):
|
||||||
type_cotisation="All",
|
type_cotisation="All",
|
||||||
prix=0,
|
prix=0,
|
||||||
)
|
)
|
||||||
delta = relativedelta(self.user.end_connexion(), date)
|
self.f.reorder_purchases()
|
||||||
delta.microseconds = 0
|
end = self.user.end_connexion()
|
||||||
self.assertEqual(delta, relativedelta(months=1))
|
expected_end = date + relativedelta(months=1)
|
||||||
|
self.assertEqual(end.day, expected_end.day)
|
||||||
|
self.assertEqual(end.month, expected_end.month)
|
||||||
|
self.assertEqual(end.year, expected_end.year)
|
||||||
|
|
||||||
def test_one_month_and_one_week_cotisation(self):
|
def test_one_month_and_one_week_cotisation(self):
|
||||||
"""
|
"""
|
||||||
It should be possible to have one day membership.
|
It should be possible to have one day membership.
|
||||||
"""
|
"""
|
||||||
date = timezone.now()
|
date = timezone.now()
|
||||||
purchase = Vente.objects.create(
|
Vente.objects.create(
|
||||||
facture=self.f,
|
facture=self.f,
|
||||||
number=1,
|
number=1,
|
||||||
name="Test purchase",
|
name="Test purchase",
|
||||||
|
@ -68,11 +72,82 @@ class VenteModelTests(TestCase):
|
||||||
type_cotisation="All",
|
type_cotisation="All",
|
||||||
prix=0,
|
prix=0,
|
||||||
)
|
)
|
||||||
delta = relativedelta(self.user.end_connexion(), date)
|
self.f.reorder_purchases()
|
||||||
delta.microseconds = 0
|
end = self.user.end_connexion()
|
||||||
self.assertEqual(delta, relativedelta(months=1, days=7))
|
expected_end = date + relativedelta(months=1, days=7)
|
||||||
|
self.assertEqual(end.day, expected_end.day)
|
||||||
|
self.assertEqual(end.month, expected_end.month)
|
||||||
|
self.assertEqual(end.year, expected_end.year)
|
||||||
|
|
||||||
|
def test_date_start_cotisation(self):
|
||||||
|
"""
|
||||||
|
It should be possible to add a cotisation with a specific start date
|
||||||
|
"""
|
||||||
|
v = Vente(
|
||||||
|
facture=self.f,
|
||||||
|
number=1,
|
||||||
|
name="Test purchase",
|
||||||
|
duration=0,
|
||||||
|
duration_days=1,
|
||||||
|
type_cotisation = 'All',
|
||||||
|
prix=0
|
||||||
|
)
|
||||||
|
v.create_cotis(date_start=timezone.make_aware(datetime.datetime(1998, 10, 16)))
|
||||||
|
v.save()
|
||||||
|
self.assertEqual(v.cotisation.date_end, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.f.delete()
|
self.f.delete()
|
||||||
self.user.delete()
|
self.user.delete()
|
||||||
self.paiement.delete()
|
self.paiement.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class FactureModelTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org")
|
||||||
|
self.paiement = Paiement.objects.create(moyen="test payment")
|
||||||
|
def tearDown(self):
|
||||||
|
self.user.delete()
|
||||||
|
self.paiement.delete()
|
||||||
|
def test_cotisations_prolongation(self):
|
||||||
|
"""When user already have one valid cotisation, the new one should be
|
||||||
|
added at the end of the existing one."""
|
||||||
|
date = timezone.now()
|
||||||
|
invoice1 = Facture.objects.create(
|
||||||
|
user=self.user, paiement=self.paiement, valid=True
|
||||||
|
)
|
||||||
|
Vente.objects.create(
|
||||||
|
facture=invoice1,
|
||||||
|
number=1,
|
||||||
|
name="Test purchase",
|
||||||
|
duration=1,
|
||||||
|
duration_days=0,
|
||||||
|
type_cotisation="All",
|
||||||
|
prix=0,
|
||||||
|
)
|
||||||
|
invoice1.reorder_purchases()
|
||||||
|
invoice2 = Facture.objects.create(
|
||||||
|
user=self.user, paiement=self.paiement, valid=True
|
||||||
|
)
|
||||||
|
Vente.objects.create(
|
||||||
|
facture=invoice2,
|
||||||
|
number=1,
|
||||||
|
name="Test purchase",
|
||||||
|
duration=1,
|
||||||
|
duration_days=0,
|
||||||
|
type_cotisation="All",
|
||||||
|
prix=0,
|
||||||
|
)
|
||||||
|
invoice1.reorder_purchases()
|
||||||
|
delta = relativedelta(self.user.end_connexion(), date)
|
||||||
|
delta.microseconds = 0
|
||||||
|
try:
|
||||||
|
self.assertEqual(delta, relativedelta(months=2))
|
||||||
|
except Exception as e:
|
||||||
|
invoice1.delete()
|
||||||
|
invoice2.delete()
|
||||||
|
raise e
|
||||||
|
invoice1.delete()
|
||||||
|
invoice2.delete()
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,8 @@ def new_facture(request, user, userid):
|
||||||
price_ok = True
|
price_ok = True
|
||||||
if price_ok:
|
if price_ok:
|
||||||
new_invoice_instance.save()
|
new_invoice_instance.save()
|
||||||
|
# Saving purchases so the invoice can find them. Invoice
|
||||||
|
# will modify them after being validated to put the right dates.
|
||||||
for p in purchases:
|
for p in purchases:
|
||||||
p.facture = new_invoice_instance
|
p.facture = new_invoice_instance
|
||||||
p.save()
|
p.save()
|
||||||
|
|
Loading…
Reference in a new issue