diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index 1875a21e..cbdf0fe3 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -1,4 +1,13 @@ {% extends 'kfet/base.html' %} +{% load staticfiles %} + +{% block extra_head %} + + + + + +{% endblock %} {% block title %}Transferts{% endblock %} {% block content-header-title %}Transferts{% endblock %} @@ -24,13 +33,13 @@

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 }} @@ -44,4 +53,93 @@
+ + {% endblock %} diff --git a/kfet/urls.py b/kfet/urls.py index e0bacf9a..9b9ebf21 100644 --- a/kfet/urls.py +++ b/kfet/urls.py @@ -170,6 +170,8 @@ 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 98c4760a..4b115942 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1477,6 +1477,105 @@ 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 .select_related('by', 'order')