forked from DGNum/gestioCOF
Merge branch 'Aufinal/transferts_historique' of git.eleves.ens.fr:cof-geek/gestioCOF into Aufinal/transferts_historique
This commit is contained in:
commit
8895daff6a
7 changed files with 313 additions and 251 deletions
|
@ -40,6 +40,11 @@
|
|||
width:90px;
|
||||
}
|
||||
|
||||
#history .opegroup .info {
|
||||
text-align:center;
|
||||
width:145px;
|
||||
}
|
||||
|
||||
#history .opegroup .valid_by {
|
||||
padding-left:20px
|
||||
}
|
||||
|
@ -67,6 +72,10 @@
|
|||
text-align:right;
|
||||
}
|
||||
|
||||
#history .ope .glyphicon {
|
||||
padding-left:15px;
|
||||
}
|
||||
|
||||
#history .ope .infos2 {
|
||||
padding-left:15px;
|
||||
}
|
||||
|
|
|
@ -15,10 +15,18 @@ function KHistory(options={}) {
|
|||
|
||||
var trigramme = opegroup['on_acc_trigramme'];
|
||||
var is_cof = opegroup['is_cof'];
|
||||
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);
|
||||
if (opegroup['type'] == 'opegroup') {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
for (var i=0; i<opegroup['opes'].length; i++) {
|
||||
var $transfer = this._transferHtml(opegroup['opes'][i]);
|
||||
$transfer.data('transfergroup', opegroup['id']);
|
||||
$opegroup.after($transfer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +49,8 @@ function KHistory(options={}) {
|
|||
}
|
||||
|
||||
$ope_html
|
||||
.data('ope', ope['id'])
|
||||
.data('type', 'ope')
|
||||
.data('id', ope['id'])
|
||||
.find('.amount').text(amount).end()
|
||||
.find('.infos1').text(infos1).end()
|
||||
.find('.infos2').text(infos2).end();
|
||||
|
@ -58,6 +67,25 @@ function KHistory(options={}) {
|
|||
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) {
|
||||
if (!$ope)
|
||||
$ope = this.findOpe(ope['id']);
|
||||
|
@ -72,23 +100,36 @@ function KHistory(options={}) {
|
|||
}
|
||||
|
||||
this._opeGroupHtml = function(opegroup) {
|
||||
var $opegroup_html = $(this.template_opegroup);
|
||||
|
||||
var type = opegroup['type'];
|
||||
|
||||
|
||||
if (type == 'opegroup') {
|
||||
var $opegroup_html = $(this.template_opegroup);
|
||||
var trigramme = opegroup['on_acc__trigramme'];
|
||||
var amount = amountDisplay(
|
||||
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
|
||||
} else {
|
||||
var $opegroup_html = $(this.template_transfergroup);
|
||||
var trigramme = '';
|
||||
var amount = '' ;
|
||||
}
|
||||
|
||||
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'] || '';
|
||||
|
||||
$opegroup_html
|
||||
.data('opegroup', opegroup['id'])
|
||||
.data('type', type)
|
||||
.data('id', opegroup['id'])
|
||||
.find('.time').text(at).end()
|
||||
.find('.trigramme').text(trigramme).end()
|
||||
.find('.info').text("Transferts").end()
|
||||
.find('.amount').text(amount).end()
|
||||
.find('.comment').text(comment).end()
|
||||
.find('.trigramme').text(trigramme).end();
|
||||
.find('.comment').text(comment).end();
|
||||
|
||||
if (!this.display_trigramme)
|
||||
$opegroup_html.find('.trigramme').remove();
|
||||
$opegroup_html.find('.info').remove();
|
||||
|
||||
if (opegroup['valid_by__trigramme'])
|
||||
$opegroup_html.find('.valid_by').text('Par '+opegroup['valid_by__trigramme']);
|
||||
|
@ -114,9 +155,9 @@ function KHistory(options={}) {
|
|||
});
|
||||
}
|
||||
|
||||
this.findOpe = function(id) {
|
||||
this.findOpe = function(id, type) {
|
||||
return this.$container.find('.ope').filter(function() {
|
||||
return $(this).data('ope') == id
|
||||
return ($(this).data('id') == id && $(this).data('type') == type)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -134,6 +175,8 @@ KHistory.default_options = {
|
|||
container: '#history',
|
||||
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_transfergroup: '<div class="opegroup"><span class="time"></span><span class="info"></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_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,
|
||||
}
|
||||
|
|
|
@ -138,3 +138,8 @@ function requestAuth(data, callback, focus_next = null) {
|
|||
|
||||
});
|
||||
}
|
||||
|
||||
String.prototype.pluralize = function(count, irreg_plural = false) {
|
||||
plural = irreg_plural ? irreg_plural : this + 's' ;
|
||||
return (count==1 ? this : plural) ;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
$(document).ready(function() {
|
||||
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})}
|
||||
|
||||
lock = 0 ;
|
||||
|
||||
khistory = new KHistory();
|
||||
|
||||
var $from_date = $('#from_date');
|
||||
|
@ -142,9 +144,10 @@ $(document).ready(function() {
|
|||
selected: function(e, ui) {
|
||||
$(ui.selected).each(function() {
|
||||
if ($(this).hasClass('opegroup')) {
|
||||
var opegroup = $(this).data('opegroup');
|
||||
var type = $(this).data('type');
|
||||
var id = $(this).data('id');
|
||||
$(this).siblings('.ope').filter(function() {
|
||||
return $(this).data('opegroup') == opegroup
|
||||
return $(this).data(type) == id
|
||||
}).addClass('ui-selected');
|
||||
}
|
||||
});
|
||||
|
@ -156,7 +159,7 @@ $(document).ready(function() {
|
|||
// DEL (Suppr)
|
||||
var opes_to_cancel = [];
|
||||
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)
|
||||
confirmCancel(opes_to_cancel);
|
||||
|
@ -165,7 +168,10 @@ $(document).ready(function() {
|
|||
|
||||
function confirmCancel(opes_to_cancel) {
|
||||
var nb = opes_to_cancel.length;
|
||||
var content = nb+" opérations vont être annulées";
|
||||
var content = nb+' opération'.pluralize(nb)
|
||||
+' va'.pluralize(nb, ' vont')
|
||||
+ ' être'
|
||||
+ ' annulée'.pluralize(nb);
|
||||
$.confirm({
|
||||
title: 'Confirmation',
|
||||
content: content,
|
||||
|
@ -179,50 +185,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 = '') {
|
||||
if (lock == 1)
|
||||
return false
|
||||
lock = 1 ;
|
||||
var data = { 'operations' : opes_array }
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
|
@ -238,6 +204,7 @@ $(document).ready(function() {
|
|||
})
|
||||
.done(function(data) {
|
||||
khistory.$container.find('.ui-selected').removeClass('ui-selected');
|
||||
lock = 0 ;
|
||||
})
|
||||
.fail(function($xhr) {
|
||||
var data = $xhr.responseJSON;
|
||||
|
@ -251,6 +218,7 @@ $(document).ready(function() {
|
|||
displayErrors(getErrorsHtml(data));
|
||||
break;
|
||||
}
|
||||
lock = 0 ;
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
<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/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/history.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}Transferts{% endblock %}
|
||||
|
@ -31,22 +35,7 @@
|
|||
<div class="content-right">
|
||||
<div class="content-right-block">
|
||||
<h2>Liste des transferts</h2>
|
||||
<div id="history">
|
||||
{% 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 id="history" class="table">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -70,15 +59,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({
|
||||
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",
|
||||
data : data,
|
||||
beforeSend: function ($xhr) {
|
||||
|
@ -89,11 +143,7 @@ $(document).ready(function() {
|
|||
|
||||
})
|
||||
.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');
|
||||
khistory.$container.find('.ui-selected').removeClass('ui-selected');
|
||||
lock = 0;
|
||||
})
|
||||
.fail(function($xhr) {
|
||||
|
@ -101,7 +151,7 @@ $(document).ready(function() {
|
|||
switch ($xhr.status) {
|
||||
case 403:
|
||||
requestAuth(data, function(password) {
|
||||
cancelTransfers(transfers_array, password);
|
||||
cancelOperations(opes_array, password);
|
||||
});
|
||||
break;
|
||||
case 400:
|
||||
|
@ -112,31 +162,7 @@ $(document).ready(function() {
|
|||
});
|
||||
}
|
||||
|
||||
$('#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);
|
||||
}
|
||||
});
|
||||
getHistory();
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -178,8 +178,6 @@ 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
|
||||
|
|
283
kfet/views.py
283
kfet/views.py
|
@ -18,7 +18,7 @@ from django.contrib.auth.models import User, Permission, Group
|
|||
from django.http import HttpResponse, JsonResponse, Http404
|
||||
from django.forms import modelformset_factory, formset_factory
|
||||
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.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
|
@ -26,7 +26,7 @@ from gestioncof.models import CofProfile, Clipper
|
|||
from kfet.decorators import teamkfet_required
|
||||
from kfet.models import (Account, Checkout, Article, Settings, AccountNegative,
|
||||
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
|
||||
InventoryArticle, Order, OrderArticle)
|
||||
InventoryArticle, Order, OrderArticle, TransferGroup, Transfer)
|
||||
from kfet.forms import *
|
||||
from collections import defaultdict
|
||||
from kfet import consumers
|
||||
|
@ -1097,26 +1097,47 @@ def kpsul_perform_operations(request):
|
|||
@teamkfet_required
|
||||
def kpsul_cancel_operations(request):
|
||||
# Pour la réponse
|
||||
data = { 'canceled': [], 'warnings': {}, 'errors': {}}
|
||||
data = {'canceled': {}, 'warnings': {}, 'errors': {}}
|
||||
|
||||
# Checking if BAD REQUEST (opes_pk not int or not existing)
|
||||
try:
|
||||
# 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:
|
||||
return JsonResponse(data, status=400)
|
||||
|
||||
opes_all = (
|
||||
Operation.objects
|
||||
.select_related('group', 'group__on_acc', 'group__on_acc__negative')
|
||||
.filter(pk__in=opes_post))
|
||||
opes_pk = [ ope.pk for ope in opes_all ]
|
||||
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)
|
||||
|
||||
opes_already_canceled = [] # Déjà annulée
|
||||
opes = [] # Pas déjà annulée
|
||||
already_canceled = {} # Opération/Transfert déjà annulé
|
||||
opes = [] # Pas déjà annulée
|
||||
transfers = []
|
||||
required_perms = set()
|
||||
stop_all = False
|
||||
cancel_duration = Settings.CANCEL_DURATION()
|
||||
|
@ -1127,7 +1148,7 @@ def kpsul_cancel_operations(request):
|
|||
for ope in opes_all:
|
||||
if ope.canceled_at:
|
||||
# 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:
|
||||
opes.append(ope.pk)
|
||||
# Si opé il y a plus de CANCEL_DURATION, permission requise
|
||||
|
@ -1161,7 +1182,7 @@ def kpsul_cancel_operations(request):
|
|||
if not last_statement or last_statement.at < ope.group.at:
|
||||
if ope.type == Operation.PURCHASE:
|
||||
if ope.group.on_acc.is_cash:
|
||||
to_checkouts_balances[ope.group.checkout] -= - ope.amount
|
||||
to_checkouts_balances[ope.group.checkout] -= -ope.amount
|
||||
else:
|
||||
to_checkouts_balances[ope.group.checkout] -= ope.amount
|
||||
|
||||
|
@ -1174,22 +1195,38 @@ def kpsul_cancel_operations(request):
|
|||
# est recalculé automatiquement
|
||||
if ope.article and ope.article_nb:
|
||||
last_stock = (InventoryArticle.objects
|
||||
.select_related('inventory')
|
||||
.filter(article=ope.article)
|
||||
.order_by('inventory__at')
|
||||
.last())
|
||||
.select_related('inventory')
|
||||
.filter(article=ope.article)
|
||||
.order_by('inventory__at')
|
||||
.last())
|
||||
if not last_stock or last_stock.inventory.at < ope.group.at:
|
||||
to_articles_stocks[ope.article] += ope.article_nb
|
||||
|
||||
if not opes:
|
||||
data['warnings']['already_canceled'] = opes_already_canceled
|
||||
for transfer in transfers_all:
|
||||
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)
|
||||
|
||||
negative_accounts = []
|
||||
# Checking permissions or stop
|
||||
for account in to_accounts_balances:
|
||||
(perms, stop) = account.perms_to_perform_operation(
|
||||
amount = to_accounts_balances[account])
|
||||
amount=to_accounts_balances[account])
|
||||
required_perms |= perms
|
||||
stop_all = stop_all or stop
|
||||
if stop:
|
||||
|
@ -1209,25 +1246,31 @@ def kpsul_cancel_operations(request):
|
|||
with transaction.atomic():
|
||||
(Operation.objects.filter(pk__in=opes)
|
||||
.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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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 = { 'opegroups': [], 'opes': [], 'checkouts': [], 'articles': [] }
|
||||
websocket_data = {'opegroups': [], 'opes': [],
|
||||
'checkouts': [], 'articles': []}
|
||||
# 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
|
||||
.values('id','amount','is_cof').filter(pk__in=opegroups_pk))
|
||||
.values('id', 'amount', 'is_cof')
|
||||
.filter(pk__in=opegroups_pk))
|
||||
for opegroup in opegroups:
|
||||
websocket_data['opegroups'].append({
|
||||
'cancellation': True,
|
||||
|
@ -1244,15 +1287,16 @@ def kpsul_cancel_operations(request):
|
|||
'canceled_at': canceled_at,
|
||||
})
|
||||
# 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
|
||||
.values('id', 'balance').filter(pk__in=checkouts_pk))
|
||||
.values('id', 'balance')
|
||||
.filter(pk__in=checkouts_pk))
|
||||
for checkout in checkouts:
|
||||
websocket_data['checkouts'].append({
|
||||
'id': checkout['id'],
|
||||
'balance': checkout['balance']})
|
||||
# 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)
|
||||
for article in articles:
|
||||
websocket_data['articles'].append({
|
||||
|
@ -1260,9 +1304,10 @@ def kpsul_cancel_operations(request):
|
|||
'stock': article['stock']})
|
||||
consumers.KPsul.group_send('kfet.kpsul', websocket_data)
|
||||
|
||||
data['canceled'] = opes
|
||||
if opes_already_canceled:
|
||||
data['warnings']['already_canceled'] = opes_already_canceled
|
||||
data['canceled']['opes'] = opes
|
||||
data['canceled']['transfers'] = transfers
|
||||
if already_canceled:
|
||||
data['warnings']['already_canceled'] = already_canceled
|
||||
return JsonResponse(data)
|
||||
|
||||
@login_required
|
||||
|
@ -1270,45 +1315,77 @@ def history_json(request):
|
|||
# Récupération des paramètres
|
||||
from_date = request.POST.get('from', None)
|
||||
to_date = request.POST.get('to', None)
|
||||
limit = request.POST.get('limit', None);
|
||||
checkouts = request.POST.getlist('checkouts[]', 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
|
||||
queryset_prefetch = Operation.objects.select_related(
|
||||
ope_queryset_prefetch = Operation.objects.select_related(
|
||||
'canceled_by__trigramme', 'addcost_for__trigramme',
|
||||
'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
|
||||
opegroups = (OperationGroup.objects
|
||||
.prefetch_related(Prefetch('opes', queryset = queryset_prefetch))
|
||||
.prefetch_related(ope_prefetch)
|
||||
.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
|
||||
if from_date:
|
||||
opegroups = opegroups.filter(at__gte=from_date)
|
||||
transfergroups = transfergroups.filter(at__gte=from_date)
|
||||
if to_date:
|
||||
opegroups = opegroups.filter(at__lt=to_date)
|
||||
transfergroups = transfergroups.filter(at__lt=to_date)
|
||||
if checkouts:
|
||||
opegroups = opegroups.filter(checkout_id__in=checkouts)
|
||||
transfergroups = TransferGroup.objects.none()
|
||||
if transfers_only:
|
||||
opegroups = OperationGroup.objects.none()
|
||||
if accounts:
|
||||
opegroups = opegroups.filter(on_acc_id__in=accounts)
|
||||
# Un non-membre de l'équipe n'a que accès à son historique
|
||||
if not request.user.has_perm('kfet.is_team'):
|
||||
opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet)
|
||||
if limit:
|
||||
opegroups = opegroups[:limit]
|
||||
|
||||
|
||||
# Construction de la réponse
|
||||
opegroups_list = []
|
||||
for opegroup in opegroups:
|
||||
opegroup_dict = {
|
||||
'type' : 'opegroup',
|
||||
'id' : opegroup.id,
|
||||
'amount' : opegroup.amount,
|
||||
'at' : opegroup.at,
|
||||
'checkout_id': opegroup.checkout_id,
|
||||
'is_cof' : opegroup.is_cof,
|
||||
'comment' : opegroup.comment,
|
||||
'opes' : [],
|
||||
|
@ -1337,7 +1414,41 @@ def history_json(request):
|
|||
ope.canceled_by and ope.canceled_by.trigramme or None)
|
||||
opegroup_dict['opes'].append(ope_dict)
|
||||
opegroups_list.append(opegroup_dict)
|
||||
return JsonResponse({ 'opegroups': opegroups_list })
|
||||
|
||||
for transfergroup in transfergroups:
|
||||
if transfergroup.filtered_transfers:
|
||||
transfergroup_dict = {
|
||||
'type': 'transfergroup',
|
||||
'id': transfergroup.id,
|
||||
'at': transfergroup.at,
|
||||
'comment': transfergroup.comment,
|
||||
'opes': [],
|
||||
}
|
||||
if request.user.has_perm('kfet.is_team'):
|
||||
transfergroup_dict['valid_by__trigramme'] = (
|
||||
transfergroup.valid_by
|
||||
and transfergroup.valid_by.trigramme
|
||||
or None)
|
||||
|
||||
for transfer in transfergroup.filtered_transfers:
|
||||
transfer_dict = {
|
||||
'id': transfer.id,
|
||||
'amount': transfer.amount,
|
||||
'canceled_at': transfer.canceled_at,
|
||||
'from_acc': transfer.from_acc.trigramme,
|
||||
'to_acc': transfer.to_acc.trigramme,
|
||||
}
|
||||
if request.user.has_perm('kfet.is_team'):
|
||||
transfer_dict['canceled_by__trigramme'] = (
|
||||
transfer.canceled_by
|
||||
and transfer.canceled_by.trigramme
|
||||
or None)
|
||||
transfergroup_dict['opes'].append(transfer_dict)
|
||||
opegroups_list.append(transfergroup_dict)
|
||||
|
||||
opegroups_list.sort(key=lambda group: group['at'])
|
||||
|
||||
return JsonResponse({'opegroups': opegroups_list})
|
||||
|
||||
@teamkfet_required
|
||||
def kpsul_articles_data(request):
|
||||
|
@ -1483,104 +1594,6 @@ 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
|
||||
|
|
Loading…
Reference in a new issue