forked from DGNum/gestioCOF
Merge branch 'k-fet' into qwann/k-fet/home
This commit is contained in:
commit
f52aba8ca6
|
@ -1,21 +1,17 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (absolute_import, division,
|
|
||||||
print_function, unicode_literals)
|
|
||||||
from builtins import *
|
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import MinLengthValidator
|
from django.core.validators import MinLengthValidator
|
||||||
from django.contrib.auth.models import User, Group, Permission
|
from django.contrib.auth.models import User, Group, Permission
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.forms import modelformset_factory, inlineformset_factory
|
from django.forms import modelformset_factory
|
||||||
from django.forms.models import BaseInlineFormSet
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from kfet.models import (Account, Checkout, Article, OperationGroup, Operation,
|
from kfet.models import (
|
||||||
|
Account, Checkout, Article, OperationGroup, Operation,
|
||||||
CheckoutStatement, ArticleCategory, Settings, AccountNegative, Transfer,
|
CheckoutStatement, ArticleCategory, Settings, AccountNegative, Transfer,
|
||||||
TransferGroup, Supplier, Inventory, InventoryArticle)
|
TransferGroup, Supplier)
|
||||||
from gestioncof.models import CofProfile
|
from gestioncof.models import CofProfile
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
|
@ -131,7 +127,16 @@ class UserRestrictTeamForm(UserForm):
|
||||||
|
|
||||||
class UserGroupForm(forms.ModelForm):
|
class UserGroupForm(forms.ModelForm):
|
||||||
groups = forms.ModelMultipleChoiceField(
|
groups = forms.ModelMultipleChoiceField(
|
||||||
Group.objects.filter(name__icontains='K-Fêt'))
|
Group.objects.filter(name__icontains='K-Fêt'),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
def clean_groups(self):
|
||||||
|
groups = self.cleaned_data.get('groups')
|
||||||
|
# Si aucun groupe, on le dénomme
|
||||||
|
if not groups:
|
||||||
|
groups = self.instance.groups.exclude(name__icontains='K-Fêt')
|
||||||
|
return groups
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['groups']
|
fields = ['groups']
|
||||||
|
|
|
@ -18,6 +18,7 @@ from django.db.models import F
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
import re
|
import re
|
||||||
|
import hashlib
|
||||||
|
|
||||||
def choices_length(choices):
|
def choices_length(choices):
|
||||||
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
|
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
|
||||||
|
@ -154,6 +155,7 @@ class Account(models.Model):
|
||||||
# - Enregistre User, CofProfile à partir de "data"
|
# - Enregistre User, CofProfile à partir de "data"
|
||||||
# - Enregistre Account
|
# - Enregistre Account
|
||||||
def save(self, data = {}, *args, **kwargs):
|
def save(self, data = {}, *args, **kwargs):
|
||||||
|
|
||||||
if self.pk and data:
|
if self.pk and data:
|
||||||
# Account update
|
# Account update
|
||||||
|
|
||||||
|
@ -200,6 +202,11 @@ class Account(models.Model):
|
||||||
self.cofprofile = cof
|
self.cofprofile = cof
|
||||||
super(Account, self).save(*args, **kwargs)
|
super(Account, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def change_pwd(self, pwd):
|
||||||
|
pwd_sha256 = hashlib.sha256(pwd.encode('utf-8'))\
|
||||||
|
.hexdigest()
|
||||||
|
self.password = pwd_sha256
|
||||||
|
|
||||||
# Surcharge de delete
|
# Surcharge de delete
|
||||||
# Pas de suppression possible
|
# Pas de suppression possible
|
||||||
# Cas à régler plus tard
|
# Cas à régler plus tard
|
||||||
|
|
|
@ -8,7 +8,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#account, #checkout, input, #history, #basket, #basket_rel, #previous_op, #articles_data {
|
#account, #checkout, #article_selection, #history, #basket, #basket_rel, #previous_op, #articles_data {
|
||||||
background:#fff;
|
background:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#article_selection input {
|
#article_selection input, #article_selection span {
|
||||||
height:100%;
|
height:100%;
|
||||||
float:left;
|
float:left;
|
||||||
border:0;
|
border:0;
|
||||||
|
@ -263,12 +263,12 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#article_selection input+input {
|
#article_selection input+input #article_selection input+span {
|
||||||
border-right:0;
|
border-right:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#article_autocomplete {
|
#article_autocomplete {
|
||||||
width:90%;
|
width:80%;
|
||||||
padding-left:10px;
|
padding-left:10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,14 +277,24 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#article_stock {
|
||||||
|
width:10%;
|
||||||
|
line-height:38px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width:1200px) {
|
@media (min-width:1200px) {
|
||||||
#article_autocomplete {
|
#article_autocomplete {
|
||||||
width:92%
|
width:84%
|
||||||
}
|
}
|
||||||
|
|
||||||
#article_number {
|
#article_number {
|
||||||
width:8%;
|
width:8%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#article_stock {
|
||||||
|
width:8%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Article data */
|
/* Article data */
|
||||||
|
@ -319,6 +329,10 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
padding-left:20px;
|
padding-left:20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#articles_data .article.low-stock {
|
||||||
|
background:rgba(236,100,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
#articles_data .article:hover {
|
#articles_data .article:hover {
|
||||||
background:rgba(200,16,46,0.3);
|
background:rgba(200,16,46,0.3);
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
|
@ -384,6 +398,11 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
text-align:right;
|
text-align:right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#basket tr .lowstock {
|
||||||
|
display:none;
|
||||||
|
padding-right:15px;
|
||||||
|
}
|
||||||
|
|
||||||
#basket tr.ui-selected, #basket tr.ui-selecting {
|
#basket tr.ui-selected, #basket tr.ui-selecting {
|
||||||
background-color:rgba(200,16,46,0.6);
|
background-color:rgba(200,16,46,0.6);
|
||||||
color:#FFF;
|
color:#FFF;
|
||||||
|
|
|
@ -37,9 +37,10 @@ function amountDisplay(amount, is_cof=false, tri='') {
|
||||||
return amountToUKF(amount, is_cof);
|
return amountToUKF(amount, is_cof);
|
||||||
}
|
}
|
||||||
|
|
||||||
function amountToUKF(amount, is_cof=false) {
|
function amountToUKF(amount, is_cof=false, account=false) {
|
||||||
|
var rounding = account ? Math.floor : Math.round ;
|
||||||
var coef_cof = is_cof ? 1 + settings['subvention_cof'] / 100 : 1;
|
var coef_cof = is_cof ? 1 + settings['subvention_cof'] / 100 : 1;
|
||||||
return Math.floor(amount * coef_cof * 10);
|
return rounding(amount * coef_cof * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidTrigramme(trigramme) {
|
function isValidTrigramme(trigramme) {
|
||||||
|
|
|
@ -1,18 +1,197 @@
|
||||||
var STAT = {};
|
(function($){
|
||||||
|
window.StatsGroup = function (url, target) {
|
||||||
|
// a class to properly display statictics
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
// url : points to an ObjectResumeStat that lists the options through JSON
|
||||||
// FONCTIONS
|
// target : element of the DOM where to put the stats
|
||||||
// Permet de raffraichir un champ, étant donné :
|
|
||||||
// thing_url : l'url contenant le contenu
|
var self = this;
|
||||||
// thing_div : le div où le mettre
|
var element = $(target);
|
||||||
// empty_... : le truc à dire si on a un contenu vide
|
var content = $("<div>");
|
||||||
STAT.get_thing = function(thing_url, thing_div, empty_thing_message) {
|
var buttons;
|
||||||
$.get(thing_url, function(data) {
|
|
||||||
if(jQuery.trim(data).length==0) {
|
function dictToArray (dict, start) {
|
||||||
thing_div.html(empty_thing_message);
|
// converts the dicts returned by JSONResponse to Arrays
|
||||||
} else {
|
// necessary because for..in does not guarantee the order
|
||||||
thing_div.html(data);
|
if (start === undefined) start = 0;
|
||||||
|
var array = new Array();
|
||||||
|
for (var k in dict) {
|
||||||
|
array[k] = dict[k];
|
||||||
}
|
}
|
||||||
|
array.splice(0, start);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTimeChart (dict) {
|
||||||
|
// reads the balance data and put it into chartjs formatting
|
||||||
|
var data = dictToArray(dict, 0);
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var source = data[i];
|
||||||
|
data[i] = { x: new Date(source.at),
|
||||||
|
y: source.balance,
|
||||||
|
label: source.label }
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showStats () {
|
||||||
|
// CALLBACK : called when a button is selected
|
||||||
|
|
||||||
|
// shows the focus on the correct button
|
||||||
|
buttons.find(".focus").removeClass("focus");
|
||||||
|
$(this).addClass("focus");
|
||||||
|
|
||||||
|
// loads data and shows it
|
||||||
|
$.getJSON(this.stats_target_url + "?format=json",
|
||||||
|
displayStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayStats (data) {
|
||||||
|
// reads the json data and updates the chart display
|
||||||
|
|
||||||
|
var chart_datasets = [];
|
||||||
|
var charts = dictToArray(data.charts);
|
||||||
|
|
||||||
|
// are the points indexed by timestamps?
|
||||||
|
var is_time_chart = data.is_time_chart || false;
|
||||||
|
|
||||||
|
// reads the charts data
|
||||||
|
for (var i = 0; i < charts.length; i++) {
|
||||||
|
var chart = charts[i];
|
||||||
|
|
||||||
|
// format the data
|
||||||
|
var chart_data = is_time_chart ? handleTimeChart(chart.values) : dictToArray(chart.values, 1);
|
||||||
|
|
||||||
|
chart_datasets.push(
|
||||||
|
{
|
||||||
|
label: chart.label,
|
||||||
|
borderColor: chart.color,
|
||||||
|
backgroundColor: chart.color,
|
||||||
|
fill: is_time_chart,
|
||||||
|
lineTension: 0,
|
||||||
|
data: chart_data,
|
||||||
|
steppedLine: is_time_chart,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
// options for chartjs
|
||||||
|
var chart_options =
|
||||||
|
{
|
||||||
|
responsive: true,
|
||||||
|
tooltips: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
mode: 'nearest',
|
||||||
|
intersect: false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// additionnal options for time-indexed charts
|
||||||
|
if (is_time_chart) {
|
||||||
|
chart_options['scales'] = {
|
||||||
|
xAxes: [{
|
||||||
|
type: "time",
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
display: false,
|
||||||
|
labelString: 'Date'
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
tooltipFormat: 'll HH:mm',
|
||||||
|
displayFormats: {
|
||||||
|
'millisecond': 'SSS [ms]',
|
||||||
|
'second': 'mm:ss a',
|
||||||
|
'minute': 'DD MMM',
|
||||||
|
'hour': 'ddd h[h]',
|
||||||
|
'day': 'DD MMM',
|
||||||
|
'week': 'DD MMM',
|
||||||
|
'month': 'MMM',
|
||||||
|
'quarter': 'MMM',
|
||||||
|
'year': 'YYYY',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
display: false,
|
||||||
|
labelString: 'value'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// global object for the options
|
||||||
|
var chart_model =
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
options: chart_options,
|
||||||
|
data: {
|
||||||
|
labels: dictToArray(data.labels, 1),
|
||||||
|
datasets: chart_datasets,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// saves the previous charts to be destroyed
|
||||||
|
var prev_chart = content.children();
|
||||||
|
|
||||||
|
// creates a blank canvas element and attach it to the DOM
|
||||||
|
var canvas = $("<canvas>");
|
||||||
|
content.append(canvas);
|
||||||
|
|
||||||
|
// create the chart
|
||||||
|
var chart = new Chart(canvas, chart_model);
|
||||||
|
|
||||||
|
// clean
|
||||||
|
prev_chart.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the interface
|
||||||
|
function initialize (data) {
|
||||||
|
// creates the bar with the buttons
|
||||||
|
buttons = $("<div>",
|
||||||
|
{class: "btn-group btn-group-justified",
|
||||||
|
role: "group",
|
||||||
|
"aria-label": "select-period"});
|
||||||
|
|
||||||
|
var to_click;
|
||||||
|
var context = dictToArray(data.stats);
|
||||||
|
|
||||||
|
for (var i = 0; i < context.length; i++) {
|
||||||
|
// creates the button
|
||||||
|
var btn_wrapper = $("<div>",
|
||||||
|
{class: "btn-group",
|
||||||
|
role:"group"});
|
||||||
|
var btn = $("<button>",
|
||||||
|
{class: "btn btn-primary",
|
||||||
|
type: "button"})
|
||||||
|
.text(context[i].label)
|
||||||
|
.prop("stats_target_url", context[i].url)
|
||||||
|
.on("click", showStats);
|
||||||
|
|
||||||
|
// saves the default option to select
|
||||||
|
if (i == data.default_stat || i == 0)
|
||||||
|
to_click = btn;
|
||||||
|
|
||||||
|
// append the elements to the parent
|
||||||
|
btn_wrapper.append(btn);
|
||||||
|
buttons.append(btn_wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
// appends the contents to the DOM
|
||||||
|
element.append(buttons);
|
||||||
|
element.append(content);
|
||||||
|
|
||||||
|
// shows the default chart
|
||||||
|
to_click.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
(function () {
|
||||||
|
$.getJSON(url + "?format=json", initialize);
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
})(jQuery);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.db.models import Sum
|
||||||
|
|
||||||
|
KFET_WAKES_UP_AT = 7
|
||||||
|
|
||||||
# donne le nom des jours d'une liste de dates
|
# donne le nom des jours d'une liste de dates
|
||||||
# dans un dico ordonné
|
# dans un dico ordonné
|
||||||
|
@ -28,6 +30,7 @@ def monthnames(dates):
|
||||||
names[i] = dates[i].strftime("%B")
|
names[i] = dates[i].strftime("%B")
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
# rend les dates des nb derniers jours
|
# rend les dates des nb derniers jours
|
||||||
# dans l'ordre chronologique
|
# dans l'ordre chronologique
|
||||||
# aujourd'hui compris
|
# aujourd'hui compris
|
||||||
|
@ -59,7 +62,8 @@ def lastmonths(nb):
|
||||||
year = this_year + (nb - i) // 12
|
year = this_year + (nb - i) // 12
|
||||||
first_days[i] = timezone.datetime(year=year,
|
first_days[i] = timezone.datetime(year=year,
|
||||||
month=month,
|
month=month,
|
||||||
day=1)
|
day=1,
|
||||||
|
hour=KFET_WAKES_UP_AT)
|
||||||
return first_days
|
return first_days
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,7 +71,8 @@ def this_first_month_day():
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
first_day = timezone.datetime(year=now.year,
|
first_day = timezone.datetime(year=now.year,
|
||||||
month=now.month,
|
month=now.month,
|
||||||
day=1)
|
day=1,
|
||||||
|
hour=KFET_WAKES_UP_AT)
|
||||||
return first_day
|
return first_day
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +81,8 @@ def this_monday_morning():
|
||||||
monday = now - timezone.timedelta(days=now.isoweekday()-1)
|
monday = now - timezone.timedelta(days=now.isoweekday()-1)
|
||||||
monday_morning = timezone.datetime(year=monday.year,
|
monday_morning = timezone.datetime(year=monday.year,
|
||||||
month=monday.month,
|
month=monday.month,
|
||||||
day=monday.day)
|
day=monday.day,
|
||||||
|
hour=KFET_WAKES_UP_AT)
|
||||||
return monday_morning
|
return monday_morning
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,14 +90,13 @@ def this_morning():
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
morning = timezone.datetime(year=now.year,
|
morning = timezone.datetime(year=now.year,
|
||||||
month=now.month,
|
month=now.month,
|
||||||
day=now.day)
|
day=now.day,
|
||||||
|
hour=KFET_WAKES_UP_AT)
|
||||||
return morning
|
return morning
|
||||||
|
|
||||||
|
|
||||||
# Étant donné un queryset d'operations
|
# Étant donné un queryset d'operations
|
||||||
# rend la somme des article_nb
|
# rend la somme des article_nb
|
||||||
def tot_ventes(queryset):
|
def tot_ventes(queryset):
|
||||||
res = 0
|
res = queryset.aggregate(Sum('article_nb'))['article_nb__sum']
|
||||||
for op in queryset:
|
return res and res or 0
|
||||||
res += op.article_nb
|
|
||||||
return res
|
|
||||||
|
|
|
@ -14,14 +14,12 @@
|
||||||
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
|
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
|
||||||
<script type="text/javascript" src="{% static 'kfet/js/statistic.js' %}"></script>
|
<script type="text/javascript" src="{% static 'kfet/js/statistic.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
jQuery(document).ready(function() {
|
jQuery(document).ready(function() {
|
||||||
var stat_last = $("#stat_last");
|
var stat_last = new StatsGroup("{% url 'kfet.account.stat.last' trigramme=account.trigramme %}",
|
||||||
var stat_balance = $("#stat_balance");
|
$("#stat_last"));
|
||||||
var stat_last_url = "{% url 'kfet.account.stat.last' trigramme=account.trigramme %}";
|
var stat_balance = new StatsGroup("{% url 'kfet.account.stat.balance' trigramme=account.trigramme %}",
|
||||||
var stat_balance_url = "{% url 'kfet.account.stat.balance' trigramme=account.trigramme %}";
|
$("#stat_balance"));
|
||||||
STAT.get_thing(stat_last_url, stat_last, "Stat non trouvées :(");
|
});
|
||||||
STAT.get_thing(stat_balance_url, stat_balance, "Stat non trouvées :(");
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
{% load dictionary_extras %}
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<canvas id="{{ chart_id }}" height="100"></canvas>
|
|
||||||
{% comment %}
|
|
||||||
<ul>
|
|
||||||
{% for change in changes %}
|
|
||||||
<li>
|
|
||||||
{{ change | get_item:'label'}}
|
|
||||||
| {{ change | get_item:'at'}}
|
|
||||||
| ({{ change | get_item:'amount'}})
|
|
||||||
| balance {{ change | get_item:'balance'}}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endcomment %}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
var ctx1 = $({{ chart_id }});
|
|
||||||
var myChart = new Chart(ctx1, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [{
|
|
||||||
label: 'Balance',
|
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.5)',
|
|
||||||
data: [
|
|
||||||
{% for change in changes %}
|
|
||||||
{
|
|
||||||
x: new Date("{{ change | get_item:'at'}}"),
|
|
||||||
y: {{ change | get_item:'balance'| stringformat:"f" }},
|
|
||||||
label: "{{change|get_item:'label'}}"
|
|
||||||
}{% if not forloop.last %}, {% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
fill: true,
|
|
||||||
steppedLine: true,
|
|
||||||
lineTension: 0,
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
tooltips: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
mode: 'nearest',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
xAxes: [{
|
|
||||||
type: "time",
|
|
||||||
display: true,
|
|
||||||
scaleLabel: {
|
|
||||||
display: false,
|
|
||||||
labelString: 'Date'
|
|
||||||
},
|
|
||||||
time: {
|
|
||||||
tooltipFormat: 'll HH:mm',
|
|
||||||
min: new Date("{{ min_date }}"),
|
|
||||||
max: new Date("{{ max_date }}"),
|
|
||||||
displayFormats: {
|
|
||||||
'millisecond': 'SSS [ms]',
|
|
||||||
'second': 'mm:ss a',
|
|
||||||
'minute': 'DD MMM',
|
|
||||||
'hour': 'ddd h[h]',
|
|
||||||
'day': 'DD MMM',
|
|
||||||
'week': 'DD MMM',
|
|
||||||
'month': 'MMM',
|
|
||||||
'quarter': 'MMM',
|
|
||||||
'year': 'YYYY',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}],
|
|
||||||
yAxes: [{
|
|
||||||
display: true,
|
|
||||||
scaleLabel: {
|
|
||||||
display: false,
|
|
||||||
labelString: 'value'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
|
@ -1,52 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<canvas id="{{ chart_id }}" height="100" ></canvas>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
var ctx1 = $({{ chart_id }});
|
|
||||||
var myChart = new Chart(ctx1, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: [
|
|
||||||
{% for k,label in labels.items %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
"{{ label }}"
|
|
||||||
{% else %}
|
|
||||||
"{{ label }}",
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
datasets: [{
|
|
||||||
label: 'Nb items achetés',
|
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
|
||||||
backgroundColor: 'rgb(255, 99, 132)',
|
|
||||||
data: [
|
|
||||||
{% for k,nb in nb_ventes.items %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
"{{ nb }}"
|
|
||||||
{% else %}
|
|
||||||
"{{ nb }}",
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
fill: false,
|
|
||||||
lineTension: 0,
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
tooltips: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
mode: 'nearest',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
|
@ -1,82 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<canvas id="{{ chart_id }}" height="100" ></canvas>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
var ctx1 = $({{ chart_id }});
|
|
||||||
var myChart = new Chart(ctx1, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: [
|
|
||||||
{% for k,label in labels.items %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
"{{ label }}"
|
|
||||||
{% else %}
|
|
||||||
"{{ label }}",
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
datasets: [{
|
|
||||||
label: 'Toutes consommations',
|
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
|
||||||
backgroundColor: 'rgb(255, 99, 132)',
|
|
||||||
data: [
|
|
||||||
{% for k,nb in nb_ventes.items %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
"{{ nb }}"
|
|
||||||
{% else %}
|
|
||||||
"{{ nb }}",
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
fill: false,
|
|
||||||
lineTension: 0,
|
|
||||||
} , {
|
|
||||||
label: 'LIQ',
|
|
||||||
borderColor: 'rgb(54, 162, 235)',
|
|
||||||
backgroundColor: 'rgb(54, 162, 235)',
|
|
||||||
data: [
|
|
||||||
{% for k,nb in nb_liq.items %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
"{{ nb }}"
|
|
||||||
{% else %}
|
|
||||||
"{{ nb }}",
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
fill: false,
|
|
||||||
lineTension: 0,
|
|
||||||
} , {
|
|
||||||
label: 'Comptes K-Fêt',
|
|
||||||
borderColor: 'rgb(255, 205, 86)',
|
|
||||||
backgroundColor: 'rgb(255, 205, 86)',
|
|
||||||
data: [
|
|
||||||
{% for k,nb in nb_accounts.items %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
"{{ nb }}"
|
|
||||||
{% else %}
|
|
||||||
"{{ nb }}",
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
],
|
|
||||||
fill: false,
|
|
||||||
lineTension: 0,
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
tooltips: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
mode: 'nearest',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
|
@ -123,6 +123,7 @@
|
||||||
<div id="article_selection">
|
<div id="article_selection">
|
||||||
<input type="text" id="article_autocomplete" autocomplete="off">
|
<input type="text" id="article_autocomplete" autocomplete="off">
|
||||||
<input type="number" id="article_number" step="1" min="1">
|
<input type="number" id="article_number" step="1" min="1">
|
||||||
|
<span type="stock" id="article_stock"></span>
|
||||||
<input type="hidden" id="article_id" value="">
|
<input type="hidden" id="article_id" value="">
|
||||||
</div>
|
</div>
|
||||||
<div id="articles_data">
|
<div id="articles_data">
|
||||||
|
@ -221,7 +222,7 @@ $(document).ready(function() {
|
||||||
function displayAccountData() {
|
function displayAccountData() {
|
||||||
var balance = account_data['trigramme'] != 'LIQ' ? account_data['balance'] : '';
|
var balance = account_data['trigramme'] != 'LIQ' ? account_data['balance'] : '';
|
||||||
if (balance != '')
|
if (balance != '')
|
||||||
balance = amountToUKF(account_data['balance'], account_data['is_cof']);
|
balance = amountToUKF(account_data['balance'], account_data['is_cof'], true);
|
||||||
var is_cof = account_data['trigramme'] ? account_data['is_cof'] : '';
|
var is_cof = account_data['trigramme'] ? account_data['is_cof'] : '';
|
||||||
if (is_cof !== '')
|
if (is_cof !== '')
|
||||||
is_cof = is_cof ? '<b>COF</b>' : '<b>Non-COF</b>';
|
is_cof = is_cof ? '<b>COF</b>' : '<b>Non-COF</b>';
|
||||||
|
@ -616,7 +617,10 @@ $(document).ready(function() {
|
||||||
for (var elem in article) {
|
for (var elem in article) {
|
||||||
article_html.find('.'+elem).text(article[elem])
|
article_html.find('.'+elem).text(article[elem])
|
||||||
}
|
}
|
||||||
article_html.find('.price').text(amountToUKF(article['price'], false));
|
if (-5 <= article['stock'] && article['stock'] <= 5) {
|
||||||
|
article_html.addClass('low-stock');
|
||||||
|
}
|
||||||
|
article_html.find('.price').text(amountToUKF(article['price'], false, false)+' UKF');
|
||||||
var category_html = articles_container
|
var category_html = articles_container
|
||||||
.find('#data-category-'+article['category_id']);
|
.find('#data-category-'+article['category_id']);
|
||||||
if (category_html.length == 0) {
|
if (category_html.length == 0) {
|
||||||
|
@ -642,7 +646,7 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
$after.after(article_html);
|
$after.after(article_html);
|
||||||
// Pour l'autocomplétion
|
// Pour l'autocomplétion
|
||||||
articlesList.push([article['name'],article['id'],article['category_id'],article['price']]);
|
articlesList.push([article['name'],article['id'],article['category_id'],article['price'], article['stock']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getArticles() {
|
function getArticles() {
|
||||||
|
@ -670,8 +674,9 @@ $(document).ready(function() {
|
||||||
var articleSelect = $('#article_autocomplete');
|
var articleSelect = $('#article_autocomplete');
|
||||||
var articleId = $('#article_id');
|
var articleId = $('#article_id');
|
||||||
var articleNb = $('#article_number');
|
var articleNb = $('#article_number');
|
||||||
// 8:Backspace|9:Tab|13:Enter|46:DEL|112-117:F1-6|119-123:F8-F12
|
var articleStock = $('#article_stock');
|
||||||
var normalKeys = /^(8|9|13|46|112|113|114|115|116|117|119|120|121|122|123)$/;
|
// 8:Backspace|9:Tab|13:Enter|38-40:Arrows|46:DEL|112-117:F1-6|119-123:F8-F12
|
||||||
|
var normalKeys = /^(8|9|13|37|38|39|40|46|112|113|114|115|116|117|119|120|121|122|123)$/;
|
||||||
var articlesList = [];
|
var articlesList = [];
|
||||||
|
|
||||||
function deleteNonMatching(array, str) {
|
function deleteNonMatching(array, str) {
|
||||||
|
@ -727,6 +732,7 @@ $(document).ready(function() {
|
||||||
if (commit) {
|
if (commit) {
|
||||||
articleId.val(articlesMatch[0][1]);
|
articleId.val(articlesMatch[0][1]);
|
||||||
articleSelect.val(articlesMatch[0][0]);
|
articleSelect.val(articlesMatch[0][0]);
|
||||||
|
articleStock.text('/'+articlesMatch[0][4]);
|
||||||
displayMatchedArticles(articlesList);
|
displayMatchedArticles(articlesList);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -773,10 +779,15 @@ $(document).ready(function() {
|
||||||
return $article.find('.name').text();
|
return $article.find('.name').text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getArticleStock($article) {
|
||||||
|
return $article.find('.stock').text();
|
||||||
|
}
|
||||||
|
|
||||||
// Sélection des articles à la souris/tactile
|
// Sélection des articles à la souris/tactile
|
||||||
articles_container.on('click', '.article', function() {
|
articles_container.on('click', '.article', function() {
|
||||||
articleId.val(getArticleId($(this)));
|
articleId.val(getArticleId($(this)));
|
||||||
articleSelect.val(getArticleName($(this)));
|
articleSelect.val(getArticleName($(this)));
|
||||||
|
articleStock.text('/'+getArticleStock($(this)));
|
||||||
displayMatchedArticles(articlesList);
|
displayMatchedArticles(articlesList);
|
||||||
goToArticleNb();
|
goToArticleNb();
|
||||||
});
|
});
|
||||||
|
@ -790,6 +801,7 @@ $(document).ready(function() {
|
||||||
addPurchase(articleId.val(), articleNb.val());
|
addPurchase(articleId.val(), articleNb.val());
|
||||||
articleSelect.val('');
|
articleSelect.val('');
|
||||||
articleNb.val('');
|
articleNb.val('');
|
||||||
|
articleStock.text('');
|
||||||
articleSelect.focus();
|
articleSelect.focus();
|
||||||
displayMatchedArticles(articlesList);
|
displayMatchedArticles(articlesList);
|
||||||
return false;
|
return false;
|
||||||
|
@ -809,7 +821,7 @@ $(document).ready(function() {
|
||||||
// Basket
|
// Basket
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
var item_basket_default_html = '<tr><td class="amount"></td><td class="number"></td><td class="name"></td></tr>';
|
var item_basket_default_html = '<tr><td class="amount"></td><td class="number"></td><td ><span class="lowstock glyphicon glyphicon-alert"></span></td><td class="name"></td></tr>';
|
||||||
var basket_container = $('#basket table');
|
var basket_container = $('#basket table');
|
||||||
var arrowKeys = /^(37|38|39|40)$/;
|
var arrowKeys = /^(37|38|39|40)$/;
|
||||||
|
|
||||||
|
@ -827,17 +839,40 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPurchase(id, nb) {
|
function addPurchase(id, nb) {
|
||||||
|
var existing = false;
|
||||||
|
formset_container.find('[data-opeindex]').each(function () {
|
||||||
|
var opeindex = $(this).attr('data-opeindex');
|
||||||
|
var article_id = $(this).find('#id_form-'+opeindex+'-article').val();
|
||||||
|
if (article_id == id) {
|
||||||
|
existing = true ;
|
||||||
|
addExistingPurchase(opeindex, nb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!existing) {
|
||||||
var amount_euro = amountEuroPurchase(id, nb).toFixed(2);
|
var amount_euro = amountEuroPurchase(id, nb).toFixed(2);
|
||||||
var index = addPurchaseToFormset(article_data[1], nb, amount_euro);
|
var index = addPurchaseToFormset(article_data[1], nb, amount_euro);
|
||||||
article_basket_html = $(item_basket_default_html);
|
article_basket_html = $(item_basket_default_html);
|
||||||
article_basket_html
|
article_basket_html
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(nb).end()
|
.find('.number').text('('+nb+'/'+article_data[4]+')').end()
|
||||||
.find('.name').text(article_data[0]).end()
|
.find('.name').text(article_data[0]).end()
|
||||||
.find('.amount').text(amountToUKF(amount_euro, account_data['is_cof']));
|
.find('.amount').text(amountToUKF(amount_euro, account_data['is_cof']), false);
|
||||||
basket_container.prepend(article_basket_html);
|
basket_container.prepend(article_basket_html);
|
||||||
|
if (is_low_stock(id, nb))
|
||||||
|
article_basket_html.find('.lowstock')
|
||||||
|
.show();
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_low_stock(id, nb) {
|
||||||
|
var i = 0 ;
|
||||||
|
while (i<articlesList.length && id != articlesList[i][1]) i++;
|
||||||
|
article_data = articlesList[i];
|
||||||
|
|
||||||
|
stock = article_data[4] ;
|
||||||
|
return (-5 <= stock - nb && stock - nb <= 5);
|
||||||
|
}
|
||||||
|
|
||||||
function addDeposit(amount, is_checkout=1) {
|
function addDeposit(amount, is_checkout=1) {
|
||||||
var deposit_basket_html = $(item_basket_default_html);
|
var deposit_basket_html = $(item_basket_default_html);
|
||||||
|
@ -848,7 +883,7 @@ $(document).ready(function() {
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(amount+"€").end()
|
.find('.number').text(amount+"€").end()
|
||||||
.find('.name').text(text).end()
|
.find('.name').text(text).end()
|
||||||
.find('.amount').text(amountToUKF(amount, account_data['is_cof']));
|
.find('.amount').text(amountToUKF(amount, account_data['is_cof'], false));
|
||||||
basket_container.prepend(deposit_basket_html);
|
basket_container.prepend(deposit_basket_html);
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
@ -861,7 +896,7 @@ $(document).ready(function() {
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(amount+"€").end()
|
.find('.number').text(amount+"€").end()
|
||||||
.find('.name').text('Retrait').end()
|
.find('.name').text('Retrait').end()
|
||||||
.find('.amount').text(amountToUKF(amount, account_data['is_cof']));
|
.find('.amount').text(amountToUKF(amount, account_data['is_cof'], false));
|
||||||
basket_container.prepend(withdraw_basket_html);
|
basket_container.prepend(withdraw_basket_html);
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
@ -871,11 +906,25 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('keydown', function (e) {
|
$(document).on('keydown', function (e) {
|
||||||
if (e.keyCode == 46) {
|
switch(e.which) {
|
||||||
|
case 46:
|
||||||
// DEL (Suppr)
|
// DEL (Suppr)
|
||||||
basket_container.find('.ui-selected').each(function () {
|
basket_container.find('.ui-selected').each(function () {
|
||||||
deleteFromBasket($(this).data('opeindex'));
|
deleteFromBasket($(this).data('opeindex'));
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
// Arrow up
|
||||||
|
basket_container.find('.ui-selected').each(function () {
|
||||||
|
addExistingPurchase($(this).data('opeindex'), 1);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
// Arrow down
|
||||||
|
basket_container.find('.ui-selected').each(function () {
|
||||||
|
addExistingPurchase($(this).data('opeindex'), -1);
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -903,7 +952,7 @@ $(document).ready(function() {
|
||||||
var amount = $(this).find('#id_form-'+opeindex+'-amount');
|
var amount = $(this).find('#id_form-'+opeindex+'-amount');
|
||||||
if (!deleted && type == "purchase")
|
if (!deleted && type == "purchase")
|
||||||
amount.val(amountEuroPurchase(article_id, article_nb));
|
amount.val(amountEuroPurchase(article_id, article_nb));
|
||||||
basket_container.find('[data-opeindex='+opeindex+'] .amount').text(amountToUKF(amount.val(), account_data['is_cof']));
|
basket_container.find('[data-opeindex='+opeindex+'] .amount').text(amountToUKF(amount.val(), account_data['is_cof'], false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,9 +971,9 @@ $(document).ready(function() {
|
||||||
basketrel_html += '<div>Sur 20€: '+ (20-amount).toFixed(2) +' €</div>';
|
basketrel_html += '<div>Sur 20€: '+ (20-amount).toFixed(2) +' €</div>';
|
||||||
} else if (account_data['trigramme'] != '' && !isBasketEmpty()) {
|
} else if (account_data['trigramme'] != '' && !isBasketEmpty()) {
|
||||||
var amount = getAmountBasket();
|
var amount = getAmountBasket();
|
||||||
var amountUKF = amountToUKF(amount, account_data['is_cof']);
|
var amountUKF = amountToUKF(amount, account_data['is_cof'], false);
|
||||||
var newBalance = account_data['balance'] + amount;
|
var newBalance = account_data['balance'] + amount;
|
||||||
var newBalanceUKF = amountToUKF(newBalance, account_data['is_cof']);
|
var newBalanceUKF = amountToUKF(newBalance, account_data['is_cof'], true);
|
||||||
basketrel_html += '<div>Total: '+amountUKF+'</div>';
|
basketrel_html += '<div>Total: '+amountUKF+'</div>';
|
||||||
basketrel_html += '<div>Nouveau solde: '+newBalanceUKF+'</div>';
|
basketrel_html += '<div>Nouveau solde: '+newBalanceUKF+'</div>';
|
||||||
if (newBalance < 0)
|
if (newBalance < 0)
|
||||||
|
@ -939,6 +988,46 @@ $(document).ready(function() {
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addExistingPurchase(opeindex, nb) {
|
||||||
|
var type = formset_container.find("#id_form-"+opeindex+"-type").val();
|
||||||
|
var id = formset_container.find("#id_form-"+opeindex+"-article").val();
|
||||||
|
var nb_before = formset_container.find("#id_form-"+opeindex+"-article_nb").val();
|
||||||
|
var nb_after = parseInt(nb_before) + parseInt(nb);
|
||||||
|
var amountEuro_after = amountEuroPurchase(id, nb_after);
|
||||||
|
var amountUKF_after = amountToUKF(amountEuro_after, account_data['is_cof']);
|
||||||
|
|
||||||
|
if (type == 'purchase') {
|
||||||
|
if (nb_after == 0) {
|
||||||
|
deleteFromBasket(opeindex);
|
||||||
|
} else if (nb_after > 0 && nb_after <= 25) {
|
||||||
|
if (nb_before > 0) {
|
||||||
|
var article_html = basket_container.find('[data-opeindex='+opeindex+']');
|
||||||
|
article_html.find('.amount').text(amountUKF_after).end()
|
||||||
|
.find('.number').text('('+nb_after+'/'+article_data[4]+')').end() ;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
article_html = $(item_basket_default_html);
|
||||||
|
article_html
|
||||||
|
.attr('data-opeindex', opeindex)
|
||||||
|
.find('.number').text('('+nb_after+'/'+article_data[4]+')').end()
|
||||||
|
.find('.name').text(article_data[0]).end()
|
||||||
|
.find('.amount').text(amountUKF_after);
|
||||||
|
basket_container.prepend(article_basket_html);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_low_stock(id, nb_after))
|
||||||
|
article_html.find('.lowstock')
|
||||||
|
.show();
|
||||||
|
else
|
||||||
|
article_html.find('.lowstock')
|
||||||
|
.hide();
|
||||||
|
updateExistingFormset(opeindex, nb_after, amountEuro_after);
|
||||||
|
updateBasketRel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function resetBasket() {
|
function resetBasket() {
|
||||||
basket_container.find('tr').remove();
|
basket_container.find('tr').remove();
|
||||||
mngmt_total_forms = 1;
|
mngmt_total_forms = 1;
|
||||||
|
@ -948,6 +1037,7 @@ $(document).ready(function() {
|
||||||
articleId.val(0);
|
articleId.val(0);
|
||||||
articleSelect.val('');
|
articleSelect.val('');
|
||||||
articleNb.val('');
|
articleNb.val('');
|
||||||
|
articleStock.text('');
|
||||||
displayMatchedArticles(articlesList);
|
displayMatchedArticles(articlesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -959,7 +1049,7 @@ $(document).ready(function() {
|
||||||
var title = is_checkout ? 'Montant de la charge' : "Montant de l'édition";
|
var title = is_checkout ? 'Montant de la charge' : "Montant de l'édition";
|
||||||
$.confirm({
|
$.confirm({
|
||||||
title: title,
|
title: title,
|
||||||
content: '<input type="number" step="0.01" min="0.01" on autofocus placeholder="€">',
|
content: '<input type="number" lang="en" step="0.01" min="0.01" on autofocus placeholder="€">',
|
||||||
backgroundDismiss: true,
|
backgroundDismiss: true,
|
||||||
animation:'top',
|
animation:'top',
|
||||||
closeAnimation:'bottom',
|
closeAnimation:'bottom',
|
||||||
|
@ -986,7 +1076,7 @@ $(document).ready(function() {
|
||||||
function askWithdraw() {
|
function askWithdraw() {
|
||||||
$.confirm({
|
$.confirm({
|
||||||
title: 'Montant du retrait',
|
title: 'Montant du retrait',
|
||||||
content: '<input type="number" step="0.01" min="0.01" on autofocus placeholder="€">',
|
content: '<input type="number" lang="en" step="0.01" min="0.01" on autofocus placeholder="€">',
|
||||||
backgroundDismiss: true,
|
backgroundDismiss: true,
|
||||||
animation:'top',
|
animation:'top',
|
||||||
closeAnimation:'bottom',
|
closeAnimation:'bottom',
|
||||||
|
@ -1072,7 +1162,14 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteFromFormset(opeindex) {
|
function deleteFromFormset(opeindex) {
|
||||||
formset_container.find('#id_form-'+opeindex+'-DELETE').prop('checked', true);
|
updateExistingFormset(opeindex, 0, '0.00');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateExistingFormset(opeindex, nb, amount) {
|
||||||
|
formset_container
|
||||||
|
.find('#id_form-'+opeindex+'-amount').val((parseFloat(amount)).toFixed(2)).end()
|
||||||
|
.find('#id_form-'+opeindex+'-article_nb').val(nb).end()
|
||||||
|
.find('#id_form-'+opeindex+'-DELETE').prop('checked', !nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
@ -1164,7 +1261,7 @@ $(document).ready(function() {
|
||||||
function askAddcost(errors = '') {
|
function askAddcost(errors = '') {
|
||||||
$.confirm({
|
$.confirm({
|
||||||
title: 'Majoration',
|
title: 'Majoration',
|
||||||
content: errors + '<input type="text" placeholder="Trigramme" autocomplete="off" name="trigramme" spellcheck="false" style="text-transform:uppercase" autofocus><input type="number" step="0.01" min="0.01" placeholder="€" name="amount">',
|
content: errors + '<input type="text" placeholder="Trigramme" autocomplete="off" name="trigramme" spellcheck="false" style="text-transform:uppercase" autofocus><input type="number" lang="en" step="0.01" min="0.01" placeholder="€" name="amount">',
|
||||||
backgroundDismiss: true,
|
backgroundDismiss: true,
|
||||||
animation:'top',
|
animation:'top',
|
||||||
closeAnimation:'bottom',
|
closeAnimation:'bottom',
|
||||||
|
@ -1251,7 +1348,9 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
for (var i=0; i<data['articles'].length; i++) {
|
for (var i=0; i<data['articles'].length; i++) {
|
||||||
article = data['articles'][i];
|
article = data['articles'][i];
|
||||||
articles_container.find('[data-article='+article['id']+'] .stock')
|
articles_container.find('#data-article-'+article['id'])
|
||||||
|
.addClass('low-stock');
|
||||||
|
articles_container.find('#data-article-'+article['id']+' .stock')
|
||||||
.text(article['stock']);
|
.text(article['stock']);
|
||||||
}
|
}
|
||||||
if (data['addcost']) {
|
if (data['addcost']) {
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load dictionary_extras %}
|
|
||||||
<body>
|
|
||||||
<div class="btn-group btn-group-justified" role="group" aria-label="select-period">
|
|
||||||
{% for k,stat in stats.items %}
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<button id="{{ stat | get_item:'btn' }}" type="button" class="btn btn-primary {{ id_prefix }}-btn {%if k == default_stat%} focus{%endif%}">{{ stat | get_item:'label' }}</button>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div><!-- /boutons -->
|
|
||||||
<div id="{{ content_id}}">
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
// VARIABLES
|
|
||||||
{{id_prefix}}_content_id = $("#{{content_id}}");
|
|
||||||
{{id_prefix}}_btns = $(".{{id_prefix}}-btn");
|
|
||||||
{% for k,stat in stats.items %}
|
|
||||||
{% if k == default_stat %}
|
|
||||||
{{id_prefix}}_default_url = "{{ stat | get_item:'url' }}";
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
// INIT
|
|
||||||
STAT.get_thing({{id_prefix}}_default_url, {{id_prefix}}_content_id, "Ouppss ?");
|
|
||||||
{% for k,stat in stats.items %}
|
|
||||||
$("#{{stat|get_item:'btn'}}").on('click', function() {
|
|
||||||
STAT.get_thing("{{stat|get_item:'url'}}", {{id_prefix}}_content_id, "Ouuups ?");
|
|
||||||
{{id_prefix}}_btns.removeClass("focus");
|
|
||||||
$("#{{stat|get_item:'btn'}}").addClass("focus");
|
|
||||||
});
|
|
||||||
{% endfor %}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
|
@ -41,5 +41,4 @@ def highlight_clipper(clipper, q):
|
||||||
@register.filter()
|
@register.filter()
|
||||||
def ukf(balance, is_cof):
|
def ukf(balance, is_cof):
|
||||||
grant = is_cof and (1 + Settings.SUBVENTION_COF() / 100) or 1
|
grant = is_cof and (1 + Settings.SUBVENTION_COF() / 100) or 1
|
||||||
# float nécessaire car sinon problème avec le round de future.builtins
|
return floor(balance * 10 * grant)
|
||||||
return floor(float(balance) * 10 * grant)
|
|
||||||
|
|
141
kfet/tests.py
141
kfet/tests.py
|
@ -2,143 +2,4 @@
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from kfet.models import Account
|
# Écrire les tests ici
|
||||||
from gestioncof.models import CofProfile, User
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
names = ['Abelardus',
|
|
||||||
'Abrahamus',
|
|
||||||
'Acacius',
|
|
||||||
'Accius',
|
|
||||||
'Achaicus',
|
|
||||||
'Achill',
|
|
||||||
'Achilles',
|
|
||||||
'Achilleus',
|
|
||||||
'Acrisius',
|
|
||||||
'Actaeon',
|
|
||||||
'Acteon',
|
|
||||||
'Adalricus',
|
|
||||||
'Adelfonsus',
|
|
||||||
'Adelphus',
|
|
||||||
'Adeodatus',
|
|
||||||
'Adolfus',
|
|
||||||
'Adolphus',
|
|
||||||
'Adrastus',
|
|
||||||
'Adrianus',
|
|
||||||
'Ægidius',
|
|
||||||
'Ælia',
|
|
||||||
'Ælianus',
|
|
||||||
'Æmilianus',
|
|
||||||
'Æmilius',
|
|
||||||
'Aeneas',
|
|
||||||
'Æolus',
|
|
||||||
'Æschylus',
|
|
||||||
'Æson',
|
|
||||||
'Æsop',
|
|
||||||
'Æther',
|
|
||||||
'Ætius',
|
|
||||||
'Agapetus',
|
|
||||||
'Agapitus',
|
|
||||||
'Agapius',
|
|
||||||
'Agathangelus',
|
|
||||||
'Aigidius',
|
|
||||||
'Aiolus',
|
|
||||||
'Ajax',
|
|
||||||
'Alair',
|
|
||||||
'Alaricus',
|
|
||||||
'Albanus',
|
|
||||||
'Alberic',
|
|
||||||
'Albericus',
|
|
||||||
'Albertus',
|
|
||||||
'Albinus',
|
|
||||||
'Albus',
|
|
||||||
'Alcaeus',
|
|
||||||
'Alcander',
|
|
||||||
'Alcimus',
|
|
||||||
'Alcinder',
|
|
||||||
'Alerio',
|
|
||||||
'Alexandrus',
|
|
||||||
'Alexis',
|
|
||||||
'Alexius',
|
|
||||||
'Alexus',
|
|
||||||
'Alfonsus',
|
|
||||||
'Alfredus',
|
|
||||||
'Almericus',
|
|
||||||
'Aloisius',
|
|
||||||
'Aloysius',
|
|
||||||
'Alphaeus',
|
|
||||||
'Alpheaus',
|
|
||||||
'Alpheus',
|
|
||||||
'Alphoeus',
|
|
||||||
'Alphonsus',
|
|
||||||
'Alphonzus',
|
|
||||||
'Alvinius',
|
|
||||||
'Alvredus',
|
|
||||||
'Amadeus',
|
|
||||||
'Amaliricus',
|
|
||||||
'Amandus',
|
|
||||||
'Amantius',
|
|
||||||
'Amarandus',
|
|
||||||
'Amaranthus',
|
|
||||||
'Amatus',
|
|
||||||
'Ambrosianus',
|
|
||||||
'Ambrosius',
|
|
||||||
'Amedeus',
|
|
||||||
'Americus',
|
|
||||||
'Amlethus',
|
|
||||||
'Amletus',
|
|
||||||
'Amor',
|
|
||||||
'Ampelius',
|
|
||||||
'Amphion',
|
|
||||||
'Anacletus',
|
|
||||||
'Anastasius',
|
|
||||||
'Anastatius',
|
|
||||||
'Anastius',
|
|
||||||
'Anatolius',
|
|
||||||
'Androcles',
|
|
||||||
'Andronicus',
|
|
||||||
'Anencletus',
|
|
||||||
'Angelicus',
|
|
||||||
'Angelus',
|
|
||||||
'Anicetus',
|
|
||||||
'Antigonus',
|
|
||||||
'Antipater',
|
|
||||||
'Antoninus',
|
|
||||||
'Antonius',
|
|
||||||
'Aphrodisius',
|
|
||||||
'Apollinaris']
|
|
||||||
|
|
||||||
|
|
||||||
# Ne pas supprimer, peut être utile pour regénérer
|
|
||||||
# d'autres fixtures à l'avenir
|
|
||||||
class TestFoo(TestCase):
|
|
||||||
fixtures = ['users', 'sites', 'gestion', 'articles']
|
|
||||||
|
|
||||||
def test_foo(self):
|
|
||||||
pass
|
|
||||||
# for name in names:
|
|
||||||
# user = User(username=name,
|
|
||||||
# last_name='Romain',
|
|
||||||
# first_name=name)
|
|
||||||
# user.email = '{}.{}@ens.fr'.format(user.first_name, user.last_name)
|
|
||||||
# user.save()
|
|
||||||
|
|
||||||
# galois_trigrammes = map('{:03d}'.format, range(40))
|
|
||||||
# galois = CofProfile.objects.filter(user__last_name='Gaulois')
|
|
||||||
|
|
||||||
# romains_trigrammes = map(lambda x: str(100+x), range(40))
|
|
||||||
# romains = CofProfile.objects.filter(user__last_name='Romain')
|
|
||||||
|
|
||||||
# for t, c in zip(galois_trigrammes, galois):
|
|
||||||
# Account.objects.create(cofprofile=c, trigramme=t)
|
|
||||||
# for t, c in zip(romains_trigrammes, romains):
|
|
||||||
# Account.objects.create(cofprofile=c, trigramme=t)
|
|
||||||
|
|
||||||
|
|
||||||
# class TestBar(TestCase):
|
|
||||||
# fixtures = ['users', 'sites', 'gestion', 'articles', 'groups', 'accounts']
|
|
||||||
|
|
||||||
|
|
||||||
# def test_foo(self):
|
|
||||||
# pass
|
|
||||||
|
|
163
kfet/views.py
163
kfet/views.py
|
@ -1,37 +1,49 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.views.generic import ListView, DetailView, TemplateView
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.views.generic.list import BaseListView, MultipleObjectTemplateResponseMixin
|
from django.views.generic.list import BaseListView, MultipleObjectTemplateResponseMixin
|
||||||
from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin
|
from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin
|
||||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.contrib.auth.models import User, Permission, Group
|
from django.contrib.auth.models import User, Permission, Group
|
||||||
from django.http import HttpResponse, JsonResponse, Http404
|
from django.http import JsonResponse, Http404
|
||||||
from django.forms import modelformset_factory, formset_factory
|
from django.forms import formset_factory
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import transaction
|
||||||
from django.db.models import F, Sum, Prefetch, Count, Func
|
from django.db.models import F, Sum, Prefetch, Count
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from gestioncof.models import CofProfile, Clipper
|
from gestioncof.models import CofProfile, Clipper
|
||||||
from kfet.decorators import teamkfet_required
|
from kfet.decorators import teamkfet_required
|
||||||
from kfet.models import (Account, Checkout, Article, Settings, AccountNegative,
|
from kfet.models import (
|
||||||
|
Account, Checkout, Article, Settings, AccountNegative,
|
||||||
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
|
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
|
||||||
InventoryArticle, Order, OrderArticle, Operation, OperationGroup, Transfer)
|
InventoryArticle, Order, OrderArticle, Operation, OperationGroup,
|
||||||
from kfet.forms import *
|
TransferGroup, Transfer)
|
||||||
|
from kfet.forms import (
|
||||||
|
AccountTriForm, AccountBalanceForm, AccountNoTriForm, UserForm, CofForm,
|
||||||
|
UserRestrictTeamForm, UserGroupForm, AccountForm, CofRestrictForm,
|
||||||
|
AccountPwdForm, AccountNegativeForm, UserRestrictForm, AccountRestrictForm,
|
||||||
|
GroupForm, CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm,
|
||||||
|
CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm,
|
||||||
|
KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm,
|
||||||
|
KPsulOperationFormSet, AddcostForm, FilterHistoryForm, SettingsForm,
|
||||||
|
TransferFormSet, InventoryArticleForm, OrderArticleForm,
|
||||||
|
OrderArticleToInventoryForm
|
||||||
|
)
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from kfet import consumers
|
from kfet import consumers
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from decimal import Decimal
|
||||||
import django_cas_ng
|
import django_cas_ng
|
||||||
import hashlib
|
|
||||||
import heapq
|
import heapq
|
||||||
import statistics
|
import statistics
|
||||||
from .statistic import daynames, monthnames, weeknames, \
|
from .statistic import daynames, monthnames, weeknames, \
|
||||||
|
@ -54,6 +66,7 @@ class Home(TemplateView):
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(TemplateView, self).dispatch(*args, **kwargs)
|
return super(TemplateView, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
def login_genericteam(request):
|
def login_genericteam(request):
|
||||||
# Check si besoin de déconnecter l'utilisateur de CAS
|
# Check si besoin de déconnecter l'utilisateur de CAS
|
||||||
|
@ -359,6 +372,7 @@ def account_read(request, trigramme):
|
||||||
|
|
||||||
# Account - Update
|
# Account - Update
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def account_update(request, trigramme):
|
def account_update(request, trigramme):
|
||||||
account = get_object_or_404(Account, trigramme=trigramme)
|
account = get_object_or_404(Account, trigramme=trigramme)
|
||||||
|
@ -375,7 +389,8 @@ def account_update(request, trigramme):
|
||||||
cof_form = CofRestrictForm(instance=account.cofprofile)
|
cof_form = CofRestrictForm(instance=account.cofprofile)
|
||||||
pwd_form = AccountPwdForm()
|
pwd_form = AccountPwdForm()
|
||||||
if account.balance < 0 and not hasattr(account, 'negative'):
|
if account.balance < 0 and not hasattr(account, 'negative'):
|
||||||
AccountNegative.objects.create(account=account, start=timezone.now())
|
AccountNegative.objects.create(account=account,
|
||||||
|
start=timezone.now())
|
||||||
account.refresh_from_db()
|
account.refresh_from_db()
|
||||||
if hasattr(account, 'negative'):
|
if hasattr(account, 'negative'):
|
||||||
negative_form = AccountNegativeForm(instance=account.negative)
|
negative_form = AccountNegativeForm(instance=account.negative)
|
||||||
|
@ -396,12 +411,15 @@ def account_update(request, trigramme):
|
||||||
|
|
||||||
if request.user.has_perm('kfet.is_team'):
|
if request.user.has_perm('kfet.is_team'):
|
||||||
account_form = AccountForm(request.POST, instance=account)
|
account_form = AccountForm(request.POST, instance=account)
|
||||||
cof_form = CofRestrictForm(request.POST, instance=account.cofprofile)
|
cof_form = CofRestrictForm(request.POST,
|
||||||
user_form = UserRestrictTeamForm(request.POST, instance=account.user)
|
instance=account.cofprofile)
|
||||||
|
user_form = UserRestrictTeamForm(request.POST,
|
||||||
|
instance=account.user)
|
||||||
group_form = UserGroupForm(request.POST, instance=account.user)
|
group_form = UserGroupForm(request.POST, instance=account.user)
|
||||||
pwd_form = AccountPwdForm(request.POST)
|
pwd_form = AccountPwdForm(request.POST)
|
||||||
if hasattr(account, 'negative'):
|
if hasattr(account, 'negative'):
|
||||||
negative_form = AccountNegativeForm(request.POST, instance=account.negative)
|
negative_form = AccountNegativeForm(request.POST,
|
||||||
|
instance=account.negative)
|
||||||
|
|
||||||
if (request.user.has_perm('kfet.change_account')
|
if (request.user.has_perm('kfet.change_account')
|
||||||
and account_form.is_valid() and cof_form.is_valid()
|
and account_form.is_valid() and cof_form.is_valid()
|
||||||
|
@ -413,15 +431,14 @@ def account_update(request, trigramme):
|
||||||
put_cleaned_data_in_dict(data, cof_form)
|
put_cleaned_data_in_dict(data, cof_form)
|
||||||
|
|
||||||
# Updating
|
# Updating
|
||||||
account_form.save(data = data)
|
account_form.save(data=data)
|
||||||
|
|
||||||
# Checking perm to update password
|
# Checking perm to update password
|
||||||
if (request.user.has_perm('kfet.change_account_password')
|
if (request.user.has_perm('kfet.change_account_password')
|
||||||
and pwd_form.is_valid()):
|
and pwd_form.is_valid()):
|
||||||
pwd = pwd_form.cleaned_data['pwd1']
|
pwd = pwd_form.cleaned_data['pwd1']
|
||||||
pwd_sha256 = hashlib.sha256(pwd.encode('utf-8')).hexdigest()
|
account.change_pwd(pwd)
|
||||||
Account.objects.filter(pk=account.pk).update(
|
account.save()
|
||||||
password = pwd_sha256)
|
|
||||||
messages.success(request, 'Mot de passe mis à jour')
|
messages.success(request, 'Mot de passe mis à jour')
|
||||||
|
|
||||||
# Checking perm to manage perms
|
# Checking perm to manage perms
|
||||||
|
@ -437,47 +454,64 @@ def account_update(request, trigramme):
|
||||||
if (hasattr(account, 'negative')
|
if (hasattr(account, 'negative')
|
||||||
and request.user.has_perm('kfet.change_accountnegative')
|
and request.user.has_perm('kfet.change_accountnegative')
|
||||||
and negative_form.is_valid()):
|
and negative_form.is_valid()):
|
||||||
balance_offset_new = negative_form.cleaned_data['balance_offset']
|
balance_offset_new = \
|
||||||
|
negative_form.cleaned_data['balance_offset']
|
||||||
if not balance_offset_new:
|
if not balance_offset_new:
|
||||||
balance_offset_new = 0
|
balance_offset_new = 0
|
||||||
balance_offset_diff = balance_offset_new - balance_offset_old
|
balance_offset_diff = (balance_offset_new
|
||||||
|
- balance_offset_old)
|
||||||
Account.objects.filter(pk=account.pk).update(
|
Account.objects.filter(pk=account.pk).update(
|
||||||
balance = F('balance') + balance_offset_diff)
|
balance=F('balance') + balance_offset_diff)
|
||||||
negative_form.save()
|
negative_form.save()
|
||||||
if not balance_offset_new and Account.objects.get(pk=account.pk).balance >= 0:
|
if Account.objects.get(pk=account.pk).balance >= 0 \
|
||||||
|
and not balance_offset_new:
|
||||||
AccountNegative.objects.get(account=account).delete()
|
AccountNegative.objects.get(account=account).delete()
|
||||||
|
|
||||||
success = True
|
success = True
|
||||||
messages.success(request,
|
messages.success(
|
||||||
'Informations du compte %s mises à jour' % account.trigramme)
|
request,
|
||||||
|
'Informations du compte %s mises à jour'
|
||||||
|
% account.trigramme)
|
||||||
|
|
||||||
|
# Modification de ses propres informations
|
||||||
if request.user == account.user:
|
if request.user == account.user:
|
||||||
missing_perm = False
|
missing_perm = False
|
||||||
account.refresh_from_db()
|
account.refresh_from_db()
|
||||||
user_form = UserRestrictForm(request.POST, instance=account.user)
|
user_form = UserRestrictForm(request.POST, instance=account.user)
|
||||||
account_form = AccountRestrictForm(request.POST, instance=account)
|
account_form = AccountRestrictForm(request.POST, instance=account)
|
||||||
|
pwd_form = AccountPwdForm(request.POST)
|
||||||
|
|
||||||
if user_form.is_valid() and account_form.is_valid():
|
if user_form.is_valid() and account_form.is_valid():
|
||||||
user_form.save()
|
user_form.save()
|
||||||
account_form.save()
|
account_form.save()
|
||||||
success = True
|
success = True
|
||||||
messages.success(request, 'Vos informations ont été mises à jour')
|
messages.success(request,
|
||||||
|
'Vos informations ont été mises à jour')
|
||||||
|
|
||||||
|
if request.user.has_perm('kfet.is_team') \
|
||||||
|
and pwd_form.is_valid():
|
||||||
|
pwd = pwd_form.cleaned_data['pwd1']
|
||||||
|
account.change_pwd(pwd)
|
||||||
|
account.save()
|
||||||
|
messages.success(
|
||||||
|
request, 'Votre mot de passe a été mis à jour')
|
||||||
|
|
||||||
if missing_perm:
|
if missing_perm:
|
||||||
messages.error(request, 'Permission refusée')
|
messages.error(request, 'Permission refusée')
|
||||||
if success:
|
if success:
|
||||||
return redirect('kfet.account.read', account.trigramme)
|
return redirect('kfet.account.read', account.trigramme)
|
||||||
else:
|
else:
|
||||||
messages.error(request, 'Informations non mises à jour. Corrigez les erreurs')
|
messages.error(
|
||||||
|
request, 'Informations non mises à jour. Corrigez les erreurs')
|
||||||
|
|
||||||
return render(request, "kfet/account_update.html", {
|
return render(request, "kfet/account_update.html", {
|
||||||
'account' : account,
|
'account': account,
|
||||||
'account_form' : account_form,
|
'account_form': account_form,
|
||||||
'cof_form' : cof_form,
|
'cof_form': cof_form,
|
||||||
'user_form' : user_form,
|
'user_form': user_form,
|
||||||
'group_form' : group_form,
|
'group_form': group_form,
|
||||||
'negative_form': negative_form,
|
'negative_form': negative_form,
|
||||||
'pwd_form' : pwd_form,
|
'pwd_form': pwd_form,
|
||||||
})
|
})
|
||||||
|
|
||||||
@permission_required('kfet.manage_perms')
|
@permission_required('kfet.manage_perms')
|
||||||
|
@ -1703,6 +1737,7 @@ def order_create(request, pk):
|
||||||
|
|
||||||
articles = (Article.objects
|
articles = (Article.objects
|
||||||
.filter(suppliers=supplier.pk)
|
.filter(suppliers=supplier.pk)
|
||||||
|
.distinct()
|
||||||
.select_related('category')
|
.select_related('category')
|
||||||
.order_by('category__name', 'name'))
|
.order_by('category__name', 'name'))
|
||||||
|
|
||||||
|
@ -1993,6 +2028,14 @@ class JSONResponseMixin(object):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class JSONDetailView(JSONResponseMixin,
|
||||||
|
BaseDetailView):
|
||||||
|
"""
|
||||||
|
Returns a DetailView that renders a JSON
|
||||||
|
"""
|
||||||
|
def render_to_response(self, context):
|
||||||
|
return self.render_to_json_response(context)
|
||||||
|
|
||||||
class HybridDetailView(JSONResponseMixin,
|
class HybridDetailView(JSONResponseMixin,
|
||||||
SingleObjectTemplateResponseMixin,
|
SingleObjectTemplateResponseMixin,
|
||||||
BaseDetailView):
|
BaseDetailView):
|
||||||
|
@ -2023,14 +2066,13 @@ class HybridListView(JSONResponseMixin,
|
||||||
return super(HybridListView, self).render_to_response(context)
|
return super(HybridListView, self).render_to_response(context)
|
||||||
|
|
||||||
|
|
||||||
class ObjectResumeStat(DetailView):
|
class ObjectResumeStat(JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Summarize all the stats of an object
|
Summarize all the stats of an object
|
||||||
DOES NOT RETURN A JSON RESPONSE
|
Handles JSONResponse
|
||||||
"""
|
"""
|
||||||
template_name = 'kfet/object_stat_resume.html'
|
|
||||||
context_object_name = ''
|
context_object_name = ''
|
||||||
id_prefix = 'id_a_definir'
|
id_prefix = ''
|
||||||
# nombre de vues à résumer
|
# nombre de vues à résumer
|
||||||
nb_stat = 2
|
nb_stat = 2
|
||||||
# Le combienième est celui par defaut ?
|
# Le combienième est celui par defaut ?
|
||||||
|
@ -2059,7 +2101,7 @@ class ObjectResumeStat(DetailView):
|
||||||
'btn': "btn_%s_%d_%d" % (self.id_prefix,
|
'btn': "btn_%s_%d_%d" % (self.id_prefix,
|
||||||
object_id,
|
object_id,
|
||||||
i),
|
i),
|
||||||
'url': reverse_lazy(self.stat_urls[i],
|
'url': reverse(self.stat_urls[i],
|
||||||
kwargs=dict(
|
kwargs=dict(
|
||||||
self.get_object_url_kwargs(),
|
self.get_object_url_kwargs(),
|
||||||
**url_kwargs[i]
|
**url_kwargs[i]
|
||||||
|
@ -2082,7 +2124,7 @@ ID_PREFIX_ACC_BALANCE = "balance_acc"
|
||||||
|
|
||||||
|
|
||||||
# Un résumé de toutes les vues ArticleStatBalance
|
# Un résumé de toutes les vues ArticleStatBalance
|
||||||
# NE REND PAS DE JSON
|
# REND DU JSON
|
||||||
class AccountStatBalanceAll(ObjectResumeStat):
|
class AccountStatBalanceAll(ObjectResumeStat):
|
||||||
model = Account
|
model = Account
|
||||||
context_object_name = 'account'
|
context_object_name = 'account'
|
||||||
|
@ -2115,9 +2157,9 @@ class AccountStatBalanceAll(ObjectResumeStat):
|
||||||
return super(AccountStatBalanceAll, self).dispatch(*args, **kwargs)
|
return super(AccountStatBalanceAll, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AccountStatBalance(HybridDetailView):
|
class AccountStatBalance(JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Returns a graph (or a JSON Response) of the evolution a the personnal
|
Returns a JSON containing the evolution a the personnal
|
||||||
balance of a trigramme between timezone.now() and `nb_days`
|
balance of a trigramme between timezone.now() and `nb_days`
|
||||||
ago (specified to the view as an argument)
|
ago (specified to the view as an argument)
|
||||||
takes into account the Operations and the Transfers
|
takes into account the Operations and the Transfers
|
||||||
|
@ -2126,7 +2168,6 @@ class AccountStatBalance(HybridDetailView):
|
||||||
model = Account
|
model = Account
|
||||||
trigramme_url_kwarg = 'trigramme'
|
trigramme_url_kwarg = 'trigramme'
|
||||||
nb_date_url_kwargs = 'nb_date'
|
nb_date_url_kwargs = 'nb_date'
|
||||||
template_name = 'kfet/account_stat_balance.html'
|
|
||||||
context_object_name = 'account'
|
context_object_name = 'account'
|
||||||
id_prefix = ""
|
id_prefix = ""
|
||||||
|
|
||||||
|
@ -2233,10 +2274,10 @@ class AccountStatBalance(HybridDetailView):
|
||||||
nb_days_string = 'anytime'
|
nb_days_string = 'anytime'
|
||||||
else:
|
else:
|
||||||
nb_days_string = str(int(nb_days))
|
nb_days_string = str(int(nb_days))
|
||||||
context['changes'] = changes
|
context['charts'] = [ { "color": "rgb(255, 99, 132)",
|
||||||
context['chart_id'] = "%s_%s_%s_days" % (self.id_prefix,
|
"label": "Balance",
|
||||||
self.object.id,
|
"values": changes } ]
|
||||||
nb_days_string)
|
context['is_time_chart'] = True
|
||||||
context['min_date'] = changes[len(changes)-1]['at']
|
context['min_date'] = changes[len(changes)-1]['at']
|
||||||
context['max_date'] = changes[0]['at']
|
context['max_date'] = changes[0]['at']
|
||||||
# TODO: offset
|
# TODO: offset
|
||||||
|
@ -2282,14 +2323,13 @@ class AccountStatLastAll(ObjectResumeStat):
|
||||||
return super(AccountStatLastAll, self).dispatch(*args, **kwargs)
|
return super(AccountStatLastAll, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AccountStatLast(HybridDetailView):
|
class AccountStatLast(JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Returns a graph (or a JSON Response) of the evolution a the personnal
|
Returns a JSON containing the evolution a the personnal
|
||||||
consommation of a trigramme at the diffent dates specified
|
consommation of a trigramme at the diffent dates specified
|
||||||
"""
|
"""
|
||||||
model = Account
|
model = Account
|
||||||
trigramme_url_kwarg = 'trigramme'
|
trigramme_url_kwarg = 'trigramme'
|
||||||
template_name = 'kfet/account_stat_last.html'
|
|
||||||
context_object_name = 'account'
|
context_object_name = 'account'
|
||||||
end_date = timezone.now()
|
end_date = timezone.now()
|
||||||
id_prefix = ""
|
id_prefix = ""
|
||||||
|
@ -2341,10 +2381,9 @@ class AccountStatLast(HybridDetailView):
|
||||||
operations = self.sort_operations()
|
operations = self.sort_operations()
|
||||||
for i in operations:
|
for i in operations:
|
||||||
nb_ventes[i] = tot_ventes(operations[i])
|
nb_ventes[i] = tot_ventes(operations[i])
|
||||||
context['nb_ventes'] = nb_ventes
|
context['charts'] = [ { "color": "rgb(255, 99, 132)",
|
||||||
# ID unique
|
"label": "NB items achetés",
|
||||||
context['chart_id'] = "%s_%d" % (self.id_prefix,
|
"values": nb_ventes } ]
|
||||||
self.object.id)
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
|
@ -2421,13 +2460,12 @@ class ArticleStatLastAll(ObjectResumeStat):
|
||||||
return super(ArticleStatLastAll, self).dispatch(*args, **kwargs)
|
return super(ArticleStatLastAll, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ArticleStatLast(HybridDetailView):
|
class ArticleStatLast(JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Returns a graph (or a JSON Response) of the consommation
|
Returns a JSON containing the consommation
|
||||||
of an article at the diffent dates precised
|
of an article at the diffent dates precised
|
||||||
"""
|
"""
|
||||||
model = Article
|
model = Article
|
||||||
template_name = 'kfet/article_stat_last.html'
|
|
||||||
context_object_name = 'article'
|
context_object_name = 'article'
|
||||||
end_date = timezone.now()
|
end_date = timezone.now()
|
||||||
id_prefix = ""
|
id_prefix = ""
|
||||||
|
@ -2488,12 +2526,15 @@ class ArticleStatLast(HybridDetailView):
|
||||||
operations[i]
|
operations[i]
|
||||||
.exclude(group__on_acc__trigramme='LIQ')
|
.exclude(group__on_acc__trigramme='LIQ')
|
||||||
)
|
)
|
||||||
context['nb_ventes'] = nb_ventes
|
context['charts'] = [ { "color": "rgb(255, 99, 132)",
|
||||||
context['nb_accounts'] = nb_accounts
|
"label": "Toutes consommations",
|
||||||
context['nb_liq'] = nb_liq
|
"values": nb_ventes },
|
||||||
# ID unique
|
{ "color": "rgb(54, 162, 235)",
|
||||||
context['chart_id'] = "%s_%d" % (self.id_prefix,
|
"label": "LIQ",
|
||||||
self.object.id)
|
"values": nb_liq },
|
||||||
|
{ "color": "rgb(255, 205, 86)",
|
||||||
|
"label": "Comptes K-Fêt",
|
||||||
|
"values": nb_accounts } ]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
|
|
Loading…
Reference in a new issue