forked from DGNum/gestioCOF
Merge branch 'qwann/k-fet/category_addcost' into 'master'
K-Fêt - Majorations - Seulement les catégories préalablement sélectionnées sont majorées le le cas échéant. - Pour modifier cette sélection, suivre le lien "Catégories" depuis la liste des articles. Fixes #149 See merge request !189
This commit is contained in:
commit
ebf948d042
10 changed files with 265 additions and 85 deletions
|
@ -233,6 +233,16 @@ class CheckoutStatementUpdateForm(forms.ModelForm):
|
|||
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
|
||||
# -----
|
||||
|
|
24
kfet/migrations/0052_category_addcost.py
Normal file
24
kfet/migrations/0052_category_addcost.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kfet', '0051_verbose_names'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='articlecategory',
|
||||
name='has_addcost',
|
||||
field=models.BooleanField(default=True, help_text="Si oui et qu'une majoration est active, celle-ci sera appliquée aux articles de cette catégorie.", verbose_name='majorée'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='articlecategory',
|
||||
name='name',
|
||||
field=models.CharField(max_length=45, verbose_name='nom'),
|
||||
),
|
||||
]
|
|
@ -338,13 +338,20 @@ class CheckoutStatement(models.Model):
|
|||
balance=F('balance') - last_statement.balance_new + self.balance_new)
|
||||
super(CheckoutStatement, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ArticleCategory(models.Model):
|
||||
name = models.CharField(max_length = 45)
|
||||
name = models.CharField("nom", max_length=45)
|
||||
has_addcost = models.BooleanField("majorée", default=True,
|
||||
help_text="Si oui et qu'une majoration "
|
||||
"est active, celle-ci sera "
|
||||
"appliquée aux articles de "
|
||||
"cette catégorie.")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Article(models.Model):
|
||||
name = models.CharField("nom", max_length = 45)
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
<a class="btn btn-primary btn-lg" href="{% url 'kfet.article.create' %}">
|
||||
Nouvel article
|
||||
</a>
|
||||
<a class="btn btn-primary btn-lg" href="{% url 'kfet.category' %}">
|
||||
Catégories
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div class="row form-only">
|
||||
<div class="col-sm-12 col-md-8 col-md-offset-2">
|
||||
<div class="content-form">
|
||||
<form submit="" method="post" class="form-horizontal">
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
{% include 'kfet/form_snippet.html' with form=form %}
|
||||
{% if not perms.kfet.change_article %}
|
||||
|
|
53
kfet/templates/kfet/category.html
Normal file
53
kfet/templates/kfet/category.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
{% extends 'kfet/base.html' %}
|
||||
|
||||
{% block title %}Categories d'articles{% endblock %}
|
||||
{% block content-header-title %}Categories d'articles{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-4 col-md-3 col-content-left">
|
||||
<div class="content-left">
|
||||
<div class="content-left-top">
|
||||
<div class="line line-big">{{ categories|length }}</div>
|
||||
<div class="line line-bigsub">catégorie{{ categories|length|pluralize }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-9 col-content-right">
|
||||
{% include 'kfet/base_messages.html' %}
|
||||
<div class="content-right">
|
||||
<div class="content-right-block">
|
||||
<h2>Liste des catégories</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Nom</td>
|
||||
<td class="text-right">Nombre d'articles</td>
|
||||
<td class="text-right">Peut être majorée</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in categories %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'kfet.category.update' category.pk %}">
|
||||
<span class="glyphicon glyphicon-cog"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ category.name }}</td>
|
||||
<td class="text-right">{{ category.articles.all|length }}</td>
|
||||
<td class="text-right">{{ category.has_addcost | yesno:"Oui,Non"}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
25
kfet/templates/kfet/category_update.html
Normal file
25
kfet/templates/kfet/category_update.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
{% extends 'kfet/base.html' %}
|
||||
|
||||
{% block title %}Édition de la catégorie {{ category.name }}{% endblock %}
|
||||
{% block content-header-title %}Catégorie {{ category.name }} - Édition{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% include "kfet/base_messages.html" %}
|
||||
|
||||
<div class="row form-only">
|
||||
<div class="col-sm-12 col-md-8 col-md-offset-2">
|
||||
<div class="content-form">
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
{% include 'kfet/form_snippet.html' with form=form %}
|
||||
{% if not perms.kfet.edit_articlecategory %}
|
||||
{% include 'kfet/form_authentication_snippet.html' %}
|
||||
{% endif %}
|
||||
{% include 'kfet/form_submit_snippet.html' with value="Enregistrer"%}
|
||||
<form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -647,7 +647,7 @@ $(document).ready(function() {
|
|||
});
|
||||
$after.after(article_html);
|
||||
// Pour l'autocomplétion
|
||||
articlesList.push([article['name'],article['id'],article['category_id'],article['price'], article['stock']]);
|
||||
articlesList.push([article['name'],article['id'],article['category_id'],article['price'], article['stock'],article['category__has_addcost']]);
|
||||
}
|
||||
|
||||
function getArticles() {
|
||||
|
@ -831,7 +831,10 @@ $(document).ready(function() {
|
|||
while (i<articlesList.length && id != articlesList[i][1]) i++;
|
||||
article_data = articlesList[i];
|
||||
var amount_euro = - article_data[3] * nb ;
|
||||
if (settings['addcost_for'] && settings['addcost_amount'] && account_data['trigramme'] != settings['addcost_for'])
|
||||
if (settings['addcost_for']
|
||||
&& settings['addcost_amount']
|
||||
&& account_data['trigramme'] != settings['addcost_for']
|
||||
&& article_data[5])
|
||||
amount_euro -= settings['addcost_amount'] * nb;
|
||||
var reduc_divisor = 1;
|
||||
if (account_data['is_cof'])
|
||||
|
|
12
kfet/urls.py
12
kfet/urls.py
|
@ -69,10 +69,10 @@ urlpatterns = [
|
|||
name='kfet.account.negative'),
|
||||
|
||||
# Account - Statistics
|
||||
url('^accounts/(?P<trigramme>.{3})/stat/operations/list$',
|
||||
url(r'^accounts/(?P<trigramme>.{3})/stat/operations/list$',
|
||||
views.AccountStatOperationList.as_view(),
|
||||
name='kfet.account.stat.operation.list'),
|
||||
url('^accounts/(?P<trigramme>.{3})/stat/operations$',
|
||||
url(r'^accounts/(?P<trigramme>.{3})/stat/operations$',
|
||||
views.AccountStatOperation.as_view(),
|
||||
name='kfet.account.stat.operation'),
|
||||
|
||||
|
@ -125,6 +125,14 @@ urlpatterns = [
|
|||
# Article urls
|
||||
# -----
|
||||
|
||||
# Category - General
|
||||
url('^categories/$',
|
||||
teamkfet_required(views.CategoryList.as_view()),
|
||||
name='kfet.category'),
|
||||
# Category - Update
|
||||
url('^categories/(?P<pk>\d+)/edit$',
|
||||
teamkfet_required(views.CategoryUpdate.as_view()),
|
||||
name='kfet.category.update'),
|
||||
# Article - General
|
||||
url('^articles/$',
|
||||
teamkfet_required(views.ArticleList.as_view()),
|
||||
|
|
|
@ -29,7 +29,7 @@ from kfet.models import (
|
|||
Account, Checkout, Article, Settings, AccountNegative,
|
||||
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
|
||||
InventoryArticle, Order, OrderArticle, Operation, OperationGroup,
|
||||
TransferGroup, Transfer)
|
||||
TransferGroup, Transfer, ArticleCategory)
|
||||
from kfet.forms import (
|
||||
AccountTriForm, AccountBalanceForm, AccountNoTriForm, UserForm, CofForm,
|
||||
UserRestrictTeamForm, UserGroupForm, AccountForm, CofRestrictForm,
|
||||
|
@ -39,7 +39,7 @@ from kfet.forms import (
|
|||
KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm,
|
||||
KPsulOperationFormSet, AddcostForm, FilterHistoryForm, SettingsForm,
|
||||
TransferFormSet, InventoryArticleForm, OrderArticleForm,
|
||||
OrderArticleToInventoryForm
|
||||
OrderArticleToInventoryForm, CategoryForm
|
||||
)
|
||||
from collections import defaultdict
|
||||
from kfet import consumers
|
||||
|
@ -723,12 +723,44 @@ class CheckoutStatementUpdate(SuccessMessageMixin, UpdateView):
|
|||
form.instance.amount_taken = getAmountTaken(form.instance)
|
||||
return super(CheckoutStatementUpdate, self).form_valid(form)
|
||||
|
||||
# -----
|
||||
# Category views
|
||||
# -----
|
||||
|
||||
|
||||
# Category - General
|
||||
class CategoryList(ListView):
|
||||
queryset = (ArticleCategory.objects
|
||||
.prefetch_related('articles')
|
||||
.order_by('name'))
|
||||
template_name = 'kfet/category.html'
|
||||
context_object_name = 'categories'
|
||||
|
||||
|
||||
# Category - Update
|
||||
class CategoryUpdate(SuccessMessageMixin, UpdateView):
|
||||
model = ArticleCategory
|
||||
template_name = 'kfet/category_update.html'
|
||||
form_class = CategoryForm
|
||||
success_url = reverse_lazy('kfet.category')
|
||||
success_message = "Informations mises à jour pour la catégorie : %(name)s"
|
||||
|
||||
# Surcharge de la validation
|
||||
def form_valid(self, form):
|
||||
# Checking permission
|
||||
if not self.request.user.has_perm('kfet.change_articlecategory'):
|
||||
form.add_error(None, 'Permission refusée')
|
||||
return self.form_invalid(form)
|
||||
|
||||
# Updating
|
||||
return super(CategoryUpdate, self).form_valid(form)
|
||||
|
||||
# -----
|
||||
# Article views
|
||||
# -----
|
||||
|
||||
# Article - General
|
||||
|
||||
# Article - General
|
||||
class ArticleList(ListView):
|
||||
queryset = (Article.objects
|
||||
.select_related('category')
|
||||
|
@ -739,8 +771,8 @@ class ArticleList(ListView):
|
|||
template_name = 'kfet/article.html'
|
||||
context_object_name = 'articles'
|
||||
|
||||
# Article - Create
|
||||
|
||||
# Article - Create
|
||||
class ArticleCreate(SuccessMessageMixin, CreateView):
|
||||
model = Article
|
||||
template_name = 'kfet/article_create.html'
|
||||
|
@ -784,8 +816,8 @@ class ArticleCreate(SuccessMessageMixin, CreateView):
|
|||
# Creating
|
||||
return super(ArticleCreate, self).form_valid(form)
|
||||
|
||||
# Article - Read
|
||||
|
||||
# Article - Read
|
||||
class ArticleRead(DetailView):
|
||||
model = Article
|
||||
template_name = 'kfet/article_read.html'
|
||||
|
@ -805,8 +837,8 @@ class ArticleRead(DetailView):
|
|||
context['supplierarts'] = supplierarts
|
||||
return context
|
||||
|
||||
# Article - Update
|
||||
|
||||
# Article - Update
|
||||
class ArticleUpdate(SuccessMessageMixin, UpdateView):
|
||||
model = Article
|
||||
template_name = 'kfet/article_update.html'
|
||||
|
@ -930,7 +962,8 @@ def kpsul_update_addcost(request):
|
|||
if not request.user.has_perms(required_perms):
|
||||
data = {
|
||||
'errors': {
|
||||
'missing_perms': get_missing_perms(required_perms, request.user)
|
||||
'missing_perms': get_missing_perms(required_perms,
|
||||
request.user)
|
||||
}
|
||||
}
|
||||
return JsonResponse(data, status=403)
|
||||
|
@ -938,7 +971,8 @@ def kpsul_update_addcost(request):
|
|||
trigramme = addcost_form.cleaned_data['trigramme']
|
||||
account = trigramme and Account.objects.get(trigramme=trigramme) or None
|
||||
Settings.objects.filter(name='ADDCOST_FOR').update(value_account=account)
|
||||
Settings.objects.filter(name='ADDCOST_AMOUNT').update(value_decimal=addcost_form.cleaned_data['amount'])
|
||||
(Settings.objects.filter(name='ADDCOST_AMOUNT')
|
||||
.update(value_decimal=addcost_form.cleaned_data['amount']))
|
||||
cache.delete('ADDCOST_FOR')
|
||||
cache.delete('ADDCOST_AMOUNT')
|
||||
data = {
|
||||
|
@ -950,15 +984,19 @@ def kpsul_update_addcost(request):
|
|||
consumers.KPsul.group_send('kfet.kpsul', data)
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
def get_missing_perms(required_perms, user):
|
||||
missing_perms_codenames = [(perm.split('.'))[1]
|
||||
for perm in required_perms if not user.has_perm(perm)]
|
||||
for perm in required_perms
|
||||
if not user.has_perm(perm)]
|
||||
missing_perms = list(
|
||||
Permission.objects
|
||||
.filter(codename__in=missing_perms_codenames)
|
||||
.values_list('name', flat=True))
|
||||
.values_list('name', flat=True)
|
||||
)
|
||||
return missing_perms
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def kpsul_perform_operations(request):
|
||||
# Initializing response data
|
||||
|
@ -995,24 +1033,26 @@ def kpsul_perform_operations(request):
|
|||
to_addcost_for_balance = 0 # For balance of addcost_for
|
||||
to_checkout_balance = 0 # For balance of selected checkout
|
||||
to_articles_stocks = defaultdict(lambda: 0) # For stocks articles
|
||||
is_addcost = (addcost_for and addcost_amount
|
||||
and addcost_for != operationgroup.on_acc)
|
||||
is_addcost = all((addcost_for, addcost_amount,
|
||||
addcost_for != operationgroup.on_acc))
|
||||
need_comment = operationgroup.on_acc.need_comment
|
||||
|
||||
# Filling data of each operations + operationgroup + calculating other stuffs
|
||||
# Filling data of each operations
|
||||
# + operationgroup + calculating other stuffs
|
||||
for operation in operations:
|
||||
if operation.type == Operation.PURCHASE:
|
||||
operation.amount = - operation.article.price * operation.article_nb
|
||||
if is_addcost:
|
||||
if is_addcost & operation.article.category.has_addcost:
|
||||
operation.addcost_for = addcost_for
|
||||
operation.addcost_amount = addcost_amount * operation.article_nb
|
||||
operation.addcost_amount = addcost_amount \
|
||||
* operation.article_nb
|
||||
operation.amount -= operation.addcost_amount
|
||||
to_addcost_for_balance += operation.addcost_amount
|
||||
if operationgroup.on_acc.is_cash:
|
||||
to_checkout_balance += -operation.amount
|
||||
if operationgroup.on_acc.is_cof:
|
||||
if is_addcost:
|
||||
operation.addcost_amount = operation.addcost_amount / cof_grant_divisor
|
||||
if is_addcost and operation.article.category.has_addcost:
|
||||
operation.addcost_amount /= cof_grant_divisor
|
||||
operation.amount = operation.amount / cof_grant_divisor
|
||||
to_articles_stocks[operation.article] -= operation.article_nb
|
||||
else:
|
||||
|
@ -1029,8 +1069,10 @@ def kpsul_perform_operations(request):
|
|||
if operationgroup.on_acc.is_cof:
|
||||
to_addcost_for_balance = to_addcost_for_balance / cof_grant_divisor
|
||||
|
||||
(perms, stop) = operationgroup.on_acc.perms_to_perform_operation(
|
||||
(perms, stop) = (operationgroup.on_acc
|
||||
.perms_to_perform_operation(
|
||||
amount=operationgroup.amount)
|
||||
)
|
||||
required_perms |= perms
|
||||
|
||||
if need_comment:
|
||||
|
@ -1072,8 +1114,8 @@ def kpsul_perform_operations(request):
|
|||
negative = AccountNegative(
|
||||
account=operationgroup.on_acc, start=timezone.now())
|
||||
negative.save()
|
||||
elif (hasattr(operationgroup.on_acc, 'negative')
|
||||
and not operationgroup.on_acc.negative.balance_offset):
|
||||
elif (hasattr(operationgroup.on_acc, 'negative') and
|
||||
not operationgroup.on_acc.negative.balance_offset):
|
||||
operationgroup.on_acc.negative.delete()
|
||||
|
||||
# Updating checkout's balance
|
||||
|
@ -1118,10 +1160,13 @@ def kpsul_perform_operations(request):
|
|||
}]
|
||||
for operation in operations:
|
||||
ope_data = {
|
||||
'id': operation.pk, 'type': operation.type, 'amount': operation.amount,
|
||||
'id': operation.pk, 'type': operation.type,
|
||||
'amount': operation.amount,
|
||||
'addcost_amount': operation.addcost_amount,
|
||||
'addcost_for__trigramme': operation.addcost_for and addcost_for.trigramme or None,
|
||||
'article__name': operation.article and operation.article.name or None,
|
||||
'addcost_for__trigramme': (
|
||||
operation.addcost_for and addcost_for.trigramme or None),
|
||||
'article__name': (
|
||||
operation.article and operation.article.name or None),
|
||||
'article_nb': operation.article_nb,
|
||||
'group_id': operationgroup.pk,
|
||||
'canceled_by__trigramme': None, 'canceled_at': None,
|
||||
|
@ -1145,6 +1190,7 @@ def kpsul_perform_operations(request):
|
|||
consumers.KPsul.group_send('kfet.kpsul', websocket_data)
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def kpsul_cancel_operations(request):
|
||||
# Pour la réponse
|
||||
|
@ -1393,7 +1439,8 @@ def history_json(request):
|
|||
def kpsul_articles_data(request):
|
||||
articles = (
|
||||
Article.objects
|
||||
.values('id', 'name', 'price', 'stock', 'category_id', 'category__name')
|
||||
.values('id', 'name', 'price', 'stock', 'category_id',
|
||||
'category__name', 'category__has_addcost')
|
||||
.filter(is_sold=True))
|
||||
return JsonResponse({ 'articles': list(articles) })
|
||||
|
||||
|
|
Loading…
Reference in a new issue