gestioCOF/kfet/forms.py

698 lines
21 KiB
Python
Raw Permalink Normal View History

from datetime import date, timedelta
from decimal import Decimal
2016-08-02 10:40:46 +02:00
from django import forms
2021-02-20 20:59:54 +01:00
from django.conf import settings
from django.contrib.auth.models import User
from django.core import validators
from django.core.exceptions import ValidationError
from django.forms import modelformset_factory
2016-08-22 02:52:59 +02:00
from django.utils import timezone
2022-06-29 16:09:50 +02:00
from django.utils.translation import gettext_lazy as _
from djconfig.forms import ConfigForm
2016-08-02 10:40:46 +02:00
from gestioncof.models import CofProfile
from kfet.models import (
Account,
Article,
ArticleCategory,
Checkout,
CheckoutStatement,
Operation,
OperationGroup,
Supplier,
Transfer,
TransferGroup,
)
2020-09-16 17:16:14 +02:00
from kfet.statistic import SCALE_CLASS_CHOICES
2016-08-02 10:40:46 +02:00
from . import KFET_DELETED_TRIGRAMME
from .auth import KFET_GENERIC_TRIGRAMME
from .auth.forms import UserGroupForm # noqa
# -----
# Widgets
# -----
class DateTimeWidget(forms.DateTimeInput):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attrs["format"] = "%Y-%m-%d %H:%M"
class Media:
css = {"all": ("kfet/vendor/bootstrap/bootstrap-datetimepicker.min.css",)}
js = ("kfet/vendor/bootstrap/bootstrap-datetimepicker.min.js",)
class ContactForm(forms.Form):
from_email = forms.EmailField(
label="Adresse mail",
help_text="Si aucune adresse mail n'est renseignée, la soumission sera anonyme.",
required=False,
)
subject = forms.CharField(label="Objet", required=True)
message = forms.CharField(widget=forms.Textarea, required=True)
def clean_from_email(self):
return self.cleaned_data["from_email"] or "Anonyme <k-fet@ens.psl.eu>"
class DemandeSoireeForm(forms.Form):
HORAIRE_CHOICES = map(lambda s: (s, s), ("22h", "23h", "00h", "01h", "02h", "03h"))
SERVICE_CHOICES = (
("K-Fêt", "K-Fêt standard (L'équipe K-Fêt fait le service normal au bar)"),
("Kalô", "Type Kalô (Vous ramenez vos propres boissons et servez vous-mêmes)"),
)
nom = forms.CharField()
from_email = forms.EmailField(label="Adresse mail de contact")
contact_boum = forms.BooleanField(label="Contacter le Boum", required=False)
contact_pls = forms.BooleanField(label="Contacter PLS", required=False)
theme = forms.CharField(label="Thème de la soirée")
horaire_fin = forms.ChoiceField(label="Horaire de fin", choices=HORAIRE_CHOICES)
service = forms.ChoiceField(label="Mode de service", choices=SERVICE_CHOICES)
date = forms.CharField(label="Date souhaitée")
respo1 = forms.CharField(label="Nom de la personne respo n°1")
respo2 = forms.CharField(label="Nom de la personne respo n°2")
respo3 = forms.CharField(label="Nom de la personne respo n°3")
respo4 = forms.CharField(label="Nom de la personne respo n°4")
remarques = forms.CharField(
label="Remarques supplémentaires", widget=forms.Textarea
)
# -----
# Account forms
# -----
def default_promo():
now = date.today()
return now.month <= 8 and now.year - 1 or now.year
2021-07-01 10:29:14 +02:00
def get_promo_choices():
return [("", "Sans promo")] + [(r, r) for r in range(1980, date.today().year + 1)]
class AccountForm(forms.ModelForm):
promo = forms.TypedChoiceField(
2021-07-01 10:29:14 +02:00
choices=get_promo_choices,
coerce=int,
empty_value=None,
2021-07-01 10:29:14 +02:00
initial=default_promo,
required=False,
)
# Surcharge pour passer data à Account.save()
def save(self, data={}, *args, **kwargs):
obj = super().save(commit=False, *args, **kwargs)
obj.save(data=data)
return obj
2016-08-02 10:40:46 +02:00
class Meta:
model = Account
2021-03-17 21:01:55 +01:00
fields = ["trigramme", "promo", "nickname"]
widgets = {"trigramme": forms.TextInput(attrs={"autocomplete": "off"})}
2016-08-02 10:40:46 +02:00
class AccountBalanceForm(forms.ModelForm):
class Meta:
model = Account
fields = ["balance"]
2016-09-03 16:41:02 +02:00
class AccountTriForm(AccountForm):
2016-09-03 16:41:02 +02:00
def clean_trigramme(self):
trigramme = self.cleaned_data["trigramme"]
2016-09-03 16:41:02 +02:00
return trigramme.upper()
class Meta(AccountForm.Meta):
fields = ["trigramme"]
class AccountNoTriForm(AccountForm):
class Meta(AccountForm.Meta):
exclude = ["trigramme"]
2016-09-01 16:31:18 +02:00
class AccountPwdForm(forms.Form):
pwd1 = forms.CharField(
label="Mot de passe K-Fêt",
required=False,
help_text="Le mot de passe doit contenir au moins huit caractères",
2021-02-20 15:44:44 +01:00
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
min_length=8,
)
2016-09-01 16:31:18 +02:00
pwd2 = forms.CharField(
2021-02-20 15:44:44 +01:00
label="Confirmer le mot de passe",
required=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
)
2016-09-01 16:31:18 +02:00
2021-02-20 15:44:44 +01:00
def __init__(self, *args, account=None, **kwargs):
super().__init__(*args, **kwargs)
self.account = account
2016-09-01 16:31:18 +02:00
def clean(self):
pwd1 = self.cleaned_data.get("pwd1", "")
pwd2 = self.cleaned_data.get("pwd2", "")
2016-09-01 16:31:18 +02:00
if pwd1 != pwd2:
2021-02-20 15:44:44 +01:00
self.add_error("pwd2", "Les mots de passe doivent être identiques !")
2018-01-16 16:22:52 +01:00
super().clean()
2016-09-01 16:31:18 +02:00
2021-02-20 15:44:44 +01:00
def save(self, commit=True):
password = self.cleaned_data["pwd1"]
2021-02-20 17:04:45 +01:00
self.account.change_pwd(password)
2021-02-20 15:44:44 +01:00
if commit:
self.account.save()
return self.account
class AccountFrozenForm(forms.ModelForm):
class Meta:
model = Account
fields = ["is_frozen"]
2016-08-02 10:40:46 +02:00
class CofForm(forms.ModelForm):
def clean_is_cof(self):
instance = getattr(self, "instance", None)
2016-08-02 10:40:46 +02:00
if instance and instance.pk:
return instance.is_cof
else:
return False
2016-08-02 10:40:46 +02:00
class Meta:
model = CofProfile
fields = ["login_clipper", "is_cof", "departement"]
2016-08-02 10:40:46 +02:00
2017-09-04 13:25:09 +02:00
class UserForm(forms.ModelForm):
2016-08-02 10:40:46 +02:00
class Meta:
2017-09-04 13:25:09 +02:00
model = User
fields = ["username", "first_name", "last_name", "email"]
help_texts = {"username": ""}
2017-09-04 13:25:09 +02:00
class UserRestrictForm(UserForm):
class Meta(UserForm.Meta):
fields = ["first_name", "last_name"]
class UserInfoForm(UserForm):
first_name = forms.CharField(label="Prénom", disabled=True)
last_name = forms.CharField(label="Nom de famille", disabled=True)
class Meta(UserForm.Meta):
fields = ["first_name", "last_name"]
# -----
# Checkout forms
# -----
class CheckoutForm(forms.ModelForm):
class Meta:
model = Checkout
fields = ["name", "valid_from", "valid_to", "balance", "is_protected"]
widgets = {"valid_from": DateTimeWidget(), "valid_to": DateTimeWidget()}
class CheckoutRestrictForm(CheckoutForm):
class Meta(CheckoutForm.Meta):
fields = ["name", "valid_from", "valid_to"]
class CheckoutStatementCreateForm(forms.ModelForm):
balance_001 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_002 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_005 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_01 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_02 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_05 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_1 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_2 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_5 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_10 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_20 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_50 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_100 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_200 = forms.IntegerField(min_value=0, initial=0, required=False)
balance_500 = forms.IntegerField(min_value=0, initial=0, required=False)
class Meta:
model = CheckoutStatement
exclude = [
"by",
"at",
"checkout",
"amount_error",
"amount_taken",
"balance_old",
"balance_new",
]
def clean(self):
not_count = self.cleaned_data["not_count"]
if not not_count and (
self.cleaned_data["balance_001"] is None
or self.cleaned_data["balance_002"] is None
or self.cleaned_data["balance_005"] is None
or self.cleaned_data["balance_01"] is None
or self.cleaned_data["balance_02"] is None
or self.cleaned_data["balance_05"] is None
or self.cleaned_data["balance_1"] is None
or self.cleaned_data["balance_2"] is None
or self.cleaned_data["balance_5"] is None
or self.cleaned_data["balance_10"] is None
or self.cleaned_data["balance_20"] is None
or self.cleaned_data["balance_50"] is None
or self.cleaned_data["balance_100"] is None
or self.cleaned_data["balance_200"] is None
or self.cleaned_data["balance_500"] is None
):
raise ValidationError(
2018-10-06 12:47:19 +02:00
"Y'a un problème. Si tu comptes la caisse, mets au moins des 0 stp."
)
2018-01-16 16:22:52 +01:00
super().clean()
class CheckoutStatementUpdateForm(forms.ModelForm):
2016-08-11 15:14:23 +02:00
class Meta:
model = CheckoutStatement
exclude = ["by", "at", "checkout", "amount_error", "amount_taken"]
2016-08-11 15:14:23 +02:00
2017-04-04 21:36:02 +02:00
# -----
# Category
# -----
2017-04-04 21:36:02 +02:00
class CategoryForm(forms.ModelForm):
class Meta:
model = ArticleCategory
2019-11-27 14:14:33 +01:00
fields = ["name", "has_addcost", "has_reduction"]
2017-04-04 21:36:02 +02:00
# -----
# Article forms
# -----
class ArticleForm(forms.ModelForm):
category_new = forms.CharField(
label="Créer une catégorie", max_length=45, required=False
)
category = forms.ModelChoiceField(
label="Catégorie", queryset=ArticleCategory.objects.all(), required=False
)
suppliers = forms.ModelMultipleChoiceField(
label="Fournisseurs", queryset=Supplier.objects.all(), required=False
)
supplier_new = forms.CharField(
label="Créer un fournisseur", max_length=45, required=False
)
def __init__(self, *args, **kwargs):
2018-01-16 16:22:52 +01:00
super().__init__(*args, **kwargs)
if self.instance.pk:
self.initial["suppliers"] = self.instance.suppliers.values_list(
"pk", flat=True
)
def clean(self):
category = self.cleaned_data.get("category")
category_new = self.cleaned_data.get("category_new")
if not category and not category_new:
raise ValidationError("Sélectionnez une catégorie ou créez en une")
elif not category:
category, _ = ArticleCategory.objects.get_or_create(name=category_new)
self.cleaned_data["category"] = category
2018-01-16 16:22:52 +01:00
super().clean()
class Meta:
model = Article
fields = [
"name",
"is_sold",
"hidden",
"price",
"stock",
"category",
"box_type",
"box_capacity",
]
class ArticleRestrictForm(ArticleForm):
class Meta(ArticleForm.Meta):
fields = [
"name",
"is_sold",
"hidden",
"price",
"category",
"box_type",
"box_capacity",
]
# -----
# K-Psul forms
# -----
class KPsulOperationGroupForm(forms.ModelForm):
# FIXME(AD): Use timezone.now instead of timezone.now() to avoid using a
# fixed datetime (application boot here).
# One may even use: Checkout.objects.is_valid() if changing
# to now = timezone.now is ok in 'is_valid' definition.
checkout = forms.ModelChoiceField(
queryset=Checkout.objects.filter(
is_protected=False,
valid_from__lte=timezone.now(),
valid_to__gte=timezone.now(),
),
widget=forms.HiddenInput(),
)
on_acc = forms.ModelChoiceField(
queryset=Account.objects.exclude(
trigramme__in=[KFET_DELETED_TRIGRAMME, KFET_GENERIC_TRIGRAMME]
),
widget=forms.HiddenInput(),
)
class Meta:
model = OperationGroup
fields = ["on_acc", "checkout", "comment"]
class KPsulAccountForm(forms.ModelForm):
class Meta:
model = Account
fields = ["trigramme"]
widgets = {
"trigramme": forms.TextInput(
attrs={"autocomplete": "off", "spellcheck": "false"}
)
}
class KPsulCheckoutForm(forms.Form):
checkout = forms.ModelChoiceField(
queryset=None, widget=forms.Select(attrs={"id": "id_checkout_select"})
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Create the queryset on form instanciation to use the current time.
self.fields["checkout"].queryset = Checkout.objects.is_valid().filter(
is_protected=False
)
class KPsulOperationForm(forms.ModelForm):
article = forms.ModelChoiceField(
queryset=Article.objects.select_related("category").all(),
required=False,
widget=forms.HiddenInput(),
)
article_nb = forms.IntegerField(
required=False,
initial=None,
validators=[validators.MinValueValidator(1)],
widget=forms.HiddenInput(),
)
class Meta:
model = Operation
fields = ["type", "amount", "article", "article_nb"]
widgets = {"type": forms.HiddenInput(), "amount": forms.HiddenInput()}
def clean(self):
2018-01-16 16:22:52 +01:00
super().clean()
type_ope = self.cleaned_data.get("type")
amount = self.cleaned_data.get("amount")
article = self.cleaned_data.get("article")
article_nb = self.cleaned_data.get("article_nb")
errors = []
if type_ope and type_ope == Operation.PURCHASE:
if not article or article_nb is None or article_nb < 1:
errors.append(
ValidationError("Un achat nécessite un article et une quantité")
)
elif type_ope and type_ope in [Operation.DEPOSIT, Operation.WITHDRAW]:
if not amount or article or article_nb:
errors.append(ValidationError("Bad request"))
else:
if type_ope == Operation.DEPOSIT and amount <= 0:
errors.append(ValidationError("Charge non positive"))
elif type_ope == Operation.WITHDRAW and amount >= 0:
errors.append(ValidationError("Retrait non négatif"))
self.cleaned_data["article"] = None
self.cleaned_data["article_nb"] = None
if errors:
raise ValidationError(errors)
KPsulOperationFormSet = modelformset_factory(
Operation,
form=KPsulOperationForm,
can_delete=True,
extra=0,
min_num=1,
validate_min=True,
)
class AddcostForm(forms.Form):
trigramme = forms.CharField(required=False)
amount = forms.DecimalField(
required=False, max_digits=6, decimal_places=2, min_value=Decimal(0)
)
def clean(self):
trigramme = self.cleaned_data.get("trigramme")
if trigramme:
try:
Account.objects.get(trigramme=trigramme)
except Account.DoesNotExist:
raise ValidationError("Compte invalide")
else:
self.cleaned_data["amount"] = 0
2018-01-16 16:22:52 +01:00
super().clean()
# -----
# Settings forms
# -----
class KFetConfigForm(ConfigForm):
kfet_reduction_cof = forms.DecimalField(
label="Réduction COF",
initial=Decimal("20"),
max_digits=6,
decimal_places=2,
help_text="Réduction, à donner en pourcentage, appliquée lors d'un "
"achat par un-e membre du COF sur le montant en euros.",
)
kfet_addcost_amount = forms.DecimalField(
label="Montant de la majoration (en €)",
initial=Decimal("0"),
required=False,
max_digits=6,
decimal_places=2,
)
kfet_addcost_for = forms.ModelChoiceField(
label="Destinataire de la majoration",
initial=None,
required=False,
help_text="Laissez vide pour désactiver la majoration.",
queryset=(
Account.objects.select_related("cofprofile", "cofprofile__user").all()
),
)
kfet_overdraft_duration = forms.DurationField(
label="Durée du découvert autorisé par défaut", initial=timedelta(days=1)
)
kfet_overdraft_amount = forms.DecimalField(
label="Montant du découvert autorisé par défaut (en €)",
initial=Decimal("20"),
max_digits=6,
decimal_places=2,
)
kfet_cancel_duration = forms.DurationField(
label="Durée pour annuler une commande sans mot de passe",
initial=timedelta(minutes=5),
)
class FilterHistoryForm(forms.Form):
2021-02-19 10:32:12 +01:00
start = forms.DateTimeField(
label=_("De"),
widget=DateTimeWidget,
required=False,
2021-02-20 20:59:54 +01:00
help_text="Limité à {} jours ({} pour les chefs/trez)".format(
settings.KFET_HISTORY_DATE_LIMIT.days,
settings.KFET_HISTORY_LONG_DATE_LIMIT.days,
),
2021-02-19 10:32:12 +01:00
)
end = forms.DateTimeField(label=_("À"), widget=DateTimeWidget, required=False)
checkout = forms.ModelChoiceField(
label=_("Caisse"),
queryset=Checkout.objects.all(),
required=False,
empty_label=_("Toutes les caisses"),
)
account = forms.ModelChoiceField(
label=_("Compte"),
queryset=Account.objects.all(),
required=False,
empty_label=_("Tous les comptes"),
)
transfers_only = forms.BooleanField(widget=forms.HiddenInput, required=False)
opes_only = forms.BooleanField(widget=forms.HiddenInput, required=False)
2016-08-26 15:30:40 +02:00
# -----
# Transfer forms
# -----
2016-08-26 15:30:40 +02:00
class TransferGroupForm(forms.ModelForm):
class Meta:
model = TransferGroup
fields = ["comment"]
2016-08-26 15:30:40 +02:00
class TransferForm(forms.ModelForm):
from_acc = forms.ModelChoiceField(
queryset=Account.objects.exclude(trigramme__in=["LIQ", "#13", "GNR"]),
widget=forms.HiddenInput(),
2016-08-26 15:30:40 +02:00
)
to_acc = forms.ModelChoiceField(
queryset=Account.objects.exclude(trigramme__in=["LIQ", "#13", "GNR"]),
widget=forms.HiddenInput(),
2016-08-26 15:30:40 +02:00
)
def clean_amount(self):
amount = self.cleaned_data["amount"]
2016-08-26 15:30:40 +02:00
if amount <= 0:
2021-02-28 02:02:31 +01:00
raise forms.ValidationError("Le montant d'un transfert doit être positif")
2016-08-26 15:30:40 +02:00
return amount
class Meta:
model = Transfer
fields = ["from_acc", "to_acc", "amount"]
2016-08-26 15:30:40 +02:00
TransferFormSet = modelformset_factory(
Transfer, form=TransferForm, min_num=1, validate_min=True, extra=9
2016-08-26 15:30:40 +02:00
)
# -----
# Inventory forms
# -----
class InventoryArticleForm(forms.Form):
article = forms.ModelChoiceField(
queryset=Article.objects.all(), widget=forms.HiddenInput()
)
2017-03-31 16:07:37 +02:00
stock_new = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs):
2018-01-16 16:22:52 +01:00
super().__init__(*args, **kwargs)
if "initial" in kwargs:
self.name = kwargs["initial"]["name"]
self.stock_old = kwargs["initial"]["stock_old"]
self.category = kwargs["initial"]["category"]
self.category_name = kwargs["initial"]["category__name"]
self.box_capacity = kwargs["initial"]["box_capacity"]
2020-12-09 22:03:54 +01:00
self.is_sold = kwargs["initial"]["is_sold"]
# -----
# Order forms
# -----
2017-02-12 05:03:41 +01:00
class OrderArticleForm(forms.Form):
article = forms.ModelChoiceField(
queryset=Article.objects.all(), widget=forms.HiddenInput()
)
2017-03-31 16:24:38 +02:00
quantity_ordered = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs):
2018-01-16 16:22:52 +01:00
super().__init__(*args, **kwargs)
if "initial" in kwargs:
self.name = kwargs["initial"]["name"]
self.stock = kwargs["initial"]["stock"]
self.category = kwargs["initial"]["category"]
self.category_name = kwargs["initial"]["category__name"]
self.box_capacity = kwargs["initial"]["box_capacity"]
self.v_all = kwargs["initial"]["v_all"]
self.v_moy = kwargs["initial"]["v_moy"]
self.v_et = kwargs["initial"]["v_et"]
self.v_prev = kwargs["initial"]["v_prev"]
self.c_rec = kwargs["initial"]["c_rec"]
2020-12-09 22:03:54 +01:00
self.is_sold = kwargs["initial"]["is_sold"]
class OrderArticleToInventoryForm(forms.Form):
article = forms.ModelChoiceField(
queryset=Article.objects.all(), widget=forms.HiddenInput()
)
price_HT = forms.DecimalField(max_digits=7, decimal_places=4, required=False)
TVA = forms.DecimalField(max_digits=7, decimal_places=2, required=False)
rights = forms.DecimalField(max_digits=7, decimal_places=4, required=False)
2017-03-31 16:24:38 +02:00
quantity_received = forms.IntegerField()
def __init__(self, *args, **kwargs):
2018-01-16 16:22:52 +01:00
super().__init__(*args, **kwargs)
if "initial" in kwargs:
self.name = kwargs["initial"]["name"]
self.category = kwargs["initial"]["category"]
self.category_name = kwargs["initial"]["category__name"]
self.quantity_ordered = kwargs["initial"]["quantity_ordered"]
2020-09-16 17:16:14 +02:00
# ----
# Formulaires pour les statistiques K-Fêt
# ----
class StatScaleForm(forms.Form):
"""Formulaire pour nettoyer les paramètres envoyés aux
vues de statistiques K-Fêt. Non destiné à être affiché.
"""
name = forms.ChoiceField(choices=SCALE_CLASS_CHOICES)
begin = forms.DateTimeField(required=False)
end = forms.DateTimeField(required=False)
n_steps = forms.IntegerField(required=False)
last = forms.BooleanField(required=False)
class AccountStatForm(forms.Form):
2021-05-04 18:12:47 +02:00
"""Idem, mais pour la balance d'un compte"""
2020-09-16 17:16:14 +02:00
begin_date = forms.DateTimeField(required=False)
end_date = forms.DateTimeField(required=False)
last_days = forms.IntegerField(required=False)