forked from DGNum/gestioCOF
f73b25e65f
Nouveau relevé: Il faut donner le détail du nombre de chaque pièces/billets pris et laissé en caisse pour calculer les valeurs `balance_new` et `amount_taken` d'un relevé (`CheckoutStatement`). L'erreur est directement calculée par rapport à la balance actuelle de la caisse et ces 2 valeurs. Une erreur positive correspond à un surplus d'argent et inversement. Modification d'un relevé: Il est possible de modifier les infos d'un ancien relevé. L'erreur est ensuite recalculée à partir de ces infos. Important: Dans le cas où `balance_new` est modifiée et qu'il s'agit du relevé le plus récent sur cette caisse. Alors la balance de la caisse est mise à jour en prenant en compte cette correction (et en conservant les modifications s'il y a eu des mouvements sur la caisse)
324 lines
11 KiB
Python
324 lines
11 KiB
Python
from decimal import Decimal
|
|
from django import forms
|
|
from django.core.exceptions import ValidationError
|
|
from django.contrib.auth.models import User, Group, Permission
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
|
from django.forms import modelformset_factory
|
|
from django.utils import timezone
|
|
from kfet.models import (Account, Checkout, Article, OperationGroup, Operation,
|
|
CheckoutStatement, ArticleCategory, Settings, AccountNegative)
|
|
from gestioncof.models import CofProfile
|
|
|
|
# -----
|
|
# Widgets
|
|
# -----
|
|
|
|
class DateTimeWidget(forms.DateTimeInput):
|
|
def __init__(self, attrs = None):
|
|
super(DateTimeWidget, self).__init__(attrs)
|
|
self.attrs['format'] = '%Y-%m-%d %H:%M'
|
|
class Media:
|
|
css = {
|
|
'all': ('bootstrap-datetimepicker.min.css',)
|
|
}
|
|
js = (
|
|
'moment.js',
|
|
'moment-fr.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']
|
|
widgets = {
|
|
'trigramme': forms.TextInput(attrs={'autocomplete': 'off'}),
|
|
}
|
|
|
|
class AccountTriForm(AccountForm):
|
|
class Meta(AccountForm.Meta):
|
|
fields = ['trigramme']
|
|
|
|
class AccountNoTriForm(AccountForm):
|
|
class Meta(AccountForm.Meta):
|
|
exclude = ['trigramme']
|
|
|
|
class AccountRestrictForm(AccountForm):
|
|
class Meta(AccountForm.Meta):
|
|
fields = ['promo']
|
|
|
|
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):
|
|
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'))
|
|
class Meta:
|
|
model = User
|
|
fields = ['groups']
|
|
|
|
class GroupForm(forms.ModelForm):
|
|
permissions = forms.ModelMultipleChoiceField(
|
|
queryset= Permission.objects.filter(content_type__in=
|
|
ContentType.objects.filter(app_label='kfet')))
|
|
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)
|
|
balance_002 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_005 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_01 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_02 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_05 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_1 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_2 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_5 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_10 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_20 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_50 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_100 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_200 = forms.IntegerField(min_value=0, initial=0)
|
|
balance_500 = forms.IntegerField(min_value=0, initial=0)
|
|
|
|
class Meta:
|
|
model = CheckoutStatement
|
|
exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken',
|
|
'balance_old', 'balance_new']
|
|
|
|
class CheckoutStatementUpdateForm(forms.ModelForm):
|
|
class Meta:
|
|
model = CheckoutStatement
|
|
exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken']
|
|
|
|
# -----
|
|
# Article forms
|
|
# -----
|
|
|
|
class ArticleForm(forms.ModelForm):
|
|
category_new = forms.CharField(
|
|
max_length=45,
|
|
required = False)
|
|
category = forms.ModelChoiceField(
|
|
queryset = ArticleCategory.objects.all(),
|
|
required = False)
|
|
|
|
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', 'price', 'stock', 'category']
|
|
|
|
class ArticleRestrictForm(ArticleForm):
|
|
class Meta(ArticleForm.Meta):
|
|
fields = ['name', 'is_sold', 'price', 'category']
|
|
|
|
# -----
|
|
# 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())
|
|
class Meta:
|
|
model = OperationGroup
|
|
fields = ['on_acc', 'checkout']
|
|
widgets = {
|
|
'on_acc' : forms.HiddenInput(),
|
|
}
|
|
|
|
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', 'is_checkout', 'article', 'article_nb']
|
|
widgets = {
|
|
'type': forms.HiddenInput(),
|
|
'amount': forms.HiddenInput(),
|
|
'is_checkout': 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")
|
|
self.cleaned_data['is_checkout'] = True
|
|
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 SettingsForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Settings
|
|
fields = ['value_decimal', 'value_account', 'value_duration']
|
|
|
|
def clean(self):
|
|
name = self.instance.name
|
|
value_decimal = self.cleaned_data.get('value_decimal')
|
|
value_account = self.cleaned_data.get('value_account')
|
|
value_duration = self.cleaned_data.get('value_duration')
|
|
|
|
type_decimal = ['SUBVENTION_COF', 'ADDCOST_AMOUNT', 'OVERDRAFT_AMOUNT']
|
|
type_account = ['ADDCOST_FOR']
|
|
type_duration = ['OVERDRAFT_DURATION', 'CANCEL_DURATION']
|
|
|
|
self.cleaned_data['name'] = name
|
|
if name in type_decimal:
|
|
if not value_decimal:
|
|
raise ValidationError('Renseignez une valeur décimale')
|
|
self.cleaned_data['value_account'] = None
|
|
self.cleaned_data['value_duration'] = None
|
|
elif name in type_account:
|
|
self.cleaned_data['value_decimal'] = None
|
|
self.cleaned_data['value_duration'] = None
|
|
elif name in type_duration:
|
|
if not value_duration:
|
|
raise ValidationError('Renseignez une durée')
|
|
self.cleaned_data['value_decimal'] = None
|
|
self.cleaned_data['value_account'] = None
|
|
cache.delete(name)
|
|
super(SettingsForm, self).clean()
|