mirror of
https://github.com/nanoy42/coope
synced 2024-11-25 22:22:27 +00:00
commit
d7e21b6600
9 changed files with 111 additions and 27 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
## v3.6.4
|
||||||
|
* Ajout d'un champ use_stocks
|
||||||
|
* Séparation des formulaires de fût
|
||||||
## v3.6.3
|
## v3.6.3
|
||||||
* Refonte totale du système de stocks
|
* Refonte totale du système de stocks
|
||||||
* Fix price profile
|
* Fix price profile
|
||||||
|
|
|
@ -59,10 +59,10 @@ class ProductAdmin(SimpleHistoryAdmin):
|
||||||
"""
|
"""
|
||||||
The admin class for :class:`Products <gestion.models.Product>`.
|
The admin class for :class:`Products <gestion.models.Product>`.
|
||||||
"""
|
"""
|
||||||
list_display = ('name', 'amount', 'is_active', 'category', 'adherentRequired', 'stock', 'volume', 'deg')
|
list_display = ('name', 'amount', 'is_active', 'category', 'adherentRequired', 'stock', 'volume', 'deg', 'use_stocks')
|
||||||
ordering = ('name', 'amount', 'stock', 'deg')
|
ordering = ('name', 'amount', 'stock', 'deg')
|
||||||
search_fields = ('name',)
|
search_fields = ('name',)
|
||||||
list_filter = ('is_active', 'adherentRequired', 'category')
|
list_filter = ('is_active', 'adherentRequired', 'category', 'use_stocks')
|
||||||
|
|
||||||
class ReloadAdmin(SimpleHistoryAdmin):
|
class ReloadAdmin(SimpleHistoryAdmin):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -42,9 +42,9 @@ class ProductForm(forms.ModelForm):
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
widgets = {'amount': forms.TextInput}
|
widgets = {'amount': forms.TextInput}
|
||||||
|
|
||||||
class KegForm(forms.ModelForm):
|
class CreateKegForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
A form to create and edit a :class:`~gestion.models.Keg`.
|
A form to create a :class:`~gestion.models.Keg`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -52,9 +52,24 @@ class KegForm(forms.ModelForm):
|
||||||
fields = ["name", "stockHold", "amount", "capacity"]
|
fields = ["name", "stockHold", "amount", "capacity"]
|
||||||
widgets = {'amount': forms.TextInput}
|
widgets = {'amount': forms.TextInput}
|
||||||
|
|
||||||
category = forms.ModelChoiceField(queryset=Category.objects.all(), label="Catégorie")
|
category = forms.ModelChoiceField(queryset=Category.objects.all(), label="Catégorie", help_text="Catégorie dans laquelle placer les produits pinte, demi (et galopin si besoin).")
|
||||||
deg = forms.DecimalField(max_digits=5, decimal_places=2, label="Degré", validators=[MinValueValidator(0)])
|
deg = forms.DecimalField(max_digits=5, decimal_places=2, label="Degré", validators=[MinValueValidator(0)])
|
||||||
create_galopin = forms.BooleanField(label="Créer le produit galopin ?")
|
create_galopin = forms.BooleanField(required=False, label="Créer le produit galopin ?")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
if cleaned_data.get("name")[0:4] != "Fût ":
|
||||||
|
raise ValidationError("Le nom du fût doit être sous la forme 'Fût nom de la bière'")
|
||||||
|
|
||||||
|
class EditKegForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
A form to edit a :class:`~gestion.models.Keg`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Keg
|
||||||
|
fields = ["name", "stockHold", "amount", "capacity", "pinte", "demi", "galopin"]
|
||||||
|
widgets = {'amount': forms.TextInput}
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super().clean()
|
cleaned_data = super().clean()
|
||||||
|
|
23
gestion/migrations/0013_auto_20190829_1219.py
Normal file
23
gestion/migrations/0013_auto_20190829_1219.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.1 on 2019-08-29 10:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gestion', '0012_auto_20190827_2119'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='historicalproduct',
|
||||||
|
name='use_stocks',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='Utiliser les stocks ?'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='use_stocks',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='Utiliser les stocks ?'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -27,6 +27,13 @@ class Category(models.Model):
|
||||||
"""
|
"""
|
||||||
return self.product_set.filter(is_active=True)
|
return self.product_set.filter(is_active=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_stock_products(self):
|
||||||
|
"""
|
||||||
|
Return active products that use stocks
|
||||||
|
"""
|
||||||
|
return self.product_set.filter(is_active=True).filter(use_stocks=True)
|
||||||
|
|
||||||
class Product(models.Model):
|
class Product(models.Model):
|
||||||
"""
|
"""
|
||||||
Stores a product.
|
Stores a product.
|
||||||
|
@ -87,6 +94,7 @@ class Product(models.Model):
|
||||||
On the graphs on :func:`users.views.profile` view, the number of total consumptions is divised by the showingMultiplier
|
On the graphs on :func:`users.views.profile` view, the number of total consumptions is divised by the showingMultiplier
|
||||||
"""
|
"""
|
||||||
draft_category = models.IntegerField(choices=DRAFT_TYPES, default=DRAFT_NONE, verbose_name="Type de pression")
|
draft_category = models.IntegerField(choices=DRAFT_TYPES, default=DRAFT_NONE, verbose_name="Type de pression")
|
||||||
|
use_stocks = models.BooleanField(default=True, verbose_name="Utiliser les stocks ?")
|
||||||
history = HistoricalRecords()
|
history = HistoricalRecords()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -19,5 +19,6 @@
|
||||||
<strong>Actif</strong> : {{ product.is_active | yesno:"Oui, Non"}}<br>
|
<strong>Actif</strong> : {{ product.is_active | yesno:"Oui, Non"}}<br>
|
||||||
<strong>Dégré</strong> : {{ product.deg }}<br>
|
<strong>Dégré</strong> : {{ product.deg }}<br>
|
||||||
<strong>Volume</strong> : {{ product.volume }}cl<br>
|
<strong>Volume</strong> : {{ product.volume }}cl<br>
|
||||||
|
<strong>Utiliser les stocks</strong> : {{product.use_stocks|yesno:"Oui,Non"}}<br>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for product in category.active_products %}
|
{% for product in category.active_stock_products %}
|
||||||
<tr id="tr-{{product.pk}}">
|
<tr id="tr-{{product.pk}}">
|
||||||
<td><a href="{% url 'gestion:productProfile' product.pk %}">{{ product.name }}</a></td>
|
<td><a href="{% url 'gestion:productProfile' product.pk %}">{{ product.name }}</a></td>
|
||||||
<td id="stock-{{product.pk}}">{{ product.stock }}</td>
|
<td id="stock-{{product.pk}}">{{ product.stock }}</td>
|
||||||
|
|
|
@ -22,7 +22,7 @@ from decimal import *
|
||||||
import os
|
import os
|
||||||
from math import floor, ceil
|
from math import floor, ceil
|
||||||
|
|
||||||
from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm, CategoryForm, SearchCategoryForm, GenerateInvoiceForm, ComputePriceForm
|
from .forms import ReloadForm, RefundForm, ProductForm, CreateKegForm, EditKegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm, CategoryForm, SearchCategoryForm, GenerateInvoiceForm, ComputePriceForm
|
||||||
from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload, Refund, Category
|
from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload, Refund, Category
|
||||||
from users.models import School
|
from users.models import School
|
||||||
from preferences.models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile
|
from preferences.models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile
|
||||||
|
@ -160,7 +160,7 @@ def order(request):
|
||||||
kegHistory.quantitySold += Decimal(quantity * 0.125)
|
kegHistory.quantitySold += Decimal(quantity * 0.125)
|
||||||
kegHistory.amountSold += Decimal(quantity * product.amount)
|
kegHistory.amountSold += Decimal(quantity * product.amount)
|
||||||
kegHistory.save()
|
kegHistory.save()
|
||||||
else:
|
if product.use_stocks:
|
||||||
if(product.stock > quantity):
|
if(product.stock > quantity):
|
||||||
product.stock -= quantity
|
product.stock -= quantity
|
||||||
product.save()
|
product.save()
|
||||||
|
@ -197,6 +197,31 @@ def order(request):
|
||||||
consumption, _ = Consumption.objects.get_or_create(customer=user, product=article)
|
consumption, _ = Consumption.objects.get_or_create(customer=user, product=article)
|
||||||
consumption.quantity += quantity
|
consumption.quantity += quantity
|
||||||
consumption.save()
|
consumption.save()
|
||||||
|
if(article.draft_category == Product.DRAFT_PINTE):
|
||||||
|
keg = get_object_or_404(Keg, pinte=article)
|
||||||
|
if(not keg.is_active):
|
||||||
|
raise Exception("Fût non actif")
|
||||||
|
kegHistory = get_object_or_404(KegHistory, keg=keg, isCurrentKegHistory=True)
|
||||||
|
kegHistory.quantitySold += Decimal(quantity * 0.5)
|
||||||
|
kegHistory.amountSold += Decimal(quantity * product.amount)
|
||||||
|
kegHistory.save()
|
||||||
|
elif(article.draft_category == Product.DRAFT_DEMI):
|
||||||
|
keg = get_object_or_404(Keg, demi=article)
|
||||||
|
if(not keg.is_active):
|
||||||
|
raise Exception("Fût non actif")
|
||||||
|
kegHistory = get_object_or_404(KegHistory, keg=keg, isCurrentKegHistory=True)
|
||||||
|
kegHistory.quantitySold += Decimal(quantity * 0.25)
|
||||||
|
kegHistory.amountSold += Decimal(quantity * product.amount)
|
||||||
|
kegHistory.save()
|
||||||
|
elif(article.draft_category == Product.DRAFT_GALOPIN):
|
||||||
|
keg = get_object_or_404(Keg, galopin=article)
|
||||||
|
if(not keg.is_active):
|
||||||
|
raise Exception("Fût non actif")
|
||||||
|
kegHistory = get_object_or_404(KegHistory, keg=keg, isCurrentKegHistory=True)
|
||||||
|
kegHistory.quantitySold += Decimal(quantity * 0.125)
|
||||||
|
kegHistory.amountSold += Decimal(quantity * product.amount)
|
||||||
|
kegHistory.save()
|
||||||
|
if article.use_stocks:
|
||||||
if(article.stock > quantity):
|
if(article.stock > quantity):
|
||||||
article.stock -= quantity
|
article.stock -= quantity
|
||||||
article.save()
|
article.save()
|
||||||
|
@ -296,6 +321,7 @@ def cancel_consumption(request, pk):
|
||||||
consumptionT = Consumption.objects.get(customer=user, product=consumption.product)
|
consumptionT = Consumption.objects.get(customer=user, product=consumption.product)
|
||||||
consumptionT.quantity -= consumption.quantity
|
consumptionT.quantity -= consumption.quantity
|
||||||
consumptionT.save()
|
consumptionT.save()
|
||||||
|
if product.use_stocks:
|
||||||
product.stock += consumption.quantity
|
product.stock += consumption.quantity
|
||||||
product.save()
|
product.save()
|
||||||
consumption.delete()
|
consumption.delete()
|
||||||
|
@ -467,6 +493,7 @@ class ActiveProductsAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
def update_stock(request, pk):
|
def update_stock(request, pk):
|
||||||
product = get_object_or_404(Product, pk=pk)
|
product = get_object_or_404(Product, pk=pk)
|
||||||
if("stock" in request.GET):
|
if("stock" in request.GET):
|
||||||
|
if product.use_stocks:
|
||||||
product.stock = request.GET.get("stock")
|
product.stock = request.GET.get("stock")
|
||||||
product.save()
|
product.save()
|
||||||
return HttpResponse("Le stock a bien été mis à jour")
|
return HttpResponse("Le stock a bien été mis à jour")
|
||||||
|
@ -488,12 +515,16 @@ def stocks(request):
|
||||||
@permission_required('gestion.add_keg')
|
@permission_required('gestion.add_keg')
|
||||||
def addKeg(request):
|
def addKeg(request):
|
||||||
"""
|
"""
|
||||||
Displays a :class:`gestion.forms.KegForm` to add a :class:`gestion.models.Keg`.
|
Displays a :class:`gestion.forms.CreateKegForm` to add a :class:`gestion.models.Keg`.
|
||||||
"""
|
"""
|
||||||
form = KegForm(request.POST or None)
|
form = CreateKegForm(request.POST or None)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
try:
|
||||||
|
price_profile = PriceProfile.objects.get(use_for_draft=True)
|
||||||
|
except:
|
||||||
|
messages.error(request, "Il n'y a pas de profil de prix pour les pressions")
|
||||||
|
return redirect(reverse('preferences:priceProfilesIndex'))
|
||||||
keg = form.save(commit=False)
|
keg = form.save(commit=False)
|
||||||
price_profile = get_object_or_404(PriceProfile, use_for_draft=True)
|
|
||||||
pinte_price = compute_price(form.cleaned_data["amount"]/(2*form.cleaned_data["capacity"]), price_profile.a, price_profile.b, price_profile.c, price_profile.alpha)
|
pinte_price = compute_price(form.cleaned_data["amount"]/(2*form.cleaned_data["capacity"]), price_profile.a, price_profile.b, price_profile.c, price_profile.alpha)
|
||||||
pinte_price = ceil(10*pinte_price)/10
|
pinte_price = ceil(10*pinte_price)/10
|
||||||
name = form.cleaned_data["name"][4:]
|
name = form.cleaned_data["name"][4:]
|
||||||
|
@ -509,7 +540,8 @@ def addKeg(request):
|
||||||
deg = form.cleaned_data["deg"],
|
deg = form.cleaned_data["deg"],
|
||||||
adherentRequired = True,
|
adherentRequired = True,
|
||||||
showingMultiplier = 1,
|
showingMultiplier = 1,
|
||||||
draft_category = Product.DRAFT_PINTE
|
draft_category = Product.DRAFT_PINTE,
|
||||||
|
use_stocks=False,
|
||||||
)
|
)
|
||||||
pinte.save()
|
pinte.save()
|
||||||
keg.pinte = pinte
|
keg.pinte = pinte
|
||||||
|
@ -524,7 +556,8 @@ def addKeg(request):
|
||||||
deg = form.cleaned_data["deg"],
|
deg = form.cleaned_data["deg"],
|
||||||
adherentRequired = True,
|
adherentRequired = True,
|
||||||
showingMultiplier = 1,
|
showingMultiplier = 1,
|
||||||
draft_category = Product.DRAFT_DEMI
|
draft_category = Product.DRAFT_DEMI,
|
||||||
|
use_stocks=False,
|
||||||
)
|
)
|
||||||
demi.save()
|
demi.save()
|
||||||
keg.demi = demi
|
keg.demi = demi
|
||||||
|
@ -540,7 +573,8 @@ def addKeg(request):
|
||||||
deg = form.cleaned_data["deg"],
|
deg = form.cleaned_data["deg"],
|
||||||
adherentRequired = True,
|
adherentRequired = True,
|
||||||
showingMultiplier = 1,
|
showingMultiplier = 1,
|
||||||
draft_category = Product.DRAFT_DEMI
|
draft_category = Product.DRAFT_DEMI,
|
||||||
|
use_stocks=False,
|
||||||
)
|
)
|
||||||
galopin.save()
|
galopin.save()
|
||||||
keg.galopin = galopin
|
keg.galopin = galopin
|
||||||
|
@ -554,13 +588,13 @@ def addKeg(request):
|
||||||
@permission_required('gestion.change_keg')
|
@permission_required('gestion.change_keg')
|
||||||
def editKeg(request, pk):
|
def editKeg(request, pk):
|
||||||
"""
|
"""
|
||||||
Displays a :class:`gestion.forms.KegForm` to edit a :class:`gestion.models.Keg`.
|
Displays a :class:`gestion.forms.EditKegForm` to edit a :class:`gestion.models.Keg`.
|
||||||
|
|
||||||
pk
|
pk
|
||||||
The primary key of the :class:`gestion.models.Keg` to edit.
|
The primary key of the :class:`gestion.models.Keg` to edit.
|
||||||
"""
|
"""
|
||||||
keg = get_object_or_404(Keg, pk=pk)
|
keg = get_object_or_404(Keg, pk=pk)
|
||||||
form = KegForm(request.POST or None, instance=keg)
|
form = EditKegForm(request.POST or None, instance=keg)
|
||||||
if(form.is_valid()):
|
if(form.is_valid()):
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(request, "Le fût a bien été modifié")
|
messages.success(request, "Le fût a bien été modifié")
|
||||||
|
|
|
@ -42,6 +42,6 @@
|
||||||
<li><a href="https://www.facebook.com/coopesmetz/" class="icon fa-facebook alt"><span class="label">Facebook</span></a></li>
|
<li><a href="https://www.facebook.com/coopesmetz/" class="icon fa-facebook alt"><span class="label">Facebook</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<p class="copyright">coope.rez v3.6.3 (release stable) © 2018-2019 Yoann Pietri. <a href="{% url 'about'%}">À propos du projet</a>.</p>
|
<p class="copyright">coope.rez v3.6.4 (release stable) © 2018-2019 Yoann Pietri. <a href="{% url 'about'%}">À propos du projet</a>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue