]> git.parisson.com Git - teleforma.git/commitdiff
Auto-generating payment objects
authorGael Le Mignot <gael@pilotsystems.net>
Fri, 3 Sep 2021 10:20:16 +0000 (12:20 +0200)
committerGael Le Mignot <gael@pilotsystems.net>
Fri, 3 Sep 2021 10:20:16 +0000 (12:20 +0200)
app/settings.py
teleforma/migrations/0010_student_payment_generated.py [new file with mode: 0644]
teleforma/models/crfpa.py
teleforma/views/crfpa.py

index 1a9382c756a2aa8bc6382042a48f04fdfe60db8a..b37cf04588c18d1521134d9f5e0660911e3903af 100644 (file)
@@ -472,6 +472,8 @@ PAYMENT_PARAMETERS = { 'merchant_id' : { 'Semestrielle': "014295303911111",
 }
 
 
+ORAL_OPTION_PRICE = 250
+
 if DEBUG_TOOLBAR:
     def show_toolbar(request):
         return True
diff --git a/teleforma/migrations/0010_student_payment_generated.py b/teleforma/migrations/0010_student_payment_generated.py
new file mode 100644 (file)
index 0000000..bc22ba2
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.3 on 2021-09-02 16:32
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('teleforma', '0009_alter_conference_public_id'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='student',
+            name='payment_generated',
+            field=models.BooleanField(default=False, verbose_name='échances générées'),
+        ),
+    ]
index 7f482a3fa9e64dd5d5a51c8770ced06f64483f63..8fbe6a53d264731ed5f64337b6c1ea14ff951c6d 100755 (executable)
@@ -36,6 +36,7 @@
 
 import datetime
 
+from django.conf import settings
 import django.db.models as models
 from django.contrib.auth.models import User
 from django.db.models import signals
@@ -207,6 +208,8 @@ class Student(models.Model):
     receipt_id = models.IntegerField('numéro de facture', blank=True, null=True,
                                      unique=True)
 
+    payment_generated = models.BooleanField(_('échances générées'), default=False)
+
     def __str__(self):
         try:
             return self.user.last_name + ' ' + self.user.first_name
@@ -280,6 +283,106 @@ class Student(models.Model):
     def get_absolute_url(self):
         return reverse_lazy('teleforma-profile-detail', kwargs={'username': self.user.username})
 
+    def create_payment_objects(self):
+        """
+        Create the payment objects if the account is activated
+        """
+        if not self.is_subscribed:
+            return
+
+        if self.payment_generated:
+            return
+
+        # If we already have some payments, then forget about it
+        if self.payments.count():
+            self.payment_generated = True
+            self.save()
+            return
+
+        total = self.total_fees
+        oneday = datetime.timedelta(days = 1)
+        tomorrow = datetime.date.today() + oneday
+        period = self.period
+
+        oral_1 = self.oral_1 and self.oral_1.title != 'Aucune'
+        oral_price = settings.ORAL_OPTION_PRICE if oral_1 else 0
+        total -= oral_price
+        
+        def endofmonth(year, month):
+            """
+            Get the last day of a month. To avoid quirky varying month length,
+            we look at 01/m+1 and then remove one day
+            """
+            month = month + 1
+            if month > 12:
+                year += 1
+                month -= 12
+            return datetime.date(year, month + 1, 1) - oneday
+
+        oral_date = None
+        payments = None
+        # Full or partial ?
+        if self.payment_schedule == 'split':
+            if period.name == 'Semestrielle':
+                part = int(total * 0.25)
+                remaining = total - 3 * part
+                payments = ((remaining, tomorrow),)
+                if tomorrow.month <= 6:                    
+                    # Late registration, so next three end of month
+                    for i in range(3):                        
+                        date = endofmonth(tomorrow.year, tomorrow.month + 1 + i)
+                        payments += ((part, date),)
+                    oral_date = endofmonth(tomorrow.year, 6)
+                else:
+                    # Normal registration, so first three months of next year
+                    for month in (1, 2, 3):
+                        # To avoid quirky varying month length, we
+                        # look at 01/m+1 and then remove one day
+                        date = endofmonth(tomorrow.year + 1, month)
+                        payments += ((part, date),)
+                    oral_date = endofmonth(tomorrow.year + 1, 6)
+            elif period.name == 'Estivale':
+                part = int(total * 0.35)
+                remaining = total - 2 * part
+                payments = ((remaining, tomorrow),)
+                if tomorrow.month >= 6:
+                    # Late registration, so next two end of month
+                    for i in range(2):
+                        # To avoid quirky varying month length, we
+                        # look at 01/m+1 and then remove one day
+                        date = endofmonth(tomorrow.year, tomorrow.month + 1 + i)
+                        payments += ((part, date),)
+                    oral_date = endofmonth(tomorrow.year, 8)
+                else:
+                    # Normal registration, so end of june and end of
+                    # july
+                    for month in (6, 7):
+                        # To avoid quirky varying month length, we
+                        # look at 01/m+1 and then remove one day
+                        date = endofmonth(tomorrow.year, month)
+                        payments += ((part, date),)
+                    oral_date = endofmonth(tomorrow.year, 8)
+        elif self.payment_schedule == 'once':
+            payments = ((total, tomorrow), )
+
+        if not payments:
+            return
+
+        if oral_price and oral_date:
+            payments += ((oral_price, oral_date), )
+        
+        self.payment_generated = True
+        self.save()
+
+        for amount, date in payments:
+            payment = Payment(student = self,
+                              value = amount,
+                              month = date.month,
+                              scheduled = date,
+                              online_paid = False,
+                              type = self.payment_type)
+            payment.save()
+    
     class Meta(MetaCore):
         db_table = app_label + '_' + 'student'
         verbose_name = _('Student')
@@ -294,7 +397,11 @@ def update_balance_signal(sender, instance, *args, **kwargs):
         instance.student.update_balance()
 
 
+def create_payment_objects(sender, instance, *args, **kwargs):
+    instance.create_payment_objects()
+        
 signals.post_save.connect(update_balance_signal)
+signals.post_save.connect(create_payment_objects, sender=Student)
 signals.post_delete.connect(update_balance_signal)
 
 
@@ -462,7 +569,7 @@ class Home(models.Model):
         verbose_name = "Page d'accueil"
         verbose_name_plural = "Page d'accueil"
 
-    def is_for_period(self, period):
+    def is_for_period(self, period):        
         """
         Check if it's available for given period
         """
index 62a7bf04285ce3d251fec6ab619c41bacc7c5f50..aa1a50036ea94c5c40149c87204a4f07b912bcf5 100644 (file)
@@ -57,6 +57,7 @@ from django.views.generic.list import ListView
 from postman.forms import AnonymousWriteForm
 from postman.views import WriteView as PostmanWriteView
 from xlwt import Workbook
+from django.conf import settings
 
 from ..decorators import access_required
 from ..forms import (CorrectorForm, NewsItemForm, UserForm, WriteForm,
@@ -68,8 +69,6 @@ from ..views.core import (PDFTemplateResponseMixin, format_courses,
                           get_courses, get_periods)
 from ..views.profile import ProfileView
 
-ORAL_OPTION_PRICE = 250
-
 def get_course_code(obj):
     if obj:
         return str(obj.code)
@@ -877,7 +876,7 @@ class ReceiptPDFView(PDFTemplateResponseMixin, TemplateView):
         oral_1 = student.oral_1 and student.oral_1.title != 'Aucune'
 
         if oral_1:
-            substract += ORAL_OPTION_PRICE
+            substract += settings.ORAL_OPTION_PRICE
 
         items.append({ 'label': label,
                        'unit_price': student.total_fees - substract - student.total_discount,
@@ -885,7 +884,7 @@ class ReceiptPDFView(PDFTemplateResponseMixin, TemplateView):
                        'discount': student.total_discount, }, )
         if oral_1:
             items.append({ 'label': "<b>Option langue</b>",
-                           'unit_price': ORAL_OPTION_PRICE,
+                           'unit_price': settings.ORAL_OPTION_PRICE,
                            'amount': 1,
                            'discount': 0, }, )
         for item in items: