diff --git a/kfet/forms.py b/kfet/forms.py index 80504b03..317effb3 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -1,21 +1,17 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from builtins import * - from decimal import Decimal from django import forms from django.core.exceptions import ValidationError from django.core.validators import MinLengthValidator from django.contrib.auth.models import User, Group, Permission from django.contrib.contenttypes.models import ContentType -from django.forms import modelformset_factory, inlineformset_factory -from django.forms.models import BaseInlineFormSet +from django.forms import modelformset_factory 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, - TransferGroup, Supplier, Inventory, InventoryArticle) + TransferGroup, Supplier) from gestioncof.models import CofProfile # ----- @@ -131,7 +127,16 @@ class UserRestrictTeamForm(UserForm): class UserGroupForm(forms.ModelForm): 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: model = User fields = ['groups'] diff --git a/kfet/models.py b/kfet/models.py index 55ae7b76..ca9fa177 100644 --- a/kfet/models.py +++ b/kfet/models.py @@ -18,6 +18,7 @@ from django.db.models import F from django.core.cache import cache from datetime import date, timedelta import re +import hashlib def choices_length(choices): 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 Account def save(self, data = {}, *args, **kwargs): + if self.pk and data: # Account update @@ -200,6 +202,11 @@ class Account(models.Model): self.cofprofile = cof 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 # Pas de suppression possible # Cas à régler plus tard @@ -561,7 +568,7 @@ class Operation(models.Model): templates = { self.PURCHASE: "{nb} {article.name} ({amount}€)", self.DEPOSIT: "charge ({amount})", - self.WITHDRAW: "retrait ({amount})", + self.WITHDRAW: "retrait ({amount})", self.INITIAL: "initial ({amount})", } return templates[self.type].format(nb=self.article_nb, diff --git a/kfet/static/kfet/css/kpsul.css b/kfet/static/kfet/css/kpsul.css index e7950914..ba88e433 100644 --- a/kfet/static/kfet/css/kpsul.css +++ b/kfet/static/kfet/css/kpsul.css @@ -8,7 +8,7 @@ input[type=number]::-webkit-outer-spin-button { 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; } @@ -252,7 +252,7 @@ input[type=number]::-webkit-outer-spin-button { width:100%; } -#article_selection input { +#article_selection input, #article_selection span { height:100%; float:left; border:0; @@ -263,12 +263,12 @@ input[type=number]::-webkit-outer-spin-button { font-weight:bold; } -#article_selection input+input { +#article_selection input+input #article_selection input+span { border-right:0; } #article_autocomplete { - width:90%; + width:80%; padding-left:10px; } @@ -277,14 +277,24 @@ input[type=number]::-webkit-outer-spin-button { text-align:center; } +#article_stock { + width:10%; + line-height:38px; + text-align:center; +} + @media (min-width:1200px) { #article_autocomplete { - width:92% + width:84% } #article_number { width:8%; } + + #article_stock { + width:8%; + } } /* Article data */ @@ -319,6 +329,10 @@ input[type=number]::-webkit-outer-spin-button { padding-left:20px; } +#articles_data .article.low-stock { + background:rgba(236,100,0,0.3); +} + #articles_data .article:hover { background:rgba(200,16,46,0.3); cursor:pointer; @@ -384,6 +398,11 @@ input[type=number]::-webkit-outer-spin-button { text-align:right; } +#basket tr .lowstock { + display:none; + padding-right:15px; +} + #basket tr.ui-selected, #basket tr.ui-selecting { background-color:rgba(200,16,46,0.6); color:#FFF; diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index 31758c36..bab21c12 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -37,9 +37,10 @@ function amountDisplay(amount, is_cof=false, tri='') { 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; - return Math.floor(amount * coef_cof * 10); + return rounding(amount * coef_cof * 10); } function isValidTrigramme(trigramme) { diff --git a/kfet/static/kfet/js/statistic.js b/kfet/static/kfet/js/statistic.js index 258f8cc8..7ab56f1d 100644 --- a/kfet/static/kfet/js/statistic.js +++ b/kfet/static/kfet/js/statistic.js @@ -1,18 +1,197 @@ -var STAT = {}; +(function($){ + window.StatsGroup = function (url, target) { + // a class to properly display statictics + + // url : points to an ObjectResumeStat that lists the options through JSON + // target : element of the DOM where to put the stats + + var self = this; + var element = $(target); + var content = $("
"); + var buttons; -jQuery(document).ready(function() { - // FONCTIONS - // Permet de raffraichir un champ, étant donné : - // thing_url : l'url contenant le contenu - // thing_div : le div où le mettre - // empty_... : le truc à dire si on a un contenu vide - STAT.get_thing = function(thing_url, thing_div, empty_thing_message) { - $.get(thing_url, function(data) { - if(jQuery.trim(data).length==0) { - thing_div.html(empty_thing_message); - } else { - thing_div.html(data); - } - }); - } -}); + function dictToArray (dict, start) { + // converts the dicts returned by JSONResponse to Arrays + // necessary because for..in does not guarantee the order + 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 = $(""); + 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 = $("
", + {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 = $("
", + {class: "btn-group", + role:"group"}); + var btn = $(" -
- {% endfor %} -
-
-
- - diff --git a/kfet/templatetags/kfet_tags.py b/kfet/templatetags/kfet_tags.py index 59840b27..3b1bb639 100644 --- a/kfet/templatetags/kfet_tags.py +++ b/kfet/templatetags/kfet_tags.py @@ -41,5 +41,4 @@ def highlight_clipper(clipper, q): @register.filter() def ukf(balance, is_cof): 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(float(balance) * 10 * grant) + return floor(balance * 10 * grant) diff --git a/kfet/tests.py b/kfet/tests.py index 98558001..5bea7afa 100644 --- a/kfet/tests.py +++ b/kfet/tests.py @@ -2,143 +2,4 @@ from django.test import TestCase -from kfet.models import Account -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 +# Écrire les tests ici diff --git a/kfet/views.py b/kfet/views.py index ac885675..7ba37f55 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1,37 +1,49 @@ # -*- coding: utf-8 -*- 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.views.generic import ListView, DetailView, TemplateView from django.views.generic.list import BaseListView, MultipleObjectTemplateResponseMixin from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin 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.messages.views import SuccessMessageMixin from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.models import User, Permission, Group -from django.http import HttpResponse, JsonResponse, Http404 -from django.forms import modelformset_factory, formset_factory -from django.db import IntegrityError, transaction -from django.db.models import F, Sum, Prefetch, Count, Func +from django.http import JsonResponse, Http404 +from django.forms import formset_factory +from django.db import transaction +from django.db.models import F, Sum, Prefetch, Count from django.db.models.functions import Coalesce from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.decorators import method_decorator from gestioncof.models import CofProfile, Clipper 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, - InventoryArticle, Order, OrderArticle, Operation, OperationGroup, Transfer) -from kfet.forms import * + InventoryArticle, Order, OrderArticle, Operation, OperationGroup, + 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 kfet import consumers from datetime import timedelta +from decimal import Decimal import django_cas_ng -import hashlib import heapq import statistics from .statistic import daynames, monthnames, weeknames, \ @@ -54,6 +66,7 @@ class Home(TemplateView): def dispatch(self, *args, **kwargs): return super(TemplateView, self).dispatch(*args, **kwargs) + @teamkfet_required def login_genericteam(request): # Check si besoin de déconnecter l'utilisateur de CAS @@ -359,6 +372,7 @@ def account_read(request, trigramme): # Account - Update + @login_required def account_update(request, trigramme): account = get_object_or_404(Account, trigramme=trigramme) @@ -369,39 +383,43 @@ def account_update(request, trigramme): raise PermissionDenied if request.user.has_perm('kfet.is_team'): - user_form = UserRestrictTeamForm(instance=account.user) - group_form = UserGroupForm(instance=account.user) + user_form = UserRestrictTeamForm(instance=account.user) + group_form = UserGroupForm(instance=account.user) account_form = AccountForm(instance=account) - cof_form = CofRestrictForm(instance=account.cofprofile) - pwd_form = AccountPwdForm() + cof_form = CofRestrictForm(instance=account.cofprofile) + pwd_form = AccountPwdForm() 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() if hasattr(account, 'negative'): negative_form = AccountNegativeForm(instance=account.negative) else: negative_form = None else: - user_form = UserRestrictForm(instance=account.user) + user_form = UserRestrictForm(instance=account.user) account_form = AccountRestrictForm(instance=account) - cof_form = None - group_form = None + cof_form = None + group_form = None negative_form = None - pwd_form = None + pwd_form = None if request.method == "POST": # Update attempt - success = False + success = False missing_perm = True if request.user.has_perm('kfet.is_team'): account_form = AccountForm(request.POST, instance=account) - cof_form = CofRestrictForm(request.POST, instance=account.cofprofile) - user_form = UserRestrictTeamForm(request.POST, instance=account.user) - group_form = UserGroupForm(request.POST, instance=account.user) - pwd_form = AccountPwdForm(request.POST) + cof_form = CofRestrictForm(request.POST, + instance=account.cofprofile) + user_form = UserRestrictTeamForm(request.POST, + instance=account.user) + group_form = UserGroupForm(request.POST, instance=account.user) + pwd_form = AccountPwdForm(request.POST) 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') 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) # Updating - account_form.save(data = data) + account_form.save(data=data) # Checking perm to update password if (request.user.has_perm('kfet.change_account_password') and pwd_form.is_valid()): pwd = pwd_form.cleaned_data['pwd1'] - pwd_sha256 = hashlib.sha256(pwd.encode('utf-8')).hexdigest() - Account.objects.filter(pk=account.pk).update( - password = pwd_sha256) + account.change_pwd(pwd) + account.save() messages.success(request, 'Mot de passe mis à jour') # Checking perm to manage perms @@ -435,49 +452,66 @@ def account_update(request, trigramme): if account.negative.balance_offset: balance_offset_old = account.negative.balance_offset 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()): - balance_offset_new = negative_form.cleaned_data['balance_offset'] + balance_offset_new = \ + negative_form.cleaned_data['balance_offset'] if not balance_offset_new: 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( - balance = F('balance') + balance_offset_diff) + balance=F('balance') + balance_offset_diff) 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() success = True - messages.success(request, - 'Informations du compte %s mises à jour' % account.trigramme) + messages.success( + request, + 'Informations du compte %s mises à jour' + % account.trigramme) + # Modification de ses propres informations if request.user == account.user: missing_perm = False account.refresh_from_db() user_form = UserRestrictForm(request.POST, instance=account.user) account_form = AccountRestrictForm(request.POST, instance=account) + pwd_form = AccountPwdForm(request.POST) if user_form.is_valid() and account_form.is_valid(): user_form.save() account_form.save() 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: messages.error(request, 'Permission refusée') if success: return redirect('kfet.account.read', account.trigramme) 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", { - 'account' : account, - 'account_form' : account_form, - 'cof_form' : cof_form, - 'user_form' : user_form, - 'group_form' : group_form, + 'account': account, + 'account_form': account_form, + 'cof_form': cof_form, + 'user_form': user_form, + 'group_form': group_form, 'negative_form': negative_form, - 'pwd_form' : pwd_form, + 'pwd_form': pwd_form, }) @permission_required('kfet.manage_perms') @@ -1703,6 +1737,7 @@ def order_create(request, pk): articles = (Article.objects .filter(suppliers=supplier.pk) + .distinct() .select_related('category') .order_by('category__name', 'name')) @@ -1993,6 +2028,14 @@ class JSONResponseMixin(object): 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, SingleObjectTemplateResponseMixin, BaseDetailView): @@ -2023,14 +2066,13 @@ class HybridListView(JSONResponseMixin, return super(HybridListView, self).render_to_response(context) -class ObjectResumeStat(DetailView): +class ObjectResumeStat(JSONDetailView): """ 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 = '' - id_prefix = 'id_a_definir' + id_prefix = '' # nombre de vues à résumer nb_stat = 2 # Le combienième est celui par defaut ? @@ -2059,7 +2101,7 @@ class ObjectResumeStat(DetailView): 'btn': "btn_%s_%d_%d" % (self.id_prefix, object_id, i), - 'url': reverse_lazy(self.stat_urls[i], + 'url': reverse(self.stat_urls[i], kwargs=dict( self.get_object_url_kwargs(), **url_kwargs[i] @@ -2082,7 +2124,7 @@ ID_PREFIX_ACC_BALANCE = "balance_acc" # Un résumé de toutes les vues ArticleStatBalance -# NE REND PAS DE JSON +# REND DU JSON class AccountStatBalanceAll(ObjectResumeStat): model = Account context_object_name = 'account' @@ -2115,9 +2157,9 @@ class AccountStatBalanceAll(ObjectResumeStat): 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` ago (specified to the view as an argument) takes into account the Operations and the Transfers @@ -2126,7 +2168,6 @@ class AccountStatBalance(HybridDetailView): model = Account trigramme_url_kwarg = 'trigramme' nb_date_url_kwargs = 'nb_date' - template_name = 'kfet/account_stat_balance.html' context_object_name = 'account' id_prefix = "" @@ -2233,10 +2274,10 @@ class AccountStatBalance(HybridDetailView): nb_days_string = 'anytime' else: nb_days_string = str(int(nb_days)) - context['changes'] = changes - context['chart_id'] = "%s_%s_%s_days" % (self.id_prefix, - self.object.id, - nb_days_string) + context['charts'] = [ { "color": "rgb(255, 99, 132)", + "label": "Balance", + "values": changes } ] + context['is_time_chart'] = True context['min_date'] = changes[len(changes)-1]['at'] context['max_date'] = changes[0]['at'] # TODO: offset @@ -2282,14 +2323,13 @@ class AccountStatLastAll(ObjectResumeStat): 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 """ model = Account trigramme_url_kwarg = 'trigramme' - template_name = 'kfet/account_stat_last.html' context_object_name = 'account' end_date = timezone.now() id_prefix = "" @@ -2341,10 +2381,9 @@ class AccountStatLast(HybridDetailView): operations = self.sort_operations() for i in operations: nb_ventes[i] = tot_ventes(operations[i]) - context['nb_ventes'] = nb_ventes - # ID unique - context['chart_id'] = "%s_%d" % (self.id_prefix, - self.object.id) + context['charts'] = [ { "color": "rgb(255, 99, 132)", + "label": "NB items achetés", + "values": nb_ventes } ] return context @method_decorator(login_required) @@ -2421,13 +2460,12 @@ class ArticleStatLastAll(ObjectResumeStat): 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 """ model = Article - template_name = 'kfet/article_stat_last.html' context_object_name = 'article' end_date = timezone.now() id_prefix = "" @@ -2488,12 +2526,15 @@ class ArticleStatLast(HybridDetailView): operations[i] .exclude(group__on_acc__trigramme='LIQ') ) - context['nb_ventes'] = nb_ventes - context['nb_accounts'] = nb_accounts - context['nb_liq'] = nb_liq - # ID unique - context['chart_id'] = "%s_%d" % (self.id_prefix, - self.object.id) + context['charts'] = [ { "color": "rgb(255, 99, 132)", + "label": "Toutes consommations", + "values": nb_ventes }, + { "color": "rgb(54, 162, 235)", + "label": "LIQ", + "values": nb_liq }, + { "color": "rgb(255, 205, 86)", + "label": "Comptes K-Fêt", + "values": nb_accounts } ] return context @method_decorator(login_required)