WIP: Aureplop/kpsul js refactor #501
11 changed files with 1542 additions and 841 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -84,11 +93,11 @@
|
||||||
color:#FFF;
|
color:#FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
#history .ope.canceled, #history .transfer.canceled {
|
#history [canceled="true"] {
|
||||||
color:#444;
|
color:#444;
|
||||||
}
|
}
|
||||||
|
|
||||||
#history .ope.canceled::before, #history.transfer.canceled::before {
|
#history [canceled="true"]::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
width:100%;
|
width:100%;
|
||||||
|
|
|
@ -1,148 +1,215 @@
|
||||||
function KHistory(options={}) {
|
var cancelHistory = new Event("cancel_done");
|
||||||
$.extend(this, KHistory.default_options, options);
|
|
||||||
|
|
||||||
this.$container = $(this.container);
|
class KHistory {
|
||||||
|
|
||||||
|
static get default_options() {
|
||||||
|
return {
|
||||||
|
'templates': {
|
||||||
|
'purchase': '<div class="ope"><span class="amount"></span><span class="infos1"></span><span class="infos2"></span><span class="addcost"></span><span class="canceled"></span></div>',
|
||||||
|
'specialope': '<div class="ope"><span class="amount"></span><span class="infos1"></span><span class="infos2"></span><span class="addcost"></span><span class="canceled"></span></div>',
|
||||||
|
'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>',
|
||||||
|
'transfergroup': '<div class="opegroup"><span class="time"></span><span class="infos"></span><span class="valid_by"></span><span class="comment"></span></div>',
|
||||||
|
'day': '<div class="day"><span class="date"></span></div>',
|
||||||
|
'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>',
|
||||||
|
},
|
||||||
|
|
||||||
|
'api_options': {
|
||||||
|
from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
},
|
||||||
|
|
||||||
this.reset = function() {
|
|
||||||
this.$container.html('');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addOpeGroup = function(opegroup) {
|
|
||||||
var $day = this._getOrCreateDay(opegroup['at']);
|
|
||||||
var $opegroup = this._opeGroupHtml(opegroup);
|
|
||||||
|
|
||||||
$day.after($opegroup);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._opeHtml = function(ope, is_cof, trigramme) {
|
constructor(options) {
|
||||||
var $ope_html = $(this.template_ope);
|
var all_options = $.extend({}, this.constructor.default_options, options);
|
||||||
var parsed_amount = parseFloat(ope['amount']);
|
this.api_options = all_options.api_options;
|
||||||
var amount = amountDisplay(parsed_amount, is_cof, trigramme);
|
|
||||||
var infos1 = '', infos2 = '';
|
|
||||||
|
|
||||||
if (ope['type'] == 'purchase') {
|
this._$container = $('#history');
|
||||||
infos1 = ope['article_nb'];
|
this._$nb_opes = $('#nb_opes');
|
||||||
infos2 = ope['article__name'];
|
|
||||||
} else {
|
this.data = new OperationList();
|
||||||
infos1 = parsed_amount.toFixed(2)+'€';
|
|
||||||
switch (ope['type']) {
|
if (!all_options.no_select)
|
||||||
case 'initial':
|
this.selection = new KHistorySelection(this);
|
||||||
infos2 = 'Initial';
|
|
||||||
break;
|
if (!all_options.static)
|
||||||
case 'withdraw':
|
OperationWebSocket.add_handler(data => this.update_data(data));
|
||||||
infos2 = 'Retrait';
|
|
||||||
break;
|
var templates = all_options.templates
|
||||||
case 'deposit':
|
if (all_options.no_trigramme)
|
||||||
infos2 = 'Charge';
|
templates['opegroup'] =
|
||||||
break;
|
'<div class="opegroup"><span class="time"></span><span class="amount"></span><span class="valid_by"></span><span class="comment"></span></div>';
|
||||||
case 'edit':
|
|
||||||
infos2 = 'Édition';
|
this.display = new ForestDisplay(this._$container, templates, this.data);
|
||||||
break;
|
|
||||||
}
|
this._init_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
$ope_html
|
fetch(api_options) {
|
||||||
.data('ope', ope['id'])
|
this.data.clear();
|
||||||
.find('.amount').text(amount).end()
|
|
||||||
.find('.infos1').text(infos1).end()
|
$.extend(this.api_options, api_options);
|
||||||
.find('.infos2').text(infos2).end();
|
|
||||||
|
this.data.fromAPI(this.api_options)
|
||||||
|
.done( () => this.display_data() );
|
||||||
|
|
||||||
var addcost_for = ope['addcost_for__trigramme'];
|
|
||||||
if (addcost_for) {
|
|
||||||
var addcost_amount = parseFloat(ope['addcost_amount']);
|
|
||||||
$ope_html.find('.addcost').text('('+amountDisplay(addcost_amount, is_cof)+'UKF pour '+addcost_for+')');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ope['canceled_at'])
|
display_data() {
|
||||||
this.cancelOpe(ope, $ope_html);
|
this.display.clear();
|
||||||
|
this.display.render(this.data);
|
||||||
return $ope_html;
|
var nb_opes = this._$container.find('.ope[canceled="false"]').length;
|
||||||
|
this._$nb_opes.text(nb_opes);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cancelOpe = function(ope, $ope = null) {
|
_init_events() {
|
||||||
if (!$ope)
|
var that = this;
|
||||||
$ope = this.findOpe(ope['id']);
|
$(document).on('keydown', function(e) {
|
||||||
|
if (e.keyCode == 46 && that.selection) {
|
||||||
|
//DEL key ; we delete the selected operations (if any)
|
||||||
|
var to_cancel = that.selection.get_selected();
|
||||||
|
|
||||||
var cancel = 'Annulé';
|
if (to_cancel['opes'].length > 0 || to_cancel['transfers'].length > 0)
|
||||||
var canceled_at = dateUTCToParis(ope['canceled_at']);
|
that.cancel_operations(to_cancel);
|
||||||
if (ope['canceled_by__trigramme'])
|
|
||||||
cancel += ' par '+ope['canceled_by__trigramme'];
|
|
||||||
cancel += ' le '+canceled_at.format('DD/MM/YY à HH:mm:ss');
|
|
||||||
|
|
||||||
$ope.addClass('canceled').find('.canceled').text(cancel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._opeGroupHtml = function(opegroup) {
|
|
||||||
var $opegroup_html = $(this.template_opegroup);
|
|
||||||
|
|
||||||
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'])
|
|
||||||
.find('.time').text(at).end()
|
|
||||||
.find('.amount').text(amount).end()
|
|
||||||
.find('.comment').text(comment).end()
|
|
||||||
.find('.trigramme').text(trigramme).end();
|
|
||||||
|
|
||||||
if (!this.display_trigramme)
|
|
||||||
$opegroup_html.find('.trigramme').remove();
|
|
||||||
|
|
||||||
if (opegroup['valid_by__trigramme'])
|
|
||||||
$opegroup_html.find('.valid_by').text('Par '+opegroup['valid_by__trigramme']);
|
|
||||||
|
|
||||||
return $opegroup_html;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._getOrCreateDay = function(date) {
|
|
||||||
var at = dateUTCToParis(date);
|
|
||||||
var at_ser = at.format('YYYY-MM-DD');
|
|
||||||
var $day = this.$container.find('.day').filter(function() {
|
|
||||||
return $(this).data('date') == at_ser
|
|
||||||
});
|
|
||||||
if ($day.length == 1)
|
|
||||||
return $day;
|
|
||||||
var $day = $(this.template_day).prependTo(this.$container);
|
|
||||||
return $day.data('date', at_ser).text(at.format('D MMMM'));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.findOpeGroup = function(id) {
|
|
||||||
return this.$container.find('.opegroup').filter(function() {
|
|
||||||
return $(this).data('opegroup') == id
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.findOpe = function(id) {
|
cancel_operations(to_cancel) {
|
||||||
return this.$container.find('.ope').filter(function() {
|
var that = this ;
|
||||||
return $(this).data('ope') == id
|
var on_success = function() {
|
||||||
});
|
if (that.selection)
|
||||||
|
that.selection.reset() ;
|
||||||
|
$(that).trigger("cancel_done");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cancelOpeGroup = function(opegroup) {
|
api_with_auth({
|
||||||
var $opegroup = this.findOpeGroup(opegroup['id']);
|
url: Urls['kfet.kpsul.cancel_operations'](),
|
||||||
var trigramme = $opegroup.find('.trigramme').text();
|
data: to_cancel,
|
||||||
var amount = amountDisplay(
|
on_success: on_success,
|
||||||
parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme));
|
})
|
||||||
$opegroup.find('.amount').text(amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_node(data) {
|
||||||
|
var node = this.data.get_or_create(data.modelname, data.content, 0);
|
||||||
|
this.display.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_node(modelname, id, update_data) {
|
||||||
|
var updated = this.data.update(modelname, id, update_data)
|
||||||
|
if (!updated)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.display.update(updated);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid(opegroup) {
|
||||||
|
var options = this.api_options;
|
||||||
|
|
||||||
|
if (options.from && dateUTCToParis(opegroup.at).isBefore(moment(options.from)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (options.to && dateUTCToParis(opegroup.at).isAfter(moment(options.to)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (options.transfersonly && opegroup.constructor.verbose_name == 'opegroup')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (options.opesonly && opegroup.constructor.verbose_name == 'transfergroup')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (options.accounts && options.accounts.length &&
|
||||||
|
options.accounts.indexOf(opegroup.account_id) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (options.checkouts && options.checkouts.length &&
|
||||||
|
(opegroup.modelname == 'transfergroup' ||
|
||||||
|
options.checkouts.indexOf(opegroup.checkout_id) < 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_data(data) {
|
||||||
|
var opegroups = data['opegroups'];
|
||||||
|
var opes = data['opes'];
|
||||||
|
|
||||||
|
for (let ope of opes) {
|
||||||
|
if (ope['cancellation']) {
|
||||||
|
var update_data = {
|
||||||
|
'canceled_at': ope.canceled_at,
|
||||||
|
'canceled_by': ope.canceled_by,
|
||||||
|
};
|
||||||
|
if (ope.modelname === 'ope') {
|
||||||
|
this.update_node('purchase', ope.id, update_data)
|
||||||
|
|| this.update_node('specialope', ope.id, update_data);
|
||||||
|
} else if (ope.modelname === 'transfer') {
|
||||||
|
this.update_node('transfer', ope.id, update_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let opegroup of opegroups) {
|
||||||
|
if (opegroup['cancellation']) {
|
||||||
|
var update_data = { 'amount': opegroup.amount };
|
||||||
|
this.update_node('opegroup', opegroup.id, update_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opegroup['add'] && this.is_valid(opegroup)) {
|
||||||
|
this.add_node(opegroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nb_opes = this._$container.find('.ope[canceled="false"]').length;
|
||||||
|
$('#nb_opes').text(nb_opes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KHistory.default_options = {
|
class KHistorySelection {
|
||||||
container: '#history',
|
|
||||||
template_day: '<div class="day"></div>',
|
constructor(history) {
|
||||||
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>',
|
this._$container = history._$container;
|
||||||
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>',
|
this._init();
|
||||||
display_trigramme: true,
|
}
|
||||||
|
|
||||||
|
_init() {
|
||||||
|
this._$container.selectable({
|
||||||
|
filter: 'div.opegroup, div.ope',
|
||||||
|
selected: function(e, ui) {
|
||||||
|
$(ui.selected).each(function() {
|
||||||
|
if ($(this).hasClass('opegroup')) {
|
||||||
|
$(this).parent().find('.ope').addClass('ui-selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unselected: function(e, ui) {
|
||||||
|
$(ui.unselected).each(function() {
|
||||||
|
if ($(this).hasClass('opegroup')) {
|
||||||
|
$(this).parent().find('.ope').removeClass('ui-selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get_selected() {
|
||||||
|
var selected = {'transfers': [], 'opes': [],};
|
||||||
|
this._$container.find('.ope.ui-selected').each(function() {
|
||||||
|
var [type, id] = $(this).parent().attr('id').split('-');
|
||||||
|
|
||||||
|
if (type === 'transfer')
|
||||||
|
selected['transfers'].push(id);
|
||||||
|
else
|
||||||
|
selected['opes'].push(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this._$container.find('.ui-selected')
|
||||||
|
.removeClass('.ui-selected');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,6 +25,20 @@ String.prototype.isValidTri = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if given argument is float ;
|
||||||
|
* if not, parses given argument to float value.
|
||||||
|
* @global
|
||||||
|
* @return {float}
|
||||||
|
*/
|
||||||
|
function floatCheck(v) {
|
||||||
|
if (typeof v === 'number')
|
||||||
|
return v;
|
||||||
|
return Number.parseFloat(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function intCheck(v) {
|
function intCheck(v) {
|
||||||
return Number.parseInt(v);
|
return Number.parseInt(v);
|
||||||
}
|
}
|
||||||
|
@ -340,11 +354,24 @@ function getErrorsHtml(data) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function displayErrors(html) {
|
||||||
|
$.alert({
|
||||||
|
title: 'Erreurs',
|
||||||
|
content: html,
|
||||||
|
backgroundDismiss: true,
|
||||||
|
animation: 'top',
|
||||||
|
closeAnimation: 'bottom',
|
||||||
|
keyboardEnabled: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var authDialog = new UserDialog({
|
var authDialog = new UserDialog({
|
||||||
'title': 'Authentification requise',
|
'title': 'Authentification requise',
|
||||||
'content': '<div class="capslock"><span class="glyphicon glyphicon-lock"></span><input type="password" name="password" autofocus><div>',
|
'content': '<div class="capslock"><span class="glyphicon glyphicon-lock"></span><input type="password" name="password" autofocus><div>',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//Note/TODO: the returned ajax object can be improved by allowing chaining on errors 403/400
|
||||||
function api_with_auth(settings, password) {
|
function api_with_auth(settings, password) {
|
||||||
if (window.api_lock == 1)
|
if (window.api_lock == 1)
|
||||||
return false;
|
return false;
|
||||||
|
@ -357,7 +384,7 @@ function api_with_auth(settings, password) {
|
||||||
var on_success = settings.on_success || $.noop ;
|
var on_success = settings.on_success || $.noop ;
|
||||||
var on_400 = settings.on_400 || $.noop ;
|
var on_400 = settings.on_400 || $.noop ;
|
||||||
|
|
||||||
$.ajax({
|
return $.ajax({
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
url: url,
|
url: url,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -392,3 +419,9 @@ function api_with_auth(settings, password) {
|
||||||
window.api_lock = 0;
|
window.api_lock = 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String.prototype.pluralize = function(count, irreg_plural) {
|
||||||
|
if (Math.abs(count) >= 2)
|
||||||
|
return irreg_plural ? irreg_plural : this+'s' ;
|
||||||
|
return this ;
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ class KPsulManager {
|
||||||
this.account_manager = new AccountManager(this);
|
this.account_manager = new AccountManager(this);
|
||||||
this.checkout_manager = new CheckoutManager(this);
|
this.checkout_manager = new CheckoutManager(this);
|
||||||
this.article_manager = new ArticleManager(this);
|
this.article_manager = new ArticleManager(this);
|
||||||
|
this.history = new KHistory({
|
||||||
|
api_options: {'opesonly': true},
|
||||||
|
});
|
||||||
|
|
||||||
|
this._init_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(soft) {
|
reset(soft) {
|
||||||
|
@ -23,6 +28,7 @@ class KPsulManager {
|
||||||
if (!soft) {
|
if (!soft) {
|
||||||
this.checkout_manager.reset();
|
this.checkout_manager.reset();
|
||||||
this.article_manager.reset_data();
|
this.article_manager.reset_data();
|
||||||
|
this.history.fetch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +43,14 @@ class KPsulManager {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_init_events() {
|
||||||
|
var that = this ;
|
||||||
|
$(this.history).on("cancel_done", function(e) {
|
||||||
|
that.reset(true);
|
||||||
|
that.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -414,14 +428,19 @@ class ArticleManager {
|
||||||
this._$input = $('#article_autocomplete');
|
this._$input = $('#article_autocomplete');
|
||||||
this._$nb = $('#article_number');
|
this._$nb = $('#article_number');
|
||||||
this._$stock = $('#article_stock');
|
this._$stock = $('#article_stock');
|
||||||
this.templates = {'category': '<div class="category"><span class="name"></span></div>',
|
|
||||||
'article' : '<div class="article"><span class="name"></span><span class="price"></span><span class="stock"></span></div>'}
|
|
||||||
|
|
||||||
this.selected = new Article() ;
|
this.selected = new Article() ;
|
||||||
this.list = new ArticleList() ;
|
this.data = new ArticleList() ;
|
||||||
this.autocomplete = new ArticleAutocomplete(this);
|
var $container = $('#articles_data');
|
||||||
|
var templates = {
|
||||||
|
'category': '<div class="category"><span class="name"></span></div>',
|
||||||
|
'article' : '<div class="article"><span class="name"></span><span class="price"></span><span class="stock"></span></div>'
|
||||||
|
} ;
|
||||||
|
this.display = new ForestDisplay($container, templates, this.data);
|
||||||
|
this.autocomplete = new ArticleAutocomplete(this, $container);
|
||||||
|
|
||||||
this._init_events();
|
this._init_events();
|
||||||
|
OperationWebSocket.add_handler(data => this.update_data(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
get nb() {
|
get nb() {
|
||||||
|
@ -429,7 +448,7 @@ class ArticleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
display_list() {
|
display_list() {
|
||||||
this.list.display(this._$container, this.templates) ;
|
this.display.render(this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(article) {
|
validate(article) {
|
||||||
|
@ -449,25 +468,18 @@ class ArticleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_data() {
|
reset_data() {
|
||||||
this._$container.html('');
|
this.display.clear();
|
||||||
this.list.clear();
|
this.data.clear();
|
||||||
this.list.fromAPI()
|
this.data.fromAPI()
|
||||||
.done( () => this.display_list() );
|
.done( () => this.display_list() );
|
||||||
}
|
}
|
||||||
|
|
||||||
get_article(id) {
|
|
||||||
return this.list.find('article', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_data(data) {
|
update_data(data) {
|
||||||
for (let article_dict of data.articles) {
|
for (let article_dict of data.articles) {
|
||||||
var article = this.get_article(article_dict.id);
|
|
||||||
|
|
||||||
// For now, article additions are disregarded
|
var updated = this.data.update('article', article_dict.id, article_dict);
|
||||||
if (article) {
|
if (updated) {
|
||||||
article.stock = article_dict.stock;
|
this.display.update(updated);
|
||||||
this._$container.find('#article-'+article.id+' .stock')
|
|
||||||
.text(article.stock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,7 +508,7 @@ class ArticleManager {
|
||||||
|
|
||||||
this._$container.on('click', '.article', function() {
|
this._$container.on('click', '.article', function() {
|
||||||
var id = $(this).parent().attr('id').split('-')[1];
|
var id = $(this).parent().attr('id').split('-')[1];
|
||||||
var article = that.list.find('article', id);
|
var article = that.data.find('article', id);
|
||||||
if (article)
|
if (article)
|
||||||
that.validate(article);
|
that.validate(article);
|
||||||
});
|
});
|
||||||
|
@ -507,18 +519,26 @@ class ArticleManager {
|
||||||
that.reset();
|
that.reset();
|
||||||
that.focus();
|
that.focus();
|
||||||
}
|
}
|
||||||
if (normalKeys.test(e.keyCode) || arrowKeys.test(e.KeyCode) || e.ctrlKey) {
|
|
||||||
|
if (normalKeys.test(e.keyCode) || arrowKeys.test(e.keyCode) || e.ctrlKey) {
|
||||||
if (e.ctrlKey && e.charCode == 65)
|
if (e.ctrlKey && e.charCode == 65)
|
||||||
that._$nb.val('');
|
that._$nb.val('');
|
||||||
return true ;
|
return true ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (that.constructor.check_nb(that.nb+e.key))
|
if (that.constructor.check_nb(that.nb+e.key))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Note : this function may not be needed after the whole rework
|
||||||
|
get_article(id) {
|
||||||
|
return this.data.find('article', id) ;
|
||||||
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
if (this.is_empty())
|
if (this.is_empty())
|
||||||
this._$input.focus();
|
this._$input.focus();
|
||||||
|
@ -535,9 +555,9 @@ class ArticleManager {
|
||||||
|
|
||||||
class ArticleAutocomplete {
|
class ArticleAutocomplete {
|
||||||
|
|
||||||
constructor(article_manager) {
|
constructor(article_manager, $container) {
|
||||||
this.manager = article_manager;
|
this.manager = article_manager;
|
||||||
this._$container = article_manager._$container ;
|
this._$container = $container ;
|
||||||
this._$input = $('#article_autocomplete');
|
this._$input = $('#article_autocomplete');
|
||||||
|
|
||||||
this.showAll() ;
|
this.showAll() ;
|
||||||
|
@ -549,11 +569,12 @@ class ArticleAutocomplete {
|
||||||
|
|
||||||
// 8:Backspace|9:Tab|13:Enter|46:DEL|112-117:F1-6|119-123:F8-F12
|
// 8:Backspace|9:Tab|13:Enter|46:DEL|112-117:F1-6|119-123:F8-F12
|
||||||
var normalKeys = /^(8|9|13|46|112|113|114|115|116|117|119|120|121|122|123)$/;
|
var normalKeys = /^(8|9|13|46|112|113|114|115|116|117|119|120|121|122|123)$/;
|
||||||
|
var arrowKeys = /^(37|38|39|40)$/;
|
||||||
|
|
||||||
this._$input
|
this._$input
|
||||||
.on('keydown', function(e) {
|
.on('keydown', function(e) {
|
||||||
var text = that._$input.val() ;
|
var text = that._$input.val() ;
|
||||||
if (normalKeys.test(e.keyCode) || e.ctrlKey) {
|
if (normalKeys.test(e.keyCode) || arrowKeys.test(e.keyCode) || e.ctrlKey) {
|
||||||
// For the backspace key, we suppose the cursor is at the very end
|
// For the backspace key, we suppose the cursor is at the very end
|
||||||
if(e.keyCode == 8) {
|
if(e.keyCode == 8) {
|
||||||
that.update(text.substring(0, text.length-1), true);
|
that.update(text.substring(0, text.length-1), true);
|
||||||
|
@ -571,7 +592,7 @@ class ArticleAutocomplete {
|
||||||
update(prefix, backspace) {
|
update(prefix, backspace) {
|
||||||
|
|
||||||
this.resetMatch();
|
this.resetMatch();
|
||||||
var article_list = this.manager.list ;
|
var article_list = this.manager.data ;
|
||||||
var lower = prefix.toLowerCase() ;
|
var lower = prefix.toLowerCase() ;
|
||||||
var that = this ;
|
var that = this ;
|
||||||
|
|
||||||
|
@ -599,7 +620,7 @@ class ArticleAutocomplete {
|
||||||
updateDisplay() {
|
updateDisplay() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
this.manager.list.traverse('category', function(category) {
|
this.manager.data.traverse('category', function(category) {
|
||||||
var is_active = false;
|
var is_active = false;
|
||||||
for (let article of category.articles) {
|
for (let article of category.articles) {
|
||||||
if (that.matching.indexOf(article) != -1) {
|
if (that.matching.indexOf(article) != -1) {
|
||||||
|
@ -634,7 +655,7 @@ class ArticleAutocomplete {
|
||||||
showAll() {
|
showAll() {
|
||||||
var that = this;
|
var that = this;
|
||||||
this.resetMatch();
|
this.resetMatch();
|
||||||
this.manager.list.traverse('article', function(article) {
|
this.manager.data.traverse('article', function(article) {
|
||||||
that.matching.push(article);
|
that.matching.push(article);
|
||||||
});
|
});
|
||||||
this.updateDisplay();
|
this.updateDisplay();
|
||||||
|
|
|
@ -4,10 +4,16 @@
|
||||||
{% load l10n %}
|
{% load l10n %}
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
|
<link rel="stylesheet" type="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-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>
|
||||||
<script type="text/javascript" src="{% static 'kfet/js/moment-fr.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/moment-timezone-with-data-2010-2020.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% url 'js_reverse' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'kfet/js/kfet.api.js' %}"></script>
|
||||||
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
|
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
|
||||||
{% if account.user == request.user %}
|
{% if account.user == request.user %}
|
||||||
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
|
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
|
||||||
|
@ -94,33 +100,11 @@ $(document).ready(function() {
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
|
'use strict';
|
||||||
|
|
||||||
khistory = new KHistory({
|
var khistory = new KHistory({'no_trigramme': true});
|
||||||
display_trigramme: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
function getHistory() {
|
Config.reset(() => khistory.fetch({'accounts': [{{account.pk}}]}));
|
||||||
var data = {
|
|
||||||
'accounts': [{{ account.pk }}],
|
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
dataType: "json",
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
var nb_opes = khistory.$container.find('.ope:not(.canceled)').length;
|
|
||||||
$('#nb_opes').text(nb_opes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getHistory();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -38,6 +39,9 @@
|
||||||
<div class="line"><b>Comptes</b> {{ filter_form.accounts }}</div>
|
<div class="line"><b>Comptes</b> {{ filter_form.accounts }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a id="update_history" class="btn btn-primary btn-lg">Mettre à jour</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 col-md-9 col-content-right">
|
<div class="col-sm-8 col-md-9 col-content-right">
|
||||||
|
@ -51,8 +55,8 @@
|
||||||
<div class="content-right-block">
|
<div class="content-right-block">
|
||||||
<h2>Opérations</h2>
|
<h2>Opérations</h2>
|
||||||
<div>
|
<div>
|
||||||
<table id="history" class="table">
|
<div id="history">
|
||||||
</table>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,9 +65,9 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
|
'use strict';
|
||||||
|
|
||||||
khistory = new KHistory();
|
var khistory = new KHistory();
|
||||||
|
|
||||||
var $from_date = $('#from_date');
|
var $from_date = $('#from_date');
|
||||||
var $to_date = $('#to_date');
|
var $to_date = $('#to_date');
|
||||||
|
@ -78,7 +82,9 @@ $(document).ready(function() {
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHistory() {
|
function updateHistory() {
|
||||||
|
|
||||||
|
// Get API options
|
||||||
var data = {};
|
var data = {};
|
||||||
if ($from_date.val())
|
if ($from_date.val())
|
||||||
data['from'] = moment($from_date.val()).format('YYYY-MM-DD HH:mm:ss');
|
data['from'] = moment($from_date.val()).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
@ -90,19 +96,8 @@ $(document).ready(function() {
|
||||||
var accounts = getSelectedMultiple($accounts);
|
var accounts = getSelectedMultiple($accounts);
|
||||||
data['accounts'] = accounts;
|
data['accounts'] = accounts;
|
||||||
|
|
||||||
$.ajax({
|
// Update history
|
||||||
dataType: "json",
|
khistory.fetch(data);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
var nb_opes = khistory.$container.find('.ope:not(.canceled)').length;
|
|
||||||
$('#nb_opes').text(nb_opes);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#from_date').datetimepicker({
|
$('#from_date').datetimepicker({
|
||||||
|
@ -117,7 +112,7 @@ $(document).ready(function() {
|
||||||
timeZone : 'Europe/Paris',
|
timeZone : 'Europe/Paris',
|
||||||
format : 'YYYY-MM-DD HH:mm',
|
format : 'YYYY-MM-DD HH:mm',
|
||||||
stepping : 5,
|
stepping : 5,
|
||||||
defaultDate: moment(),
|
defaultDate: moment().add(5, 'minutes'), // workaround for 'stepping' rounding
|
||||||
locale : 'fr',
|
locale : 'fr',
|
||||||
showTodayButton: true,
|
showTodayButton: true,
|
||||||
});
|
});
|
||||||
|
@ -133,69 +128,11 @@ $(document).ready(function() {
|
||||||
filter: true,
|
filter: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$("input").on('dp.change change', function() {
|
$("#update_history").on('click', function() {
|
||||||
khistory.reset();
|
updateHistory();
|
||||||
getHistory();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
khistory.$container.selectable({
|
Config.reset(updateHistory);
|
||||||
filter: 'div.opegroup, div.ope',
|
|
||||||
selected: function(e, ui) {
|
|
||||||
$(ui.selected).each(function() {
|
|
||||||
if ($(this).hasClass('opegroup')) {
|
|
||||||
var opegroup = $(this).data('opegroup');
|
|
||||||
$(this).siblings('.ope').filter(function() {
|
|
||||||
return $(this).data('opegroup') == opegroup
|
|
||||||
}).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('ope'));
|
|
||||||
});
|
|
||||||
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érations vont être annulées";
|
|
||||||
$.confirm({
|
|
||||||
title: 'Confirmation',
|
|
||||||
content: content,
|
|
||||||
backgroundDismiss: true,
|
|
||||||
animation: 'top',
|
|
||||||
closeAnimation: 'bottom',
|
|
||||||
keyboardEnabled: true,
|
|
||||||
confirm: function() {
|
|
||||||
cancelOperations(opes_to_cancel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelOperations(opes_array) {
|
|
||||||
var data = { 'operations' : opes_array }
|
|
||||||
api_with_auth({
|
|
||||||
url: Urls['kfet.kpsul.cancel_operations'](),
|
|
||||||
data: data,
|
|
||||||
on_success: function(data) {
|
|
||||||
khistory.$container.find('.ui-selected').removeClass('ui-selected');
|
|
||||||
},
|
|
||||||
on_400: function(data) {
|
|
||||||
displayErrors(getErrorsHtml(data));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getHistory();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -156,9 +156,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="history2">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form id="operationgroup_form" style="display:none;">{{ operationgroup_form }}</form>
|
<form id="operationgroup_form" style="display:none;">{{ operationgroup_form }}</form>
|
||||||
<form id="operation_formset" style="display:none;">{{ operation_formset }}</form>
|
<form id="operation_formset" style="display:none;">{{ operation_formset }}</form>
|
||||||
|
|
||||||
|
@ -199,21 +196,6 @@ $(document).ready(function() {
|
||||||
$('#id_comment').val('');
|
$('#id_comment').val('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----
|
|
||||||
// Errors ajax
|
|
||||||
// -----
|
|
||||||
|
|
||||||
function displayErrors(html) {
|
|
||||||
$.alert({
|
|
||||||
title: 'Erreurs',
|
|
||||||
content: html,
|
|
||||||
backgroundDismiss: true,
|
|
||||||
animation: 'top',
|
|
||||||
closeAnimation: 'bottom',
|
|
||||||
keyboardEnabled: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
// Perform operations
|
// Perform operations
|
||||||
// -----
|
// -----
|
||||||
|
@ -246,33 +228,6 @@ $(document).ready(function() {
|
||||||
performOperations();
|
performOperations();
|
||||||
});
|
});
|
||||||
|
|
||||||
// -----
|
|
||||||
// Cancel operations
|
|
||||||
// -----
|
|
||||||
|
|
||||||
var cancelButton = $('#cancel_operations');
|
|
||||||
var cancelForm = $('#cancel_form');
|
|
||||||
|
|
||||||
function cancelOperations(opes_array) {
|
|
||||||
var data = { 'operations' : opes_array }
|
|
||||||
api_with_auth({
|
|
||||||
url: Urls['kfet.kpsul.cancel_operations'](),
|
|
||||||
data: data,
|
|
||||||
on_success: function() {
|
|
||||||
coolReset();
|
|
||||||
},
|
|
||||||
on_400: function(response) {
|
|
||||||
displayErrors(getErrorsHtml(response));
|
|
||||||
},
|
|
||||||
next_focus: kpsul.account_manager,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event listeners
|
|
||||||
cancelButton.on('click', function() {
|
|
||||||
cancelOperations();
|
|
||||||
});
|
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
// Basket
|
// Basket
|
||||||
// -----
|
// -----
|
||||||
|
@ -311,7 +266,7 @@ $(document).ready(function() {
|
||||||
var article_basket_html = $(item_basket_default_html);
|
var article_basket_html = $(item_basket_default_html);
|
||||||
article_basket_html
|
article_basket_html
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(nb).end()
|
.find('.number').text('('+nb+'/'+article.stock+')').end()
|
||||||
.find('.name').text(article.name).end()
|
.find('.name').text(article.name).end()
|
||||||
.find('.amount').text(amountToUKF(amount_euro, kpsul.account_manager.account.is_cof));
|
.find('.amount').text(amountToUKF(amount_euro, kpsul.account_manager.account.is_cof));
|
||||||
basket_container.prepend(article_basket_html);
|
basket_container.prepend(article_basket_html);
|
||||||
|
@ -634,29 +589,6 @@ $(document).ready(function() {
|
||||||
.find('#id_form-'+opeindex+'-DELETE').prop('checked', !nb);
|
.find('#id_form-'+opeindex+'-DELETE').prop('checked', !nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----
|
|
||||||
// History
|
|
||||||
// -----
|
|
||||||
|
|
||||||
var khistory = new KHistory();
|
|
||||||
|
|
||||||
function getHistory() {
|
|
||||||
var data = {
|
|
||||||
from: moment().subtract(1, 'days').format('YYYY-MM-DD HH:mm:ss'),
|
|
||||||
};
|
|
||||||
$.ajax({
|
|
||||||
dataType: "json",
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousop_container = $('#previous_op');
|
var previousop_container = $('#previous_op');
|
||||||
|
|
||||||
function updatePreviousOp() {
|
function updatePreviousOp() {
|
||||||
|
@ -716,69 +648,6 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----
|
|
||||||
// Cancel from history
|
|
||||||
// -----
|
|
||||||
|
|
||||||
khistory.$container.selectable({
|
|
||||||
filter: 'div.opegroup, div.ope',
|
|
||||||
selected: function(e, ui) {
|
|
||||||
$(ui.selected).each(function() {
|
|
||||||
if ($(this).hasClass('opegroup')) {
|
|
||||||
var opegroup = $(this).data('opegroup');
|
|
||||||
$(this).siblings('.ope').filter(function() {
|
|
||||||
return $(this).data('opegroup') == opegroup
|
|
||||||
}).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('ope'));
|
|
||||||
});
|
|
||||||
if (opes_to_cancel.length > 0)
|
|
||||||
cancelOperations(opes_to_cancel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// -----
|
|
||||||
// Synchronization
|
|
||||||
// -----
|
|
||||||
|
|
||||||
OperationWebSocket.add_handler(function(data) {
|
|
||||||
for (var i=0; i<data['opegroups'].length; i++) {
|
|
||||||
if (data['opegroups'][i]['add']) {
|
|
||||||
khistory.addOpeGroup(data['opegroups'][i]);
|
|
||||||
} else 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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var i=0; i<data['checkouts'].length; i++) {
|
|
||||||
if (checkout_data['id'] == data['checkouts'][i]['id']) {
|
|
||||||
checkout_data['balance'] = data['checkouts'][i]['balance'];
|
|
||||||
displayCheckoutData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kpsul.article_manager.update_data(data);
|
|
||||||
|
|
||||||
if (data['addcost']) {
|
|
||||||
Config.set('addcost_for', data['addcost']['for']);
|
|
||||||
Config.set('addcost_amount', data['addcost']['amount']);
|
|
||||||
displayAddcost();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
// General
|
// General
|
||||||
// -----
|
// -----
|
||||||
|
@ -798,11 +667,10 @@ $(document).ready(function() {
|
||||||
coolReset(give_tri_focus);
|
coolReset(give_tri_focus);
|
||||||
kpsul.checkout_manager.reset();
|
kpsul.checkout_manager.reset();
|
||||||
resetPreviousOp();
|
resetPreviousOp();
|
||||||
khistory.reset();
|
|
||||||
Config.reset(function() {
|
Config.reset(function() {
|
||||||
kpsul.article_manager.reset_data();
|
kpsul.article_manager.reset_data();
|
||||||
displayAddcost();
|
displayAddcost();
|
||||||
getHistory();
|
kpsul.history.fetch();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,10 +750,23 @@ $(document).ready(function() {
|
||||||
updateBasketAmount: updateBasketAmount,
|
updateBasketAmount: updateBasketAmount,
|
||||||
updateBasketRel: updateBasketRel,
|
updateBasketRel: updateBasketRel,
|
||||||
performOperations: performOperations,
|
performOperations: performOperations,
|
||||||
|
coolReset: coolReset,
|
||||||
};
|
};
|
||||||
|
|
||||||
window.kpsul = new KPsulManager(env);
|
window.kpsul = new KPsulManager(env);
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// Synchronization
|
||||||
|
// -----
|
||||||
|
|
||||||
|
OperationWebSocket.add_handler(function(data) {
|
||||||
|
if (data['addcost']) {
|
||||||
|
Config.set('addcost_for', data['addcost']['for']);
|
||||||
|
Config.set('addcost_amount', data['addcost']['amount']);
|
||||||
|
displayAddcost();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
hardReset();
|
hardReset();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,9 +4,15 @@
|
||||||
{% 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/kfet.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="{% url 'js_reverse' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'kfet/js/kfet.api.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block title %}Transferts{% endblock %}
|
{% block title %}Transferts{% endblock %}
|
||||||
|
@ -18,6 +24,8 @@
|
||||||
<div class="col-sm-4 col-md-3 col-content-left">
|
<div class="col-sm-4 col-md-3 col-content-left">
|
||||||
<div class="content-left">
|
<div class="content-left">
|
||||||
<div class="content-left-top">
|
<div class="content-left-top">
|
||||||
|
<div class="line line-big" id="nb_opes"></div>
|
||||||
|
<div class="line line-bigsub">transferts</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<a class="btn btn-primary btn-lg" href="{% url 'kfet.transfers.create' %}">
|
<a class="btn btn-primary btn-lg" href="{% url 'kfet.transfers.create' %}">
|
||||||
|
@ -31,23 +39,8 @@
|
||||||
<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">
|
<div 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>
|
</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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,88 +49,11 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
lock = 0;
|
var khistory = new KHistory();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
Config.reset(() => khistory.fetch({'transfersonly': true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -203,8 +203,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
|
||||||
|
|
517
kfet/views.py
517
kfet/views.py
|
@ -18,7 +18,7 @@ from django.contrib.auth.models import User, Permission, Group
|
||||||
from django.http import JsonResponse, Http404
|
from django.http import JsonResponse, Http404
|
||||||
from django.forms import formset_factory
|
from django.forms import formset_factory
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import F, Sum, Prefetch, Count
|
from django.db.models import Q, F, Sum, Prefetch, Count
|
||||||
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
|
||||||
|
@ -1158,31 +1158,47 @@ def kpsul_perform_operations(request):
|
||||||
websocket_data = {}
|
websocket_data = {}
|
||||||
websocket_data['opegroups'] = [{
|
websocket_data['opegroups'] = [{
|
||||||
'add': True,
|
'add': True,
|
||||||
|
'modelname': 'opegroup',
|
||||||
|
'content': {
|
||||||
'id': operationgroup.pk,
|
'id': operationgroup.pk,
|
||||||
'amount': operationgroup.amount,
|
'amount': operationgroup.amount,
|
||||||
'checkout__name': operationgroup.checkout.name,
|
|
||||||
'at': operationgroup.at,
|
'at': operationgroup.at,
|
||||||
'is_cof': operationgroup.is_cof,
|
'is_cof': operationgroup.is_cof,
|
||||||
'comment': operationgroup.comment,
|
'comment': operationgroup.comment,
|
||||||
'valid_by__trigramme': (operationgroup.valid_by and
|
'valid_by': (operationgroup.valid_by and
|
||||||
operationgroup.valid_by.trigramme or None),
|
operationgroup.valid_by.trigramme or None),
|
||||||
'on_acc__trigramme': operationgroup.on_acc.trigramme,
|
'trigramme': operationgroup.on_acc.trigramme,
|
||||||
'opes': [],
|
# Used to filter websocket updates
|
||||||
|
'account_id': operationgroup.on_acc.pk,
|
||||||
|
'checkout_id': operationgroup.checkout.pk,
|
||||||
|
'children': [],
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
for operation in operations:
|
for ope in operations:
|
||||||
ope_data = {
|
ope_data = {
|
||||||
'id': operation.pk, 'type': operation.type,
|
'content': {
|
||||||
'amount': operation.amount,
|
'id': ope.id,
|
||||||
'addcost_amount': operation.addcost_amount,
|
'amount': ope.amount,
|
||||||
'addcost_for__trigramme': (
|
'canceled_at': None,
|
||||||
operation.addcost_for and addcost_for.trigramme or None),
|
'canceled_by': None,
|
||||||
'article__name': (
|
},
|
||||||
operation.article and operation.article.name or None),
|
|
||||||
'article_nb': operation.article_nb,
|
|
||||||
'group_id': operationgroup.pk,
|
|
||||||
'canceled_by__trigramme': None, 'canceled_at': None,
|
|
||||||
}
|
}
|
||||||
websocket_data['opegroups'][0]['opes'].append(ope_data)
|
|
||||||
|
if ope.type == Operation.PURCHASE:
|
||||||
|
ope_data['modelname'] = 'purchase'
|
||||||
|
ope_data['content'].update({
|
||||||
|
'article_name': ope.article.name,
|
||||||
|
'article_nb': ope.article_nb,
|
||||||
|
'addcost_amount': ope.addcost_amount,
|
||||||
|
'addcost_for':
|
||||||
|
ope.addcost_for and ope.addcost_for.trigramme or None,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
ope_data['modelname'] = 'specialope'
|
||||||
|
ope_data['content'].update({
|
||||||
|
'type': ope.type,
|
||||||
|
})
|
||||||
|
websocket_data['opegroups'][0]['content']['children'].append(ope_data)
|
||||||
# Need refresh from db cause we used update on queryset
|
# Need refresh from db cause we used update on queryset
|
||||||
operationgroup.checkout.refresh_from_db()
|
operationgroup.checkout.refresh_from_db()
|
||||||
websocket_data['checkouts'] = [{
|
websocket_data['checkouts'] = [{
|
||||||
|
@ -1205,37 +1221,63 @@ def kpsul_perform_operations(request):
|
||||||
@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(int, filter(None, request.POST.getlist('opes[]', []))))
|
||||||
|
)
|
||||||
|
transfers_post = (
|
||||||
|
set(map(int, filter(None, request.POST.getlist('transfers[]', []))))
|
||||||
|
)
|
||||||
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]
|
||||||
|
|
||||||
|
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:
|
if opes_notexisting:
|
||||||
data['errors']['opes_notexisting'] = 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 = kfet_config.cancel_duration
|
cancel_duration = kfet_config.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
|
||||||
|
@ -1262,7 +1304,8 @@ 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 = \
|
||||||
|
(CheckoutStatement.objects
|
||||||
.filter(checkout=ope.group.checkout)
|
.filter(checkout=ope.group.checkout)
|
||||||
.order_by('at')
|
.order_by('at')
|
||||||
.last())
|
.last())
|
||||||
|
@ -1281,23 +1324,41 @@ def kpsul_cancel_operations(request):
|
||||||
# Note : si InventoryArticle est maj par .save(), stock_error
|
# Note : si InventoryArticle est maj par .save(), stock_error
|
||||||
# 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:
|
||||||
|
@ -1317,6 +1378,10 @@ 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
|
Account.objects
|
||||||
|
@ -1329,20 +1394,22 @@ def kpsul_cancel_operations(request):
|
||||||
account.update_negative()
|
account.update_negative()
|
||||||
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,
|
||||||
|
@ -1350,24 +1417,35 @@ def kpsul_cancel_operations(request):
|
||||||
'amount': opegroup['amount'],
|
'amount': opegroup['amount'],
|
||||||
'is_cof': opegroup['is_cof'],
|
'is_cof': opegroup['is_cof'],
|
||||||
})
|
})
|
||||||
canceled_by__trigramme = canceled_by and canceled_by.trigramme or None
|
canceled_by = canceled_by and canceled_by.trigramme or None
|
||||||
for ope in opes:
|
for ope in opes:
|
||||||
websocket_data['opes'].append({
|
websocket_data['opes'].append({
|
||||||
'cancellation': True,
|
'cancellation': True,
|
||||||
|
'modelname': 'ope',
|
||||||
'id': ope,
|
'id': ope,
|
||||||
'canceled_by__trigramme': canceled_by__trigramme,
|
'canceled_by': canceled_by,
|
||||||
'canceled_at': canceled_at,
|
'canceled_at': canceled_at,
|
||||||
})
|
})
|
||||||
|
for ope in transfers:
|
||||||
|
websocket_data['opes'].append({
|
||||||
|
'cancellation': True,
|
||||||
|
'modelname': 'transfer',
|
||||||
|
'id': ope,
|
||||||
|
'canceled_by': canceled_by,
|
||||||
|
'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({
|
||||||
|
@ -1375,83 +1453,169 @@ 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.GET.get('from', None)
|
||||||
to_date = request.POST.get('to', None)
|
to_date = request.GET.get('to', None)
|
||||||
limit = request.POST.get('limit', None);
|
checkouts = request.GET.getlist('checkouts[]', None)
|
||||||
checkouts = request.POST.getlist('checkouts[]', None)
|
accounts = request.GET.getlist('accounts[]', None)
|
||||||
accounts = request.POST.getlist('accounts[]', None)
|
transfers_only = request.GET.get('transfersonly', None)
|
||||||
|
opes_only = request.GET.get('opesonly', None)
|
||||||
|
|
||||||
|
# Un non-membre de l'équipe n'a que accès à son historique
|
||||||
|
if not request.user.has_perm('kfet.is_team'):
|
||||||
|
accounts = [request.user.profile.account]
|
||||||
|
|
||||||
# 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', 'addcost_for',
|
||||||
'article__name')
|
'article')
|
||||||
|
ope_prefetch = Prefetch('opes',
|
||||||
|
queryset=ope_queryset_prefetch)
|
||||||
|
|
||||||
|
transfer_queryset_prefetch = Transfer.objects.select_related(
|
||||||
|
'from_acc', 'to_acc', 'canceled_by')
|
||||||
|
|
||||||
|
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)
|
||||||
|
.select_related('on_acc__trigramme',
|
||||||
|
'valid_by__trigramme')
|
||||||
.order_by('at')
|
.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 opes_only:
|
||||||
|
transfergroups = TransferGroup.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
|
|
||||||
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
|
# Construction de la réponse
|
||||||
opegroups_list = []
|
related_data = defaultdict(list)
|
||||||
|
objects_data = defaultdict(list)
|
||||||
for opegroup in opegroups:
|
for opegroup in opegroups:
|
||||||
opegroup_dict = {
|
opegroup_dict = {
|
||||||
'id' : opegroup.id,
|
'id': opegroup.id,
|
||||||
'amount' : opegroup.amount,
|
'amount': opegroup.amount,
|
||||||
'at' : opegroup.at,
|
'at': opegroup.at,
|
||||||
'checkout_id': opegroup.checkout_id,
|
'is_cof': opegroup.is_cof,
|
||||||
'is_cof' : opegroup.is_cof,
|
'comment': opegroup.comment,
|
||||||
'comment' : opegroup.comment,
|
'trigramme':
|
||||||
'opes' : [],
|
|
||||||
'on_acc__trigramme':
|
|
||||||
opegroup.on_acc and opegroup.on_acc.trigramme or None,
|
opegroup.on_acc and opegroup.on_acc.trigramme or None,
|
||||||
}
|
}
|
||||||
if request.user.has_perm('kfet.is_team'):
|
if request.user.has_perm('kfet.is_team'):
|
||||||
opegroup_dict['valid_by__trigramme'] = (
|
opegroup_dict['valid_by'] = (
|
||||||
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,
|
'amount': ope.amount,
|
||||||
'amount' : ope.amount,
|
'canceled_at': ope.canceled_at,
|
||||||
'article_nb' : ope.article_nb,
|
'is_cof': opegroup.is_cof,
|
||||||
'addcost_amount': ope.addcost_amount,
|
'trigramme':
|
||||||
'canceled_at' : ope.canceled_at,
|
opegroup.on_acc and opegroup.on_acc.trigramme or None,
|
||||||
'article__name':
|
'opegroup__id': opegroup.id,
|
||||||
ope.article and ope.article.name or None,
|
|
||||||
'addcost_for__trigramme':
|
|
||||||
ope.addcost_for and ope.addcost_for.trigramme or None,
|
|
||||||
}
|
}
|
||||||
if request.user.has_perm('kfet.is_team'):
|
if request.user.has_perm('kfet.is_team'):
|
||||||
ope_dict['canceled_by__trigramme'] = (
|
ope_dict['canceled_by'] = (
|
||||||
ope.canceled_by and ope.canceled_by.trigramme or None)
|
ope.canceled_by and ope.canceled_by.trigramme or None)
|
||||||
opegroup_dict['opes'].append(ope_dict)
|
|
||||||
opegroups_list.append(opegroup_dict)
|
if ope.type == Operation.PURCHASE:
|
||||||
return JsonResponse({ 'opegroups': opegroups_list })
|
ope_dict.update({
|
||||||
|
'article_name': ope.article.name,
|
||||||
|
'article_nb': ope.article_nb,
|
||||||
|
'addcost_amount': ope.addcost_amount,
|
||||||
|
'addcost_for':
|
||||||
|
ope.addcost_for and ope.addcost_for.trigramme or None,
|
||||||
|
})
|
||||||
|
objects_data['purchase'].append(ope_dict)
|
||||||
|
else:
|
||||||
|
ope_dict.update({
|
||||||
|
'type': ope.type,
|
||||||
|
})
|
||||||
|
objects_data['specialope'].append(ope_dict)
|
||||||
|
|
||||||
|
related_data['opegroup'].append(opegroup_dict)
|
||||||
|
|
||||||
|
for transfergroup in transfergroups:
|
||||||
|
if transfergroup.filtered_transfers:
|
||||||
|
transfergroup_dict = {
|
||||||
|
'id': transfergroup.id,
|
||||||
|
'at': transfergroup.at,
|
||||||
|
'comment': transfergroup.comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
'amount': transfer.amount,
|
||||||
|
'canceled_at': transfer.canceled_at,
|
||||||
|
'from_acc': transfer.from_acc.trigramme,
|
||||||
|
'to_acc': transfer.to_acc.trigramme,
|
||||||
|
'transfergroup__id': transfergroup.id,
|
||||||
|
}
|
||||||
|
if request.user.has_perm('kfet.is_team'):
|
||||||
|
transfer_dict['canceled_by'] = (
|
||||||
|
transfer.canceled_by and
|
||||||
|
transfer.canceled_by.trigramme or
|
||||||
|
None)
|
||||||
|
objects_data['transfer'].append(transfer_dict)
|
||||||
|
related_data['transfergroup'].append(transfergroup_dict)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'objects': objects_data,
|
||||||
|
'related': related_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
|
@ -1461,26 +1625,32 @@ def kpsul_articles_data(request):
|
||||||
.filter(is_sold=True)
|
.filter(is_sold=True)
|
||||||
.select_related('category'))
|
.select_related('category'))
|
||||||
articlelist = []
|
articlelist = []
|
||||||
|
categorylist = []
|
||||||
|
|
||||||
|
# TODO: nice queryset, no duplicate categories
|
||||||
for article in articles:
|
for article in articles:
|
||||||
articlelist.append({
|
articlelist.append({
|
||||||
'modelname': 'article',
|
|
||||||
'content': {
|
|
||||||
'id': article.id,
|
'id': article.id,
|
||||||
'name': article.name,
|
'name': article.name,
|
||||||
'price': article.price,
|
'price': article.price,
|
||||||
'stock': article.stock,
|
'stock': article.stock,
|
||||||
},
|
'category__id': article.category.id,
|
||||||
'parent': {
|
})
|
||||||
'modelname': 'category',
|
categorylist.append({
|
||||||
'content': {
|
|
||||||
'id': article.category.id,
|
'id': article.category.id,
|
||||||
'name': article.category.name,
|
'name': article.category.name,
|
||||||
'has_addcost': article.category.has_addcost,
|
'has_addcost': article.category.has_addcost,
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return JsonResponse(articlelist, safe=False)
|
|
||||||
|
data = {
|
||||||
|
'objects': {
|
||||||
|
'article': articlelist,
|
||||||
|
},
|
||||||
|
'related': {
|
||||||
|
'category': categorylist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1520,14 +1690,10 @@ class SettingsUpdate(SuccessMessageMixin, FormView):
|
||||||
# Transfer views
|
# Transfer views
|
||||||
# -----
|
# -----
|
||||||
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
def transfers(request):
|
def transfers(request):
|
||||||
transfergroups = (TransferGroup.objects
|
return render(request, 'kfet/transfers.html')
|
||||||
.prefetch_related('transfers')
|
|
||||||
.order_by('-at'))
|
|
||||||
return render(request, 'kfet/transfers.html', {
|
|
||||||
'transfergroups': transfergroups,
|
|
||||||
})
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
def transfers_create(request):
|
def transfers_create(request):
|
||||||
|
@ -1535,20 +1701,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
|
||||||
|
@ -1560,7 +1730,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:
|
||||||
|
@ -1586,7 +1756,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'):
|
||||||
|
@ -1595,10 +1765,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
|
||||||
|
@ -1611,106 +1781,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,
|
||||||
|
'modelname': '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 = kfet_config.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
|
||||||
|
|
Loading…
Reference in a new issue