From 971848cb1b44aa0911a062bff374ed6f8028bbce Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Thu, 1 Dec 2016 01:29:28 -0200 Subject: [PATCH 01/90] database lookups --- kfet/views.py | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index f95fb2c6..e8080774 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -18,7 +18,7 @@ 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.db.models import Q, F, Sum, Prefetch, Count, Func from django.db.models.functions import Coalesce from django.utils import timezone from django.utils.crypto import get_random_string @@ -26,7 +26,7 @@ from gestioncof.models import CofProfile, Clipper from kfet.decorators import teamkfet_required from kfet.models import (Account, Checkout, Article, Settings, AccountNegative, CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory, - InventoryArticle, Order, OrderArticle) + InventoryArticle, Order, OrderArticle, TransferGroup, Transfer) from kfet.forms import * from collections import defaultdict from kfet import consumers @@ -1268,35 +1268,63 @@ def history_json(request): # Récupération des paramètres from_date = request.POST.get('from', None) to_date = request.POST.get('to', None) - limit = request.POST.get('limit', None); checkouts = request.POST.getlist('checkouts[]', None) accounts = request.POST.getlist('accounts[]', None) # Construction de la requête (sur les opérations) pour le prefetch - queryset_prefetch = Operation.objects.select_related( + ope_queryset_prefetch = Operation.objects.select_related( 'canceled_by__trigramme', 'addcost_for__trigramme', 'article__name') + ope_prefetch = Prefetch('opes', + queryset = ope_queryset_prefetch) + + transfer_queryset_prefetch = Transfer.objects.select_related( + 'from_acc__trigramme', 'to_acc__trigramme', + 'canceled_by__trigramme') + + if accounts: + transfer_queryset_prefetch = transfer_queryset_prefetch.filter( + Q(from_acc__trigramme__in=accounts) | + Q(to_acc__trigramme__in=accounts)) + + if not request.user.has_perm('kfet.is_team'): + acc = request.user.profile.account_kfet + transfer_queryset_prefetch = transfer_queryset_prefetch.filter( + Q(from_acc=acc) | Q(to_acc=acc)) + + transfer_prefetch = Prefetch('transfers', + queryset=transfer_queryset_prefetch, + to_attr='filtered_transfers') + # Construction de la requête principale opegroups = (OperationGroup.objects - .prefetch_related(Prefetch('opes', queryset = queryset_prefetch)) + .prefetch_related(ope_prefetch) .select_related('on_acc__trigramme', 'valid_by__trigramme') .order_by('at') ) + + transfergroups = ( + TransferGroup.objects + .prefetch_related(transfer_prefetch) + .select_related('valid_by__trigramme') + .order_by('at')) + # Application des filtres if from_date: opegroups = opegroups.filter(at__gte=from_date) + transfergroups = transfergroups.filter(at__gte=from_date) if to_date: opegroups = opegroups.filter(at__lt=to_date) + transfergroups = transfergroups.filter(at__lt=to_date) if checkouts: opegroups = opegroups.filter(checkout_id__in=checkouts) + transfergroups = TransferGroup.objects.none() if accounts: opegroups = opegroups.filter(on_acc_id__in=accounts) # Un non-membre de l'équipe n'a que accès à son historique if not request.user.has_perm('kfet.is_team'): opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) - if limit: - opegroups = opegroups[:limit] # Construction de la réponse From 5a354c61a0905cc379a77fa8ebcfea2545f20c33 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 2 Dec 2016 00:16:40 -0200 Subject: [PATCH 02/90] fetch transfers as well --- kfet/views.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index e8080774..b270a96d 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1331,10 +1331,10 @@ def history_json(request): opegroups_list = [] for opegroup in opegroups: opegroup_dict = { + 'type' : 'opegroup', 'id' : opegroup.id, 'amount' : opegroup.amount, 'at' : opegroup.at, - 'checkout_id': opegroup.checkout_id, 'is_cof' : opegroup.is_cof, 'comment' : opegroup.comment, 'opes' : [], @@ -1363,7 +1363,41 @@ def history_json(request): ope.canceled_by and ope.canceled_by.trigramme or None) opegroup_dict['opes'].append(ope_dict) opegroups_list.append(opegroup_dict) - return JsonResponse({ 'opegroups': opegroups_list }) + + for transfergroup in transfergroups: + if transfergroup.filtered_transfers: + transfergroup_dict = { + 'type': 'transfergroup', + 'id': transfergroup.id, + 'at': transfergroup.at, + 'comment': transfergroup.comment, + 'opes': [], + } + if request.user.has_perm('kfet.is_team'): + transfergroup_dict['valid_by__trigramme'] = ( + transfergroup.valid_by + and transfergroup.valid_by.trigramme + or None) + + for transfer in transfergroup.filtered_transfers: + transfer_dict = { + 'id': transfer.id, + 'amount': transfer.amount, + 'canceled_at': transfer.canceled_at, + 'from_acc': transfer.from_acc.trigramme, + 'to_acc': transfer.to_acc.trigramme, + } + if request.user.has_perm('kfet.is_team'): + transfer_dict['canceled_by__trigramme'] = ( + transfer.canceled_by + and transfer.canceled_by.trigramme + or None) + transfergroup_dict['opes'].append(transfer_dict) + opegroups_list.append(transfergroup_dict) + + opegroups_list.sort(key=lambda group: group['at']) + + return JsonResponse({'opegroups': opegroups_list}) @teamkfet_required def kpsul_articles_data(request): From f747c0c389f54c5b04ec903f0dfc50c5ebebd305 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 2 Dec 2016 00:17:03 -0200 Subject: [PATCH 03/90] print transfers (BROKEN) --- kfet/static/kfet/js/history.js | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 291c106d..a50dbb6b 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -15,10 +15,18 @@ function KHistory(options={}) { var trigramme = opegroup['on_acc_trigramme']; var is_cof = opegroup['is_cof']; - for (var i=0; i', template_opegroup: '
', template_ope: '
', + template_transfer: '
', display_trigramme: true, } From ac0356386a0f233df8d2150168a7cd0e6204a89b Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 9 Dec 2016 00:26:07 -0200 Subject: [PATCH 04/90] add css for transfers --- kfet/static/kfet/css/history.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kfet/static/kfet/css/history.css b/kfet/static/kfet/css/history.css index 976f5782..64b82f96 100644 --- a/kfet/static/kfet/css/history.css +++ b/kfet/static/kfet/css/history.css @@ -40,6 +40,11 @@ width:90px; } +#history .opegroup .info { + text-align:center; + width:145px; +} + #history .opegroup .valid_by { padding-left:20px } @@ -67,6 +72,10 @@ text-align:right; } +#history .ope .glyphicon { + padding-left:15px; +} + #history .ope .infos2 { padding-left:15px; } From 36edc334d44dc5fb08662d58f111538c42e8d8a1 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 9 Dec 2016 00:26:25 -0200 Subject: [PATCH 05/90] add transfer information --- kfet/static/kfet/js/history.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index a50dbb6b..e9486a1e 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -22,6 +22,7 @@ function KHistory(options={}) { $opegroup.after($ope); } } else { + $opegroup.addClass("transfergroup"); for (var i=0; i', template_opegroup: '
', + template_transfergroup: '
', template_ope: '
', - template_transfer: '
', + template_transfer: '
', display_trigramme: true, } From 85af7fe485f2d4cb46ff269aeddffea3f24c8691 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 9 Dec 2016 17:29:40 -0200 Subject: [PATCH 06/90] filter on id --- kfet/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index 2fe284ad..8bca6462 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1282,12 +1282,13 @@ def history_json(request): transfer_queryset_prefetch = Transfer.objects.select_related( 'from_acc__trigramme', 'to_acc__trigramme', + 'from_acc__id', 'to_acc__id', 'canceled_by__trigramme') if accounts: transfer_queryset_prefetch = transfer_queryset_prefetch.filter( - Q(from_acc__trigramme__in=accounts) | - Q(to_acc__trigramme__in=accounts)) + Q(from_acc__id__in=accounts) | + Q(to_acc__id__in=accounts)) if not request.user.has_perm('kfet.is_team'): acc = request.user.profile.account_kfet From 0b61a48c65765d76c0838aeaa33100914444a510 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 10 Dec 2016 23:13:43 -0200 Subject: [PATCH 07/90] fix selection --- kfet/templates/kfet/history.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 091b4f2f..bc12effa 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -142,9 +142,10 @@ $(document).ready(function() { selected: function(e, ui) { $(ui.selected).each(function() { if ($(this).hasClass('opegroup')) { - var opegroup = $(this).data('opegroup'); + var type = $(this).data('type'); + var id = $(this).data('id'); $(this).siblings('.ope').filter(function() { - return $(this).data('opegroup') == opegroup + return $(this).data(type) == id }).addClass('ui-selected'); } }); From 2c2da60e5461defa47cae6e3f394f18088df39cf Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 10 Dec 2016 23:52:26 -0200 Subject: [PATCH 08/90] send data for cancel --- kfet/static/kfet/js/history.js | 10 ++++++---- kfet/templates/kfet/history.html | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index e9486a1e..8efa120a 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -22,7 +22,6 @@ function KHistory(options={}) { $opegroup.after($ope); } } else { - $opegroup.addClass("transfergroup"); for (var i=0; i 0) confirmCancel(opes_to_cancel); From 49bef61e53b46993bab415804231895f8a5706bc Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 14:45:52 -0200 Subject: [PATCH 09/90] filter transfers frop opes --- kfet/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kfet/views.py b/kfet/views.py index 8bca6462..5fbcea3e 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1102,7 +1102,9 @@ def kpsul_cancel_operations(request): # Checking if BAD REQUEST (opes_pk not int or not existing) try: # Set pour virer les doublons - opes_post = set(map(int, filter(None, request.POST.getlist('operations[]', [])))) + opes_post = set(map(lambda s: int(s.split()[1]), + filter(lambda s: s.split()[0] == 'ope', + request.POST.getlist('operations[]', [])))) except ValueError: return JsonResponse(data, status=400) opes_all = ( From 66304359c0f6fe6206f009f25ba6b96bc25acdd6 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 16:22:55 -0200 Subject: [PATCH 10/90] unite cancel_ope and cancel_transfer --- kfet/views.py | 97 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index 5fbcea3e..32889cff 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1097,7 +1097,7 @@ def kpsul_perform_operations(request): @teamkfet_required def kpsul_cancel_operations(request): # Pour la réponse - data = { 'canceled': [], 'warnings': {}, 'errors': {}} + data = {'canceled': {}, 'warnings': {}, 'errors': {}} # Checking if BAD REQUEST (opes_pk not int or not existing) try: @@ -1105,20 +1105,39 @@ def kpsul_cancel_operations(request): opes_post = set(map(lambda s: int(s.split()[1]), filter(lambda s: s.split()[0] == 'ope', request.POST.getlist('operations[]', [])))) + transfers_post = \ + set(map(lambda s: int(s.split()[1]), + filter(lambda s: s.split()[0] == 'transfer', + request.POST.getlist('operations[]', [])))) except ValueError: return JsonResponse(data, status=400) + opes_all = ( Operation.objects .select_related('group', 'group__on_acc', 'group__on_acc__negative') .filter(pk__in=opes_post)) opes_pk = [ ope.pk for ope in opes_all ] opes_notexisting = [ ope for ope in opes_post if ope not in opes_pk ] - if opes_notexisting: - data['errors']['opes_notexisting'] = opes_notexisting + + transfers_all = ( + Transfer.objects + .select_related('group', 'from_acc', 'from_acc__negative', + 'to_acc', 'to_acc__negative') + .filter(pk__in=transfers_post)) + transfers_pk = [transfer.pk for transfer in transfers_all] + transfers_notexisting = [transfer for transfer in transfers_post + if transfer not in transfers_pk] + + if transfers_notexisting or opes_notexisting: + if transfers_notexisting: + data['errors']['transfers_notexisting'] = transfers_notexisting + if opes_notexisting: + data['errors']['opes_notexisting'] = opes_notexisting return JsonResponse(data, status=400) - opes_already_canceled = [] # Déjà annulée - opes = [] # Pas déjà annulée + already_canceled = {} # Opération/Transfert déjà annulé + opes = [] # Pas déjà annulée + transfers = [] required_perms = set() stop_all = False cancel_duration = Settings.CANCEL_DURATION() @@ -1129,7 +1148,7 @@ def kpsul_cancel_operations(request): for ope in opes_all: if ope.canceled_at: # Opération déjà annulée, va pour un warning en Response - opes_already_canceled.append(ope.pk) + already_canceled['opes'].append(ope.pk) else: opes.append(ope.pk) # Si opé il y a plus de CANCEL_DURATION, permission requise @@ -1163,7 +1182,7 @@ def kpsul_cancel_operations(request): if not last_statement or last_statement.at < ope.group.at: if ope.type == Operation.PURCHASE: if ope.group.on_acc.is_cash: - to_checkouts_balances[ope.group.checkout] -= - ope.amount + to_checkouts_balances[ope.group.checkout] -= -ope.amount else: to_checkouts_balances[ope.group.checkout] -= ope.amount @@ -1176,22 +1195,38 @@ def kpsul_cancel_operations(request): # est recalculé automatiquement if ope.article and ope.article_nb: last_stock = (InventoryArticle.objects - .select_related('inventory') - .filter(article=ope.article) - .order_by('inventory__at') - .last()) + .select_related('inventory') + .filter(article=ope.article) + .order_by('inventory__at') + .last()) if not last_stock or last_stock.inventory.at < ope.group.at: to_articles_stocks[ope.article] += ope.article_nb - if not opes: - data['warnings']['already_canceled'] = opes_already_canceled + for transfer in transfers_all: + if transfer.canceled_at: + # Transfert déjà annulé, va pour un warning en Response + already_canceled['transfers'].append(transfer.pk) + else: + transfers.append(transfer.pk) + # Si transfer il y a plus de CANCEL_DURATION, permission requise + if transfer.group.at + cancel_duration < timezone.now(): + required_perms.add('kfet.cancel_old_operations') + + # Calcul de toutes modifs à faire en cas de validation + + # Pour les balances de comptes + to_accounts_balances[transfer.from_acc] += transfer.amount + to_accounts_balances[transfer.to_acc] += -transfer.amount + + if not opes and not transfers: + data['warnings']['already_canceled'] = already_canceled return JsonResponse(data) negative_accounts = [] # Checking permissions or stop for account in to_accounts_balances: (perms, stop) = account.perms_to_perform_operation( - amount = to_accounts_balances[account]) + amount=to_accounts_balances[account]) required_perms |= perms stop_all = stop_all or stop if stop: @@ -1211,25 +1246,31 @@ def kpsul_cancel_operations(request): with transaction.atomic(): (Operation.objects.filter(pk__in=opes) .update(canceled_by=canceled_by, canceled_at=canceled_at)) + + (Transfer.objects.filter(pk__in=transfers) + .update(canceled_by=canceled_by, canceled_at=canceled_at)) + for account in to_accounts_balances: Account.objects.filter(pk=account.pk).update( - balance = F('balance') + to_accounts_balances[account]) + balance=F('balance') + to_accounts_balances[account]) for checkout in to_checkouts_balances: Checkout.objects.filter(pk=checkout.pk).update( - balance = F('balance') + to_checkouts_balances[checkout]) + balance=F('balance') + to_checkouts_balances[checkout]) for group in to_groups_amounts: OperationGroup.objects.filter(pk=group.pk).update( - amount = F('amount') + to_groups_amounts[group]) + amount=F('amount') + to_groups_amounts[group]) for article in to_articles_stocks: Article.objects.filter(pk=article.pk).update( - stock = F('stock') + to_articles_stocks[article]) + stock=F('stock') + to_articles_stocks[article]) # Websocket data - websocket_data = { 'opegroups': [], 'opes': [], 'checkouts': [], 'articles': [] } + websocket_data = {'opegroups': [], 'opes': [], + 'checkouts': [], 'articles': []} # Need refresh from db cause we used update on querysets - opegroups_pk = [ opegroup.pk for opegroup in to_groups_amounts ] + opegroups_pk = [opegroup.pk for opegroup in to_groups_amounts] opegroups = (OperationGroup.objects - .values('id','amount','is_cof').filter(pk__in=opegroups_pk)) + .values('id', 'amount', 'is_cof') + .filter(pk__in=opegroups_pk)) for opegroup in opegroups: websocket_data['opegroups'].append({ 'cancellation': True, @@ -1246,15 +1287,16 @@ def kpsul_cancel_operations(request): 'canceled_at': canceled_at, }) # Need refresh from db cause we used update on querysets - checkouts_pk = [ checkout.pk for checkout in to_checkouts_balances] + checkouts_pk = [checkout.pk for checkout in to_checkouts_balances] checkouts = (Checkout.objects - .values('id', 'balance').filter(pk__in=checkouts_pk)) + .values('id', 'balance') + .filter(pk__in=checkouts_pk)) for checkout in checkouts: websocket_data['checkouts'].append({ 'id': checkout['id'], 'balance': checkout['balance']}) # Need refresh from db cause we used update on querysets - articles_pk = [ article.pk for articles in to_articles_stocks] + articles_pk = [article.pk for articles in to_articles_stocks] articles = Article.objects.values('id', 'stock').filter(pk__in=articles_pk) for article in articles: websocket_data['articles'].append({ @@ -1262,9 +1304,10 @@ def kpsul_cancel_operations(request): 'stock': article['stock']}) consumers.KPsul.group_send('kfet.kpsul', websocket_data) - data['canceled'] = opes - if opes_already_canceled: - data['warnings']['already_canceled'] = opes_already_canceled + data['canceled']['opes'] = opes + data['canceled']['transfers'] = transfers + if already_canceled: + data['warnings']['already_canceled'] = already_canceled return JsonResponse(data) @login_required From 1dbbad38b90144adb3e64092ac46419de253d854 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 21:00:42 -0200 Subject: [PATCH 11/90] transfer cancellation html --- kfet/static/kfet/js/history.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 8efa120a..7048d121 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -79,6 +79,9 @@ function KHistory(options={}) { .find('.infos1').text(transfer['from_acc']).end() .find('.infos2').text(transfer['to_acc']).end(); + if (transfer['canceled_at']) + this.cancelOpe(transfer, $transfer_html); + return $transfer_html ; } @@ -151,9 +154,9 @@ function KHistory(options={}) { }); } - this.findOpe = function(id) { + this.findOpe = function(id, type) { return this.$container.find('.ope').filter(function() { - return $(this).data('ope') == id + return ($(this).data('id') == id && $(this).data('type') == type) }); } @@ -173,6 +176,6 @@ KHistory.default_options = { template_opegroup: '
', template_transfergroup: '
', template_ope: '
', - template_transfer: '
', + template_transfer: '
', display_trigramme: true, } From 3f35dc2c06b464d5aae914912911c4295fc9cb0b Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 23:21:36 -0200 Subject: [PATCH 12/90] unite transfer history --- kfet/static/kfet/js/history.js | 1 + kfet/templates/kfet/transfers.html | 132 +++++++++++++++++------------ 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 7048d121..f870b4cc 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -129,6 +129,7 @@ function KHistory(options={}) { if (!this.display_trigramme) $opegroup_html.find('.trigramme').remove(); + $opegroup_html.find('.info').remove(); if (opegroup['valid_by__trigramme']) $opegroup_html.find('.valid_by').text('Par '+opegroup['valid_by__trigramme']); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index cbdf0fe3..0ba0c85e 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -6,7 +6,11 @@ + + + + {% endblock %} {% block title %}Transferts{% endblock %} @@ -31,22 +35,7 @@

Liste des transferts

-
- {% for transfergroup in transfergroups %} -
- {{ transfergroup.at }} - {{ transfergroup.valid_by.trigramme }} - {{ transfergroup.comment }} -
- {% for transfer in transfergroup.transfers.all %} -
- {{ transfer.amount }} € - {{ transfer.from_acc.trigramme }} - - {{ transfer.to_acc.trigramme }} -
- {% endfor %} - {% endfor %} +
@@ -70,15 +59,80 @@ $(document).ready(function() { }); } + khistory = new KHistory({ + display_trigramme: false, + }); + + function getHistory() { + var data = {'transfersonly': true}; - function cancelTransfers(transfers_array, password = '') { - if (lock == 1) - return false - lock = 1; - var data = { 'transfers' : transfers_array } $.ajax({ dataType: "json", - url : "{% url 'kfet.transfers.cancel' %}", + url : "{% url 'kfet.history.json' %}", + method : "POST", + data : data, + }) + .done(function(data) { + for (var i=0; i 0) + confirmCancel(opes_to_cancel); + } + }); + + function confirmCancel(opes_to_cancel) { + var nb = opes_to_cancel.length; + var content = nb+' opération'.pluralize(nb) + +' va'.pluralize(nb, ' vont') + + ' être' + + ' annulée'.pluralize(nb); + $.confirm({ + title: 'Confirmation', + content: content, + backgroundDismiss: true, + animation: 'top', + closeAnimation: 'bottom', + keyboardEnabled: true, + confirm: function() { + cancelOperations(opes_to_cancel); + } + }); + } + + function cancelOperations(opes_array, password = '') { + if (lock == 1) + return false + lock = 1 ; + var data = { 'operations' : opes_array } + $.ajax({ + dataType: "json", + url : "{% url 'kfet.kpsul.cancel_operations' %}", method : "POST", data : data, beforeSend: function ($xhr) { @@ -89,11 +143,7 @@ $(document).ready(function() { }) .done(function(data) { - for (var i=0; i 0) - cancelTransfers(transfers_to_cancel); - } - }); + getHistory(); }); From a9e1cd01db58e1757b21aa900e709c15e109caba Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 23:22:14 -0200 Subject: [PATCH 13/90] add transfersonly option --- kfet/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kfet/views.py b/kfet/views.py index 32889cff..e07c962b 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1317,6 +1317,7 @@ def history_json(request): to_date = request.POST.get('to', None) checkouts = request.POST.getlist('checkouts[]', None) accounts = request.POST.getlist('accounts[]', None) + transfers_only = request.POST.get('transfersonly', None) # Construction de la requête (sur les opérations) pour le prefetch ope_queryset_prefetch = Operation.objects.select_related( @@ -1368,6 +1369,8 @@ def history_json(request): if checkouts: opegroups = opegroups.filter(checkout_id__in=checkouts) transfergroups = TransferGroup.objects.none() + if transfers_only: + opegroups = OperationGroup.objects.none() if accounts: opegroups = opegroups.filter(on_acc_id__in=accounts) # Un non-membre de l'équipe n'a que accès à son historique From ee54b366961e873bfb7ae50b1339a773c1d82629 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 23:22:59 -0200 Subject: [PATCH 14/90] minor imprevements to history --- kfet/templates/kfet/history.html | 55 +++++++------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 01aaa359..97a0e413 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -62,6 +62,8 @@ $(document).ready(function() { settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})} + lock = 0 ; + khistory = new KHistory(); var $from_date = $('#from_date'); @@ -166,7 +168,10 @@ $(document).ready(function() { function confirmCancel(opes_to_cancel) { var nb = opes_to_cancel.length; - var content = nb+" opérations vont être annulées"; + var content = nb+' opération'.pluralize(nb) + +' va'.pluralize(nb, ' vont') + + ' être' + + ' annulée'.pluralize(nb); $.confirm({ title: 'Confirmation', content: content, @@ -180,50 +185,10 @@ $(document).ready(function() { }); } - function requestAuth(data, callback) { - var content = getErrorsHtml(data); - content += '', - $.confirm({ - title: 'Authentification requise', - content: content, - backgroundDismiss: true, - animation:'top', - closeAnimation:'bottom', - keyboardEnabled: true, - confirm: function() { - var password = this.$content.find('input').val(); - callback(password); - }, - onOpen: function() { - var that = this; - this.$content.find('input').on('keypress', function(e) { - if (e.keyCode == 13) - that.$confirmButton.click(); - }); - }, - }); - } - - function getErrorsHtml(data) { - var content = ''; - if ('missing_perms' in data['errors']) { - content += 'Permissions manquantes'; - content += '
    '; - for (var i=0; i'; - content += '
'; - } - if ('negative' in data['errors']) { - var url_base = "{% url 'kfet.account.update' LIQ}"; - url_base = base_url(0, url_base.length-8); - for (var i=0; iAutorisation de négatif requise pour '+data['errors']['negative'][i]+''; - } - } - return content; - } - function cancelOperations(opes_array, password = '') { + if (lock == 1) + return false + lock = 1 ; var data = { 'operations' : opes_array } $.ajax({ dataType: "json", @@ -239,6 +204,7 @@ $(document).ready(function() { }) .done(function(data) { khistory.$container.find('.ui-selected').removeClass('ui-selected'); + lock = 0 ; }) .fail(function($xhr) { var data = $xhr.responseJSON; @@ -252,6 +218,7 @@ $(document).ready(function() { displayErrors(getErrorsHtml(data)); break; } + lock = 0 ; }); } From e52c44580f4cf24c09c8ac6338ad60dd9a3f6ae8 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 23:23:12 -0200 Subject: [PATCH 15/90] pluralize function --- kfet/static/kfet/js/kfet.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index e3e5a6d9..6bb43470 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -138,3 +138,8 @@ function requestAuth(data, callback, focus_next = null) { }); } + +String.prototype.pluralize = function(count, irreg_plural = false) { + plural = irreg_plural ? irreg_plural : this + 's' ; + return (count==1 ? this : plural) ; +} From f06a732da5fdfde868b4b185c0822cdaff951a0d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 11 Dec 2016 23:29:05 -0200 Subject: [PATCH 16/90] remove unnecessary function --- kfet/urls.py | 2 -- kfet/views.py | 98 --------------------------------------------------- 2 files changed, 100 deletions(-) diff --git a/kfet/urls.py b/kfet/urls.py index 9b9ebf21..e0bacf9a 100644 --- a/kfet/urls.py +++ b/kfet/urls.py @@ -170,8 +170,6 @@ urlpatterns = [ name = 'kfet.transfers.create'), url(r'^transfers/perform$', views.perform_transfers, name = 'kfet.transfers.perform'), - url(r'^transfers/cancel$', views.cancel_transfers, - name = 'kfet.transfers.cancel'), # ----- # Inventories urls diff --git a/kfet/views.py b/kfet/views.py index e07c962b..b5ade6ae 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1594,104 +1594,6 @@ def perform_transfers(request): return JsonResponse(data) -@teamkfet_required -def cancel_transfers(request): - # Pour la réponse - data = { 'canceled': [], 'warnings': {}, 'errors': {}} - - # Checking if BAD REQUEST (transfers_pk not int or not existing) - try: - # Set pour virer les doublons - transfers_post = set(map(int, filter(None, request.POST.getlist('transfers[]', [])))) - except ValueError: - return JsonResponse(data, status=400) - transfers_all = ( - Transfer.objects - .select_related('group', 'from_acc', 'from_acc__negative', - 'to_acc', 'to_acc__negative') - .filter(pk__in=transfers_post)) - transfers_pk = [ transfer.pk for transfer in transfers_all ] - transfers_notexisting = [ transfer for transfer in transfers_post - if transfer not in transfers_pk ] - if transfers_notexisting: - data['errors']['transfers_notexisting'] = transfers_notexisting - return JsonResponse(data, status=400) - - transfers_already_canceled = [] # Déjà annulée - transfers = [] # Pas déjà annulée - required_perms = set() - stop_all = False - cancel_duration = Settings.CANCEL_DURATION() - to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes - for transfer in transfers_all: - if transfer.canceled_at: - # Transfert déjà annulé, va pour un warning en Response - transfers_already_canceled.append(transfer.pk) - else: - transfers.append(transfer.pk) - # Si transfer il y a plus de CANCEL_DURATION, permission requise - if transfer.group.at + cancel_duration < timezone.now(): - required_perms.add('kfet.cancel_old_operations') - - # Calcul de toutes modifs à faire en cas de validation - - # Pour les balances de comptes - to_accounts_balances[transfer.from_acc] += transfer.amount - to_accounts_balances[transfer.to_acc] += -transfer.amount - - if not transfers: - data['warnings']['already_canceled'] = transfers_already_canceled - return JsonResponse(data) - - negative_accounts = [] - # Checking permissions or stop - for account in to_accounts_balances: - (perms, stop) = account.perms_to_perform_operation( - amount = to_accounts_balances[account]) - required_perms |= perms - stop_all = stop_all or stop - if stop: - negative_accounts.append(account.trigramme) - - print(required_perms) - print(request.user.get_all_permissions()) - - if stop_all or not request.user.has_perms(required_perms): - missing_perms = get_missing_perms(required_perms, request.user) - if missing_perms: - data['errors']['missing_perms'] = missing_perms - if stop_all: - data['errors']['negative'] = negative_accounts - return JsonResponse(data, status=403) - - canceled_by = required_perms and request.user.profile.account_kfet or None - canceled_at = timezone.now() - - with transaction.atomic(): - (Transfer.objects.filter(pk__in=transfers) - .update(canceled_by=canceled_by, canceled_at=canceled_at)) - - for account in to_accounts_balances: - Account.objects.filter(pk=account.pk).update( - balance = F('balance') + to_accounts_balances[account]) - account.refresh_from_db() - if account.balance < 0: - if hasattr(account, 'negative'): - if not account.negative.start: - account.negative.start = timezone.now() - account.negative.save() - else: - negative = AccountNegative( - account = account, start = timezone.now()) - negative.save() - elif (hasattr(account, 'negative') - and not account.negative.balance_offset): - account.negative.delete() - - data['canceled'] = transfers - if transfers_already_canceled: - data['warnings']['already_canceled'] = transfers_already_canceled - return JsonResponse(data) class InventoryList(ListView): queryset = (Inventory.objects From f8aa67721ceae6bc39b532c038410a9e658ade51 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 5 Feb 2017 02:19:20 -0200 Subject: [PATCH 17/90] fix socket update --- kfet/static/kfet/css/history.css | 2 +- kfet/static/kfet/js/history.js | 10 +++++----- kfet/templates/kfet/kpsul.html | 2 +- kfet/views.py | 11 +++++++++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/kfet/static/kfet/css/history.css b/kfet/static/kfet/css/history.css index 64b82f96..fc6a46ad 100644 --- a/kfet/static/kfet/css/history.css +++ b/kfet/static/kfet/css/history.css @@ -40,7 +40,7 @@ width:90px; } -#history .opegroup .info { +#history .opegroup .infos { text-align:center; width:145px; } diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index f870b4cc..bde8b149 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -58,7 +58,7 @@ function KHistory(options={}) { var addcost_for = ope['addcost_for__trigramme']; if (addcost_for) { var addcost_amount = parseFloat(ope['addcost_amount']); - $ope_html.find('.addcost').text('('+amountDisplay(addcost_amount, is_cof)+'UKF pour '+addcost_for+')'); + $ope_html.find('.addcost').text('('+amountDisplay(addcost_amount, is_cof)+' UKF pour '+addcost_for+')'); } if (ope['canceled_at']) @@ -88,7 +88,7 @@ function KHistory(options={}) { this.cancelOpe = function(ope, $ope = null) { if (!$ope) - $ope = this.findOpe(ope['id']); + $ope = this.findOpe(ope['id'], ope['type']); var cancel = 'Annulé'; var canceled_at = dateUTCToParis(ope['canceled_at']); @@ -123,7 +123,7 @@ function KHistory(options={}) { .data('id', opegroup['id']) .find('.time').text(at).end() .find('.trigramme').text(trigramme).end() - .find('.info').text("Transferts").end() + .find('.infos').text("Transferts").end() .find('.amount').text(amount).end() .find('.comment').text(comment).end(); @@ -155,7 +155,7 @@ function KHistory(options={}) { }); } - this.findOpe = function(id, type) { + this.findOpe = function(id, type='ope') { return this.$container.find('.ope').filter(function() { return ($(this).data('id') == id && $(this).data('type') == type) }); @@ -175,7 +175,7 @@ KHistory.default_options = { container: '#history', template_day: '
', template_opegroup: '
', - template_transfergroup: '
', + template_transfergroup: '
', template_ope: '
', template_transfer: '
', display_trigramme: true, diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index fb59060b..a27d9069 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -1211,7 +1211,7 @@ $(document).ready(function() { // DEL (Suppr) var opes_to_cancel = []; khistory.$container.find('.ope.ui-selected').each(function () { - opes_to_cancel.push($(this).data('ope')); + opes_to_cancel.push($(this).data('type')+' '+$(this).data('id')); }); if (opes_to_cancel.length > 0) cancelOperations(opes_to_cancel); diff --git a/kfet/views.py b/kfet/views.py index b5ade6ae..5cec6eff 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1053,6 +1053,7 @@ def kpsul_perform_operations(request): websocket_data = {} websocket_data['opegroups'] = [{ 'add': True, + 'type': 'opegroup', 'id': operationgroup.pk, 'amount': operationgroup.amount, 'checkout__name': operationgroup.checkout.name, @@ -1282,10 +1283,20 @@ def kpsul_cancel_operations(request): for ope in opes: websocket_data['opes'].append({ 'cancellation': True, + 'type': 'ope', 'id': ope, 'canceled_by__trigramme': canceled_by__trigramme, 'canceled_at': canceled_at, }) + for ope in transfers: + websocket_data['opes'].append({ + 'cancellation': True, + 'type': 'transfer', + 'id': ope, + 'canceled_by__trigramme': canceled_by__trigramme, + 'canceled_at': canceled_at, + }) + # Need refresh from db cause we used update on querysets checkouts_pk = [checkout.pk for checkout in to_checkouts_balances] checkouts = (Checkout.objects From 1ea334341b3bbda6e351d1d31dd8d014f91f6199 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 5 Feb 2017 03:04:41 -0200 Subject: [PATCH 18/90] ws update for history and transfer pages --- kfet/templates/kfet/history.html | 30 ++++++++++++++++++++++++++++++ kfet/templates/kfet/transfers.html | 22 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 97a0e413..eacd6e73 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -7,6 +7,7 @@ + @@ -223,6 +224,35 @@ $(document).ready(function() { }); } + // ----- + // Synchronization + // ----- + + + websocket_msg_default = {'opegroups':[],'opes':[]} + + var websocket_protocol = window.location.protocol == 'https:' ? 'wss' : 'ws'; + var location_host = window.location.host; + var location_url = window.location.pathname.startsWith('/gestion/') ? location_host + '/gestion' : location_host; + socket = new ReconnectingWebSocket(websocket_protocol+"://" + location_url + "/ws/k-fet/k-psul/"); + socket.onmessage = function(e) { + data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); + + for (var i=0; i + @@ -162,6 +163,27 @@ $(document).ready(function() { }); } + // ----- + // Synchronization + // ----- + + + websocket_msg_default = {'opes':[]} + + var websocket_protocol = window.location.protocol == 'https:' ? 'wss' : 'ws'; + var location_host = window.location.host; + var location_url = window.location.pathname.startsWith('/gestion/') ? location_host + '/gestion' : location_host; + socket = new ReconnectingWebSocket(websocket_protocol+"://" + location_url + "/ws/k-fet/k-psul/"); + socket.onmessage = function(e) { + data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); + + for (var i=0; i Date: Sun, 5 Feb 2017 03:26:15 -0200 Subject: [PATCH 19/90] ws for transfers --- kfet/views.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/kfet/views.py b/kfet/views.py index 5cec6eff..aa3faa2c 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1603,6 +1603,29 @@ def perform_transfers(request): transfer.save() data['transfers'].append(transfer.pk) + # Websocket data + websocket_data = {} + websocket_data['opegroups'] = [{ + 'add': True, + 'type': 'transfergroup', + 'id': transfergroup.pk, + 'at': transfergroup.at, + 'comment': transfergroup.comment, + 'valid_by__trigramme': ( transfergroup.valid_by and + transfergroup.valid_by.trigramme or None), + 'opes': [], + }] + for transfer in transfers: + ope_data = { + 'id': transfer.pk, + 'amount': transfer.amount, + 'from_acc': transfer.from_acc.trigramme, + 'to_acc': transfer.to_acc.trigramme, + 'canceled_by__trigramme': None, 'canceled_at': None, + } + websocket_data['opegroups'][0]['opes'].append(ope_data) + + consumers.KPsul.group_send('kfet.kpsul', websocket_data) return JsonResponse(data) From b404c989ffdf500b761773758d9e861514a46a81 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 5 Feb 2017 03:42:02 -0200 Subject: [PATCH 20/90] pep8 --- kfet/views.py | 118 +++++++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index aa3faa2c..36e45487 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1095,6 +1095,7 @@ def kpsul_perform_operations(request): consumers.KPsul.group_send('kfet.kpsul', websocket_data) return JsonResponse(data) + @teamkfet_required def kpsul_cancel_operations(request): # Pour la réponse @@ -1117,8 +1118,8 @@ def kpsul_cancel_operations(request): Operation.objects .select_related('group', 'group__on_acc', 'group__on_acc__negative') .filter(pk__in=opes_post)) - opes_pk = [ ope.pk for ope in opes_all ] - opes_notexisting = [ ope for ope in opes_post if ope not in opes_pk ] + opes_pk = [ope.pk for ope in opes_all] + opes_notexisting = [ope for ope in opes_post if ope not in opes_pk] transfers_all = ( Transfer.objects @@ -1127,7 +1128,7 @@ def kpsul_cancel_operations(request): .filter(pk__in=transfers_post)) transfers_pk = [transfer.pk for transfer in transfers_all] transfers_notexisting = [transfer for transfer in transfers_post - if transfer not in transfers_pk] + if transfer not in transfers_pk] if transfers_notexisting or opes_notexisting: if transfers_notexisting: @@ -1140,12 +1141,16 @@ def kpsul_cancel_operations(request): opes = [] # Pas déjà annulée transfers = [] required_perms = set() - stop_all = False + stop_all = False cancel_duration = Settings.CANCEL_DURATION() - to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes - to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé - to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses - to_articles_stocks = defaultdict(lambda:0) # ------ sur les stocks d'articles + # Modifs à faire sur les balances des comptes + to_accounts_balances = defaultdict(lambda: 0) + # ------ sur les montants des groupes d'opé + to_groups_amounts = defaultdict(lambda: 0) + # ------ sur les balances de caisses + to_checkouts_balances = defaultdict(lambda: 0) + # ------ sur les stocks d'articles + to_articles_stocks = defaultdict(lambda: 0) for ope in opes_all: if ope.canceled_at: # Opération déjà annulée, va pour un warning en Response @@ -1176,10 +1181,11 @@ def kpsul_cancel_operations(request): # par `.save()`, amount_error est recalculé automatiquement, # ce qui n'est pas le cas en faisant un update sur queryset # TODO ? : Maj les balance_old de relevés pour modifier l'erreur - last_statement = (CheckoutStatement.objects - .filter(checkout=ope.group.checkout) - .order_by('at') - .last()) + last_statement = \ + (CheckoutStatement.objects + .filter(checkout=ope.group.checkout) + .order_by('at') + .last()) if not last_statement or last_statement.at < ope.group.at: if ope.type == Operation.PURCHASE: if ope.group.on_acc.is_cash: @@ -1321,13 +1327,14 @@ def kpsul_cancel_operations(request): data['warnings']['already_canceled'] = already_canceled return JsonResponse(data) + @login_required def history_json(request): # Récupération des paramètres from_date = request.POST.get('from', None) - to_date = request.POST.get('to', None) + to_date = request.POST.get('to', None) checkouts = request.POST.getlist('checkouts[]', None) - accounts = request.POST.getlist('accounts[]', None) + accounts = request.POST.getlist('accounts[]', None) transfers_only = request.POST.get('transfersonly', None) # Construction de la requête (sur les opérations) pour le prefetch @@ -1335,7 +1342,7 @@ def history_json(request): 'canceled_by__trigramme', 'addcost_for__trigramme', 'article__name') ope_prefetch = Prefetch('opes', - queryset = ope_queryset_prefetch) + queryset=ope_queryset_prefetch) transfer_queryset_prefetch = Transfer.objects.select_related( 'from_acc__trigramme', 'to_acc__trigramme', @@ -1356,19 +1363,21 @@ def history_json(request): queryset=transfer_queryset_prefetch, to_attr='filtered_transfers') - # Construction de la requête principale - opegroups = (OperationGroup.objects - .prefetch_related(ope_prefetch) - .select_related('on_acc__trigramme', 'valid_by__trigramme') - .order_by('at') + opegroups = ( + OperationGroup.objects + .prefetch_related(ope_prefetch) + .select_related('on_acc__trigramme', + 'valid_by__trigramme') + .order_by('at') ) transfergroups = ( TransferGroup.objects .prefetch_related(transfer_prefetch) .select_related('valid_by__trigramme') - .order_by('at')) + .order_by('at') + ) # Application des filtres if from_date: @@ -1388,18 +1397,17 @@ def history_json(request): if not request.user.has_perm('kfet.is_team'): opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) - # Construction de la réponse opegroups_list = [] for opegroup in opegroups: opegroup_dict = { - 'type' : 'opegroup', - 'id' : opegroup.id, - 'amount' : opegroup.amount, - 'at' : opegroup.at, - 'is_cof' : opegroup.is_cof, - 'comment' : opegroup.comment, - 'opes' : [], + 'type': 'opegroup', + 'id': opegroup.id, + 'amount': opegroup.amount, + 'at': opegroup.at, + 'is_cof': opegroup.is_cof, + 'comment': opegroup.comment, + 'opes': [], 'on_acc__trigramme': opegroup.on_acc and opegroup.on_acc.trigramme or None, } @@ -1408,13 +1416,13 @@ def history_json(request): opegroup.valid_by and opegroup.valid_by.trigramme or None) for ope in opegroup.opes.all(): ope_dict = { - 'id' : ope.id, - 'type' : ope.type, - 'amount' : ope.amount, - 'article_nb' : ope.article_nb, - 'is_checkout' : ope.is_checkout, + 'id': ope.id, + 'type': ope.type, + 'amount': ope.amount, + 'article_nb': ope.article_nb, + 'is_checkout': ope.is_checkout, 'addcost_amount': ope.addcost_amount, - 'canceled_at' : ope.canceled_at, + 'canceled_at': ope.canceled_at, 'article__name': ope.article and ope.article.name or None, 'addcost_for__trigramme': @@ -1437,9 +1445,9 @@ def history_json(request): } if request.user.has_perm('kfet.is_team'): transfergroup_dict['valid_by__trigramme'] = ( - transfergroup.valid_by - and transfergroup.valid_by.trigramme - or None) + transfergroup.valid_by and + transfergroup.valid_by.trigramme or + None) for transfer in transfergroup.filtered_transfers: transfer_dict = { @@ -1451,9 +1459,9 @@ def history_json(request): } if request.user.has_perm('kfet.is_team'): transfer_dict['canceled_by__trigramme'] = ( - transfer.canceled_by - and transfer.canceled_by.trigramme - or None) + transfer.canceled_by and + transfer.canceled_by.trigramme or + None) transfergroup_dict['opes'].append(transfer_dict) opegroups_list.append(transfergroup_dict) @@ -1527,20 +1535,24 @@ def transfers_create(request): return render(request, 'kfet/transfers_create.html', { 'transfer_formset': transfer_formset }) + @teamkfet_required def perform_transfers(request): - data = { 'errors': {}, 'transfers': [], 'transfergroup': 0 } + data = {'errors': {}, 'transfers': [], 'transfergroup': 0} # Checking transfer_formset transfer_formset = TransferFormSet(request.POST) if not transfer_formset.is_valid(): - return JsonResponse({ 'errors': list(transfer_formset.errors)}, status=400) + return JsonResponse({'errors': list(transfer_formset.errors)}, + status=400) - transfers = transfer_formset.save(commit = False) + transfers = transfer_formset.save(commit=False) # Initializing vars - required_perms = set(['kfet.add_transfer']) # Required perms to perform all transfers - to_accounts_balances = defaultdict(lambda:0) # For balances of accounts + # Required perms to perform all transfers + required_perms = set(['kfet.add_transfer']) + # For balances of accounts + to_accounts_balances = defaultdict(lambda: 0) for transfer in transfers: to_accounts_balances[transfer.from_acc] -= transfer.amount @@ -1552,7 +1564,7 @@ def perform_transfers(request): # Checking if ok on all accounts for account in to_accounts_balances: (perms, stop) = account.perms_to_perform_operation( - amount = to_accounts_balances[account]) + amount=to_accounts_balances[account]) required_perms |= perms stop_all = stop_all or stop if stop: @@ -1578,7 +1590,7 @@ def perform_transfers(request): # Updating balances accounts for account in to_accounts_balances: Account.objects.filter(pk=account.pk).update( - balance = F('balance') + to_accounts_balances[account]) + balance=F('balance') + to_accounts_balances[account]) account.refresh_from_db() if account.balance < 0: if hasattr(account, 'negative'): @@ -1587,10 +1599,10 @@ def perform_transfers(request): account.negative.save() else: negative = AccountNegative( - account = account, start = timezone.now()) + account=account, start=timezone.now()) negative.save() - elif (hasattr(account, 'negative') - and not account.negative.balance_offset): + elif (hasattr(account, 'negative') and + not account.negative.balance_offset): account.negative.delete() # Saving transfer group @@ -1611,8 +1623,8 @@ def perform_transfers(request): 'id': transfergroup.pk, 'at': transfergroup.at, 'comment': transfergroup.comment, - 'valid_by__trigramme': ( transfergroup.valid_by and - transfergroup.valid_by.trigramme or None), + 'valid_by__trigramme': (transfergroup.valid_by and + transfergroup.valid_by.trigramme or None), 'opes': [], }] for transfer in transfers: From db94a8904c35397b240cbf513bd90e2dde835ff2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Tue, 7 Feb 2017 18:45:57 -0200 Subject: [PATCH 21/90] remove addition of new opegroups --- kfet/templates/kfet/history.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index eacd6e73..4a158e9e 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -239,10 +239,7 @@ $(document).ready(function() { data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); for (var i=0; i Date: Sat, 11 Feb 2017 21:38:07 -0200 Subject: [PATCH 22/90] use switch + move text --- kfet/static/kfet/js/history.js | 51 +++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index bde8b149..47ae038b 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -15,18 +15,22 @@ function KHistory(options={}) { var trigramme = opegroup['on_acc_trigramme']; var is_cof = opegroup['is_cof']; - if (opegroup['type'] == 'opegroup') { - for (var i=0; i Date: Sat, 11 Feb 2017 21:41:09 -0200 Subject: [PATCH 23/90] better pluralize --- kfet/static/kfet/js/kfet.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index e54d0e74..1412cd4b 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -140,6 +140,7 @@ function requestAuth(data, callback, focus_next = null) { } String.prototype.pluralize = function(count, irreg_plural = false) { - plural = irreg_plural ? irreg_plural : this + 's' ; - return (count==1 ? this : plural) ; + if (Math.abs(count) >= 2) + return irreg_plural ? irreg_plural : this+'s' ; + return this ; } From 481409253b85ef0bb51202ea6a9e46d46735570a Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 11 Feb 2017 21:44:32 -0200 Subject: [PATCH 24/90] simpler string pluralizing --- kfet/templates/kfet/history.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 4a158e9e..78f8ddea 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -169,10 +169,7 @@ $(document).ready(function() { function confirmCancel(opes_to_cancel) { var nb = opes_to_cancel.length; - var content = nb+' opération'.pluralize(nb) - +' va'.pluralize(nb, ' vont') - + ' être' - + ' annulée'.pluralize(nb); + var content = nb+' opération va être annulée'.pluralize(nb, ' opérations vont être annulées') $.confirm({ title: 'Confirmation', content: content, From eac6c42041190a205eedb98d978188f8b34f4873 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 10 Mar 2017 19:57:36 -0300 Subject: [PATCH 25/90] day, opegroup and ope model drafts --- kfet/static/kfet/js/kfet.api.js | 316 +++++++++++++++++++++++++++++++- 1 file changed, 314 insertions(+), 2 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 2a0c398b..20f1203f 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -547,23 +547,199 @@ class Article extends ModelObject { /** * Verbose name for Article model * @default 'article' - * @see {@link Models.ModelObject.compare|ModelObject.compare} + * @see {@link Models.ModelObject.verbose_name|ModelObject.verbose_name} */ static get verbose_name() { return 'article'; } /** - * @default {@link Formatters.ArticleCategoryFormatter} + * @default {@link Formatters.ArticleFormatter} */ formatter() { return ArticleFormatter; } + /** + * Comparison function between Article model instances. + * @see {@link Models.ModelObject.compare|ModelObject.compare} + */ + static compare(a, b) { + return a.name.localeCompare(b.name); + } + // Take care of 'price' type // API currently returns a string object (serialization of Decimal type within Django) get price() { return this._price; } set price(v) { this._price = floatCheck(v); } } +/** + * Day model. Cannot be accessed through API. + * @extends Model.ModelObject + * @memberof Models + */ +class Day extends ModelObject { + + /** + * Properties associated to a day. + * @default ['id', 'date'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ + static get props() { return ['id', 'date'] } + + /** + * Default values for Day model instances + * @default {'id': '', 'date': moment()} + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ + static get default_data() { return {'id': '', 'date': moment()}; } + + /** + * Verbose name for Day model. + * @default 'day' + * @see {@link Models.ModelObject.verbose_name[ModelObject.verbose_name} + */ + static get verbose_name() { return 'day'; } + + /** + * @default {@link Formatters.DayFormatter} + */ + formatter() { + return DayFormatter; + } + + /** + * Comparison function between Day model instances. + * @see {@link Models.ModelObject.compare|ModelObject.compare} + */ + static compare (a, b) { + //Days are sorted by most recent first + if (a.date < b.date) return 1; + else if (a.date > b.date) return -1; + else return 0; + } + + //Parse date and round it + get date() { return this._date; } + set date(v) { this._date = dateUTCToParis(v).startof('date'); } +} + + +/** + * OperationGroup model. Cannot be accessed through API. + * @extends Models.ModelObject + * @memberof Models + */ +class OperationGroup extends ModelObject { + + /** + * Properties associated with an opegroup. + * @default ['id', 'amount', 'at', 'is_cof', + * 'comment', 'trigramme', 'valid_by'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ + static get props() { + return ['id', 'amount', 'at', 'is_cof', 'comment', + 'trigramme', 'valid_by', 'day']; + } + + /** + * Default values for OperationGroup instances. + * @default {'id': 0, 'amount': 0, 'at': moment(), 'is_cof': false, + 'comment': '', 'trigramme': '', 'valid_by': ''} + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ + static get default_data() { + return {'id': 0, 'amount': 0, 'at': moment(), 'is_cof': false, + 'comment': '', 'trigramme': '', 'valid_by': '', 'day': new Day()}; + } + + /** + * Verbose name for OperationGroup model. + * @default 'opegroup' + * @see {@link Models.ModelObject.verbose_name|ModelObject.verbose_name} + */ + static get verbose_name() { return 'opegroup'; } + + /** + * @default {@link Formatters.OpegroupFormatter} + */ + formatter() { + return OpegroupFormatter; + } + + /** + * Comparison function between OperationGroup model instances. + * @see {@link Models.ModelObject.compare|ModelObject.compare} + */ + static compare(a, b) { + //Opegroups are sorted by most recent first + if (a.at < b.at) return 1; + else if (a.at > b.at) return -1; + else return 0; + } + + get amount() { return this._amount; } + set amount(v) { this._amount = floatCheck(v); } + + // Parse the date to a moment() object + get at() { return this._at; } + set at(v) { this._at = dateUTCToParis(v); } +} + +/** + * Operation model. Cannot be accessed through API. + * @extends Models.ModelObject + * @memberof Models + */ +class Operation extends ModelObject { + + /** + * Properties associated to an operation. + * @default ['id', 'type', 'amount', 'article_name', 'article_nb', + * 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at', + * 'canceled_by'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ + static get props() { + return ['id', 'type', 'amount', 'article_name', 'article_nb', + 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at', + 'canceled_by', 'opegroup'] + } + + /** + * Default values for Operation model instances + * @default {'id': 0, 'type': '', 'amount': 0, 'article_name': '', + * 'article_nb': 0, 'is_checkout': false, 'addcost_amount': 0, + * 'addcost_for': '', 'canceled_at': undefined, 'canceled_by': ''} + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ + static get default_data() { + return {'id': 0, 'type': '', 'amount': 0, 'article_name': '', 'article_nb': 0, + 'is_checkout': false, 'addcost_amount': 0, 'addcost_for': '', + 'canceled_at': undefined, 'canceled_by': '', 'opegroup': new OperationGroup() }; + } + + static get verbose_name() { return 'operation'; } + + formatter() { + return OperationFormatter; + } + + get amount() { return this._amount; } + set amount(v) { this._amount = floatCheck(v); } + + get addcost_amount() { return this._addcost_amount; } + set addcost_amount(v) { this._addcost_amount = floatCheck(v); } + + get canceled_at() { return this._canceled_at; } + set canceled_at(v) { + if (v) + this._canceled_at = dateUTCToParis(v); + else + this._canceled_at = undefined; + } +} + /** * Simple {@link Models.ModelObject} list. @@ -1089,3 +1265,139 @@ class ArticleFormatter extends Formatter { else /* a.stock < -5 */ { return this._data_stock.neg; } } } + + +/** + * @extends Formatters.Formatter + * @memberof Formatters + * @todo don't display trigramme in account_read + */ + +//TODO display_trigramme option +class OpegroupFormatter extends Formatter { + + /** + * Properties renderable to html. + * @default {@link Models.Opegroup.props} + */ + static get props() { + return OperationGroup.props.concat(['time']); + } + + static prop_amount(a) { + return amountDisplay(a.amount, a.is_cof, a.trigramme); + } + + static prop_time(a) { + return a.at.format('HH:mm:ss'); + } + + static prop_valid_by(a) { + return 'Par '+a.valid_by; + } +} + +/** + * @extends Formatters.Formatter + * @memberof Formatters + */ + +class DayFormatter extends Formatter { + + /** + * Properties renderable to html. + * @default {@link Models.Day.props} + */ + static get props() { + return Day.props; + } + + static prop_date(a) { + return a.date.format('D MMMM'); + } +} + +/** + * @extends Formatters.Formatter + * @memberof Formatters + */ +class OperationFormatter extends Formatter { + + static get props() { + return ['amount', 'infos1', 'infos2', 'addcost', 'canceled']; + } + + static get attrs() { + return ['canceled']; + } + + static prop_amount(a) { + return amountDisplay(a.amount, a.opegroup.is_cof, a.opegroup.trigramme); + } + + static prop_addcost(a) { + if (a.addcost_for) { + return '('+amountDisplay(a.addcost_amount, a.opegroup.is_cof) + +'UKF pour '+a.addcost_for+')'; + } else { + return ''; + } + } + + static prop_canceled(a) { + if (a.canceled_at) { + cancel = 'Annulé'; + if (a.canceled_by) + cancel += ' par '+a.canceled_by; + + cancel += ' le '+a.canceled_at.format('DD/MM/YY à HH:mm:ss'); + return cancel ; + } else { + return ''; + } + } + + static attr_canceled(a) { + return a.canceled_at ? 'true' : 'false' ; + } +} + +class PurchaseFormatter extends OperationFormatter { + + static prop_infos1(a) { + return a.article_nb; + } + + static prop_infos2(a) { + return a.article_name; + } +} + +class SpecialOpeFormatter extends OperationFormatter { + + static prop_infos1(a) { + return a.amount.toFixed(2)+'€'; + } + + static prop_infos2(a) { + if (!a.is_checkout) + return 'Édition'; + + switch (a.type) { + case 'initial': + return 'Initial'; + break; + + case 'deposit': + return 'Charge'; + break; + + case 'withdraw': + return 'Retrait'; + break; + + default: + return ''; + } + } +} From 8eae3cee7f4bc5851b590e5649879c798d93eeb3 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 10 Mar 2017 22:08:44 -0300 Subject: [PATCH 26/90] adapt history view --- kfet/views.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index 4b356d43..50704f6d 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1301,21 +1301,21 @@ def history_json(request): # Construction de la réponse - opegroups_list = [] + ope_list = [] for opegroup in opegroups: opegroup_dict = { 'id' : opegroup.id, 'amount' : opegroup.amount, 'at' : opegroup.at, - 'checkout_id': opegroup.checkout_id, 'is_cof' : opegroup.is_cof, 'comment' : opegroup.comment, - 'opes' : [], - 'on_acc__trigramme': + 'trigramme': opegroup.on_acc and opegroup.on_acc.trigramme or None, + 'day': {'id': opegroup.at.strftime('%Y%m%d'), + 'date': opegroup.at}, } if request.user.has_perm('kfet.is_team'): - opegroup_dict['valid_by__trigramme'] = ( + opegroup_dict['valid_by'] = ( opegroup.valid_by and opegroup.valid_by.trigramme or None) for ope in opegroup.opes.all(): ope_dict = { @@ -1326,17 +1326,17 @@ def history_json(request): 'is_checkout' : ope.is_checkout, 'addcost_amount': ope.addcost_amount, 'canceled_at' : ope.canceled_at, - 'article__name': + 'article_name': ope.article and ope.article.name or None, - 'addcost_for__trigramme': + 'addcost_for': ope.addcost_for and ope.addcost_for.trigramme or None, + 'opegroup': opegroup_dict, } if request.user.has_perm('kfet.is_team'): - ope_dict['canceled_by__trigramme'] = ( + ope_dict['canceled_by'] = ( ope.canceled_by and ope.canceled_by.trigramme or None) - opegroup_dict['opes'].append(ope_dict) - opegroups_list.append(opegroup_dict) - return JsonResponse({ 'opegroups': opegroups_list }) + ope_list.append(ope_dict) + return JsonResponse(ope_list, safe=False) @teamkfet_required From 741bac880bc4565f3631426887384ceebcf9a406 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 10 Mar 2017 22:09:02 -0300 Subject: [PATCH 27/90] dummy History test class --- kfet/static/kfet/js/history.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 291c106d..7deb407d 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,3 +1,22 @@ +class History { + + constructor() { + this.templates = { + 'operation': '
', + 'opegroup': '
', + 'day': '
', + }; + + this._$container = $('#history'); + + this.list = new OperationList(); + this.api_options = { + 'from': moment().subtract(3, 'days').format('YYYY-MM-DD HH:mm:ss'), + }; + } +} + + function KHistory(options={}) { $.extend(this, KHistory.default_options, options); From ac2e773f9e14982125d1884f625a0bb446d049c9 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 10 Mar 2017 22:09:23 -0300 Subject: [PATCH 28/90] opelist class --- kfet/static/kfet/js/kfet.api.js | 41 ++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 20f1203f..d94a8b5b 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -620,7 +620,7 @@ class Day extends ModelObject { //Parse date and round it get date() { return this._date; } - set date(v) { this._date = dateUTCToParis(v).startof('date'); } + set date(v) { this._date = dateUTCToParis(v).startOf('date'); } } @@ -722,7 +722,10 @@ class Operation extends ModelObject { static get verbose_name() { return 'operation'; } formatter() { - return OperationFormatter; + if (this.type === 'purchase') + return PurchaseFormatter; + else + return SpecialOpeFormatter; } get amount() { return this._amount; } @@ -961,16 +964,15 @@ class ArticleList extends APIModelList { /** * Default structure for ArticleList instances - * @abstract * @default [Article, ArticleCategory] + * @see {@link Models.ModelList.models|ModelList.models} */ static get models() { return [Article, ArticleCategory]; } /** - * Default url to get ArticlList data - * @abstract + * Default url to get ArticleList data * @default django-js-reverse('kfet.kpsul.articles_data') * @see {@link Models.APIModelList.url_model|APIModelList.url_model} */ @@ -979,6 +981,30 @@ class ArticleList extends APIModelList { } } +/** + * OperationList model. Can be accessed through API. + * @extends Models.APIModelList + * @memberof Models + */ +class OperationList extends APIModelList { + /** + * Default structure for OperationList instances. + * @default [Operation, OperationGroup, Day] + * @see {@link Models.ModelList.models|ModelList.models} + */ + static get models() { + return [Operation, OperationGroup, Day]; + } + + /** + * Default url to get OperationList data. + * @default django-js-reverse('kfet.history.json') + * @see {@link Models.APIModelList.url_model|APIModelList.url_model} + */ + static get url_model() { + return Urls['kfet.history.json'](); + } +} /* ---------- ---------- */ @@ -1293,7 +1319,10 @@ class OpegroupFormatter extends Formatter { } static prop_valid_by(a) { - return 'Par '+a.valid_by; + if (a.valid_by) + return 'Par '+a.valid_by; + else + return ''; } } From 66beeb5bd0ff49ba783495fb30f6ac6d07d32250 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 11 Mar 2017 01:41:21 -0300 Subject: [PATCH 29/90] transfer history added --- kfet/static/kfet/js/history.js | 2 ++ kfet/static/kfet/js/kfet.api.js | 54 +++++++++++++++++++++++++-------- kfet/views.py | 8 ++--- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 599dd9c6..670429f9 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -4,7 +4,9 @@ class History { this.templates = { 'operation': '
', 'opegroup': '
', + 'transfergroup': '
Transferts
', 'day': '
', + 'transfer': '
', }; this._$container = $('#history'); diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index d94a8b5b..5748c3ee 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -634,12 +634,12 @@ class OperationGroup extends ModelObject { /** * Properties associated with an opegroup. * @default ['id', 'amount', 'at', 'is_cof', - * 'comment', 'trigramme', 'valid_by'] + * 'comment', 'trigramme', 'valid_by', 'day', 'type'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { return ['id', 'amount', 'at', 'is_cof', 'comment', - 'trigramme', 'valid_by', 'day']; + 'trigramme', 'valid_by', 'day', 'type']; } /** @@ -649,8 +649,9 @@ class OperationGroup extends ModelObject { * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return {'id': 0, 'amount': 0, 'at': moment(), 'is_cof': false, - 'comment': '', 'trigramme': '', 'valid_by': '', 'day': new Day()}; + return {'id': '', 'amount': 0, 'at': moment(), 'is_cof': false, + 'comment': '', 'trigramme': '', 'valid_by': '', 'day': new Day(), + 'type': ''}; } /** @@ -697,13 +698,17 @@ class Operation extends ModelObject { * Properties associated to an operation. * @default ['id', 'type', 'amount', 'article_name', 'article_nb', * 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at', - * 'canceled_by'] + * 'canceled_by', 'from_acc', 'to_acc', 'opegroup'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return ['id', 'type', 'amount', 'article_name', 'article_nb', - 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at', - 'canceled_by', 'opegroup'] + var common = ['id', 'type', 'amount', 'canceled_at', 'canceled_by', + 'is_checkout', 'opegroup']; + var transfer_only = ['from_acc', 'to_acc']; + var purchase_only = ['article_name', 'article_nb', 'addcost_for', + 'addcost_amount']; + + return common.concat(transfer_only, purchase_only); } /** @@ -714,9 +719,14 @@ class Operation extends ModelObject { * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return {'id': 0, 'type': '', 'amount': 0, 'article_name': '', 'article_nb': 0, - 'is_checkout': false, 'addcost_amount': 0, 'addcost_for': '', - 'canceled_at': undefined, 'canceled_by': '', 'opegroup': new OperationGroup() }; + var common = {'id': '', 'type': '', 'amount': 0, 'is_checkout': false, + 'canceled_at': undefined, 'canceled_by': '', + 'opegroup': new OperationGroup()}; + var transfer_only = {'from_acc': '', 'to_acc': ''}; + var purchase_only = {'article_name': '', 'article_nb': 0, + 'addcost_amount': 0, 'addcost_for': 0}; + + return $.extend({}, common, transfer_only, purchase_only); } static get verbose_name() { return 'operation'; } @@ -724,6 +734,8 @@ class Operation extends ModelObject { formatter() { if (this.type === 'purchase') return PurchaseFormatter; + else if (this.type == 'transfer') + return TransferFormatter; else return SpecialOpeFormatter; } @@ -847,8 +859,9 @@ class ModelList { render_element(elt, templates, options) { var name = elt.constructor.verbose_name; var depth = this.constructor.names.indexOf(name); - var template = templates[name]; - var options = options || {} ; + // Allows for more granular template specification + var template = templates[elt.type] || templates[name]; + var options = options || {} ; if (depth == -1) { return $(); @@ -1430,3 +1443,18 @@ class SpecialOpeFormatter extends OperationFormatter { } } } + +class TransferFormatter extends OperationFormatter { + + static prop_amount(a) { + return a.amount.toFixed(2)+'€'; + } + + static prop_infos1(a) { + return a.from_acc; + } + + static prop_infos2(a) { + return a.to_acc; + } +} diff --git a/kfet/views.py b/kfet/views.py index 760949ea..ca750bc3 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1400,7 +1400,7 @@ def history_json(request): ope_list = [] for opegroup in opegroups: opegroup_dict = { - 'id': opegroup.id, + 'id': 'ope'+str(opegroup.id), 'type': 'opegroup', 'amount': opegroup.amount, 'at': opegroup.at, @@ -1416,7 +1416,7 @@ def history_json(request): opegroup.valid_by and opegroup.valid_by.trigramme or None) for ope in opegroup.opes.all(): ope_dict = { - 'id': ope.id, + 'id': 'ope'+str(ope.id), 'type': ope.type, 'amount': ope.amount, 'article_nb': ope.article_nb, @@ -1437,7 +1437,7 @@ def history_json(request): for transfergroup in transfergroups: if transfergroup.filtered_transfers: transfergroup_dict = { - 'id': transfergroup.id, + 'id': 'tr'+str(transfergroup.id), 'type': 'transfergroup', 'at': transfergroup.at, 'comment': transfergroup.comment, @@ -1452,7 +1452,7 @@ def history_json(request): for transfer in transfergroup.filtered_transfers: transfer_dict = { - 'id': transfer.id, + 'id': 'tr'+str(transfer.id), 'type': 'transfer', 'amount': transfer.amount, 'canceled_at': transfer.canceled_at, From df0ea96b417d2365199a99a5957bc1f6d0bdd435 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 17 Mar 2017 15:59:21 -0300 Subject: [PATCH 30/90] Adapt history_json return value to ModelForest standards --- kfet/views.py | 115 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index 888c8001..09755bbf 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1397,77 +1397,112 @@ def history_json(request): opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) # Construction de la réponse - ope_list = [] + opegroup_list = [] for opegroup in opegroups: opegroup_dict = { - 'id': 'ope'+str(opegroup.id), 'type': 'opegroup', - 'amount': opegroup.amount, - 'at': opegroup.at, - 'is_cof': opegroup.is_cof, - 'comment': opegroup.comment, - 'trigramme': - opegroup.on_acc and opegroup.on_acc.trigramme or None, - 'day': {'id': opegroup.at.strftime('%Y%m%d'), - 'date': opegroup.at}, + 'content': { + 'id': opegroup.id, + 'amount': opegroup.amount, + 'at': opegroup.at, + 'is_cof': opegroup.is_cof, + 'comment': opegroup.comment, + 'trigramme': + opegroup.on_acc and opegroup.on_acc.trigramme or None, + }, + 'parent': { + 'type': 'day', + 'content': { + 'id': opegroup.at.strftime('%Y%m%d'), + 'date': opegroup.at + }, + 'child_sort': 'opegroup', + }, + 'children': [], } if request.user.has_perm('kfet.is_team'): - opegroup_dict['valid_by'] = ( + opegroup_dict['content']['valid_by'] = ( opegroup.valid_by and opegroup.valid_by.trigramme or None) + for ope in opegroup.opes.all(): ope_dict = { - 'id': 'ope'+str(ope.id), - 'type': ope.type, - 'amount': ope.amount, - 'article_nb': ope.article_nb, - 'is_checkout': ope.is_checkout, - 'addcost_amount': ope.addcost_amount, - 'canceled_at': ope.canceled_at, - 'article_name': - ope.article and ope.article.name or None, - 'addcost_for': - ope.addcost_for and ope.addcost_for.trigramme or None, - 'opegroup': opegroup_dict, + 'content': { + 'id': ope.id, + 'amount': ope.amount, + 'canceled_at': ope.canceled_at, + 'is_cof': opegroup.is_cof, + 'trigramme': + opegroup.on_acc and opegroup.on_acc.trigramme or None, + }, } if request.user.has_perm('kfet.is_team'): - ope_dict['canceled_by'] = ( + ope_dict['content']['canceled_by'] = ( ope.canceled_by and ope.canceled_by.trigramme or None) - ope_list.append(ope_dict) + + if ope.type == Operation.PURCHASE: + ope_dict['type'] = 'purchase' + ope_dict['content'].update({ + 'article_name': ope.article.name, + 'article_nb': ope.article_nb, + 'addcost_amount': ope.addcost_amount, + 'addcost_for': + ope.addcost_for and ope.addcost_for.trigramme or None, + }) + else: + ope_dict['type'] = 'specialope' + ope_dict['content'].update({ + 'type': ope.type, + 'is_checkout': ope.is_checkout, + }) + + opegroup_dict['children'].append(ope_dict) + opegroup_list.append(opegroup_dict) for transfergroup in transfergroups: if transfergroup.filtered_transfers: transfergroup_dict = { - 'id': 'tr'+str(transfergroup.id), 'type': 'transfergroup', - 'at': transfergroup.at, - 'comment': transfergroup.comment, - 'day': {'id': transfergroup.at.strftime('%Y%m%d'), - 'date': transfergroup.at}, + 'content': { + 'id': transfergroup.id, + 'at': transfergroup.at, + 'comment': transfergroup.comment, + }, + 'parent': { + 'type': 'day', + 'content': { + 'id': transfergroup.at.strftime('%Y%m%d'), + 'date': transfergroup.at + }, + }, + 'children': [], } + if request.user.has_perm('kfet.is_team'): - transfergroup_dict['valid_by'] = ( + transfergroup_dict['content']['valid_by'] = ( transfergroup.valid_by and transfergroup.valid_by.trigramme or None) for transfer in transfergroup.filtered_transfers: transfer_dict = { - 'id': 'tr'+str(transfer.id), 'type': 'transfer', - 'amount': transfer.amount, - 'canceled_at': transfer.canceled_at, - 'from_acc': transfer.from_acc.trigramme, - 'to_acc': transfer.to_acc.trigramme, - 'opegroup': transfergroup_dict, + 'content': { + 'id': transfer.id, + 'amount': transfer.amount, + 'canceled_at': transfer.canceled_at, + 'from_acc': transfer.from_acc.trigramme, + 'to_acc': transfer.to_acc.trigramme, + }, } if request.user.has_perm('kfet.is_team'): - transfer_dict['canceled_by'] = ( + transfer_dict['content']['canceled_by'] = ( transfer.canceled_by and transfer.canceled_by.trigramme or None) - ope_list.append(transfer_dict) + transfergroup_dict['children'].append(transfer_dict) + opegroup_list.append(transfergroup_dict) - return JsonResponse(ope_list, safe=False) + return JsonResponse(opegroup_list, safe=False) @teamkfet_required def kpsul_articles_data(request): From 47fe74fbb0460e6add8442b6334b7d751daa48c2 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 17 Mar 2017 15:59:52 -0300 Subject: [PATCH 31/90] template specification --- kfet/static/kfet/js/history.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 670429f9..008eb8d1 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -2,9 +2,10 @@ class History { constructor() { this.templates = { - 'operation': '
', + 'purchase': '
', + 'specialope': '
', 'opegroup': '
', - 'transfergroup': '
Transferts
', + 'transfergroup': '
', 'day': '
', 'transfer': '
', }; From 58c57c6f89ac0e6a3dd0f732f3399c373cd5621d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 17 Mar 2017 16:00:49 -0300 Subject: [PATCH 32/90] Add hierarchy of needed models, w/ formatters --- kfet/static/kfet/js/kfet.api.js | 237 ++++++++++++++++++++------------ 1 file changed, 149 insertions(+), 88 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index f40ad20f..1d99aaae 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -171,8 +171,8 @@ class ModelObject { * Returns a string value for the model, to use in comparisons * @see {@link Models.TreeNode.compare|TreeNode.compare} */ - comparevalue() { - return this.id.toString(); + static compare(a, b) { + return a.id - b.id; } } @@ -520,15 +520,11 @@ class Article extends ModelObject { /** * Default values for Article model instances. - * @default { 'id': 0, 'name': '', 'price': 0, 'stock': 0, - * 'category': new ArticleCategory() } + * @default { 'id': 0, 'name': '', 'price': 0, 'stock': 0 } * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return { - 'id': 0, 'name': '', 'price': 0, 'stock': 0, - 'category': new ArticleCategory(), - }; + return { 'id': 0, 'name': '', 'price': 0, 'stock': 0 }; } /** @@ -603,13 +599,43 @@ class Day extends ModelObject { set date(v) { this._date = dateUTCToParis(v).startOf('date'); } } - /** - * OperationGroup model. Cannot be accessed through API. + * HistoryGroup model. Should not be used without extending. * @extends Models.ModelObject * @memberof Models */ -class OperationGroup extends ModelObject { +class HistoryGroup extends ModelObject { + static get props() { + return ['id', 'at', 'comment', 'valid_by']; + } + + static get default_data() { + return {'id': 0, 'at': moment(), 'comment': '', + 'valid_by': '',}; + } + + /** + * Comparison function between OperationGroup model instances. + * @see {@link Models.ModelObject.compare|ModelObject.compare} + */ + static compare(a, b) { + //Groups are sorted by most recent first + if (a.at < b.at) return 1; + else if (a.at > b.at) return -1; + else return 0; + } + + // Parse the date to a moment() object + get at() { return this._at; } + set at(v) { this._at = dateUTCToParis(v); } +} + +/** + * OperationGroup model. Cannot be accessed through API. + * @extends Models.HistoryGroup + * @memberof Models + */ +class OperationGroup extends HistoryGroup { /** * Properties associated with an opegroup. @@ -618,8 +644,7 @@ class OperationGroup extends ModelObject { * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return ['id', 'amount', 'at', 'is_cof', 'comment', - 'trigramme', 'valid_by', 'day', 'type']; + return HistoryGroup.props.concat(['is_cof', 'trigramme']); } /** @@ -629,18 +654,10 @@ class OperationGroup extends ModelObject { * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return {'id': '', 'amount': 0, 'at': moment(), 'is_cof': false, - 'comment': '', 'trigramme': '', 'valid_by': '', 'day': new Day(), - 'type': ''}; + return $.extend({}, HistoryGroup.default_data, + {'amount': 0, 'is_cof': false, 'trigramme': ''}); } - /** - * Verbose name for OperationGroup model. - * @default 'opegroup' - * @see {@link Models.ModelObject.verbose_name|ModelObject.verbose_name} - */ - static get verbose_name() { return 'opegroup'; } - /** * @default {@link Formatters.OpegroupFormatter} */ @@ -648,27 +665,27 @@ class OperationGroup extends ModelObject { return OpegroupFormatter; } - /** - * Comparison function between OperationGroup model instances. - * @see {@link Models.ModelObject.compare|ModelObject.compare} - */ - static compare(a, b) { - //Opegroups are sorted by most recent first - if (a.at < b.at) return 1; - else if (a.at > b.at) return -1; - else return 0; - } - get amount() { return this._amount; } set amount(v) { this._amount = floatCheck(v); } - - // Parse the date to a moment() object - get at() { return this._at; } - set at(v) { this._at = dateUTCToParis(v); } } /** - * Operation model. Cannot be accessed through API. + * TransferGroup model. Cannot be accessed through API. + * @extends Models.HistoryGroup + * @memberof Models + */ +class TransferGroup extends HistoryGroup { + + /** + * @default {@link Formatters.TransferGroupFormatter} + */ + formatter() { + return TransferGroupFormatter; + } +} + +/** + * Operation model. Should not be used directly. * @extends Models.ModelObject * @memberof Models */ @@ -682,13 +699,7 @@ class Operation extends ModelObject { * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - var common = ['id', 'type', 'amount', 'canceled_at', 'canceled_by', - 'is_checkout', 'opegroup']; - var transfer_only = ['from_acc', 'to_acc']; - var purchase_only = ['article_name', 'article_nb', 'addcost_for', - 'addcost_amount']; - - return common.concat(transfer_only, purchase_only); + return ['id', 'amount', 'canceled_at', 'canceled_by']; } /** @@ -699,33 +710,12 @@ class Operation extends ModelObject { * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - var common = {'id': '', 'type': '', 'amount': 0, 'is_checkout': false, - 'canceled_at': undefined, 'canceled_by': '', - 'opegroup': new OperationGroup()}; - var transfer_only = {'from_acc': '', 'to_acc': ''}; - var purchase_only = {'article_name': '', 'article_nb': 0, - 'addcost_amount': 0, 'addcost_for': 0}; - - return $.extend({}, common, transfer_only, purchase_only); - } - - static get verbose_name() { return 'operation'; } - - formatter() { - if (this.type === 'purchase') - return PurchaseFormatter; - else if (this.type == 'transfer') - return TransferFormatter; - else - return SpecialOpeFormatter; + return {'id': '', 'amount': 0, 'canceled_at': undefined, 'canceled_by': '' }; } get amount() { return this._amount; } set amount(v) { this._amount = floatCheck(v); } - get addcost_amount() { return this._addcost_amount; } - set addcost_amount(v) { this._addcost_amount = floatCheck(v); } - get canceled_at() { return this._canceled_at; } set canceled_at(v) { if (v) @@ -735,6 +725,53 @@ class Operation extends ModelObject { } } +class Purchase extends Operation { + static get props() { + return Operation.props.concat( + ['article_name', 'article_nb', 'addcost_amount', 'addcost_for'] + ); + } + + static get default_data() { + return $.extend({}, Operation.default_data, { + 'article_name': '', 'article_nb': 0, + 'addcost_amount': 0, 'addcost_for': '' + }); + } + + formatter() { + return PurchaseFormatter; + } +} + +class SpecialOperation extends Operation { + static get props() { + return Operation.props.concat(['type', 'is_checkout']); + } + + static get default_data() { + return $.extend({}, Operation.default_data, {'type': '', 'is_checkout': false}); + } + + formatter() { + return SpecialOpeFormatter; + } +} + +class Transfer extends Operation { + static get props() { + return Operation.props.concat(['from_acc', 'to_acc']); + } + + static get default_data() { + return $.extend({}, Operation.default_data, {'from_acc': '', 'to_acc': ''}); + } + + formatter() { + return TransferFormatter; + } +} + /** * Node for ModelForest object @@ -805,7 +842,9 @@ class ModelForest { var node = new TreeNode(data.type, content); if (data.child_sort) - node.child_sort = data.child_sort + node.child_sort = this.constructor.models[data.child_sort]; + else + node.child_sort = ModelObject; if (direction <= 0) { if (data.parent) { @@ -864,7 +903,7 @@ class ModelForest { $container.append($rendered); //dirty - node.children.sort(this.constructor.compare.bind(null, this.constructor.models[node.child_sort])); + node.children.sort(this.constructor.compare.bind(null, node.child_sort)); for (let child of node.children) { var $child = this.render_element(child, templates, options); @@ -1018,14 +1057,21 @@ class ArticleList extends APIModelForest { * @extends Models.APIModelList * @memberof Models */ -class OperationList extends APIModelList { +class OperationList extends APIModelForest { /** * Default structure for OperationList instances. * @default [Operation, OperationGroup, Day] * @see {@link Models.ModelList.models|ModelList.models} */ static get models() { - return [Operation, OperationGroup, Day]; + return { + 'day': Day, + 'opegroup': OperationGroup, + 'transfergroup': TransferGroup, + 'purchase': Purchase, + 'specialope': SpecialOperation, + 'transfer': Transfer, + }; } /** @@ -1036,6 +1082,11 @@ class OperationList extends APIModelList { static get url_model() { return Urls['kfet.history.json'](); } + + constructor() { + super(); + this.root_sort = Day; + } } @@ -1331,20 +1382,7 @@ class ArticleFormatter extends Formatter { * @todo don't display trigramme in account_read */ -//TODO display_trigramme option -class OpegroupFormatter extends Formatter { - - /** - * Properties renderable to html. - * @default {@link Models.Opegroup.props} - */ - static get props() { - return OperationGroup.props.concat(['time']); - } - - static prop_amount(a) { - return amountDisplay(a.amount, a.is_cof, a.trigramme); - } +class HistoryGroupFormatter extends Formatter { static prop_time(a) { return a.at.format('HH:mm:ss'); @@ -1358,6 +1396,29 @@ class OpegroupFormatter extends Formatter { } } +class TransferGroupFormatter extends HistoryGroupFormatter { + + static get props() { + return TransferGroup.props.concat(['infos', 'time']); + } + + static prop_infos(a) { + //TODO find better thing to put here + return 'Transferts'; + } +} + +class OpegroupFormatter extends HistoryGroupFormatter { + + static get props() { + return OperationGroup.props.concat(['time']); + } + + static prop_amount(a) { + return amountDisplay(a.amount, a.is_cof, a.trigramme); + } +} + /** * @extends Formatters.Formatter * @memberof Formatters @@ -1393,12 +1454,12 @@ class OperationFormatter extends Formatter { } static prop_amount(a) { - return amountDisplay(a.amount, a.opegroup.is_cof, a.opegroup.trigramme); + return amountDisplay(a.amount, a.is_cof, a.trigramme); } static prop_addcost(a) { if (a.addcost_for) { - return '('+amountDisplay(a.addcost_amount, a.opegroup.is_cof) + return '('+amountDisplay(a.addcost_amount, a.is_cof) +'UKF pour '+a.addcost_for+')'; } else { return ''; From b0b1fdf936a7b14fb71bf63d0fac4427d8cad6ba Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 17 Mar 2017 21:00:58 -0300 Subject: [PATCH 33/90] Add jsdoc comments to history models --- kfet/static/kfet/js/kfet.api.js | 194 +++++++++++++++++++++++++++----- 1 file changed, 165 insertions(+), 29 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 1d99aaae..c90d864d 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -85,7 +85,18 @@ class Config { * Models without API support: * {@link Models.Statement}, * {@link Models.ArticleCategory}, - * {@link Models.Article}. + * {@link Models.Article}, + * {@link Models.HistoryGroup}, + * {@link Models.OperationGroup}, + * {@link Models.TransferGroup}, + * {@link Models.Operation}, + * {@link Models.Purchase}, + * {@link Models.SpecialOperation}, + * {@link Models.Transfer}. + *
+ * Implementations of ModelForest: + * {@link Models.ArticleList} + * {@link Models.OperationList} * * @namespace Models */ @@ -168,8 +179,7 @@ class ModelObject { } /** - * Returns a string value for the model, to use in comparisons - * @see {@link Models.TreeNode.compare|TreeNode.compare} + * Compares two ModelObject instances. */ static compare(a, b) { return a.id - b.id; @@ -569,13 +579,6 @@ class Day extends ModelObject { */ static get default_data() { return {'id': '', 'date': moment()}; } - /** - * Verbose name for Day model. - * @default 'day' - * @see {@link Models.ModelObject.verbose_name[ModelObject.verbose_name} - */ - static get verbose_name() { return 'day'; } - /** * @default {@link Formatters.DayFormatter} */ @@ -600,22 +603,34 @@ class Day extends ModelObject { } /** - * HistoryGroup model. Should not be used without extending. + * HistoryGroup model. Should not be used directly. * @extends Models.ModelObject * @memberof Models */ class HistoryGroup extends ModelObject { + + /** + * Properties assowiated to HistoryGroup instances. + * @default ['id', 'at', 'comment', 'valid_by'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ static get props() { return ['id', 'at', 'comment', 'valid_by']; } + /** + * Default values for HistoryGroup model instances + * @default { 'id': 0, 'at': moment(), 'comment': '', + 'valid_by': '' } + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ static get default_data() { return {'id': 0, 'at': moment(), 'comment': '', 'valid_by': '',}; } /** - * Comparison function between OperationGroup model instances. + * Comparison function between HistoryGroup model instances. * @see {@link Models.ModelObject.compare|ModelObject.compare} */ static compare(a, b) { @@ -639,8 +654,7 @@ class OperationGroup extends HistoryGroup { /** * Properties associated with an opegroup. - * @default ['id', 'amount', 'at', 'is_cof', - * 'comment', 'trigramme', 'valid_by', 'day', 'type'] + * @default {@link Models.HistoryGroup.props|HistoryGroup.props} + ['is_cof', 'trigramme'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { @@ -649,8 +663,8 @@ class OperationGroup extends HistoryGroup { /** * Default values for OperationGroup instances. - * @default {'id': 0, 'amount': 0, 'at': moment(), 'is_cof': false, - 'comment': '', 'trigramme': '', 'valid_by': ''} + * @default {@link Models.HistoryGroup.default_data|HistoryGroup.default_data} + + * {'amount': 0, 'is_cof': false, 'trigramme': ''} * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { @@ -693,9 +707,7 @@ class Operation extends ModelObject { /** * Properties associated to an operation. - * @default ['id', 'type', 'amount', 'article_name', 'article_nb', - * 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at', - * 'canceled_by', 'from_acc', 'to_acc', 'opegroup'] + * @default ['id', 'amount', 'canceled_at', 'canceled_by'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { @@ -704,9 +716,7 @@ class Operation extends ModelObject { /** * Default values for Operation model instances - * @default {'id': 0, 'type': '', 'amount': 0, 'article_name': '', - * 'article_nb': 0, 'is_checkout': false, 'addcost_amount': 0, - * 'addcost_for': '', 'canceled_at': undefined, 'canceled_by': ''} + * @default {'id': '', 'amount': 0, 'canceled_at': undefined, 'canceled_by': '' } * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { @@ -725,13 +735,33 @@ class Operation extends ModelObject { } } +/** + * Purchase model. Cannot be accessed through API. + * @extends Models.Operation + * @memberof Models + */ class Purchase extends Operation { + + /** + * Additional properties for purchases. + * @default {@link Models.Operation.props|Operation.props} + ['article_name', 'article_nb', + * 'addcost_amount', 'addcost_for'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ static get props() { return Operation.props.concat( ['article_name', 'article_nb', 'addcost_amount', 'addcost_for'] ); } + /** + * Default values for Operation model instances + * @default {@link Models.Operation.default_data|Operation.default_data} + { + * 'article_name': '', 'article_nb': 0, + * 'addcost_amount': 0, 'addcost_for': '' + * } + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ static get default_data() { return $.extend({}, Operation.default_data, { 'article_name': '', 'article_nb': 0, @@ -739,34 +769,75 @@ class Purchase extends Operation { }); } + /** + * @default {@link Formatters.PurchaseFormatter} + */ formatter() { return PurchaseFormatter; } } +/** + * Special operation model. Cannot be accessed through API. + * @extends Models.Operation + * @memberof Models + */ class SpecialOperation extends Operation { + + /** + * Additional properties for special operations. + * @default {@link Models.Operation.props|Operation.props} + ['type', 'is_checkout'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ static get props() { return Operation.props.concat(['type', 'is_checkout']); } + /** + * Default values for SpecialOperation model instances + * @default {@link Models.Operation.default_data|Operation.default_data} + {'type': '', 'is_checkout': false} + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ static get default_data() { return $.extend({}, Operation.default_data, {'type': '', 'is_checkout': false}); } + /** + * @default {@link Formatters.SpecialOpeFormatter} + */ formatter() { return SpecialOpeFormatter; } } +/** + * Transfer model. Cannot be accessed through API. + * @exetends Models.Operation + * @memberof Models + */ class Transfer extends Operation { + + /** + * Additional properties for transfers. + * @default {@link Models.Operation.props|Operation.props} + ['from_acc', 'to_acc'] + * @see {@link Models.ModelObject.props|ModelObject.props} + */ static get props() { return Operation.props.concat(['from_acc', 'to_acc']); } + /** + * Default values for Transfer model instances + * @default {@link Models.Operation.default_data|Operation.default_data} + {'from_acc': '', 'to_acc': ''} + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ static get default_data() { return $.extend({}, Operation.default_data, {'from_acc': '', 'to_acc': ''}); } + /** + * @default {@link Formatters.TransferFormatter} + */ formatter() { return TransferFormatter; } @@ -1346,7 +1417,6 @@ class ArticleCategoryFormatter extends Formatter { * @memberof Formatters * @extends Formatters.Formatter */ - class ArticleFormatter extends Formatter { /** @@ -1357,6 +1427,9 @@ class ArticleFormatter extends Formatter { return Article.props; } + /** + * s.price converted to UKF. + */ static prop_price(s) { return amountToUKF(s.price, true); } @@ -1368,10 +1441,13 @@ class ArticleFormatter extends Formatter { }; } - static attr_data_stock(a) { - if (a.stock >= 5) { return this._data_stock.ok; } - else if (a.stock >= -5) { return this._data_stock.low; } - else /* a.stock < -5 */ { return this._data_stock.neg; } + /** + * Value of data_stock attribute based on s.stock. + */ + static attr_data_stock(s) { + if (s.stock >= 5) { return this._data_stock.ok; } + else if (s.stock >= -5) { return this._data_stock.low; } + else /* s.stock < -5 */{ return this._data_stock.neg; } } } @@ -1381,13 +1457,18 @@ class ArticleFormatter extends Formatter { * @memberof Formatters * @todo don't display trigramme in account_read */ - class HistoryGroupFormatter extends Formatter { + /** + * a.time formatted as HH:mm:ss + */ static prop_time(a) { return a.at.format('HH:mm:ss'); } + /** + * valid_by property is displayed only if a.valid_by is nonempty. + */ static prop_valid_by(a) { if (a.valid_by) return 'Par '+a.valid_by; @@ -1396,24 +1477,46 @@ class HistoryGroupFormatter extends Formatter { } } +/** + * @memberof Formatters + * @extends Formatters.HistoryGroupFormatter + */ class TransferGroupFormatter extends HistoryGroupFormatter { + /** + * Properties renderable to html. + * @default {@link Models.TransferGroup.props} + ['infos', 'time'] + */ static get props() { return TransferGroup.props.concat(['infos', 'time']); } + /** + * Generic info for transfergroups. + */ static prop_infos(a) { //TODO find better thing to put here return 'Transferts'; } } +/** + * @memberof Formatters + * @extends Formatters.HistoryGroupFormatter + */ class OpegroupFormatter extends HistoryGroupFormatter { + /** + * Properties renderable to html. + * @default {@link Models.OperationGroup.props} + ['time'] + */ static get props() { return OperationGroup.props.concat(['time']); } + /** + * a.amount displayed according to a.is_cof and a.trigramme values. + */ static prop_amount(a) { return amountDisplay(a.amount, a.is_cof, a.trigramme); } @@ -1423,7 +1526,6 @@ class OpegroupFormatter extends HistoryGroupFormatter { * @extends Formatters.Formatter * @memberof Formatters */ - class DayFormatter extends Formatter { /** @@ -1434,6 +1536,9 @@ class DayFormatter extends Formatter { return Day.props; } + /** + * a.date formatted as D MMMM + */ static prop_date(a) { return a.date.format('D MMMM'); } @@ -1445,6 +1550,10 @@ class DayFormatter extends Formatter { */ class OperationFormatter extends Formatter { + /** + * Properties renderable to html. + * @default ['amount', 'infos1', 'infos2', 'addcost', 'canceled'] + */ static get props() { return ['amount', 'infos1', 'infos2', 'addcost', 'canceled']; } @@ -1453,10 +1562,16 @@ class OperationFormatter extends Formatter { return ['canceled']; } + /** + * a.amount displayed according to a.is_cof and a.trigramme values. + */ static prop_amount(a) { return amountDisplay(a.amount, a.is_cof, a.trigramme); } + /** + * addcost property is displayed iff a.addcost_for is nonempty. + */ static prop_addcost(a) { if (a.addcost_for) { return '('+amountDisplay(a.addcost_amount, a.is_cof) @@ -1466,6 +1581,9 @@ class OperationFormatter extends Formatter { } } + /** + * canceled property is displayed iff a.canceled_at is defined. + */ static prop_canceled(a) { if (a.canceled_at) { cancel = 'Annulé'; @@ -1484,6 +1602,10 @@ class OperationFormatter extends Formatter { } } +/** + * @extends Formatters.OperationFormatter + * @memberof Formatters + */ class PurchaseFormatter extends OperationFormatter { static prop_infos1(a) { @@ -1495,8 +1617,15 @@ class PurchaseFormatter extends OperationFormatter { } } +/** + * @extends Formatters.OperationFormatter + * @memberof Formatters + */ class SpecialOpeFormatter extends OperationFormatter { + /** + * a.amount with two decimal places. + */ static prop_infos1(a) { return a.amount.toFixed(2)+'€'; } @@ -1524,8 +1653,15 @@ class SpecialOpeFormatter extends OperationFormatter { } } +/** + * @extends Formatters.OperationFormatter + * @memberof Formatters + */ class TransferFormatter extends OperationFormatter { + /** + * a.amount with two decimal places. + */ static prop_amount(a) { return a.amount.toFixed(2)+'€'; } From 5c9c206f685e2826b8b3b49318b72b983c34a9be Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 02:25:44 -0300 Subject: [PATCH 34/90] Adapt css to new canceled syntax --- kfet/static/kfet/css/history.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kfet/static/kfet/css/history.css b/kfet/static/kfet/css/history.css index fc6a46ad..0e75741a 100644 --- a/kfet/static/kfet/css/history.css +++ b/kfet/static/kfet/css/history.css @@ -93,11 +93,11 @@ color:#FFF; } -#history .ope.canceled, #history .transfer.canceled { +#history [canceled="true"] { color:#444; } -#history .ope.canceled::before, #history.transfer.canceled::before { +#history [canceled="true"]::before { position: absolute; content: ' '; width:100%; From 14b922634d52dbcae0725f793299b8113abce052 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 02:28:01 -0300 Subject: [PATCH 35/90] Remove deprecated history functions --- kfet/templates/kfet/kpsul.html | 127 ++------------------------------- 1 file changed, 6 insertions(+), 121 deletions(-) diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index f653ae61..e12e836d 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -157,9 +157,6 @@
-
-
- @@ -299,55 +296,6 @@ $(document).ready(function() { performOperations(); }); - // ----- - // Cancel operations - // ----- - - var cancelButton = $('#cancel_operations'); - var cancelForm = $('#cancel_form'); - - function cancelOperations(opes_array, password = '') { - if (lock == 1) - return false - lock = 1; - var data = { 'operations' : opes_array } - $.ajax({ - dataType: "json", - url : "{% url 'kfet.kpsul.cancel_operations' %}", - method : "POST", - data : data, - beforeSend: function ($xhr) { - $xhr.setRequestHeader("X-CSRFToken", csrftoken); - if (password != '') - $xhr.setRequestHeader("KFetPassword", password); - }, - - }) - .done(function(data) { - coolReset(); - lock = 0; - }) - .fail(function($xhr) { - var data = $xhr.responseJSON; - switch ($xhr.status) { - case 403: - requestAuth(data, function(password) { - cancelOperations(opes_array, password); - }, account_manager._$input_trigramme); - break; - case 400: - displayErrors(getErrorsHtml(data)); - break; - } - lock = 0; - }); - } - - // Event listeners - cancelButton.on('click', function() { - cancelOperations(); - }); - // ----- // Articles data // ----- @@ -625,29 +573,6 @@ $(document).ready(function() { formset_container.find('#id_form-'+opeindex+'-DELETE').prop('checked', true); } - // ----- - // History - // ----- - - var khistory = new KHistory(); - - function getHistory() { - var data = { - from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), - }; - $.ajax({ - dataType: "json", - url : "{% url 'kfet.history.json' %}", - method : "POST", - data : data, - }) - .done(function(data) { - for (var i=0; i 0) - cancelOperations(opes_to_cancel); - } - }); - // ----- // Synchronization // ----- @@ -781,29 +676,19 @@ $(document).ready(function() { socket.onmessage = function(e) { var data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); - for (var i=0; i Date: Sat, 18 Mar 2017 02:29:02 -0300 Subject: [PATCH 36/90] Add history to KPsul manager --- kfet/static/kfet/js/kpsul.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 1a6ca4b2..c11e78dc 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -5,6 +5,8 @@ class KPsulManager { this.account_manager = new AccountManager(this); this.checkout_manager = new CheckoutManager(this); this.article_manager = new ArticleManager(this); + this.history = new History(this); + this.lock = 0 ; } reset(soft) { From 34bb680570c3156419553110cd0aeb18e8ac618d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 02:31:52 -0300 Subject: [PATCH 37/90] Add History equivalent functions --- kfet/static/kfet/js/history.js | 231 +++++++++++---------------------- 1 file changed, 77 insertions(+), 154 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 008eb8d1..607b8147 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -16,171 +16,94 @@ class History { this.api_options = { 'from': moment().subtract(3, 'days').format('YYYY-MM-DD HH:mm:ss'), }; - } -} + this.display_options = {} ; -function KHistory(options={}) { - $.extend(this, KHistory.default_options, options); - - this.$container = $(this.container); - - this.reset = function() { - this.$container.html(''); - }; - - this.addOpeGroup = function(opegroup) { - var $day = this._getOrCreateDay(opegroup['at']); - var $opegroup = this._opeGroupHtml(opegroup); - - $day.after($opegroup); - - var trigramme = opegroup['on_acc_trigramme']; - var is_cof = opegroup['is_cof']; - var type = opegroup['type'] - switch (type) { - case 'opegroup': - for (var i=0; i 0) + that.cancel_operations(to_cancel); + } + }); + } + + //TODO: permission management in another class ? + cancel_operations(to_cancel, password='') { + if (kpsul.lock == 1) + return false; + kpsul.lock = 1; + + var data = { 'operations': to_cancel }; + + $.ajax({ + dataType: "json", + url : Urls['kfet.kpsul.cancel_operations'](), + method : "POST", + data : data, + beforeSend: function ($xhr) { + $xhr.setRequestHeader("X-CSRFToken", csrftoken); + if (password != '') + $xhr.setRequestHeader("KFetPassword", password); + }, + }) + .done(function(data) { + kpsul._env.coolReset(); + kpsul.lock = 0; + }) + .fail(function($xhr) { + var data = $xhr.responseJSON; + switch ($xhr.status) { + case 403: + requestAuth(data, function(password) { + cancelOperations(to_cancel, password); + }, kpsul.account_manager._$input_trigramme); + break; + case 400: + displayErrors(getErrorsHtml(data)); + break; + } + kpsul.lock = 0; }); } From 644b08973a7a6a4925f7ebf0ea4b51771eca32ce Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 02:32:27 -0300 Subject: [PATCH 38/90] Add websocket support for history --- kfet/static/kfet/js/history.js | 64 +++++++++++++++++-------- kfet/static/kfet/js/kfet.api.js | 29 ++++++++---- kfet/views.py | 83 ++++++++++++++++++++++----------- 3 files changed, 121 insertions(+), 55 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 607b8147..a0394b8a 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -107,28 +107,54 @@ class History { }); } - this.findOpe = function(id, type='ope') { - return this.$container.find('.ope').filter(function() { - return ($(this).data('id') == id && $(this).data('type') == type) - }); + add_node(data) { + var node = this.list.get_or_create(data, 0); + this.list.add_to_container(this._$container, node, this.templates, this.display_options); } - this.cancelOpeGroup = function(opegroup) { - var $opegroup = this.findOpeGroup(opegroup['id']); - var trigramme = $opegroup.find('.trigramme').text(); - var amount = amountDisplay( - parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme)); - $opegroup.find('.amount').text(amount); + update_node(type, id, update_data) { + var to_update = this.list.find(type, id); + if (!to_update) + return false; + + $.extend(to_update.content, update_data); + var $new_elt = to_update.content.display($(this.templates[type]), this.display_options); + + var $to_replace = this._$container.find('#'+type+'-'+id+'>:first-child'); + $to_replace.replaceWith($new_elt); + + return true; } -} + update_data(data) { + //Filter opegroups before ? + var opegroups = data['opegroups']; + var opes = data['opes']; -KHistory.default_options = { - container: '#history', - template_day: '
', - template_opegroup: '
', - template_transfergroup: '
', - template_ope: '
', - template_transfer: '
', - display_trigramme: true, + for (let ope of opes) { + if (ope['cancellation']) { + var update_data = { + 'canceled_at': ope.canceled_at, + 'canceled_by': ope.canceled_by, + }; + if (ope.type === 'ope') { + this.update_node('purchase', ope.id, update_data) + || this.update_node('specialope', ope.id, update_data); + } else if (ope.type === 'transfer') { + this.update_node('transfer', ope.id, update_data); + } + } + } + + for (let opegroup of opegroups) { + if (opegroup['cancellation']) { + var update_data = { 'amount': opegroup.amount }; + this.update_node('opegroup', opegroup.id, update_data); + } + + if (opegroup['add']) { + this.add_node(opegroup); + } + } + } } diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index c90d864d..8db9b0fe 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -654,11 +654,12 @@ class OperationGroup extends HistoryGroup { /** * Properties associated with an opegroup. - * @default {@link Models.HistoryGroup.props|HistoryGroup.props} + ['is_cof', 'trigramme'] + * @default {@link Models.HistoryGroup.props|HistoryGroup.props} + + * ['amount', 'is_cof', 'trigramme'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return HistoryGroup.props.concat(['is_cof', 'trigramme']); + return HistoryGroup.props.concat(['amount', 'is_cof', 'trigramme']); } /** @@ -984,19 +985,29 @@ class ModelForest { return $container; } + /** + * Renders node and adds it to given container.
+ * Assumes that the inserted node is the 'youngest'. + * @param {jQuery} $container + * @param {Models.TreeNode} node + * @param {Object} templates Templates to render each model + * @param {Object} [options] Options for element render method + */ add_to_container($container, node, templates, options) { var existing = node.parent ; var first_missing = node; - while (existing && !($container.find('#'+existing.type+'-'+existing.id))) { + while (existing && !($container.find('#'+existing.type+'-'+existing.content.id).length)) { first_missing = existing ; existing = existing.parent; } - var $to_insert = render_element(first_missing, templates, options); - var $insert_in = existing ? $container.find('#'+existing.type+'-'+existing.id) - : $container ; - $insert_in.prepend($to_insert); + var $to_insert = this.render_element(first_missing, templates, options); + if (existing) { + $container.find('#'+existing.type+'-'+existing.content.id+'>:first-child').after($to_insert); + } else { + $container.prepend($to_insert); + } } /** @@ -1019,7 +1030,7 @@ class ModelForest { callback(node) ; for (let child of node.children) - callback(child); + recurse(child); } for (let root of this.roots) @@ -1586,7 +1597,7 @@ class OperationFormatter extends Formatter { */ static prop_canceled(a) { if (a.canceled_at) { - cancel = 'Annulé'; + var cancel = 'Annulé'; if (a.canceled_by) cancel += ' par '+a.canceled_by; diff --git a/kfet/views.py b/kfet/views.py index 09755bbf..27de08f8 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1053,29 +1053,57 @@ def kpsul_perform_operations(request): websocket_data['opegroups'] = [{ 'add': True, 'type': 'opegroup', - 'id': operationgroup.pk, - 'amount': operationgroup.amount, - 'checkout__name': operationgroup.checkout.name, - 'at': operationgroup.at, - 'is_cof': operationgroup.is_cof, - 'comment': operationgroup.comment, - 'valid_by__trigramme': ( operationgroup.valid_by and - operationgroup.valid_by.trigramme or None), - 'on_acc__trigramme': operationgroup.on_acc.trigramme, - 'opes': [], + 'content': { + 'id': operationgroup.pk, + 'amount': operationgroup.amount, + 'at': operationgroup.at, + 'is_cof': operationgroup.is_cof, + 'comment': operationgroup.comment, + 'valid_by': (operationgroup.valid_by and + operationgroup.valid_by.trigramme or None), + 'trigramme': operationgroup.on_acc.trigramme, + }, + 'parent': { + 'type': 'day', + 'content': { + 'id': operationgroup.at.strftime('%Y%m%d'), + 'date': operationgroup.at + }, + 'child_sort': 'opegroup', + }, + 'children': [], }] - for operation in operations: + for ope in operations: ope_data = { - 'id': operation.pk, 'type': operation.type, 'amount': operation.amount, - 'addcost_amount': operation.addcost_amount, - 'addcost_for__trigramme': is_addcost and addcost_for.trigramme or None, - 'is_checkout': operation.is_checkout, - 'article__name': operation.article and operation.article.name or None, - 'article_nb': operation.article_nb, - 'group_id': operationgroup.pk, - 'canceled_by__trigramme': None, 'canceled_at': None, + 'content': { + 'id': ope.id, + 'amount': ope.amount, + 'canceled_at': ope.canceled_at, + 'canceled_by': + ope.canceled_by and ope.canceled_by.trigramme or None, + 'is_cof': operationgroup.is_cof, + 'trigramme': ( + operationgroup.on_acc and + operationgroup.on_acc.trigramme or None), + }, } - websocket_data['opegroups'][0]['opes'].append(ope_data) + + if ope.type == Operation.PURCHASE: + ope_data['type'] = 'purchase' + ope_data['content'].update({ + 'article_name': ope.article.name, + 'article_nb': ope.article_nb, + 'addcost_amount': ope.addcost_amount, + 'addcost_for': + ope.addcost_for and ope.addcost_for.trigramme or None, + }) + else: + ope_data['type'] = 'specialope' + ope_data['content'].update({ + 'type': ope.type, + 'is_checkout': ope.is_checkout, + }) + websocket_data['opegroups'][0]['children'].append(ope_data) # Need refresh from db cause we used update on queryset operationgroup.checkout.refresh_from_db() websocket_data['checkouts'] = [{ @@ -1103,12 +1131,13 @@ def kpsul_cancel_operations(request): # Checking if BAD REQUEST (opes_pk not int or not existing) try: # Set pour virer les doublons - opes_post = set(map(lambda s: int(s.split()[1]), - filter(lambda s: s.split()[0] == 'ope', + opes_post = set(map(lambda s: int(s.split('-')[1]), + filter(lambda s: s.split('-')[0] == 'purchase' + or s.split('-')[0] == 'specialope', request.POST.getlist('operations[]', [])))) transfers_post = \ - set(map(lambda s: int(s.split()[1]), - filter(lambda s: s.split()[0] == 'transfer', + set(map(lambda s: int(s.split('-')[1]), + filter(lambda s: s.split('-')[0] == 'transfer', request.POST.getlist('operations[]', [])))) except ValueError: return JsonResponse(data, status=400) @@ -1284,13 +1313,13 @@ def kpsul_cancel_operations(request): 'amount': opegroup['amount'], 'is_cof': opegroup['is_cof'], }) - canceled_by__trigramme = canceled_by and canceled_by.trigramme or None + canceled_by = canceled_by and canceled_by.trigramme or None for ope in opes: websocket_data['opes'].append({ 'cancellation': True, 'type': 'ope', 'id': ope, - 'canceled_by__trigramme': canceled_by__trigramme, + 'canceled_by': canceled_by, 'canceled_at': canceled_at, }) for ope in transfers: @@ -1298,7 +1327,7 @@ def kpsul_cancel_operations(request): 'cancellation': True, 'type': 'transfer', 'id': ope, - 'canceled_by__trigramme': canceled_by__trigramme, + 'canceled_by': canceled_by, 'canceled_at': canceled_at, }) From 9e905b0f8bc20cb490591d3d0e70dc5089c216c8 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 02:40:34 -0300 Subject: [PATCH 39/90] Remove kpsul dependence from history (oops) --- kfet/static/kfet/js/history.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index a0394b8a..da96dce7 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -18,6 +18,7 @@ class History { }; this.display_options = {} ; + this.lock = 0 ; this._init_selection(); this._init_events(); @@ -70,9 +71,18 @@ class History { //TODO: permission management in another class ? cancel_operations(to_cancel, password='') { - if (kpsul.lock == 1) + if (this.lock == 1) return false; - kpsul.lock = 1; + this.lock = 1; + + if (window.kpsul) { + var on_success = kpsul.env.coolReset; + var focus_next = kpsul.account_manager._$input_trigramme; + } else { + var on_success = () => this._$container.find('.ui-selected') + .removeClass('.ui-selected) ; + var focus_next = undefined; + } var data = { 'operations': to_cancel }; @@ -88,8 +98,8 @@ class History { }, }) .done(function(data) { - kpsul._env.coolReset(); - kpsul.lock = 0; + on_success(); + this.lock = 0; }) .fail(function($xhr) { var data = $xhr.responseJSON; @@ -97,13 +107,13 @@ class History { case 403: requestAuth(data, function(password) { cancelOperations(to_cancel, password); - }, kpsul.account_manager._$input_trigramme); + }, focus_next); break; case 400: displayErrors(getErrorsHtml(data)); break; } - kpsul.lock = 0; + this.lock = 0; }); } From 1d532616b73909e218a00147fccb126eefbb3139 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 02:47:30 -0300 Subject: [PATCH 40/90] Fix bugs introduced by previous commit --- kfet/static/kfet/js/history.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index da96dce7..6493bb5c 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -74,13 +74,14 @@ class History { if (this.lock == 1) return false; this.lock = 1; + var that = this; if (window.kpsul) { - var on_success = kpsul.env.coolReset; + var on_success = kpsul._env.coolReset; var focus_next = kpsul.account_manager._$input_trigramme; } else { var on_success = () => this._$container.find('.ui-selected') - .removeClass('.ui-selected) ; + .removeClass('.ui-selected') ; var focus_next = undefined; } @@ -99,7 +100,7 @@ class History { }) .done(function(data) { on_success(); - this.lock = 0; + that.lock = 0; }) .fail(function($xhr) { var data = $xhr.responseJSON; @@ -113,7 +114,7 @@ class History { displayErrors(getErrorsHtml(data)); break; } - this.lock = 0; + that.lock = 0; }); } From b8a307b4a620b08cc5cc75ca9b13c55abf5d0ae9 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 14:05:11 -0300 Subject: [PATCH 41/90] Add support for kfet/history page --- kfet/static/kfet/js/history.js | 26 ++++--- kfet/static/kfet/js/kfet.api.js | 20 +++-- kfet/static/kfet/js/kfet.js | 30 ++++++++ kfet/static/kfet/js/kpsul.js | 3 +- kfet/templates/kfet/history.html | 128 ++++--------------------------- kfet/templates/kfet/kpsul.html | 23 +----- 6 files changed, 76 insertions(+), 154 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 6493bb5c..3719042e 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,6 +1,6 @@ class History { - constructor() { + constructor(api_options, display_options) { this.templates = { 'purchase': '
', 'specialope': '
', @@ -11,14 +11,12 @@ class History { }; this._$container = $('#history'); + this._$nb_opes = $('#nb_opes'); this.list = new OperationList(); - this.api_options = { - 'from': moment().subtract(3, 'days').format('YYYY-MM-DD HH:mm:ss'), - }; - - this.display_options = {} ; - this.lock = 0 ; + this.api_options = + api_options || { from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), }; + this.display_options = display_options || {}; this._init_selection(); this._init_events(); @@ -26,6 +24,8 @@ class History { display() { this.list.display(this._$container, this.templates, this.display_options); + var nb_opes = this._$container.find('.ope[canceled="false"]').length; + $('#nb_opes').text(nb_opes); } reset() { @@ -70,10 +70,11 @@ class History { } //TODO: permission management in another class ? + //TODO: idem for confirmation cancel_operations(to_cancel, password='') { - if (this.lock == 1) + if (lock == 1) return false; - this.lock = 1; + lock = 1; var that = this; if (window.kpsul) { @@ -100,7 +101,7 @@ class History { }) .done(function(data) { on_success(); - that.lock = 0; + lock = 0; }) .fail(function($xhr) { var data = $xhr.responseJSON; @@ -114,7 +115,7 @@ class History { displayErrors(getErrorsHtml(data)); break; } - that.lock = 0; + lock = 0; }); } @@ -167,5 +168,8 @@ class History { this.add_node(opegroup); } } + + var nb_opes = this._$container.find('.ope[canceled="false"]').length; + $('#nb_opes').text(nb_opes); } } diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 8db9b0fe..1b6e40fc 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1087,12 +1087,17 @@ class APIModelForest extends ModelForest { api_options['format'] = 'json'; - $.getJSON(this.constructor.url_model, api_options) - .done(function(json, textStatus, jqXHR) { - that.from(json); - on_success(json, textStatus, jqXHR); - }) - .fail(on_error); + $.ajax({ + dataType: "json", + url : this.constructor.url_model, + method : "POST", + data : api_options, + }) + .done(function(json, textStatus, jqXHR) { + that.from(json); + on_success(json, textStatus, jqXHR); + }) + .fail(on_error); } } @@ -1244,6 +1249,9 @@ class Formatter { var props = options.override_props ? options.props : this.props.concat(options.props); var attrs = options.override_attrs ? options.attrs : this.attrs.concat(options.attrs); + props = options.remove_props ? props.diff(options.remove_props) : props; + attrs = options.remove_attrs ? props.diff(options.remove_attrs) : attrs; + var prefix_prop = options.prefix_prop !== undefined ? options.prefix_prop : '.'; var prefix_attr = options.prefix_attr !== undefined ? options.prefix_attr : ''; diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index aa58d1ce..9c5326c7 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -24,6 +24,36 @@ String.prototype.isValidTri = function() { return pattern.test(this); } +/** + * Array method + * @global + * @return {[]} Array elements not in given argument + */ +// Note : we define it this way to avoid problems in for loops +Object.defineProperty(Array.prototype, 'diff', { + enumerable: false, + configurable: true, + value: function(a) { + return this.filter(function (elt) { + return a.indexOf(elt) < 0 ; + }); + } +}); + + +/** + * Checks if given argument is float ; + * if not, parses given argument to float value. + * @global + * @return {float} + */ +function floatCheck(v) { + if (typeof v === 'number') + return v; + return Number.parseFloat(v); +} + + $(document).ready(function() { $(window).scroll(function() { diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index c11e78dc..90ff988e 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -5,8 +5,7 @@ class KPsulManager { this.account_manager = new AccountManager(this); this.checkout_manager = new CheckoutManager(this); this.article_manager = new ArticleManager(this); - this.history = new History(this); - this.lock = 0 ; + this.history = new History(); } reset(soft) { diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 78f8ddea..64ccf23d 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -15,7 +15,9 @@ + + {% endblock %} @@ -61,11 +63,12 @@ diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index e12e836d..45b50905 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -167,27 +167,6 @@ {% csrf_token %} + + {% endblock %} @@ -23,6 +25,8 @@
+
+
transferts
@@ -46,146 +50,31 @@ From 7a000961709f757597723710094b4f3f05876e30 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 18 Mar 2017 22:48:50 -0300 Subject: [PATCH 44/90] Add support for account_read history --- kfet/templates/kfet/account_read.html | 38 +++++++++++---------------- kfet/templates/kfet/transfers.html | 5 +++- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 2d91197b..7618375c 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -4,11 +4,17 @@ {% load l10n %} {% block extra_head %} + + + + + + {% endblock %} @@ -62,33 +68,19 @@ diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index d61e69fe..9b50aaa2 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -55,7 +55,10 @@ $(document).ready(function() { // Lock to avoid multiple requests window.lock = 0; - var history = new History({'transfersonly': true}, {}); + var history = new History(); + history.api_options = { + 'transfersonly': true, + }; // ----- // Synchronization From 66c5a6953ce24303de7d1ab10ddb24e21c628140 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 19 Mar 2017 04:29:54 +0100 Subject: [PATCH 45/90] Improve websocket filter for special history pages --- kfet/static/kfet/js/history.js | 29 +++++++++++++++++++++++---- kfet/templates/kfet/account_read.html | 15 +++++++++++++- kfet/templates/kfet/history.html | 2 +- kfet/views.py | 5 ++++- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 33e9a318..ecf86878 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -138,6 +138,30 @@ class History { return true; } + is_valid(opegroup) { + var options = this.api_options; + + if (options.from && dateUTCToParis(opegroup.content.at).isBefore(moment(options.from))) + return false; + + if (options.to && dateUTCToParis(opegroup.content.at).isAfter(moment(options.to))) + return false; + + if (options.transfersonly && opegroup.type === 'opegroup') + return false; + + if (options.accounts && options.accounts.length && + options.accounts.indexOf(opegroup.content.account_id) < 0) + return false; + + if (options.checkouts && options.checkouts.length && + (opegroup.type === 'transfergroup' || + options.checkouts.indexOf(opegroup.content.checkout_id) < 0)) + return false; + + return true; + } + update_data(data) { //Filter opegroups before ? var opegroups = data['opegroups']; @@ -164,10 +188,7 @@ class History { this.update_node('opegroup', opegroup.id, update_data); } - if (opegroup['add']) { - if (opegroup.type === 'opegroup' && this.api_options.transfersonly) - return; - + if (opegroup['add'] && this.is_valid(opegroup)) { this.add_node(opegroup); } } diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 7618375c..7f3c951f 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -78,7 +78,20 @@ $(document).ready(function() { 'accounts': [{{ account.pk }}], }; - //TODO: add ws support ? + // ----- + // Synchronization + // ----- + + var websocket_msg_default = {'opegroups':[],'opes':[]} + + var websocket_protocol = window.location.protocol == 'https:' ? 'wss' : 'ws'; + var location_host = window.location.host; + var location_url = window.location.pathname.startsWith('/gestion/') ? location_host + '/gestion' : location_host; + var socket = new ReconnectingWebSocket(websocket_protocol+"://" + location_url + "/ws/k-fet/k-psul/"); + socket.onmessage = function(e) { + var data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); + history.update_data(data); + } history.reset(); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 0b6841ae..34d2ac15 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -115,7 +115,7 @@ $(document).ready(function() { timeZone : 'Europe/Paris', format : 'YYYY-MM-DD HH:mm', stepping : 5, - defaultDate: moment(), + defaultDate: moment().add(5, 'minutes'), // workaround for 'stepping' rounding locale : 'fr', showTodayButton: true, }); diff --git a/kfet/views.py b/kfet/views.py index 27de08f8..0354d655 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1060,8 +1060,11 @@ def kpsul_perform_operations(request): 'is_cof': operationgroup.is_cof, 'comment': operationgroup.comment, 'valid_by': (operationgroup.valid_by and - operationgroup.valid_by.trigramme or None), + operationgroup.valid_by.trigramme or None), 'trigramme': operationgroup.on_acc.trigramme, + # Used to filter websocket updates + 'account_id': operationgroup.on_acc.pk, + 'checkout_id': operationgroup.checkout.pk, }, 'parent': { 'type': 'day', From c99e4f26d01e399b456506ce8d91fff38bb2c8ce Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 19 Mar 2017 04:59:55 +0100 Subject: [PATCH 46/90] Move history initialisation as Config.reset callback --- kfet/templates/kfet/account_read.html | 2 +- kfet/templates/kfet/history.html | 3 ++- kfet/templates/kfet/transfers.html | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 7f3c951f..98ae755f 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -93,7 +93,7 @@ $(document).ready(function() { history.update_data(data); } - history.reset(); + Config.reset(history.reset.bind(history)); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 34d2ac15..c227a235 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -151,7 +151,8 @@ $(document).ready(function() { } - updateHistory(); + + Config.reset(updateHistory); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 9b50aaa2..2c878f3c 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -77,7 +77,7 @@ $(document).ready(function() { } - history.reset(); + Config.reset(history.reset.bind(history)); }); From 1fcd53d780c2aa0efe5a1dceb8ea631154ab6ef7 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 20 Mar 2017 00:36:42 -0300 Subject: [PATCH 47/90] Continue renaming node.type to node.modelname --- kfet/static/kfet/js/history.js | 8 ++++---- kfet/views.py | 28 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index ecf86878..7a842822 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -147,7 +147,7 @@ class History { if (options.to && dateUTCToParis(opegroup.content.at).isAfter(moment(options.to))) return false; - if (options.transfersonly && opegroup.type === 'opegroup') + if (options.transfersonly && opegroup.modelname === 'opegroup') return false; if (options.accounts && options.accounts.length && @@ -155,7 +155,7 @@ class History { return false; if (options.checkouts && options.checkouts.length && - (opegroup.type === 'transfergroup' || + (opegroup.modelname === 'transfergroup' || options.checkouts.indexOf(opegroup.content.checkout_id) < 0)) return false; @@ -173,10 +173,10 @@ class History { 'canceled_at': ope.canceled_at, 'canceled_by': ope.canceled_by, }; - if (ope.type === 'ope') { + if (ope.modelname === 'ope') { this.update_node('purchase', ope.id, update_data) || this.update_node('specialope', ope.id, update_data); - } else if (ope.type === 'transfer') { + } else if (ope.modelname === 'transfer') { this.update_node('transfer', ope.id, update_data); } } diff --git a/kfet/views.py b/kfet/views.py index 48579a01..73edc4bd 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1052,7 +1052,7 @@ def kpsul_perform_operations(request): websocket_data = {} websocket_data['opegroups'] = [{ 'add': True, - 'type': 'opegroup', + 'modelname': 'opegroup', 'content': { 'id': operationgroup.pk, 'amount': operationgroup.amount, @@ -1067,7 +1067,7 @@ def kpsul_perform_operations(request): 'checkout_id': operationgroup.checkout.pk, }, 'parent': { - 'type': 'day', + 'modelname': 'day', 'content': { 'id': operationgroup.at.strftime('%Y%m%d'), 'date': operationgroup.at @@ -1092,7 +1092,7 @@ def kpsul_perform_operations(request): } if ope.type == Operation.PURCHASE: - ope_data['type'] = 'purchase' + ope_data['modelname'] = 'purchase' ope_data['content'].update({ 'article_name': ope.article.name, 'article_nb': ope.article_nb, @@ -1101,7 +1101,7 @@ def kpsul_perform_operations(request): ope.addcost_for and ope.addcost_for.trigramme or None, }) else: - ope_data['type'] = 'specialope' + ope_data['modelname'] = 'specialope' ope_data['content'].update({ 'type': ope.type, 'is_checkout': ope.is_checkout, @@ -1320,7 +1320,7 @@ def kpsul_cancel_operations(request): for ope in opes: websocket_data['opes'].append({ 'cancellation': True, - 'type': 'ope', + 'modelname': 'ope', 'id': ope, 'canceled_by': canceled_by, 'canceled_at': canceled_at, @@ -1328,7 +1328,7 @@ def kpsul_cancel_operations(request): for ope in transfers: websocket_data['opes'].append({ 'cancellation': True, - 'type': 'transfer', + 'modelname': 'transfer', 'id': ope, 'canceled_by': canceled_by, 'canceled_at': canceled_at, @@ -1432,7 +1432,7 @@ def history_json(request): opegroup_list = [] for opegroup in opegroups: opegroup_dict = { - 'type': 'opegroup', + 'modelname': 'opegroup', 'content': { 'id': opegroup.id, 'amount': opegroup.amount, @@ -1443,7 +1443,7 @@ def history_json(request): opegroup.on_acc and opegroup.on_acc.trigramme or None, }, 'parent': { - 'type': 'day', + 'modelname': 'day', 'content': { 'id': opegroup.at.strftime('%Y%m%d'), 'date': opegroup.at @@ -1472,7 +1472,7 @@ def history_json(request): ope.canceled_by and ope.canceled_by.trigramme or None) if ope.type == Operation.PURCHASE: - ope_dict['type'] = 'purchase' + ope_dict['modelname'] = 'purchase' ope_dict['content'].update({ 'article_name': ope.article.name, 'article_nb': ope.article_nb, @@ -1481,7 +1481,7 @@ def history_json(request): ope.addcost_for and ope.addcost_for.trigramme or None, }) else: - ope_dict['type'] = 'specialope' + ope_dict['modelname'] = 'specialope' ope_dict['content'].update({ 'type': ope.type, 'is_checkout': ope.is_checkout, @@ -1493,14 +1493,14 @@ def history_json(request): for transfergroup in transfergroups: if transfergroup.filtered_transfers: transfergroup_dict = { - 'type': 'transfergroup', + 'modelname': 'transfergroup', 'content': { 'id': transfergroup.id, 'at': transfergroup.at, 'comment': transfergroup.comment, }, 'parent': { - 'type': 'day', + 'modelname': 'day', 'content': { 'id': transfergroup.at.strftime('%Y%m%d'), 'date': transfergroup.at @@ -1517,7 +1517,7 @@ def history_json(request): for transfer in transfergroup.filtered_transfers: transfer_dict = { - 'type': 'transfer', + 'modelname': 'transfer', 'content': { 'id': transfer.id, 'amount': transfer.amount, @@ -1706,7 +1706,7 @@ def perform_transfers(request): websocket_data = {} websocket_data['opegroups'] = [{ 'add': True, - 'type': 'transfergroup', + 'modelname': 'transfergroup', 'id': transfergroup.pk, 'at': transfergroup.at, 'comment': transfergroup.comment, From aa6a50a6e7bbbfc7f29cb1f439c98a4955c932b0 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 24 Mar 2017 23:57:27 -0300 Subject: [PATCH 48/90] Simplify JS-Python interface for cancel_ops --- kfet/static/kfet/js/history.js | 15 +++++++++------ kfet/views.py | 12 ++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 7a842822..e23d4c6a 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -59,11 +59,16 @@ class History { $(document).on('keydown', function(e) { if (e.keyCode == 46) { //DEL key ; we delete the selected operations (if any) - var to_cancel = []; + var to_cancel = {'transfers': [], 'opes': [],}; that._$container.find('.ope.ui-selected').each(function() { - to_cancel.push($(this).parent().attr('id')) ; + var [type, id] = $(this).parent().attr('id').split('-'); + + if (type === 'transfer') + to_cancel['transfers'].push(id); + else + to_cancel['opes'].push(id); }); - if (to_cancel.length > 0) + if (to_cancel['opes'].length > 0 || to_cancel['transfers'].length > 0) that.cancel_operations(to_cancel); } }); @@ -86,13 +91,11 @@ class History { var focus_next = undefined; } - var data = { 'operations': to_cancel }; - $.ajax({ dataType: "json", url : Urls['kfet.kpsul.cancel_operations'](), method : "POST", - data : data, + data : to_cancel, beforeSend: function ($xhr) { $xhr.setRequestHeader("X-CSRFToken", csrftoken); if (password != '') diff --git a/kfet/views.py b/kfet/views.py index 73edc4bd..c901bff7 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1134,14 +1134,10 @@ def kpsul_cancel_operations(request): # Checking if BAD REQUEST (opes_pk not int or not existing) try: # Set pour virer les doublons - opes_post = set(map(lambda s: int(s.split('-')[1]), - filter(lambda s: s.split('-')[0] == 'purchase' - or s.split('-')[0] == 'specialope', - request.POST.getlist('operations[]', [])))) - transfers_post = \ - set(map(lambda s: int(s.split('-')[1]), - filter(lambda s: s.split('-')[0] == 'transfer', - request.POST.getlist('operations[]', [])))) + opes_post = set(map(lambda s: int(s), + request.POST.getlist('opes[]', []))) + transfers_post = set(map(lambda s: int(s), + request.POST.getlist('transfers[]', []))) except ValueError: return JsonResponse(data, status=400) From a1c976185ca3a33a6aceaf4a942b5869a840bbd6 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sat, 25 Mar 2017 13:44:35 -0300 Subject: [PATCH 49/90] Fix transfer sort --- kfet/static/kfet/js/kfet.api.js | 4 ++-- kfet/views.py | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 31221a1f..b1e8a2e7 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -635,8 +635,8 @@ class HistoryGroup extends ModelObject { */ static compare(a, b) { //Groups are sorted by most recent first - if (a.at < b.at) return 1; - else if (a.at > b.at) return -1; + if (a.at.isBefore(b.at)) return 1; + else if (a.at.isAfter(b.at)) return -1; else return 0; } diff --git a/kfet/views.py b/kfet/views.py index bdb5c1e1..de3ac145 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1502,6 +1502,7 @@ def history_json(request): 'id': transfergroup.at.strftime('%Y%m%d'), 'date': transfergroup.at }, + 'child_sort': 'transfergroup', }, 'children': [], } @@ -1604,14 +1605,10 @@ class SettingsUpdate(SuccessMessageMixin, UpdateView): # Transfer views # ----- + @teamkfet_required def transfers(request): - transfergroups = (TransferGroup.objects - .prefetch_related('transfers') - .order_by('-at')) - return render(request, 'kfet/transfers.html', { - 'transfergroups': transfergroups, - }) + return render(request, 'kfet/transfers.html') @teamkfet_required def transfers_create(request): From a173be4f7d8996fa7f281042f435daf7b4bfedaf Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 3 Apr 2017 21:14:06 -0300 Subject: [PATCH 50/90] Use api_with_auth in history --- kfet/static/kfet/js/history.js | 40 +++++----------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index e23d4c6a..c95b3575 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -74,14 +74,7 @@ class History { }); } - //TODO: permission management in another class ? - //TODO: idem for confirmation cancel_operations(to_cancel, password='') { - if (lock == 1) - return false; - lock = 1; - var that = this; - if (window.kpsul) { var on_success = kpsul._env.coolReset; var focus_next = kpsul.account_manager._$input_trigramme; @@ -91,35 +84,12 @@ class History { var focus_next = undefined; } - $.ajax({ - dataType: "json", - url : Urls['kfet.kpsul.cancel_operations'](), - method : "POST", - data : to_cancel, - beforeSend: function ($xhr) { - $xhr.setRequestHeader("X-CSRFToken", csrftoken); - if (password != '') - $xhr.setRequestHeader("KFetPassword", password); - }, + api_with_auth({ + url: Urls['kfet.kpsul.cancel_operations'](), + data: to_cancel, + on_success: on_success, + focus_next: focus_next, }) - .done(function(data) { - on_success(); - lock = 0; - }) - .fail(function($xhr) { - var data = $xhr.responseJSON; - switch ($xhr.status) { - case 403: - requestAuth(data, function(password) { - cancelOperations(to_cancel, password); - }, focus_next); - break; - case 400: - displayErrors(getErrorsHtml(data)); - break; - } - lock = 0; - }); } add_node(data) { From 20b7156e1fb405cf4d4d7bbc25fd5d800603a272 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 3 Apr 2017 21:18:07 -0300 Subject: [PATCH 51/90] Improve type display --- kfet/static/kfet/js/kfet.api.js | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index ca3d1003..79893a83 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -746,6 +746,19 @@ class SpecialOperation extends Operation { return Operation.props.concat(['type', 'is_checkout']); } + /** + * Verbose names for operation types + * @type {Object} + */ + static get verbose_types() { + return { + 'deposit': 'Charge', + 'withdraw': 'Retrait', + 'edit': 'Édition', + 'initial': 'Initial', + }; + } + /** * Default values for SpecialOperation model instances * @default {@link Models.Operation.default_data|Operation.default_data} + {'type': '', 'is_checkout': false} @@ -1606,25 +1619,7 @@ class SpecialOpeFormatter extends OperationFormatter { } static prop_infos2(a) { - if (!a.is_checkout) - return 'Édition'; - - switch (a.type) { - case 'initial': - return 'Initial'; - break; - - case 'deposit': - return 'Charge'; - break; - - case 'withdraw': - return 'Retrait'; - break; - - default: - return ''; - } + return SpecialOperation.verbose_types(a.type) || '' ; } } From ec9f47274ac28cdf4f3244d309818e10a3792de9 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 3 Apr 2017 21:23:54 -0300 Subject: [PATCH 52/90] Fix WS update functions --- kfet/templates/kfet/history.html | 2 +- kfet/templates/kfet/kpsul.html | 27 ++++++++++++++------------- kfet/templates/kfet/transfers.html | 1 - 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 5bcb960b..6d1b7497 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -135,7 +135,7 @@ $(document).ready(function() { // Synchronization // ----- - OperationWebSocket.add_handler(history.update_data); + OperationWebSocket.add_handler(data => history.update_data(data)); diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index a15546bb..e8bc3c39 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -667,19 +667,6 @@ $(document).ready(function() { } - // ----- - // Synchronization - // ----- - - OperationWebSocket.add_handler(kpsul.history.update_data); - OperationWebSocket.add_handler(kpsul.article_manager.update_data); - OperationWebSocket.add_handler(function(data) { - if (data['addcost']) { - Config.set('addcost_for', data['addcost']['for']); - Config.set('addcost_amount', data['addcost']['amount']); - displayAddcost(); - } - }); // ----- // General @@ -789,6 +776,20 @@ $(document).ready(function() { window.kpsul = new KPsulManager(env); + // ----- + // Synchronization + // ----- + + OperationWebSocket.add_handler(data => kpsul.history.update_data(data)); + OperationWebSocket.add_handler(data => kpsul.article_manager.update_data(data)); + OperationWebSocket.add_handler(function(data) { + if (data['addcost']) { + Config.set('addcost_for', data['addcost']['for']); + Config.set('addcost_amount', data['addcost']['amount']); + displayAddcost(); + } + }); + hardReset(); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 2c878f3c..15a546a9 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -11,7 +11,6 @@ - {% endblock %} From 514f1da6dfa11d39a719820a939e80eb4921f920 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 3 Apr 2017 21:26:33 -0300 Subject: [PATCH 53/90] Fix SpecialOpeFormatter --- kfet/static/kfet/js/kfet.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 79893a83..0020b636 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1619,7 +1619,7 @@ class SpecialOpeFormatter extends OperationFormatter { } static prop_infos2(a) { - return SpecialOperation.verbose_types(a.type) || '' ; + return SpecialOperation.verbose_types[a.type] || '' ; } } From 360c442a4ec5db91bd6fe6e1e14808f1a5ce42f8 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 09:24:27 -0300 Subject: [PATCH 54/90] Remove useless class --- kfet/templates/kfet/history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 6d1b7497..d4e49e9a 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -52,7 +52,7 @@

Opérations

-
+
From e051631a34bdaf91188e437f1276db31bc434499 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 09:28:32 -0300 Subject: [PATCH 55/90] Use WebSocket classes --- kfet/templates/kfet/account_read.html | 12 +----------- kfet/templates/kfet/transfers.html | 13 +------------ kfet/views.py | 2 +- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 7decefc2..3974e0a2 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -13,7 +13,6 @@ - {% if account.user == request.user %} @@ -115,16 +114,7 @@ $(document).ready(function() { // Synchronization // ----- - var websocket_msg_default = {'opegroups':[],'opes':[]} - - var websocket_protocol = window.location.protocol == 'https:' ? 'wss' : 'ws'; - var location_host = window.location.host; - var location_url = window.location.pathname.startsWith('/gestion/') ? location_host + '/gestion' : location_host; - var socket = new ReconnectingWebSocket(websocket_protocol+"://" + location_url + "/ws/k-fet/k-psul/"); - socket.onmessage = function(e) { - var data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); - history.update_data(data); - } + OperationWebSocket.add_handler((data) => history.update_data(data)); Config.reset(history.reset.bind(history)); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 15a546a9..088228d8 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -63,18 +63,7 @@ $(document).ready(function() { // Synchronization // ----- - - var websocket_msg_default = {'opes':[]} - - var websocket_protocol = window.location.protocol == 'https:' ? 'wss' : 'ws'; - var location_host = window.location.host; - var location_url = window.location.pathname.startsWith('/gestion/') ? location_host + '/gestion' : location_host; - var socket = new ReconnectingWebSocket(websocket_protocol+"://" + location_url + "/ws/k-fet/k-psul/"); - socket.onmessage = function(e) { - var data = $.extend({}, websocket_msg_default, JSON.parse(e.data)); - history.update_data(data); - - } + OperationWebSocket.add_handler((data) => history.update_data(data)); Config.reset(history.reset.bind(history)); }); diff --git a/kfet/views.py b/kfet/views.py index 7ff4a59e..b7d47022 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -17,7 +17,7 @@ from django.http import JsonResponse, Http404 from django.forms import formset_factory from django.forms.models import model_to_dict from django.db import transaction -from django.db.models import F, Sum, Prefetch, Count +from django.db.models import Q, 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 7ec7ed26964f06aa760bc15a0ab8a0d62d98784d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 09:53:12 -0300 Subject: [PATCH 56/90] Rename History class --- kfet/static/kfet/js/history.js | 2 +- kfet/static/kfet/js/kpsul.js | 2 +- kfet/templates/kfet/account_read.html | 2 +- kfet/templates/kfet/history.html | 2 +- kfet/templates/kfet/transfers.html | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index c95b3575..9f6a31ee 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,4 +1,4 @@ -class History { +class KHistory { constructor(api_options, display_options) { this.templates = { diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 1424914a..eade395c 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -5,7 +5,7 @@ class KPsulManager { this.account_manager = new AccountManager(this); this.checkout_manager = new CheckoutManager(this); this.article_manager = new ArticleManager(this); - this.history = new History(); + this.history = new KHistory(); } reset(soft) { diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 3974e0a2..791c7512 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -105,7 +105,7 @@ $(document).ready(function() { // Lock to avoid multiple requests window.lock = 0; - var history = new History(); + var history = new KHistory(); history.api_options = { 'accounts': [{{ account.pk }}], }; diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index d4e49e9a..04cfd062 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -65,7 +65,7 @@ $(document).ready(function() { 'use strict'; - var history = new History(); + var history = new KHistory(); var $from_date = $('#from_date'); var $to_date = $('#to_date'); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 088228d8..b8bf8c70 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -54,7 +54,7 @@ $(document).ready(function() { // Lock to avoid multiple requests window.lock = 0; - var history = new History(); + var history = new KHistory(); history.api_options = { 'transfersonly': true, }; From 8d13c0a4bb01bb85e60fa7874da19f24dde75a2b Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 10:59:59 -0300 Subject: [PATCH 57/90] Add fetch method --- kfet/static/kfet/js/history.js | 29 ++++++++++++++++++--------- kfet/static/kfet/js/kpsul.js | 1 + kfet/templates/kfet/account_read.html | 7 ++----- kfet/templates/kfet/history.html | 3 +-- kfet/templates/kfet/kpsul.html | 2 +- kfet/templates/kfet/transfers.html | 5 +---- kfet/views.py | 10 ++++----- 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 9f6a31ee..01ca1699 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,6 +1,10 @@ class KHistory { + + static get default_options() { + return { from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), }; + } - constructor(api_options, display_options) { + constructor(display_options) { this.templates = { 'purchase': '
', 'specialope': '
', @@ -14,26 +18,33 @@ class KHistory { this._$nb_opes = $('#nb_opes'); this.list = new OperationList(); - this.api_options = - api_options || { from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), }; + this.display_options = display_options || {}; this._init_selection(); this._init_events(); } + fetch(api_options) { + this._$container.html(''); + this.list.clear(); + + if (api_options) + this.api_options = api_options; + else if (!this.api_options) + this.api_options = this.constructor.default_options; + + this.list.fromAPI(this.api_options) + .done( () => this.display() ); + + } + display() { this.list.display(this._$container, this.templates, this.display_options); var nb_opes = this._$container.find('.ope[canceled="false"]').length; $('#nb_opes').text(nb_opes); } - reset() { - this._$container.html(''); - this.list.clear(); - this.list.fromAPI(this.api_options, this.display.bind(this), $.noop); - } - _init_selection() { this._$container.selectable({ filter: 'div.opegroup, div.ope', diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 81ee2341..65f6fad8 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -24,6 +24,7 @@ class KPsulManager { if (!soft) { this.checkout_manager.reset(); this.article_manager.reset_data(); + this.history.fetch(); } } diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 791c7512..b11a2cb0 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -105,10 +105,7 @@ $(document).ready(function() { // Lock to avoid multiple requests window.lock = 0; - var history = new KHistory(); - history.api_options = { - 'accounts': [{{ account.pk }}], - }; + var history = new KHistory({'remove_props': ['trigramme']}); // ----- // Synchronization @@ -116,7 +113,7 @@ $(document).ready(function() { OperationWebSocket.add_handler((data) => history.update_data(data)); - Config.reset(history.reset.bind(history)); + Config.reset(() => history.fetch({'accounts': [{{account.pk}}]})); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 04cfd062..1d0d8e06 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -93,10 +93,9 @@ $(document).ready(function() { data['checkouts'] = checkouts; var accounts = getSelectedMultiple($accounts); data['accounts'] = accounts; - history.api_options = data ; // Update history - history.reset(); + history.fetch(data); } $('#from_date').datetimepicker({ diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index 2b98838e..2ba18251 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -668,7 +668,7 @@ $(document).ready(function() { Config.reset(function() { kpsul.article_manager.reset_data(); displayAddcost(); - kpsul.history.reset(); + kpsul.history.fetch(); }); } diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index b8bf8c70..ba59d197 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -55,9 +55,6 @@ $(document).ready(function() { window.lock = 0; var history = new KHistory(); - history.api_options = { - 'transfersonly': true, - }; // ----- // Synchronization @@ -65,7 +62,7 @@ $(document).ready(function() { OperationWebSocket.add_handler((data) => history.update_data(data)); - Config.reset(history.reset.bind(history)); + Config.reset(() => history.fetch({'transfersonly': true})); }); diff --git a/kfet/views.py b/kfet/views.py index 60ba25ac..5ef21330 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1441,11 +1441,11 @@ def kpsul_cancel_operations(request): @login_required def history_json(request): # Récupération des paramètres - from_date = request.POST.get('from', None) - to_date = request.POST.get('to', None) - checkouts = request.POST.getlist('checkouts[]', None) - accounts = request.POST.getlist('accounts[]', None) - transfers_only = request.POST.get('transfersonly', None) + from_date = request.GET.get('from', None) + to_date = request.GET.get('to', None) + checkouts = request.GET.getlist('checkouts[]', None) + accounts = request.GET.getlist('accounts[]', None) + transfers_only = request.GET.get('transfersonly', None) # Construction de la requête (sur les opérations) pour le prefetch ope_queryset_prefetch = Operation.objects.select_related( From 88f7ea941db792850ee02585ca4d815b86a9ce45 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 11:26:33 -0300 Subject: [PATCH 58/90] Move selection logic to another class --- kfet/static/kfet/js/history.js | 93 +++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 01ca1699..5fcb8f2c 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -4,7 +4,7 @@ class KHistory { return { from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), }; } - constructor(display_options) { + constructor(display_options, noselect) { this.templates = { 'purchase': '
', 'specialope': '
', @@ -21,7 +21,10 @@ class KHistory { this.display_options = display_options || {}; - this._init_selection(); + if (!noselect) { + this.selection = new KHistorySelection(this); + } + this._init_events(); } @@ -45,53 +48,25 @@ class KHistory { $('#nb_opes').text(nb_opes); } - _init_selection() { - this._$container.selectable({ - filter: 'div.opegroup, div.ope', - selected: function(e, ui) { - $(ui.selected).each(function() { - if ($(this).hasClass('opegroup')) { - $(this).parent().find('.ope').addClass('ui-selected'); - } - }); - }, - unselected: function(e, ui) { - $(ui.unselected).each(function() { - if ($(this).hasClass('opegroup')) { - $(this).parent().find('.ope').removeClass('ui-selected'); - } - }); - }, - }); - } - _init_events() { var that = this; $(document).on('keydown', function(e) { - if (e.keyCode == 46) { + if (e.keyCode == 46 && that.selection) { //DEL key ; we delete the selected operations (if any) - var to_cancel = {'transfers': [], 'opes': [],}; - that._$container.find('.ope.ui-selected').each(function() { - var [type, id] = $(this).parent().attr('id').split('-'); + var to_cancel = that.selection.get_selected(); - if (type === 'transfer') - to_cancel['transfers'].push(id); - else - to_cancel['opes'].push(id); - }); if (to_cancel['opes'].length > 0 || to_cancel['transfers'].length > 0) that.cancel_operations(to_cancel); } }); } - cancel_operations(to_cancel, password='') { + cancel_operations(to_cancel) { if (window.kpsul) { var on_success = kpsul._env.coolReset; - var focus_next = kpsul.account_manager._$input_trigramme; + var focus_next = kpsul; } else { - var on_success = () => this._$container.find('.ui-selected') - .removeClass('.ui-selected') ; + var on_success = () => this.selection.reset() var focus_next = undefined; } @@ -147,7 +122,6 @@ class KHistory { } update_data(data) { - //Filter opegroups before ? var opegroups = data['opegroups']; var opes = data['opes']; @@ -181,3 +155,50 @@ class KHistory { $('#nb_opes').text(nb_opes); } } + +class KHistorySelection { + + constructor(history) { + this._$container = history._$container; + this._init(); + } + + _init() { + this._$container.selectable({ + filter: 'div.opegroup, div.ope', + selected: function(e, ui) { + $(ui.selected).each(function() { + if ($(this).hasClass('opegroup')) { + $(this).parent().find('.ope').addClass('ui-selected'); + } + }); + }, + unselected: function(e, ui) { + $(ui.unselected).each(function() { + if ($(this).hasClass('opegroup')) { + $(this).parent().find('.ope').removeClass('ui-selected'); + } + }); + }, + }); + } + + get_selected() { + var selected = {'transfers': [], 'opes': [],}; + this._$container.find('.ope.ui-selected').each(function() { + var [type, id] = $(this).parent().attr('id').split('-'); + + if (type === 'transfer') + selected['transfers'].push(id); + else + selected['opes'].push(id); + }); + + return selected; + } + + reset() { + this._$container.find('.ui-selected') + .removeClass('.ui-selected'); + } +} From f57c292184441fa68acf609d0ba9bc938e1c5dd9 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 12:13:24 -0300 Subject: [PATCH 59/90] Rename history var --- kfet/templates/kfet/account_read.html | 9 +++------ kfet/templates/kfet/history.html | 7 +++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index b11a2cb0..206b40ce 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -102,18 +102,15 @@ $(document).ready(function() { 'use strict'; - // Lock to avoid multiple requests - window.lock = 0; - - var history = new KHistory({'remove_props': ['trigramme']}); + var khistory = new KHistory({'remove_props': ['trigramme']}); // ----- // Synchronization // ----- - OperationWebSocket.add_handler((data) => history.update_data(data)); + OperationWebSocket.add_handler((data) => khistory.update_data(data)); - Config.reset(() => history.fetch({'accounts': [{{account.pk}}]})); + Config.reset(() => khistory.fetch({'accounts': [{{account.pk}}]})); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 1d0d8e06..864396eb 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -64,8 +64,7 @@ $(document).ready(function() { 'use strict'; - - var history = new KHistory(); + var khistory = new KHistory(); var $from_date = $('#from_date'); var $to_date = $('#to_date'); @@ -95,7 +94,7 @@ $(document).ready(function() { data['accounts'] = accounts; // Update history - history.fetch(data); + khistory.fetch(data); } $('#from_date').datetimepicker({ @@ -134,7 +133,7 @@ $(document).ready(function() { // Synchronization // ----- - OperationWebSocket.add_handler(data => history.update_data(data)); + OperationWebSocket.add_handler(data => khistory.update_data(data)); From ed0a82ed5d7d5548fba12f5f5540980b0e565f11 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Wed, 5 Apr 2017 16:51:17 -0300 Subject: [PATCH 60/90] Add no_trigramme option --- kfet/static/kfet/js/history.js | 12 ++++++------ kfet/static/kfet/js/kfet.api.js | 3 --- kfet/static/kfet/js/kfet.js | 16 ---------------- kfet/templates/kfet/account_read.html | 2 +- 4 files changed, 7 insertions(+), 26 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 5fcb8f2c..a41173b2 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -4,7 +4,7 @@ class KHistory { return { from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), }; } - constructor(display_options, noselect) { + constructor(options) { this.templates = { 'purchase': '
', 'specialope': '
', @@ -19,12 +19,12 @@ class KHistory { this.list = new OperationList(); - this.display_options = display_options || {}; - - if (!noselect) { + if (!options.no_select) this.selection = new KHistorySelection(this); - } + if (options.no_trigramme) + this.templates['opegroup'] = '
' + this._init_events(); } @@ -43,7 +43,7 @@ class KHistory { } display() { - this.list.display(this._$container, this.templates, this.display_options); + this.list.display(this._$container, this.templates, {}); var nb_opes = this._$container.find('.ope[canceled="false"]').length; $('#nb_opes').text(nb_opes); } diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index ee5376af..10e52989 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1230,9 +1230,6 @@ class Formatter { var props = options.override_props ? options.props : this.props.concat(options.props); var attrs = options.override_attrs ? options.attrs : this.attrs.concat(options.attrs); - props = options.remove_props ? props.diff(options.remove_props) : props; - attrs = options.remove_attrs ? props.diff(options.remove_attrs) : attrs; - var prefix_prop = options.prefix_prop !== undefined ? options.prefix_prop : '.'; var prefix_attr = options.prefix_attr !== undefined ? options.prefix_attr : ''; diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index d9e2624c..d8799b1d 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -24,22 +24,6 @@ String.prototype.isValidTri = function() { return pattern.test(this); } -/** - * Array method - * @global - * @return {[]} Array elements not in given argument - */ -// Note : we define it this way to avoid problems in for loops -Object.defineProperty(Array.prototype, 'diff', { - enumerable: false, - configurable: true, - value: function(a) { - return this.filter(function (elt) { - return a.indexOf(elt) < 0 ; - }); - } -}); - /** * Checks if given argument is float ; diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 206b40ce..86e1b43e 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -102,7 +102,7 @@ $(document).ready(function() { 'use strict'; - var khistory = new KHistory({'remove_props': ['trigramme']}); + var khistory = new KHistory({'no_trigramme': true}); // ----- // Synchronization From 688d5bba29758294b08dedabf2f2dca275036a6d Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 9 Apr 2017 17:54:37 -0300 Subject: [PATCH 61/90] Adapt history to new structure --- kfet/static/kfet/js/history.js | 20 ++-- kfet/static/kfet/js/kfet.api.js | 156 +++++++++++++++++++++++++------- kfet/templates/kfet/kpsul.html | 66 -------------- kfet/views.py | 39 +------- 4 files changed, 134 insertions(+), 147 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index a41173b2..9578b83d 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -19,10 +19,10 @@ class KHistory { this.list = new OperationList(); - if (!options.no_select) + if (!options || !options.no_select) this.selection = new KHistorySelection(this); - if (options.no_trigramme) + if (options && options.no_trigramme) this.templates['opegroup'] = '
' this._init_events(); @@ -80,7 +80,7 @@ class KHistory { add_node(data) { var node = this.list.get_or_create(data, 0); - this.list.add_to_container(this._$container, node, this.templates, this.display_options); + this.list.add_to_container(this._$container, node, this.templates); } update_node(type, id, update_data) { @@ -88,8 +88,8 @@ class KHistory { if (!to_update) return false; - $.extend(to_update.content, update_data); - var $new_elt = to_update.content.display($(this.templates[type]), this.display_options); + $.extend(to_update, update_data); + var $new_elt = to_update.display($(this.templates[type]), {}); var $to_replace = this._$container.find('#'+type+'-'+id+'>:first-child'); $to_replace.replaceWith($new_elt); @@ -100,22 +100,22 @@ class KHistory { is_valid(opegroup) { var options = this.api_options; - if (options.from && dateUTCToParis(opegroup.content.at).isBefore(moment(options.from))) + if (options.from && dateUTCToParis(opegroup.at).isBefore(moment(options.from))) return false; - if (options.to && dateUTCToParis(opegroup.content.at).isAfter(moment(options.to))) + if (options.to && dateUTCToParis(opegroup.at).isAfter(moment(options.to))) return false; - if (options.transfersonly && opegroup.modelname === 'opegroup') + if (options.transfersonly && opegroup.constructor.verbose_name === 'opegroup') return false; if (options.accounts && options.accounts.length && - options.accounts.indexOf(opegroup.content.account_id) < 0) + options.accounts.indexOf(opegroup.account_id) < 0) return false; if (options.checkouts && options.checkouts.length && (opegroup.modelname === 'transfergroup' || - options.checkouts.indexOf(opegroup.content.checkout_id) < 0)) + options.checkouts.indexOf(opegroup.checkout_id) < 0)) return false; return true; diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index e08efced..6afc179f 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -565,14 +565,20 @@ class Day extends ModelObject { * @default ['id', 'date'] * @see {@link Models.ModelObject.props|ModelObject.props} */ - static get props() { return ['id', 'date'] } + static get props() { return ['id', 'date', 'opegroups'] } /** * Default values for Day model instances * @default {'id': '', 'date': moment()} * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ - static get default_data() { return {'id': '', 'date': moment()}; } + static get default_data() { return {'id': '', 'date': moment(), 'opegroups': []}; } + + /** + * Verbose name for Article model + * @default 'day' + */ + static get verbose_name() { return 'day'; } /** * @default {@link Formatters.DayFormatter} @@ -610,7 +616,7 @@ class HistoryGroup extends ModelObject { * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return ['id', 'at', 'comment', 'valid_by']; + return ['id', 'at', 'comment', 'valid_by', 'day']; } /** @@ -621,7 +627,7 @@ class HistoryGroup extends ModelObject { */ static get default_data() { return {'id': 0, 'at': moment(), 'comment': '', - 'valid_by': '',}; + 'valid_by': '', 'day': new Day()}; } /** @@ -665,9 +671,17 @@ class OperationGroup extends HistoryGroup { */ static get default_data() { return $.extend({}, HistoryGroup.default_data, - {'amount': 0, 'is_cof': false, 'trigramme': ''}); + {'amount': 0, 'is_cof': false, 'trigramme': '', + 'opes': []}); } + /** + * Verbose name for OperationGroup model + * @default 'opegroup' + */ + static get verbose_name() { return 'opegroup'; } + + /** * @default {@link Formatters.OpegroupFormatter} */ @@ -686,6 +700,24 @@ class OperationGroup extends HistoryGroup { */ class TransferGroup extends HistoryGroup { + /** + * Default values for OperationGroup instances. + * @default {@link Models.HistoryGroup.default_data|HistoryGroup.default_data} + + * {'transfers': []} + * @see {@link Models.ModelObject.default_data|ModelObject.default_data} + */ + static get default_data() { + return $.extend({}, HistoryGroup.default_data, + {'transfers': []}); + } + + /** + * Verbose name for TransferGroup model + * @default 'transfergroup' + */ + static get verbose_name() { return 'transfergroup'; } + + /** * @default {@link Formatters.TransferGroupFormatter} */ @@ -707,7 +739,7 @@ class Operation extends ModelObject { * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return ['id', 'amount', 'canceled_at', 'canceled_by']; + return ['id', 'amount', 'canceled_at', 'canceled_by', 'group']; } /** @@ -716,7 +748,8 @@ class Operation extends ModelObject { * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return {'id': '', 'amount': 0, 'canceled_at': undefined, 'canceled_by': '' }; + return {'id': '', 'amount': 0, 'canceled_at': undefined, 'canceled_by': '', + 'group': new HistoryGroup()}; } get amount() { return this._amount; } @@ -765,6 +798,13 @@ class Purchase extends Operation { }); } + /** + * Verbose name for Purchase model + * @default 'purchase' + */ + static get verbose_name() { return 'purchase'; } + + /** * @default {@link Formatters.PurchaseFormatter} */ @@ -786,9 +826,16 @@ class SpecialOperation extends Operation { * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return Operation.props.concat(['type', 'is_checkout']); + return Operation.props.concat(['type']); } + /** + * Verbose name for SpecialOperation model + * @default 'specialope' + */ + static get verbose_name() { return 'specialope'; } + + /** * Verbose names for operation types * @type {Object} @@ -808,7 +855,7 @@ class SpecialOperation extends Operation { * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return $.extend({}, Operation.default_data, {'type': '', 'is_checkout': false}); + return $.extend({}, Operation.default_data, {'type': ''}); } /** @@ -835,6 +882,12 @@ class Transfer extends Operation { return Operation.props.concat(['from_acc', 'to_acc']); } + /** + * Verbose name for Transfer model + * @default 'transfer' + */ + static get verbose_name() { return 'transfer'; } + /** * Default values for Transfer model instances * @default {@link Models.Operation.default_data|Operation.default_data} + {'from_acc': '', 'to_acc': ''} @@ -873,6 +926,13 @@ class ModelForest { this.from(datalist || []); } + /** + * Return true if instance is empty + */ + is_empty() { + return this.roots.length == 0; + } + /** * Shortcut functions to get parent and children of a given node */ @@ -920,11 +980,11 @@ class ModelForest { if (direction >= 0) { var child_name = struct_data.children; - var child_struct = this.constructor.structure[child_name]; if (data.children && data.children.length) { for (let child_data of data.children) { var child = this.get_or_create(child_data, 1); - child[child_struct.parent] = node; + var child_parent = this.constructor.structure[child.constructor.verbose_name]; + child[child_parent] = node; node[child_name].push(child); } } @@ -1008,7 +1068,8 @@ class ModelForest { var $to_insert = this.render_element(first_missing, templates, options); if (existing) { - $container.find('#'+existing.modelname+'-'+existing.content.id+'>:first-child').after($to_insert); + $container.find('#'+existing.constructor.verbose_name+'-'+existing.id+'>:first-child') + .after($to_insert); } else { $container.prepend($to_insert); } @@ -1021,6 +1082,9 @@ class ModelForest { * @param {Object} [options] Options for element render method */ display($container, templates, options) { + if (this.is_empty()) + return; + if (this.constructor.root_sort) this.roots.sort(this.constructor.root_sort); else @@ -1161,14 +1225,34 @@ class OperationList extends APIModelForest { * @default [Operation, OperationGroup, Day] * @see {@link Models.ModelList.models|ModelList.models} */ - static get models() { + static get structure() { return { - 'day': Day, - 'opegroup': OperationGroup, - 'transfergroup': TransferGroup, - 'purchase': Purchase, - 'specialope': SpecialOperation, - 'transfer': Transfer, + 'day': { + 'model': Day, + 'children': 'opegroups' + }, + 'opegroup': { + 'model': OperationGroup, + 'parent': 'day', + 'children': 'opes', + }, + 'transfergroup': { + 'model': TransferGroup, + 'parent': 'day', + 'children': 'transfers' + }, + 'purchase': { + 'model': Purchase, + 'parent': 'opegroup', + }, + 'specialope': { + 'model': SpecialOperation, + 'parent': 'opegroup', + }, + 'transfer': { + 'model': Transfer, + 'parent': 'transfergroup', + }, }; } @@ -1583,10 +1667,10 @@ class OperationFormatter extends Formatter { /** * Properties renderable to html. - * @default ['amount', 'infos1', 'infos2', 'addcost', 'canceled'] + * @default ['amount', 'infos1', 'infos2', 'canceled'] */ static get props() { - return ['amount', 'infos1', 'infos2', 'addcost', 'canceled']; + return ['amount', 'infos1', 'infos2', 'canceled']; } static get attrs() { @@ -1597,19 +1681,7 @@ class OperationFormatter extends Formatter { * a.amount displayed according to a.is_cof and a.trigramme values. */ static prop_amount(a) { - return amountDisplay(a.amount, a.is_cof, a.trigramme); - } - - /** - * addcost property is displayed iff a.addcost_for is nonempty. - */ - static prop_addcost(a) { - if (a.addcost_for) { - return '('+amountDisplay(a.addcost_amount, a.is_cof) - +'UKF pour '+a.addcost_for+')'; - } else { - return ''; - } + return amountDisplay(a.amount, a.group.is_cof, a.group.trigramme); } /** @@ -1639,6 +1711,22 @@ class OperationFormatter extends Formatter { */ class PurchaseFormatter extends OperationFormatter { + static get props() { + return OperationFormatter.props.concat(['addcost']); + } + + /** + * addcost property is displayed iff a.addcost_for is nonempty. + */ + static prop_addcost(a) { + if (a.addcost_for) { + return '('+amountDisplay(a.addcost_amount, a.is_cof) + +'UKF pour '+a.addcost_for+')'; + } else { + return ''; + } + } + static prop_infos1(a) { return a.article_nb; } diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index 3aefb527..c1e5fec6 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -648,72 +648,6 @@ $(document).ready(function() { } -<<<<<<< HEAD -======= - // ----- - // Cancel from history - // ----- - - khistory.$container.selectable({ - filter: 'div.opegroup, div.ope', - selected: function(e, ui) { - $(ui.selected).each(function() { - if ($(this).hasClass('opegroup')) { - var opegroup = $(this).data('opegroup'); - $(this).siblings('.ope').filter(function() { - return $(this).data('opegroup') == opegroup - }).addClass('ui-selected'); - } - }); - }, - }); - - $(document).on('keydown', function (e) { - if (e.keyCode == 46) { - // DEL (Suppr) - var opes_to_cancel = []; - khistory.$container.find('.ope.ui-selected').each(function () { - opes_to_cancel.push($(this).data('ope')); - }); - if (opes_to_cancel.length > 0) - cancelOperations(opes_to_cancel); - } - }); - - // ----- - // Synchronization - // ----- - - OperationWebSocket.add_handler(function(data) { - for (var i=0; i>>>>>> origin/aureplop/kpsul_js_refactor - // ----- // General // ----- diff --git a/kfet/views.py b/kfet/views.py index 57e7ae95..7826f31d 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1168,7 +1168,6 @@ def kpsul_perform_operations(request): websocket_data = {} websocket_data['opegroups'] = [{ 'add': True, -<<<<<<< HEAD 'modelname': 'opegroup', 'content': { 'id': operationgroup.pk, @@ -1189,48 +1188,17 @@ def kpsul_perform_operations(request): 'id': operationgroup.at.strftime('%Y%m%d'), 'date': operationgroup.at }, - 'child_sort': 'opegroup', }, 'children': [], -======= - 'id': operationgroup.pk, - 'amount': operationgroup.amount, - 'checkout__name': operationgroup.checkout.name, - 'at': operationgroup.at, - 'is_cof': operationgroup.is_cof, - 'comment': operationgroup.comment, - 'valid_by__trigramme': (operationgroup.valid_by and - operationgroup.valid_by.trigramme or None), - 'on_acc__trigramme': operationgroup.on_acc.trigramme, - 'opes': [], ->>>>>>> origin/aureplop/kpsul_js_refactor }] for ope in operations: ope_data = { -<<<<<<< HEAD 'content': { 'id': ope.id, 'amount': ope.amount, - 'canceled_at': ope.canceled_at, - 'canceled_by': - ope.canceled_by and ope.canceled_by.trigramme or None, - 'is_cof': operationgroup.is_cof, - 'trigramme': ( - operationgroup.on_acc and - operationgroup.on_acc.trigramme or None), + 'canceled_at': None, + 'canceled_by': None, }, -======= - 'id': operation.pk, 'type': operation.type, - 'amount': operation.amount, - 'addcost_amount': operation.addcost_amount, - 'addcost_for__trigramme': ( - operation.addcost_for and addcost_for.trigramme or None), - 'article__name': ( - operation.article and operation.article.name or None), - 'article_nb': operation.article_nb, - 'group_id': operationgroup.pk, - 'canceled_by__trigramme': None, 'canceled_at': None, ->>>>>>> origin/aureplop/kpsul_js_refactor } if ope.type == Operation.PURCHASE: @@ -1246,7 +1214,6 @@ def kpsul_perform_operations(request): ope_data['modelname'] = 'specialope' ope_data['content'].update({ 'type': ope.type, - 'is_checkout': ope.is_checkout, }) websocket_data['opegroups'][0]['children'].append(ope_data) # Need refresh from db cause we used update on queryset @@ -1586,7 +1553,6 @@ def history_json(request): 'id': opegroup.at.strftime('%Y%m%d'), 'date': opegroup.at }, - 'child_sort': 'opegroup', }, 'children': [], } @@ -1643,7 +1609,6 @@ def history_json(request): 'id': transfergroup.at.strftime('%Y%m%d'), 'date': transfergroup.at }, - 'child_sort': 'transfergroup', }, 'children': [], } From 5e8752632cff4c885ebe6780bd9276990adcdbf5 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 9 Apr 2017 21:50:47 -0300 Subject: [PATCH 62/90] Add index for Day objects --- kfet/static/kfet/js/kfet.api.js | 68 ++++++++++++++++++++++++--------- kfet/views.py | 21 ---------- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 6afc179f..23fcd4b6 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -565,14 +565,14 @@ class Day extends ModelObject { * @default ['id', 'date'] * @see {@link Models.ModelObject.props|ModelObject.props} */ - static get props() { return ['id', 'date', 'opegroups'] } + static get props() { return ['id', 'at', 'opegroups'] } /** * Default values for Day model instances * @default {'id': '', 'date': moment()} * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ - static get default_data() { return {'id': '', 'date': moment(), 'opegroups': []}; } + static get default_data() { return {'id': '', 'at': moment(), 'opegroups': []}; } /** * Verbose name for Article model @@ -580,6 +580,11 @@ class Day extends ModelObject { */ static get verbose_name() { return 'day'; } + from(data) { + super.from(data); + this.id = this.at.format("YYYYMMDD"); + } + /** * @default {@link Formatters.DayFormatter} */ @@ -593,14 +598,14 @@ class Day extends ModelObject { */ static compare (a, b) { //Days are sorted by most recent first - if (a.date < b.date) return 1; - else if (a.date > b.date) return -1; + if (a.at < b.at) return 1; + else if (a.at > b.at) return -1; else return 0; } //Parse date and round it - get date() { return this._date; } - set date(v) { this._date = dateUTCToParis(v).startOf('date'); } + get at() { return this._at; } + set at(v) { this._at = dateUTCToParis(v).startOf('date'); } } /** @@ -955,7 +960,8 @@ class ModelForest { * @param {number} direction */ get_or_create(data, direction) { - var struct_data = this.constructor.structure[data.modelname]; + var struct = this.constructor.structure ; + var struct_data = struct[data.modelname]; var model = struct_data.model; var existing = this.find(data.modelname, data.content.id); @@ -963,19 +969,41 @@ class ModelForest { return existing; } - var node = new model(data.content); + var node; + if (data.content instanceof ModelObject) + node = data.content; + else + node = new model(data.content); if (direction <= 0) { var parent_name = struct_data.parent; - var parent_data = data.parent; - var parent_struct = this.constructor.structure[parent_name]; - if (parent_data) { - var parent_node = this.get_or_create(parent_data, -1); - node[parent_name] = parent_node; - parent_node[parent_struct.children].push(node); - } else { + if (!parent_name) { this.roots.push(node); + return node; } + + // If index, we create it + if (struct_data.index) { + var new_parent = {} + for (let key of struct_data.index.fields) { + new_parent[key] = data.content[key]; + } + + // We create model in case there are some special fields + var data_parent = { + 'modelname': struct_data.index.modelname, + 'content': new struct[struct_data.index.modelname].model(new_parent), + } + console.log(data_parent.content); + + } else { + var data_parent = data.parent ; + } + + var parent = this.get_or_create(data_parent, -1); + var parent_childname = struct[parent.constructor.verbose_name].children; + node[parent_name] = parent ; + parent[parent_childname].push(node); } if (direction >= 0) { @@ -983,7 +1011,7 @@ class ModelForest { if (data.children && data.children.length) { for (let child_data of data.children) { var child = this.get_or_create(child_data, 1); - var child_parent = this.constructor.structure[child.constructor.verbose_name]; + var child_parent = struct[child.constructor.verbose_name]; child[child_parent] = node; node[child_name].push(child); } @@ -1235,6 +1263,10 @@ class OperationList extends APIModelForest { 'model': OperationGroup, 'parent': 'day', 'children': 'opes', + 'index': { + 'modelname': 'day', + 'fields': ["at"] + }, }, 'transfergroup': { 'model': TransferGroup, @@ -1648,14 +1680,14 @@ class DayFormatter extends Formatter { * @default {@link Models.Day.props} */ static get props() { - return Day.props; + return ['date']; } /** * a.date formatted as D MMMM */ static prop_date(a) { - return a.date.format('D MMMM'); + return a.at.format('D MMMM'); } } diff --git a/kfet/views.py b/kfet/views.py index 7826f31d..589b8d41 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1182,13 +1182,6 @@ def kpsul_perform_operations(request): 'account_id': operationgroup.on_acc.pk, 'checkout_id': operationgroup.checkout.pk, }, - 'parent': { - 'modelname': 'day', - 'content': { - 'id': operationgroup.at.strftime('%Y%m%d'), - 'date': operationgroup.at - }, - }, 'children': [], }] for ope in operations: @@ -1547,13 +1540,6 @@ def history_json(request): 'trigramme': opegroup.on_acc and opegroup.on_acc.trigramme or None, }, - 'parent': { - 'modelname': 'day', - 'content': { - 'id': opegroup.at.strftime('%Y%m%d'), - 'date': opegroup.at - }, - }, 'children': [], } if request.user.has_perm('kfet.is_team'): @@ -1603,13 +1589,6 @@ def history_json(request): 'at': transfergroup.at, 'comment': transfergroup.comment, }, - 'parent': { - 'modelname': 'day', - 'content': { - 'id': transfergroup.at.strftime('%Y%m%d'), - 'date': transfergroup.at - }, - }, 'children': [], } From 47da80f21c93ef67da246a83a73ebd05999c3178 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 9 Apr 2017 23:24:50 -0300 Subject: [PATCH 63/90] Add related objects --- kfet/static/kfet/js/history.js | 2 +- kfet/static/kfet/js/kfet.api.js | 79 +++++++++++++------ kfet/views.py | 134 ++++++++++++++++---------------- 3 files changed, 123 insertions(+), 92 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 9578b83d..eaecfb8f 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -79,7 +79,7 @@ class KHistory { } add_node(data) { - var node = this.list.get_or_create(data, 0); + var node = this.list.get_or_create(data.modelname, data.content, 0); this.list.add_to_container(this._$container, node, this.templates); } diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 23fcd4b6..0f6a078e 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -927,8 +927,8 @@ class ModelForest { * Creates empty instance and populates it with data if given * @param {Object[]} [datalist=[]] */ - constructor(datalist) { - this.from(datalist || []); + constructor(data) { + this.from(data || {}); } /** @@ -959,21 +959,21 @@ class ModelForest { * @param {Object} data * @param {number} direction */ - get_or_create(data, direction) { + get_or_create(modelname, data, direction) { var struct = this.constructor.structure ; - var struct_data = struct[data.modelname]; + var struct_data = struct[modelname]; var model = struct_data.model; - var existing = this.find(data.modelname, data.content.id); + var existing = this.find(modelname, data.id); if (existing) { return existing; } var node; - if (data.content instanceof ModelObject) - node = data.content; + if (data instanceof ModelObject) + node = data; else - node = new model(data.content); + node = new model(data); if (direction <= 0) { var parent_name = struct_data.parent; @@ -982,26 +982,32 @@ class ModelForest { return node; } + var parent_modelname, parent_data; // If index, we create it if (struct_data.index) { var new_parent = {} for (let key of struct_data.index.fields) { - new_parent[key] = data.content[key]; + new_parent[key] = data[key]; } // We create model in case there are some special fields - var data_parent = { - 'modelname': struct_data.index.modelname, - 'content': new struct[struct_data.index.modelname].model(new_parent), - } - console.log(data_parent.content); + parent_data = new struct[struct_data.index.modelname].model(new_parent); + parent_modelname = struct_data.index.modelname; + + } else if (struct_data.related) { + var related = struct_data.related; + var field = data[related.modelname+'__'+related.field]; + + parent_data = this.related[related.modelname].find( (obj) => obj[related.field] === field); + parent_modelname = related.modelname; } else { - var data_parent = data.parent ; + parent_data = data.parent.content ; + parent_modelname = data.parent.modelname ; } - var parent = this.get_or_create(data_parent, -1); - var parent_childname = struct[parent.constructor.verbose_name].children; + var parent = this.get_or_create(parent_modelname, parent_data, -1); + var parent_childname = struct[parent_modelname].children; node[parent_name] = parent ; parent[parent_childname].push(node); } @@ -1010,8 +1016,9 @@ class ModelForest { var child_name = struct_data.children; if (data.children && data.children.length) { for (let child_data of data.children) { - var child = this.get_or_create(child_data, 1); - var child_parent = struct[child.constructor.verbose_name]; + console.log(child_data); + var child = this.get_or_create(child_data.modelname, child_data.content, 1); + var child_parent = struct[child_data.modelname]; child[child_parent] = node; node[child_name].push(child); } @@ -1025,10 +1032,14 @@ class ModelForest { * Resets then populates the instance with the given data. * @param {Object[]} datalist */ - from(datalist) { + from(data) { this.roots = []; - for (let data of datalist) { - this.get_or_create(data, 0); + if (data.objects) { + this.related = data.related + for (let modelname in data.objects) { + for (let obj_data of data.objects[modelname]) + this.get_or_create(modelname, obj_data, 0); + } } } @@ -1036,7 +1047,7 @@ class ModelForest { * Removes all Models.TreeNode from the tree. */ clear() { - this.from([]); + this.from({}); } /** @@ -1223,6 +1234,10 @@ class ArticleList extends APIModelForest { 'article': { 'model': Article, 'parent': 'category', + 'related': { + 'modelname': 'category', + 'field': 'id', + } }, 'category': { 'model': ArticleCategory, @@ -1271,19 +1286,35 @@ class OperationList extends APIModelForest { 'transfergroup': { 'model': TransferGroup, 'parent': 'day', - 'children': 'transfers' + 'children': 'transfers', + 'index': { + 'modelname': 'day', + 'fields': ["at"] + }, }, 'purchase': { 'model': Purchase, 'parent': 'opegroup', + 'related': { + 'modelname': 'opegroup', + 'field': 'id', + }, }, 'specialope': { 'model': SpecialOperation, 'parent': 'opegroup', + 'related': { + 'modelname': 'opegroup', + 'field': 'id', + }, }, 'transfer': { 'model': Transfer, 'parent': 'transfergroup', + 'related': { + 'modelname': 'transfergroup', + 'field': 'id', + }, }, }; } diff --git a/kfet/views.py b/kfet/views.py index 589b8d41..57bef710 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1181,8 +1181,8 @@ def kpsul_perform_operations(request): # Used to filter websocket updates 'account_id': operationgroup.on_acc.pk, 'checkout_id': operationgroup.checkout.pk, + 'children': [], }, - 'children': [], }] for ope in operations: ope_data = { @@ -1208,7 +1208,7 @@ def kpsul_perform_operations(request): ope_data['content'].update({ 'type': ope.type, }) - websocket_data['opegroups'][0]['children'].append(ope_data) + websocket_data['opegroups'][0]['content']['children'].append(ope_data) # Need refresh from db cause we used update on queryset operationgroup.checkout.refresh_from_db() websocket_data['checkouts'] = [{ @@ -1527,97 +1527,91 @@ def history_json(request): opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) # Construction de la réponse - opegroup_list = [] + related_data = defaultdict(list) + objects_data = defaultdict(list) for opegroup in opegroups: opegroup_dict = { - 'modelname': 'opegroup', - 'content': { - 'id': opegroup.id, - 'amount': opegroup.amount, - 'at': opegroup.at, - 'is_cof': opegroup.is_cof, - 'comment': opegroup.comment, - 'trigramme': - opegroup.on_acc and opegroup.on_acc.trigramme or None, - }, - 'children': [], + 'id': opegroup.id, + 'amount': opegroup.amount, + 'at': opegroup.at, + 'is_cof': opegroup.is_cof, + 'comment': opegroup.comment, + 'trigramme': + opegroup.on_acc and opegroup.on_acc.trigramme or None, } if request.user.has_perm('kfet.is_team'): - opegroup_dict['content']['valid_by'] = ( + opegroup_dict['valid_by'] = ( opegroup.valid_by and opegroup.valid_by.trigramme or None) for ope in opegroup.opes.all(): ope_dict = { - 'content': { - 'id': ope.id, - 'amount': ope.amount, - 'canceled_at': ope.canceled_at, - 'is_cof': opegroup.is_cof, - 'trigramme': - opegroup.on_acc and opegroup.on_acc.trigramme or None, - }, + 'id': ope.id, + 'amount': ope.amount, + 'canceled_at': ope.canceled_at, + 'is_cof': opegroup.is_cof, + 'trigramme': + opegroup.on_acc and opegroup.on_acc.trigramme or None, + 'opegroup__id': opegroup.id, } if request.user.has_perm('kfet.is_team'): - ope_dict['content']['canceled_by'] = ( + ope_dict['canceled_by'] = ( ope.canceled_by and ope.canceled_by.trigramme or None) if ope.type == Operation.PURCHASE: - ope_dict['modelname'] = 'purchase' - ope_dict['content'].update({ + ope_dict.update({ 'article_name': ope.article.name, 'article_nb': ope.article_nb, 'addcost_amount': ope.addcost_amount, 'addcost_for': ope.addcost_for and ope.addcost_for.trigramme or None, }) + objects_data['purchase'].append(ope_dict) else: - ope_dict['modelname'] = 'specialope' - ope_dict['content'].update({ + ope_dict.update({ 'type': ope.type, - 'is_checkout': ope.is_checkout, }) + objects_data['specialope'].append(ope_dict) - opegroup_dict['children'].append(ope_dict) - opegroup_list.append(opegroup_dict) + related_data['opegroup'].append(opegroup_dict) for transfergroup in transfergroups: if transfergroup.filtered_transfers: transfergroup_dict = { - 'modelname': 'transfergroup', - 'content': { - 'id': transfergroup.id, - 'at': transfergroup.at, - 'comment': transfergroup.comment, - }, - 'children': [], + 'id': transfergroup.id, + 'at': transfergroup.at, + 'comment': transfergroup.comment, } if request.user.has_perm('kfet.is_team'): - transfergroup_dict['content']['valid_by'] = ( + transfergroup_dict['valid_by'] = ( transfergroup.valid_by and transfergroup.valid_by.trigramme or None) for transfer in transfergroup.filtered_transfers: transfer_dict = { - 'modelname': 'transfer', - 'content': { - 'id': transfer.id, - 'amount': transfer.amount, - 'canceled_at': transfer.canceled_at, - 'from_acc': transfer.from_acc.trigramme, - 'to_acc': transfer.to_acc.trigramme, - }, + 'id': transfer.id, + 'amount': transfer.amount, + 'canceled_at': transfer.canceled_at, + 'from_acc': transfer.from_acc.trigramme, + 'to_acc': transfer.to_acc.trigramme, + 'transfergroup__id': transfergroup.id, } if request.user.has_perm('kfet.is_team'): - transfer_dict['content']['canceled_by'] = ( + transfer_dict['canceled_by'] = ( transfer.canceled_by and transfer.canceled_by.trigramme or None) - transfergroup_dict['children'].append(transfer_dict) - opegroup_list.append(transfergroup_dict) + objects_data['transfer'].append(transfer_dict) + related_data['transfergroup'].append(transfergroup_dict) + + data = { + 'objects': objects_data, + 'related': related_data, + } + + return JsonResponse(data) - return JsonResponse(opegroup_list, safe=False) @teamkfet_required def kpsul_articles_data(request): @@ -1626,26 +1620,32 @@ def kpsul_articles_data(request): .filter(is_sold=True) .select_related('category')) articlelist = [] + categorylist = [] + # TODO: nice queryset, no duplicate categories for article in articles: articlelist.append({ - 'modelname': 'article', - 'content': { - 'id': article.id, - 'name': article.name, - 'price': article.price, - 'stock': article.stock, - }, - 'parent': { - 'modelname': 'category', - 'content': { - 'id': article.category.id, - 'name': article.category.name, - 'has_addcost': article.category.has_addcost, - }, - } + 'id': article.id, + 'name': article.name, + 'price': article.price, + 'stock': article.stock, + 'category__id': article.category.id, }) - return JsonResponse(articlelist, safe=False) + categorylist.append({ + 'id': article.category.id, + 'name': article.category.name, + 'has_addcost': article.category.has_addcost, + }) + + data = { + 'objects': { + 'article': articlelist, + }, + 'related': { + 'category': categorylist + } + } + return JsonResponse(data) @teamkfet_required From cfb39b10509dd401ca3fd7c25de65f8dd0ac2b17 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 11:30:00 -0300 Subject: [PATCH 64/90] Better default options --- kfet/static/kfet/js/history.js | 36 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index eaecfb8f..6a77a8ff 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,28 +1,38 @@ class KHistory { - static get default_options() { - return { from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), }; + static get default_options { + return { + 'templates': { + 'purchase': '
', + 'specialope': '
', + 'opegroup': '
', + 'transfergroup': '
', + 'day': '
', + 'transfer': '
', + }, + + 'api_options': { + from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'), + }, + + }; } constructor(options) { - this.templates = { - 'purchase': '
', - 'specialope': '
', - 'opegroup': '
', - 'transfergroup': '
', - 'day': '
', - 'transfer': '
', - }; + var all_options = $.extend({}, this.constructor.default_options, options); + + this.templates = all_options.templates; + this.api_options = all_options.api_options; this._$container = $('#history'); this._$nb_opes = $('#nb_opes'); this.list = new OperationList(); - if (!options || !options.no_select) + if (!all_options.no_select) this.selection = new KHistorySelection(this); - if (options && options.no_trigramme) + if (all_options.no_trigramme) this.templates['opegroup'] = '
' this._init_events(); @@ -34,8 +44,6 @@ class KHistory { if (api_options) this.api_options = api_options; - else if (!this.api_options) - this.api_options = this.constructor.default_options; this.list.fromAPI(this.api_options) .done( () => this.display() ); From 983a55780f8e953a184c037199bde3eeaa4b3d6c Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 12:32:35 -0300 Subject: [PATCH 65/90] Add cancel_history event --- kfet/static/kfet/js/history.js | 13 ++++--------- kfet/static/kfet/js/kpsul.js | 10 ++++++++++ kfet/templates/kfet/account_read.html | 4 ++++ kfet/templates/kfet/history.html | 4 ++++ kfet/templates/kfet/transfers.html | 7 ++++--- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 6a77a8ff..15e63438 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,6 +1,8 @@ +var cancelHistory = new Event("history_cancel"); + class KHistory { - static get default_options { + static get default_options() { return { 'templates': { 'purchase': '
', @@ -70,19 +72,12 @@ class KHistory { } cancel_operations(to_cancel) { - if (window.kpsul) { - var on_success = kpsul._env.coolReset; - var focus_next = kpsul; - } else { - var on_success = () => this.selection.reset() - var focus_next = undefined; - } + var on_success = () => document.dispatchEvent(cancelHistory); api_with_auth({ url: Urls['kfet.kpsul.cancel_operations'](), data: to_cancel, on_success: on_success, - focus_next: focus_next, }) } diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 137843c7..8833d072 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -13,6 +13,8 @@ class KPsulManager { this.checkout_manager = new CheckoutManager(this); this.article_manager = new ArticleManager(this); this.history = new KHistory(); + + this._init_events(); } reset(soft) { @@ -39,6 +41,14 @@ class KPsulManager { return this; } + _init_events() { + var that = this ; + $(document).on("history_cancel", function(e) { + that.reset(true); + that.focus; + }); + } + } diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 6c0d697a..54b91de4 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -109,6 +109,10 @@ $(document).ready(function() { var khistory = new KHistory({'no_trigramme': true}); + $(document).on("cancel_history", function(e) { + khistory.selection.reset(); + }); + // ----- // Synchronization // ----- diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 864396eb..d432079f 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -129,6 +129,10 @@ $(document).ready(function() { updateHistory(); }); + $(document).on("cancel_history", function(e) { + khistory.selection.reset(); + }); + // ----- // Synchronization // ----- diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index ba59d197..910c5ab9 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -51,11 +51,12 @@ $(document).ready(function() { 'use strict'; - // Lock to avoid multiple requests - window.lock = 0; - var history = new KHistory(); + $(document).on("cancel_history", function(e) { + khistory.selection.reset(); + }); + // ----- // Synchronization // ----- From cd0e4c6f3ec150ce513a9f947bda1929effeed4e Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 12:33:24 -0300 Subject: [PATCH 66/90] Allow (basic) chaining on api_with_auth --- kfet/static/kfet/js/kfet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index d8799b1d..a2b245cf 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -382,7 +382,7 @@ function api_with_auth(settings, password) { var on_success = settings.on_success || $.noop ; var on_400 = settings.on_400 || $.noop ; - $.ajax({ + return $.ajax({ dataType: "json", url: url, method: "POST", From 84d478271bfb6cb06d100a959deb7e984e099ed4 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 12:34:43 -0300 Subject: [PATCH 67/90] Compatibility changes on history.js --- kfet/static/kfet/js/history.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 15e63438..5b6ec75b 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -91,7 +91,7 @@ class KHistory { if (!to_update) return false; - $.extend(to_update, update_data); + to_update.update(update_data); var $new_elt = to_update.display($(this.templates[type]), {}); var $to_replace = this._$container.find('#'+type+'-'+id+'>:first-child'); @@ -109,7 +109,7 @@ class KHistory { if (options.to && dateUTCToParis(opegroup.at).isAfter(moment(options.to))) return false; - if (options.transfersonly && opegroup.constructor.verbose_name === 'opegroup') + if (options.transfersonly && opegroup.constructor.verbose_name == 'opegroup') return false; if (options.accounts && options.accounts.length && @@ -117,7 +117,7 @@ class KHistory { return false; if (options.checkouts && options.checkouts.length && - (opegroup.modelname === 'transfergroup' || + (opegroup.modelname == 'transfergroup' || options.checkouts.indexOf(opegroup.checkout_id) < 0)) return false; From ce3d8aa6f7025881aba002f23dfc23604bc07cf5 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 15:08:17 -0300 Subject: [PATCH 68/90] Change event triggered when canceling opes --- kfet/static/kfet/js/history.js | 4 +--- kfet/static/kfet/js/kfet.js | 1 + kfet/static/kfet/js/kpsul.js | 2 +- kfet/templates/kfet/account_read.html | 2 +- kfet/templates/kfet/history.html | 2 +- kfet/templates/kfet/transfers.html | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 5b6ec75b..6231c678 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,5 +1,3 @@ -var cancelHistory = new Event("history_cancel"); - class KHistory { static get default_options() { @@ -72,7 +70,7 @@ class KHistory { } cancel_operations(to_cancel) { - var on_success = () => document.dispatchEvent(cancelHistory); + var on_success = () => $(document).trigger("custom_reset"); api_with_auth({ url: Urls['kfet.kpsul.cancel_operations'](), diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index a2b245cf..81f8e845 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -133,6 +133,7 @@ class Config { } +var resetEvent = new Event("custom_reset"); $(document).ready(function() { $(window).scroll(function() { diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 8833d072..e42d5569 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -43,7 +43,7 @@ class KPsulManager { _init_events() { var that = this ; - $(document).on("history_cancel", function(e) { + $(document).on("custom_reset", function(e) { that.reset(true); that.focus; }); diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 54b91de4..8991162e 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -109,7 +109,7 @@ $(document).ready(function() { var khistory = new KHistory({'no_trigramme': true}); - $(document).on("cancel_history", function(e) { + $(document).on("custom_reset", function(e) { khistory.selection.reset(); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index d432079f..4edb233a 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -129,7 +129,7 @@ $(document).ready(function() { updateHistory(); }); - $(document).on("cancel_history", function(e) { + $(document).on("custom_reset", function(e) { khistory.selection.reset(); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 910c5ab9..49f36327 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -53,7 +53,7 @@ $(document).ready(function() { var history = new KHistory(); - $(document).on("cancel_history", function(e) { + $(document).on("custom_reset", function(e) { khistory.selection.reset(); }); From 2eba6892a2cfdc4cf276a54de65e9b3751d6a62c Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 15:34:57 -0300 Subject: [PATCH 69/90] Revert "Change event triggered when canceling opes" This reverts commit ce3d8aa6f7025881aba002f23dfc23604bc07cf5. --- kfet/static/kfet/js/history.js | 4 +++- kfet/static/kfet/js/kfet.js | 1 - kfet/static/kfet/js/kpsul.js | 2 +- kfet/templates/kfet/account_read.html | 2 +- kfet/templates/kfet/history.html | 2 +- kfet/templates/kfet/transfers.html | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 6231c678..5b6ec75b 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,3 +1,5 @@ +var cancelHistory = new Event("history_cancel"); + class KHistory { static get default_options() { @@ -70,7 +72,7 @@ class KHistory { } cancel_operations(to_cancel) { - var on_success = () => $(document).trigger("custom_reset"); + var on_success = () => document.dispatchEvent(cancelHistory); api_with_auth({ url: Urls['kfet.kpsul.cancel_operations'](), diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index 81f8e845..a2b245cf 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -133,7 +133,6 @@ class Config { } -var resetEvent = new Event("custom_reset"); $(document).ready(function() { $(window).scroll(function() { diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index e42d5569..8833d072 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -43,7 +43,7 @@ class KPsulManager { _init_events() { var that = this ; - $(document).on("custom_reset", function(e) { + $(document).on("history_cancel", function(e) { that.reset(true); that.focus; }); diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 8991162e..54b91de4 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -109,7 +109,7 @@ $(document).ready(function() { var khistory = new KHistory({'no_trigramme': true}); - $(document).on("custom_reset", function(e) { + $(document).on("cancel_history", function(e) { khistory.selection.reset(); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 4edb233a..d432079f 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -129,7 +129,7 @@ $(document).ready(function() { updateHistory(); }); - $(document).on("custom_reset", function(e) { + $(document).on("cancel_history", function(e) { khistory.selection.reset(); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 49f36327..910c5ab9 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -53,7 +53,7 @@ $(document).ready(function() { var history = new KHistory(); - $(document).on("custom_reset", function(e) { + $(document).on("cancel_history", function(e) { khistory.selection.reset(); }); From 8c02e5da0c36f6c7b93ae26cb513dcdf11764036 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 10 Apr 2017 15:40:29 -0300 Subject: [PATCH 70/90] Trigger event on history itself --- kfet/static/kfet/js/history.js | 4 ++-- kfet/static/kfet/js/kfet.api.js | 1 - kfet/static/kfet/js/kpsul.js | 2 +- kfet/templates/kfet/account_read.html | 2 +- kfet/templates/kfet/history.html | 2 +- kfet/templates/kfet/transfers.html | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 5b6ec75b..c7663509 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -1,4 +1,4 @@ -var cancelHistory = new Event("history_cancel"); +var cancelHistory = new Event("cancel_done"); class KHistory { @@ -72,7 +72,7 @@ class KHistory { } cancel_operations(to_cancel) { - var on_success = () => document.dispatchEvent(cancelHistory); + var on_success = () => $(this).trigger("cancel_done"); api_with_auth({ url: Urls['kfet.kpsul.cancel_operations'](), diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 0f6a078e..d7fd5823 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1016,7 +1016,6 @@ class ModelForest { var child_name = struct_data.children; if (data.children && data.children.length) { for (let child_data of data.children) { - console.log(child_data); var child = this.get_or_create(child_data.modelname, child_data.content, 1); var child_parent = struct[child_data.modelname]; child[child_parent] = node; diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 8833d072..05e3654c 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -43,7 +43,7 @@ class KPsulManager { _init_events() { var that = this ; - $(document).on("history_cancel", function(e) { + $(this.history).on("cancel_done", function(e) { that.reset(true); that.focus; }); diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 54b91de4..7fd2d9c7 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -109,7 +109,7 @@ $(document).ready(function() { var khistory = new KHistory({'no_trigramme': true}); - $(document).on("cancel_history", function(e) { + $(khistory).on("cancel_done", function(e) { khistory.selection.reset(); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index d432079f..43e8df93 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -129,7 +129,7 @@ $(document).ready(function() { updateHistory(); }); - $(document).on("cancel_history", function(e) { + $(khistory).on("cancel_history", function(e) { khistory.selection.reset(); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 910c5ab9..69e5eb33 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -53,7 +53,7 @@ $(document).ready(function() { var history = new KHistory(); - $(document).on("cancel_history", function(e) { + $(khistory).on("cancel_done", function(e) { khistory.selection.reset(); }); From de865c61aa01587cacca953c98cdc4ac542a92f3 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 14 Apr 2017 11:36:52 -0300 Subject: [PATCH 71/90] Move container clearing --- kfet/static/kfet/js/history.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index c7663509..772ac1bf 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -41,7 +41,6 @@ class KHistory { } fetch(api_options) { - this._$container.html(''); this.list.clear(); if (api_options) @@ -53,6 +52,7 @@ class KHistory { } display() { + this._$container.html(''); this.list.display(this._$container, this.templates, {}); var nb_opes = this._$container.find('.ope[canceled="false"]').length; $('#nb_opes').text(nb_opes); From 034a6614444999b57a861dbe682b5ae269a4b924 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 14 Apr 2017 12:12:43 -0300 Subject: [PATCH 72/90] Tweaks on kfet.js --- kfet/static/kfet/js/kfet.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index a2b245cf..a7e893b6 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -370,6 +370,8 @@ var authDialog = new UserDialog({ 'content': '
', }); + +//Note/TODO: the returned ajax object can be improved by allowing chaining on errors 403/400 function api_with_auth(settings, password) { if (window.api_lock == 1) return false; @@ -418,7 +420,7 @@ function api_with_auth(settings, password) { }); } -String.prototype.pluralize = function(count, irreg_plural = false) { +String.prototype.pluralize = function(count, irreg_plural) { if (Math.abs(count) >= 2) return irreg_plural ? irreg_plural : this+'s' ; return this ; From 659b20891e622b1d20c44f98bbefc7e0ae1d93c7 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 14 Apr 2017 12:43:59 -0300 Subject: [PATCH 73/90] Add filter to cancel_opes --- kfet/static/kfet/js/kpsul.js | 2 +- kfet/views.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 05e3654c..b6f15b6d 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -45,7 +45,7 @@ class KPsulManager { var that = this ; $(this.history).on("cancel_done", function(e) { that.reset(true); - that.focus; + that.focus(); }); } diff --git a/kfet/views.py b/kfet/views.py index 57bef710..481c4967 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1236,10 +1236,12 @@ def kpsul_cancel_operations(request): # Checking if BAD REQUEST (opes_pk not int or not existing) try: # Set pour virer les doublons - opes_post = set(map(lambda s: int(s), - request.POST.getlist('opes[]', []))) - transfers_post = set(map(lambda s: int(s), - request.POST.getlist('transfers[]', []))) + opes_post = ( + set(map(int, filter(None, request.POST.getlist('opes[]', [])))) + ) + transfers_post = ( + set(map(int, filter(None, request.POST.getlist('transfers[]', [])))) + ) except ValueError: return JsonResponse(data, status=400) From b544d6c5b3e2891cdced7bd9a9513cb3c398828a Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 14 Apr 2017 12:45:38 -0300 Subject: [PATCH 74/90] Better alignment --- kfet/views.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index 481c4967..0f25e647 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1332,11 +1332,13 @@ def kpsul_cancel_operations(request): # Note : si InventoryArticle est maj par .save(), stock_error # est recalculé automatiquement if ope.article and ope.article_nb: - last_stock = (InventoryArticle.objects - .select_related('inventory') - .filter(article=ope.article) - .order_by('inventory__at') - .last()) + last_stock = ( + InventoryArticle.objects + .select_related('inventory') + .filter(article=ope.article) + .order_by('inventory__at') + .last() + ) if not last_stock or last_stock.inventory.at < ope.group.at: to_articles_stocks[ope.article] += ope.article_nb From 100686457b66f225631a91366226057160074dc3 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 14 Apr 2017 12:47:15 -0300 Subject: [PATCH 75/90] Change select_related for future compatibility --- kfet/views.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index 0f25e647..ca6cab8d 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1472,15 +1472,13 @@ def history_json(request): # Construction de la requête (sur les opérations) pour le prefetch ope_queryset_prefetch = Operation.objects.select_related( - 'canceled_by__trigramme', 'addcost_for__trigramme', - 'article__name') + 'canceled_by', 'addcost_for', + 'article') ope_prefetch = Prefetch('opes', queryset=ope_queryset_prefetch) transfer_queryset_prefetch = Transfer.objects.select_related( - 'from_acc__trigramme', 'to_acc__trigramme', - 'from_acc__id', 'to_acc__id', - 'canceled_by__trigramme') + 'from_acc', 'to_acc', 'canceled_by') if accounts: transfer_queryset_prefetch = transfer_queryset_prefetch.filter( From b2a5dfd68269469a6eadb93325ff1738d2b5b7f4 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Fri, 14 Apr 2017 12:51:58 -0300 Subject: [PATCH 76/90] Move permission check --- kfet/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index ca6cab8d..16fdc44c 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1470,6 +1470,10 @@ def history_json(request): accounts = request.GET.getlist('accounts[]', None) transfers_only = request.GET.get('transfersonly', None) + # Un non-membre de l'équipe n'a que accès à son historique + if not request.user.has_perm('kfet.is_team'): + accounts = [request.user.profile.account] + # Construction de la requête (sur les opérations) pour le prefetch ope_queryset_prefetch = Operation.objects.select_related( 'canceled_by', 'addcost_for', @@ -1524,9 +1528,6 @@ def history_json(request): opegroups = OperationGroup.objects.none() if accounts: opegroups = opegroups.filter(on_acc_id__in=accounts) - # Un non-membre de l'équipe n'a que accès à son historique - if not request.user.has_perm('kfet.is_team'): - opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) # Construction de la réponse related_data = defaultdict(list) From e283439ebc34d329f0e9c1fe66ed13f8c2c2c067 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 24 Apr 2017 13:21:56 -0300 Subject: [PATCH 77/90] Create ForestDisplay class - Store templates, container and data - All display functions removed from ModelForest --- kfet/static/kfet/js/kfet.api.js | 223 ++++++++++++++++++++------------ 1 file changed, 138 insertions(+), 85 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index d7fd5823..19d89caa 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1049,91 +1049,6 @@ class ModelForest { this.from({}); } - /** - * Renders a node (and all its offspring) and returns the - * corresponding jQuery object. - * @param {Models.TreeNode} node - * @param {Object} templates Templates to render each model - * @param {Object} [options] Options for element render method - */ - render_element(node, templates, options) { - var modelname = node.constructor.verbose_name; - var struct_data = this.constructor.structure[modelname]; - - var template = templates[modelname]; - var options = options || {} ; - - var $container = $('
'); - $container.attr('id', modelname+'-'+node.id); - - var $rendered = node.display($(template), options); - $container.append($rendered); - - var children = this.get_children(node); - - if (children && children.length) { - if (struct_data.child_sort) - children.sort(struct_data.child_sort); - else - children.sort(children[0].constructor.compare); - - for (let child of children) { - var $child = this.render_element(child, templates, options); - $container.append($child); - } - } - - return $container; - } - - /** - * Renders node and adds it to given container.
- * Assumes that the inserted node is the 'youngest'. - * @param {jQuery} $container - * @param {Models.TreeNode} node - * @param {Object} templates Templates to render each model - * @param {Object} [options] Options for element render method - */ - add_to_container($container, node, templates, options) { - var struct = this.constructor.structure; - var existing = this.get_parent(node) ; - var first_missing = node; - - while (existing && !($container.find('#'+existing.modelname+'-'+existing.id))) { - first_missing = existing; - existing = this.get_parent(existing); - } - - var $to_insert = this.render_element(first_missing, templates, options); - if (existing) { - $container.find('#'+existing.constructor.verbose_name+'-'+existing.id+'>:first-child') - .after($to_insert); - } else { - $container.prepend($to_insert); - } - } - - /** - * Display stored data in container. - * @param {jQuery} $container - * @param {Object} templates Templates to render each model - * @param {Object} [options] Options for element render method - */ - display($container, templates, options) { - if (this.is_empty()) - return; - - if (this.constructor.root_sort) - this.roots.sort(this.constructor.root_sort); - else - this.roots.sort(this.roots[0].constructor.compare); - - for (let root of this.roots) { - $container.append(this.render_element(root, templates, options)); - } - - return $container; - } /** * Performs for each node (in a DFS order) the callback function @@ -1184,6 +1099,22 @@ class ModelForest { return result; } + + update(modelname, id, update_data) { + var updated = null ; + + function callback(node) { + if (node.id == id) { + node.update(update_data); + updated = node ; + return true ; + } + } + + this.traverse(modelname, callback); + + return updated; + } } @@ -1334,6 +1265,128 @@ class OperationList extends APIModelForest { } +class ForestDisplay { + + constructor($container, templates) { + this._templates = templates ; + this._$container = $container; + this.data = new ModelForest(); + } + + + /** + * Renders a node (and all its offspring) and returns the + * corresponding jQuery object. + * @param {Models.TreeNode} node + * @param {Object} templates Templates to render each model + * @param {Object} [options] Options for element render method + */ + render_element(node, options) { + var modelname = node.constructor.verbose_name; + var struct_data = this.data.constructor.structure[modelname]; + + var template = this._templates[modelname]; + var options = options || {} ; + + var $container = $('
'); + $container.attr('id', modelname+'-'+node.id); + + var $rendered = node.display($(template), options); + $container.append($rendered); + + var children = this.data.get_children(node); + + if (children && children.length) { + if (struct_data.child_sort) + children.sort(struct_data.child_sort); + else + children.sort(children[0].constructor.compare); + + for (let child of children) { + var $child = this.render_element(child, options); + $container.append($child); + } + } + + return $container; + } + + + /** + * Renders node and adds it to the container.
+ * Assumes that the inserted node is the 'youngest'. + * @param {Models.TreeNode} node + * @param {Object} [options] Options for element render method + */ + add(node, options) { + var struct = this.data.constructor.structure; + var existing = this.data.get_parent(node) ; + var first_missing = node; + + while (existing && !(this._$container.find('#'+existing.modelname+'-'+existing.id))) { + first_missing = existing; + existing = this.data.get_parent(existing); + } + + var $to_insert = this.render_element(first_missing, options); + //TODO better placement + if (existing) { + this._$container.find('#'+existing.constructor.verbose_name+'-'+existing.id+'>:first-child') + .after($to_insert); + } else { + this._$container.prepend($to_insert); + } + } + + + /** + * Updates stored data and displays it in container. + * @param {Models.ModelForest} data + * @param {Object} [options] Options for element render method + */ + render(data, options) { + if (data) { + this.data = data; + } + + var forest = this.data ; + + if (forest.is_empty()) + return; + + if (forest.constructor.root_sort) + forest.roots.sort(forest.constructor.root_sort); + else + forest.roots.sort(forest.roots[0].constructor.compare); + + for (let root of forest.roots) { + this._$container.append(this.render_element(root, options)); + } + + return this._$container; + } + + /** + * Updates the display with same modelname and id as the given data. + * @param {Object} data + */ + update(data) { + var modelname = data.constructor.verbose_name ; + var $new_elt = data.display($(this._templates[modelname]), {}); + + var $to_replace = this._$container.find('#'+modelname+'-'+data.id+'>:first-child'); + $to_replace.replaceWith($new_elt); + } + + /** + * Clears all elements from container + */ + clear() { + this._$container.html(''); + } +} + + /* ---------- ---------- */ /** From 46ac82fd2703a81285192fc679531195d07fb852 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 24 Apr 2017 13:25:07 -0300 Subject: [PATCH 78/90] Adapt history to changes --- kfet/static/kfet/js/history.js | 40 ++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index 772ac1bf..a5f0bccc 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -22,40 +22,42 @@ class KHistory { constructor(options) { var all_options = $.extend({}, this.constructor.default_options, options); - - this.templates = all_options.templates; this.api_options = all_options.api_options; this._$container = $('#history'); this._$nb_opes = $('#nb_opes'); - this.list = new OperationList(); + this.data = new OperationList(); if (!all_options.no_select) this.selection = new KHistorySelection(this); + var templates = all_options.templates if (all_options.no_trigramme) - this.templates['opegroup'] = '
' + templates['opegroup'] = + '
'; + + this.display = new ForestDisplay(this._$container, templates); this._init_events(); } fetch(api_options) { - this.list.clear(); + this.data.clear(); if (api_options) this.api_options = api_options; - this.list.fromAPI(this.api_options) - .done( () => this.display() ); + this.data.fromAPI(this.api_options) + .done( () => this.display_data() ); } - display() { - this._$container.html(''); - this.list.display(this._$container, this.templates, {}); + display_data() { + this.display.clear(); + this.display.render(this.data); var nb_opes = this._$container.find('.ope[canceled="false"]').length; - $('#nb_opes').text(nb_opes); + this._$nb_opes.text(nb_opes); } _init_events() { @@ -82,20 +84,16 @@ class KHistory { } add_node(data) { - var node = this.list.get_or_create(data.modelname, data.content, 0); - this.list.add_to_container(this._$container, node, this.templates); + var node = this.data.get_or_create(data.modelname, data.content, 0); + this.display.add(node); } - update_node(type, id, update_data) { - var to_update = this.list.find(type, id); - if (!to_update) + update_node(modelname, id, update_data) { + var updated = this.data.update(modelname, id, update_data) + if (!updated) return false; - to_update.update(update_data); - var $new_elt = to_update.display($(this.templates[type]), {}); - - var $to_replace = this._$container.find('#'+type+'-'+id+'>:first-child'); - $to_replace.replaceWith($new_elt); + this.display.update(updated); return true; } From 5096e5f12937ff0cdd252c21d79ced8e2139c4ab Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 24 Apr 2017 13:25:18 -0300 Subject: [PATCH 79/90] Adapt ArticleManager --- kfet/static/kfet/js/kpsul.js | 45 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index b6f15b6d..37246522 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -426,12 +426,16 @@ class ArticleManager { this._$input = $('#article_autocomplete'); this._$nb = $('#article_number'); this._$stock = $('#article_stock'); - this.templates = {'category': '
', - 'article' : '
'} this.selected = new Article() ; - this.list = new ArticleList() ; - this.autocomplete = new ArticleAutocomplete(this); + this.data = new ArticleList() ; + var $container = $('#articles_data'); + var templates = { + 'category': '
', + 'article' : '
' + } ; + this.display = new ForestDisplay($container, templates); + this.autocomplete = new ArticleAutocomplete(this, $container); this._init_events(); } @@ -441,7 +445,7 @@ class ArticleManager { } display_list() { - this.list.display(this._$container, this.templates) ; + this.display.render(this.data); } validate(article) { @@ -461,25 +465,18 @@ class ArticleManager { } reset_data() { - this._$container.html(''); - this.list.clear(); - this.list.fromAPI() + this.display.clear(); + this.data.clear(); + this.data.fromAPI() .done( () => this.display_list() ); } - get_article(id) { - return this.list.find('article', id); - } - update_data(data) { for (let article_dict of data.articles) { - var article = this.get_article(article_dict.id); - // For now, article additions are disregarded - if (article) { - article.stock = article_dict.stock; - this._$container.find('#article-'+article.id+' .stock') - .text(article.stock); + var updated = this.data.update('article', article_dict.id, article_dict); + if (updated) { + this.display.update(updated); } } } @@ -508,7 +505,7 @@ class ArticleManager { this._$container.on('click', '.article', function() { var id = $(this).parent().attr('id').split('-')[1]; - var article = that.list.find('article', id); + var article = that.data.find('article', id); if (article) that.validate(article); }); @@ -547,9 +544,9 @@ class ArticleManager { class ArticleAutocomplete { - constructor(article_manager) { + constructor(article_manager, $container) { this.manager = article_manager; - this._$container = article_manager._$container ; + this._$container = $container ; this._$input = $('#article_autocomplete'); this.showAll() ; @@ -583,7 +580,7 @@ class ArticleAutocomplete { update(prefix, backspace) { this.resetMatch(); - var article_list = this.manager.list ; + var article_list = this.manager.data ; var lower = prefix.toLowerCase() ; var that = this ; @@ -611,7 +608,7 @@ class ArticleAutocomplete { updateDisplay() { var that = this; - this.manager.list.traverse('category', function(category) { + this.manager.data.traverse('category', function(category) { var is_active = false; for (let article of category.articles) { if (that.matching.indexOf(article) != -1) { @@ -646,7 +643,7 @@ class ArticleAutocomplete { showAll() { var that = this; this.resetMatch(); - this.manager.list.traverse('article', function(article) { + this.manager.data.traverse('article', function(article) { that.matching.push(article); }); this.updateDisplay(); From 16dbfed9774ff60de0304b959a7594c9c7ec2baf Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 24 Apr 2017 14:12:03 -0300 Subject: [PATCH 80/90] Add chidren div in display --- kfet/static/kfet/js/kfet.api.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 19d89caa..d563a6e0 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1302,10 +1302,12 @@ class ForestDisplay { else children.sort(children[0].constructor.compare); + var $child_div = $('
'); for (let child of children) { var $child = this.render_element(child, options); - $container.append($child); + $child_div.append($child); } + $container.append($child_div); } return $container; @@ -1329,10 +1331,9 @@ class ForestDisplay { } var $to_insert = this.render_element(first_missing, options); - //TODO better placement if (existing) { - this._$container.find('#'+existing.constructor.verbose_name+'-'+existing.id+'>:first-child') - .after($to_insert); + this._$container.find('#'+existing.constructor.verbose_name+'-'+existing.id+'>.children') + .prepend($to_insert); } else { this._$container.prepend($to_insert); } From 8b8a3f8a256f414523eef35dda8506ca30416b4f Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 24 Apr 2017 14:46:16 -0300 Subject: [PATCH 81/90] Update button in history --- kfet/templates/kfet/history.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 43e8df93..4d711f47 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -39,6 +39,9 @@
Comptes {{ filter_form.accounts }}
+
@@ -125,7 +128,7 @@ $(document).ready(function() { filter: true, }); - $("input").on('dp.change change', function() { + $("#update_history").on('click', function() { updateHistory(); }); From 31b742fdb7e6f3791a41978cb3813780a5423431 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 14 May 2017 17:19:09 -0300 Subject: [PATCH 82/90] Move ws update to respective classes --- kfet/static/kfet/js/history.js | 3 +++ kfet/static/kfet/js/kpsul.js | 1 + kfet/templates/kfet/kpsul.html | 2 -- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index a5f0bccc..fbae1226 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -32,6 +32,9 @@ class KHistory { if (!all_options.no_select) this.selection = new KHistorySelection(this); + if (!all_options.static) + OperationWebSocket.add_handler(data => this.update_data(data)); + var templates = all_options.templates if (all_options.no_trigramme) templates['opegroup'] = diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 37246522..9b235972 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -438,6 +438,7 @@ class ArticleManager { this.autocomplete = new ArticleAutocomplete(this, $container); this._init_events(); + OperationWebSocket.add_handler(data => this.update_data(data)); } get nb() { diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index 36d4c3af..16119bce 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -759,8 +759,6 @@ $(document).ready(function() { // Synchronization // ----- - OperationWebSocket.add_handler(data => kpsul.history.update_data(data)); - OperationWebSocket.add_handler(data => kpsul.article_manager.update_data(data)); OperationWebSocket.add_handler(function(data) { if (data['addcost']) { Config.set('addcost_for', data['addcost']['for']); From c12c705f8b048076053d0c9dec9d44d285db5ca1 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Sun, 14 May 2017 20:18:31 -0300 Subject: [PATCH 83/90] Bind ForestDisplay to initial data --- kfet/static/kfet/js/history.js | 2 +- kfet/static/kfet/js/kfet.api.js | 13 ++++--------- kfet/static/kfet/js/kpsul.js | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index fbae1226..faf1e0e2 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -40,7 +40,7 @@ class KHistory { templates['opegroup'] = '
'; - this.display = new ForestDisplay(this._$container, templates); + this.display = new ForestDisplay(this._$container, templates, this.data); this._init_events(); } diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index d563a6e0..ae0d9bd1 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -1267,10 +1267,10 @@ class OperationList extends APIModelForest { class ForestDisplay { - constructor($container, templates) { + constructor($container, templates, data) { this._templates = templates ; this._$container = $container; - this.data = new ModelForest(); + this.data = data || new ModelForest(); } @@ -1341,15 +1341,10 @@ class ForestDisplay { /** - * Updates stored data and displays it in container. - * @param {Models.ModelForest} data + * Displays stored data in container. * @param {Object} [options] Options for element render method */ - render(data, options) { - if (data) { - this.data = data; - } - + render(options) { var forest = this.data ; if (forest.is_empty()) diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 9b235972..64a7a64e 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -434,7 +434,7 @@ class ArticleManager { 'category': '
', 'article' : '
' } ; - this.display = new ForestDisplay($container, templates); + this.display = new ForestDisplay($container, templates, this.data); this.autocomplete = new ArticleAutocomplete(this, $container); this._init_events(); From f4cb1e2e83c037d506edc20f4519a5a4a41dd6b4 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 14:17:58 -0300 Subject: [PATCH 84/90] Add opesonly option --- kfet/static/kfet/js/history.js | 3 +++ kfet/static/kfet/js/kpsul.js | 4 +++- kfet/views.py | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index faf1e0e2..d3bd6308 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -113,6 +113,9 @@ class KHistory { if (options.transfersonly && opegroup.constructor.verbose_name == 'opegroup') return false; + if (options.opesonly && opegroup.constructor.verbose_name == 'transfergroup') + return false; + if (options.accounts && options.accounts.length && options.accounts.indexOf(opegroup.account_id) < 0) return false; diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 64a7a64e..5645263d 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -12,7 +12,9 @@ class KPsulManager { this.account_manager = new AccountManager(this); this.checkout_manager = new CheckoutManager(this); this.article_manager = new ArticleManager(this); - this.history = new KHistory(); + this.history = new KHistory({ + api_options: {'opesonly': true}, + }); this._init_events(); } diff --git a/kfet/views.py b/kfet/views.py index e0fc6e35..acd9f0a2 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1468,6 +1468,7 @@ def history_json(request): checkouts = request.GET.getlist('checkouts[]', None) accounts = request.GET.getlist('accounts[]', None) transfers_only = request.GET.get('transfersonly', None) + opes_only = request.GET.get('opesonly', None) # Un non-membre de l'équipe n'a que accès à son historique if not request.user.has_perm('kfet.is_team'): @@ -1525,6 +1526,8 @@ def history_json(request): transfergroups = TransferGroup.objects.none() if transfers_only: opegroups = OperationGroup.objects.none() + if opes_only: + transfergroups = TransferGroup.objects.none() if accounts: opegroups = opegroups.filter(on_acc_id__in=accounts) From ad42687293374463e6cd65e75c61131afd2c558b Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 15:27:46 -0300 Subject: [PATCH 85/90] Fix tranfers page --- kfet/templates/kfet/transfers.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 69e5eb33..4acd737f 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -51,7 +51,7 @@ $(document).ready(function() { 'use strict'; - var history = new KHistory(); + var khistory = new KHistory(); $(khistory).on("cancel_done", function(e) { khistory.selection.reset(); @@ -61,9 +61,9 @@ $(document).ready(function() { // Synchronization // ----- - OperationWebSocket.add_handler((data) => history.update_data(data)); + OperationWebSocket.add_handler((data) => khistory.update_data(data)); - Config.reset(() => history.fetch({'transfersonly': true})); + Config.reset(() => khistory.fetch({'transfersonly': true})); }); From ac33e6302e184936dcd01642ffa16d29db204915 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 17:39:33 -0300 Subject: [PATCH 86/90] Fix addExistingPurchase + few other bugs --- kfet/static/kfet/js/kpsul.js | 13 +++++++++++-- kfet/templates/kfet/kpsul.html | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 5645263d..e6fbd0c9 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -519,18 +519,26 @@ class ArticleManager { that.reset(); that.focus(); } - if (normalKeys.test(e.keyCode) || arrowKeys.test(e.KeyCode) || e.ctrlKey) { + + if (normalKeys.test(e.keyCode) || arrowKeys.test(e.keyCode) || e.ctrlKey) { if (e.ctrlKey && e.charCode == 65) that._$nb.val(''); return true ; } + if (that.constructor.check_nb(that.nb+e.key)) return true; + return false; }); } + //Note : this function may not be needed after the whole rework + get_article(id) { + return this.data.find('article', id) ; + } + focus() { if (this.is_empty()) this._$input.focus(); @@ -561,11 +569,12 @@ class ArticleAutocomplete { // 8:Backspace|9:Tab|13:Enter|46:DEL|112-117:F1-6|119-123:F8-F12 var normalKeys = /^(8|9|13|46|112|113|114|115|116|117|119|120|121|122|123)$/; + var arrowKeys = /^(37|38|39|40)$/; this._$input .on('keydown', function(e) { var text = that._$input.val() ; - if (normalKeys.test(e.keyCode) || e.ctrlKey) { + if (normalKeys.test(e.keyCode) || arrowKeys.test(e.keyCode) || e.ctrlKey) { // For the backspace key, we suppose the cursor is at the very end if(e.keyCode == 8) { that.update(text.substring(0, text.length-1), true); diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index 16119bce..e94f5c6c 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -266,7 +266,7 @@ $(document).ready(function() { var article_basket_html = $(item_basket_default_html); article_basket_html .attr('data-opeindex', index) - .find('.number').text(nb).end() + .find('.number').text('('+nb+'/'+article.stock+')').end() .find('.name').text(article.name).end() .find('.amount').text(amountToUKF(amount_euro, kpsul.account_manager.account.is_cof)); basket_container.prepend(article_basket_html); From c2da055b605210e2637f0aabf93bbfac792a7f33 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 18:59:18 -0300 Subject: [PATCH 87/90] Remove duplicate ws updates --- kfet/templates/kfet/account_read.html | 6 ------ kfet/templates/kfet/history.html | 8 -------- kfet/templates/kfet/transfers.html | 6 ------ 3 files changed, 20 deletions(-) diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 3183fdc4..0ea0bdaa 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -108,12 +108,6 @@ $(document).ready(function() { khistory.selection.reset(); }); - // ----- - // Synchronization - // ----- - - OperationWebSocket.add_handler((data) => khistory.update_data(data)); - Config.reset(() => khistory.fetch({'accounts': [{{account.pk}}]})); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 4d711f47..51dcac38 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -136,14 +136,6 @@ $(document).ready(function() { khistory.selection.reset(); }); - // ----- - // Synchronization - // ----- - - OperationWebSocket.add_handler(data => khistory.update_data(data)); - - - Config.reset(updateHistory); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 4acd737f..500fcbc6 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -57,12 +57,6 @@ $(document).ready(function() { khistory.selection.reset(); }); - // ----- - // Synchronization - // ----- - - OperationWebSocket.add_handler((data) => khistory.update_data(data)); - Config.reset(() => khistory.fetch({'transfersonly': true})); }); From 93c8844b3f768eb449e2f7cb88651ca91add2951 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 19:28:24 -0300 Subject: [PATCH 88/90] typo --- kfet/templates/kfet/history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 51dcac38..60e675d2 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -132,7 +132,7 @@ $(document).ready(function() { updateHistory(); }); - $(khistory).on("cancel_history", function(e) { + $(khistory).on("cancel_done", function(e) { khistory.selection.reset(); }); From 43e772363efd235da72620274cd95cc97b55ba58 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 19:29:12 -0300 Subject: [PATCH 89/90] Extend history options --- kfet/static/kfet/js/history.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index d3bd6308..d161e081 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -48,8 +48,7 @@ class KHistory { fetch(api_options) { this.data.clear(); - if (api_options) - this.api_options = api_options; + $.extend(this.api_options, api_options); this.data.fromAPI(this.api_options) .done( () => this.display_data() ); From 6a6fc38ead8287374d6dc4fb1340191b2e4b8295 Mon Sep 17 00:00:00 2001 From: Ludovic Stephan Date: Mon, 15 May 2017 21:10:39 -0300 Subject: [PATCH 90/90] Add selection reset to cancel_opes --- kfet/static/kfet/js/history.js | 7 ++++++- kfet/templates/kfet/account_read.html | 4 ---- kfet/templates/kfet/history.html | 4 ---- kfet/templates/kfet/transfers.html | 4 ---- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js index d161e081..41c2f28f 100644 --- a/kfet/static/kfet/js/history.js +++ b/kfet/static/kfet/js/history.js @@ -76,7 +76,12 @@ class KHistory { } cancel_operations(to_cancel) { - var on_success = () => $(this).trigger("cancel_done"); + var that = this ; + var on_success = function() { + if (that.selection) + that.selection.reset() ; + $(that).trigger("cancel_done"); + } api_with_auth({ url: Urls['kfet.kpsul.cancel_operations'](), diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html index 0ea0bdaa..58f1f4a9 100644 --- a/kfet/templates/kfet/account_read.html +++ b/kfet/templates/kfet/account_read.html @@ -104,10 +104,6 @@ $(document).ready(function() { var khistory = new KHistory({'no_trigramme': true}); - $(khistory).on("cancel_done", function(e) { - khistory.selection.reset(); - }); - Config.reset(() => khistory.fetch({'accounts': [{{account.pk}}]})); }); diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 60e675d2..2075cf30 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -132,10 +132,6 @@ $(document).ready(function() { updateHistory(); }); - $(khistory).on("cancel_done", function(e) { - khistory.selection.reset(); - }); - Config.reset(updateHistory); }); diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 500fcbc6..d94ae73b 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -53,10 +53,6 @@ $(document).ready(function() { var khistory = new KHistory(); - $(khistory).on("cancel_done", function(e) { - khistory.selection.reset(); - }); - Config.reset(() => khistory.fetch({'transfersonly': true})); });