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
This commit is contained in:
Aurélien Delobelle 2017-04-05 03:43:51 +02:00
parent 9d2298a089
commit efbcde163b
6 changed files with 155 additions and 81 deletions

View file

@ -130,13 +130,28 @@ class APIModelObject extends ModelObject {
*/ */
static get url_model() {} 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. * Request url to get a single model instance data.
* @abstract * @abstract
* @param {*} api_pk - Identifier of a model instance in api requests. * @param {*} api_pk - Identifier of a model instance.
* @return {string} * @return {string}
*/ */
static url_object_for(api_pk) {} static url_read_for(api_pk) {}
/** /**
* See {@link Models.ModelObject|new ModelObject(data)}. * See {@link Models.ModelObject|new ModelObject(data)}.
@ -158,17 +173,17 @@ class APIModelObject extends ModelObject {
/** /**
* Request url used to get current instance data. * Request url used to get current instance data.
*/ */
get url_object() { get url_read() {
if (this._url_object === undefined) 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; 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 * 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 {object} [api_options] Additional data appended to the request.
* @param {jQueryAjaxSuccess} [on_success] A function to be called if the request succeeds. * @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. * @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'; api_options['format'] = 'json';
$.getJSON(this.url_object, api_options) $.getJSON(this.url_read, api_options)
.done(function (json, textStatus, jqXHR) { .done(function (json, textStatus, jqXHR) {
that.from(json); that.from(json);
on_success(json, textStatus, jqXHR); on_success(json, textStatus, jqXHR);
@ -199,7 +214,7 @@ class APIModelObject extends ModelObject {
* @see {@link Models.APIModelObject#fromAPI|fromAPI} * @see {@link Models.APIModelObject#fromAPI|fromAPI}
*/ */
get_by_apipk(api_pk, api_options, on_success, on_error) { 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); 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 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 <tt>django-js-reverse('kfet.account.read')(trigramme)</tt> * @default <tt>django-js-reverse('kfet.account.read')(trigramme)</tt>
* @param {string} 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); var trigramme_url = encodeURIComponent(trigramme);
return Urls['kfet.account.read'](trigramme_url); return Urls['kfet.account.read'](trigramme_url);
} }
@ -332,10 +356,10 @@ class Checkout extends APIModelObject {
/** /**
* @default <tt>django-js-reverse('kfet.kpsul.checkout_data.read')(pk)</tt> * @default <tt>django-js-reverse('kfet.kpsul.checkout_data.read')(pk)</tt>
* @param {string} api_pk - a checkout id * @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) { static url_read_for(api_pk) {
return Urls['kfet.kpsul.checkout_data.read'](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} * @default {@link Formatters.StatementFormatter}
*/ */

View file

@ -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. * Get and store K-Psul config from API.
* <br><br> * <br><br>

View file

@ -1,3 +1,10 @@
/**
* @file K-Psul JS
* @copyright 2017 cof-geek
* @license MIT
*/
class KPsulManager { class KPsulManager {
constructor(env) { constructor(env) {
@ -27,6 +34,15 @@ class AccountManager {
this.account = new Account(); this.account = new Account();
this.selection = new AccountSelection(this); this.selection = new AccountSelection(this);
this.search = new AccountSearch(this); this.search = new AccountSearch(this);
// buttons: search, read or create
this.$buttons_container = this._$container.find('.buttons');
this.buttons_templates = {
create: template`<a href="${'url'}" class="btn btn-primary" target="_blank" title="Créer ce compte"><span class="glyphicon glyphicon-plus"></span></a>`,
read: template`<a href="${'url'}" class="btn btn-primary" target="_blank" title="Détails du compte"><span class="glyphicon glyphicon-info-sign"></span></a>`,
search: template`<button class="btn btn-primary search" title="Rechercher"><span class="glyphicon glyphicon-search"></span></button>`,
}
} }
is_empty() { return this.account.is_empty(); } is_empty() { return this.account.is_empty(); }
@ -43,25 +59,22 @@ class AccountManager {
} }
_display_buttons() { _display_buttons() {
// dirty var buttons;
var buttons = '';
if (this.is_empty()) { if (this.is_empty()) {
var trigramme = this.selection.get(); var trigramme = this.selection.get();
if (trigramme.isValidTri()) { if (trigramme.isValidTri()) {
var url_base = Urls['kfet.account.create'](); var url = Account.url_create(trigramme);
var url = url_base + '?trigramme=' + encodeURIComponent(trigramme); buttons = this.buttons_templates.create({url: url});
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 */ } else { /* trigramme input is empty or invalid */
buttons += '<button class="btn btn-primary search" title="Rechercher"><span class="glyphicon glyphicon-search"></span></button>'; buttons = this.buttons_templates.search();
} }
} else { /* an account is loaded */ } else { /* an account is loaded */
var url = this.account.url_object; var url = this.account.url_read;
buttons += '<a href="'+url+'" class="btn btn-primary" target="_blank" title="Détails du compte"><span class="glyphicon glyphicon-cog"></span></a>'; buttons = this.buttons_templates.read({url: url});
} }
this._$container.find('.buttons').html(buttons); this.$buttons_container.html(buttons);
} }
update(trigramme) { update(trigramme) {
@ -248,6 +261,12 @@ class CheckoutManager {
this._$laststatement_container = $('#last_statement'); this._$laststatement_container = $('#last_statement');
this.laststatement = new Statement(); this.laststatement = new Statement();
this.laststatement_display_prefix = '#checkout-last_statement_'; this.laststatement_display_prefix = '#checkout-last_statement_';
this.$buttons_container = this._$container.find('.buttons');
this.buttons_templates = {
read: template`<a class="btn btn-primary" href="${'url'}" title="En savoir plus" target="_blank"><span class="glyphicon glyphicon-info-sign"></span></a>`,
statement_create: template`<a href="${'url'}" title="Effectuer un relevé" class="btn btn-primary" target="_blank"><span class="glyphicon glyphicon-euro"></span></a>`,
}
} }
update(id) { update(id) {
@ -308,13 +327,13 @@ class CheckoutManager {
_display_buttons() { _display_buttons() {
var buttons = ''; var buttons = '';
if (!this.is_empty()) { if (!this.is_empty()) {
var id = this.checkout.id; var url_newcheckout = Statement.url_create(this.checkout.id);
var url_details = Urls['kfet.checkout.read'](id); buttons += this.buttons_templates.statement_create({
var url_newcheckout = Urls['kfet.checkoutstatement.create'](id); url: url_newcheckout});
buttons += '<a href="' + url_newcheckout + '" title="Effectuer un relevé" class="btn btn-primary" target="_blank"><span class="glyphicon glyphicon-euro"></span></a>'; var url_read = this.checkout.url_read;
buttons += '<a class="btn btn-primary" href="' + url_details + '" title="Modifier" target="_blank"><span class="glyphicon glyphicon-cog"></span></a>'; buttons += this.buttons_templates.read({url: url_read});
} }
this._$container.find('.buttons').html(buttons); this.$buttons_container.html(buttons);
} }
reset() { reset() {

View file

@ -529,9 +529,7 @@ $(document).ready(function() {
}); });
if (!existing) { if (!existing) {
var amount_euro = amountEuroPurchase(id, nb).toFixed(2); var amount_euro = amountEuroPurchase(id, nb).toFixed(2);
console.log(amount_euro);
var index = addPurchaseToFormset(article_data[1], nb, amount_euro); var index = addPurchaseToFormset(article_data[1], nb, amount_euro);
console.log(index);
var article_basket_html = $(item_basket_default_html); var article_basket_html = $(item_basket_default_html);
article_basket_html article_basket_html
.attr('data-opeindex', index) .attr('data-opeindex', index)

View file

@ -169,8 +169,6 @@ urlpatterns = [
# ----- # -----
url('^k-psul/$', views.kpsul, name='kfet.kpsul'), url('^k-psul/$', views.kpsul, name='kfet.kpsul'),
url(r'^k-psul/checkout_data/(?P<pk>\d+)$', views.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,

View file

@ -52,6 +52,33 @@ from .statistic import daynames, monthnames, weeknames, \
this_morning, this_monday_morning, this_first_month_day, \ this_morning, this_monday_morning, this_first_month_day, \
tot_ventes 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): class Home(TemplateView):
template_name = "kfet/home.html" template_name = "kfet/home.html"
@ -618,18 +645,44 @@ class CheckoutCreate(SuccessMessageMixin, CreateView):
return super(CheckoutCreate, self).form_valid(form) return super(CheckoutCreate, self).form_valid(form)
# Checkout - Read # Checkout - Read
class CheckoutRead(DetailView): class CheckoutRead(JSONResponseMixin, DetailView):
model = Checkout model = Checkout
template_name = 'kfet/checkout_read.html' template_name = 'kfet/checkout_read.html'
context_object_name = 'checkout' context_object_name = 'checkout'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CheckoutRead, self).get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['statements'] = context['checkout'].statements.order_by('-at') 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 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 # Checkout - Update
class CheckoutUpdate(SuccessMessageMixin, UpdateView): class CheckoutUpdate(SuccessMessageMixin, UpdateView):
@ -897,28 +950,6 @@ def kpsul_get_settings(request):
return JsonResponse(data) 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 @teamkfet_required
def kpsul_update_addcost(request): def kpsul_update_addcost(request):
addcost_form = AddcostForm(request.POST) addcost_form = AddcostForm(request.POST)
@ -2003,29 +2034,6 @@ class SupplierUpdate(SuccessMessageMixin, UpdateView):
# --------------- # ---------------
# Vues génériques # 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, class JSONDetailView(JSONResponseMixin,