From efbcde163b661b47f083df679f8843959a30ef3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Wed, 5 Apr 2017 03:43:51 +0200 Subject: [PATCH 1/4] clean some js - clean buttons code on account and checkout - merge CheckoutRead and kpsul_checkout_data views (the first won) APIModelObject interface - add url_create, url_update, url_update_for - rename url_object_for and url_object to url_read_for and url_read --- kfet/static/kfet/js/kfet.api.js | 54 +++++++++++++---- kfet/static/kfet/js/kfet.js | 23 +++++++ kfet/static/kfet/js/kpsul.js | 51 +++++++++++----- kfet/templates/kfet/kpsul.html | 2 - kfet/urls.py | 2 - kfet/views.py | 104 +++++++++++++++++--------------- 6 files changed, 155 insertions(+), 81 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 6b1f8fe3..2418195d 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -130,13 +130,28 @@ class APIModelObject extends ModelObject { */ static get url_model() {} + /** + * Request url to create an instance. + * @abstract + * @type {string} + */ + static url_create() {} + + /** + * Request url to edit an instance of this model. + * @abstract + * @param {*} api_pk - Identifier of a model instance. + * @type {string} + */ + static url_update_for(api_pk) {} + /** * Request url to get a single model instance data. * @abstract - * @param {*} api_pk - Identifier of a model instance in api requests. + * @param {*} api_pk - Identifier of a model instance. * @return {string} */ - static url_object_for(api_pk) {} + static url_read_for(api_pk) {} /** * See {@link Models.ModelObject|new ModelObject(data)}. @@ -158,17 +173,17 @@ class APIModelObject extends ModelObject { /** * Request url used to get current instance data. */ - get url_object() { + get url_read() { if (this._url_object === undefined) - return this.is_empty() ? '' : this.constructor.url_object_for(this.api_pk); + return this.is_empty() ? '' : this.constructor.url_read_for(this.api_pk); return this._url_object; } - set url_object(v) { this._url_object = v; } + set url_read(v) { this._url_object = v; } /** * Get data of a distant model instance. It sends a GET HTTP request to - * {@link Models.APIModelObject#url_object}. + * {@link Models.APIModelObject#url_read}. * @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. @@ -182,7 +197,7 @@ class APIModelObject extends ModelObject { api_options['format'] = 'json'; - $.getJSON(this.url_object, api_options) + $.getJSON(this.url_read, api_options) .done(function (json, textStatus, jqXHR) { that.from(json); on_success(json, textStatus, jqXHR); @@ -199,7 +214,7 @@ class APIModelObject extends ModelObject { * @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.url_read = this.constructor.url_read_for(api_pk); this.fromAPI(api_options, on_success, on_error); } @@ -260,12 +275,21 @@ class Account extends APIModelObject { */ static get url_model() { return Urls['kfet.account'](); } + static url_create(trigramme) { + var url = Urls['kfet.account.create'](); + if (trigramme) { + var trigramme_url = encodeURIComponent(trigramme); + url += `?trigramme=${trigramme_url}` + } + return url; + } + /** * @default django-js-reverse('kfet.account.read')(trigramme) * @param {string} trigramme - * @see {@link Models.APIModelObject.url_object_for|APIModelObject.url_object_for} + * @see {@link Models.APIModelObject.url_read_for|APIModelObject.url_read_for} */ - static url_object_for(trigramme) { + static url_read_for(trigramme) { var trigramme_url = encodeURIComponent(trigramme); return Urls['kfet.account.read'](trigramme_url); } @@ -332,10 +356,10 @@ class Checkout extends APIModelObject { /** * @default django-js-reverse('kfet.kpsul.checkout_data.read')(pk) * @param {string} api_pk - a checkout id - * @see {@link Models.APIModelObject.url_object_for|APIModelObject.url_object_for} + * @see {@link Models.APIModelObject.url_read_for|APIModelObject.url_read_for} */ - static url_object_for(api_pk) { - return Urls['kfet.kpsul.checkout_data.read'](api_pk); + static url_read_for(api_pk) { + return Urls['kfet.checkout.read'](api_pk); } /** @@ -381,6 +405,10 @@ class Statement extends ModelObject { }; } + static url_create(checkout_pk) { + return Urls['kfet.checkoutstatement.create'](checkout_pk); + } + /** * @default {@link Formatters.StatementFormatter} */ diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index 6155d3ed..63f9b939 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -40,6 +40,29 @@ function booleanCheck(v) { } +/** + * Short: Equivalent to python str format. + * Source: [MDN]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals}. + * @example + * var t1Closure = template`${0}${1}${0}!`; + * t1Closure('Y', 'A'); // "YAY!" + * @example + * var t2Closure = template`${0} ${'foo'}!`; + * t2Closure('Hello', {foo: 'World'}); // "Hello World!" + */ +function template(strings, ...keys) { + return (function(...values) { + var dict = values[values.length - 1] || {}; + var result = [strings[0]]; + keys.forEach(function(key, i) { + var value = Number.isInteger(key) ? values[key] : dict[key]; + result.push(value, strings[i + 1]); + }); + return result.join(''); + }); +} + + /** * Get and store K-Psul config from API. *

diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 6052ad56..1917c53e 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -1,3 +1,10 @@ +/** + * @file K-Psul JS + * @copyright 2017 cof-geek + * @license MIT + */ + + class KPsulManager { constructor(env) { @@ -27,6 +34,15 @@ class AccountManager { this.account = new Account(); this.selection = new AccountSelection(this); this.search = new AccountSearch(this); + + // buttons: search, read or create + this.$buttons_container = this._$container.find('.buttons'); + this.buttons_templates = { + create: template``, + read: template``, + search: template``, + } + } is_empty() { return this.account.is_empty(); } @@ -43,25 +59,22 @@ class AccountManager { } _display_buttons() { - // dirty - - var buttons = ''; + var buttons; if (this.is_empty()) { var trigramme = this.selection.get(); if (trigramme.isValidTri()) { - var url_base = Urls['kfet.account.create'](); - var url = url_base + '?trigramme=' + encodeURIComponent(trigramme); - buttons += ''; + var url = Account.url_create(trigramme); + buttons = this.buttons_templates.create({url: url}); } else { /* trigramme input is empty or invalid */ - buttons += ''; + buttons = this.buttons_templates.search(); } } else { /* an account is loaded */ - var url = this.account.url_object; - buttons += ''; + var url = this.account.url_read; + buttons = this.buttons_templates.read({url: url}); } - this._$container.find('.buttons').html(buttons); + this.$buttons_container.html(buttons); } update(trigramme) { @@ -248,6 +261,12 @@ class CheckoutManager { this._$laststatement_container = $('#last_statement'); this.laststatement = new Statement(); this.laststatement_display_prefix = '#checkout-last_statement_'; + + this.$buttons_container = this._$container.find('.buttons'); + this.buttons_templates = { + read: template``, + statement_create: template``, + } } update(id) { @@ -308,13 +327,13 @@ class CheckoutManager { _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 += ''; - buttons += ''; + var url_newcheckout = Statement.url_create(this.checkout.id); + buttons += this.buttons_templates.statement_create({ + url: url_newcheckout}); + var url_read = this.checkout.url_read; + buttons += this.buttons_templates.read({url: url_read}); } - this._$container.find('.buttons').html(buttons); + this.$buttons_container.html(buttons); } reset() { diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index 8e010396..7b74021f 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -529,9 +529,7 @@ $(document).ready(function() { }); if (!existing) { var amount_euro = amountEuroPurchase(id, nb).toFixed(2); - console.log(amount_euro); var index = addPurchaseToFormset(article_data[1], nb, amount_euro); - console.log(index); var article_basket_html = $(item_basket_default_html); article_basket_html .attr('data-opeindex', index) diff --git a/kfet/urls.py b/kfet/urls.py index bec39516..60f6545b 100644 --- a/kfet/urls.py +++ b/kfet/urls.py @@ -169,8 +169,6 @@ urlpatterns = [ # ----- url('^k-psul/$', views.kpsul, name='kfet.kpsul'), - url(r'^k-psul/checkout_data/(?P\d+)$', views.kpsul_checkout_data, - name='kfet.kpsul.checkout_data.read'), url('^k-psul/perform_operations$', views.kpsul_perform_operations, name='kfet.kpsul.perform_operations'), url('^k-psul/cancel_operations$', views.kpsul_cancel_operations, diff --git a/kfet/views.py b/kfet/views.py index 1545f76d..cc69dcda 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -52,6 +52,33 @@ from .statistic import daynames, monthnames, weeknames, \ this_morning, this_monday_morning, this_first_month_day, \ tot_ventes + +# source : docs.djangoproject.com/fr/1.10/topics/class-based-views/mixins/ +class JSONResponseMixin(object): + """ + A mixin that can be used to render a JSON response. + """ + def render_to_json_response(self, context, **response_kwargs): + """ + Returns a JSON response, transforming 'context' to make the payload. + """ + print(context) + return JsonResponse( + self.get_data(context), + **response_kwargs + ) + + def get_data(self, context): + """ + Returns an object that will be serialized as JSON by json.dumps(). + """ + # Note: This is *EXTREMELY* naive; in reality, you'll need + # to do much more complex handling to ensure that arbitrary + # objects -- such as Django model instances or querysets + # -- can be serialized as JSON. + return context + + class Home(TemplateView): template_name = "kfet/home.html" @@ -618,18 +645,44 @@ class CheckoutCreate(SuccessMessageMixin, CreateView): return super(CheckoutCreate, self).form_valid(form) + # Checkout - Read -class CheckoutRead(DetailView): +class CheckoutRead(JSONResponseMixin, DetailView): model = Checkout template_name = 'kfet/checkout_read.html' context_object_name = 'checkout' def get_context_data(self, **kwargs): - context = super(CheckoutRead, self).get_context_data(**kwargs) - context['statements'] = context['checkout'].statements.order_by('-at') + context = super().get_context_data(**kwargs) + checkout = self.object + if self.request.GET.get('last_statement'): + context['laststatement'] = checkout.statements.latest('at') + else: + context['statements'] = checkout.statements.order_by('-at') return context + def render_to_response(self, context, **kwargs): + if self.request.GET.get('format') == 'json': + data = model_to_dict( + context['checkout'], + fields=['id', 'name', 'balance', 'valid_from', 'valid_to'] + ) + if 'laststatement' in context: + last_statement = context['laststatement'] + last_statement_data = model_to_dict( + last_statement, + fields=['id', 'at', 'balance_new', 'balance_old', 'by'] + ) + last_statement_data['by'] = str(last_statement.by) + # ``at`` is not editable, so skipped by ``model_to_dict`` + last_statement_data['at'] = last_statement.at + data['laststatement'] = last_statement_data + return self.render_to_json_response(data) + else: + return super().render_to_response(context, **kwargs) + + # Checkout - Update class CheckoutUpdate(SuccessMessageMixin, UpdateView): @@ -897,28 +950,6 @@ def kpsul_get_settings(request): return JsonResponse(data) -@teamkfet_required -def kpsul_checkout_data(request, pk): - checkout = get_object_or_404(Checkout, pk=pk) - data = model_to_dict( - checkout, - fields=['id', 'name', 'balance', 'valid_from', 'valid_to'] - ) - - if request.GET.get('last_statement'): - last_statement = checkout.statements.latest('at') - last_statement_data = model_to_dict( - last_statement, - fields=['id', 'at', 'balance_new', 'balance_old', 'by'] - ) - last_statement_data['by'] = str(last_statement.by) - # ``at`` is not editable, so skipped by ``model_to_dict`` - last_statement_data['at'] = last_statement.at - data['laststatement'] = last_statement_data - - return JsonResponse(data) - - @teamkfet_required def kpsul_update_addcost(request): addcost_form = AddcostForm(request.POST) @@ -2003,29 +2034,6 @@ class SupplierUpdate(SuccessMessageMixin, UpdateView): # --------------- # Vues génériques # --------------- -# source : docs.djangoproject.com/fr/1.10/topics/class-based-views/mixins/ -class JSONResponseMixin(object): - """ - A mixin that can be used to render a JSON response. - """ - def render_to_json_response(self, context, **response_kwargs): - """ - Returns a JSON response, transforming 'context' to make the payload. - """ - return JsonResponse( - self.get_data(context), - **response_kwargs - ) - - def get_data(self, context): - """ - Returns an object that will be serialized as JSON by json.dumps(). - """ - # Note: This is *EXTREMELY* naive; in reality, you'll need - # to do much more complex handling to ensure that arbitrary - # objects -- such as Django model instances or querysets - # -- can be serialized as JSON. - return context class JSONDetailView(JSONResponseMixin, From f1aaad7317b554d7813f2cf9b076ff96768d818f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Wed, 5 Apr 2017 04:05:31 +0200 Subject: [PATCH 2/4] Better jquery ajax calls management It becomes the same as the original jQuery ajax object. For example, callbacks can be queued. get_by_apipk and from_API of ModelObject returns the ajax object. Example (js): Account.get_by_apipk('AAA') .done(function (data) { console.log(data) }) .fail( () => console.log('cool') ) ... .done( ... --- kfet/static/kfet/js/kfet.api.js | 20 +++++--------------- kfet/static/kfet/js/kpsul.js | 13 ++++++------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 2418195d..bfadc4dd 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -185,24 +185,14 @@ class APIModelObject extends ModelObject { * Get data of a distant model instance. It sends a GET HTTP request to * {@link Models.APIModelObject#url_read}. * @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; - + fromAPI(api_options) { api_options = api_options || {}; - on_success = on_success || $.noop; - on_error = on_error || $.noop; api_options['format'] = 'json'; - $.getJSON(this.url_read, api_options) - .done(function (json, textStatus, jqXHR) { - that.from(json); - on_success(json, textStatus, jqXHR); - }) - .fail(on_error); + return $.getJSON(this.url_read, api_options) + .done( (json) => this.from(json) ); } /** @@ -213,9 +203,9 @@ class APIModelObject extends ModelObject { * @param {jQueryAjaxError} [on_error] * @see {@link Models.APIModelObject#fromAPI|fromAPI} */ - get_by_apipk(api_pk, api_options, on_success, on_error) { + get_by_apipk(api_pk, api_options) { this.url_read = this.constructor.url_read_for(api_pk); - this.fromAPI(api_options, on_success, on_error); + return this.fromAPI(api_options); } /** diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index 1917c53e..f9aa8e23 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -84,10 +84,9 @@ class AccountManager { trigramme = trigramme || this.selection.get(); if (trigramme.isValidTri()) { - this.account.get_by_apipk(trigramme, {}, - () => this._update_on_success(), - () => this.reset_data() - ); + this.account.get_by_apipk(trigramme) + .done( () => this._update_on_success() ) + .fail( () => this.reset_data() ); } else { this.reset_data(); } @@ -279,9 +278,9 @@ class CheckoutManager { 'last_statement': true, }; - this.checkout.get_by_apipk(id, api_options, - (data) => this._update_on_success(data), - () => this.reset_data()); + this.checkout.get_by_apipk(id, api_options) + .done( (data) => this._update_on_success(data) ) + .fail( () => this.reset_data() ); if (kpsul.account_manager.is_empty()) { kpsul.account_manager.focus(); From 6be6202b3fe9c3dc1e6d377f240d52941b15a281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Wed, 5 Apr 2017 04:26:50 +0200 Subject: [PATCH 3/4] few cleans --- kfet/static/kfet/js/kfet.api.js | 9 +++++++++ kfet/static/kfet/js/kpsul.js | 22 +++++++++++----------- kfet/views.py | 1 - 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index bfadc4dd..dd5d011b 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -284,6 +284,11 @@ class Account extends APIModelObject { return Urls['kfet.account.read'](trigramme_url); } + static url_update_for(trigramme) { + var trigramme_url = encodeURIComponent(trigramme); + return Urls['kfet.account.update'](trigramme_url); + } + /** * @default this.trigramme */ @@ -352,6 +357,10 @@ class Checkout extends APIModelObject { return Urls['kfet.checkout.read'](api_pk); } + static url_update_for(api_pk) { + return Urls['kfet.checkout.update'](api_pk); + } + /** * @default {@link Formatters.CheckoutFormatter} */ diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index f9aa8e23..c4052d73 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -36,8 +36,8 @@ class AccountManager { this.search = new AccountSearch(this); // buttons: search, read or create - this.$buttons_container = this._$container.find('.buttons'); - this.buttons_templates = { + this._$buttons_container = this._$container.find('.buttons'); + this._buttons_templates = { create: template``, read: template``, search: template``, @@ -65,16 +65,16 @@ class AccountManager { var trigramme = this.selection.get(); if (trigramme.isValidTri()) { var url = Account.url_create(trigramme); - buttons = this.buttons_templates.create({url: url}); + buttons = this._buttons_templates['create']({url: url}); } else { /* trigramme input is empty or invalid */ - buttons = this.buttons_templates.search(); + buttons = this._buttons_templates['search'](); } } else { /* an account is loaded */ var url = this.account.url_read; - buttons = this.buttons_templates.read({url: url}); + buttons = this._buttons_templates['read']({url: url}); } - this.$buttons_container.html(buttons); + this._$buttons_container.html(buttons); } update(trigramme) { @@ -261,8 +261,8 @@ class CheckoutManager { this.laststatement = new Statement(); this.laststatement_display_prefix = '#checkout-last_statement_'; - this.$buttons_container = this._$container.find('.buttons'); - this.buttons_templates = { + this._$buttons_container = this._$container.find('.buttons'); + this._buttons_templates = { read: template``, statement_create: template``, } @@ -327,12 +327,12 @@ class CheckoutManager { var buttons = ''; if (!this.is_empty()) { var url_newcheckout = Statement.url_create(this.checkout.id); - buttons += this.buttons_templates.statement_create({ + buttons += this._buttons_templates['statement_create']({ url: url_newcheckout}); var url_read = this.checkout.url_read; - buttons += this.buttons_templates.read({url: url_read}); + buttons += this._buttons_templates['read']({url: url_read}); } - this.$buttons_container.html(buttons); + this._$buttons_container.html(buttons); } reset() { diff --git a/kfet/views.py b/kfet/views.py index cc69dcda..a2e4ca17 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -62,7 +62,6 @@ class JSONResponseMixin(object): """ Returns a JSON response, transforming 'context' to make the payload. """ - print(context) return JsonResponse( self.get_data(context), **response_kwargs From 2e0de75471a53c799d5c8153177eef7c40225c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Wed, 5 Apr 2017 13:18:01 +0200 Subject: [PATCH 4/4] kpsul - fix account balance ukf --- kfet/static/kfet/js/kfet.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index dd5d011b..6adc1133 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -309,7 +309,7 @@ class Account extends APIModelObject { /** * Balance converted to UKF according to cof status. */ - get balance_ukf() { return amountToUKF(this.balance, this.is_cof); } + get balance_ukf() { return amountToUKF(this.balance, this.is_cof, true); } }