diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js
index 2a0c398b..20f1203f 100644
--- a/kfet/static/kfet/js/kfet.api.js
+++ b/kfet/static/kfet/js/kfet.api.js
@@ -547,23 +547,199 @@ class Article extends ModelObject {
/**
* Verbose name for Article model
* @default 'article'
- * @see {@link Models.ModelObject.compare|ModelObject.compare}
+ * @see {@link Models.ModelObject.verbose_name|ModelObject.verbose_name}
*/
static get verbose_name() { return 'article'; }
/**
- * @default {@link Formatters.ArticleCategoryFormatter}
+ * @default {@link Formatters.ArticleFormatter}
*/
formatter() {
return ArticleFormatter;
}
+ /**
+ * Comparison function between Article model instances.
+ * @see {@link Models.ModelObject.compare|ModelObject.compare}
+ */
+ static compare(a, b) {
+ return a.name.localeCompare(b.name);
+ }
+
// Take care of 'price' type
// API currently returns a string object (serialization of Decimal type within Django)
get price() { return this._price; }
set price(v) { this._price = floatCheck(v); }
}
+/**
+ * Day model. Cannot be accessed through API.
+ * @extends Model.ModelObject
+ * @memberof Models
+ */
+class Day extends ModelObject {
+
+ /**
+ * Properties associated to a day.
+ * @default ['id', 'date']
+ * @see {@link Models.ModelObject.props|ModelObject.props}
+ */
+ static get props() { return ['id', 'date'] }
+
+ /**
+ * Default values for Day model instances
+ * @default {'id': '', 'date': moment()}
+ * @see {@link Models.ModelObject.default_data|ModelObject.default_data}
+ */
+ static get default_data() { return {'id': '', 'date': moment()}; }
+
+ /**
+ * Verbose name for Day model.
+ * @default 'day'
+ * @see {@link Models.ModelObject.verbose_name[ModelObject.verbose_name}
+ */
+ static get verbose_name() { return 'day'; }
+
+ /**
+ * @default {@link Formatters.DayFormatter}
+ */
+ formatter() {
+ return DayFormatter;
+ }
+
+ /**
+ * Comparison function between Day model instances.
+ * @see {@link Models.ModelObject.compare|ModelObject.compare}
+ */
+ static compare (a, b) {
+ //Days are sorted by most recent first
+ if (a.date < b.date) return 1;
+ else if (a.date > b.date) return -1;
+ else return 0;
+ }
+
+ //Parse date and round it
+ get date() { return this._date; }
+ set date(v) { this._date = dateUTCToParis(v).startof('date'); }
+}
+
+
+/**
+ * OperationGroup model. Cannot be accessed through API.
+ * @extends Models.ModelObject
+ * @memberof Models
+ */
+class OperationGroup extends ModelObject {
+
+ /**
+ * Properties associated with an opegroup.
+ * @default ['id', 'amount', 'at', 'is_cof',
+ * 'comment', 'trigramme', 'valid_by']
+ * @see {@link Models.ModelObject.props|ModelObject.props}
+ */
+ static get props() {
+ return ['id', 'amount', 'at', 'is_cof', 'comment',
+ 'trigramme', 'valid_by', 'day'];
+ }
+
+ /**
+ * Default values for OperationGroup instances.
+ * @default {'id': 0, 'amount': 0, 'at': moment(), 'is_cof': false,
+ 'comment': '', 'trigramme': '', 'valid_by': ''}
+ * @see {@link Models.ModelObject.default_data|ModelObject.default_data}
+ */
+ static get default_data() {
+ return {'id': 0, 'amount': 0, 'at': moment(), 'is_cof': false,
+ 'comment': '', 'trigramme': '', 'valid_by': '', 'day': new Day()};
+ }
+
+ /**
+ * Verbose name for OperationGroup model.
+ * @default 'opegroup'
+ * @see {@link Models.ModelObject.verbose_name|ModelObject.verbose_name}
+ */
+ static get verbose_name() { return 'opegroup'; }
+
+ /**
+ * @default {@link Formatters.OpegroupFormatter}
+ */
+ formatter() {
+ return OpegroupFormatter;
+ }
+
+ /**
+ * Comparison function between OperationGroup model instances.
+ * @see {@link Models.ModelObject.compare|ModelObject.compare}
+ */
+ static compare(a, b) {
+ //Opegroups are sorted by most recent first
+ if (a.at < b.at) return 1;
+ else if (a.at > b.at) return -1;
+ else return 0;
+ }
+
+ get amount() { return this._amount; }
+ set amount(v) { this._amount = floatCheck(v); }
+
+ // Parse the date to a moment() object
+ get at() { return this._at; }
+ set at(v) { this._at = dateUTCToParis(v); }
+}
+
+/**
+ * Operation model. Cannot be accessed through API.
+ * @extends Models.ModelObject
+ * @memberof Models
+ */
+class Operation extends ModelObject {
+
+ /**
+ * Properties associated to an operation.
+ * @default ['id', 'type', 'amount', 'article_name', 'article_nb',
+ * 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at',
+ * 'canceled_by']
+ * @see {@link Models.ModelObject.props|ModelObject.props}
+ */
+ static get props() {
+ return ['id', 'type', 'amount', 'article_name', 'article_nb',
+ 'is_checkout', 'addcost_amount', 'addcost_for', 'canceled_at',
+ 'canceled_by', 'opegroup']
+ }
+
+ /**
+ * Default values for Operation model instances
+ * @default {'id': 0, 'type': '', 'amount': 0, 'article_name': '',
+ * 'article_nb': 0, 'is_checkout': false, 'addcost_amount': 0,
+ * 'addcost_for': '', 'canceled_at': undefined, 'canceled_by': ''}
+ * @see {@link Models.ModelObject.default_data|ModelObject.default_data}
+ */
+ static get default_data() {
+ return {'id': 0, 'type': '', 'amount': 0, 'article_name': '', 'article_nb': 0,
+ 'is_checkout': false, 'addcost_amount': 0, 'addcost_for': '',
+ 'canceled_at': undefined, 'canceled_by': '', 'opegroup': new OperationGroup() };
+ }
+
+ static get verbose_name() { return 'operation'; }
+
+ formatter() {
+ return OperationFormatter;
+ }
+
+ get amount() { return this._amount; }
+ set amount(v) { this._amount = floatCheck(v); }
+
+ get addcost_amount() { return this._addcost_amount; }
+ set addcost_amount(v) { this._addcost_amount = floatCheck(v); }
+
+ get canceled_at() { return this._canceled_at; }
+ set canceled_at(v) {
+ if (v)
+ this._canceled_at = dateUTCToParis(v);
+ else
+ this._canceled_at = undefined;
+ }
+}
+
/**
* Simple {@link Models.ModelObject} list.
@@ -1089,3 +1265,139 @@ class ArticleFormatter extends Formatter {
else /* a.stock < -5 */ { return this._data_stock.neg; }
}
}
+
+
+/**
+ * @extends Formatters.Formatter
+ * @memberof Formatters
+ * @todo don't display trigramme in account_read
+ */
+
+//TODO display_trigramme option
+class OpegroupFormatter extends Formatter {
+
+ /**
+ * Properties renderable to html.
+ * @default {@link Models.Opegroup.props}
+ */
+ static get props() {
+ return OperationGroup.props.concat(['time']);
+ }
+
+ static prop_amount(a) {
+ return amountDisplay(a.amount, a.is_cof, a.trigramme);
+ }
+
+ static prop_time(a) {
+ return a.at.format('HH:mm:ss');
+ }
+
+ static prop_valid_by(a) {
+ return 'Par '+a.valid_by;
+ }
+}
+
+/**
+ * @extends Formatters.Formatter
+ * @memberof Formatters
+ */
+
+class DayFormatter extends Formatter {
+
+ /**
+ * Properties renderable to html.
+ * @default {@link Models.Day.props}
+ */
+ static get props() {
+ return Day.props;
+ }
+
+ static prop_date(a) {
+ return a.date.format('D MMMM');
+ }
+}
+
+/**
+ * @extends Formatters.Formatter
+ * @memberof Formatters
+ */
+class OperationFormatter extends Formatter {
+
+ static get props() {
+ return ['amount', 'infos1', 'infos2', 'addcost', 'canceled'];
+ }
+
+ static get attrs() {
+ return ['canceled'];
+ }
+
+ static prop_amount(a) {
+ return amountDisplay(a.amount, a.opegroup.is_cof, a.opegroup.trigramme);
+ }
+
+ static prop_addcost(a) {
+ if (a.addcost_for) {
+ return '('+amountDisplay(a.addcost_amount, a.opegroup.is_cof)
+ +'UKF pour '+a.addcost_for+')';
+ } else {
+ return '';
+ }
+ }
+
+ static prop_canceled(a) {
+ if (a.canceled_at) {
+ cancel = 'Annulé';
+ if (a.canceled_by)
+ cancel += ' par '+a.canceled_by;
+
+ cancel += ' le '+a.canceled_at.format('DD/MM/YY à HH:mm:ss');
+ return cancel ;
+ } else {
+ return '';
+ }
+ }
+
+ static attr_canceled(a) {
+ return a.canceled_at ? 'true' : 'false' ;
+ }
+}
+
+class PurchaseFormatter extends OperationFormatter {
+
+ static prop_infos1(a) {
+ return a.article_nb;
+ }
+
+ static prop_infos2(a) {
+ return a.article_name;
+ }
+}
+
+class SpecialOpeFormatter extends OperationFormatter {
+
+ static prop_infos1(a) {
+ return a.amount.toFixed(2)+'€';
+ }
+
+ static prop_infos2(a) {
+ if (!a.is_checkout)
+ return 'Édition';
+
+ switch (a.type) {
+ case 'initial':
+ return 'Initial';
+ break;
+
+ case 'deposit':
+ return 'Charge';
+ break;
+
+ case 'withdraw':
+ return 'Retrait';
+ break;
+
+ default:
+ return '';
+ }
+ }
+}