# -*- coding: utf-8 -*- from datetime import timedelta from decimal import Decimal from django import forms from django.core.exceptions import ValidationError from django.core.validators import MinLengthValidator from django.contrib.auth.models import User, Group, Permission from django.contrib.contenttypes.models import ContentType from django.forms import modelformset_factory, widgets from django.utils import timezone from djconfig.forms import ConfigForm from kfet.models import ( Account, Checkout, Article, OperationGroup, Operation, CheckoutStatement, ArticleCategory, AccountNegative, Transfer, TransferGroup, Supplier) from gestioncof.models import CofProfile # ----- # 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/css/bootstrap-datetimepicker.min.css',) } js = ('kfet/js/bootstrap-datetimepicker.min.js',) # ----- # Account forms # ----- class AccountForm(forms.ModelForm): # Surcharge pour passer data à Account.save() def save(self, data = {}, *args, **kwargs): obj = super(AccountForm, self).save(commit = False, *args, **kwargs) obj.save(data = data) return obj class Meta: model = Account fields = ['trigramme', 'promo', 'nickname', 'is_frozen'] widgets = { 'trigramme': forms.TextInput(attrs={'autocomplete': 'off'}), } class AccountBalanceForm(forms.ModelForm): class Meta: model = Account fields = ['balance'] class AccountTriForm(AccountForm): def clean_trigramme(self): trigramme = self.cleaned_data['trigramme'] return trigramme.upper() class Meta(AccountForm.Meta): fields = ['trigramme'] class AccountNoTriForm(AccountForm): class Meta(AccountForm.Meta): exclude = ['trigramme'] class AccountRestrictForm(AccountForm): class Meta(AccountForm.Meta): fields = ['is_frozen'] class AccountPwdForm(forms.Form): pwd1 = forms.CharField( label="Mot de passe K-Fêt", help_text="Le mot de passe doit contenir au moins huit caractères", widget=forms.PasswordInput) pwd2 = forms.CharField( label="Confirmer le mot de passe", widget=forms.PasswordInput) def clean(self): pwd1 = self.cleaned_data.get('pwd1', '') pwd2 = self.cleaned_data.get('pwd2', '') if len(pwd1) < 8: raise ValidationError("Mot de passe trop court") if pwd1 != pwd2: raise ValidationError("Les mots de passes sont différents") super(AccountPwdForm, self).clean() class CofForm(forms.ModelForm): def clean_is_cof(self): instance = getattr(self, 'instance', None) if instance and instance.pk: return instance.is_cof else: return False class Meta: model = CofProfile fields = ['login_clipper', 'is_cof', 'departement'] class CofRestrictForm(CofForm): class Meta(CofForm.Meta): fields = ['departement'] class UserForm(forms.ModelForm): def __init__(self, *args, **kwargs): from_clipper = kwargs.pop('from_clipper', False) new_user = kwargs.get('instance') is None and not from_clipper super(UserForm, self).__init__(*args, **kwargs) if new_user: self.fields['username'].validators = [MinLengthValidator(9)] class Meta: model = User fields = ['username', 'first_name', 'last_name', 'email'] help_texts = { 'username': '' } class UserRestrictForm(UserForm): class Meta(UserForm.Meta): fields = ['first_name', 'last_name'] class UserRestrictTeamForm(UserForm): class Meta(UserForm.Meta): fields = ['first_name', 'last_name', 'email'] class UserGroupForm(forms.ModelForm): groups = forms.ModelMultipleChoiceField( Group.objects.filter(name__icontains='K-Fêt'), label='Statut équipe', required=False) def clean_groups(self): kfet_groups = self.cleaned_data.get('groups') other_groups = self.instance.groups.exclude(name__icontains='K-Fêt') return list(kfet_groups) + list(other_groups) class Meta: model = User fields = ['groups'] class KFetPermissionsField(forms.ModelMultipleChoiceField): def __init__(self, *args, **kwargs): queryset = Permission.objects.filter( content_type__in=ContentType.objects.filter(app_label="kfet"), ) super().__init__( queryset=queryset, widget=widgets.CheckboxSelectMultiple, *args, **kwargs ) def label_from_instance(self, obj): return obj.name class GroupForm(forms.ModelForm): permissions = KFetPermissionsField() def clean_name(self): name = self.cleaned_data['name'] return 'K-Fêt %s' % name class Meta: model = Group fields = ['name', 'permissions'] class AccountNegativeForm(forms.ModelForm): class Meta: model = AccountNegative fields = ['authz_overdraft_amount', 'authz_overdraft_until', 'balance_offset', 'comment'] widgets = { 'authz_overdraft_until': DateTimeWidget(), } # ----- # 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("Y'a un problème. Si tu comptes la caisse, mets au moins des 0 stp (et t'as pas idée de comment c'est long de vérifier que t'as mis des valeurs de partout...)") super(CheckoutStatementCreateForm, self).clean() class CheckoutStatementUpdateForm(forms.ModelForm): class Meta: model = CheckoutStatement exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken'] # ----- # Category # ----- class CategoryForm(forms.ModelForm): class Meta: model = ArticleCategory fields = ['name', 'has_addcost'] # ----- # 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): super(ArticleForm, self).__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 super(ArticleForm, self).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): 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='GNR'), 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=( Checkout.objects .filter( is_protected=False, valid_from__lte=timezone.now(), valid_to__gte=timezone.now(), ) ), widget=forms.Select(attrs={'id': 'id_checkout_select'}), ) class KPsulOperationForm(forms.ModelForm): article = forms.ModelChoiceField( queryset=Article.objects.select_related('category').all(), required=False, widget = forms.HiddenInput()) class Meta: model = Operation fields = ['type', 'amount', 'article', 'article_nb'] widgets = { 'type': forms.HiddenInput(), 'amount': forms.HiddenInput(), 'article_nb': forms.HiddenInput(), } def clean(self): super(KPsulOperationForm, self).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') if type_ope and type_ope == Operation.PURCHASE: if not article or not article_nb: raise ValidationError( "Un achat nécessite un article et une quantité") if article_nb < 1: raise ValidationError("Impossible d'acheter moins de 1 article") elif type_ope and type_ope in [Operation.DEPOSIT, Operation.WITHDRAW]: if not amount or article or article_nb: raise ValidationError("Bad request") if type_ope == Operation.DEPOSIT and amount <= 0: raise ValidationError("Charge non positive") if type_ope == Operation.WITHDRAW and amount >= 0: raise ValidationError("Retrait non négatif") self.cleaned_data['article'] = None self.cleaned_data['article_nb'] = None 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 super(AddcostForm, self).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): checkouts = forms.ModelMultipleChoiceField(queryset=Checkout.objects.all()) accounts = forms.ModelMultipleChoiceField(queryset=Account.objects.all()) from_date = forms.DateTimeField(widget=DateTimeWidget) to_date = forms.DateTimeField(widget=DateTimeWidget) # ----- # Transfer forms # ----- class TransferGroupForm(forms.ModelForm): class Meta: model = TransferGroup fields = ['comment'] class TransferForm(forms.ModelForm): from_acc = forms.ModelChoiceField( queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13', 'GNR']), widget = forms.HiddenInput() ) to_acc = forms.ModelChoiceField( queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13', 'GNR']), widget = forms.HiddenInput() ) def clean_amount(self): amount = self.cleaned_data['amount'] if amount <= 0: raise forms.ValidationError("Montant invalide") return amount class Meta: model = Transfer fields = ['from_acc', 'to_acc', 'amount'] TransferFormSet = modelformset_factory( Transfer, form = TransferForm, min_num = 1, validate_min = True, extra = 9, ) # ----- # Inventory forms # ----- class InventoryArticleForm(forms.Form): article = forms.ModelChoiceField( queryset = Article.objects.all(), widget = forms.HiddenInput(), ) stock_new = forms.IntegerField(required=False) def __init__(self, *args, **kwargs): super(InventoryArticleForm, self).__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'] # ----- # Order forms # ----- class OrderArticleForm(forms.Form): article = forms.ModelChoiceField( queryset=Article.objects.all(), widget=forms.HiddenInput(), ) quantity_ordered = forms.IntegerField(required=False) def __init__(self, *args, **kwargs): super(OrderArticleForm, self).__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'] 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) quantity_received = forms.IntegerField() def __init__(self, *args, **kwargs): super(OrderArticleToInventoryForm, self).__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']