Annulation transferts
Possibilité d'annuler un (ou plusieurs) transferts depuis la page "Transferts"
This commit is contained in:
parent
fc90ab1379
commit
1ce16f48f6
|
@ -1,4 +1,13 @@
|
|||
{% extends 'kfet/base.html' %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" style="text/css" href="{% static 'kfet/css/jquery-ui.min.css' %}">
|
||||
<script type="text/javascript" src="{% static 'kfet/js/js.cookie.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/jquery-ui.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/jquery-confirm.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}Transferts{% endblock %}
|
||||
{% block content-header-title %}Transferts{% endblock %}
|
||||
|
@ -24,13 +33,13 @@
|
|||
<h2>Liste des transferts</h2>
|
||||
<div id="history">
|
||||
{% for transfergroup in transfergroups %}
|
||||
<div class="opegroup">
|
||||
<div class="opegroup transfergroup" data-transfergroup="{{ transfergroup.pk }}">
|
||||
<span>{{ transfergroup.at }}</span>
|
||||
<span>{{ transfergroup.valid_by.trigramme }}</span>
|
||||
<span>{{ transfergroup.comment }}</span>
|
||||
</div>
|
||||
{% for transfer in transfergroup.transfers.all %}
|
||||
<div class="ope transfer">
|
||||
<div class="ope transfer{% if transfer.canceled_at %} canceled{% endif %}" data-transfer="{{ transfer.pk }}" data-transfergroup="{{ transfergroup.pk }}">
|
||||
<span class="amount">{{ transfer.amount }} €</span>
|
||||
<span class="from_acc">{{ transfer.from_acc.trigramme }}</span>
|
||||
<span class="glyphicon glyphicon-arrow-right"></span>
|
||||
|
@ -44,4 +53,93 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
lock = 0;
|
||||
|
||||
function displayErrors(html) {
|
||||
$.alert({
|
||||
title: 'Erreurs',
|
||||
content: html,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: 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' %}",
|
||||
method : "POST",
|
||||
data : data,
|
||||
beforeSend: function ($xhr) {
|
||||
$xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
if (password != '')
|
||||
$xhr.setRequestHeader("KFetPassword", password);
|
||||
},
|
||||
|
||||
})
|
||||
.done(function(data) {
|
||||
for (var i=0; i<data['canceled'].length; i++) {
|
||||
$('#history').find('.transfer[data-transfer='+data['canceled'][i]+']')
|
||||
.addClass('canceled');
|
||||
}
|
||||
$('#history').find('.ui-selected').removeClass('ui-selected');
|
||||
lock = 0;
|
||||
})
|
||||
.fail(function($xhr) {
|
||||
var data = $xhr.responseJSON;
|
||||
switch ($xhr.status) {
|
||||
case 403:
|
||||
requestAuth(data, function(password) {
|
||||
cancelTransfers(transfers_array, password);
|
||||
});
|
||||
break;
|
||||
case 400:
|
||||
displayErrors(getErrorsHtml(data));
|
||||
break;
|
||||
}
|
||||
lock = 0;
|
||||
});
|
||||
}
|
||||
|
||||
$('#history').selectable({
|
||||
filter: 'div.transfergroup, div.transfer',
|
||||
selected: function(e, ui) {
|
||||
$(ui.selected).each(function() {
|
||||
if ($(this).hasClass('transfergroup')) {
|
||||
var transfergroup = $(this).attr('data-transfergroup');
|
||||
$(this).siblings('.ope').filter(function() {
|
||||
return $(this).attr('data-transfergroup') == transfergroup
|
||||
}).addClass('ui-selected');
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
$(document).on('keydown', function (e) {
|
||||
if (e.keyCode == 46) {
|
||||
// DEL (Suppr)
|
||||
var transfers_to_cancel = [];
|
||||
$('#history').find('.transfer.ui-selected').each(function () {
|
||||
transfers_to_cancel.push($(this).attr('data-transfer'));
|
||||
});
|
||||
if (transfers_to_cancel.length > 0)
|
||||
cancelTransfers(transfers_to_cancel);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in a new issue