Merge branch 'issue63' into k-fet
This commit is contained in:
commit
3a777a7045
3 changed files with 201 additions and 2 deletions
|
@ -1,4 +1,13 @@
|
||||||
{% extends 'kfet/base.html' %}
|
{% 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 title %}Transferts{% endblock %}
|
||||||
{% block content-header-title %}Transferts{% endblock %}
|
{% block content-header-title %}Transferts{% endblock %}
|
||||||
|
@ -24,13 +33,13 @@
|
||||||
<h2>Liste des transferts</h2>
|
<h2>Liste des transferts</h2>
|
||||||
<div id="history">
|
<div id="history">
|
||||||
{% for transfergroup in transfergroups %}
|
{% for transfergroup in transfergroups %}
|
||||||
<div class="opegroup">
|
<div class="opegroup transfergroup" data-transfergroup="{{ transfergroup.pk }}">
|
||||||
<span>{{ transfergroup.at }}</span>
|
<span>{{ transfergroup.at }}</span>
|
||||||
<span>{{ transfergroup.valid_by.trigramme }}</span>
|
<span>{{ transfergroup.valid_by.trigramme }}</span>
|
||||||
<span>{{ transfergroup.comment }}</span>
|
<span>{{ transfergroup.comment }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% for transfer in transfergroup.transfers.all %}
|
{% 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="amount">{{ transfer.amount }} €</span>
|
||||||
<span class="from_acc">{{ transfer.from_acc.trigramme }}</span>
|
<span class="from_acc">{{ transfer.from_acc.trigramme }}</span>
|
||||||
<span class="glyphicon glyphicon-arrow-right"></span>
|
<span class="glyphicon glyphicon-arrow-right"></span>
|
||||||
|
@ -44,4 +53,93 @@
|
||||||
</div>
|
</div>
|
||||||
</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 %}
|
{% endblock %}
|
||||||
|
|
|
@ -170,6 +170,8 @@ urlpatterns = [
|
||||||
name = 'kfet.transfers.create'),
|
name = 'kfet.transfers.create'),
|
||||||
url(r'^transfers/perform$', views.perform_transfers,
|
url(r'^transfers/perform$', views.perform_transfers,
|
||||||
name = 'kfet.transfers.perform'),
|
name = 'kfet.transfers.perform'),
|
||||||
|
url(r'^transfers/cancel$', views.cancel_transfers,
|
||||||
|
name = 'kfet.transfers.cancel'),
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# Inventories urls
|
# Inventories urls
|
||||||
|
|
|
@ -1475,6 +1475,105 @@ def perform_transfers(request):
|
||||||
|
|
||||||
return JsonResponse(data)
|
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):
|
class InventoryList(ListView):
|
||||||
queryset = (Inventory.objects
|
queryset = (Inventory.objects
|
||||||
.select_related('by', 'order')
|
.select_related('by', 'order')
|
||||||
|
|
Loading…
Reference in a new issue