forked from DGNum/gestioCOF
Better k-fet js and more
JavaScript ---------- - Basic classes that can be inherited to define a new class for a django model in javascript. - Formatters classes are used to render properties and attributes of the instances of models classes. - New classes to handle Account, Checkout, Statement models. - Refactor K-Psul JS (part n/m). - Better file organization. Views ----- - 'kpsul.checkout_data' is cleaner. Last statement is added to the JSON response with GET paramater 'last_statement'. - 'account.read.json' is merged in account.read. JSON response is sent if GET parametter 'format' is set to 'json'. - Fix PEP8 of concerned views. New requirement: django-js-reverse ---------------------------------- Used to resolve the URLs defined in the project in JavaScript. See https://github.com/ierror/django-js-reverse
This commit is contained in:
parent
ee848d210e
commit
a9cb50b38d
10 changed files with 1168 additions and 574 deletions
|
@ -50,6 +50,7 @@ INSTALLED_APPS = (
|
||||||
'kfet',
|
'kfet',
|
||||||
'channels',
|
'channels',
|
||||||
'widget_tweaks',
|
'widget_tweaks',
|
||||||
|
'django_js_reverse',
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
MIDDLEWARE_CLASSES = (
|
||||||
|
|
|
@ -14,9 +14,11 @@ from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.views.decorators.cache import cache_page
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from django.contrib.auth import views as django_views
|
from django.contrib.auth import views as django_views
|
||||||
from django_cas_ng import views as django_cas_views
|
from django_cas_ng import views as django_cas_views
|
||||||
|
from django_js_reverse.views import urls_js
|
||||||
|
|
||||||
from gestioncof import views as gestioncof_views, csv_views
|
from gestioncof import views as gestioncof_views, csv_views
|
||||||
from gestioncof.urls import export_patterns, petitcours_patterns, \
|
from gestioncof.urls import export_patterns, petitcours_patterns, \
|
||||||
|
@ -85,6 +87,7 @@ urlpatterns = [
|
||||||
url(r'^utile_cof/diff_cof$', gestioncof_views.liste_diffcof),
|
url(r'^utile_cof/diff_cof$', gestioncof_views.liste_diffcof),
|
||||||
url(r'^utile_bda/bda_revente$', gestioncof_views.liste_bdarevente),
|
url(r'^utile_bda/bda_revente$', gestioncof_views.liste_bdarevente),
|
||||||
url(r'^k-fet/', include('kfet.urls')),
|
url(r'^k-fet/', include('kfet.urls')),
|
||||||
|
url(r'^jsreverse/$', cache_page(3600)(urls_js), name='js_reverse'),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
|
@ -38,10 +38,10 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
height:120px;
|
height:120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#account[data-balance="ok"] #account_form input { background:#009011; color:#FFF;}
|
#account[data_balance="ok"] #account_form input { background:#009011; color:#FFF;}
|
||||||
#account[data-balance="low"] #account_form input { background:#EC6400; color:#FFF; }
|
#account[data_balance="low"] #account_form input { background:#EC6400; color:#FFF; }
|
||||||
#account[data-balance="neg"] #account_form input { background:#C8102E; color:#FFF; }
|
#account[data_balance="neg"] #account_form input { background:#C8102E; color:#FFF; }
|
||||||
#account[data-balance="frozen"] #account_form input { background:#000FBA; color:#FFF; }
|
#account[data_balance="frozen"] #account_form input { background:#000FBA; color:#FFF; }
|
||||||
|
|
||||||
#account_form {
|
#account_form {
|
||||||
padding:0;
|
padding:0;
|
||||||
|
@ -79,7 +79,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
font-size:12px;
|
font-size:12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#account_data #account-balance {
|
#account_data #account-balance_ukf {
|
||||||
height:40px;
|
height:40px;
|
||||||
line-height:40px;
|
line-height:40px;
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
line-height:24px;
|
line-height:24px;
|
||||||
}
|
}
|
||||||
#account_data #account-balance {
|
#account_data #account-balance_ukf {
|
||||||
font-size:50px;
|
font-size:50px;
|
||||||
line-height:60px;
|
line-height:60px;
|
||||||
height:60px;
|
height:60px;
|
||||||
|
|
685
kfet/static/kfet/js/kfet.api.js
Normal file
685
kfet/static/kfet/js/kfet.api.js
Normal file
|
@ -0,0 +1,685 @@
|
||||||
|
/**
|
||||||
|
* @file Interact with k-fet API.
|
||||||
|
* @copyright 2017 cof-geek
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get and store K-Psul config from API.
|
||||||
|
* <br><br>
|
||||||
|
*
|
||||||
|
* Config should be accessed statically only.
|
||||||
|
*/
|
||||||
|
class Config {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create config object.
|
||||||
|
* @private
|
||||||
|
* @return {object} object - config keys/values
|
||||||
|
*/
|
||||||
|
static _get_or_create_config() {
|
||||||
|
if (window.config === undefined)
|
||||||
|
window.config = {};
|
||||||
|
return window.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get config from API.
|
||||||
|
* @param {jQueryAjaxComplete} [callback] - A function to be called when
|
||||||
|
* the request finishes.
|
||||||
|
*/
|
||||||
|
static reset(callback) {
|
||||||
|
$.getJSON(Urls['kfet.kpsul.get_settings']())
|
||||||
|
.done(function(data) {
|
||||||
|
for (var key in data) {
|
||||||
|
Config.set(key, data[key]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.always(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value for key in config.
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
static get(key) {
|
||||||
|
return this._get_or_create_config()[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set value for key in config.
|
||||||
|
* @param {string} key
|
||||||
|
* @param {*} value
|
||||||
|
*/
|
||||||
|
static set(key, value) {
|
||||||
|
// API currently returns string for Decimal type
|
||||||
|
if (['addcost_amount', 'subvention_cof'].indexOf(key) > -1)
|
||||||
|
value = floatCheck(value);
|
||||||
|
this._get_or_create_config()[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- ---------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual namespace for models.
|
||||||
|
* <br><br>
|
||||||
|
*
|
||||||
|
* A model subclasses {@link Models.ModelObject}.<br>
|
||||||
|
* A model whose instances can be got from API subclasses
|
||||||
|
* {@link Models.APIModelObject}.<br>
|
||||||
|
* These two classes should not be used directly.
|
||||||
|
* <br><br>
|
||||||
|
*
|
||||||
|
* Models with API support:
|
||||||
|
* {@link Models.Account} (partial),
|
||||||
|
* {@link Models.Checkout} (partial).
|
||||||
|
* <br>
|
||||||
|
* Models without API support:
|
||||||
|
* {@link Models.Statement}.
|
||||||
|
*
|
||||||
|
* @namespace Models
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended object
|
||||||
|
* @memberof Models
|
||||||
|
*/
|
||||||
|
class ModelObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These properties always exist on instances of this class.
|
||||||
|
* @abstract
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
static get props() { return []; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default values for properties given by <tt>props</tt> static member.
|
||||||
|
* @abstract
|
||||||
|
* @type {Object.<string, *>}
|
||||||
|
*/
|
||||||
|
static get default_data() { return {}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new instance from data or default values.
|
||||||
|
* @param {Object} [data={}] - data to store in instance
|
||||||
|
*/
|
||||||
|
constructor(data) {
|
||||||
|
this.from(data || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an instance is empty.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
is_empty() {
|
||||||
|
return ( this.id === undefined ) ? false : this.id == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Formatters|Formatter} used for rendering.
|
||||||
|
* @default {@link Formatters.Formatter}
|
||||||
|
* @type {Formatter}
|
||||||
|
*/
|
||||||
|
formatter() { return Formatter; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set properties of this instance from data object ones. If a property is
|
||||||
|
* not present in data object, its value fallback to
|
||||||
|
* {@link Models.ModelObject.default_data} one.
|
||||||
|
* @param {Object} data
|
||||||
|
* @todo Restrict to props
|
||||||
|
*/
|
||||||
|
from(data) {
|
||||||
|
// TODO: add restrict
|
||||||
|
$.extend(this, this.constructor.default_data, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear properties to {@link Models.ModelObject.default_data|default_data}.
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.from({});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display stored data in container.
|
||||||
|
* @param {jQuery} $container
|
||||||
|
* @param {Object} [options] Options for formatter render method.
|
||||||
|
* @param {Formatters.Formatter}
|
||||||
|
* [formatter={@link Models.ModelObject#formatter|this.formatter()}]
|
||||||
|
* Formatter class to use.
|
||||||
|
*/
|
||||||
|
display($container, options, formatter) {
|
||||||
|
formatter = formatter || this.formatter();
|
||||||
|
formatter.render(this, $container, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a model whose instances can be obtained through API.
|
||||||
|
* @extends Models.ModelObject
|
||||||
|
* @memberof Models
|
||||||
|
*/
|
||||||
|
class APIModelObject extends ModelObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request url to get array of model instances data.
|
||||||
|
* @abstract
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static get url_model() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request url to get a single model instance data.
|
||||||
|
* @abstract
|
||||||
|
* @param {*} api_pk - Identifier of a model instance in api requests.
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
static url_object_for(api_pk) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link Models.ModelObject|new ModelObject(data)}.
|
||||||
|
* @param {Object} [data={}] - data to store in instance
|
||||||
|
*/
|
||||||
|
constructor(data) {
|
||||||
|
super(data);
|
||||||
|
|
||||||
|
if (this.id === undefined)
|
||||||
|
this.id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier of the model instance in api requests.
|
||||||
|
* @default <tt>this.id</tt>
|
||||||
|
*/
|
||||||
|
get api_pk() { return this.id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request url used to get current instance data.
|
||||||
|
*/
|
||||||
|
get url_object() {
|
||||||
|
if (this._url_object === undefined)
|
||||||
|
return this.is_empty() ? '' : this.constructor.url_object_for(this.api_pk);
|
||||||
|
return this._url_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
set url_object(v) { this._url_object = v; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data of a distant model instance. It sends a GET HTTP request to
|
||||||
|
* {@link Models.APIModelObject#url_object}.
|
||||||
|
* @param {object} [api_options] Additional data appended to the request.
|
||||||
|
* @param {jQueryAjaxSuccess} [on_success] A function to be called if the request succeeds.
|
||||||
|
* @param {jQueryAjaxError} [on_error] A function to be called if the request fails.
|
||||||
|
*/
|
||||||
|
fromAPI(api_options, on_success, on_error) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
api_options = api_options || {};
|
||||||
|
on_success = on_success || $.noop;
|
||||||
|
on_error = on_error || $.noop;
|
||||||
|
|
||||||
|
api_options['format'] = 'json';
|
||||||
|
|
||||||
|
$.getJSON(this.url_object, api_options)
|
||||||
|
.done(function (json, textStatus, jqXHR) {
|
||||||
|
that.from(json)
|
||||||
|
on_success(json, textStatus, jqXHR);
|
||||||
|
})
|
||||||
|
.fail(on_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data of a distant model instance via its id.
|
||||||
|
* @param {*} api_pk
|
||||||
|
* @param {object} [api_options]
|
||||||
|
* @param {jQueryAjaxSuccess} [on_success]
|
||||||
|
* @param {jQueryAjaxError} [on_error]
|
||||||
|
* @see {@link Models.APIModelObject#fromAPI|fromAPI}
|
||||||
|
*/
|
||||||
|
get_by_apipk(api_pk, api_options, on_success, on_error) {
|
||||||
|
this.url_object = this.constructor.url_object_for(api_pk);
|
||||||
|
this.fromAPI(api_options, on_success, on_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add <tt>empty_props</tt> and <tt>empty_attrs</tt> options to
|
||||||
|
* <tt>render</tt> call in {@link Models.ModelObject#display} if instance
|
||||||
|
* is empty.
|
||||||
|
* @see {@link Models.ModelObject#display}
|
||||||
|
* @see {@link Models.APIModelObject#is_empty}
|
||||||
|
*/
|
||||||
|
display($container, options, formatter) {
|
||||||
|
if (this.is_empty()) {
|
||||||
|
options.empty_props = true;
|
||||||
|
options.empty_attrs = true;
|
||||||
|
}
|
||||||
|
return ModelObject.prototype.display.call(this, $container, options, formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account model. Can be accessed through API.
|
||||||
|
* @extends Models.APIModelObject
|
||||||
|
* @memberof Models
|
||||||
|
*/
|
||||||
|
class Account extends APIModelObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties retrieved through API.
|
||||||
|
* @default <tt>['id', 'trigramme', 'name', 'nickname', 'email', 'is_cof',
|
||||||
|
* 'promo', 'balance', 'is_frozen', 'departement']</tt>
|
||||||
|
* @see {@link Models.ModelObject.props|ModelObject.props}
|
||||||
|
*/
|
||||||
|
static get props() {
|
||||||
|
return ['id', 'trigramme', 'name', 'nickname', 'email', 'is_cof',
|
||||||
|
'promo', 'balance', 'is_frozen', 'departement'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default values for Account model instances.
|
||||||
|
* @default <tt>{ 'id': 0, 'trigramme': '', 'name': '', 'nickname': '',
|
||||||
|
* 'email': '', ''is_cof': false, 'promo': '', 'balance': 0,
|
||||||
|
* 'is_frozen': false, 'departement': '' }</tt>
|
||||||
|
* @see {@link Models.ModelObject.default_data|ModelObject.default_data}
|
||||||
|
*/
|
||||||
|
static get default_data() {
|
||||||
|
return {
|
||||||
|
'id': 0, 'trigramme': '', 'name': '', 'nickname': '', 'email': '',
|
||||||
|
'is_cof' : false, 'promo': '', 'balance': 0, 'is_frozen': false,
|
||||||
|
'departement': '',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default <tt>django-js-reverse('kfet.account')</tt>
|
||||||
|
* @see {@link Models.APIModelObject.url_model|APIModelObject.url_model}
|
||||||
|
*/
|
||||||
|
static get url_model() { return Urls['kfet.account'](); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default <tt>django-js-reverse('kfet.account.read')(trigramme)</tt>
|
||||||
|
* @param {string} trigramme
|
||||||
|
* @see {@link Models.APIModelObject.url_object_for|APIModelObject.url_object_for}
|
||||||
|
*/
|
||||||
|
static url_object_for(trigramme) {
|
||||||
|
var trigramme_url = encodeURIComponent(trigramme);
|
||||||
|
return Urls['kfet.account.read'](trigramme_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default <tt>this.trigramme</tt>
|
||||||
|
*/
|
||||||
|
get api_pk() { return this.trigramme; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default {@link Formatters.AccountFormatter}
|
||||||
|
*/
|
||||||
|
formatter() {
|
||||||
|
return (this.trigramme == 'LIQ') ? LIQFormatter : AccountFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of "balance" type
|
||||||
|
// API currently returns a string object (serialization of Decimal type within Django)
|
||||||
|
get balance() { return this._balance; }
|
||||||
|
set balance(v) { return this._balance = floatCheck(v); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance converted to UKF according to cof status.
|
||||||
|
*/
|
||||||
|
get balance_ukf() { return amountToUKF(this.balance, this.is_cof); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkout model. Can be accessed through API.
|
||||||
|
* @extends Models.APIModelObject
|
||||||
|
* @memberof Models
|
||||||
|
*/
|
||||||
|
class Checkout extends APIModelObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties retrieved through API.
|
||||||
|
* @default <tt>['id', 'name', 'balance', 'valid_from', 'valid_to']</tt>
|
||||||
|
* @see {@link Models.ModelObject.props|ModelObject.props}
|
||||||
|
*/
|
||||||
|
static get props() {
|
||||||
|
return ['id', 'name', 'balance', 'valid_from', 'valid_to'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default values for Account model instances.
|
||||||
|
* @default <tt>{ 'id': 0, 'name': '', 'balance': 0, 'valid_from': '',
|
||||||
|
* 'valid_to': '' }</tt>
|
||||||
|
* @see {@link Models.ModelObject.default_data|ModelObject.default_data}
|
||||||
|
*/
|
||||||
|
static get default_data() {
|
||||||
|
return {
|
||||||
|
'id': 0, 'name': '', 'balance': 0, 'valid_from': '', 'valid_to': '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not supported by API yet.
|
||||||
|
* @see {@link Models.APIModelObject.url_model|APIModelObject.url_model}
|
||||||
|
*/
|
||||||
|
static get url_model() { return ''; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default <tt>django-js-reverse('kfet.kpsul.checkout_data.read')(pk)</tt>
|
||||||
|
* @param {string} api_pk - a checkout id
|
||||||
|
* @see {@link Models.APIModelObject.url_object_for|APIModelObject.url_object_for}
|
||||||
|
*/
|
||||||
|
static url_object_for(api_pk) {
|
||||||
|
return Urls['kfet.kpsul.checkout_data.read'](api_pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default {@link Formatters.CheckoutFormatter}
|
||||||
|
*/
|
||||||
|
formatter() {
|
||||||
|
return CheckoutFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of "balance" type
|
||||||
|
// API currently returns a string object (serialization of Decimal type within Django)
|
||||||
|
get balance() { return this._balance; }
|
||||||
|
set balance(v) { this._balance = floatCheck(v); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statement model. Cannot be accessed through API.
|
||||||
|
* @extends Models.ModelObject
|
||||||
|
* @memberof Models
|
||||||
|
*/
|
||||||
|
class Statement extends ModelObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties associated to a statement.
|
||||||
|
* @default <tt>['id', 'at', 'balance_old', 'balance_new', 'by']</tt>
|
||||||
|
* @see {@link Models.ModelObject.props|ModelObject.props}
|
||||||
|
*/
|
||||||
|
static get props() {
|
||||||
|
return ['id', 'at', 'balance_old', 'balance_new', 'by'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default values for Statement model instances.
|
||||||
|
* @default <tt>{ 'id': 0, 'at': '', 'balance_old': 0, 'balance_new': 0,
|
||||||
|
* 'by': '' }</tt>
|
||||||
|
* @see {@link Models.ModelObject.default_data|ModelObject.default_data}
|
||||||
|
*/
|
||||||
|
static get default_data() {
|
||||||
|
return {
|
||||||
|
'id': 0, 'at': '', 'balance_old': 0, 'balance_new': 0, 'by': '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default {@link Formatters.StatementFormatter}
|
||||||
|
*/
|
||||||
|
formatter() {
|
||||||
|
return StatementFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of "balance" type
|
||||||
|
// API currently returns a string object (serialization of Decimal type within Django)
|
||||||
|
get balance_old() { return this._balance_old; }
|
||||||
|
set balance_old(v) { this._balance_old = floatCheck(v); }
|
||||||
|
|
||||||
|
get balance_new() { return this._balance_new; }
|
||||||
|
set balance_new(v) { this._balance_new = floatCheck(v); }
|
||||||
|
|
||||||
|
get at() { return this._at; }
|
||||||
|
set at(v) { this._at = moment.isMoment(v) ? v : moment.tz(v, 'UTC'); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- ---------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual namespace for formatters.
|
||||||
|
* <br><br>
|
||||||
|
*
|
||||||
|
* A formatter subclasses {@link Formatters.Formatter}.<br>
|
||||||
|
* Formatters should be accessed statically only.
|
||||||
|
*
|
||||||
|
* @namespace Formatters
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof Formatters
|
||||||
|
*/
|
||||||
|
class Formatter {
|
||||||
|
|
||||||
|
static get props() { return []; }
|
||||||
|
static get attrs() { return []; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value of a property through current formatter if defined, object itself otherwise.
|
||||||
|
*
|
||||||
|
* @param {*} object - Object used for formatting
|
||||||
|
* @param {string} name - Name of property to render
|
||||||
|
* @param {string} [prefix=''] - Prefix used for formatter method search
|
||||||
|
* @return {*} Formatted property value
|
||||||
|
*/
|
||||||
|
static get(object, name, prefix) {
|
||||||
|
prefix = prefix || '';
|
||||||
|
var method = prefix + name;
|
||||||
|
if (this[method] !== undefined)
|
||||||
|
return this[method](object);
|
||||||
|
return object[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted value of a property using prefix 'prop_'.
|
||||||
|
* @see Formatter.get
|
||||||
|
*/
|
||||||
|
static get_prop(object, name) {
|
||||||
|
return this.get(object, name, 'prop_');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted value of a property using prefix 'attr_'.
|
||||||
|
* @see Formatter.get
|
||||||
|
*/
|
||||||
|
static get_attr(obj, attr) {
|
||||||
|
return this.get(obj, attr, 'attr_');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render container.
|
||||||
|
* @param {*} object - Object used for formatting
|
||||||
|
* @param {jQueryNode} $container
|
||||||
|
* @param {object} [options]
|
||||||
|
* <tt>options['props']</tt> should be an array of
|
||||||
|
* properties (default: <tt>class.props</tt>).<br>
|
||||||
|
* <tt>options['prefix_prop']</tt> is added in front of
|
||||||
|
* properties names in selector (default: <tt>'.'</tt>).<br>
|
||||||
|
* <tt>options['prefix_attr']</tt> is added in front of
|
||||||
|
* attributes names for container (default: <tt>''</tt>).
|
||||||
|
*/
|
||||||
|
static render(object, $container, options) {
|
||||||
|
options.props = options.props || [];
|
||||||
|
options.attrs = options.attrs || [];
|
||||||
|
|
||||||
|
var props = options.override_props ? options.props : this.props.concat(options.props);
|
||||||
|
var attrs = options.override_attrs ? options.attrs : this.attrs.concat(options.attrs);
|
||||||
|
|
||||||
|
var prefix_prop = options.prefix_prop !== undefined ? options.prefix_prop : '.';
|
||||||
|
var prefix_attr = options.prefix_attr !== undefined ? options.prefix_attr : '';
|
||||||
|
|
||||||
|
for (var i in props) {
|
||||||
|
var selector = prefix_prop + props[i];
|
||||||
|
var html = options.empty_props ? '' : this.get_prop(object, props[i]);
|
||||||
|
$container.find( selector ).html( html );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i in attrs) {
|
||||||
|
var name = prefix_attr + attrs[i];
|
||||||
|
var value = options.empty_attrs ? '' : this.get_attr(object, attrs[i]);
|
||||||
|
$container.attr( name, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof Formatters
|
||||||
|
* @extends Formatters.Formatter
|
||||||
|
*/
|
||||||
|
class AccountFormatter extends Formatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties renderable in html.
|
||||||
|
* @default {@link Models.Account.props}<tt> + ['balance_ukf']</tt>
|
||||||
|
*/
|
||||||
|
static get props() {
|
||||||
|
return Account.props.concat(['balance_ukf']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Added attributes to $container element.
|
||||||
|
* @default <tt>['data-balance']</tt>
|
||||||
|
*/
|
||||||
|
static get attrs() {
|
||||||
|
return ['data_balance'];
|
||||||
|
}
|
||||||
|
|
||||||
|
static get _data_balance() {
|
||||||
|
return {
|
||||||
|
'default': '', 'frozen': 'frozen',
|
||||||
|
'ok': 'ok', 'low': 'low', 'neg': 'neg',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <tt>a.balance</tt> with two decimals.
|
||||||
|
*/
|
||||||
|
static prop_balance(a) {
|
||||||
|
return a.balance.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <tt>'COF' if account is cof or 'Non-COF'
|
||||||
|
*/
|
||||||
|
static prop_is_cof(a) {
|
||||||
|
return a.is_cof ? 'COF' : 'Non-COF';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of data_attribute according to is_frozen status and balance of
|
||||||
|
* account <tt>a</tt>.
|
||||||
|
*/
|
||||||
|
static attr_data_balance(a) {
|
||||||
|
if (a.is_frozen) { return this._data_balance.frozen; }
|
||||||
|
else if (a.balance >= 5) { return this._data_balance.ok; }
|
||||||
|
else if (a.balance >= 0) { return this._data_balance.low; }
|
||||||
|
else /* a.balance < 0 */ { return this._data_balance.neg; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof Formatters
|
||||||
|
* @extends Formatters.AccountFormatter
|
||||||
|
*/
|
||||||
|
class LIQFormatter extends AccountFormatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering a property always returns the empty string
|
||||||
|
* @default <tt>''</tt>
|
||||||
|
*/
|
||||||
|
static get_prop() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute <tt>data_balance</tt> is always <tt>ok</tt>.
|
||||||
|
*/
|
||||||
|
static attr_data_balance(a) { return this._data_balance.ok; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof Formatters
|
||||||
|
* @extends Formatters.Formatter
|
||||||
|
*/
|
||||||
|
class CheckoutFormatter extends Formatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties renderable to html.
|
||||||
|
* @default {@link Models.Checkout.props}
|
||||||
|
*/
|
||||||
|
static get props() {
|
||||||
|
return Checkout.props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <tt>c.balance</tt> with two decimals.
|
||||||
|
*/
|
||||||
|
static prop_balance(c) {
|
||||||
|
return c.balance.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof Formatters
|
||||||
|
* @extends Formatters.Formatter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class StatementFormatter extends Formatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties renderable to html.
|
||||||
|
* @default {@link Models.Statement.props}
|
||||||
|
*/
|
||||||
|
static get props() {
|
||||||
|
return Statement.props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <tt>s.balance_old</tt> with two decimals.
|
||||||
|
*/
|
||||||
|
static prop_balance_old(s) {
|
||||||
|
return s.balance_old.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <tt>s.balance_new</tt> with two decimals.
|
||||||
|
*/
|
||||||
|
static prop_balance_new(s) {
|
||||||
|
return s.balance_new.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <tt>s.at</tt> formatted as <tt>DD/MM/YY à HH:MM</tt>.
|
||||||
|
*/
|
||||||
|
static prop_at(s) {
|
||||||
|
return moment.isMoment(s.at) ? s.at.tz('Europe/Paris').format('DD/MM/YY à HH:mm') : s.at;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,3 +1,30 @@
|
||||||
|
/**
|
||||||
|
* @file Miscellaneous JS definitions for <tt>k-fet</tt> app.
|
||||||
|
* @copyright 2017 cof-geek
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String method
|
||||||
|
* @memberof String
|
||||||
|
* @return {String} String formatted as trigramme
|
||||||
|
*/
|
||||||
|
String.prototype.formatTri = function() {
|
||||||
|
return this.toUpperCase().substr(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String method
|
||||||
|
* @global
|
||||||
|
* @return {Boolean} true iff String follows trigramme pattern
|
||||||
|
*/
|
||||||
|
String.prototype.isValidTri = function() {
|
||||||
|
var pattern = /^[^a-z]{3}$/;
|
||||||
|
return pattern.test(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$(window).scroll(function() {
|
$(window).scroll(function() {
|
||||||
if ($(window).width() >= 768 && $(this).scrollTop() > 72.6) {
|
if ($(window).width() >= 768 && $(this).scrollTop() > 72.6) {
|
||||||
|
@ -38,7 +65,7 @@ function amountDisplay(amount, is_cof=false, tri='') {
|
||||||
}
|
}
|
||||||
|
|
||||||
function amountToUKF(amount, is_cof=false) {
|
function amountToUKF(amount, is_cof=false) {
|
||||||
var coef_cof = is_cof ? 1 + Config.getAll().subvention_cof / 100 : 1;
|
var coef_cof = is_cof ? 1 + Config.get('subvention_cof') / 100 : 1;
|
||||||
return Math.round(amount * coef_cof * 10);
|
return Math.round(amount * coef_cof * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
369
kfet/static/kfet/js/kpsul.js
Normal file
369
kfet/static/kfet/js/kpsul.js
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
class KPsulManager {
|
||||||
|
|
||||||
|
constructor(env) {
|
||||||
|
this._env = env;
|
||||||
|
this.account_manager = new AccountManager(this);
|
||||||
|
this.checkout_manager = new CheckoutManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(soft) {
|
||||||
|
soft = soft || false;
|
||||||
|
|
||||||
|
this.account_manager.reset();
|
||||||
|
|
||||||
|
if (!soft) {
|
||||||
|
this.checkout_manager.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AccountManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._$container = $('#account');
|
||||||
|
|
||||||
|
this.account = new Account();
|
||||||
|
this.selection = new AccountSelection(this);
|
||||||
|
this.search = new AccountSearch(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_empty() { return this.account.is_empty(); }
|
||||||
|
|
||||||
|
display() {
|
||||||
|
this._display_data();
|
||||||
|
this._display_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
_display_data() {
|
||||||
|
this.account.display(this._$container, {
|
||||||
|
'prefix_prop': '#account-',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_display_buttons() {
|
||||||
|
// dirty
|
||||||
|
|
||||||
|
var buttons = '';
|
||||||
|
|
||||||
|
if (this.is_empty()) {
|
||||||
|
var trigramme = this.selection.get();
|
||||||
|
if (trigramme.isValidTri()) {
|
||||||
|
var url_base = "{% url 'kfet.account.create' %}";
|
||||||
|
var url = url_base + '?trigramme=' + encodeURIComponent(trigramme);
|
||||||
|
buttons += '<a href="'+url+'" class="btn btn-primary" target="_blank" title="Créer ce compte"><span class="glyphicon glyphicon-plus"></span></a>';
|
||||||
|
} else { /* trigramme input is empty or invalid */
|
||||||
|
buttons += '<button class="btn btn-primary search" title="Rechercher"><span class="glyphicon glyphicon-search"></span></button>';
|
||||||
|
}
|
||||||
|
} else { /* an account is loaded */
|
||||||
|
var url = Urls['kfet.account.update'](encodeURIComponent(this.account.trigramme));
|
||||||
|
buttons += '<a href="'+url+'" class="btn btn-primary" target="_blank" title="Modifier ce compte"><span class="glyphicon glyphicon-cog"></span></a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
this._$container.find('.buttons').html(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(trigramme) {
|
||||||
|
if (trigramme !== undefined)
|
||||||
|
this.selection.set(trigramme);
|
||||||
|
|
||||||
|
trigramme = trigramme || this.selection.get();
|
||||||
|
|
||||||
|
if (trigramme.isValidTri()) {
|
||||||
|
this.account.get_by_apipk(trigramme, {},
|
||||||
|
() => this._update_on_success(),
|
||||||
|
() => this.reset_data()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.reset_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_update_on_success() {
|
||||||
|
$('#id_on_acc').val(this.account.id);
|
||||||
|
this.display();
|
||||||
|
|
||||||
|
kpsul._env.articleSelect.focus();
|
||||||
|
kpsul._env.updateBasketAmount();
|
||||||
|
kpsul._env.updateBasketRel();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
$('#id_on_acc').val(0);
|
||||||
|
this.selection.reset();
|
||||||
|
this.search.reset();
|
||||||
|
this.reset_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_data() {
|
||||||
|
this.account.clear();
|
||||||
|
this.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.selection.focus();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AccountSelection {
|
||||||
|
|
||||||
|
constructor(manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
this._$input = $('#id_trigramme');
|
||||||
|
|
||||||
|
this._init_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_events() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
// user change trigramme
|
||||||
|
this._$input
|
||||||
|
.on('input', () => this.manager.update());
|
||||||
|
|
||||||
|
// LIQ shortcuts
|
||||||
|
this._$input
|
||||||
|
.on('keydown', function(e) {
|
||||||
|
// keys: 13:Enter|40:Arrow-Down
|
||||||
|
if (e.keyCode == 13 || e.keyCode == 40)
|
||||||
|
that.manager.update('LIQ');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this._$input.val().formatTri();
|
||||||
|
}
|
||||||
|
|
||||||
|
set(v) {
|
||||||
|
this._$input.val(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this._$input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.set('');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AccountSearch {
|
||||||
|
|
||||||
|
constructor(manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
|
||||||
|
this._content = '<input type="text" name="q" id="search_autocomplete" autocomplete="off" spellcheck="false" autofocus><div id="account_results"></div>' ;
|
||||||
|
this._input = '#search_autocomplete';
|
||||||
|
this._results_container = '#account_results';
|
||||||
|
|
||||||
|
this._init_outer_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
var that = this;
|
||||||
|
this._$dialog = $.dialog({
|
||||||
|
title: 'Recherche de compte',
|
||||||
|
content: this._content,
|
||||||
|
backgroundDismiss: true,
|
||||||
|
animation: 'top',
|
||||||
|
closeAnimation: 'bottom',
|
||||||
|
keyboardEnabled: true,
|
||||||
|
onOpen: function() {
|
||||||
|
that._$input = $(that._input);
|
||||||
|
that._$results_container = $(that._results_container);
|
||||||
|
that._init_form()
|
||||||
|
._init_inner_events();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_form() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this._$input.yourlabsAutocomplete({
|
||||||
|
url: '{% url "kfet.account.search.autocomplete" %}',
|
||||||
|
minimumCharacters: 2,
|
||||||
|
id: 'search_autocomplete',
|
||||||
|
choiceSelector: '.choice',
|
||||||
|
placeholder: "Chercher un utilisateur K-Fêt",
|
||||||
|
container: that._$results_container,
|
||||||
|
box: that._$results_container,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_outer_events() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
/* open search on button click */
|
||||||
|
this.manager._$container
|
||||||
|
.on('click', '.search', () => this.open());
|
||||||
|
|
||||||
|
/* open search on Ctrl-F */
|
||||||
|
this.manager._$container
|
||||||
|
.on('keydown', function(e) {
|
||||||
|
if (e.which == 70 && e.ctrlKey) {
|
||||||
|
that.open();
|
||||||
|
e.preventDefault() ;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_inner_events() {
|
||||||
|
this._$input.bind('selectChoice',
|
||||||
|
(e, choice, autocomplete) => this._on_select(e, choice, autocomplete));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_on_select(e, choice, autocomplete) {
|
||||||
|
this.manager.update(choice.find('.trigramme').text());
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
if (this._$dialog !== undefined) {
|
||||||
|
this._$dialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CheckoutManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._$container = $('#checkout');
|
||||||
|
this.display_prefix = '#checkout-';
|
||||||
|
|
||||||
|
this.checkout = new Checkout();
|
||||||
|
this.selection = new CheckoutSelection(this);
|
||||||
|
|
||||||
|
this._$laststatement_container = $('#last_statement');
|
||||||
|
this.laststatement = new Statement();
|
||||||
|
this.laststatement_display_prefix = '#checkout-last_statement_';
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id) {
|
||||||
|
if (id !== undefined)
|
||||||
|
this.selection.set(id);
|
||||||
|
|
||||||
|
id = id || this.selection.get();
|
||||||
|
|
||||||
|
var api_options = {
|
||||||
|
'last_statement': true,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.checkout.get_by_apipk(id, api_options,
|
||||||
|
(data) => this._update_on_success(data),
|
||||||
|
() => this.reset_data());
|
||||||
|
|
||||||
|
if (kpsul.account_manager.is_empty()) {
|
||||||
|
kpsul.account_manager.focus();
|
||||||
|
} else {
|
||||||
|
kpsul._env.articleSelect.focus().select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_update_on_success(data) {
|
||||||
|
if (data['laststatement'] !== undefined)
|
||||||
|
this.laststatement.from(data['laststatement']);
|
||||||
|
|
||||||
|
$('#id_checkout').val(this.checkout.id);
|
||||||
|
this.display();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
is_empty() { return this.checkout.is_empty(); }
|
||||||
|
|
||||||
|
display() {
|
||||||
|
this._display_data();
|
||||||
|
this._display_laststatement();
|
||||||
|
this._display_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
_display_data() {
|
||||||
|
this.checkout.display(this._$container, {
|
||||||
|
'prefix_prop': this.display_prefix,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_display_laststatement() {
|
||||||
|
if (this.laststatement.is_empty()) {
|
||||||
|
this._$laststatement_container.hide();
|
||||||
|
} else {
|
||||||
|
this.laststatement.display(this._$laststatement_container, {
|
||||||
|
'prefix_prop': this.laststatement_display_prefix
|
||||||
|
});
|
||||||
|
this._$laststatement_container.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_display_buttons() {
|
||||||
|
var buttons = '';
|
||||||
|
if (!this.is_empty()) {
|
||||||
|
var id = this.checkout.id;
|
||||||
|
var url_details = Urls['kfet.checkout.read'](id);
|
||||||
|
var url_newcheckout = Urls['kfet.checkoutstatement.create'](id);
|
||||||
|
buttons += '<a href="' + url_newcheckout + '" title="Effectuer un relevé" class="btn btn-primary" target="_blank"><span class="glyphicon glyphicon-euro"></span></a>';
|
||||||
|
buttons += '<a class="btn btn-primary" href="' + url_details + '" title="Modifier" target="_blank"><span class="glyphicon glyphicon-cog"></span></a>';
|
||||||
|
}
|
||||||
|
this._$container.find('.buttons').html(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
$('#id_checkout').val(0);
|
||||||
|
|
||||||
|
this.selection.reset();
|
||||||
|
this.reset_data();
|
||||||
|
|
||||||
|
if (this.selection.choices.length == 1)
|
||||||
|
this.update(this.selection.choices[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_data() {
|
||||||
|
this.checkout.clear();
|
||||||
|
this.laststatement.clear();
|
||||||
|
|
||||||
|
this.display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CheckoutSelection {
|
||||||
|
|
||||||
|
constructor(manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
this._$input = $('#id_checkout_select');
|
||||||
|
|
||||||
|
this._init_events();
|
||||||
|
|
||||||
|
this.choices = [
|
||||||
|
for (option of this._$input.find('option'))
|
||||||
|
if ($(option).attr('value') != '')
|
||||||
|
parseInt($(option).attr('value'))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_events() {
|
||||||
|
this._$input.on('change', () => this.manager.update());
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this._$input.val() || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(v) {
|
||||||
|
this._$input.find('option[value='+ v +']').prop('selected', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this._$input.find('option:first').prop('selected', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,8 +12,11 @@
|
||||||
<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.js' %}"></script>
|
<script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script>
|
||||||
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
|
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'kfet/js/kfet.api.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'kfet/js/kpsul.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block title %}K-Psul{% endblock %}
|
{% block title %}K-Psul{% endblock %}
|
||||||
|
@ -80,7 +83,7 @@
|
||||||
{{ trigramme_form.trigramme }}
|
{{ trigramme_form.trigramme }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-9 col-xs-8" id="account_data">
|
<div class="col-lg-9 col-xs-8" id="account_data">
|
||||||
<div id="account-balance" class="data_line"></div>
|
<div id="account-balance_ukf" class="data_line"></div>
|
||||||
<div id="account-name" class="data_line"></div>
|
<div id="account-name" class="data_line"></div>
|
||||||
<div id="account-nickname" class="data_line"></div>
|
<div id="account-nickname" class="data_line"></div>
|
||||||
<div class="data_line">
|
<div class="data_line">
|
||||||
|
@ -102,7 +105,9 @@
|
||||||
<div>
|
<div>
|
||||||
<b>En caisse:</b> <span id="checkout-balance"></span> €
|
<b>En caisse:</b> <span id="checkout-balance"></span> €
|
||||||
</div>
|
</div>
|
||||||
<div id="last_statement">
|
<div id="last_statement" style="display:none">
|
||||||
|
<b>Dernier relevé:</b><br>
|
||||||
|
<span id="checkout-last_statement_balance_new"></span>€ le <span id="checkout-last_statement_at"></span> par <span id="checkout-last_statement_by"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
</div>
|
</div>
|
||||||
|
@ -184,413 +189,6 @@ function booleanCheck(v) {
|
||||||
return v == true;
|
return v == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function functionCheck(v) {
|
|
||||||
if (typeof v === 'function')
|
|
||||||
return v;
|
|
||||||
return function(){};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Config {
|
|
||||||
|
|
||||||
static getAll() {
|
|
||||||
if (typeof window.config === 'undefined')
|
|
||||||
window.config = {};
|
|
||||||
return window.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
static reset(callback) {
|
|
||||||
$.ajax({
|
|
||||||
dataType: "json",
|
|
||||||
url : "{% url 'kfet.kpsul.get_settings' %}",
|
|
||||||
method : "POST",
|
|
||||||
})
|
|
||||||
.done(function(data) {
|
|
||||||
Config.addcost_for = data['addcost_for'];
|
|
||||||
Config.addcost_amount = data['addcost_amount'];
|
|
||||||
Config.subvention_cof = data['subvention_cof'];
|
|
||||||
})
|
|
||||||
.always(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static set addcost_for(v) {
|
|
||||||
Config.getAll().addcost_for = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static set addcost_amount(v) {
|
|
||||||
Config.getAll().addcost_amount = floatCheck(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static set subvention_cof(v) {
|
|
||||||
Config.getAll().subvention_cof = floatCheck(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
String.prototype.formatTri = function() {
|
|
||||||
return this.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
String.prototype.isValidTri = function() {
|
|
||||||
var pattern = /^[^a-z]{3}$/;
|
|
||||||
return pattern.test(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
String.prototype.urlify = function() {
|
|
||||||
return encodeURIComponent(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Account {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
$.extend(this, this.constructor.default_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get default_data() {
|
|
||||||
return {
|
|
||||||
'id': 0, 'trigramme': '', 'name': '', 'nickname': '', 'email': '',
|
|
||||||
'is_cof' : false, 'promo': '', 'balance': 0, 'is_frozen': false,
|
|
||||||
'departement': '',
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
get id() { return this._id; }
|
|
||||||
get balance() { return this._balance; }
|
|
||||||
get balance_ukf() { return amountToUKF(this.balance, this.is_cof); }
|
|
||||||
get is_cof() { return this._is_cof; }
|
|
||||||
get is_frozen() { return this._is_frozen; }
|
|
||||||
|
|
||||||
set id(v) { this._id = intCheck(v); }
|
|
||||||
set balance(v) { this._balance = floatCheck(v); }
|
|
||||||
set is_cof(v) { this._is_cof = booleanCheck(v); }
|
|
||||||
set is_frozen(v) { this._is_frozen = booleanCheck(v); }
|
|
||||||
|
|
||||||
from(data, callback) {
|
|
||||||
callback = functionCheck(callback);
|
|
||||||
$.extend(this, Account.default_data, data);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
fromAPI(trigramme, on_success, on_error) {
|
|
||||||
on_error = functionCheck(on_error);
|
|
||||||
var that = this;
|
|
||||||
$.ajax({
|
|
||||||
dataType: "json",
|
|
||||||
url : "{% url 'kfet.account.read.json' %}",
|
|
||||||
method : "POST",
|
|
||||||
data : { trigramme: trigramme },
|
|
||||||
})
|
|
||||||
.done((data) => this.from(data, on_success))
|
|
||||||
.fail(on_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.from({});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AccountFormatter {
|
|
||||||
|
|
||||||
constructor(account) {
|
|
||||||
this._account = account;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get _data_balance_default() { return ''; }
|
|
||||||
static get _data_balance_frozen() { return 'frozen'; }
|
|
||||||
static get _data_balance_ok() { return 'ok'; }
|
|
||||||
static get _data_balance_low() { return 'low'; }
|
|
||||||
static get _data_balance_neg() { return 'neg'; }
|
|
||||||
|
|
||||||
get str_balance_ukf() { return ''; }
|
|
||||||
get str_is_cof() { return ''; }
|
|
||||||
get data_balance() { return this.constructor._data_balance_default; }
|
|
||||||
|
|
||||||
display($container) {
|
|
||||||
var a = this._account;
|
|
||||||
$container
|
|
||||||
.find('#account-balance')
|
|
||||||
.text(this.str_balance_ukf)
|
|
||||||
.end()
|
|
||||||
.find('#account-is_cof')
|
|
||||||
.text(this.str_is_cof)
|
|
||||||
.end()
|
|
||||||
.find('#account-name')
|
|
||||||
.text(a.name)
|
|
||||||
.end()
|
|
||||||
.find('#account-nickname')
|
|
||||||
.text(a.nickname)
|
|
||||||
.end()
|
|
||||||
.find('#account-departement')
|
|
||||||
.text(a.departement)
|
|
||||||
.end()
|
|
||||||
.find('#account-promo')
|
|
||||||
.text(a.promo)
|
|
||||||
.end()
|
|
||||||
.find('#account-email')
|
|
||||||
.text(a.email)
|
|
||||||
.end();
|
|
||||||
$container.attr('data-balance', this.data_balance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class StandardAccountFormatter extends AccountFormatter {
|
|
||||||
|
|
||||||
get str_balance_ukf() {
|
|
||||||
return this._account.balance_ukf;
|
|
||||||
}
|
|
||||||
|
|
||||||
get str_is_cof() {
|
|
||||||
return this._account.is_cof ? 'COF' : 'Non-COF';
|
|
||||||
}
|
|
||||||
|
|
||||||
get data_balance() {
|
|
||||||
var is_frozen = this._account.is_frozen;
|
|
||||||
var balance = this._account.balance;
|
|
||||||
if (is_frozen) { return this.constructor._data_balance_frozen; }
|
|
||||||
else if (balance >= 5) { return this.constructor._data_balance_ok; }
|
|
||||||
else if (balance >= 0) { return this.constructor._data_balance_low; }
|
|
||||||
else /* balance < 0 */ { return this.constructor._data_balance_neg; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LIQAccountFormatter extends AccountFormatter {
|
|
||||||
|
|
||||||
get str_balance_ukf() { return ''; }
|
|
||||||
get str_is_cof() { return ''; }
|
|
||||||
get data_balance() { return this.constructor.data_balance_ok; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AccountManager {
|
|
||||||
|
|
||||||
constructor(env) {
|
|
||||||
this._env = env; // temporary, should be a link to "kpsul_manager" or something like this
|
|
||||||
|
|
||||||
this._$container = $('#account');
|
|
||||||
|
|
||||||
this.account = new Account();
|
|
||||||
this.selection = new AccountSelection(this);
|
|
||||||
this.search = new AccountSearch(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
get is_empty() {
|
|
||||||
return this.account.id == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
get formatter() {
|
|
||||||
if (this.account.trigramme == 'LIQ') { return LIQAccountFormatter; }
|
|
||||||
else if (!this.is_empty) { return StandardAccountFormatter; }
|
|
||||||
else /* account is empty */ { return AccountFormatter; }
|
|
||||||
}
|
|
||||||
|
|
||||||
display() {
|
|
||||||
this._display_data();
|
|
||||||
this._display_buttons();
|
|
||||||
}
|
|
||||||
|
|
||||||
_display_data() {
|
|
||||||
(new (this.formatter)(this.account)).display(this._$container);
|
|
||||||
}
|
|
||||||
|
|
||||||
_display_buttons() {
|
|
||||||
// dirty
|
|
||||||
|
|
||||||
var buttons = '';
|
|
||||||
|
|
||||||
if (this.is_empty) {
|
|
||||||
var trigramme = this.selection.get();
|
|
||||||
if (trigramme.isValidTri()) {
|
|
||||||
var url_base = "{% url 'kfet.account.create' %}";
|
|
||||||
var url = url_base + '?trigramme=' + trigramme.urlify();
|
|
||||||
buttons += '<a href="'+url+'" class="btn btn-primary" target="_blank" title="Créer ce compte"><span class="glyphicon glyphicon-plus"></span></a>';
|
|
||||||
} else { /* trigramme input is empty or invalid */
|
|
||||||
buttons += '<button class="btn btn-primary search" title="Rechercher"><span class="glyphicon glyphicon-search"></span></button>';
|
|
||||||
}
|
|
||||||
} else { /* an account is loaded */
|
|
||||||
var url_pattern = "{% url 'kfet.account.read' 'LIQ' %}";
|
|
||||||
var url_base = url_pattern.substr(0, url_pattern.length - 3); /* dirty */
|
|
||||||
var url = url_base + this.account.trigramme.urlify();
|
|
||||||
buttons += '<a href="'+url+'" class="btn btn-primary" target="_blank" title="Modifier ce compte"><span class="glyphicon glyphicon-cog"></span></a>';
|
|
||||||
}
|
|
||||||
|
|
||||||
this._$container.find('.buttons').html(buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(trigramme) {
|
|
||||||
if (typeof trigramme !== 'undefined')
|
|
||||||
this.selection.set(trigramme);
|
|
||||||
|
|
||||||
trigramme = trigramme || this.selection.get();
|
|
||||||
|
|
||||||
if (trigramme.isValidTri()) {
|
|
||||||
this.account.fromAPI(trigramme,
|
|
||||||
() => this._update_on_success(),
|
|
||||||
() => this.reset_data()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.reset_data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_update_on_success() {
|
|
||||||
$('#id_on_acc').val(this.account.id);
|
|
||||||
this.display();
|
|
||||||
|
|
||||||
this._env.articleSelect.focus();
|
|
||||||
this._env.updateBasketAmount();
|
|
||||||
this._env.updateBasketRel();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
$('#id_on_acc').val(0);
|
|
||||||
this.selection.reset();
|
|
||||||
this.reset_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_data() {
|
|
||||||
this.account.reset();
|
|
||||||
this.display();
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this.selection.focus();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class AccountSearch {
|
|
||||||
|
|
||||||
constructor(manager) {
|
|
||||||
this.manager = manager;
|
|
||||||
|
|
||||||
this._content = '<input type="text" name="q" id="search_autocomplete" autocomplete="off" spellcheck="false" autofocus><div id="account_results"></div>' ;
|
|
||||||
this._input = '#search_autocomplete';
|
|
||||||
this._results_container = '#account_results';
|
|
||||||
|
|
||||||
this._init_outer_events();
|
|
||||||
}
|
|
||||||
|
|
||||||
open() {
|
|
||||||
console.log('plop');
|
|
||||||
var that = this;
|
|
||||||
this._$dialog = $.dialog({
|
|
||||||
title: 'Recherche de compte',
|
|
||||||
content: this._content,
|
|
||||||
backgroundDismiss: true,
|
|
||||||
animation: 'top',
|
|
||||||
closeAnimation: 'bottom',
|
|
||||||
keyboardEnabled: true,
|
|
||||||
onOpen: function() {
|
|
||||||
that._$input = $(that._input);
|
|
||||||
that._$results_container = $(that._results_container);
|
|
||||||
that
|
|
||||||
._init_form();
|
|
||||||
._init_inner_events();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_init_form() {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
this._$input.yourlabsAutocomplete({
|
|
||||||
url: '{% url "kfet.account.search.autocomplete" %}',
|
|
||||||
minimumCharacters: 2,
|
|
||||||
id: 'search_autocomplete',
|
|
||||||
choiceSelector: '.choice',
|
|
||||||
placeholder: "Chercher un utilisateur K-Fêt",
|
|
||||||
container: that._$results_container,
|
|
||||||
box: that._$results_container,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
_init_outer_events() {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
/* open search on button click */
|
|
||||||
this.manager._$container
|
|
||||||
.on('click', '.search', () => this.open());
|
|
||||||
|
|
||||||
/* open search on Ctrl-F */
|
|
||||||
this.manager._$container
|
|
||||||
.on('keydown', function(e) {
|
|
||||||
if (e.which == 70 && e.ctrlKey) {
|
|
||||||
that.open();
|
|
||||||
e.preventDefault() ;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_init_inner_events() {
|
|
||||||
this._$input
|
|
||||||
.bind('selectChoice', (e, choice, autocomplete) => this._on_select(e, choice, autocomplete));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
_on_select(e, choice, autocomplete) {
|
|
||||||
this.manager.update(choice.find('.trigramme').text());
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this._$dialog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AccountSelection {
|
|
||||||
|
|
||||||
constructor(manager) {
|
|
||||||
this.manager = manager;
|
|
||||||
this._$input = $('#id_trigramme');
|
|
||||||
|
|
||||||
this._init_events();
|
|
||||||
}
|
|
||||||
|
|
||||||
_init_events() {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
// user change trigramme
|
|
||||||
this._$input
|
|
||||||
.on('input', () => this.manager.update());
|
|
||||||
|
|
||||||
// LIQ shortcuts
|
|
||||||
this._$input
|
|
||||||
.on('keydown', function(e) {
|
|
||||||
// keys: 13:Enter|40:Arrow-Down
|
|
||||||
if (e.keyCode == 13 || e.keyCode == 40)
|
|
||||||
that.manager.update('LIQ');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get() {
|
|
||||||
return this._$input.val().formatTri();
|
|
||||||
}
|
|
||||||
|
|
||||||
set(v) {
|
|
||||||
this._$input.val(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this._$input.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.set('');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
@ -601,91 +199,6 @@ $(document).ready(function() {
|
||||||
// Lock to avoid multiple requests
|
// Lock to avoid multiple requests
|
||||||
var lock = 0;
|
var lock = 0;
|
||||||
|
|
||||||
// -----
|
|
||||||
// Checkout data management
|
|
||||||
// -----
|
|
||||||
|
|
||||||
// Initializing
|
|
||||||
var checkout_container = $('#checkout');
|
|
||||||
var checkoutInput = $('#id_checkout_select');
|
|
||||||
var checkout_data = {}
|
|
||||||
var checkout_data_default = {
|
|
||||||
'id' : 0,
|
|
||||||
'name': '',
|
|
||||||
'balance' : '',
|
|
||||||
'valid_from': '',
|
|
||||||
'valid_to' : '',
|
|
||||||
'last_statement_balance': '',
|
|
||||||
'last_statement_at' : '',
|
|
||||||
'last_statement_by_trigramme' : '',
|
|
||||||
'last_statement_by_first_name': '',
|
|
||||||
'last_statement_by_last_name' : '' ,
|
|
||||||
}
|
|
||||||
var last_statement_container = $('#last_statement');
|
|
||||||
var last_statement_html_default = '<b>Dernier relevé: </b><br><span id="checkout-last_statement_balance"></span>€ le <span id="checkout-last_statement_at"></span> par <span id="checkout-last_statement_by_trigramme"></span>';
|
|
||||||
|
|
||||||
|
|
||||||
// Display data
|
|
||||||
function displayCheckoutData() {
|
|
||||||
var at_formated = '';
|
|
||||||
if (checkout_data['last_statement_at']) {
|
|
||||||
last_statement_container.html(last_statement_html_default);
|
|
||||||
var at = moment.tz(checkout_data['last_statement_at'], 'UTC');
|
|
||||||
at_formated = at.tz('Europe/Paris').format('DD/MM/YY à HH:mm');
|
|
||||||
} else {
|
|
||||||
last_statement_container.html('');
|
|
||||||
}
|
|
||||||
for (var elem in checkout_data) {
|
|
||||||
$('#checkout-'+elem).text(checkout_data[elem]);
|
|
||||||
}
|
|
||||||
$('#checkout-last_statement_at').text(at_formated);
|
|
||||||
var buttons = '';
|
|
||||||
if (checkout_data['id'] !== 0) {
|
|
||||||
var url_base = "{% url 'kfet.checkoutstatement.create' 1 %}";
|
|
||||||
url_base = url_base.substr(0,url_base.length - 16);
|
|
||||||
buttons += '<a class="btn btn-primary" href="'+url_base+checkout_data['id']+'/statements/add" title="Effectuer un relevé" target="_blank"><span class="glyphicon glyphicon-euro"></span></a>';
|
|
||||||
buttons += '<a class="btn btn-primary" href="'+url_base+checkout_data['id']+'" title="Modifier" target="_blank"><span class="glyphicon glyphicon-cog"></span></a>';
|
|
||||||
}
|
|
||||||
checkout_container.find('.buttons').html(buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear data
|
|
||||||
function resetCheckout() {
|
|
||||||
checkout_data = checkout_data_default;
|
|
||||||
$('#id_checkout').val(0);
|
|
||||||
checkoutInput.find('option:first').prop('selected', true);
|
|
||||||
displayCheckoutData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store data
|
|
||||||
function storeCheckoutData(data) {
|
|
||||||
checkout_data = $.extend({}, checkout_data_default, data);
|
|
||||||
$('#id_checkout').val(checkout_data['id']);
|
|
||||||
displayCheckoutData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve data
|
|
||||||
function retrieveCheckoutData(id) {
|
|
||||||
$.ajax({
|
|
||||||
dataType: "json",
|
|
||||||
url : "{% url 'kfet.kpsul.checkout_data' %}",
|
|
||||||
method : "POST",
|
|
||||||
data : { 'pk': id },
|
|
||||||
})
|
|
||||||
.done(function(data) { storeCheckoutData(data) })
|
|
||||||
.fail(function() { resetCheckout() });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event listener
|
|
||||||
checkoutInput.on('change', function() {
|
|
||||||
retrieveCheckoutData(checkoutInput.val());
|
|
||||||
if (!account_manager.is_empty()) {
|
|
||||||
articleSelect.focus().select();
|
|
||||||
} else {
|
|
||||||
account_manager.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
// Auth
|
// Auth
|
||||||
// -----
|
// -----
|
||||||
|
@ -1047,13 +560,12 @@ $(document).ready(function() {
|
||||||
var arrowKeys = /^(37|38|39|40)$/;
|
var arrowKeys = /^(37|38|39|40)$/;
|
||||||
|
|
||||||
function amountEuroPurchase(article_data, nb) {
|
function amountEuroPurchase(article_data, nb) {
|
||||||
var cfg = Config.getAll();
|
|
||||||
var amount_euro = - article_data[3] * nb ;
|
var amount_euro = - article_data[3] * nb ;
|
||||||
if (cfg.addcost_for && cfg.addcost_amount && account_manager.account.trigramme != cfg.addcost_for)
|
if (Config.get('addcost_for') && Config.get('addcost_amount') && account_manager.account.trigramme != Config.get('addcost_for'))
|
||||||
amount_euro -= cfg.addcost_amount * nb;
|
amount_euro -= Config.get('addcost_amount') * nb;
|
||||||
var reduc_divisor = 1;
|
var reduc_divisor = 1;
|
||||||
if (account_manager.account.is_cof)
|
if (kpsul.account_manager.account.is_cof)
|
||||||
reduc_divisor = 1 + cfg.subvention_cof / 100;
|
reduc_divisor = 1 + Config.get('subvention_cof') / 100;
|
||||||
return amount_euro / reduc_divisor;
|
return amount_euro / reduc_divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,7 +582,7 @@ $(document).ready(function() {
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(nb).end()
|
.find('.number').text(nb).end()
|
||||||
.find('.name').text(article_data[0]).end()
|
.find('.name').text(article_data[0]).end()
|
||||||
.find('.amount').text(amountToUKF(amount_euro, 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);
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
@ -1084,7 +596,7 @@ $(document).ready(function() {
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(amount+"€").end()
|
.find('.number').text(amount+"€").end()
|
||||||
.find('.name').text(text).end()
|
.find('.name').text(text).end()
|
||||||
.find('.amount').text(amountToUKF(amount, account_manager.account.is_cof));
|
.find('.amount').text(amountToUKF(amount, kpsul.account_manager.account.is_cof));
|
||||||
basket_container.prepend(deposit_basket_html);
|
basket_container.prepend(deposit_basket_html);
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
@ -1097,7 +609,7 @@ $(document).ready(function() {
|
||||||
.attr('data-opeindex', index)
|
.attr('data-opeindex', index)
|
||||||
.find('.number').text(amount+"€").end()
|
.find('.number').text(amount+"€").end()
|
||||||
.find('.name').text('Retrait').end()
|
.find('.name').text('Retrait').end()
|
||||||
.find('.amount').text(amountToUKF(amount, account_manager.account.is_cof));
|
.find('.amount').text(amountToUKF(amount, kpsul.account_manager.account.is_cof));
|
||||||
basket_container.prepend(withdraw_basket_html);
|
basket_container.prepend(withdraw_basket_html);
|
||||||
updateBasketRel();
|
updateBasketRel();
|
||||||
}
|
}
|
||||||
|
@ -1139,7 +651,7 @@ $(document).ready(function() {
|
||||||
var amount = $(this).find('#id_form-'+opeindex+'-amount');
|
var amount = $(this).find('#id_form-'+opeindex+'-amount');
|
||||||
if (!deleted && type == "purchase")
|
if (!deleted && type == "purchase")
|
||||||
amount.val(amountEuroPurchase(article_id, article_nb));
|
amount.val(amountEuroPurchase(article_id, article_nb));
|
||||||
basket_container.find('[data-opeindex='+opeindex+'] .amount').text(amountToUKF(amount.val(), account_manager.account.is_cof));
|
basket_container.find('[data-opeindex='+opeindex+'] .amount').text(amountToUKF(amount.val(), kpsul.account_manager.account.is_cof));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1147,8 +659,9 @@ $(document).ready(function() {
|
||||||
|
|
||||||
function updateBasketRel() {
|
function updateBasketRel() {
|
||||||
var basketrel_html = '';
|
var basketrel_html = '';
|
||||||
var trigramme = account_manager.account.trigramme;
|
var account = kpsul.account_manager.account;
|
||||||
var is_cof = account_manager.account.is_cof;
|
var trigramme = account.trigramme;
|
||||||
|
var is_cof = account.is_cof;
|
||||||
if (trigramme == 'LIQ' && !isBasketEmpty()) {
|
if (trigramme == 'LIQ' && !isBasketEmpty()) {
|
||||||
var amount = - getAmountBasket();
|
var amount = - getAmountBasket();
|
||||||
basketrel_html += '<div>Total: '+amount.toFixed(2)+' €</div>';
|
basketrel_html += '<div>Total: '+amount.toFixed(2)+' €</div>';
|
||||||
|
@ -1161,7 +674,7 @@ $(document).ready(function() {
|
||||||
} else if (trigramme != '' && !isBasketEmpty()) {
|
} else if (trigramme != '' && !isBasketEmpty()) {
|
||||||
var amount = getAmountBasket();
|
var amount = getAmountBasket();
|
||||||
var amountUKF = amountToUKF(amount, is_cof);
|
var amountUKF = amountToUKF(amount, is_cof);
|
||||||
var newBalance = account_manager.account.balance + amount;
|
var newBalance = account.balance + amount;
|
||||||
var newBalanceUKF = amountToUKF(newBalance, is_cof);
|
var newBalanceUKF = amountToUKF(newBalance, is_cof);
|
||||||
basketrel_html += '<div>Total: '+amountUKF+'</div>';
|
basketrel_html += '<div>Total: '+amountUKF+'</div>';
|
||||||
basketrel_html += '<div>Nouveau solde: '+newBalanceUKF+'</div>';
|
basketrel_html += '<div>Nouveau solde: '+newBalanceUKF+'</div>';
|
||||||
|
@ -1340,7 +853,7 @@ $(document).ready(function() {
|
||||||
|
|
||||||
function updatePreviousOp() {
|
function updatePreviousOp() {
|
||||||
var previousop_html = '';
|
var previousop_html = '';
|
||||||
var trigramme = account_manager.account.trigramme;
|
var trigramme = kpsul.account_manager.account.trigramme;
|
||||||
previousop_html += '<div class="trigramme">Trigramme : '+trigramme+'</div>';
|
previousop_html += '<div class="trigramme">Trigramme : '+trigramme+'</div>';
|
||||||
previousop_html += basketrel_container.html();
|
previousop_html += basketrel_container.html();
|
||||||
previousop_container.html(previousop_html);
|
previousop_container.html(previousop_html);
|
||||||
|
@ -1357,14 +870,13 @@ $(document).ready(function() {
|
||||||
var addcost_default_html = '<div id="addcost">Majoration vers <span class="addcost_for"></span> de <span class="addcost_amount"></span>€</div>';
|
var addcost_default_html = '<div id="addcost">Majoration vers <span class="addcost_for"></span> de <span class="addcost_amount"></span>€</div>';
|
||||||
|
|
||||||
function displayAddcost() {
|
function displayAddcost() {
|
||||||
var cfg = Config.getAll();
|
$('#addcost').remove();
|
||||||
checkout_container.find('#addcost').remove();
|
|
||||||
$('body').css('animation', 'none');
|
$('body').css('animation', 'none');
|
||||||
if (cfg.addcost_for && cfg.addcost_amount) {
|
if (Config.get('addcost_for') && Config.get('addcost_amount')) {
|
||||||
var addcost_html = $(addcost_default_html);
|
var addcost_html = $(addcost_default_html);
|
||||||
addcost_html
|
addcost_html
|
||||||
.find('.addcost_for').text(cfg.addcost_for).end()
|
.find('.addcost_for').text(Config.get('addcost_for')).end()
|
||||||
.find('.addcost_amount').text(cfg.addcost_amount.toFixed(2)).end();
|
.find('.addcost_amount').text(Config.get('addcost_amount').toFixed(2)).end();
|
||||||
checkout_container.prepend(addcost_html);
|
checkout_container.prepend(addcost_html);
|
||||||
$('body').css('animation', 'colorchange 3s infinite');
|
$('body').css('animation', 'colorchange 3s infinite');
|
||||||
}
|
}
|
||||||
|
@ -1494,8 +1006,8 @@ $(document).ready(function() {
|
||||||
.text(article['stock']);
|
.text(article['stock']);
|
||||||
}
|
}
|
||||||
if (data['addcost']) {
|
if (data['addcost']) {
|
||||||
Config.addcost_for = data['addcost']['for'];
|
Config.set('addcost_for', data['addcost']['for']);
|
||||||
Config.addcost_amount = data['addcost']['amount'];
|
Config.set('addcost_amount', data['addcost']['amount']);
|
||||||
displayAddcost();
|
displayAddcost();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1507,17 +1019,17 @@ $(document).ready(function() {
|
||||||
// Reset functions
|
// Reset functions
|
||||||
|
|
||||||
function coolReset(give_tri_focus=true) {
|
function coolReset(give_tri_focus=true) {
|
||||||
account_manager.reset();
|
kpsul.account_manager.reset();
|
||||||
resetBasket();
|
resetBasket();
|
||||||
resetComment();
|
resetComment();
|
||||||
resetSelectable();
|
resetSelectable();
|
||||||
if (give_tri_focus)
|
if (give_tri_focus)
|
||||||
account_manager.focus();
|
kpsul.account_manager.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hardReset(give_tri_focus=true) {
|
function hardReset(give_tri_focus=true) {
|
||||||
coolReset(give_tri_focus);
|
coolReset(give_tri_focus);
|
||||||
checkoutInput.trigger('change');
|
kpsul.checkout_manager.reset();
|
||||||
resetArticles();
|
resetArticles();
|
||||||
resetPreviousOp();
|
resetPreviousOp();
|
||||||
khistory.reset();
|
khistory.reset();
|
||||||
|
@ -1605,7 +1117,7 @@ $(document).ready(function() {
|
||||||
updateBasketRel: updateBasketRel,
|
updateBasketRel: updateBasketRel,
|
||||||
};
|
};
|
||||||
|
|
||||||
var account_manager = new AccountManager(env);
|
window.kpsul = new KPsulManager(env);
|
||||||
|
|
||||||
hardReset();
|
hardReset();
|
||||||
|
|
||||||
|
|
|
@ -132,8 +132,8 @@ urlpatterns = [
|
||||||
# -----
|
# -----
|
||||||
|
|
||||||
url('^k-psul/$', views.kpsul, name='kfet.kpsul'),
|
url('^k-psul/$', views.kpsul, name='kfet.kpsul'),
|
||||||
url('^k-psul/checkout_data$', views.kpsul_checkout_data,
|
url(r'^k-psul/checkout_data/(?P<pk>\d+)$', views.kpsul_checkout_data,
|
||||||
name='kfet.kpsul.checkout_data'),
|
name='kfet.kpsul.checkout_data.read'),
|
||||||
url('^k-psul/perform_operations$', views.kpsul_perform_operations,
|
url('^k-psul/perform_operations$', views.kpsul_perform_operations,
|
||||||
name='kfet.kpsul.perform_operations'),
|
name='kfet.kpsul.perform_operations'),
|
||||||
url('^k-psul/cancel_operations$', views.kpsul_cancel_operations,
|
url('^k-psul/cancel_operations$', views.kpsul_cancel_operations,
|
||||||
|
@ -151,9 +151,6 @@ urlpatterns = [
|
||||||
|
|
||||||
url(r'^history.json$', views.history_json,
|
url(r'^history.json$', views.history_json,
|
||||||
name='kfet.history.json'),
|
name='kfet.history.json'),
|
||||||
url(r'^accounts/read.json$', views.account_read_json,
|
|
||||||
name='kfet.account.read.json'),
|
|
||||||
|
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# Settings urls
|
# Settings urls
|
||||||
|
|
|
@ -17,6 +17,7 @@ from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.contrib.auth.models import User, Permission, Group
|
from django.contrib.auth.models import User, Permission, Group
|
||||||
from django.http import HttpResponse, JsonResponse, Http404
|
from django.http import HttpResponse, JsonResponse, Http404
|
||||||
from django.forms import modelformset_factory, formset_factory
|
from django.forms import modelformset_factory, formset_factory
|
||||||
|
from django.forms.models import model_to_dict
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.db.models import F, Sum, Prefetch, Count, Func
|
from django.db.models import F, Sum, Prefetch, Count, Func
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
@ -316,12 +317,14 @@ def account_create_ajax(request, username=None, login_clipper=None):
|
||||||
'user_form' : forms['user_form'],
|
'user_form' : forms['user_form'],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# Account - Read
|
# Account - Read
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def account_read(request, trigramme):
|
def account_read(request, trigramme):
|
||||||
try:
|
try:
|
||||||
account = Account.objects.select_related('negative').get(trigramme=trigramme)
|
account = Account.objects.select_related('negative')\
|
||||||
|
.get(trigramme=trigramme)
|
||||||
except Account.DoesNotExist:
|
except Account.DoesNotExist:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
@ -330,19 +333,32 @@ def account_read(request, trigramme):
|
||||||
and request.user != account.user:
|
and request.user != account.user:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
addcosts = (OperationGroup.objects
|
if request.GET.get('format') == 'json':
|
||||||
.filter(opes__addcost_for=account,opes__canceled_at=None)
|
data = model_to_dict(
|
||||||
.extra({'date':"date(at)"})
|
account,
|
||||||
.values('date')
|
fields=['id', 'trigramme', 'firstname', 'lastname', 'email',
|
||||||
.annotate(sum_addcosts=Sum('opes__addcost_amount'))
|
'is_cof', 'promo', 'balance', 'is_frozen', 'departement',
|
||||||
.order_by('-date'))
|
'nickname', 'trigramme']
|
||||||
|
)
|
||||||
|
data['name'] = account.name
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
addcosts = (
|
||||||
|
OperationGroup.objects
|
||||||
|
.filter(opes__addcost_for=account, opes__canceled_at=None)
|
||||||
|
.extra({'date': "date(at)"})
|
||||||
|
.values('date')
|
||||||
|
.annotate(sum_addcosts=Sum('opes__addcost_amount'))
|
||||||
|
.order_by('-date')
|
||||||
|
)
|
||||||
|
|
||||||
return render(request, "kfet/account_read.html", {
|
return render(request, "kfet/account_read.html", {
|
||||||
'account' : account,
|
'account': account,
|
||||||
'addcosts': addcosts,
|
'addcosts': addcosts,
|
||||||
'settings': { 'subvention_cof': Settings.SUBVENTION_COF() },
|
'settings': {'subvention_cof': Settings.SUBVENTION_COF()},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# Account - Update
|
# Account - Update
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -827,45 +843,28 @@ def kpsul_get_settings(request):
|
||||||
}
|
}
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
@teamkfet_required
|
|
||||||
def account_read_json(request):
|
|
||||||
trigramme = request.POST.get('trigramme', '')
|
|
||||||
account = get_object_or_404(Account, trigramme=trigramme)
|
|
||||||
data = { 'id': account.pk, 'name': account.name, 'email': account.email,
|
|
||||||
'is_cof': account.is_cof, 'promo': account.promo,
|
|
||||||
'balance': account.balance, 'is_frozen': account.is_frozen,
|
|
||||||
'departement': account.departement, 'nickname': account.nickname,
|
|
||||||
'trigramme': account.trigramme }
|
|
||||||
return JsonResponse(data)
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
def kpsul_checkout_data(request):
|
def kpsul_checkout_data(request, pk):
|
||||||
pk = request.POST.get('pk', 0)
|
checkout = get_object_or_404(Checkout, pk=pk)
|
||||||
if not pk:
|
data = model_to_dict(
|
||||||
pk = 0
|
checkout,
|
||||||
data = (Checkout.objects
|
fields=['id', 'name', 'balance', 'valid_from', 'valid_to']
|
||||||
.annotate(
|
)
|
||||||
last_statement_by_first_name=F('statements__by__cofprofile__user__first_name'),
|
|
||||||
last_statement_by_last_name=F('statements__by__cofprofile__user__last_name'),
|
if request.GET.get('last_statement'):
|
||||||
last_statement_by_trigramme=F('statements__by__trigramme'),
|
last_statement = checkout.statements.latest('at')
|
||||||
last_statement_balance=F('statements__balance_new'),
|
last_statement_data = model_to_dict(
|
||||||
last_statement_at=F('statements__at'))
|
last_statement,
|
||||||
.values(
|
fields=['id', 'at', 'balance_new', 'balance_old', 'by']
|
||||||
'id', 'name', 'balance', 'valid_from', 'valid_to',
|
)
|
||||||
'last_statement_balance', 'last_statement_at',
|
# ``at`` is not editable, so skipped by ``model_to_dict``
|
||||||
'last_statement_by_trigramme', 'last_statement_by_last_name',
|
last_statement_data['at'] = last_statement.at
|
||||||
'last_statement_by_first_name')
|
data['laststatement'] = last_statement_data
|
||||||
.select_related(
|
|
||||||
'statements'
|
|
||||||
'statements__by',
|
|
||||||
'statements__by__cofprofile__user')
|
|
||||||
.filter(pk=pk)
|
|
||||||
.order_by('statements__at')
|
|
||||||
.last())
|
|
||||||
if data is None:
|
|
||||||
raise Http404
|
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
@teamkfet_required
|
@teamkfet_required
|
||||||
def kpsul_update_addcost(request):
|
def kpsul_update_addcost(request):
|
||||||
addcost_form = AddcostForm(request.POST)
|
addcost_form = AddcostForm(request.POST)
|
||||||
|
|
|
@ -18,3 +18,4 @@ statistics==1.0.3.5
|
||||||
future==0.15.2
|
future==0.15.2
|
||||||
django-widget-tweaks==1.4.1
|
django-widget-tweaks==1.4.1
|
||||||
git+https://github.com/Aureplop/channels.git#egg=channels
|
git+https://github.com/Aureplop/channels.git#egg=channels
|
||||||
|
django-js-reverse==0.7.3
|
||||||
|
|
Loading…
Reference in a new issue