forked from DGNum/gestioCOF
Many tables in kfet app templates become sortable: account list, negative account list, article list, article inventory list, article supplier list, article category list, checkout list, checkout statement list, inventory list, inventory details, order list, order creation, order details. This is achieved thanks to the jQuery plugin 'tablesorter': https://mottie.github.io/tablesorter/docs/ - Affected tables also got sticky headers (it stays visible on scroll). - Dates format are modified in order to ease the date sorting with the plugin (it avoids writing a custom parser, or an extractor from additional hidden element in the table cells). - Tables whose content is classified by category (of articles) now uses several tbodies. This has minor effects on the tables style. - Tags of the header help signs become 'i', instead of 'span', in order to avoid weird spacing.
311 lines
9.2 KiB
JavaScript
311 lines
9.2 KiB
JavaScript
/**
|
|
* CSRF Token
|
|
*/
|
|
|
|
var csrftoken = '';
|
|
if (typeof Cookies !== 'undefined')
|
|
csrftoken = Cookies.get('csrftoken');
|
|
|
|
// Add CSRF token in header of AJAX requests.
|
|
|
|
function csrfSafeMethod(method) {
|
|
// these HTTP methods do not require CSRF protection
|
|
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
|
}
|
|
|
|
$.ajaxSetup({
|
|
beforeSend: function(xhr, settings) {
|
|
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
|
|
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
|
}
|
|
}
|
|
});
|
|
|
|
function add_csrf_form($form) {
|
|
$form.append(
|
|
$('<input>', {'name': 'csrfmiddlewaretoken', 'value': csrftoken})
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Generic Websocket class and k-psul ws instanciation
|
|
*/
|
|
|
|
class KfetWebsocket {
|
|
|
|
static get defaults() {
|
|
return {
|
|
relative_url: '',
|
|
default_msg: {},
|
|
handlers: [],
|
|
base_path: '/ws/k-fet/'
|
|
};
|
|
}
|
|
|
|
constructor(data) {
|
|
$.extend(this, this.constructor.defaults, data);
|
|
if (window.location.pathname.startsWith('/gestion/'))
|
|
this.base_path = '/gestion' + this.base_path;
|
|
}
|
|
|
|
get url() {
|
|
var protocol = window.location.protocol == 'https:' ? 'wss' : 'ws';
|
|
var host = window.location.host;
|
|
return protocol + "://" + host + this.base_path + this.relative_url;
|
|
}
|
|
|
|
add_handler(handler) {
|
|
if (!this.socket)
|
|
this.listen();
|
|
|
|
this.handlers.push(handler);
|
|
}
|
|
|
|
listen() {
|
|
var that = this;
|
|
this.socket = new ReconnectingWebSocket(this.url);
|
|
|
|
this.socket.onmessage = function(e) {
|
|
var data = $.extend({}, that.default_msg, JSON.parse(e.data));
|
|
for (let handler of that.handlers) {
|
|
handler(data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var OperationWebSocket = new KfetWebsocket({
|
|
'relative_url': 'k-psul/',
|
|
'default_msg': {'opegroups':[],'opes':[],'checkouts':[],'articles':[]},
|
|
});
|
|
|
|
function dateUTCToParis(date) {
|
|
return moment.tz(date, 'UTC').tz('Europe/Paris');
|
|
}
|
|
|
|
function amountDisplay(amount, is_cof=false, tri='') {
|
|
if (tri == 'LIQ')
|
|
return (- amount).toFixed(2) +'€';
|
|
return amountToUKF(amount, is_cof);
|
|
}
|
|
|
|
function amountToUKF(amount, is_cof=false, account=false) {
|
|
var rounding = account ? Math.floor : Math.round ;
|
|
var coef_cof = is_cof ? 1 + settings['subvention_cof'] / 100 : 1;
|
|
return rounding(amount * coef_cof * 10);
|
|
}
|
|
|
|
function isValidTrigramme(trigramme) {
|
|
var pattern = /^[^a-z]{3}$/;
|
|
return trigramme.match(pattern);
|
|
}
|
|
|
|
function getErrorsHtml(data) {
|
|
var content = '';
|
|
if (!data)
|
|
return "L'utilisateur n'est pas dans l'équipe";
|
|
if ('operation_group' in data['errors']) {
|
|
content += 'Général';
|
|
content += '<ul>';
|
|
if (data['errors']['operation_group'].indexOf('on_acc') != -1)
|
|
content += '<li>Pas de compte sélectionné</li>';
|
|
if (data['errors']['operation_group'].indexOf('checkout') != -1)
|
|
content += '<li>Pas de caisse sélectionnée</li>';
|
|
content += '</ul>';
|
|
}
|
|
if ('missing_perms' in data['errors']) {
|
|
content += 'Permissions manquantes';
|
|
content += '<ul>';
|
|
for (var i=0; i<data['errors']['missing_perms'].length; i++)
|
|
content += '<li>'+data['errors']['missing_perms'][i]+'</li>';
|
|
content += '</ul>';
|
|
}
|
|
if ('negative' in data['errors']) {
|
|
if (window.location.pathname.startsWith('/gestion/')) {
|
|
var url_base = '/gestion/k-fet/accounts/';
|
|
} else {
|
|
var url_base = '/k-fet/accounts/';
|
|
}
|
|
for (var i=0; i<data['errors']['negative'].length; i++) {
|
|
content += '<a class="btn btn-primary" href="'+url_base+data['errors']['negative'][i]+'/edit" target="_blank" style="width:100%">Autorisation de négatif requise pour '+data['errors']['negative'][i]+'</a>';
|
|
}
|
|
}
|
|
if ('addcost' in data['errors']) {
|
|
content += '<ul>';
|
|
if (data['errors']['addcost'].indexOf('__all__') != -1)
|
|
content += '<li>Compte invalide</li>';
|
|
if (data['errors']['addcost'].indexOf('amount') != -1)
|
|
content += '<li>Montant invalide</li>';
|
|
content += '</ul>';
|
|
}
|
|
if ('account' in data['errors']) {
|
|
content += 'Général';
|
|
content += '<ul>';
|
|
content += '<li>Opération invalide sur le compte '+data['errors']['account']+'</li>';
|
|
content += '</ul>';
|
|
}
|
|
return content;
|
|
}
|
|
|
|
function requestAuth(data, callback, focus_next = null) {
|
|
var content = getErrorsHtml(data);
|
|
content += '<div class="capslock"><span class="glyphicon glyphicon-lock"></span><input type="password" name="password" autofocus><div>',
|
|
$.confirm({
|
|
title: 'Authentification requise',
|
|
content: content,
|
|
backgroundDismiss: true,
|
|
animation:'top',
|
|
closeAnimation:'bottom',
|
|
keyboardEnabled: true,
|
|
confirm: function() {
|
|
var password = this.$content.find('input').val();
|
|
callback(password);
|
|
},
|
|
onOpen: function() {
|
|
var that = this;
|
|
var capslock = -1 ; // 1 -> caps on ; 0 -> caps off ; -1 or 2 -> unknown
|
|
this.$content.find('input').on('keypress', function(e) {
|
|
if (e.keyCode == 13)
|
|
that.$confirmButton.click();
|
|
|
|
var s = String.fromCharCode(e.which);
|
|
if ((s.toUpperCase() === s && s.toLowerCase() !== s && !e.shiftKey)|| //caps on, shift off
|
|
(s.toUpperCase() !== s && s.toLowerCase() === s && e.shiftKey)) { //caps on, shift on
|
|
capslock = 1 ;
|
|
} else if ((s.toLowerCase() === s && s.toUpperCase() !== s && !e.shiftKey)|| //caps off, shift off
|
|
(s.toLowerCase() !== s && s.toUpperCase() === s && e.shiftKey)) { //caps off, shift on
|
|
capslock = 0 ;
|
|
}
|
|
if (capslock == 1)
|
|
$('.capslock .glyphicon').show() ;
|
|
else if (capslock == 0)
|
|
$('.capslock .glyphicon').hide() ;
|
|
});
|
|
// Capslock key is not detected by keypress
|
|
this.$content.find('input').on('keydown', function(e) {
|
|
if (e.which == 20) {
|
|
capslock = 1-capslock ;
|
|
}
|
|
if (capslock == 1)
|
|
$('.capslock .glyphicon').show() ;
|
|
else if (capslock == 0)
|
|
$('.capslock .glyphicon').hide() ;
|
|
});
|
|
},
|
|
onClose: function() {
|
|
if (focus_next)
|
|
this._lastFocused = focus_next;
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Setup jquery-confirm
|
|
*/
|
|
|
|
jconfirm.defaults = {
|
|
confirmButton: '<span class="glyphicon glyphicon-ok"></span>',
|
|
cancelButton: '<span class="glyphicon glyphicon-remove"></span>'
|
|
};
|
|
|
|
|
|
/**
|
|
* Create form node, given an url used as 'action', with csrftoken set.
|
|
*/
|
|
function create_form(url) {
|
|
let $form = $('<form>', {
|
|
'action': url,
|
|
'method': 'post',
|
|
});
|
|
add_csrf_form($form);
|
|
return $form;
|
|
}
|
|
|
|
|
|
/**
|
|
* Emit a POST request from <a> tag.
|
|
*
|
|
* Usage:
|
|
* <a href="#" data-url="{target url}" onclick="submit_url(this)">{…}</a>
|
|
*/
|
|
function submit_url(el) {
|
|
let url = $(el).data('url');
|
|
create_form(url).appendTo($('body')).submit();
|
|
}
|
|
|
|
|
|
/**
|
|
* jquery-tablesorter
|
|
* https://mottie.github.io/tablesorter/docs/
|
|
*
|
|
* Known bugs (v2.29.0):
|
|
* - Sort order icons in sticky headers are not updated.
|
|
* Status: Fixed in next release.
|
|
*
|
|
* TODO:
|
|
* - Handle i18n.
|
|
*/
|
|
|
|
|
|
function registerBoolParser(id, true_str, false_str) {
|
|
$.tablesorter.addParser({
|
|
id: id,
|
|
format: function(s) {
|
|
return s.toLowerCase()
|
|
.replace(true_str, 1)
|
|
.replace(false_str, 0);
|
|
},
|
|
type: 'numeric'
|
|
});
|
|
}
|
|
|
|
|
|
// Parsers for the text representations of boolean.
|
|
registerBoolParser('yesno', 'oui', 'non');
|
|
|
|
registerBoolParser('article__is_sold', 'en vente', 'non vendu');
|
|
registerBoolParser('article__hidden', 'caché', 'affiché');
|
|
|
|
|
|
// https://mottie.github.io/tablesorter/docs/index.html#variable-defaults
|
|
$.extend(true, $.tablesorter.defaults, {
|
|
headerTemplate: '{content} {icon}',
|
|
|
|
cssIconAsc : 'glyphicon glyphicon-chevron-up',
|
|
cssIconDesc : 'glyphicon glyphicon-chevron-down',
|
|
cssIconNone : 'glyphicon glyphicon-resize-vertical',
|
|
|
|
// Only four-digits format year is handled by the builtin parser
|
|
// 'shortDate'.
|
|
dateFormat: 'ddmmyyyy',
|
|
|
|
// Accented characters are replaced with their non-accented one.
|
|
sortLocaleCompare: true,
|
|
// French format: 1 234,56
|
|
usNumberFormat: false,
|
|
|
|
widgets: ['stickyHeaders'],
|
|
widgetOptions: {
|
|
stickyHeaders_offset: '.navbar',
|
|
}
|
|
});
|
|
|
|
|
|
// https://mottie.github.io/tablesorter/docs/index.html#variable-language
|
|
$.extend($.tablesorter.language, {
|
|
sortAsc : 'Trié par ordre croissant, ',
|
|
sortDesc : 'Trié par ordre décroissant, ',
|
|
sortNone : 'Non trié, ',
|
|
sortDisabled : 'tri désactivé et/ou non-modifiable',
|
|
nextAsc : 'cliquer pour trier par ordre croissant',
|
|
nextDesc : 'cliquer pour trier par ordre décroissant',
|
|
nextNone : 'cliquer pour retirer le tri'
|
|
});
|
|
|
|
|
|
$( function() {
|
|
$('.sortable').tablesorter();
|
|
});
|