Browse Source

Add price profiles

pull/1/head
Yoann Piétri 7 years ago
parent
commit
94715e4f99
  1. 7
      coopeV3/utils.py
  2. 12
      preferences/admin.py
  3. 9
      preferences/forms.py
  4. 25
      preferences/migrations/0016_priceprofile.py
  5. 38
      preferences/migrations/0017_auto_20190623_1453.py
  6. 27
      preferences/models.py
  7. 45
      preferences/templates/preferences/price_profiles_index.html
  8. 4
      preferences/urls.py
  9. 65
      preferences/views.py
  10. 7
      templates/nav.html
  11. 17
      users/migrations/0009_auto_20190623_1437.py

7
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

12
preferences/admin.py

@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin from simple_history.admin import SimpleHistoryAdmin
from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile
class CotisationAdmin(SimpleHistoryAdmin): class CotisationAdmin(SimpleHistoryAdmin):
""" """
@ -24,6 +24,15 @@ class PaymentMethodAdmin(SimpleHistoryAdmin):
search_fields = ('name',) search_fields = ('name',)
list_filter = ('is_active', 'is_usable_in_cotisation', 'is_usable_in_reload', 'affect_balance') list_filter = ('is_active', 'is_usable_in_cotisation', 'is_usable_in_reload', 'affect_balance')
class PriceProfileAdmin(SimpleHistoryAdmin):
"""
The admin class for :class:`Consumptions <preferences.models.PriceProfile>`.
"""
list_display = ('name', 'a', 'b', 'c', 'alpha', 'use_for_draft')
ordering = ('name',)
search_fields = ('name',)
list_filter = ('use_for_draft',)
class DivideHistoryAdmin(SimpleHistoryAdmin): class DivideHistoryAdmin(SimpleHistoryAdmin):
""" """
The admin class for Divide histories The admin class for Divide histories
@ -34,4 +43,5 @@ class DivideHistoryAdmin(SimpleHistoryAdmin):
admin.site.register(PaymentMethod, PaymentMethodAdmin) admin.site.register(PaymentMethod, PaymentMethodAdmin)
admin.site.register(GeneralPreferences, GeneralPreferencesAdmin) admin.site.register(GeneralPreferences, GeneralPreferencesAdmin)
admin.site.register(Cotisation, CotisationAdmin) admin.site.register(Cotisation, CotisationAdmin)
admin.site.register(PriceProfile, PriceProfileAdmin)
admin.site.register(DivideHistory, DivideHistoryAdmin) admin.site.register(DivideHistory, DivideHistoryAdmin)

9
preferences/forms.py

@ -1,7 +1,7 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from .models import Cotisation, PaymentMethod, GeneralPreferences from .models import Cotisation, PaymentMethod, GeneralPreferences, PriceProfile
class CotisationForm(forms.ModelForm): class CotisationForm(forms.ModelForm):
""" """
@ -25,6 +25,13 @@ class PaymentMethodForm(forms.ModelForm):
model = PaymentMethod model = PaymentMethod
fields = "__all__" 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): class GeneralPreferencesForm(forms.ModelForm):
""" """

25
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)),
],
),
]

38
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 ?'),
),
]

27
preferences/models.py

@ -172,4 +172,29 @@ class DivideHistory(models.Model):
def __str__(self): def __str__(self):
return "Répartition du " + str(self.date) return "Répartition du " + str(self.date)
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

45
preferences/templates/preferences/price_profiles_index.html

@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block entete %}Gestion des profils de prix{% endblock %}
{% block navbar %}
<ul>
<li><a href="#first">Liste des profils de prix</a></li>
</ul>
{% endblock %}
{% block content %}
<section id="first" class="main">
<header class="major">
<h2>Liste des profils de prix</h2>
</header>
{% if perms.preferences.add_priceprofile %}
<a class="button" href="{% url 'preferences:addPriceProfile' %}"><i class="fa fa-plus-square"></i> Créer un profil de prix</a><br><br>
{% endif %}
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Nom</th>
<th>a (marge constante)</th>
<th>b (marge variable)</th>
<th>c (paramètre de forme)</th>
<th>alpha (étendue)</th>
<th>Pression ?</th>
<th>Administration</th>
</tr>
</thead>
<tbody>
{% for pp in price_profiles %}
<tr>
<td>{{ pp.name }} </td>
<td>{{ pp.a }}</td>
<td>{{ pp.b }}</td>
<td>{{ pp.c }}</td>
<td>{{ pp.alpha }}</td>
<td>{{ pp.use_for_draft | yesno:"Oui,Non"}}</td>
<td>{% if perms.preferences.change_priceprofile %}<a class="button small" href="{% url 'preferences:editPriceProfile' pp.pk %}"><i class="fa fa-pencil-alt"></i> Modifier</a> {% endif %}{% if perms.preferences.delete_priceprofile %}<a class="button small" href="{% url 'preferences:deletePriceProfile' pp.pk %}"><i class="fa fa-trash"></i> Supprimer</a>{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
{% endblock %}

4
preferences/urls.py

@ -13,6 +13,10 @@ urlpatterns = [
path('addPaymentMethod', views.addPaymentMethod, name="addPaymentMethod"), path('addPaymentMethod', views.addPaymentMethod, name="addPaymentMethod"),
path('editPaymentMethod/<int:pk>', views.editPaymentMethod, name="editPaymentMethod"), path('editPaymentMethod/<int:pk>', views.editPaymentMethod, name="editPaymentMethod"),
path('deletePaymentMethod/<int:pk>', views.deletePaymentMethod, name="deletePaymentMethod"), path('deletePaymentMethod/<int:pk>', views.deletePaymentMethod, name="deletePaymentMethod"),
path('priceProfilesIndex', views.price_profiles_index, name="priceProfilesIndex"),
path('addPriceProfile', views.add_price_profile, name="addPriceProfile"),
path('editPriceProfile/<int:pk>', views.edit_price_profile, name="editPriceProfile"),
path('deletePriceProfile/<int:pk>', views.delete_price_profile, name="deletePriceProfile"),
path('inactive', views.inactive, name="inactive"), path('inactive', views.inactive, name="inactive"),
path('getConfig', views.get_config, name="getConfig"), path('getConfig', views.get_config, name="getConfig"),
path('getCotisation/<int:pk>', views.get_cotisation, name="getCotisation") path('getCotisation/<int:pk>', views.get_cotisation, name="getCotisation")

65
preferences/views.py

@ -10,9 +10,9 @@ from django.http import Http404
from coopeV3.acl import active_required 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 @active_required
@login_required @login_required
@ -185,4 +185,63 @@ def get_config(request):
del gp_dict["alcohol_charter"] del gp_dict["alcohol_charter"]
data = json.dumps(gp_dict) data = json.dumps(gp_dict)
return HttpResponse(data, content_type='application/json') return HttpResponse(data, content_type='application/json')
########## 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'))

7
templates/nav.html

@ -52,11 +52,16 @@
<i class="fa fa-calendar-check"></i> <a href="{% url 'preferences:cotisationsIndex' %}">Cotisations</a> <i class="fa fa-calendar-check"></i> <a href="{% url 'preferences:cotisationsIndex' %}">Cotisations</a>
</span> </span>
{% endif %} {% endif %}
{% if perms.preferences.view_cotisation %} {% if perms.preferences.view_paymentmethod %}
<span class="tabulation2"> <span class="tabulation2">
<i class="fa fa-comments-dollar"></i> <a href="{% url 'preferences:paymentMethodsIndex' %}">Moyens de paiement</a> <i class="fa fa-comments-dollar"></i> <a href="{% url 'preferences:paymentMethodsIndex' %}">Moyens de paiement</a>
</span> </span>
{% endif %} {% endif %}
{% if perms.preferences.view_priceprofile %}
<span class="tabulation2">
<i class="fa fa-search-dollar"></i> <a href="{% url 'preferences:priceProfilesIndex' %}">Profils de prix</a>
</span>
{% endif %}
<span class="tabulation2"> <span class="tabulation2">
<i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a> <i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a>
</span> </span>

17
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'},
),
]
Loading…
Cancel
Save