Merge branch 'Aufinal/inventaires' into 'master'

K-Fêt - Création d'inventaires
- Amélioration de l'interface
- Ajout de colonnes intermédiaires pour faciliter le calcul
- Ajout d'indication de conflits possibles avec les opérations en cours
- Résolution semi-automatique de ces conflits

Closes #141

See merge request !200
This commit is contained in:
Aurélien Delobelle 2017-04-04 10:59:09 +02:00
commit 0405eee7a7
7 changed files with 230 additions and 41 deletions

View file

@ -463,7 +463,7 @@ class InventoryArticleForm(forms.Form):
queryset = Article.objects.all(), queryset = Article.objects.all(),
widget = forms.HiddenInput(), widget = forms.HiddenInput(),
) )
stock_new = forms.IntegerField(required = False) stock_new = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(InventoryArticleForm, self).__init__(*args, **kwargs) super(InventoryArticleForm, self).__init__(*args, **kwargs)
@ -472,6 +472,7 @@ class InventoryArticleForm(forms.Form):
self.stock_old = kwargs['initial']['stock_old'] self.stock_old = kwargs['initial']['stock_old']
self.category = kwargs['initial']['category'] self.category = kwargs['initial']['category']
self.category_name = kwargs['initial']['category__name'] self.category_name = kwargs['initial']['category__name']
self.box_capacity = kwargs['initial']['box_capacity']
# ----- # -----
# Order forms # Order forms

View file

@ -32,6 +32,7 @@ textarea {
.table { .table {
margin-bottom:0; margin-bottom:0;
border-bottom:1px solid #ddd;
} }
.table { .table {
@ -230,6 +231,9 @@ textarea {
height:28px; height:28px;
margin:3px 0px; margin:3px 0px;
} }
.content-center .auth-form {
margin:15px;
}
/* /*
* Pages formulaires seuls * Pages formulaires seuls
@ -549,3 +553,18 @@ thead .tooltip {
.help-block { .help-block {
padding-top: 15px; padding-top: 15px;
} }
/* Inventaires */
.inventory_modified {
background:rgba(236,100,0,0.15);
}
.stock_diff {
padding-left: 5px;
color:#C8102E;
}
.inventory_update {
display:none;
}

View file

@ -28,6 +28,7 @@
.jconfirm .jconfirm-box .content { .jconfirm .jconfirm-box .content {
border-bottom:1px solid #ddd; border-bottom:1px solid #ddd;
padding:5px 10px;
} }
.jconfirm .jconfirm-box input { .jconfirm .jconfirm-box input {

View file

@ -1,4 +1,11 @@
{% extends 'kfet/base.html' %} {% extends 'kfet/base.html' %}
{% load staticfiles %}
{% load widget_tweaks %}
{% block extra_head %}
<script type="text/javascript" src="{% static 'kfet/js/reconnecting-websocket.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/jquery-confirm.js' %}"></script>
{% endblock %}
{% block title %}Nouvel inventaire{% endblock %} {% block title %}Nouvel inventaire{% endblock %}
{% block content-header-title %}Nouvel inventaire{% endblock %} {% block content-header-title %}Nouvel inventaire{% endblock %}
@ -6,38 +13,194 @@
{% block content %} {% block content %}
{% include 'kfet/base_messages.html' %} {% include 'kfet/base_messages.html' %}
<div class="content-center">
<form action="" method="post"> <div>
<table> <form id='inventoryform' action="" method="post">
<thead> <table class="table text-center">
<tr> <thead>
<td>Article</td>
<td>Théo.</td>
<td>Réel</td>
</tr>
</thead>
<tbody>
{% for form in formset %}
{% ifchanged form.category %}
<tr> <tr>
<td colspan="3">{{ form.category_name }}</td> <td>Article</td>
<td>Quantité par caisse</td>
<td>Stock Théorique</td>
<td>Caisses en réserve</td>
<td>Caisses en arrière</td>
<td>Vrac</td>
<td>Stock total</td>
<td>Compte terminé</td>
</tr> </tr>
{% endifchanged %} </thead>
<tr> <tbody>
{{ form.article }} {% for form in formset %}
<td>{{ form.name }}</td> {% ifchanged form.category %}
<td>{{ form.stock_old }}</td> <tr class='section'>
<td>{{ form.stock_new }}</td> <td>{{ form.category_name }}</td>
</tr> <td colspan="7"></td>
{% endfor %} </tr>
</tbody> {% endifchanged %}
</table> <tr>
{% if not perms.kfet.add_inventory %} {{ form.article }}
<input type="password" name="KFETPASSWORD"> <td class='name'>{{ form.name }}</td>
{% endif %} <td class='box_capacity'>{{ form.box_capacity }}</td>
{% csrf_token %} <td><span class='current_stock'>{{ form.stock_old }}</span><span class='stock_diff'></span></td>
{{ formset.management_form }} <td class='box_cellar'>
<input type="submit" value="Enregistrer" class="btn btn-primary btn-lg"> <div class='col-md-2'></div>
</form> <div class='col-md-8'>
<input type='number' class='form-control' step='1'>
</div>
</td>
<td class='box_bar'>
<div class='col-md-offset-2 col-md-8'><input type='number' class='form-control' step='1'></div>
</td>
<td class='misc'>
<div class='col-md-offset-2 col-md-8'><input type='number' class='form-control' step='1'></div>
</td>
<td class='stock_new'>
<div class='col-md-offset-2 col-md-8'>{{ form.stock_new | attr:"readonly"| add_class:"form-control" }}</div>
<div class='col-md-2 inventory_update'><button type='button' class='btn-sm btn-primary'>MàJ</button></div>
</td>
<td class='finished'><input type='checkbox' class='form_control'></td>
</tr>
{% endfor %}
</tbody>
</table>
{{ formset.management_form }}
{% if not perms.kfet.add_inventory %}
<div class='auth-form form-horizontal'>
{% include "kfet/form_authentication_snippet.html" %}
</div>
{% endif %}
<input type="submit" value="Enregistrer" class="btn btn-primary btn-lg btn-block">
{% csrf_token %}
</form>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
'use strict';
var conflicts = new Set();
/**
* Autofill new stock from other inputs
*/
$('input[type="number"]').on('input', function() {
var $line = $(this).closest('tr');
var box_capacity = +$line.find('.box_capacity').text();
var box_cellar = $line.find('.box_cellar input').val();
var box_bar = $line.find('.box_bar input').val();
var misc = $line.find('.misc input').val();
if (box_cellar || box_bar || misc)
$line.find('.stock_new input').val(
box_capacity*((+box_cellar) +(+box_bar))+(+misc));
else
$line.find('.stock_new input').val('');
});
/*
* Remove warning and update stock
*/
function update_stock($line, update_count) {
$line.removeClass('inventory_modified');
$line.find('.inventory_update').hide();
var old_stock = +$line.find('.current_stock').text()
var stock_diff = +$line.find('.stock_diff').text();
$line.find('.current_stock').text(old_stock + stock_diff);
$line.find('.stock_diff').text('');
if ($line.find('.stock_new input').val() && update_count) {
var old_misc = +$line.find('.misc input').val();
$line.find('.misc input').val(old_misc + stock_diff)
.trigger('input');
}
var id = $line.find('input[type="hidden"]').val();
conflicts.delete(parseInt(id));
}
$('.finished input').change(function() {
var $line = $(this).closest('tr');
update_stock($line, false);
});
$('.inventory_update button').click(function() {
var $line = $(this).closest('tr');
update_stock($line, true);
});
/**
* Websocket
*/
OperationWebSocket.add_handler(function(data) {
for (let article of data['articles']) {
var $line = $('input[value="'+article.id+'"]').parent();
if ($line.find('.finished input').is(":checked")) {
conflicts.add(article.id);
//Display warning
$line.addClass('inventory_modified');
//Realigning input and displaying update button
$line.find('.inventory_update').show();
//Displaying stock changes
var stock = $line.find('.current_stock').text();
$line.find('.stock_diff').text(article.stock - stock);
} else {
// If we haven't counted the article yet, we simply update the expected stock
$line.find('.current_stock').text(article.stock);
}
}
});
$('input[type="submit"]').on("click", function(e) {
e.preventDefault();
if (conflicts.size) {
content = '';
content += "Conflits possibles :"
content += '<ul>';
for (let id of conflicts) {
var $line = $('input[value="'+id+'"]').closest('tr');
var name = $line.find('.name').text();
var stock_diff = $line.find('.stock_diff').text();
content += '<li>'+name+' ('+stock_diff+')</li>';
}
content += '</ul>'
} else {
// Prevent erroneous enter key confirmations
// Kinda complicated to filter if click or enter key...
content="Voulez-vous confirmer l'inventaire ?";
}
$.confirm({
title: "Confirmer l'inventaire",
content: content,
backgroundDismiss: true,
animation: 'top',
closeAnimation: 'bottom',
keyboardEnabled: true,
confirm: function() {
$('#inventoryform').submit();
},
onOpen: function() {
var that = this;
this.$content.find('input').on('keydown', function(e) {
if (e.keyCode == 13)
that.$confirmButton.click();
});
},
});
});
});
</script>
{% endblock %} {% endblock %}

View file

@ -66,11 +66,13 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if not perms.kfet.add_order %}
<input type="password" name="KFETPASSWORD">
{% endif %}
{{ formset.management_form }} {{ formset.management_form }}
<input type="submit" class="btn btn-primary btn-lg btn-block" value="Envoyer"> {% if not perms.kfet.add_inventory %}
<div class='auth-form form-horizontal'>
{% include "kfet/form_authentication_snippet.html" %}
</div>
{% endif %}
<input type="submit" value="Enregistrer" class="btn btn-primary btn-lg btn-block">
</form> </form>
</div> </div>
</div> </div>

View file

@ -42,11 +42,13 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if not perms.kfet.order_to_inventory %}
<input type="password" name="KFETPASSWORD">
{% endif %}
{{ formset.management_form }} {{ formset.management_form }}
<input type="submit" class="btn btn-primary btn-lg btn-block" value="Enregistrer"> {% if not perms.kfet.add_inventory %}
<div class='auth-form form-horizontal'>
{% include "kfet/form_authentication_snippet.html" %}
</div>
{% endif %}
<input type="submit" value="Enregistrer" class="btn btn-primary btn-lg btn-block">
</form> </form>
</div> </div>
</div> </div>

View file

@ -1654,7 +1654,8 @@ def inventory_create(request):
'stock_old': article.stock, 'stock_old': article.stock,
'name' : article.name, 'name' : article.name,
'category' : article.category_id, 'category' : article.category_id,
'category__name': article.category.name 'category__name': article.category.name,
'box_capacity': article.box_capacity or 0,
}) })
cls_formset = formset_factory( cls_formset = formset_factory(