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 @@
{% 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')