mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 11:23:10 +00:00
Merge branch 'fix_memb_adh' into 'dev'
Fix memb adh See merge request re2o/re2o!562
This commit is contained in:
commit
b0fdeca744
15 changed files with 767 additions and 202 deletions
|
@ -64,8 +64,10 @@ class VenteSerializer(NamespacedHMSerializer):
|
|||
"number",
|
||||
"name",
|
||||
"prix",
|
||||
"duration",
|
||||
"type_cotisation",
|
||||
"duration_connection",
|
||||
"duration_days_connection",
|
||||
"duration_membership",
|
||||
"duration_days_membership",
|
||||
"prix_total",
|
||||
"api_url",
|
||||
)
|
||||
|
@ -77,7 +79,7 @@ class ArticleSerializer(NamespacedHMSerializer):
|
|||
|
||||
class Meta:
|
||||
model = cotisations.Article
|
||||
fields = ("name", "prix", "duration", "type_user", "type_cotisation", "api_url")
|
||||
fields = ("name", "prix", "duration_membership", "duration_days_membership", "duration_connection", "duration_days_connection", "type_user", "api_url")
|
||||
|
||||
|
||||
class BanqueSerializer(NamespacedHMSerializer):
|
||||
|
@ -104,7 +106,7 @@ class CotisationSerializer(NamespacedHMSerializer):
|
|||
|
||||
class Meta:
|
||||
model = cotisations.Cotisation
|
||||
fields = ("vente", "type_cotisation", "date_start", "date_end", "api_url")
|
||||
fields = ("vente", "type_cotisation", "date_start_con", "date_end_con", "date_start_memb", "date_end_memb", "api_url")
|
||||
|
||||
|
||||
class ReminderUsersSerializer(UserSerializer):
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-09-20 17:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0042_auto_20191120_0159'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# migrations.RemoveField(
|
||||
# model_name='article',
|
||||
# name='duration',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='article',
|
||||
# name='duration_days',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='article',
|
||||
# name='type_cotisation',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='cotisation',
|
||||
# name='date_end',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='cotisation',
|
||||
# name='date_start',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='cotisation',
|
||||
# name='type_cotisation',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='vente',
|
||||
# name='duration',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='vente',
|
||||
# name='duration_days',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='vente',
|
||||
# name='type_cotisation',
|
||||
# ),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='duration_connection',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='duration_days_connection',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='duration_days_membership',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='duration_membership',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cotisation',
|
||||
name='date_end_con',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='end date for the connection'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cotisation',
|
||||
name='date_end_memb',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='end date for the membership'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cotisation',
|
||||
name='date_start_con',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date for the connection'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cotisation',
|
||||
name='date_start_memb',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date for the membership'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vente',
|
||||
name='duration_connection',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration of the connection (in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vente',
|
||||
name='duration_days_connection',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vente',
|
||||
name='duration_days_membership',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vente',
|
||||
name='duration_membership',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration of the membership (in months)'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,140 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-09-20 17:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0043_separation_membership_connection_p1'),
|
||||
]
|
||||
|
||||
def split_dates(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
cotisation = apps.get_model("cotisations", "Cotisation")
|
||||
cotisations = cotisation.objects.using(db_alias).all()
|
||||
for cotis in cotisations:
|
||||
cotis.date_start_con = cotis.date_start
|
||||
cotis.date_start_memb = cotis.date_start
|
||||
cotis.date_end_con = cotis.date_end
|
||||
cotis.date_end_memb = cotis.date_end
|
||||
if cotis.type_cotisation == 'Connexion':
|
||||
cotis.date_end_memb = cotis.date_start
|
||||
if cotis.type_cotisation == 'Adhesion':
|
||||
cotis.date_end_con = cotis.date_start
|
||||
cotis.save()
|
||||
|
||||
|
||||
|
||||
def split_duration_articles_and_ventes(apps, schema_editor):
|
||||
def split_duration(e):
|
||||
e.duration_membership = e.duration
|
||||
e.duration_connection = e.duration
|
||||
e.duration_days_membership = e.duration_days
|
||||
e.duration_days_connection = e.duration_days
|
||||
if e.type_cotisation == 'Connexion':
|
||||
e.duration_membership = 0
|
||||
e.duration_days_membership = 0
|
||||
if e.type_cotisation == 'Adhesion':
|
||||
e.duration_connection = 0
|
||||
e.duration_days_connection = 0
|
||||
e.save()
|
||||
db_alias = schema_editor.connection.alias
|
||||
article = apps.get_model("cotisations", "Article")
|
||||
vente = apps.get_model("cotisations", "Vente")
|
||||
for a in article.objects.using(db_alias).all():
|
||||
split_duration(a)
|
||||
for v in vente.objects.using(db_alias).all():
|
||||
split_duration(v)
|
||||
|
||||
def unsplit_dates(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
cotisation = apps.get_model("cotisations", "Cotisation")
|
||||
cotisations = cotisation.objects.using(db_alias).all()
|
||||
for cotis in cotisations:
|
||||
connection = cotis.date_start_con != cotis.date_end_con
|
||||
adhesion = cotis.date_start_memb != cotis.date_end_memb
|
||||
cotis.date_start = cotis.date_start_con
|
||||
cotis.date_end = max(cotis.date_end_con, cotis.date_end_memb)
|
||||
if connection:
|
||||
cotis.type_cotisation = 'Connexion'
|
||||
if adhesion:
|
||||
cotis.type_cotisation = 'Adhesion'
|
||||
if connection and adhesion:
|
||||
cotis.type_cotisation = 'All'
|
||||
if not (connection or adhesion):
|
||||
cotis.type_cotisation = None
|
||||
cotis.save()
|
||||
|
||||
|
||||
|
||||
def unsplit_duration_articles_and_ventes(apps, schema_editor):
|
||||
def unsplit_duration(e):
|
||||
e.duration = max(e.duration_membership, e.duration_connection)
|
||||
e.duration_days = max(e.duration_days_membership, e.duration_days_connection)
|
||||
connection = not (((e.duration_connection == 0) or (e.duration_connection__isnull)) and \
|
||||
((e.duration_days_connection == 0) or (e.duration_days_connection__isnull)))
|
||||
membership = not (((e.duration_membership == 0) or (e.duration_membership__isnull)) and \
|
||||
((e.duration_days_membership == 0) or (e.duration_days_membership__isnull)))
|
||||
if connection:
|
||||
e.type_cotisation = 'Connection'
|
||||
if membership:
|
||||
e.type_cotisation = 'Adhesion'
|
||||
if connection and membership:
|
||||
e.type_cotisation = 'All'
|
||||
if not (connection or membership):
|
||||
e.type_cotisation = None
|
||||
e.save()
|
||||
db_alias = schema_editor.connection.alias
|
||||
article = apps.get_model("cotisations", "Article")
|
||||
vente = apps.get_model("cotisations", "Vente")
|
||||
for a in article.objects.using(db_alias).all():
|
||||
unsplit_duration(a)
|
||||
for v in vente.objects.using(db_alias).all():
|
||||
unsplit_duration(v)
|
||||
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(split_dates, unsplit_dates),
|
||||
migrations.RunPython(split_duration_articles_and_ventes, unsplit_duration_articles_and_ventes),
|
||||
# migrations.RemoveField(
|
||||
# model_name='article',
|
||||
# name='duration',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='article',
|
||||
# name='duration_days',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='article',
|
||||
# name='type_cotisation',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='cotisation',
|
||||
# name='date_end',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='cotisation',
|
||||
# name='date_start',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='cotisation',
|
||||
# name='type_cotisation',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='vente',
|
||||
# name='duration',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='vente',
|
||||
# name='duration_days',
|
||||
# ),
|
||||
# migrations.RemoveField(
|
||||
# model_name='vente',
|
||||
# name='type_cotisation',
|
||||
# ),
|
||||
]
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-09-20 17:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0044_separation_membership_connection_p2'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='duration',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='duration_days',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='type_cotisation',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cotisation',
|
||||
name='date_end',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cotisation',
|
||||
name='date_start',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cotisation',
|
||||
name='type_cotisation',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='vente',
|
||||
name='duration',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='vente',
|
||||
name='duration_days',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='vente',
|
||||
name='type_cotisation',
|
||||
),
|
||||
]
|
20
cotisations/migrations/0046_article_need_membership.py
Normal file
20
cotisations/migrations/0046_article_need_membership.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-09-25 16:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0045_separation_membership_connection_p3'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='need_membership',
|
||||
field=models.BooleanField(default=True, verbose_name='can be purcharsed without membership'),
|
||||
),
|
||||
]
|
28
cotisations/migrations/0047_article_need_membership_init.py
Normal file
28
cotisations/migrations/0047_article_need_membership_init.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-09-25 16:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0046_article_need_membership'),
|
||||
]
|
||||
|
||||
def init_need_membership(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
article = apps.get_model("cotisations", "Article")
|
||||
articles = article.objects.using(db_alias).all()
|
||||
for art in articles:
|
||||
v = False
|
||||
v = v or bool(art.duration_membership)
|
||||
v = v or bool(art.duration_days_membership)
|
||||
v = v or not (bool(art.duration_connection) or bool(art.duration_days_connection))
|
||||
art.need_membership = v
|
||||
art.save()
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(init_need_membership, lambda *args, **kargs: None),
|
||||
]
|
50
cotisations/migrations/0048_auto_20201017_0018.py
Normal file
50
cotisations/migrations/0048_auto_20201017_0018.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-10-16 22:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0047_article_need_membership_init'),
|
||||
]
|
||||
|
||||
|
||||
def set_value_to_0(apps, schema_editor):
|
||||
ventes = apps.get_model("cotisations", "Vente")
|
||||
ventes.filter(duration_connection__isnull=True).update(duration_connection=0)
|
||||
ventes.filter(duration_days_connection__isnull=True).update(duration_days_connection=0)
|
||||
ventes.filter(duration_membership__isnull=True).update(duration_membership=0)
|
||||
ventes.filter(duration_days_membership__isnull=True).update(duration_days_membership=0)
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(set_value_to_0),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='need_membership',
|
||||
field=models.BooleanField(default=True, verbose_name='need membership to be purchased'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vente',
|
||||
name='duration_connection',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='duration of the connection (in months)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vente',
|
||||
name='duration_days_connection',
|
||||
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vente',
|
||||
name='duration_days_membership',
|
||||
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vente',
|
||||
name='duration_membership',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='duration of the membership (in months)'),
|
||||
),
|
||||
]
|
|
@ -283,7 +283,8 @@ class Facture(BaseInvoice):
|
|||
"""Returns every subscription associated with this invoice."""
|
||||
return Cotisation.objects.filter(
|
||||
vente__in=self.vente_set.filter(
|
||||
Q(type_cotisation="All") | Q(type_cotisation="Adhesion")
|
||||
~(Q(duration_membership=0)) |\
|
||||
~(Q(duration_days_membership=0))
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -297,33 +298,18 @@ class Facture(BaseInvoice):
|
|||
for purchase in self.vente_set.all():
|
||||
if hasattr(purchase, "cotisation"):
|
||||
cotisation = purchase.cotisation
|
||||
if cotisation.type_cotisation == "Connexion":
|
||||
cotisation.date_start = date_con
|
||||
date_con += relativedelta(
|
||||
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.date_start_con = date_con
|
||||
date_con += relativedelta(
|
||||
months=(purchase.duration_connection or 0) * purchase.number,
|
||||
days=(purchase.duration_days_connection or 0) * purchase.number,
|
||||
)
|
||||
cotisation.date_end_con = date_con
|
||||
cotisation.date_start_memb = date_adh
|
||||
date_adh += relativedelta(
|
||||
months=(purchase.duration_membership or 0) * purchase.number,
|
||||
days=(purchase.duration_days_membership or 0) * purchase.number,
|
||||
)
|
||||
cotisation.date_end_memb = date_adh
|
||||
cotisation.save()
|
||||
purchase.facture = self
|
||||
purchase.save()
|
||||
|
@ -450,13 +436,6 @@ class Vente(RevMixin, AclMixin, models.Model):
|
|||
the effect of the purchase on the time agreed for this user)
|
||||
"""
|
||||
|
||||
# TODO : change this to English
|
||||
COTISATION_TYPE = (
|
||||
("Connexion", _("Connection")),
|
||||
("Adhesion", _("Membership")),
|
||||
("All", _("Both of them")),
|
||||
)
|
||||
|
||||
# TODO : change facture to invoice
|
||||
facture = models.ForeignKey(
|
||||
"BaseInvoice", on_delete=models.CASCADE, verbose_name=_("invoice")
|
||||
|
@ -466,27 +445,28 @@ class Vente(RevMixin, AclMixin, models.Model):
|
|||
validators=[MinValueValidator(1)], verbose_name=_("amount")
|
||||
)
|
||||
# TODO : change this field for a ForeinKey to Article
|
||||
# Note: With a foreign key, modifing an Article modifis the Purchase, wich is bad.
|
||||
# To use a foreign key, you need to make Article read only
|
||||
name = models.CharField(max_length=255, verbose_name=_("article"))
|
||||
# TODO : change prix to price
|
||||
# TODO : this field is not needed if you use Article ForeignKey
|
||||
prix = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_("price"))
|
||||
# TODO : this field is not needed if you use Article ForeignKey
|
||||
duration = models.PositiveIntegerField(
|
||||
blank=True, null=True, verbose_name=_("duration (in months)")
|
||||
duration_connection = models.PositiveIntegerField(
|
||||
default=0, verbose_name=_("duration of the connection (in months)")
|
||||
)
|
||||
duration_days = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
duration_days_connection = models.PositiveIntegerField(
|
||||
default=0,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_("duration (in days, will be added to duration in months)"),
|
||||
verbose_name=_("duration of the connection (in days, will be added to duration in months)"),
|
||||
)
|
||||
# TODO : this field is not needed if you use Article ForeignKey
|
||||
type_cotisation = models.CharField(
|
||||
choices=COTISATION_TYPE,
|
||||
blank=True,
|
||||
null=True,
|
||||
max_length=255,
|
||||
verbose_name=_("subscription type"),
|
||||
duration_membership = models.PositiveIntegerField(
|
||||
default=0, verbose_name=_("duration of the membership (in months)")
|
||||
)
|
||||
duration_days_membership = models.PositiveIntegerField(
|
||||
default=0,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_("duration of the membership (in days, will be added to duration in months)"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -511,34 +491,48 @@ class Vente(RevMixin, AclMixin, models.Model):
|
|||
"""
|
||||
if hasattr(self, "cotisation"):
|
||||
cotisation = self.cotisation
|
||||
cotisation.date_end = cotisation.date_start + relativedelta(
|
||||
months=(self.duration or 0) * self.number,
|
||||
days=(self.duration_days or 0) * self.number,
|
||||
cotisation.date_end_memb = cotisation.date_start_memb + relativedelta(
|
||||
months=(self.duration_membership or 0) * self.number,
|
||||
days=(self.duration_days_membership or 0) * self.number,
|
||||
)
|
||||
cotisation.date_end_con = cotisation.date_start_con + relativedelta(
|
||||
months=(self.duration_connection or 0) * self.number,
|
||||
days=(self.duration_days_connection or 0) * self.number,
|
||||
)
|
||||
return
|
||||
|
||||
def create_cotis(self, date_start=False):
|
||||
def create_cotis(self, date_start_con=False, date_start_memb=False):
|
||||
"""
|
||||
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.
|
||||
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.
|
||||
"""
|
||||
try:
|
||||
invoice = self.facture.facture
|
||||
except Facture.DoesNotExist:
|
||||
return
|
||||
if not hasattr(self, "cotisation") and self.type_cotisation:
|
||||
if not hasattr(self, "cotisation") and self.test_membership_or_connection():
|
||||
cotisation = Cotisation(vente=self)
|
||||
cotisation.type_cotisation = self.type_cotisation
|
||||
if date_start:
|
||||
cotisation.date_start = date_start
|
||||
cotisation.date_end = cotisation.date_start + relativedelta(
|
||||
months=(self.duration or 0) * self.number,
|
||||
days=(self.duration_days or 0) * self.number,
|
||||
if date_start_con:
|
||||
cotisation.date_start_con = date_start_con
|
||||
cotisation.date_end_con = cotisation.date_start_con + relativedelta(
|
||||
months=(self.duration_connection or 0) * self.number,
|
||||
days=(self.duration_days_connection or 0) * self.number,
|
||||
)
|
||||
self.save()
|
||||
cotisation.save()
|
||||
if date_start_memb:
|
||||
cotisation.date_start_memb = date_start_memb
|
||||
cotisation.date_end_memb = cotisation.date_start_memb + relativedelta(
|
||||
months=(self.duration_membership or 0) * self.number,
|
||||
days=(self.duration_days_membership or 0) * self.number,
|
||||
)
|
||||
self.save()
|
||||
cotisation.save()
|
||||
else:
|
||||
cotisation.date_start = invoice.date
|
||||
cotisation.date_end = invoice.date
|
||||
cotisation.date_start_con = invoice.date
|
||||
cotisation.date_start_memb = invoice.date
|
||||
cotisation.date_end_con = invoice.date
|
||||
cotisation.date_end_memb = invoice.date
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""
|
||||
|
@ -546,9 +540,6 @@ class Vente(RevMixin, AclMixin, models.Model):
|
|||
It also update the associated cotisation in the changes have some
|
||||
effect on the user's cotisation
|
||||
"""
|
||||
# Checking that if a cotisation is specified, there is also a duration
|
||||
if self.type_cotisation and not (self.duration or self.duration_days):
|
||||
raise ValidationError(_("Duration must be specified for a subscription."))
|
||||
self.update_cotisation()
|
||||
super(Vente, self).save(*args, **kwargs)
|
||||
|
||||
|
@ -629,6 +620,13 @@ class Vente(RevMixin, AclMixin, models.Model):
|
|||
def __str__(self):
|
||||
return str(self.name) + " " + str(self.facture)
|
||||
|
||||
def test_membership_or_connection(self):
|
||||
""" Test if the purchase include membership or connecton
|
||||
"""
|
||||
return self.duration_membership or \
|
||||
self.duration_days_membership or \
|
||||
self.duration_connection or \
|
||||
self.duration_days_connection
|
||||
|
||||
# TODO : change vente to purchase
|
||||
@receiver(post_save, sender=Vente)
|
||||
|
@ -645,7 +643,7 @@ def vente_post_save(**kwargs):
|
|||
if hasattr(purchase, "cotisation"):
|
||||
purchase.cotisation.vente = purchase
|
||||
purchase.cotisation.save()
|
||||
if purchase.type_cotisation:
|
||||
if purchase.test_membership_or_connection():
|
||||
purchase.create_cotis()
|
||||
purchase.cotisation.save()
|
||||
user = purchase.facture.facture.user
|
||||
|
@ -677,56 +675,60 @@ class Article(RevMixin, AclMixin, models.Model):
|
|||
It's represented by:
|
||||
* a name
|
||||
* a price
|
||||
* a cotisation type (indicating if this article reprensents a
|
||||
cotisation or not)
|
||||
* a duration (if it is a cotisation)
|
||||
* a duration for the membership
|
||||
* a duration for the connection
|
||||
* if the article can be purchased without membership
|
||||
* a type of user (indicating what kind of user can buy this article)
|
||||
"""
|
||||
|
||||
# TODO : Either use TYPE or TYPES in both choices but not both
|
||||
USER_TYPES = (
|
||||
("Adherent", _("Member")),
|
||||
("Club", _("Club")),
|
||||
("All", _("Both of them")),
|
||||
)
|
||||
|
||||
COTISATION_TYPE = (
|
||||
("Connexion", _("Connection")),
|
||||
("Adhesion", _("Membership")),
|
||||
("All", _("Both of them")),
|
||||
)
|
||||
|
||||
name = models.CharField(max_length=255, verbose_name=_("designation"))
|
||||
# TODO : change prix to price
|
||||
prix = models.DecimalField(
|
||||
max_digits=5, decimal_places=2, verbose_name=_("unit price")
|
||||
)
|
||||
duration = models.PositiveIntegerField(
|
||||
|
||||
duration_membership = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_("duration (in months)"),
|
||||
verbose_name=_("duration of the membership (in months)")
|
||||
)
|
||||
duration_days = models.PositiveIntegerField(
|
||||
duration_days_membership = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_("duration (in days, will be added to duration in months)"),
|
||||
verbose_name=_("duration of the membership (in days, will be added to duration in months)"),
|
||||
)
|
||||
duration_connection = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_("duration of the connection (in months)")
|
||||
)
|
||||
duration_days_connection = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_("duration of the connection (in days, will be added to duration in months)"),
|
||||
)
|
||||
|
||||
need_membership = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name=_("need membership to be purchased"),
|
||||
)
|
||||
|
||||
type_user = models.CharField(
|
||||
choices=USER_TYPES,
|
||||
default="All",
|
||||
max_length=255,
|
||||
verbose_name=_("type of users concerned"),
|
||||
)
|
||||
type_cotisation = models.CharField(
|
||||
choices=COTISATION_TYPE,
|
||||
default=None,
|
||||
blank=True,
|
||||
null=True,
|
||||
max_length=255,
|
||||
verbose_name=_("subscription type"),
|
||||
)
|
||||
available_for_everyone = models.BooleanField(
|
||||
default=False, verbose_name=_("is available for every user")
|
||||
)
|
||||
|
@ -744,8 +746,6 @@ class Article(RevMixin, AclMixin, models.Model):
|
|||
def clean(self):
|
||||
if self.name.lower() == "solde":
|
||||
raise ValidationError(_("Solde is a reserved article name."))
|
||||
if self.type_cotisation and not (self.duration or self.duration_days):
|
||||
raise ValidationError(_("Duration must be specified for a subscription."))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -790,9 +790,9 @@ class Article(RevMixin, AclMixin, models.Model):
|
|||
)
|
||||
if target_user is not None and not target_user.is_adherent():
|
||||
objects_pool = objects_pool.filter(
|
||||
Q(type_cotisation="All")
|
||||
| Q(type_cotisation="Adhesion")
|
||||
| Q(type_cotisation__isnull=True)
|
||||
Q(duration_membership__gt=0)
|
||||
|Q(duration_days_membership__gt=0)
|
||||
|Q(need_membership=False)
|
||||
)
|
||||
if user.has_perm("cotisations.buy_every_article"):
|
||||
return objects_pool
|
||||
|
@ -882,7 +882,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
|||
|
||||
# In case a cotisation was bought, inform the user, the
|
||||
# cotisation time has been extended too
|
||||
if any(sell.type_cotisation for sell in invoice.vente_set.all()):
|
||||
if any(sell.test_membership_or_connection() for sell in invoice.vente_set.all()):
|
||||
messages.success(
|
||||
request,
|
||||
_(
|
||||
|
@ -943,31 +943,21 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
|||
The model defining a cotisation. It holds information about the time a user
|
||||
is allowed when he has paid something.
|
||||
It characterised by :
|
||||
* a date_start (the date when the cotisaiton begins/began
|
||||
* a date_end (the date when the cotisation ends/ended
|
||||
* a type of cotisation (which indicates the implication of such
|
||||
cotisation)
|
||||
* a date_start_memb (the date when the membership begins/began
|
||||
* a date_end_memb (the date when the membership ends/ended
|
||||
* a date_start_con (the date when the connection begins/began)
|
||||
* a date_end_con (the date when the connection ends/ended)
|
||||
* a purchase (the related objects this cotisation is linked to)
|
||||
"""
|
||||
|
||||
COTISATION_TYPE = (
|
||||
("Connexion", _("Connection")),
|
||||
("Adhesion", _("Membership")),
|
||||
("All", _("Both of them")),
|
||||
)
|
||||
|
||||
# TODO : change vente to purchase
|
||||
vente = models.OneToOneField(
|
||||
"Vente", on_delete=models.CASCADE, null=True, verbose_name=_("purchase")
|
||||
)
|
||||
type_cotisation = models.CharField(
|
||||
choices=COTISATION_TYPE,
|
||||
max_length=255,
|
||||
default="All",
|
||||
verbose_name=_("subscription type"),
|
||||
)
|
||||
date_start = models.DateTimeField(verbose_name=_("start date"))
|
||||
date_end = models.DateTimeField(verbose_name=_("end date"))
|
||||
date_start_con = models.DateTimeField(verbose_name=_("start date for the connection"))
|
||||
date_end_con = models.DateTimeField(verbose_name=_("end date for the connection"))
|
||||
date_start_memb = models.DateTimeField(verbose_name=_("start date for the membership"))
|
||||
date_end_memb = models.DateTimeField(verbose_name=_("end date for the membership"))
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
|
@ -1037,9 +1027,14 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
|||
return (
|
||||
str(self.vente)
|
||||
+ "from "
|
||||
+ str(self.date_start)
|
||||
+ str(self.date_start_memb)
|
||||
+ " to "
|
||||
+ str(self.date_end)
|
||||
+ str(self.date_end_memb)
|
||||
+ " for membership, "
|
||||
+ str(self.date_start_con)
|
||||
+ " to "
|
||||
+ str(self.date_end_con)
|
||||
+ " for the connection."
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -32,9 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<th>{% trans "Article" %}</th>
|
||||
<th>{% trans "Price" %}</th>
|
||||
<th>{% trans "Subscription type" %}</th>
|
||||
<th>{% trans "Duration (in months)" %}</th>
|
||||
<th>{% trans "Duration (in days)" %}</th>
|
||||
<th>{% trans "Duration membership (in months)" %}</th>
|
||||
<th>{% trans "Duration membership (in days)" %}</th>
|
||||
<th>{% trans "Duration connection (in months)" %}</th>
|
||||
<th>{% trans "Duration connection (in days)" %}</th>
|
||||
<th>{% trans "Concerned users" %}</th>
|
||||
<th>{% trans "Available for everyone" %}</th>
|
||||
<th></th>
|
||||
|
@ -44,9 +45,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>{{ article.name }}</td>
|
||||
<td>{{ article.prix }}</td>
|
||||
<td>{{ article.type_cotisation }}</td>
|
||||
<td>{{ article.duration }}</td>
|
||||
<td>{{ article.duration_days }}</td>
|
||||
<td>{{ article.duration_membership }}</td>
|
||||
<td>{{ article.duration_days_membership }}</td>
|
||||
<td>{{ article.duration_connection }}</td>
|
||||
<td>{{ article.duration_days_connection }}</td>
|
||||
<td>{{ article.type_user }}</td>
|
||||
<td>{{ article.available_for_everyone | tick }}</td>
|
||||
<td class="text-right">
|
||||
|
|
|
@ -19,15 +19,17 @@ class VenteModelTests(TestCase):
|
|||
def test_one_day_cotisation(self):
|
||||
"""
|
||||
It should be possible to have one day membership.
|
||||
Add one day of membership and one day of connection.
|
||||
"""
|
||||
date = timezone.now()
|
||||
purchase = Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration=0,
|
||||
duration_days=1,
|
||||
type_cotisation="All",
|
||||
duration_connection=0,
|
||||
duration_days_connection=1,
|
||||
duration_membership=0,
|
||||
duration_days_membership=1,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
|
@ -36,48 +38,66 @@ class VenteModelTests(TestCase):
|
|||
datetime.timedelta(days=1),
|
||||
delta=datetime.timedelta(seconds=1),
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.user.end_adhesion() - date,
|
||||
datetime.timedelta(days=1),
|
||||
delta=datetime.timedelta(seconds=1),
|
||||
)
|
||||
|
||||
def test_one_month_cotisation(self):
|
||||
"""
|
||||
It should be possible to have one day membership.
|
||||
Add one mounth of membership and one mounth of connection
|
||||
"""
|
||||
date = timezone.now()
|
||||
Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration=1,
|
||||
duration_days=0,
|
||||
type_cotisation="All",
|
||||
duration_connection=1,
|
||||
duration_days_connection=0,
|
||||
duration_membership=1,
|
||||
duration_days_membership=0,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
end = self.user.end_connexion()
|
||||
end_con = self.user.end_connexion()
|
||||
end_memb = self.user.end_adhesion()
|
||||
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)
|
||||
self.assertEqual(end_con.day, expected_end.day)
|
||||
self.assertEqual(end_con.month, expected_end.month)
|
||||
self.assertEqual(end_con.year, expected_end.year)
|
||||
self.assertEqual(end_memb.day, expected_end.day)
|
||||
self.assertEqual(end_memb.month, expected_end.month)
|
||||
self.assertEqual(end_memb.year, expected_end.year)
|
||||
|
||||
def test_one_month_and_one_week_cotisation(self):
|
||||
"""
|
||||
It should be possible to have one day membership.
|
||||
Add one mounth and one week of membership and one mounth
|
||||
and one week of connection
|
||||
"""
|
||||
date = timezone.now()
|
||||
Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration=1,
|
||||
duration_days=7,
|
||||
type_cotisation="All",
|
||||
duration_connection=1,
|
||||
duration_days_connection=7,
|
||||
duration_membership=1,
|
||||
duration_days_membership=7,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
end = self.user.end_connexion()
|
||||
end_con = self.user.end_connexion()
|
||||
end_memb = self.user.end_adhesion()
|
||||
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)
|
||||
self.assertEqual(end_con.day, expected_end.day)
|
||||
self.assertEqual(end_con.month, expected_end.month)
|
||||
self.assertEqual(end_con.year, expected_end.year)
|
||||
self.assertEqual(end_memb.day, expected_end.day)
|
||||
self.assertEqual(end_memb.month, expected_end.month)
|
||||
self.assertEqual(end_memb.year, expected_end.year)
|
||||
|
||||
def test_date_start_cotisation(self):
|
||||
"""
|
||||
|
@ -87,15 +107,140 @@ class VenteModelTests(TestCase):
|
|||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration=0,
|
||||
duration_days=1,
|
||||
type_cotisation = 'All',
|
||||
duration_connection=0,
|
||||
duration_days_connection=1,
|
||||
duration_membership=0,
|
||||
duration_deys_membership=1,
|
||||
prix=0
|
||||
)
|
||||
v.create_cotis(date_start=timezone.make_aware(datetime.datetime(1998, 10, 16)))
|
||||
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
|
||||
v.save()
|
||||
self.assertEqual(v.cotisation.date_end, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
||||
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
||||
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
||||
|
||||
def test_one_day_cotisation_membership_only(self):
|
||||
"""
|
||||
It should be possible to have one day membership without connection.
|
||||
Add one day of membership and no connection.
|
||||
"""
|
||||
date = timezone.now()
|
||||
purchase = Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration_connection=0,
|
||||
duration_days_connection=0,
|
||||
duration_membership=0,
|
||||
duration_days_membership=1,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
self.assertEqual(
|
||||
self.user.end_connexion(),
|
||||
None,
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.user.end_adhesion() - date,
|
||||
datetime.timedelta(days=1),
|
||||
delta=datetime.timedelta(seconds=1),
|
||||
)
|
||||
|
||||
def test_one_month_cotisation_membership_only(self):
|
||||
"""
|
||||
It should be possible to have one month membership.
|
||||
Add one mounth of membership and no connection
|
||||
"""
|
||||
date = timezone.now()
|
||||
Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration_connection=0,
|
||||
duration_days_connection=0,
|
||||
duration_membership=1,
|
||||
duration_days_membership=0,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
end_con = self.user.end_connexion()
|
||||
end_memb = self.user.end_adhesion()
|
||||
expected_end = date + relativedelta(months=1)
|
||||
self.assertEqual(end_con, None)
|
||||
self.assertEqual(end_memb.day, expected_end.day)
|
||||
self.assertEqual(end_memb.month, expected_end.month)
|
||||
self.assertEqual(end_memb.year, expected_end.year)
|
||||
|
||||
def test_one_month_and_one_week_cotisation_membership_only(self):
|
||||
"""
|
||||
It should be possible to have one mounth and one week membership.
|
||||
Add one mounth and one week of membership and no connection.
|
||||
"""
|
||||
date = timezone.now()
|
||||
Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration_connection=0,
|
||||
duration_days_connection=0,
|
||||
duration_membership=1,
|
||||
duration_days_membership=7,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
end_con = self.user.end_connexion()
|
||||
end_memb = self.user.end_adhesion()
|
||||
expected_end = date + relativedelta(months=1, days=7)
|
||||
self.assertEqual(end_con, None)
|
||||
self.assertEqual(end_memb.day, expected_end.day)
|
||||
self.assertEqual(end_memb.month, expected_end.month)
|
||||
self.assertEqual(end_memb.year, expected_end.year)
|
||||
|
||||
def test_date_start_cotisation_membership_only(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_connection=0,
|
||||
duration_days_connection=0,
|
||||
duration_membership=0,
|
||||
duration_days_membership=1,
|
||||
prix=0
|
||||
)
|
||||
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
|
||||
v.save()
|
||||
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
||||
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 16)))
|
||||
|
||||
def test_cotisation_membership_diff_connection(self):
|
||||
"""
|
||||
It should be possible to have purchase a membership longer
|
||||
than the connection.
|
||||
"""
|
||||
date = timezone.now()
|
||||
Vente.objects.create(
|
||||
facture=self.f,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration_connection=1,
|
||||
duration_days_connection=0,
|
||||
duration_membership=2,
|
||||
duration_days_membership=0,
|
||||
prix=0,
|
||||
)
|
||||
self.f.reorder_purchases()
|
||||
end_con = self.user.end_connexion()
|
||||
end_memb = self.user.end_adhesion()
|
||||
expected_end_con = date + relativedelta(months=1)
|
||||
expected_end_memb = date + relativedelta(months=2)
|
||||
self.assertEqual(end_con.day, expected_end_con.day)
|
||||
self.assertEqual(end_con.month, expected_end_con.month)
|
||||
self.assertEqual(end_con.year, expected_end_con.year)
|
||||
self.assertEqual(end_memb.day, expected_end_memb.day)
|
||||
self.assertEqual(end_memb.month, expected_end_memb.month)
|
||||
self.assertEqual(end_memb.year, expected_end_memb.year)
|
||||
|
||||
def tearDown(self):
|
||||
self.f.delete()
|
||||
|
@ -121,9 +266,10 @@ class FactureModelTests(TestCase):
|
|||
facture=invoice1,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration=1,
|
||||
duration_days=0,
|
||||
type_cotisation="All",
|
||||
duration_connection=1,
|
||||
duration_days_connection=0,
|
||||
duration_membership=1,
|
||||
duration_days_membership=0,
|
||||
prix=0,
|
||||
)
|
||||
invoice1.reorder_purchases()
|
||||
|
@ -134,16 +280,20 @@ class FactureModelTests(TestCase):
|
|||
facture=invoice2,
|
||||
number=1,
|
||||
name="Test purchase",
|
||||
duration=1,
|
||||
duration_days=0,
|
||||
type_cotisation="All",
|
||||
duration_connection=1,
|
||||
duration_days_connection=0,
|
||||
duration_membership=1,
|
||||
duration_days_membership=0,
|
||||
prix=0,
|
||||
)
|
||||
invoice1.reorder_purchases()
|
||||
delta = relativedelta(self.user.end_connexion(), date)
|
||||
delta.microseconds = 0
|
||||
delta_con = relativedelta(self.user.end_connexion(), date)
|
||||
delta_memb = relativedelta(self.user.end_adhesion(), date)
|
||||
delta_con.microseconds = 0
|
||||
delta_memb.microseconds = 0
|
||||
try:
|
||||
self.assertEqual(delta, relativedelta(months=2))
|
||||
self.assertEqual(delta_con, relativedelta(months=2))
|
||||
self.assertEqual(delta_memb, relativedelta(months=2))
|
||||
except Exception as e:
|
||||
invoice1.delete()
|
||||
invoice2.delete()
|
||||
|
|
|
@ -38,25 +38,28 @@ class NewFactureTests(TestCase):
|
|||
self.article_one_day = Article.objects.create(
|
||||
name="One day",
|
||||
prix=0,
|
||||
duration=0,
|
||||
duration_days=1,
|
||||
type_cotisation="All",
|
||||
duration_connection=0,
|
||||
duration_days_connection=1,
|
||||
duration_membership=0,
|
||||
duration_days_membership=1,
|
||||
available_for_everyone=True,
|
||||
)
|
||||
self.article_one_month = Article.objects.create(
|
||||
name="One day",
|
||||
name="One mounth",
|
||||
prix=0,
|
||||
duration=1,
|
||||
duration_days=0,
|
||||
type_cotisation="All",
|
||||
duration_connection=1,
|
||||
duration_days_connection=0,
|
||||
duration_membership=1,
|
||||
duration_days_membership=0,
|
||||
available_for_everyone=True,
|
||||
)
|
||||
self.article_one_month_and_one_week = Article.objects.create(
|
||||
name="One day",
|
||||
name="One mounth and one week",
|
||||
prix=0,
|
||||
duration=1,
|
||||
duration_days=7,
|
||||
type_cotisation="All",
|
||||
duration_connection=1,
|
||||
duration_days_connection=7,
|
||||
duration_membership=1,
|
||||
duration_days_membership=7,
|
||||
available_for_everyone=True,
|
||||
)
|
||||
self.client.login(username="testUser", password="plopiplop")
|
||||
|
|
|
@ -105,8 +105,8 @@ def send_mail_voucher(invoice, request=None):
|
|||
"lastname": invoice.user.surname,
|
||||
"email": invoice.user.email,
|
||||
"phone": invoice.user.telephone,
|
||||
"date_end": invoice.get_subscription().latest("date_end").date_end,
|
||||
"date_begin": invoice.get_subscription().earliest("date_start").date_start,
|
||||
"date_end": invoice.get_subscription().latest("date_end").date_end_memb,
|
||||
"date_begin": invoice.get_subscription().earliest("date_start").date_start_memb,
|
||||
}
|
||||
templatename = CotisationsOption.get_cached_value(
|
||||
"voucher_template"
|
||||
|
@ -118,7 +118,7 @@ def send_mail_voucher(invoice, request=None):
|
|||
"name": "{} {}".format(invoice.user.name, invoice.user.surname),
|
||||
"asso_email": AssoOption.get_cached_value("contact"),
|
||||
"asso_name": AssoOption.get_cached_value("name"),
|
||||
"date_end": invoice.get_subscription().latest("date_end").date_end,
|
||||
"date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb,
|
||||
}
|
||||
|
||||
mail = EmailMessage(
|
||||
|
|
|
@ -130,9 +130,10 @@ def new_facture(request, user, userid):
|
|||
facture=new_invoice_instance,
|
||||
name=article.name,
|
||||
prix=article.prix,
|
||||
type_cotisation=article.type_cotisation,
|
||||
duration=article.duration,
|
||||
duration_days=article.duration_days,
|
||||
duration_connection=article.duration_connection,
|
||||
duration_days_connection=article.duration_days_connection,
|
||||
duration_membership=article.duration_membership,
|
||||
duration_days_membership=article.duration_days_membership,
|
||||
number=quantity,
|
||||
)
|
||||
purchases.append(new_purchase)
|
||||
|
@ -262,8 +263,10 @@ def new_custom_invoice(request):
|
|||
facture=new_invoice_instance,
|
||||
name=article.name,
|
||||
prix=article.prix,
|
||||
type_cotisation=article.type_cotisation,
|
||||
duration=article.duration,
|
||||
duration_membership=article.duration_membership,
|
||||
duration_days_membership=article.duration_membership,
|
||||
duration_connection=article.duration_connection,
|
||||
duration_days_connection=article.duration_days_connection,
|
||||
number=quantity,
|
||||
)
|
||||
discount_form.apply_to_invoice(new_invoice_instance)
|
||||
|
|
|
@ -93,13 +93,10 @@ def all_adherent(search_time=None, including_asso=True):
|
|||
search_time = timezone.now()
|
||||
filter_user = Q(
|
||||
facture__in=Facture.objects.filter(
|
||||
vente__in=Vente.objects.filter(
|
||||
Q(type_cotisation="All") | Q(type_cotisation="Adhesion"),
|
||||
cotisation__in=Cotisation.objects.filter(
|
||||
vente__in=Vente.objects.filter(
|
||||
facture__in=Facture.objects.all().exclude(valid=False)
|
||||
)
|
||||
).filter(Q(date_start__lt=search_time) & Q(date_end__gt=search_time)),
|
||||
vente__cotisation__in=Cotisation.objects.filter(
|
||||
Q(vente__facture__facture__valid=True) &
|
||||
Q(date_start_memb__lt=search_time) &
|
||||
Q(date_end_memb__gt=search_time)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -186,15 +183,12 @@ def all_has_access(search_time=None, including_asso=True):
|
|||
)
|
||||
| Q(
|
||||
facture__in=Facture.objects.filter(
|
||||
vente__in=Vente.objects.filter(
|
||||
cotisation__in=Cotisation.objects.filter(
|
||||
Q(type_cotisation="All") | Q(type_cotisation="Connexion"),
|
||||
vente__in=Vente.objects.filter(
|
||||
facture__in=Facture.objects.all().exclude(valid=False)
|
||||
),
|
||||
).filter(
|
||||
Q(date_start__lt=search_time) & Q(date_end__gt=search_time)
|
||||
)
|
||||
vente__cotisation__in=Cotisation.objects.filter(
|
||||
Q(vente__facture__facture__valid=True) &
|
||||
Q(date_start_con__lt=search_time) &
|
||||
Q(date_end_con__gt=search_time) &
|
||||
Q(date_start_memb__lt=search_time) &
|
||||
Q(date_end_memb__gt=search_time)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -703,8 +703,7 @@ class User(
|
|||
facture__in=Facture.objects.filter(user=self).exclude(valid=False)
|
||||
)
|
||||
)
|
||||
.filter(Q(type_cotisation="All") | Q(type_cotisation="Adhesion"))
|
||||
.aggregate(models.Max("date_end"))["date_end__max"]
|
||||
.aggregate(models.Max("date_end_memb"))["date_end_memb__max"]
|
||||
)
|
||||
return date_max
|
||||
|
||||
|
@ -724,8 +723,7 @@ class User(
|
|||
facture__in=Facture.objects.filter(user=self).exclude(valid=False)
|
||||
)
|
||||
)
|
||||
.filter(Q(type_cotisation="All") | Q(type_cotisation="Connexion"))
|
||||
.aggregate(models.Max("date_end"))["date_end__max"]
|
||||
.aggregate(models.Max("date_end_con"))["date_end_con__max"]
|
||||
)
|
||||
return date_max
|
||||
|
||||
|
@ -746,6 +744,10 @@ class User(
|
|||
return False
|
||||
else:
|
||||
return True
|
||||
# it looks wrong, we should check if there is a cotisation where
|
||||
# were date_start_memb < timezone.now() < date_end_memb,
|
||||
# in case the user purshased a cotisation starting in the futur
|
||||
# somehow
|
||||
|
||||
def is_connected(self):
|
||||
"""Methods, calculate and returns if the user has a valid membership AND a
|
||||
|
@ -765,6 +767,10 @@ class User(
|
|||
return False
|
||||
else:
|
||||
return self.is_adherent()
|
||||
# it looks wrong, we should check if there is a cotisation where
|
||||
# were date_start_con < timezone.now() < date_end_con,
|
||||
# in case the user purshased a cotisation starting in the futur
|
||||
# somehow
|
||||
|
||||
def end_ban(self):
|
||||
"""Methods, calculate and returns the end of a ban value date
|
||||
|
@ -925,11 +931,13 @@ class User(
|
|||
|
||||
"""
|
||||
if self.state == self.STATE_NOT_YET_ACTIVE:
|
||||
if self.facture_set.filter(valid=True).filter(
|
||||
Q(vente__type_cotisation="All") | Q(vente__type_cotisation="Adhesion")
|
||||
).exists() or OptionalUser.get_cached_value("all_users_active"):
|
||||
self.state = self.STATE_ACTIVE
|
||||
self.save()
|
||||
# Look for ventes with non 0 subscription duration in the invoices set
|
||||
not_zero = self.facture_set.filter(valid=True).exclude(Q(vente__duration_membership=0)).exists()
|
||||
days_not_zero = self.facture_set.filter(valid=True).exclude(Q(vente__duration_days_membership=0)).exists()
|
||||
if(not_zero or days_not_zero\
|
||||
or OptionalUser.get_cached_value("all_users_active")):
|
||||
self.state = self.STATE_ACTIVE
|
||||
self.save()
|
||||
if self.state == self.STATE_ARCHIVE or self.state == self.STATE_FULL_ARCHIVE:
|
||||
self.state = self.STATE_ACTIVE
|
||||
self.unarchive()
|
||||
|
|
Loading…
Reference in a new issue