Merge branch 'Aufinal/transferts_historique' into Aufinal/refactor_history

This commit is contained in:
Ludovic Stephan 2017-03-11 00:45:54 -03:00
commit ab6b0d52f2
8 changed files with 451 additions and 289 deletions

View file

@ -40,6 +40,11 @@
width:90px; width:90px;
} }
#history .opegroup .infos {
text-align:center;
width:145px;
}
#history .opegroup .valid_by { #history .opegroup .valid_by {
padding-left:20px padding-left:20px
} }
@ -67,6 +72,10 @@
text-align:right; text-align:right;
} }
#history .ope .glyphicon {
padding-left:15px;
}
#history .ope .infos2 { #history .ope .infos2 {
padding-left:15px; padding-left:15px;
} }

View file

@ -34,10 +34,22 @@ function KHistory(options={}) {
var trigramme = opegroup['on_acc_trigramme']; var trigramme = opegroup['on_acc_trigramme'];
var is_cof = opegroup['is_cof']; var is_cof = opegroup['is_cof'];
for (var i=0; i<opegroup['opes'].length; i++) { var type = opegroup['type']
var $ope = this._opeHtml(opegroup['opes'][i], is_cof, trigramme); switch (type) {
$ope.data('opegroup', opegroup['id']); case 'opegroup':
$opegroup.after($ope); for (var i=0; i<opegroup['opes'].length; i++) {
var $ope = this._opeHtml(opegroup['opes'][i], is_cof, trigramme);
$ope.data('opegroup', opegroup['id']);
$opegroup.after($ope);
}
break;
case 'transfergroup':
for (var i=0; i<opegroup['opes'].length; i++) {
var $transfer = this._transferHtml(opegroup['opes'][i]);
$transfer.data('transfergroup', opegroup['id']);
$opegroup.after($transfer);
}
break;
} }
} }
@ -60,7 +72,8 @@ function KHistory(options={}) {
} }
$ope_html $ope_html
.data('ope', ope['id']) .data('type', 'ope')
.data('id', ope['id'])
.find('.amount').text(amount).end() .find('.amount').text(amount).end()
.find('.infos1').text(infos1).end() .find('.infos1').text(infos1).end()
.find('.infos2').text(infos2).end(); .find('.infos2').text(infos2).end();
@ -68,7 +81,7 @@ function KHistory(options={}) {
var addcost_for = ope['addcost_for__trigramme']; var addcost_for = ope['addcost_for__trigramme'];
if (addcost_for) { if (addcost_for) {
var addcost_amount = parseFloat(ope['addcost_amount']); 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']) if (ope['canceled_at'])
@ -77,9 +90,28 @@ function KHistory(options={}) {
return $ope_html; return $ope_html;
} }
this._transferHtml = function(transfer) {
var $transfer_html = $(this.template_transfer);
var parsed_amount = parseFloat(transfer['amount']);
var amount = parsed_amount.toFixed(2) + '€';
$transfer_html
.data('type', 'transfer')
.data('id', transfer['id'])
.find('.amount').text(amount).end()
.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 ;
}
this.cancelOpe = function(ope, $ope = null) { this.cancelOpe = function(ope, $ope = null) {
if (!$ope) if (!$ope)
$ope = this.findOpe(ope['id']); $ope = this.findOpe(ope['id'], ope['type']);
var cancel = 'Annulé'; var cancel = 'Annulé';
var canceled_at = dateUTCToParis(ope['canceled_at']); var canceled_at = dateUTCToParis(ope['canceled_at']);
@ -91,23 +123,39 @@ function KHistory(options={}) {
} }
this._opeGroupHtml = function(opegroup) { this._opeGroupHtml = function(opegroup) {
var $opegroup_html = $(this.template_opegroup);
var type = opegroup['type'];
switch (type) {
case 'opegroup':
var $opegroup_html = $(this.template_opegroup);
var trigramme = opegroup['on_acc__trigramme'];
var amount = amountDisplay(
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
break;
case 'transfergroup':
var $opegroup_html = $(this.template_transfergroup);
$opegroup_html.find('.infos').text('Transferts').end()
var trigramme = '';
var amount = '' ;
break;
}
var at = dateUTCToParis(opegroup['at']).format('HH:mm:ss'); var at = dateUTCToParis(opegroup['at']).format('HH:mm:ss');
var trigramme = opegroup['on_acc__trigramme'];
var amount = amountDisplay(
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
var comment = opegroup['comment'] || ''; var comment = opegroup['comment'] || '';
$opegroup_html $opegroup_html
.data('opegroup', opegroup['id']) .data('type', type)
.data('id', opegroup['id'])
.find('.time').text(at).end() .find('.time').text(at).end()
.find('.trigramme').text(trigramme).end()
.find('.amount').text(amount).end() .find('.amount').text(amount).end()
.find('.comment').text(comment).end() .find('.comment').text(comment).end();
.find('.trigramme').text(trigramme).end();
if (!this.display_trigramme) if (!this.display_trigramme)
$opegroup_html.find('.trigramme').remove(); $opegroup_html.find('.trigramme').remove();
$opegroup_html.find('.info').remove();
if (opegroup['valid_by__trigramme']) if (opegroup['valid_by__trigramme'])
$opegroup_html.find('.valid_by').text('Par '+opegroup['valid_by__trigramme']); $opegroup_html.find('.valid_by').text('Par '+opegroup['valid_by__trigramme']);
@ -133,9 +181,9 @@ function KHistory(options={}) {
}); });
} }
this.findOpe = function(id) { this.findOpe = function(id, type='ope') {
return this.$container.find('.ope').filter(function() { return this.$container.find('.ope').filter(function() {
return $(this).data('ope') == id return ($(this).data('id') == id && $(this).data('type') == type)
}); });
} }
@ -153,6 +201,8 @@ KHistory.default_options = {
container: '#history', container: '#history',
template_day: '<div class="day"></div>', template_day: '<div class="day"></div>',
template_opegroup: '<div class="opegroup"><span class="time"></span><span class="trigramme"></span><span class="amount"></span><span class="valid_by"></span><span class="comment"></span></div>', template_opegroup: '<div class="opegroup"><span class="time"></span><span class="trigramme"></span><span class="amount"></span><span class="valid_by"></span><span class="comment"></span></div>',
template_transfergroup: '<div class="opegroup"><span class="time"></span><span class="infos"></span><span class="valid_by"></span><span class="comment"></span></div>',
template_ope: '<div class="ope"><span class="amount"></span><span class="infos1"></span><span class="infos2"></span><span class="addcost"></span><span class="canceled"></span></div>', template_ope: '<div class="ope"><span class="amount"></span><span class="infos1"></span><span class="infos2"></span><span class="addcost"></span><span class="canceled"></span></div>',
template_transfer: '<div class="ope"><span class="amount"></span><span class="infos1"></span><span class="glyphicon glyphicon-arrow-right"></span><span class="infos2"></span><span class="canceled"></span></div>',
display_trigramme: true, display_trigramme: true,
} }

View file

@ -165,3 +165,9 @@ function requestAuth(data, callback, focus_next = null) {
}); });
} }
String.prototype.pluralize = function(count, irreg_plural = false) {
if (Math.abs(count) >= 2)
return irreg_plural ? irreg_plural : this+'s' ;
return this ;
}

View file

@ -7,6 +7,7 @@
<link rel="stylesheet" type="text/css" href="{% static 'kfet/css/bootstrap-datetimepicker.min.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'kfet/css/bootstrap-datetimepicker.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'kfet/css/multiple-select.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'kfet/css/multiple-select.css' %}">
<script type="text/javascript" src="{% static 'kfet/js/js.cookie.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/js.cookie.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/reconnecting-websocket.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/jquery-ui.min.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/jquery-confirm.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/moment.js' %}"></script>
@ -62,6 +63,8 @@
$(document).ready(function() { $(document).ready(function() {
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})} settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})}
lock = 0 ;
khistory = new KHistory(); khistory = new KHistory();
var $from_date = $('#from_date'); var $from_date = $('#from_date');
@ -142,9 +145,10 @@ $(document).ready(function() {
selected: function(e, ui) { selected: function(e, ui) {
$(ui.selected).each(function() { $(ui.selected).each(function() {
if ($(this).hasClass('opegroup')) { if ($(this).hasClass('opegroup')) {
var opegroup = $(this).data('opegroup'); var type = $(this).data('type');
var id = $(this).data('id');
$(this).siblings('.ope').filter(function() { $(this).siblings('.ope').filter(function() {
return $(this).data('opegroup') == opegroup return $(this).data(type) == id
}).addClass('ui-selected'); }).addClass('ui-selected');
} }
}); });
@ -156,7 +160,7 @@ $(document).ready(function() {
// DEL (Suppr) // DEL (Suppr)
var opes_to_cancel = []; var opes_to_cancel = [];
khistory.$container.find('.ope.ui-selected').each(function () { 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) if (opes_to_cancel.length > 0)
confirmCancel(opes_to_cancel); confirmCancel(opes_to_cancel);
@ -165,7 +169,7 @@ $(document).ready(function() {
function confirmCancel(opes_to_cancel) { function confirmCancel(opes_to_cancel) {
var nb = opes_to_cancel.length; var nb = opes_to_cancel.length;
var content = nb+" opérations vont être annulées"; var content = nb+' opération va être annulée'.pluralize(nb, ' opérations vont être annulées')
$.confirm({ $.confirm({
title: 'Confirmation', title: 'Confirmation',
content: content, content: content,
@ -179,50 +183,10 @@ $(document).ready(function() {
}); });
} }
function requestAuth(data, callback) {
var content = getErrorsHtml(data);
content += '<input type="password" name="password" autofocus>',
$.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 += '<ul>';
for (var i=0; i<data['errors']['missing_perms'].length; i++)
content += '<li>'+data['errors']['missing_perms'][i]+'</li>';
content += '</ul>';
}
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; i<data['errors']['negative'].length; i++) {
content += '<a class="btn btn-primary" href="'+url_base+data['errors']['negative'][i]+'/edit" target="_blank">Autorisation de négatif requise pour '+data['errors']['negative'][i]+'</a>';
}
}
return content;
}
function cancelOperations(opes_array, password = '') { function cancelOperations(opes_array, password = '') {
if (lock == 1)
return false
lock = 1 ;
var data = { 'operations' : opes_array } var data = { 'operations' : opes_array }
$.ajax({ $.ajax({
dataType: "json", dataType: "json",
@ -238,6 +202,7 @@ $(document).ready(function() {
}) })
.done(function(data) { .done(function(data) {
khistory.$container.find('.ui-selected').removeClass('ui-selected'); khistory.$container.find('.ui-selected').removeClass('ui-selected');
lock = 0 ;
}) })
.fail(function($xhr) { .fail(function($xhr) {
var data = $xhr.responseJSON; var data = $xhr.responseJSON;
@ -251,10 +216,37 @@ $(document).ready(function() {
displayErrors(getErrorsHtml(data)); displayErrors(getErrorsHtml(data));
break; break;
} }
lock = 0 ;
}); });
} }
// -----
// 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<data['opegroups'].length; i++) {
if (data['opegroups'][i]['cancellation']) {
khistory.cancelOpeGroup(data['opegroups'][i]);
}
}
for (var i=0; i<data['opes'].length; i++) {
if (data['opes'][i]['cancellation']) {
khistory.cancelOpe(data['opes'][i]);
}
}
}
getHistory(); getHistory();
}); });

View file

@ -765,7 +765,7 @@ $(document).ready(function() {
// DEL (Suppr) // DEL (Suppr)
var opes_to_cancel = []; var opes_to_cancel = [];
khistory.$container.find('.ope.ui-selected').each(function () { 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) if (opes_to_cancel.length > 0)
cancelOperations(opes_to_cancel); cancelOperations(opes_to_cancel);

View file

@ -4,9 +4,14 @@
{% block extra_head %} {% block extra_head %}
<link rel="stylesheet" style="text/css" href="{% static 'kfet/css/jquery-ui.min.css' %}"> <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/js.cookie.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/reconnecting-websocket.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/jquery-ui.min.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/jquery-confirm.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment-fr.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment-timezone-with-data-2010-2020.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
{% endblock %} {% endblock %}
{% block title %}Transferts{% endblock %} {% block title %}Transferts{% endblock %}
@ -31,22 +36,7 @@
<div class="content-right"> <div class="content-right">
<div class="content-right-block"> <div class="content-right-block">
<h2>Liste des transferts</h2> <h2>Liste des transferts</h2>
<div id="history"> <table id="history" class="table">
{% for transfergroup in transfergroups %}
<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{% 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>
<span class="to_acc">{{ transfer.to_acc.trigramme }}</span>
</div>
{% endfor %}
{% endfor %}
</table> </table>
</div> </div>
</div> </div>
@ -70,15 +60,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({ $.ajax({
dataType: "json", dataType: "json",
url : "{% url 'kfet.transfers.cancel' %}", url : "{% url 'kfet.history.json' %}",
method : "POST",
data : data,
})
.done(function(data) {
for (var i=0; i<data['opegroups'].length; i++) {
khistory.addOpeGroup(data['opegroups'][i]);
}
});
}
khistory.$container.selectable({
filter: 'div.opegroup, div.ope',
selected: function(e, ui) {
$(ui.selected).each(function() {
if ($(this).hasClass('opegroup')) {
var type = $(this).data('type');
var id = $(this).data('id');
$(this).siblings('.ope').filter(function() {
return $(this).data(type) == id
}).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('type')+' '+$(this).data('id'));
});
if (opes_to_cancel.length > 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", method : "POST",
data : data, data : data,
beforeSend: function ($xhr) { beforeSend: function ($xhr) {
@ -89,11 +144,7 @@ $(document).ready(function() {
}) })
.done(function(data) { .done(function(data) {
for (var i=0; i<data['canceled'].length; i++) { khistory.$container.find('.ui-selected').removeClass('ui-selected');
$('#history').find('.transfer[data-transfer='+data['canceled'][i]+']')
.addClass('canceled');
}
$('#history').find('.ui-selected').removeClass('ui-selected');
lock = 0; lock = 0;
}) })
.fail(function($xhr) { .fail(function($xhr) {
@ -101,7 +152,7 @@ $(document).ready(function() {
switch ($xhr.status) { switch ($xhr.status) {
case 403: case 403:
requestAuth(data, function(password) { requestAuth(data, function(password) {
cancelTransfers(transfers_array, password); cancelOperations(opes_array, password);
}); });
break; break;
case 400: case 400:
@ -112,31 +163,28 @@ $(document).ready(function() {
}); });
} }
$('#history').selectable({ // -----
filter: 'div.transfergroup, div.transfer', // Synchronization
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) { websocket_msg_default = {'opes':[]}
// DEL (Suppr)
var transfers_to_cancel = []; var websocket_protocol = window.location.protocol == 'https:' ? 'wss' : 'ws';
$('#history').find('.transfer.ui-selected').each(function () { var location_host = window.location.host;
transfers_to_cancel.push($(this).attr('data-transfer')); 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/");
if (transfers_to_cancel.length > 0) socket.onmessage = function(e) {
cancelTransfers(transfers_to_cancel); data = $.extend({}, websocket_msg_default, JSON.parse(e.data));
for (var i=0; i<data['opes'].length; i++) {
if (data['opes'][i]['cancellation']) {
khistory.cancelOpe(data['opes'][i]);
}
} }
}); }
getHistory();
}); });

View file

@ -175,8 +175,6 @@ 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

View file

@ -19,7 +19,7 @@ from django.http import HttpResponse, JsonResponse, Http404
from django.forms import modelformset_factory, formset_factory from django.forms import modelformset_factory, formset_factory
from django.forms.models import model_to_dict from django.forms.models import model_to_dict
from django.db import IntegrityError, transaction 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.db.models.functions import Coalesce
from django.utils import timezone from django.utils import timezone
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
@ -27,7 +27,7 @@ from gestioncof.models import CofProfile, Clipper
from kfet.decorators import teamkfet_required from kfet.decorators import teamkfet_required
from kfet.models import (Account, Checkout, Article, Settings, AccountNegative, from kfet.models import (Account, Checkout, Article, Settings, AccountNegative,
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory, CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
InventoryArticle, Order, OrderArticle) InventoryArticle, Order, OrderArticle, TransferGroup, Transfer)
from kfet.forms import * from kfet.forms import *
from collections import defaultdict from collections import defaultdict
from kfet import consumers from kfet import consumers
@ -1052,6 +1052,7 @@ def kpsul_perform_operations(request):
websocket_data = {} websocket_data = {}
websocket_data['opegroups'] = [{ websocket_data['opegroups'] = [{
'add': True, 'add': True,
'type': 'opegroup',
'id': operationgroup.pk, 'id': operationgroup.pk,
'amount': operationgroup.amount, 'amount': operationgroup.amount,
'checkout__name': operationgroup.checkout.name, 'checkout__name': operationgroup.checkout.name,
@ -1093,40 +1094,66 @@ def kpsul_perform_operations(request):
consumers.KPsul.group_send('kfet.kpsul', websocket_data) consumers.KPsul.group_send('kfet.kpsul', websocket_data)
return JsonResponse(data) return JsonResponse(data)
@teamkfet_required @teamkfet_required
def kpsul_cancel_operations(request): def kpsul_cancel_operations(request):
# Pour la réponse # Pour la réponse
data = { 'canceled': [], 'warnings': {}, 'errors': {}} data = {'canceled': {}, 'warnings': {}, 'errors': {}}
# Checking if BAD REQUEST (opes_pk not int or not existing) # Checking if BAD REQUEST (opes_pk not int or not existing)
try: try:
# Set pour virer les doublons # 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[]', []))))
transfers_post = \
set(map(lambda s: int(s.split()[1]),
filter(lambda s: s.split()[0] == 'transfer',
request.POST.getlist('operations[]', []))))
except ValueError: except ValueError:
return JsonResponse(data, status=400) return JsonResponse(data, status=400)
opes_all = ( opes_all = (
Operation.objects Operation.objects
.select_related('group', 'group__on_acc', 'group__on_acc__negative') .select_related('group', 'group__on_acc', 'group__on_acc__negative')
.filter(pk__in=opes_post)) .filter(pk__in=opes_post))
opes_pk = [ ope.pk for ope in opes_all ] opes_pk = [ope.pk for ope in opes_all]
opes_notexisting = [ ope for ope in opes_post if ope not in opes_pk ] 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) return JsonResponse(data, status=400)
opes_already_canceled = [] # Déjà annulée already_canceled = {} # Opération/Transfert déjà annulé
opes = [] # Pas déjà annulée opes = [] # Pas déjà annulée
transfers = []
required_perms = set() required_perms = set()
stop_all = False stop_all = False
cancel_duration = Settings.CANCEL_DURATION() cancel_duration = Settings.CANCEL_DURATION()
to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes # Modifs à faire sur les balances des comptes
to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé to_accounts_balances = defaultdict(lambda: 0)
to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses # ------ sur les montants des groupes d'opé
to_articles_stocks = defaultdict(lambda:0) # ------ sur les stocks d'articles 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: for ope in opes_all:
if ope.canceled_at: if ope.canceled_at:
# Opération déjà annulée, va pour un warning en Response # 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: else:
opes.append(ope.pk) opes.append(ope.pk)
# Si opé il y a plus de CANCEL_DURATION, permission requise # Si opé il y a plus de CANCEL_DURATION, permission requise
@ -1153,14 +1180,15 @@ def kpsul_cancel_operations(request):
# par `.save()`, amount_error est recalculé automatiquement, # par `.save()`, amount_error est recalculé automatiquement,
# ce qui n'est pas le cas en faisant un update sur queryset # 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 # TODO ? : Maj les balance_old de relevés pour modifier l'erreur
last_statement = (CheckoutStatement.objects last_statement = \
.filter(checkout=ope.group.checkout) (CheckoutStatement.objects
.order_by('at') .filter(checkout=ope.group.checkout)
.last()) .order_by('at')
.last())
if not last_statement or last_statement.at < ope.group.at: if not last_statement or last_statement.at < ope.group.at:
if ope.type == Operation.PURCHASE: if ope.type == Operation.PURCHASE:
if ope.group.on_acc.is_cash: if ope.group.on_acc.is_cash:
to_checkouts_balances[ope.group.checkout] -= - ope.amount to_checkouts_balances[ope.group.checkout] -= -ope.amount
else: else:
to_checkouts_balances[ope.group.checkout] -= ope.amount to_checkouts_balances[ope.group.checkout] -= ope.amount
@ -1173,22 +1201,38 @@ def kpsul_cancel_operations(request):
# est recalculé automatiquement # est recalculé automatiquement
if ope.article and ope.article_nb: if ope.article and ope.article_nb:
last_stock = (InventoryArticle.objects last_stock = (InventoryArticle.objects
.select_related('inventory') .select_related('inventory')
.filter(article=ope.article) .filter(article=ope.article)
.order_by('inventory__at') .order_by('inventory__at')
.last()) .last())
if not last_stock or last_stock.inventory.at < ope.group.at: if not last_stock or last_stock.inventory.at < ope.group.at:
to_articles_stocks[ope.article] += ope.article_nb to_articles_stocks[ope.article] += ope.article_nb
if not opes: for transfer in transfers_all:
data['warnings']['already_canceled'] = opes_already_canceled 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) return JsonResponse(data)
negative_accounts = [] negative_accounts = []
# Checking permissions or stop # Checking permissions or stop
for account in to_accounts_balances: for account in to_accounts_balances:
(perms, stop) = account.perms_to_perform_operation( (perms, stop) = account.perms_to_perform_operation(
amount = to_accounts_balances[account]) amount=to_accounts_balances[account])
required_perms |= perms required_perms |= perms
stop_all = stop_all or stop stop_all = stop_all or stop
if stop: if stop:
@ -1208,25 +1252,31 @@ def kpsul_cancel_operations(request):
with transaction.atomic(): with transaction.atomic():
(Operation.objects.filter(pk__in=opes) (Operation.objects.filter(pk__in=opes)
.update(canceled_by=canceled_by, canceled_at=canceled_at)) .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: for account in to_accounts_balances:
Account.objects.filter(pk=account.pk).update( 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: for checkout in to_checkouts_balances:
Checkout.objects.filter(pk=checkout.pk).update( 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: for group in to_groups_amounts:
OperationGroup.objects.filter(pk=group.pk).update( 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: for article in to_articles_stocks:
Article.objects.filter(pk=article.pk).update( 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
websocket_data = { 'opegroups': [], 'opes': [], 'checkouts': [], 'articles': [] } websocket_data = {'opegroups': [], 'opes': [],
'checkouts': [], 'articles': []}
# Need refresh from db cause we used update on querysets # 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 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: for opegroup in opegroups:
websocket_data['opegroups'].append({ websocket_data['opegroups'].append({
'cancellation': True, 'cancellation': True,
@ -1238,20 +1288,31 @@ def kpsul_cancel_operations(request):
for ope in opes: for ope in opes:
websocket_data['opes'].append({ websocket_data['opes'].append({
'cancellation': True, 'cancellation': True,
'type': 'ope',
'id': ope, 'id': ope,
'canceled_by__trigramme': canceled_by__trigramme, 'canceled_by__trigramme': canceled_by__trigramme,
'canceled_at': canceled_at, '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 # 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 checkouts = (Checkout.objects
.values('id', 'balance').filter(pk__in=checkouts_pk)) .values('id', 'balance')
.filter(pk__in=checkouts_pk))
for checkout in checkouts: for checkout in checkouts:
websocket_data['checkouts'].append({ websocket_data['checkouts'].append({
'id': checkout['id'], 'id': checkout['id'],
'balance': checkout['balance']}) 'balance': checkout['balance']})
# Need refresh from db cause we used update on querysets # 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) articles = Article.objects.values('id', 'stock').filter(pk__in=articles_pk)
for article in articles: for article in articles:
websocket_data['articles'].append({ websocket_data['articles'].append({
@ -1259,56 +1320,92 @@ def kpsul_cancel_operations(request):
'stock': article['stock']}) 'stock': article['stock']})
consumers.KPsul.group_send('kfet.kpsul', websocket_data) consumers.KPsul.group_send('kfet.kpsul', websocket_data)
data['canceled'] = opes data['canceled']['opes'] = opes
if opes_already_canceled: data['canceled']['transfers'] = transfers
data['warnings']['already_canceled'] = opes_already_canceled if already_canceled:
data['warnings']['already_canceled'] = already_canceled
return JsonResponse(data) return JsonResponse(data)
@login_required @login_required
def history_json(request): def history_json(request):
# Récupération des paramètres # Récupération des paramètres
from_date = request.POST.get('from', None) from_date = request.POST.get('from', None)
to_date = request.POST.get('to', None) to_date = request.POST.get('to', None)
limit = request.POST.get('limit', None);
checkouts = request.POST.getlist('checkouts[]', 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 # 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', 'canceled_by__trigramme', 'addcost_for__trigramme',
'article__name') 'article__name')
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')
if accounts:
transfer_queryset_prefetch = transfer_queryset_prefetch.filter(
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
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 # Construction de la requête principale
opegroups = (OperationGroup.objects opegroups = (
.prefetch_related(Prefetch('opes', queryset = queryset_prefetch)) OperationGroup.objects
.select_related('on_acc__trigramme', 'valid_by__trigramme') .prefetch_related(ope_prefetch)
.order_by('at') .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 # Application des filtres
if from_date: if from_date:
opegroups = opegroups.filter(at__gte=from_date) opegroups = opegroups.filter(at__gte=from_date)
transfergroups = transfergroups.filter(at__gte=from_date)
if to_date: if to_date:
opegroups = opegroups.filter(at__lt=to_date) opegroups = opegroups.filter(at__lt=to_date)
transfergroups = transfergroups.filter(at__lt=to_date)
if checkouts: if checkouts:
opegroups = opegroups.filter(checkout_id__in=checkouts) opegroups = opegroups.filter(checkout_id__in=checkouts)
transfergroups = TransferGroup.objects.none()
if transfers_only:
opegroups = OperationGroup.objects.none()
if accounts: if accounts:
opegroups = opegroups.filter(on_acc_id__in=accounts) opegroups = opegroups.filter(on_acc_id__in=accounts)
# Un non-membre de l'équipe n'a que accès à son historique # Un non-membre de l'équipe n'a que accès à son historique
if not request.user.has_perm('kfet.is_team'): if not request.user.has_perm('kfet.is_team'):
opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet)
if limit:
opegroups = opegroups[:limit]
# Construction de la réponse # Construction de la réponse
ope_list = [] ope_list = []
for opegroup in opegroups: for opegroup in opegroups:
opegroup_dict = { opegroup_dict = {
'id' : opegroup.id, 'id': opegroup.id,
'amount' : opegroup.amount, 'type': 'opegroup',
'at' : opegroup.at, 'amount': opegroup.amount,
'is_cof' : opegroup.is_cof, 'at': opegroup.at,
'comment' : opegroup.comment, 'is_cof': opegroup.is_cof,
'comment': opegroup.comment,
'trigramme': 'trigramme':
opegroup.on_acc and opegroup.on_acc.trigramme or None, opegroup.on_acc and opegroup.on_acc.trigramme or None,
'day': {'id': opegroup.at.strftime('%Y%m%d'), 'day': {'id': opegroup.at.strftime('%Y%m%d'),
@ -1319,13 +1416,13 @@ def history_json(request):
opegroup.valid_by and opegroup.valid_by.trigramme or None) opegroup.valid_by and opegroup.valid_by.trigramme or None)
for ope in opegroup.opes.all(): for ope in opegroup.opes.all():
ope_dict = { ope_dict = {
'id' : ope.id, 'id': ope.id,
'type' : ope.type, 'type': ope.type,
'amount' : ope.amount, 'amount': ope.amount,
'article_nb' : ope.article_nb, 'article_nb': ope.article_nb,
'is_checkout' : ope.is_checkout, 'is_checkout': ope.is_checkout,
'addcost_amount': ope.addcost_amount, 'addcost_amount': ope.addcost_amount,
'canceled_at' : ope.canceled_at, 'canceled_at': ope.canceled_at,
'article_name': 'article_name':
ope.article and ope.article.name or None, ope.article and ope.article.name or None,
'addcost_for': 'addcost_for':
@ -1336,8 +1433,41 @@ def history_json(request):
ope_dict['canceled_by'] = ( ope_dict['canceled_by'] = (
ope.canceled_by and ope.canceled_by.trigramme or None) ope.canceled_by and ope.canceled_by.trigramme or None)
ope_list.append(ope_dict) ope_list.append(ope_dict)
return JsonResponse(ope_list, safe=False)
for transfergroup in transfergroups:
if transfergroup.filtered_transfers:
transfergroup_dict = {
'id': transfergroup.id,
'type': 'transfergroup',
'at': transfergroup.at,
'comment': transfergroup.comment,
'day': {'id': transfergroup.at.strftime('%Y%m%d'),
'date': transfergroup.at},
}
if request.user.has_perm('kfet.is_team'):
transfergroup_dict['valid_by'] = (
transfergroup.valid_by and
transfergroup.valid_by.trigramme or
None)
for transfer in transfergroup.filtered_transfers:
transfer_dict = {
'id': 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,
}
if request.user.has_perm('kfet.is_team'):
transfer_dict['canceled_by'] = (
transfer.canceled_by and
transfer.canceled_by.trigramme or
None)
ope_list.append(transfer_dict)
return JsonResponse(ope_list, safe=False)
@teamkfet_required @teamkfet_required
def kpsul_articles_data(request): def kpsul_articles_data(request):
@ -1418,20 +1548,24 @@ def transfers_create(request):
return render(request, 'kfet/transfers_create.html', return render(request, 'kfet/transfers_create.html',
{ 'transfer_formset': transfer_formset }) { 'transfer_formset': transfer_formset })
@teamkfet_required @teamkfet_required
def perform_transfers(request): def perform_transfers(request):
data = { 'errors': {}, 'transfers': [], 'transfergroup': 0 } data = {'errors': {}, 'transfers': [], 'transfergroup': 0}
# Checking transfer_formset # Checking transfer_formset
transfer_formset = TransferFormSet(request.POST) transfer_formset = TransferFormSet(request.POST)
if not transfer_formset.is_valid(): 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 # Initializing vars
required_perms = set(['kfet.add_transfer']) # Required perms to perform all transfers # Required perms to perform all transfers
to_accounts_balances = defaultdict(lambda:0) # For balances of accounts required_perms = set(['kfet.add_transfer'])
# For balances of accounts
to_accounts_balances = defaultdict(lambda: 0)
for transfer in transfers: for transfer in transfers:
to_accounts_balances[transfer.from_acc] -= transfer.amount to_accounts_balances[transfer.from_acc] -= transfer.amount
@ -1443,7 +1577,7 @@ def perform_transfers(request):
# Checking if ok on all accounts # Checking if ok on all accounts
for account in to_accounts_balances: for account in to_accounts_balances:
(perms, stop) = account.perms_to_perform_operation( (perms, stop) = account.perms_to_perform_operation(
amount = to_accounts_balances[account]) amount=to_accounts_balances[account])
required_perms |= perms required_perms |= perms
stop_all = stop_all or stop stop_all = stop_all or stop
if stop: if stop:
@ -1469,7 +1603,7 @@ def perform_transfers(request):
# Updating balances accounts # Updating balances accounts
for account in to_accounts_balances: for account in to_accounts_balances:
Account.objects.filter(pk=account.pk).update( 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() account.refresh_from_db()
if account.balance < 0: if account.balance < 0:
if hasattr(account, 'negative'): if hasattr(account, 'negative'):
@ -1478,10 +1612,10 @@ def perform_transfers(request):
account.negative.save() account.negative.save()
else: else:
negative = AccountNegative( negative = AccountNegative(
account = account, start = timezone.now()) account=account, start=timezone.now())
negative.save() negative.save()
elif (hasattr(account, 'negative') elif (hasattr(account, 'negative') and
and not account.negative.balance_offset): not account.negative.balance_offset):
account.negative.delete() account.negative.delete()
# Saving transfer group # Saving transfer group
@ -1494,106 +1628,31 @@ def perform_transfers(request):
transfer.save() transfer.save()
data['transfers'].append(transfer.pk) 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) 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