forked from DGNum/gestioCOF
Inventaire depuis une commande
- 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`
This commit is contained in:
parent
61feb9bbcd
commit
9467103879
9 changed files with 218 additions and 7 deletions
|
@ -447,3 +447,27 @@ class OrderArticleForm(forms.Form):
|
||||||
self.v_et = kwargs['initial']['v_et']
|
self.v_et = kwargs['initial']['v_et']
|
||||||
self.v_prev = kwargs['initial']['v_prev']
|
self.v_prev = kwargs['initial']['v_prev']
|
||||||
self.c_rec = kwargs['initial']['c_rec']
|
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']
|
||||||
|
|
31
kfet/migrations/0040_auto_20160829_2035.py
Normal file
31
kfet/migrations/0040_auto_20160829_2035.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import datetime
|
||||||
|
from django.utils.timezone import utc
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('kfet', '0039_auto_20160828_0430'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='order',
|
||||||
|
options={'ordering': ['-at']},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='supplierarticle',
|
||||||
|
name='at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 8, 29, 18, 35, 3, 419033, tzinfo=utc)),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='box_type',
|
||||||
|
field=models.CharField(choices=[('caisse', 'caisse'), ('carton', 'carton'), ('palette', 'palette'), ('fût', 'fût')], null=True, max_length=7, blank=True, default=None),
|
||||||
|
),
|
||||||
|
]
|
18
kfet/migrations/0041_auto_20160830_1502.py
Normal file
18
kfet/migrations/0041_auto_20160830_1502.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('kfet', '0040_auto_20160829_2035'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='globalpermissions',
|
||||||
|
options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'), ('order_to_inventory', "Générer un inventaire à partir d'une commande")), 'managed': False},
|
||||||
|
),
|
||||||
|
]
|
|
@ -381,7 +381,10 @@ class InventoryArticle(models.Model):
|
||||||
stock_error = models.IntegerField(default = 0)
|
stock_error = models.IntegerField(default = 0)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.stock_error = self.stock_new - self.stock_old
|
# S'il s'agit d'un inventaire provenant d'une livraison, il n'y a pas
|
||||||
|
# d'erreur
|
||||||
|
if not hasattr(self.inventory, 'order'):
|
||||||
|
self.stock_error = self.stock_new - self.stock_old
|
||||||
super(InventoryArticle, self).save(*args, **kwargs)
|
super(InventoryArticle, self).save(*args, **kwargs)
|
||||||
|
|
||||||
class Supplier(models.Model):
|
class Supplier(models.Model):
|
||||||
|
@ -403,6 +406,7 @@ class SupplierArticle(models.Model):
|
||||||
Supplier, on_delete = models.PROTECT)
|
Supplier, on_delete = models.PROTECT)
|
||||||
article = models.ForeignKey(
|
article = models.ForeignKey(
|
||||||
Article, on_delete = models.PROTECT)
|
Article, on_delete = models.PROTECT)
|
||||||
|
at = models.DateTimeField(auto_now_add = True)
|
||||||
price_HT = models.DecimalField(
|
price_HT = models.DecimalField(
|
||||||
max_digits = 7, decimal_places = 4,
|
max_digits = 7, decimal_places = 4,
|
||||||
blank = True, null = True, default = None)
|
blank = True, null = True, default = None)
|
||||||
|
@ -543,6 +547,7 @@ class GlobalPermissions(models.Model):
|
||||||
('manage_addcosts', 'Gérer les majorations'),
|
('manage_addcosts', 'Gérer les majorations'),
|
||||||
('perform_commented_operations', 'Enregistrer des commandes avec commentaires'),
|
('perform_commented_operations', 'Enregistrer des commandes avec commentaires'),
|
||||||
('view_negs', 'Voir la liste des négatifs'),
|
('view_negs', 'Voir la liste des négatifs'),
|
||||||
|
('order_to_inventory', "Générer un inventaire à partir d'une commande")
|
||||||
)
|
)
|
||||||
|
|
||||||
class Settings(models.Model):
|
class Settings(models.Model):
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<li><a href="{% url 'kfet.checkout' %}">Caisses</a></li>
|
<li><a href="{% url 'kfet.checkout' %}">Caisses</a></li>
|
||||||
<li><a href="{% url 'kfet.article' %}">Articles</a></li>
|
<li><a href="{% url 'kfet.article' %}">Articles</a></li>
|
||||||
<li><a href="{% url 'kfet.history' %}">Historique</a></li>
|
<li><a href="{% url 'kfet.history' %}">Historique</a></li>
|
||||||
|
<li><a href="{% url 'kfet.order' %}">Commandes</a></li>
|
||||||
{% if user.username != 'kfet_genericteam' %}
|
{% if user.username != 'kfet_genericteam' %}
|
||||||
<li><a href="{% url 'kfet.login.genericteam' %}" target="_blank" id="genericteam">Connexion standard</a></li>
|
<li><a href="{% url 'kfet.login.genericteam' %}" target="_blank" id="genericteam">Connexion standard</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -66,6 +66,14 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ order.supplier }}</td>
|
<td>{{ order.supplier }}</td>
|
||||||
|
<td>
|
||||||
|
{% if order.inventory %}
|
||||||
|
<a href="">Inventaire associé</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'kfet.order.to_inventory' order.pk %}">
|
||||||
|
Maj stock
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
48
kfet/templates/kfet/order_to_inventory.html
Normal file
48
kfet/templates/kfet/order_to_inventory.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{% extends 'kfet/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}{% endblock %}
|
||||||
|
{% block content-header-title %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% include 'kfet/base_messages.html' %}
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Article</td>
|
||||||
|
<td>HT</td>
|
||||||
|
<td>TVA</td>
|
||||||
|
<td>Droits</td>
|
||||||
|
<td>Com.</td>
|
||||||
|
<td>Reçu</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for form in formset %}
|
||||||
|
{% ifchanged form.category %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6">{{ form.category_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endifchanged %}
|
||||||
|
<tr>
|
||||||
|
{{ form.article }}
|
||||||
|
<td>{{ form.name }}</td>
|
||||||
|
<td>{{ form.price_HT }}</td>
|
||||||
|
<td>{{ form.TVA }}</td>
|
||||||
|
<td>{{ form.rights }}</td>
|
||||||
|
<td>{{ form.quantity_ordered }}</td>
|
||||||
|
<td>{{ form.quantity_received }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% if not perms.kfet.order_to_inventory %}
|
||||||
|
<input type="password" name="KFETPASSWORD">
|
||||||
|
{% endif %}
|
||||||
|
{{ formset.management_form }}
|
||||||
|
<input type="submit" value="Enregistrer">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -179,7 +179,7 @@ urlpatterns = [
|
||||||
url(r'^orders/$',
|
url(r'^orders/$',
|
||||||
permission_required('kfet.is_team')(views.OrderList.as_view()),
|
permission_required('kfet.is_team')(views.OrderList.as_view()),
|
||||||
name = 'kfet.order'),
|
name = 'kfet.order'),
|
||||||
url(r'^orders/(?P<pk>\d+)',
|
url(r'^orders/(?P<pk>\d+)$',
|
||||||
permission_required('kfet.is_team')(views.OrderRead.as_view()),
|
permission_required('kfet.is_team')(views.OrderRead.as_view()),
|
||||||
name = 'kfet.order.read'),
|
name = 'kfet.order.read'),
|
||||||
url(r'^orders/suppliers/(?P<pk>\d+)/edit$',
|
url(r'^orders/suppliers/(?P<pk>\d+)/edit$',
|
||||||
|
@ -187,4 +187,6 @@ urlpatterns = [
|
||||||
name = 'kfet.order.supplier.update'),
|
name = 'kfet.order.supplier.update'),
|
||||||
url(r'^orders/suppliers/(?P<pk>\d+)/new-order$', views.order_create,
|
url(r'^orders/suppliers/(?P<pk>\d+)/new-order$', views.order_create,
|
||||||
name = 'kfet.order.new'),
|
name = 'kfet.order.new'),
|
||||||
|
url(r'^orders/(?P<pk>\d+)/to_inventory$', views.order_to_inventory,
|
||||||
|
name = 'kfet.order.to_inventory'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1367,15 +1367,13 @@ class OrderList(ListView):
|
||||||
|
|
||||||
@permission_required('kfet.is_team')
|
@permission_required('kfet.is_team')
|
||||||
def order_create(request, pk):
|
def order_create(request, pk):
|
||||||
supplier = Supplier.objects.get(pk=pk)
|
supplier = get_object_or_404(Supplier, pk=pk)
|
||||||
|
|
||||||
articles = (Article.objects
|
articles = (Article.objects
|
||||||
.filter(suppliers=supplier.pk)
|
.filter(suppliers=supplier.pk)
|
||||||
.select_related('category')
|
.select_related('category')
|
||||||
.order_by('category__name', 'name'))
|
.order_by('category__name', 'name'))
|
||||||
|
|
||||||
print(articles[0].suppliers.all())
|
|
||||||
|
|
||||||
initial = []
|
initial = []
|
||||||
today = timezone.now().date()
|
today = timezone.now().date()
|
||||||
sales_q = (Operation.objects
|
sales_q = (Operation.objects
|
||||||
|
@ -1429,7 +1427,7 @@ def order_create(request, pk):
|
||||||
v_prev = v_moy + v_et
|
v_prev = v_moy + v_et
|
||||||
c_rec_tot = max(v_prev * 1.5 - article.stock, 0)
|
c_rec_tot = max(v_prev * 1.5 - article.stock, 0)
|
||||||
if article.box_capacity:
|
if article.box_capacity:
|
||||||
c_rec_temp = c_rec_tot / box_capacity
|
c_rec_temp = c_rec_tot / article.box_capacity
|
||||||
if c_rec_temp >= 10:
|
if c_rec_temp >= 10:
|
||||||
c_rec = round(c_rec_temp)
|
c_rec = round(c_rec_temp)
|
||||||
elif c_rec_temp > 5:
|
elif c_rec_temp > 5:
|
||||||
|
@ -1478,7 +1476,7 @@ def order_create(request, pk):
|
||||||
article = articles.get(pk=form.cleaned_data['article'].pk)
|
article = articles.get(pk=form.cleaned_data['article'].pk)
|
||||||
q_ordered = form.cleaned_data['quantity_ordered']
|
q_ordered = form.cleaned_data['quantity_ordered']
|
||||||
if article.box_capacity:
|
if article.box_capacity:
|
||||||
q_ordered /= article.box_capacity
|
q_ordered *= article.box_capacity
|
||||||
OrderArticle.objects.create(
|
OrderArticle.objects.create(
|
||||||
order = order,
|
order = order,
|
||||||
article = article,
|
article = article,
|
||||||
|
@ -1534,6 +1532,82 @@ class OrderRead(DetailView):
|
||||||
context['mail'] = mail
|
context['mail'] = mail
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def order_to_inventory(request, pk):
|
||||||
|
order = get_object_or_404(Order, pk=pk)
|
||||||
|
|
||||||
|
if hasattr(order, 'inventory'):
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
articles = (Article.objects
|
||||||
|
.filter(orders=order.pk)
|
||||||
|
.select_related('category')
|
||||||
|
.prefetch_related(Prefetch('orderarticle_set',
|
||||||
|
queryset = OrderArticle.objects.filter(order=order),
|
||||||
|
to_attr = 'order'))
|
||||||
|
.prefetch_related(Prefetch('supplierarticle_set',
|
||||||
|
queryset = (SupplierArticle.objects
|
||||||
|
.filter(supplier=order.supplier)
|
||||||
|
.order_by('-at')),
|
||||||
|
to_attr = 'supplier'))
|
||||||
|
.order_by('category__name', 'name'))
|
||||||
|
|
||||||
|
initial = []
|
||||||
|
for article in articles:
|
||||||
|
initial.append({
|
||||||
|
'article': article.pk,
|
||||||
|
'name': article.name,
|
||||||
|
'category': article.category_id,
|
||||||
|
'category__name': article.category.name,
|
||||||
|
'quantity_ordered': article.order[0].quantity_ordered,
|
||||||
|
'quantity_received': article.order[0].quantity_ordered,
|
||||||
|
'price_HT': article.supplier[0].price_HT,
|
||||||
|
'TVA': article.supplier[0].TVA,
|
||||||
|
'rights': article.supplier[0].rights,
|
||||||
|
})
|
||||||
|
|
||||||
|
cls_formset = formset_factory(OrderArticleToInventoryForm, extra=0)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
formset = cls_formset(request.POST, initial=initial)
|
||||||
|
|
||||||
|
if not request.user.has_perm('kfet.order_to_inventory'):
|
||||||
|
message.error(request, 'Permission refusée')
|
||||||
|
elif formset.is_valid():
|
||||||
|
with transaction.atomic():
|
||||||
|
inventory = Inventory()
|
||||||
|
inventory.order = order
|
||||||
|
inventory.by = request.user.profile.account_kfet
|
||||||
|
inventory.save()
|
||||||
|
for form in formset:
|
||||||
|
q_received = form.cleaned_data['quantity_received']
|
||||||
|
article = form.cleaned_data['article']
|
||||||
|
SupplierArticle.objects.create(
|
||||||
|
supplier = order.supplier,
|
||||||
|
article = article,
|
||||||
|
price_HT = form.cleaned_data['price_HT'],
|
||||||
|
TVA = form.cleaned_data['TVA'],
|
||||||
|
rights = form.cleaned_data['rights'])
|
||||||
|
(OrderArticle.objects
|
||||||
|
.filter(order=order, article=article)
|
||||||
|
.update(quantity_received = q_received))
|
||||||
|
InventoryArticle.objects.create(
|
||||||
|
inventory = inventory,
|
||||||
|
article = article,
|
||||||
|
stock_old = article.stock,
|
||||||
|
stock_new = article.stock + q_received)
|
||||||
|
article.stock += q_received
|
||||||
|
article.save()
|
||||||
|
messages.success(request, "C'est tout bon !")
|
||||||
|
return redirect('kfet.order')
|
||||||
|
else:
|
||||||
|
messages.error(request, "Corrigez les erreurs")
|
||||||
|
else:
|
||||||
|
formset = cls_formset(initial=initial)
|
||||||
|
|
||||||
|
return render(request, 'kfet/order_to_inventory.html', {
|
||||||
|
'formset': formset,
|
||||||
|
})
|
||||||
|
|
||||||
class SupplierUpdate(SuccessMessageMixin, UpdateView):
|
class SupplierUpdate(SuccessMessageMixin, UpdateView):
|
||||||
model = Supplier
|
model = Supplier
|
||||||
template_name = 'kfet/supplier_form.html'
|
template_name = 'kfet/supplier_form.html'
|
||||||
|
|
Loading…
Reference in a new issue