9467103879
- Possible de générer un inventaire à partir d'une commande passée. Préremplissage avec les valeurs commandées. - Possible d'indiquer les prix d'achat pour avoir l'historique des prix d'un article chez un fournisseur. Et bientôt, une proposition automatique de prix. - L'erreur sur le stock d'un article lors d'un inventaire n'est pas mise à jour dans le cas où l'inventaire est généré à partir d'une commande. - Ajout d'un champ `at` au modèle `SupplierArticle` afin de conserver l'historique des prix d'achat - Fix sur la vue `order_create`
473 lines
17 KiB
Python
473 lines
17 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.forms import modelformset_factory, inlineformset_factory
|
|
from django.forms.models import BaseInlineFormSet
|
|
from django.utils import timezone
|
|
from kfet.models import (Account, Checkout, Article, OperationGroup, Operation,
|
|
CheckoutStatement, ArticleCategory, Settings, AccountNegative, Transfer,
|
|
TransferGroup, Supplier, Inventory, InventoryArticle)
|
|
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, 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']
|
|
|
|
# -----
|
|
# Article forms
|
|
# -----
|
|
|
|
class ArticleForm(forms.ModelForm):
|
|
category_new = forms.CharField(
|
|
max_length=45,
|
|
required = False)
|
|
category = forms.ModelChoiceField(
|
|
queryset = ArticleCategory.objects.all(),
|
|
required = False)
|
|
|
|
suppliers = forms.ModelMultipleChoiceField(
|
|
queryset = Supplier.objects.all(),
|
|
required = False)
|
|
supplier_new = forms.CharField(
|
|
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', 'price', 'stock', 'category', 'box_type',
|
|
'box_capacity']
|
|
|
|
class ArticleRestrictForm(ArticleForm):
|
|
class Meta(ArticleForm.Meta):
|
|
fields = ['name', 'is_sold', '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())
|
|
class Meta:
|
|
model = OperationGroup
|
|
fields = ['on_acc', 'checkout', 'comment']
|
|
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()
|
|
|
|
class FilterHistoryForm(forms.Form):
|
|
checkouts = forms.ModelMultipleChoiceField(queryset = Checkout.objects.all())
|
|
accounts = forms.ModelMultipleChoiceField(queryset = Account.objects.all())
|
|
|
|
# -----
|
|
# 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']),
|
|
widget = forms.HiddenInput()
|
|
)
|
|
to_acc = forms.ModelChoiceField(
|
|
queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13']),
|
|
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']
|
|
|
|
# -----
|
|
# 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_s1 = kwargs['initial']['v_s1']
|
|
self.v_s2 = kwargs['initial']['v_s2']
|
|
self.v_s3 = kwargs['initial']['v_s3']
|
|
self.v_s4 = kwargs['initial']['v_s4']
|
|
self.v_s5 = kwargs['initial']['v_s5']
|
|
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']
|