WIP: Aureplop/kpsul js refactor #501
11 changed files with 1542 additions and 841 deletions
|
@ -40,6 +40,11 @@
|
|||
width:90px;
|
||||
}
|
||||
|
||||
#history .opegroup .infos {
|
||||
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;
|
||||
}
|
||||
|
@ -84,11 +93,11 @@
|
|||
color:#FFF;
|
||||
}
|
||||
|
||||
#history .ope.canceled, #history .transfer.canceled {
|
||||
#history [canceled="true"] {
|
||||
color:#444;
|
||||
}
|
||||
|
||||
#history .ope.canceled::before, #history.transfer.canceled::before {
|
||||
#history [canceled="true"]::before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width:100%;
|
||||
|
|
|
@ -1,148 +1,215 @@
|
|||
function KHistory(options={}) {
|
||||
$.extend(this, KHistory.default_options, options);
|
||||
var cancelHistory = new Event("cancel_done");
|
||||
|
||||
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) {
|
||||
var $ope_html = $(this.template_ope);
|
||||
var parsed_amount = parseFloat(ope['amount']);
|
||||
var amount = amountDisplay(parsed_amount, is_cof, trigramme);
|
||||
var infos1 = '', infos2 = '';
|
||||
constructor(options) {
|
||||
var all_options = $.extend({}, this.constructor.default_options, options);
|
||||
this.api_options = all_options.api_options;
|
||||
|
||||
if (ope['type'] == 'purchase') {
|
||||
infos1 = ope['article_nb'];
|
||||
infos2 = ope['article__name'];
|
||||
} else {
|
||||
infos1 = parsed_amount.toFixed(2)+'€';
|
||||
switch (ope['type']) {
|
||||
case 'initial':
|
||||
infos2 = 'Initial';
|
||||
break;
|
||||
case 'withdraw':
|
||||
infos2 = 'Retrait';
|
||||
break;
|
||||
case 'deposit':
|
||||
infos2 = 'Charge';
|
||||
break;
|
||||
case 'edit':
|
||||
infos2 = 'Édition';
|
||||
break;
|
||||
}
|
||||
this._$container = $('#history');
|
||||
this._$nb_opes = $('#nb_opes');
|
||||
|
||||
this.data = new OperationList();
|
||||
|
||||
if (!all_options.no_select)
|
||||
this.selection = new KHistorySelection(this);
|
||||
|
||||
if (!all_options.static)
|
||||
OperationWebSocket.add_handler(data => this.update_data(data));
|
||||
|
||||
var templates = all_options.templates
|
||||
if (all_options.no_trigramme)
|
||||
templates['opegroup'] =
|
||||
'<div class="opegroup"><span class="time"></span><span class="amount"></span><span class="valid_by"></span><span class="comment"></span></div>';
|
||||
|
||||
this.display = new ForestDisplay(this._$container, templates, this.data);
|
||||
|
||||
this._init_events();
|
||||
}
|
||||
|
||||
$ope_html
|
||||
.data('ope', ope['id'])
|
||||
.find('.amount').text(amount).end()
|
||||
.find('.infos1').text(infos1).end()
|
||||
.find('.infos2').text(infos2).end();
|
||||
fetch(api_options) {
|
||||
this.data.clear();
|
||||
|
||||
$.extend(this.api_options, api_options);
|
||||
|
||||
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'])
|
||||
this.cancelOpe(ope, $ope_html);
|
||||
|
||||
return $ope_html;
|
||||
display_data() {
|
||||
this.display.clear();
|
||||
this.display.render(this.data);
|
||||
var nb_opes = this._$container.find('.ope[canceled="false"]').length;
|
||||
this._$nb_opes.text(nb_opes);
|
||||
}
|
||||
|
||||
this.cancelOpe = function(ope, $ope = null) {
|
||||
if (!$ope)
|
||||
$ope = this.findOpe(ope['id']);
|
||||
_init_events() {
|
||||
var that = this;
|
||||
$(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é';
|
||||
var canceled_at = dateUTCToParis(ope['canceled_at']);
|
||||
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);
|
||||
if (to_cancel['opes'].length > 0 || to_cancel['transfers'].length > 0)
|
||||
that.cancel_operations(to_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) {
|
||||
return this.$container.find('.ope').filter(function() {
|
||||
return $(this).data('ope') == id
|
||||
});
|
||||
cancel_operations(to_cancel) {
|
||||
var that = this ;
|
||||
var on_success = function() {
|
||||
if (that.selection)
|
||||
that.selection.reset() ;
|
||||
$(that).trigger("cancel_done");
|
||||
}
|
||||
|
||||
this.cancelOpeGroup = function(opegroup) {
|
||||
var $opegroup = this.findOpeGroup(opegroup['id']);
|
||||
var trigramme = $opegroup.find('.trigramme').text();
|
||||
var amount = amountDisplay(
|
||||
parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme));
|
||||
$opegroup.find('.amount').text(amount);
|
||||
api_with_auth({
|
||||
url: Urls['kfet.kpsul.cancel_operations'](),
|
||||
data: to_cancel,
|
||||
on_success: on_success,
|
||||
})
|
||||
}
|
||||
|
||||
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 = {
|
||||
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_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>',
|
||||
display_trigramme: true,
|
||||
class KHistorySelection {
|
||||
|
||||
constructor(history) {
|
||||
this._$container = history._$container;
|
||||
this._init();
|
||||
}
|
||||
|
||||
_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) {
|
||||
return Number.parseInt(v);
|
||||
}
|
||||
|
@ -340,11 +354,24 @@ function getErrorsHtml(data) {
|
|||
return content;
|
||||
}
|
||||
|
||||
function displayErrors(html) {
|
||||
$.alert({
|
||||
title: 'Erreurs',
|
||||
content: html,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
var authDialog = new UserDialog({
|
||||
'title': 'Authentification requise',
|
||||
'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) {
|
||||
if (window.api_lock == 1)
|
||||
return false;
|
||||
|
@ -357,7 +384,7 @@ function api_with_auth(settings, password) {
|
|||
var on_success = settings.on_success || $.noop ;
|
||||
var on_400 = settings.on_400 || $.noop ;
|
||||
|
||||
$.ajax({
|
||||
return $.ajax({
|
||||
dataType: "json",
|
||||
url: url,
|
||||
method: "POST",
|
||||
|
@ -392,3 +419,9 @@ function api_with_auth(settings, password) {
|
|||
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.checkout_manager = new CheckoutManager(this);
|
||||
this.article_manager = new ArticleManager(this);
|
||||
this.history = new KHistory({
|
||||
api_options: {'opesonly': true},
|
||||
});
|
||||
|
||||
this._init_events();
|
||||
}
|
||||
|
||||
reset(soft) {
|
||||
|
@ -23,6 +28,7 @@ class KPsulManager {
|
|||
if (!soft) {
|
||||
this.checkout_manager.reset();
|
||||
this.article_manager.reset_data();
|
||||
this.history.fetch();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +43,14 @@ class KPsulManager {
|
|||
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._$nb = $('#article_number');
|
||||
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.list = new ArticleList() ;
|
||||
this.autocomplete = new ArticleAutocomplete(this);
|
||||
this.data = new ArticleList() ;
|
||||
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();
|
||||
OperationWebSocket.add_handler(data => this.update_data(data));
|
||||
}
|
||||
|
||||
get nb() {
|
||||
|
@ -429,7 +448,7 @@ class ArticleManager {
|
|||
}
|
||||
|
||||
display_list() {
|
||||
this.list.display(this._$container, this.templates) ;
|
||||
this.display.render(this.data);
|
||||
}
|
||||
|
||||
validate(article) {
|
||||
|
@ -449,25 +468,18 @@ class ArticleManager {
|
|||
}
|
||||
|
||||
reset_data() {
|
||||
this._$container.html('');
|
||||
this.list.clear();
|
||||
this.list.fromAPI()
|
||||
this.display.clear();
|
||||
this.data.clear();
|
||||
this.data.fromAPI()
|
||||
.done( () => this.display_list() );
|
||||
}
|
||||
|
||||
get_article(id) {
|
||||
return this.list.find('article', id);
|
||||
}
|
||||
|
||||
update_data(data) {
|
||||
for (let article_dict of data.articles) {
|
||||
var article = this.get_article(article_dict.id);
|
||||
|
||||
// For now, article additions are disregarded
|
||||
if (article) {
|
||||
article.stock = article_dict.stock;
|
||||
this._$container.find('#article-'+article.id+' .stock')
|
||||
.text(article.stock);
|
||||
var updated = this.data.update('article', article_dict.id, article_dict);
|
||||
if (updated) {
|
||||
this.display.update(updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -496,7 +508,7 @@ class ArticleManager {
|
|||
|
||||
this._$container.on('click', '.article', function() {
|
||||
var id = $(this).parent().attr('id').split('-')[1];
|
||||
var article = that.list.find('article', id);
|
||||
var article = that.data.find('article', id);
|
||||
if (article)
|
||||
that.validate(article);
|
||||
});
|
||||
|
@ -507,18 +519,26 @@ class ArticleManager {
|
|||
that.reset();
|
||||
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)
|
||||
that._$nb.val('');
|
||||
return true ;
|
||||
}
|
||||
|
||||
if (that.constructor.check_nb(that.nb+e.key))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//Note : this function may not be needed after the whole rework
|
||||
get_article(id) {
|
||||
return this.data.find('article', id) ;
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this.is_empty())
|
||||
this._$input.focus();
|
||||
|
@ -535,9 +555,9 @@ class ArticleManager {
|
|||
|
||||
class ArticleAutocomplete {
|
||||
|
||||
constructor(article_manager) {
|
||||
constructor(article_manager, $container) {
|
||||
this.manager = article_manager;
|
||||
this._$container = article_manager._$container ;
|
||||
this._$container = $container ;
|
||||
this._$input = $('#article_autocomplete');
|
||||
|
||||
this.showAll() ;
|
||||
|
@ -549,11 +569,12 @@ class ArticleAutocomplete {
|
|||
|
||||
// 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 arrowKeys = /^(37|38|39|40)$/;
|
||||
|
||||
this._$input
|
||||
.on('keydown', function(e) {
|
||||
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
|
||||
if(e.keyCode == 8) {
|
||||
that.update(text.substring(0, text.length-1), true);
|
||||
|
@ -571,7 +592,7 @@ class ArticleAutocomplete {
|
|||
update(prefix, backspace) {
|
||||
|
||||
this.resetMatch();
|
||||
var article_list = this.manager.list ;
|
||||
var article_list = this.manager.data ;
|
||||
var lower = prefix.toLowerCase() ;
|
||||
var that = this ;
|
||||
|
||||
|
@ -599,7 +620,7 @@ class ArticleAutocomplete {
|
|||
updateDisplay() {
|
||||
var that = this;
|
||||
|
||||
this.manager.list.traverse('category', function(category) {
|
||||
this.manager.data.traverse('category', function(category) {
|
||||
var is_active = false;
|
||||
for (let article of category.articles) {
|
||||
if (that.matching.indexOf(article) != -1) {
|
||||
|
@ -634,7 +655,7 @@ class ArticleAutocomplete {
|
|||
showAll() {
|
||||
var that = this;
|
||||
this.resetMatch();
|
||||
this.manager.list.traverse('article', function(article) {
|
||||
this.manager.data.traverse('article', function(article) {
|
||||
that.matching.push(article);
|
||||
});
|
||||
this.updateDisplay();
|
||||
|
|
|
@ -4,10 +4,16 @@
|
|||
{% load l10n %}
|
||||
|
||||
{% 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/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-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>
|
||||
{% if account.user == request.user %}
|
||||
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
|
||||
|
@ -94,33 +100,11 @@ $(document).ready(function() {
|
|||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
|
||||
'use strict';
|
||||
|
||||
khistory = new KHistory({
|
||||
display_trigramme: false,
|
||||
});
|
||||
var khistory = new KHistory({'no_trigramme': true});
|
||||
|
||||
function getHistory() {
|
||||
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();
|
||||
Config.reset(() => khistory.fetch({'accounts': [{{account.pk}}]}));
|
||||
});
|
||||
</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/multiple-select.css' %}">
|
||||
<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>
|
||||
|
@ -38,6 +39,9 @@
|
|||
<div class="line"><b>Comptes</b> {{ filter_form.accounts }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<a id="update_history" class="btn btn-primary btn-lg">Mettre à jour</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-9 col-content-right">
|
||||
|
@ -51,8 +55,8 @@
|
|||
<div class="content-right-block">
|
||||
<h2>Opérations</h2>
|
||||
<div>
|
||||
<table id="history" class="table">
|
||||
</table>
|
||||
<div id="history">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -61,9 +65,9 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
$(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 $to_date = $('#to_date');
|
||||
|
@ -78,7 +82,9 @@ $(document).ready(function() {
|
|||
return selected;
|
||||
}
|
||||
|
||||
function getHistory() {
|
||||
function updateHistory() {
|
||||
|
||||
// Get API options
|
||||
var data = {};
|
||||
if ($from_date.val())
|
||||
data['from'] = moment($from_date.val()).format('YYYY-MM-DD HH:mm:ss');
|
||||
|
@ -90,19 +96,8 @@ $(document).ready(function() {
|
|||
var accounts = getSelectedMultiple($accounts);
|
||||
data['accounts'] = accounts;
|
||||
|
||||
$.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);
|
||||
});
|
||||
// Update history
|
||||
khistory.fetch(data);
|
||||
}
|
||||
|
||||
$('#from_date').datetimepicker({
|
||||
|
@ -117,7 +112,7 @@ $(document).ready(function() {
|
|||
timeZone : 'Europe/Paris',
|
||||
format : 'YYYY-MM-DD HH:mm',
|
||||
stepping : 5,
|
||||
defaultDate: moment(),
|
||||
defaultDate: moment().add(5, 'minutes'), // workaround for 'stepping' rounding
|
||||
locale : 'fr',
|
||||
showTodayButton: true,
|
||||
});
|
||||
|
@ -133,69 +128,11 @@ $(document).ready(function() {
|
|||
filter: true,
|
||||
});
|
||||
|
||||
$("input").on('dp.change change', function() {
|
||||
khistory.reset();
|
||||
getHistory();
|
||||
$("#update_history").on('click', function() {
|
||||
updateHistory();
|
||||
});
|
||||
|
||||
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)
|
||||
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();
|
||||
Config.reset(updateHistory);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -156,9 +156,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="history2">
|
||||
</div>
|
||||
|
||||
<form id="operationgroup_form" style="display:none;">{{ operationgroup_form }}</form>
|
||||
<form id="operation_formset" style="display:none;">{{ operation_formset }}</form>
|
||||
|
||||
|
@ -199,21 +196,6 @@ $(document).ready(function() {
|
|||
$('#id_comment').val('');
|
||||
}
|
||||
|
||||
// -----
|
||||
// Errors ajax
|
||||
// -----
|
||||
|
||||
function displayErrors(html) {
|
||||
$.alert({
|
||||
title: 'Erreurs',
|
||||
content: html,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
// -----
|
||||
// Perform operations
|
||||
// -----
|
||||
|
@ -246,33 +228,6 @@ $(document).ready(function() {
|
|||
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
|
||||
// -----
|
||||
|
@ -311,7 +266,7 @@ $(document).ready(function() {
|
|||
var article_basket_html = $(item_basket_default_html);
|
||||
article_basket_html
|
||||
.attr('data-opeindex', index)
|
||||
.find('.number').text(nb).end()
|
||||
.find('.number').text('('+nb+'/'+article.stock+')').end()
|
||||
.find('.name').text(article.name).end()
|
||||
.find('.amount').text(amountToUKF(amount_euro, kpsul.account_manager.account.is_cof));
|
||||
basket_container.prepend(article_basket_html);
|
||||
|
@ -634,29 +589,6 @@ $(document).ready(function() {
|
|||
.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');
|
||||
|
||||
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
|
||||
// -----
|
||||
|
@ -798,11 +667,10 @@ $(document).ready(function() {
|
|||
coolReset(give_tri_focus);
|
||||
kpsul.checkout_manager.reset();
|
||||
resetPreviousOp();
|
||||
khistory.reset();
|
||||
Config.reset(function() {
|
||||
kpsul.article_manager.reset_data();
|
||||
displayAddcost();
|
||||
getHistory();
|
||||
kpsul.history.fetch();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -882,10 +750,23 @@ $(document).ready(function() {
|
|||
updateBasketAmount: updateBasketAmount,
|
||||
updateBasketRel: updateBasketRel,
|
||||
performOperations: performOperations,
|
||||
coolReset: coolReset,
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
});
|
||||
|
|
|
@ -4,9 +4,15 @@
|
|||
{% block extra_head %}
|
||||
<link rel="stylesheet" style="text/css" href="{% static 'kfet/css/jquery-ui.min.css' %}">
|
||||
<script type="text/javascript" src="{% static 'kfet/js/js.cookie.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/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/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 %}
|
||||
|
||||
{% block title %}Transferts{% endblock %}
|
||||
|
@ -18,6 +24,8 @@
|
|||
<div class="col-sm-4 col-md-3 col-content-left">
|
||||
<div class="content-left">
|
||||
<div class="content-left-top">
|
||||
<div class="line line-big" id="nb_opes"></div>
|
||||
<div class="line line-bigsub">transferts</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<a class="btn btn-primary btn-lg" href="{% url 'kfet.transfers.create' %}">
|
||||
|
@ -31,23 +39,8 @@
|
|||
<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 id="history" class="table">
|
||||
</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>
|
||||
|
@ -56,88 +49,11 @@
|
|||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function() {
|
||||
'use strict';
|
||||
|
||||
lock = 0;
|
||||
|
||||
function displayErrors(html) {
|
||||
$.alert({
|
||||
title: 'Erreurs',
|
||||
content: html,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function cancelTransfers(transfers_array, password = '') {
|
||||
if (lock == 1)
|
||||
return false
|
||||
lock = 1;
|
||||
var data = { 'transfers' : transfers_array }
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url : "{% url 'kfet.transfers.cancel' %}",
|
||||
method : "POST",
|
||||
data : data,
|
||||
beforeSend: function ($xhr) {
|
||||
$xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
if (password != '')
|
||||
$xhr.setRequestHeader("KFetPassword", password);
|
||||
},
|
||||
|
||||
})
|
||||
.done(function(data) {
|
||||
for (var i=0; i<data['canceled'].length; i++) {
|
||||
$('#history').find('.transfer[data-transfer='+data['canceled'][i]+']')
|
||||
.addClass('canceled');
|
||||
}
|
||||
$('#history').find('.ui-selected').removeClass('ui-selected');
|
||||
lock = 0;
|
||||
})
|
||||
.fail(function($xhr) {
|
||||
var data = $xhr.responseJSON;
|
||||
switch ($xhr.status) {
|
||||
case 403:
|
||||
requestAuth(data, function(password) {
|
||||
cancelTransfers(transfers_array, password);
|
||||
});
|
||||
break;
|
||||
case 400:
|
||||
displayErrors(getErrorsHtml(data));
|
||||
break;
|
||||
}
|
||||
lock = 0;
|
||||
});
|
||||
}
|
||||
|
||||
$('#history').selectable({
|
||||
filter: 'div.transfergroup, div.transfer',
|
||||
selected: function(e, ui) {
|
||||
$(ui.selected).each(function() {
|
||||
if ($(this).hasClass('transfergroup')) {
|
||||
var transfergroup = $(this).attr('data-transfergroup');
|
||||
$(this).siblings('.ope').filter(function() {
|
||||
return $(this).attr('data-transfergroup') == transfergroup
|
||||
}).addClass('ui-selected');
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
$(document).on('keydown', function (e) {
|
||||
if (e.keyCode == 46) {
|
||||
// DEL (Suppr)
|
||||
var transfers_to_cancel = [];
|
||||
$('#history').find('.transfer.ui-selected').each(function () {
|
||||
transfers_to_cancel.push($(this).attr('data-transfer'));
|
||||
});
|
||||
if (transfers_to_cancel.length > 0)
|
||||
cancelTransfers(transfers_to_cancel);
|
||||
}
|
||||
});
|
||||
var khistory = new KHistory();
|
||||
|
||||
Config.reset(() => khistory.fetch({'transfersonly': true}));
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
@ -203,8 +203,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
|
||||
|
|
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.forms import formset_factory
|
||||
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.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
|
@ -1158,31 +1158,47 @@ def kpsul_perform_operations(request):
|
|||
websocket_data = {}
|
||||
websocket_data['opegroups'] = [{
|
||||
'add': True,
|
||||
'modelname': 'opegroup',
|
||||
'content': {
|
||||
'id': operationgroup.pk,
|
||||
'amount': operationgroup.amount,
|
||||
'checkout__name': operationgroup.checkout.name,
|
||||
'at': operationgroup.at,
|
||||
'is_cof': operationgroup.is_cof,
|
||||
'comment': operationgroup.comment,
|
||||
'valid_by__trigramme': (operationgroup.valid_by and
|
||||
'valid_by': (operationgroup.valid_by and
|
||||
operationgroup.valid_by.trigramme or None),
|
||||
'on_acc__trigramme': operationgroup.on_acc.trigramme,
|
||||
'opes': [],
|
||||
'trigramme': operationgroup.on_acc.trigramme,
|
||||
# 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 = {
|
||||
'id': operation.pk, 'type': operation.type,
|
||||
'amount': operation.amount,
|
||||
'addcost_amount': operation.addcost_amount,
|
||||
'addcost_for__trigramme': (
|
||||
operation.addcost_for and addcost_for.trigramme or 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,
|
||||
'content': {
|
||||
'id': ope.id,
|
||||
'amount': ope.amount,
|
||||
'canceled_at': None,
|
||||
'canceled_by': 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
|
||||
operationgroup.checkout.refresh_from_db()
|
||||
websocket_data['checkouts'] = [{
|
||||
|
@ -1205,37 +1221,63 @@ 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(int, filter(None, request.POST.getlist('opes[]', []))))
|
||||
)
|
||||
transfers_post = (
|
||||
set(map(int, filter(None, request.POST.getlist('transfers[]', []))))
|
||||
)
|
||||
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 ]
|
||||
opes_pk = [ope.pk for ope in opes_all]
|
||||
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:
|
||||
data['errors']['opes_notexisting'] = opes_notexisting
|
||||
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
|
||||
transfers = []
|
||||
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
|
||||
to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé
|
||||
to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses
|
||||
to_articles_stocks = defaultdict(lambda:0) # ------ sur les stocks d'articles
|
||||
# Modifs à faire sur les balances des comptes
|
||||
to_accounts_balances = defaultdict(lambda: 0)
|
||||
# ------ sur les montants des groupes d'opé
|
||||
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:
|
||||
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
|
||||
|
@ -1262,7 +1304,8 @@ def kpsul_cancel_operations(request):
|
|||
# par `.save()`, amount_error est recalculé automatiquement,
|
||||
# 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
|
||||
last_statement = (CheckoutStatement.objects
|
||||
last_statement = \
|
||||
(CheckoutStatement.objects
|
||||
.filter(checkout=ope.group.checkout)
|
||||
.order_by('at')
|
||||
.last())
|
||||
|
@ -1281,23 +1324,41 @@ def kpsul_cancel_operations(request):
|
|||
# Note : si InventoryArticle est maj par .save(), stock_error
|
||||
# est recalculé automatiquement
|
||||
if ope.article and ope.article_nb:
|
||||
last_stock = (InventoryArticle.objects
|
||||
last_stock = (
|
||||
InventoryArticle.objects
|
||||
.select_related('inventory')
|
||||
.filter(article=ope.article)
|
||||
.order_by('inventory__at')
|
||||
.last())
|
||||
.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:
|
||||
|
@ -1317,6 +1378,10 @@ 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
|
||||
|
@ -1329,20 +1394,22 @@ def kpsul_cancel_operations(request):
|
|||
account.update_negative()
|
||||
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,
|
||||
|
@ -1350,24 +1417,35 @@ def kpsul_cancel_operations(request):
|
|||
'amount': opegroup['amount'],
|
||||
'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:
|
||||
websocket_data['opes'].append({
|
||||
'cancellation': True,
|
||||
'modelname': 'ope',
|
||||
'id': ope,
|
||||
'canceled_by__trigramme': canceled_by__trigramme,
|
||||
'canceled_by': canceled_by,
|
||||
'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
|
||||
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({
|
||||
|
@ -1375,83 +1453,169 @@ 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
|
||||
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)
|
||||
from_date = request.GET.get('from', None)
|
||||
to_date = request.GET.get('to', None)
|
||||
checkouts = request.GET.getlist('checkouts[]', None)
|
||||
accounts = request.GET.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
|
||||
queryset_prefetch = Operation.objects.select_related(
|
||||
'canceled_by__trigramme', 'addcost_for__trigramme',
|
||||
'article__name')
|
||||
ope_queryset_prefetch = Operation.objects.select_related(
|
||||
'canceled_by', 'addcost_for',
|
||||
'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
|
||||
opegroups = (OperationGroup.objects
|
||||
.prefetch_related(Prefetch('opes', queryset = queryset_prefetch))
|
||||
.select_related('on_acc__trigramme', 'valid_by__trigramme')
|
||||
opegroups = (
|
||||
OperationGroup.objects
|
||||
.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 opes_only:
|
||||
transfergroups = TransferGroup.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 = []
|
||||
related_data = defaultdict(list)
|
||||
objects_data = defaultdict(list)
|
||||
for opegroup in opegroups:
|
||||
opegroup_dict = {
|
||||
'id' : opegroup.id,
|
||||
'amount' : opegroup.amount,
|
||||
'at' : opegroup.at,
|
||||
'checkout_id': opegroup.checkout_id,
|
||||
'is_cof' : opegroup.is_cof,
|
||||
'comment' : opegroup.comment,
|
||||
'opes' : [],
|
||||
'on_acc__trigramme':
|
||||
'id': opegroup.id,
|
||||
'amount': opegroup.amount,
|
||||
'at': opegroup.at,
|
||||
'is_cof': opegroup.is_cof,
|
||||
'comment': opegroup.comment,
|
||||
'trigramme':
|
||||
opegroup.on_acc and opegroup.on_acc.trigramme or None,
|
||||
}
|
||||
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)
|
||||
|
||||
for ope in opegroup.opes.all():
|
||||
ope_dict = {
|
||||
'id' : ope.id,
|
||||
'type' : ope.type,
|
||||
'amount' : ope.amount,
|
||||
'article_nb' : ope.article_nb,
|
||||
'addcost_amount': ope.addcost_amount,
|
||||
'canceled_at' : ope.canceled_at,
|
||||
'article__name':
|
||||
ope.article and ope.article.name or None,
|
||||
'addcost_for__trigramme':
|
||||
ope.addcost_for and ope.addcost_for.trigramme or None,
|
||||
'id': ope.id,
|
||||
'amount': ope.amount,
|
||||
'canceled_at': ope.canceled_at,
|
||||
'is_cof': opegroup.is_cof,
|
||||
'trigramme':
|
||||
opegroup.on_acc and opegroup.on_acc.trigramme or None,
|
||||
'opegroup__id': opegroup.id,
|
||||
}
|
||||
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)
|
||||
opegroup_dict['opes'].append(ope_dict)
|
||||
opegroups_list.append(opegroup_dict)
|
||||
return JsonResponse({ 'opegroups': opegroups_list })
|
||||
|
||||
if ope.type == Operation.PURCHASE:
|
||||
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
|
||||
|
@ -1461,26 +1625,32 @@ def kpsul_articles_data(request):
|
|||
.filter(is_sold=True)
|
||||
.select_related('category'))
|
||||
articlelist = []
|
||||
categorylist = []
|
||||
|
||||
# TODO: nice queryset, no duplicate categories
|
||||
for article in articles:
|
||||
articlelist.append({
|
||||
'modelname': 'article',
|
||||
'content': {
|
||||
'id': article.id,
|
||||
'name': article.name,
|
||||
'price': article.price,
|
||||
'stock': article.stock,
|
||||
},
|
||||
'parent': {
|
||||
'modelname': 'category',
|
||||
'content': {
|
||||
'category__id': article.category.id,
|
||||
})
|
||||
categorylist.append({
|
||||
'id': article.category.id,
|
||||
'name': article.category.name,
|
||||
'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
|
||||
# -----
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def transfers(request):
|
||||
transfergroups = (TransferGroup.objects
|
||||
.prefetch_related('transfers')
|
||||
.order_by('-at'))
|
||||
return render(request, 'kfet/transfers.html', {
|
||||
'transfergroups': transfergroups,
|
||||
})
|
||||
return render(request, 'kfet/transfers.html')
|
||||
|
||||
@teamkfet_required
|
||||
def transfers_create(request):
|
||||
|
@ -1535,20 +1701,24 @@ def transfers_create(request):
|
|||
return render(request, 'kfet/transfers_create.html',
|
||||
{ 'transfer_formset': transfer_formset })
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def perform_transfers(request):
|
||||
data = { 'errors': {}, 'transfers': [], 'transfergroup': 0 }
|
||||
data = {'errors': {}, 'transfers': [], 'transfergroup': 0}
|
||||
|
||||
# Checking transfer_formset
|
||||
transfer_formset = TransferFormSet(request.POST)
|
||||
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
|
||||
required_perms = set(['kfet.add_transfer']) # Required perms to perform all transfers
|
||||
to_accounts_balances = defaultdict(lambda:0) # For balances of accounts
|
||||
# Required perms to perform all transfers
|
||||
required_perms = set(['kfet.add_transfer'])
|
||||
# For balances of accounts
|
||||
to_accounts_balances = defaultdict(lambda: 0)
|
||||
|
||||
for transfer in transfers:
|
||||
to_accounts_balances[transfer.from_acc] -= transfer.amount
|
||||
|
@ -1560,7 +1730,7 @@ def perform_transfers(request):
|
|||
# Checking if ok on all accounts
|
||||
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:
|
||||
|
@ -1586,7 +1756,7 @@ def perform_transfers(request):
|
|||
# Updating balances accounts
|
||||
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])
|
||||
account.refresh_from_db()
|
||||
if account.balance < 0:
|
||||
if hasattr(account, 'negative'):
|
||||
|
@ -1595,10 +1765,10 @@ def perform_transfers(request):
|
|||
account.negative.save()
|
||||
else:
|
||||
negative = AccountNegative(
|
||||
account = account, start = timezone.now())
|
||||
account=account, start=timezone.now())
|
||||
negative.save()
|
||||
elif (hasattr(account, 'negative')
|
||||
and not account.negative.balance_offset):
|
||||
elif (hasattr(account, 'negative') and
|
||||
not account.negative.balance_offset):
|
||||
account.negative.delete()
|
||||
|
||||
# Saving transfer group
|
||||
|
@ -1611,106 +1781,31 @@ def perform_transfers(request):
|
|||
transfer.save()
|
||||
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)
|
||||
|
||||
@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):
|
||||
queryset = (Inventory.objects
|
||||
|
|
Loading…
Reference in a new issue