diff --git a/coopeV3/utils.py b/coopeV3/utils.py new file mode 100644 index 0000000..d732603 --- /dev/null +++ b/coopeV3/utils.py @@ -0,0 +1,7 @@ +import math + +def compute_price(price, a, b, c, alpha): + if price < a: + return price * (a + b * math.exp(-c/(price-alpha)**2)) + else: + return price * a diff --git a/preferences/admin.py b/preferences/admin.py index 18518f8..626fa75 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, DivideHistory +from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile class CotisationAdmin(SimpleHistoryAdmin): """ @@ -24,6 +24,15 @@ class PaymentMethodAdmin(SimpleHistoryAdmin): search_fields = ('name',) list_filter = ('is_active', 'is_usable_in_cotisation', 'is_usable_in_reload', 'affect_balance') +class PriceProfileAdmin(SimpleHistoryAdmin): + """ + The admin class for :class:`Consumptions `. + """ + list_display = ('name', 'a', 'b', 'c', 'alpha', 'use_for_draft') + ordering = ('name',) + search_fields = ('name',) + list_filter = ('use_for_draft',) + class DivideHistoryAdmin(SimpleHistoryAdmin): """ The admin class for Divide histories @@ -34,4 +43,5 @@ class DivideHistoryAdmin(SimpleHistoryAdmin): admin.site.register(PaymentMethod, PaymentMethodAdmin) admin.site.register(GeneralPreferences, GeneralPreferencesAdmin) admin.site.register(Cotisation, CotisationAdmin) +admin.site.register(PriceProfile, PriceProfileAdmin) admin.site.register(DivideHistory, DivideHistoryAdmin) \ No newline at end of file diff --git a/preferences/forms.py b/preferences/forms.py index 3adfe9d..1ccd939 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -1,7 +1,7 @@ from django import forms from django.core.exceptions import ValidationError -from .models import Cotisation, PaymentMethod, GeneralPreferences +from .models import Cotisation, PaymentMethod, GeneralPreferences, PriceProfile class CotisationForm(forms.ModelForm): """ @@ -25,6 +25,13 @@ class PaymentMethodForm(forms.ModelForm): model = PaymentMethod fields = "__all__" +class PriceProfileForm(forms.ModelForm): + """ + Form to add and edit :class:`~preferences.models.PriceProfile`. + """ + class Meta: + model = PriceProfile + fields = "__all__" class GeneralPreferencesForm(forms.ModelForm): """ diff --git a/preferences/migrations/0016_priceprofile.py b/preferences/migrations/0016_priceprofile.py new file mode 100644 index 0000000..3f19662 --- /dev/null +++ b/preferences/migrations/0016_priceprofile.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1 on 2019-06-23 12:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0015_dividehistory'), + ] + + operations = [ + migrations.CreateModel( + name='PriceProfile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('a', models.DecimalField(decimal_places=2, max_digits=3, verbose_name='Marge constante')), + ('b', models.DecimalField(decimal_places=2, max_digits=3, verbose_name='Marge constante')), + ('c', models.DecimalField(decimal_places=2, max_digits=4, verbose_name='Marge constante')), + ('alpha', models.DecimalField(decimal_places=2, max_digits=4, verbose_name='Marge constante')), + ('use_for_draft', models.BooleanField(default=False)), + ], + ), + ] diff --git a/preferences/migrations/0017_auto_20190623_1453.py b/preferences/migrations/0017_auto_20190623_1453.py new file mode 100644 index 0000000..0ea8c68 --- /dev/null +++ b/preferences/migrations/0017_auto_20190623_1453.py @@ -0,0 +1,38 @@ +# Generated by Django 2.1 on 2019-06-23 12:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0016_priceprofile'), + ] + + operations = [ + migrations.AlterField( + model_name='priceprofile', + name='alpha', + field=models.DecimalField(decimal_places=2, max_digits=4, verbose_name='Étendue'), + ), + migrations.AlterField( + model_name='priceprofile', + name='b', + field=models.DecimalField(decimal_places=2, max_digits=3, verbose_name='Marge variable'), + ), + migrations.AlterField( + model_name='priceprofile', + name='c', + field=models.DecimalField(decimal_places=2, max_digits=4, verbose_name='Paramètre de forme'), + ), + migrations.AlterField( + model_name='priceprofile', + name='name', + field=models.CharField(max_length=255, verbose_name='Nom'), + ), + migrations.AlterField( + model_name='priceprofile', + name='use_for_draft', + field=models.BooleanField(default=False, verbose_name='Utiliser pour les pressions ?'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index fcbf07d..6ae3af1 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -172,4 +172,29 @@ class DivideHistory(models.Model): def __str__(self): return "Répartition du " + str(self.date) - \ No newline at end of file + + +class PriceProfile(models.Model): + """ + Stores parameters to compute price + """ + name = models.CharField(max_length=255, verbose_name="Nom") + a = models.DecimalField(verbose_name="Marge constante", max_digits=3, decimal_places=2) + b = models.DecimalField(verbose_name="Marge variable", max_digits=3, decimal_places=2) + c = models.DecimalField(verbose_name="Paramètre de forme", max_digits=4, decimal_places=2) + alpha = models.DecimalField(verbose_name="Étendue", max_digits=4, decimal_places=2) + use_for_draft = models.BooleanField(default=False, verbose_name="Utiliser pour les pressions ?") + + def save(self, *args, **kwargs): + if self.use_for_draft: + try: + temp = PriceProfile.objects.get(use_for_draft=True) + if self != temp: + temp.use_for_draft = False + temp.save() + except PriceProfile.DoesNotExist: + pass + super(PriceProfile, self).save(*args, **kwargs) + + def __str__(self): + return self.name diff --git a/preferences/templates/preferences/price_profiles_index.html b/preferences/templates/preferences/price_profiles_index.html new file mode 100644 index 0000000..8cbddae --- /dev/null +++ b/preferences/templates/preferences/price_profiles_index.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block entete %}Gestion des profils de prix{% endblock %} +{% block navbar %} + +{% endblock %} +{% block content %} +
+
+

Liste des profils de prix

+
+ {% if perms.preferences.add_priceprofile %} + Créer un profil de prix

+ {% endif %} +
+ + + + + + + + + + + + + + {% for pp in price_profiles %} + + + + + + + + + + {% endfor %} + +
Noma (marge constante)b (marge variable)c (paramètre de forme)alpha (étendue)Pression ?Administration
{{ pp.name }} {{ pp.a }}{{ pp.b }}{{ pp.c }}{{ pp.alpha }}{{ pp.use_for_draft | yesno:"Oui,Non"}}{% if perms.preferences.change_priceprofile %} Modifier {% endif %}{% if perms.preferences.delete_priceprofile %} Supprimer{% endif %}
+
+
+{% endblock %} diff --git a/preferences/urls.py b/preferences/urls.py index 2b2bac4..fa31467 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -13,6 +13,10 @@ urlpatterns = [ path('addPaymentMethod', views.addPaymentMethod, name="addPaymentMethod"), path('editPaymentMethod/', views.editPaymentMethod, name="editPaymentMethod"), path('deletePaymentMethod/', views.deletePaymentMethod, name="deletePaymentMethod"), + path('priceProfilesIndex', views.price_profiles_index, name="priceProfilesIndex"), + path('addPriceProfile', views.add_price_profile, name="addPriceProfile"), + path('editPriceProfile/', views.edit_price_profile, name="editPriceProfile"), + path('deletePriceProfile/', views.delete_price_profile, name="deletePriceProfile"), path('inactive', views.inactive, name="inactive"), path('getConfig', views.get_config, name="getConfig"), path('getCotisation/', views.get_cotisation, name="getCotisation") diff --git a/preferences/views.py b/preferences/views.py index c796f01..11dca4a 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -10,9 +10,9 @@ from django.http import Http404 from coopeV3.acl import active_required -from .models import GeneralPreferences, Cotisation, PaymentMethod +from .models import GeneralPreferences, Cotisation, PaymentMethod, PriceProfile -from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm +from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm, PriceProfileForm @active_required @login_required @@ -185,4 +185,63 @@ def get_config(request): del gp_dict["alcohol_charter"] data = json.dumps(gp_dict) return HttpResponse(data, content_type='application/json') - \ No newline at end of file + +########## Price Profiles ########## + +@active_required +@login_required +@permission_required('preferences.view_priceprofile') +def price_profiles_index(request): + """ + View which lists all the :class:`~preferences.models.PriceProfile`. + """ + price_profiles = PriceProfile.objects.all() + return render(request, "preferences/price_profiles_index.html", {"price_profiles": price_profiles}) + +@active_required +@login_required +@permission_required('preferences.add_priceprofile') +def add_price_profile(request): + """ + View which displays a :class:`~preferences.forms.PriceProfileForm` to create a :class:`~preferences.models.PriceProfile`. + """ + form = PriceProfileForm(request.POST or None) + if form.is_valid(): + price_profile = form.save() + messages.success(request, "Le profil de prix " + price_profile.name + " a bien été crée") + return redirect(reverse('preferences:priceProfilesIndex')) + return render(request, "form.html", {"form": form, "form_title": "Création d'un profil de prix", "form_button": "Créer", "form_button_icon": "plus-square"}) + +@active_required +@login_required +@permission_required('preferences.change_priceprofile') +def edit_price_profile(request, pk): + """ + View which displays a :class:`~preferences.forms.PriceProfile` to edit a :class:`~preferences.models.PriceProfile`. + + pk + The primary key of the :class:`~preferences.models.PriceProfile` to edit. + """ + price_profile = get_object_or_404(PriceProfile, pk=pk) + form = PriceProfileForm(request.POST or None, instance=price_profile) + if form.is_valid(): + price_profile = form.save() + messages.success(request, "Le profil de prix " + price_profile.name + " a bien été modifié") + return redirect(reverse('preferences:priceProfilesIndex')) + return render(request, "form.html", {"form": form, "form_title": "Modification d'un profil de prix", "form_button": "Modifier", "form_button_icon": "pencil-alt"}) + +@active_required +@login_required +@permission_required('preferences.delete_priceprofile') +def delete_price_profile(request,pk): + """ + Delete a :class:`~preferences.models.PriceProfile`. + + pk + The primary key of the :class:`~preferences.models.PriceProfile` to delete. + """ + price_profile = get_object_or_404(PriceProfile, pk=pk) + message = "Le profil de prix " + price_profile.name + " a bien été supprimé" + price_pofile.delete() + messages.success(request, message) + return redirect(reverse('preferences:priceProfilesIndex')) diff --git a/templates/nav.html b/templates/nav.html index fb84abf..0cd31f6 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -52,11 +52,16 @@ Cotisations {% endif %} -{% if perms.preferences.view_cotisation %} +{% if perms.preferences.view_paymentmethod %} Moyens de paiement {% endif %} +{% if perms.preferences.view_priceprofile %} + + Profils de prix + +{% endif %} Deconnexion diff --git a/users/migrations/0009_auto_20190623_1437.py b/users/migrations/0009_auto_20190623_1437.py new file mode 100644 index 0000000..1491aa0 --- /dev/null +++ b/users/migrations/0009_auto_20190623_1437.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1 on 2019-06-23 12:37 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0008_auto_20190623_1105'), + ] + + operations = [ + migrations.AlterModelOptions( + name='profile', + options={'permissions': (('can_generate_invoices', 'Can generate invocies'),), 'verbose_name': 'Profil'}, + ), + ]