diff --git a/kfet/forms.py b/kfet/forms.py
index 709d62ff..b615ba38 100644
--- a/kfet/forms.py
+++ b/kfet/forms.py
@@ -218,11 +218,13 @@ class ArticleForm(forms.ModelForm):
class Meta:
model = Article
- fields = ['name', 'is_sold', 'price', 'stock', 'category']
+ fields = ['name', 'is_sold', 'price', 'stock', 'category', 'box_type',
+ 'box_capacity']
class ArticleRestrictForm(ArticleForm):
class Meta(ArticleForm.Meta):
- fields = ['name', 'is_sold', 'price', 'category']
+ fields = ['name', 'is_sold', 'price', 'category', 'box_type',
+ 'box_capacity']
# -----
# K-Psul forms
@@ -416,3 +418,32 @@ class InventoryArticleForm(forms.Form):
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']
diff --git a/kfet/migrations/0038_auto_20160828_0402.py b/kfet/migrations/0038_auto_20160828_0402.py
new file mode 100644
index 00000000..215591b7
--- /dev/null
+++ b/kfet/migrations/0038_auto_20160828_0402.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('kfet', '0037_auto_20160826_2333'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='inventory',
+ options={'ordering': ['-at']},
+ ),
+ migrations.RemoveField(
+ model_name='supplierarticle',
+ name='box_capacity',
+ ),
+ migrations.RemoveField(
+ model_name='supplierarticle',
+ name='box_type',
+ ),
+ migrations.AddField(
+ model_name='article',
+ name='box_capacity',
+ field=models.PositiveSmallIntegerField(blank=True, null=True, default=None),
+ ),
+ migrations.AddField(
+ model_name='article',
+ name='box_type',
+ field=models.CharField(max_length=7, blank=True, null=True, default=None, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')]),
+ ),
+ ]
diff --git a/kfet/migrations/0039_auto_20160828_0430.py b/kfet/migrations/0039_auto_20160828_0430.py
new file mode 100644
index 00000000..271fda68
--- /dev/null
+++ b/kfet/migrations/0039_auto_20160828_0430.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('kfet', '0038_auto_20160828_0402'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='order',
+ name='amount',
+ field=models.DecimalField(default=0, decimal_places=2, max_digits=6),
+ ),
+ migrations.AlterField(
+ model_name='orderarticle',
+ name='quantity_received',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/kfet/models.py b/kfet/models.py
index 907c6938..7a206d6e 100644
--- a/kfet/models.py
+++ b/kfet/models.py
@@ -325,6 +325,18 @@ class Article(models.Model):
category = models.ForeignKey(
ArticleCategory, on_delete = models.PROTECT,
related_name = "articles")
+ BOX_TYPE_CHOICES = (
+ ("caisse", "caisse"),
+ ("carton", "carton"),
+ ("palette", "palette"),
+ ("fût", "fût"),
+ )
+ box_type = models.CharField(
+ choices = BOX_TYPE_CHOICES,
+ max_length = choices_length(BOX_TYPE_CHOICES),
+ blank = True, null = True, default = None)
+ box_capacity = models.PositiveSmallIntegerField(
+ blank = True, null = True, default = None)
def __str__(self):
return '%s - %s' % (self.category.name, self.name)
@@ -391,18 +403,6 @@ class SupplierArticle(models.Model):
Supplier, on_delete = models.PROTECT)
article = models.ForeignKey(
Article, on_delete = models.PROTECT)
- BOX_TYPE_CHOICES = (
- ("caisse", "Caisse"),
- ("carton", "Carton"),
- ("palette", "Palette"),
- ("fût", "Fût"),
- )
- box_type = models.CharField(
- choices = BOX_TYPE_CHOICES,
- max_length = choices_length(BOX_TYPE_CHOICES),
- blank = True, null = True, default = None)
- box_capacity = models.PositiveSmallIntegerField(
- blank = True, null = True, default = None)
price_HT = models.DecimalField(
max_digits = 7, decimal_places = 4,
blank = True, null = True, default = None)
@@ -422,7 +422,11 @@ class Order(models.Model):
through = "OrderArticle",
related_name = "orders")
at = models.DateTimeField(auto_now_add = True)
- amount = models.DecimalField(max_digits = 6, decimal_places = 2)
+ amount = models.DecimalField(
+ max_digits = 6, decimal_places = 2, default = 0)
+
+ class Meta:
+ ordering = ['-at']
class OrderArticle(models.Model):
order = models.ForeignKey(
@@ -430,7 +434,7 @@ class OrderArticle(models.Model):
article = models.ForeignKey(
Article, on_delete = models.PROTECT)
quantity_ordered = models.IntegerField()
- quantity_received = models.IntegerField()
+ quantity_received = models.IntegerField(default = 0)
class TransferGroup(models.Model):
at = models.DateTimeField(auto_now_add = True)
diff --git a/kfet/templates/kfet/order.html b/kfet/templates/kfet/order.html
index 7901a654..28e3d7a9 100644
--- a/kfet/templates/kfet/order.html
+++ b/kfet/templates/kfet/order.html
@@ -17,19 +17,13 @@
{% include 'kfet/base_messages.html' %}
-
-
Liste des commandes
-
-
+
+
Liste des commandes
+
+
+ {% for order in orders %}
+
+
+
+ {{ order.at }}
+
+ |
+ {{ order.supplier }} |
+
+ {% endfor %}
+
+
+
diff --git a/kfet/templates/kfet/order_create.html b/kfet/templates/kfet/order_create.html
new file mode 100644
index 00000000..efd316a6
--- /dev/null
+++ b/kfet/templates/kfet/order_create.html
@@ -0,0 +1,62 @@
+{% extends 'kfet/base.html' %}
+
+{% block title %}Nouvelle commande{% endblock %}
+{% block content-header-title %}Nouvelle commande {{ supplier.name }}{% endblock %}
+
+{% block content %}
+
+
+
+{% endblock %}
diff --git a/kfet/templates/kfet/order_read.html b/kfet/templates/kfet/order_read.html
new file mode 100644
index 00000000..beffd359
--- /dev/null
+++ b/kfet/templates/kfet/order_read.html
@@ -0,0 +1,14 @@
+{% extends 'kfet/base.html' %}
+
+{% block title %}Commande #{{ order.pk }}{% endblock %}
+{% block content-header-title %}Commande #{{ order.pk }}{% endblock %}
+
+{% block content %}
+
+
+Créée le {{ order.at }} pour {{ order.supplier.name }}
+
+
+
+
+{% endblock %}
diff --git a/kfet/urls.py b/kfet/urls.py
index 5ac6047b..5edd6d77 100644
--- a/kfet/urls.py
+++ b/kfet/urls.py
@@ -179,7 +179,12 @@ urlpatterns = [
url(r'^orders/$',
permission_required('kfet.is_team')(views.OrderList.as_view()),
name = 'kfet.order'),
+ url(r'^orders/(?P\d+)',
+ permission_required('kfet.is_team')(views.OrderRead.as_view()),
+ name = 'kfet.order.read'),
url(r'^orders/suppliers/(?P\d+)/edit$',
permission_required('kfet.is_team')(views.SupplierUpdate.as_view()),
name = 'kfet.order.supplier.update'),
+ url(r'^orders/suppliers/(?P\d+)/new-order$', views.order_create,
+ name = 'kfet.order.new'),
]
diff --git a/kfet/views.py b/kfet/views.py
index 985acec4..0dfa286c 100644
--- a/kfet/views.py
+++ b/kfet/views.py
@@ -12,19 +12,21 @@ from django.contrib.auth.models import User, Permission, Group
from django.http import HttpResponse, JsonResponse, Http404
from django.forms import modelformset_factory, formset_factory
from django.db import IntegrityError, transaction
-from django.db.models import F, Sum, Prefetch, Count
+from django.db.models import F, Sum, Prefetch, Count, Func
from django.db.models.functions import Coalesce
from django.utils import timezone
from django.utils.crypto import get_random_string
from gestioncof.models import CofProfile, Clipper
from kfet.models import (Account, Checkout, Article, Settings, AccountNegative,
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
- InventoryArticle, Order)
+ InventoryArticle, Order, OrderArticle)
from kfet.forms import *
from collections import defaultdict
from kfet import consumers
-import datetime
+from datetime import timedelta
import django_cas_ng
+import heapq
+import statistics
@login_required
def home(request):
@@ -1293,7 +1295,6 @@ def inventory_create(request):
.order_by('category__name', 'name')
)
initial = []
- data = []
for article in articles:
initial.append({
'article' : article.pk,
@@ -1324,6 +1325,7 @@ def inventory_create(request):
if form.cleaned_data['stock_new'] is not None:
if not saved:
inventory.save()
+ saved = True
article = articles.get(pk=form.cleaned_data['article'].pk)
stock_old = article.stock
@@ -1356,13 +1358,182 @@ def inventory_create(request):
class OrderList(ListView):
model = Order
template_name = 'kfet/order.html'
- context_object_name = 'inventories'
+ context_object_name = 'orders'
def get_context_data(self, **kwargs):
context = super(OrderList, self).get_context_data(**kwargs)
context['suppliers'] = Supplier.objects.order_by('name')
return context
+@permission_required('kfet.is_team')
+def order_create(request, pk):
+ supplier = Supplier.objects.get(pk=pk)
+
+ articles = (Article.objects
+ .filter(suppliers=supplier.pk)
+ .select_related('category')
+ .order_by('category__name', 'name'))
+
+ print(articles[0].suppliers.all())
+
+ initial = []
+ today = timezone.now().date()
+ sales_q = (Operation.objects
+ .select_related('group')
+ .filter(article__in=articles, canceled_at=None)
+ .values('article'))
+ sales_s1 = (sales_q
+ .filter(
+ group__at__gte = today-timedelta(weeks=5),
+ group__at__lt = today-timedelta(weeks=4))
+ .annotate(nb=Sum('article_nb'))
+ )
+ sales_s1 = { d['article']:d['nb'] for d in sales_s1 }
+ sales_s2 = (sales_q
+ .filter(
+ group__at__gte = today-timedelta(weeks=4),
+ group__at__lt = today-timedelta(weeks=3))
+ .annotate(nb=Sum('article_nb'))
+ )
+ sales_s2 = { d['article']:d['nb'] for d in sales_s2 }
+ sales_s3 = (sales_q
+ .filter(
+ group__at__gte = today-timedelta(weeks=3),
+ group__at__lt = today-timedelta(weeks=2))
+ .annotate(nb=Sum('article_nb'))
+ )
+ sales_s3 = { d['article']:d['nb'] for d in sales_s3 }
+ sales_s4 = (sales_q
+ .filter(
+ group__at__gte = today-timedelta(weeks=2),
+ group__at__lt = today-timedelta(weeks=1))
+ .annotate(nb=Sum('article_nb'))
+ )
+ sales_s4 = { d['article']:d['nb'] for d in sales_s4 }
+ sales_s5 = (sales_q
+ .filter(group__at__gte = today-timedelta(weeks=1))
+ .annotate(nb=Sum('article_nb'))
+ )
+ sales_s5 = { d['article']:d['nb'] for d in sales_s5 }
+
+ for article in articles:
+ v_s1 = sales_s1.get(article.pk, 0)
+ v_s2 = sales_s2.get(article.pk, 0)
+ v_s3 = sales_s3.get(article.pk, 0)
+ v_s4 = sales_s4.get(article.pk, 0)
+ v_s5 = sales_s5.get(article.pk, 0)
+ v_all = [v_s1, v_s2, v_s3, v_s4, v_s5]
+ v_3max = heapq.nlargest(3, v_all)
+ v_moy = statistics.mean(v_3max)
+ v_et = statistics.pstdev(v_3max, v_moy)
+ v_prev = v_moy + v_et
+ c_rec_tot = max(v_prev * 1.5 - article.stock, 0)
+ if article.box_capacity:
+ c_rec_temp = c_rec_tot / box_capacity
+ if c_rec_temp >= 10:
+ c_rec = round(c_rec_temp)
+ elif c_rec_temp > 5:
+ c_rec = 10
+ elif c_rec_temp > 2:
+ c_rec = 5
+ else:
+ c_rec = round(c_rec_temp)
+ initial.append({
+ 'article': article.pk,
+ 'name': article.name,
+ 'category': article.category_id,
+ 'category__name': article.category.name,
+ 'stock': article.stock,
+ 'box_capacity': article.box_capacity,
+ 'v_s1': v_s1,
+ 'v_s2': v_s2,
+ 'v_s3': v_s3,
+ 'v_s4': v_s4,
+ 'v_s5': v_s5,
+ 'v_moy': round(v_moy),
+ 'v_et': round(v_et),
+ 'v_prev': round(v_prev),
+ 'c_rec': article.box_capacity and c_rec or round(c_rec_tot),
+ })
+
+ cls_formset = formset_factory(
+ form = OrderArticleForm,
+ extra = 0)
+
+ if request.POST:
+ formset = cls_formset(request.POST, initial=initial)
+
+ if not request.user.has_perm('kfet.add_order'):
+ messages.error(request, 'Permission refusée')
+ elif formset.is_valid():
+ order = Order()
+ order.supplier = supplier
+ saved = False
+ for form in formset:
+ if form.cleaned_data['quantity_ordered'] is not None:
+ if not saved:
+ order.save()
+ saved = True
+
+ article = articles.get(pk=form.cleaned_data['article'].pk)
+ q_ordered = form.cleaned_data['quantity_ordered']
+ if article.box_capacity:
+ q_ordered /= article.box_capacity
+ OrderArticle.objects.create(
+ order = order,
+ article = article,
+ quantity_ordered = q_ordered)
+ if saved:
+ messages.success(request, 'Commande créée')
+ return redirect('kfet.order.read', order.pk)
+ messages.warning(request, 'Rien commandé => Pas de commande')
+ else:
+ messages.error('Corrigez les erreurs')
+ else:
+ formset = cls_formset(initial=initial)
+
+ return render(request, 'kfet/order_create.html', {
+ 'supplier': supplier,
+ 'formset' : formset,
+ })
+
+class OrderRead(DetailView):
+ model = Order
+ template_name = 'kfet/order_read.html'
+ context_object_name = 'order'
+
+ def get_context_data(self, **kwargs):
+ context = super(OrderRead, self).get_context_data(**kwargs)
+ orderarticles = (OrderArticle.objects
+ .select_related('article', 'article__category')
+ .filter(order=self.object)
+ .order_by('article__category__name', 'article__name')
+ )
+ mail = ("Bonjour,\n\nNous voudrions pour le ##DATE## à la K-Fêt de "
+ "l'ENS Ulm :")
+ category = 0
+ for orderarticle in orderarticles:
+ if category != orderarticle.article.category:
+ category = orderarticle.article.category
+ mail += '\n'
+ nb = orderarticle.quantity_ordered
+ box = ''
+ if orderarticle.article.box_capacity:
+ nb /= orderarticle.article.box_capacity
+ if nb >= 2:
+ box = ' %ss de' % orderarticle.article.box_type
+ else:
+ box = ' %s de' % orderarticle.article.box_type
+ name = orderarticle.article.name.capitalize()
+ mail += "\n- %s%s %s" % (round(nb), box, name)
+
+ mail += ("\n\nMerci d'appeler le numéro suivant lorsque les livreurs "
+ "sont là : ##TELEPHONE##\nCordialement,\n##PRENOM## ##NOM## "
+ ", pour la K-Fêt de l'ENS Ulm")
+
+ context['mail'] = mail
+ return context
+
class SupplierUpdate(SuccessMessageMixin, UpdateView):
model = Supplier
template_name = 'kfet/supplier_form.html'