diff --git a/gestion/templates/gestion/divide.html b/gestion/templates/gestion/divide.html new file mode 100644 index 0000000..f4d3465 --- /dev/null +++ b/gestion/templates/gestion/divide.html @@ -0,0 +1,77 @@ +{% extends 'base.html' %} +{% block entete %}Répartition des cotisations{% endblock %} +{% block navbar %} + +{% endblock %} +{% block content %} +
+
+

Répartition des cotisations

+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
ChampValeur
Nombre de cotisations non réparties{{total_cotisations}}
Valeur totale des cotisations non réparties{{total_amount}} €
Valeur à donner au Club Phœnix Technopôle Metz{{total_amount_ptm}} €
+
+
+ {% csrf_token %} + +
+

Attention, cliquer sur ce bouton marquera toutes les cotisations actuellement non réparties comme réparties. L'historique de cette action n'est pas simple à obtenir et l'action peut être considérée comme irreversible.

+
+
+
+
+

Historique des répartitions

+
+
+
+ + + + + + + + + + + + {% for divide_history in divide_histories %} + + + + + + + + {% endfor %} + +
DateNombre de cotisationsMontant des cotisationsMontant des cotisations pourle PhœnixCoopeman
{{ divide_history.date }}{{ divide_history.total_cotisations }}{{ divide_history.total_cotisations_amount }} €{{ divide_history.total_ptm_amount }} €{{ divide_history.coopeman }}
+
+
+
+{% endblock %} diff --git a/gestion/urls.py b/gestion/urls.py index 3e1cb1e..eb43a2b 100644 --- a/gestion/urls.py +++ b/gestion/urls.py @@ -53,4 +53,5 @@ urlpatterns = [ path('categoriesList', views.categoriesList, name="categoriesList"), path('categories-autocomplete', views.CategoriesAutocomplete.as_view(), name="categories-autocomplete"), path('stats', views.stats, name="stats"), + path('divide', views.divide, name="divide"), ] \ No newline at end of file diff --git a/gestion/views.py b/gestion/views.py index 54f10c5..b98471e 100644 --- a/gestion/views.py +++ b/gestion/views.py @@ -21,7 +21,7 @@ from decimal import * from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm, CategoryForm, SearchCategoryForm from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload, Refund, Category from users.models import School -from preferences.models import PaymentMethod, GeneralPreferences, Cotisation +from preferences.models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory from users.models import CotisationHistory @active_required @@ -877,7 +877,39 @@ def gen_releve(request): else: return render(request, "form.html", {"form": form, "form_title": "Génération d'un relevé", "form_button": "Générer", "form_button_icon": "file-pdf"}) - +@active_required +@login_required +@permission_required('preferences.can_divide') +def divide(request): + """ + Divide all non-divided cotisation + """ + if request.POST: + non_divided_cotisations = CotisationHistory.objects.filter(divided=False) + for cotisation_history in non_divided_cotisations: + cotisation_history.divided = True + cotisation_history.save() + divide_history = DivideHistory( + total_cotisations = non_divided_cotisations.count(), + total_cotisations_amount = sum([x.amount for x in non_divided_cotisations]), + total_ptm_amount = sum([x.amount_ptm for x in non_divided_cotisations]), + coopeman = request.user + ) + divide_history.save() + non_divided_cotisations = CotisationHistory.objects.filter(divided=False) + total_amount = sum([x.amount for x in non_divided_cotisations]) + total_amount_ptm = sum([x.amount_ptm for x in non_divided_cotisations]) + divide_histories = DivideHistory.objects.all().order_by('-date') + return render( + request, + "gestion/divide.html", + { + "total_cotisations": non_divided_cotisations.count(), + "total_amount": total_amount, + "total_amount_ptm": total_amount_ptm, + "divide_histories": divide_histories, + } + ) ########## categories ########## @active_required @login_required diff --git a/preferences/admin.py b/preferences/admin.py index 1641d96..18518f8 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from simple_history.admin import SimpleHistoryAdmin -from .models import PaymentMethod, GeneralPreferences, Cotisation +from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory class CotisationAdmin(SimpleHistoryAdmin): """ @@ -24,6 +24,14 @@ class PaymentMethodAdmin(SimpleHistoryAdmin): search_fields = ('name',) list_filter = ('is_active', 'is_usable_in_cotisation', 'is_usable_in_reload', 'affect_balance') +class DivideHistoryAdmin(SimpleHistoryAdmin): + """ + The admin class for Divide histories + """ + list_display = ('date', 'total_cotisations', 'total_cotisations_amount', 'total_ptm_amount', 'coopeman') + ordering = ('-date',) + admin.site.register(PaymentMethod, PaymentMethodAdmin) admin.site.register(GeneralPreferences, GeneralPreferencesAdmin) -admin.site.register(Cotisation, CotisationAdmin) \ No newline at end of file +admin.site.register(Cotisation, CotisationAdmin) +admin.site.register(DivideHistory, DivideHistoryAdmin) \ No newline at end of file diff --git a/preferences/forms.py b/preferences/forms.py index 52a7dc6..3adfe9d 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -11,6 +11,12 @@ class CotisationForm(forms.ModelForm): model = Cotisation fields = "__all__" + def clean(self): + cleaned_data = super().clean() + if cleaned_data.get("amount_ptm") > cleaned_data.get("amount"): + raise ValidationError("La quantité d'argent donnée au club doit être inférieure à\ + la quantité d'argent totale") + class PaymentMethodForm(forms.ModelForm): """ Form to add and edit :class:`~preferences.models.PaymentMethod`. diff --git a/preferences/migrations/0013_auto_20190622_2334.py b/preferences/migrations/0013_auto_20190622_2334.py new file mode 100644 index 0000000..6d0541b --- /dev/null +++ b/preferences/migrations/0013_auto_20190622_2334.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1 on 2019-06-22 21:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0012_auto_20190428_1327'), + ] + + operations = [ + migrations.AddField( + model_name='cotisation', + name='amount_ptm', + field=models.DecimalField(decimal_places=2, max_digits=5, null=True, verbose_name='Montant pour le club Phœnix Technopôle Metz'), + ), + migrations.AddField( + model_name='historicalcotisation', + name='amount_ptm', + field=models.DecimalField(decimal_places=2, max_digits=5, null=True, verbose_name='Montant pour le club Phœnix Technopôle Metz'), + ), + ] diff --git a/preferences/migrations/0014_auto_20190623_0957.py b/preferences/migrations/0014_auto_20190623_0957.py new file mode 100644 index 0000000..ee219f6 --- /dev/null +++ b/preferences/migrations/0014_auto_20190623_0957.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1 on 2019-06-23 07:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0013_auto_20190622_2334'), + ] + + operations = [ + migrations.AlterModelOptions( + name='cotisation', + options={'permissions': (('can_divide', 'Can divide money for cotisation'),)}, + ), + ] diff --git a/preferences/migrations/0015_dividehistory.py b/preferences/migrations/0015_dividehistory.py new file mode 100644 index 0000000..4f48268 --- /dev/null +++ b/preferences/migrations/0015_dividehistory.py @@ -0,0 +1,30 @@ +# Generated by Django 2.1 on 2019-06-23 08:49 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('preferences', '0014_auto_20190623_0957'), + ] + + operations = [ + migrations.CreateModel( + name='DivideHistory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('total_cotisations', models.IntegerField(verbose_name='Nombre de cotisations')), + ('total_cotisations_amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Montant total des cotisations')), + ('total_ptm_amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Montant donné au Phœnix Technopôle Metz')), + ('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='divide_realized', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Historique répartition', + }, + ), + ] diff --git a/preferences/models.py b/preferences/models.py index e854bd7..fcbf07d 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -1,6 +1,7 @@ from django.db import models from simple_history.models import HistoricalRecords from django.core.validators import MinValueValidator +from django.contrib.auth.models import User class PaymentMethod(models.Model): @@ -118,6 +119,8 @@ class Cotisation(models.Model): """ Stores cotisations. """ + class Meta: + permissions = (("can_divide", "Can divide money for cotisation"),) amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, verbose_name="Montant", validators=[MinValueValidator(0)]) """ Price of the cotisation. @@ -126,6 +129,10 @@ class Cotisation(models.Model): """ Duration (in days) of the cotisation """ + amount_ptm = models.DecimalField(max_digits=5, decimal_places=2, null=True, verbose_name="Montant pour le club Phœnix Technopôle Metz") + """ + Amount of money given to the PTM club + """ history = HistoricalRecords() def __str__(self): @@ -134,3 +141,35 @@ class Cotisation(models.Model): else: jour = "jours" return "Cotisation de " + str(self.duration) + " " + jour + " pour le prix de " + str(self.amount) + "€" + +class DivideHistory(models.Model): + """ + Stores divide history + """ + class Meta: + verbose_name = "Historique répartition" + + date = models.DateTimeField(auto_now_add=True) + """ + Date of the divide + """ + total_cotisations = models.IntegerField(verbose_name="Nombre de cotisations") + """ + Number of non-divided cotisations (before the divide) + """ + total_cotisations_amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant total des cotisations") + """ + Amount of non-divided cotisations (before the divide) + """ + total_ptm_amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant donné au Phœnix Technopôle Metz") + """ + Amount given to the PTM + """ + coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="divide_realized") + """ + Coopeman (:class:`django.contrib.auth.models.User`) who collected the reload. + """ + + def __str__(self): + return "Répartition du " + str(self.date) + \ No newline at end of file diff --git a/preferences/templates/preferences/cotisations_index.html b/preferences/templates/preferences/cotisations_index.html index bc52519..e26fc54 100644 --- a/preferences/templates/preferences/cotisations_index.html +++ b/preferences/templates/preferences/cotisations_index.html @@ -19,6 +19,7 @@ Durée de cotisation Prix + Pour PhœnixTM Administration @@ -27,6 +28,7 @@ {{ cotisation.duration }} jour{{ cotisation.duration|pluralize }} {{ cotisation.amount }} € + {{ cotisation.amount_ptm | default:0}} € {% if perms.preferences.change_cotisation %} Modifier {% endif %}{% if perms.preferences.delete_cotisation %} Supprimer{% endif %} {% endfor %} diff --git a/templates/nav.html b/templates/nav.html index db0e34d..71b38d2 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -32,9 +32,14 @@ {% if request.user.is_staff %} Stats - + - Comptabilité + Relevé + +{% endif %} +{% if perms.preferences.can_divide %} + + Répartition {% endif %} {% if perms.preferences.view_cotisation %} diff --git a/users/migrations/0007_auto_20190623_0957.py b/users/migrations/0007_auto_20190623_0957.py new file mode 100644 index 0000000..42f5dc8 --- /dev/null +++ b/users/migrations/0007_auto_20190623_0957.py @@ -0,0 +1,42 @@ +# Generated by Django 2.1 on 2019-06-23 07:57 + +from django.db import migrations, models + +def update(apps, schema_editor): + CotisationHistory = apps.get_model('users', 'CotisationHistory') + for cotisation_history in CotisationHistory.objects.all(): + cotisation_history.amount_ptm = cotisation_history.cotisation.amount_ptm + cotisation_history.save() + +def reverse_update(apps, schema_editor): + pass + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0006_auto_20190611_0105'), + ] + + operations = [ + migrations.AddField( + model_name='cotisationhistory', + name='amount_ptm', + field=models.DecimalField(decimal_places=2, max_digits=5, null=True, verbose_name='Montant pour le club Phœnix Technopôle Metz'), + ), + migrations.AddField( + model_name='cotisationhistory', + name='divided', + field=models.BooleanField(default=False, verbose_name='Répartition'), + ), + migrations.AddField( + model_name='historicalcotisationhistory', + name='amount_ptm', + field=models.DecimalField(decimal_places=2, max_digits=5, null=True, verbose_name='Montant pour le club Phœnix Technopôle Metz'), + ), + migrations.AddField( + model_name='historicalcotisationhistory', + name='divided', + field=models.BooleanField(default=False, verbose_name='Répartition'), + ), + migrations.RunPython(update, reverse_update) + ] diff --git a/users/models.py b/users/models.py index 64e5700..824bd92 100644 --- a/users/models.py +++ b/users/models.py @@ -61,6 +61,14 @@ class CotisationHistory(models.Model): """ User (:class:`django.contrib.auth.models.User`) who registered the cotisation. """ + divided = models.BooleanField(default=False, verbose_name="Répartition") + """ + True if money of cotisation have been divided between CTM and PTM + """ + amount_ptm = models.DecimalField(max_digits=5, decimal_places=2, null=True, verbose_name="Montant pour le club Phœnix Technopôle Metz") + """ + Amount of money given to the PTM club + """ history = HistoricalRecords() class WhiteListHistory(models.Model): diff --git a/users/views.py b/users/views.py index 790a1b9..a04272a 100644 --- a/users/views.py +++ b/users/views.py @@ -583,6 +583,7 @@ def addCotisationHistory(request, pk): cotisation.coopeman = request.user cotisation.amount = cotisation.cotisation.amount cotisation.duration = cotisation.cotisation.duration + cotisation.amount_ptm = cotisation.cotisation.amount_ptm if(user.profile.cotisationEnd and user.profile.cotisationEnd > timezone.now()): cotisation.endDate = user.profile.cotisationEnd + timedelta(days=cotisation.cotisation.duration) else: