diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index a4573fbee55..3b72e38a2af 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -499,7 +499,6 @@ class AccountBankStatementLine(models.Model):
'st_line': st_line.get_statement_line_for_reconciliation_widget(),
'reconciliation_proposition': rp
})
-
return ret
def get_statement_line_for_reconciliation_widget(self):
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js
index 51175be7794..8763d5acaa1 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_action.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js
@@ -30,6 +30,7 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
validate_all_balanced: '_onValidate',
change_name: '_onChangeName',
close_statement: '_onCloseStatement',
+ load_more: '_onLoadMore',
},
config: {
// used to instanciate the model
@@ -71,6 +72,7 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
'bank_statement_id': self.model.bank_statement_id,
'valuenow': self.model.valuenow,
'valuemax': self.model.valuemax,
+ 'defaultDisplayQty': self.model.defaultDisplayQty,
'title': self.title,
});
});
@@ -89,12 +91,7 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
this.update_control_panel({breadcrumbs: breadcrumbs, search_view_hidden: true}, {clear: true});
this.renderer.prependTo(self.$('.o_form_sheet'));
- _.each(this.model.lines, function (line, handle) {
- var widget = new self.config.LineRenderer(self, self.model, line);
- widget.handle = handle;
- self.widgets.push(widget);
- widget.appendTo(self.$('.o_reconciliation_lines'));
- });
+ this._renderLines();
this._openFirstLine();
},
@@ -134,6 +131,15 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
return _.find(this.widgets, function (widget) {return widget.handle===handle;});
},
+ /**
+ *
+ */
+ _loadMore: function(qty) {
+ var self = this;
+ return this.model.loadMore(qty).then(function () {
+ self._renderLines();
+ });
+ },
/**
* sitch to 'match' the first available line
*
@@ -153,6 +159,24 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
}
return handle;
},
+ /**
+ * render line widget and append to view
+ *
+ * @private
+ */
+ _renderLines: function () {
+ var self = this;
+ var linesToDisplay = this.model.getStatementLines();
+ _.each(linesToDisplay, function (line, handle) {
+ var widget = new self.config.LineRenderer(self, self.model, line);
+ widget.handle = handle;
+ self.widgets.push(widget);
+ widget.appendTo(self.$('.o_reconciliation_lines'));
+ });
+ if (this.model.hasMoreLines() === false) {
+ this.renderer.hideLoadMoreButton();
+ }
+ },
//--------------------------------------------------------------------------
// Handlers
@@ -223,6 +247,14 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
});
});
},
+ /**
+ * Load more statement and render them
+ *
+ * @param {OdooEvent} event
+ */
+ _onLoadMore: function (event) {
+ return this._loadMore(this.model.defaultDisplayQty);
+ },
/**
* call 'validate' or 'autoReconciliation' model method then destroy the
* validated lines and update the action renderer with the new status bar
@@ -246,7 +278,15 @@ var StatementAction = Widget.extend(ControlPanelMixin, {
});
_.each(result.handles, function (handle) {
self._getWidget(handle).destroy();
+ var index = _.findIndex(self.widgets, function (widget) {return widget.handle===handle;});
+ self.widgets.splice(index, 1);
});
+ // Get number of widget and if less than constant and if there are more to laod, load until constant
+ if (self.widgets.length < self.model.defaultDisplayQty
+ && self.model.valuemax - self.model.valuenow >= self.model.defaultDisplayQty) {
+ var toLoad = self.model.defaultDisplayQty - self.widgets.length;
+ self._loadMore(toLoad);
+ }
self._openFirstLine();
});
},
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
index e12f8f21009..7d3297a7003 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js
@@ -100,6 +100,8 @@ var StatementModel = BasicModel.extend({
this.lines = {};
this.valuenow = 0;
this.valuemax = 0;
+ this.alreadyDisplayed = [];
+ this.defaultDisplayQty = 10;
},
//--------------------------------------------------------------------------
@@ -283,6 +285,34 @@ var StatementModel = BasicModel.extend({
getContext: function () {
return this.context;
},
+ /**
+ * Return the lines that needs to be displayed by the widget
+ *
+ * @returns {Object} lines that are loaded and not yet displayed
+ */
+ getStatementLines: function () {
+ var self = this;
+ var linesToDisplay = _.pick(this.lines, function(value, key, object) {
+ if (value.visible === true && self.alreadyDisplayed.indexOf(key) === -1) {
+ self.alreadyDisplayed.push(key);
+ return object;
+ }
+ });
+ return linesToDisplay;
+ },
+ /**
+ * Return a boolean telling if load button needs to be displayed or not
+ *
+ * @returns {boolean} true if load more button needs to be displayed
+ */
+ hasMoreLines: function () {
+ var self = this;
+ var notDisplayed = _.filter(this.lines, function(line) { return !line.visible; });
+ if (notDisplayed.length > 0) {
+ return true;
+ }
+ return false;
+ },
/**
* get the line data for this handle
*
@@ -355,14 +385,42 @@ var StatementModel = BasicModel.extend({
line.reconcileModels = self.reconcileModels;
});
var ids = _.pluck(self.lines, 'id');
- return self._rpc({
- model: 'account.bank.statement.line',
- method: 'get_data_for_reconciliation_widget',
- args: [ids],
- })
- .then(self._formatLine.bind(self));
+ ids = ids.splice(0, self.defaultDisplayQty);
+ self.pagerIndex = ids.length;
+ return self.loadData(ids, []);
});
},
+ /**
+ * Load more bank statement line
+ *
+ * @param {integer} quantity to load
+ * @returns {Deferred}
+ */
+ loadMore: function(qty) {
+ if (qty === undefined) {
+ qty = this.defaultDisplayQty;
+ }
+ var ids = _.pluck(this.lines, 'id');
+ ids = ids.splice(this.pagerIndex, qty);
+ this.pagerIndex += qty;
+ return this.loadData(ids, this._getExcludedIds());
+ },
+ /**
+ * RPC method to load informations on lines
+ *
+ * @param {Array} ids of bank statement line passed to rpc call
+ * @param {Array} list of move_line ids that needs to be excluded from search
+ * @returns {Deferred}
+ */
+ loadData: function(ids, excluded_ids) {
+ var self = this;
+ return self._rpc({
+ model: 'account.bank.statement.line',
+ method: 'get_data_for_reconciliation_widget',
+ args: [ids, excluded_ids],
+ })
+ .then(self._formatLine.bind(self));
+ },
/**
* Add lines into the propositions from the reconcile model
* Can add 2 lines, and each with its taxes. The second line become editable
@@ -770,6 +828,7 @@ var StatementModel = BasicModel.extend({
var line = _.find(self.lines, function (l) {
return l.id === data.st_line.id;
});
+ line.visible = true;
_.extend(line, data);
self._formatLineProposition(line, line.reconciliation_proposition);
if (!line.reconciliation_proposition.length) {
@@ -850,6 +909,26 @@ var StatementModel = BasicModel.extend({
prop.amount = prop.base_amount;
return prop;
},
+ /**
+ * Return list of account_move_line that has been selected and needs to be removed
+ * from other calls.
+ *
+ * @private
+ * @returns {Array} list of excluded ids
+ */
+ _getExcludedIds: function () {
+ var excludedIds = [];
+ _.each(this.lines, function(line) {
+ if (line.reconciliation_proposition) {
+ _.each(line.reconciliation_proposition, function(prop) {
+ if (parseInt(prop['id'])) {
+ excludedIds.push(prop['id']);
+ }
+ })
+ }
+ });
+ return excludedIds;
+ },
/**
* Defined whether the line is to be displayed or not. Here, we only display
* the line if it comes from the server or if an account is defined when it
@@ -1168,7 +1247,8 @@ var ManualModel = StatementModel.extend({
'filter': "",
'reconcileModels': [],
'account_id': this._formatNameGet([data.account_id, data.account_name]),
- 'st_line': data
+ 'st_line': data,
+ 'visible': true
});
this._formatLineProposition(line, line.reconciliation_proposition);
if (!line.reconciliation_proposition.length) {
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
index 915dd2c7966..d750f73503d 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
@@ -24,6 +24,7 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, {
"click *[rel='do_action']": "_onDoAction",
'click button.button_back_to_statement': '_onGoToBankStatement',
'click button.button_close_statement': '_onCloseBankStatement',
+ 'click button.js_load_more': '_onLoadMore',
},
/**
* @override
@@ -87,6 +88,12 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, {
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
+ /*
+ * hide the button to load more statement line
+ */
+ hideLoadMoreButton: function () {
+ this.$('.js_load_more').hide();
+ },
/**
* update the statement rendering
*
@@ -207,6 +214,14 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, {
view_mode: 'form',
});
},
+ /**
+ * Load more statement lines for reconciliation
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onLoadMore: function (e) {
+ this.trigger_up('load_more');
+ },
/**
* @private
*/
diff --git a/addons/account/static/src/xml/account_reconciliation.xml b/addons/account/static/src/xml/account_reconciliation.xml
index 0023fc28b8c..da27c44d882 100644
--- a/addons/account/static/src/xml/account_reconciliation.xml
+++ b/addons/account/static/src/xml/account_reconciliation.xml
@@ -25,6 +25,9 @@
+
+
+
Tip: Hit CTRL-Enter to reconcile all the balanced items in the sheet.
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
index 4c6ede6eb6e..c5254bfc69a 100644
--- a/addons/account/static/tests/reconciliation_tests.js
+++ b/addons/account/static/tests/reconciliation_tests.js
@@ -1,7 +1,9 @@
odoo.define('account.reconciliation_tests.data', function () {
"use strict";
-var data = {
+var Datas = {};
+
+var db = {
'res.partner': {
fields: {
id: {string: "ID", type: 'integer'},
@@ -58,7 +60,7 @@ var data = {
{id: 7, display_name: "Tax 10.00% include", amount: 10, price_include: true, account_id: 288},
],
json_friendly_compute_all: function (args) {
- var tax = _.find(data['account.tax'].records, {'id': args[0][0]});
+ var tax = _.find(db['account.tax'].records, {'id': args[0][0]});
var amount = args[1];
var tax_base = tax.price_include ? amount*100/(100+tax.amount) : amount;
return $.when({
@@ -103,7 +105,7 @@ var data = {
'account.bank.statement': {
fields: {},
reconciliation_widget_preprocess: function () {
- return $.when(data_preprocess);
+ return $.when(Datas.used.data_preprocess);
},
},
'account.bank.statement.line': {
@@ -122,19 +124,19 @@ var data = {
var partner_id = args.splice(1, 1)[0];
var excluded_ids = args.splice(1, 1)[0];
var key = JSON.stringify(args);
- if (!mv_lines[key]) {
+ if (!Datas.used.mv_lines[key]) {
throw new Error("Unknown parameters for get_move_lines_for_reconciliation_widget: '"+ key + "'");
}
- return $.when(_.filter(mv_lines[key], function (line) {
+ return $.when(_.filter(Datas.used.mv_lines[key], function (line) {
return excluded_ids.indexOf(line.id) === -1 && (!partner_id || partner_id === line.partner_id);
}));
},
get_data_for_reconciliation_widget: function (args) {
var ids = args[0];
- return $.when(_.filter(data_widget, function (w) {return _.contains(ids, w.st_line.id);}));
+ return $.when(_.filter(Datas.used.data_widget, function (w) {return _.contains(ids, w.st_line.id);}));
},
reconciliation_widget_auto_reconcile: function () {
- return $.when(auto_reconciliation);
+ return $.when(Datas.used.auto_reconciliation);
},
process_reconciliations: function (args) {
var datas = args[1];
@@ -142,8 +144,8 @@ var data = {
ids = ids.concat(_.flatten(_.pluck(datas, 'payment_aml_ids')));
ids = _.compact(ids);
- for (var key in move_lines_for_manual_reconciliation) {
- move_lines_for_manual_reconciliation[key] = _.filter(move_lines_for_manual_reconciliation[key], function (mv_line) {
+ for (var key in Datas.used.move_lines_for_manual_reconciliation) {
+ Datas.used.move_lines_for_manual_reconciliation[key] = _.filter(Datas.used.move_lines_for_manual_reconciliation[key], function (mv_line) {
return ids.indexOf(mv_line.id) === -1;
});
}
@@ -154,18 +156,18 @@ var data = {
fields: {},
get_data_for_manual_reconciliation_widget: function (args) {
var key = JSON.stringify(args);
- if (!data_for_manual_reconciliation_widget[key]) {
+ if (!Datas.used.data_for_manual_reconciliation_widget[key]) {
throw new Error("Unknown parameters for get_data_for_manual_reconciliation_widget: '"+ key + "'");
}
- return $.when(data_for_manual_reconciliation_widget[key]);
+ return $.when(Datas.used.data_for_manual_reconciliation_widget[key]);
},
get_move_lines_for_manual_reconciliation: function (args) {
var excluded_ids = args.splice(2, 1)[0];
var key = JSON.stringify(args);
- if (!move_lines_for_manual_reconciliation[key]) {
+ if (!Datas.used.move_lines_for_manual_reconciliation[key]) {
throw new Error("Unknown parameters for get_move_lines_for_manual_reconciliation: '"+ key + "'");
}
- return $.when(_.filter(move_lines_for_manual_reconciliation[key], function (line) {
+ return $.when(_.filter(Datas.used.move_lines_for_manual_reconciliation[key], function (line) {
return excluded_ids.indexOf(line.id) === -1;
}));
},
@@ -174,8 +176,8 @@ var data = {
var datas = args[0];
for (var i in datas) {
var data = datas[i];
- for (var key in move_lines_for_manual_reconciliation) {
- move_lines_for_manual_reconciliation[key] = _.filter(move_lines_for_manual_reconciliation[key], function (mv_line) {
+ for (var key in Datas.used.move_lines_for_manual_reconciliation) {
+ Datas.used.move_lines_for_manual_reconciliation[key] = _.filter(Datas.used.move_lines_for_manual_reconciliation[key], function (mv_line) {
return data.mv_line_ids.indexOf(mv_line.id) === -1;
});
}
@@ -468,23 +470,22 @@ var options = {
}
};
-return {
- params: {
- data: data,
- data_preprocess: data_preprocess,
- data_widget: data_widget,
- mv_lines: mv_lines,
- auto_reconciliation: auto_reconciliation,
- data_for_manual_reconciliation_widget: data_for_manual_reconciliation_widget,
- move_lines_for_manual_reconciliation: move_lines_for_manual_reconciliation,
- session: session,
- options: options,
- },
- // this is the main function for this module. Its job is to export (and clone) all data for a test.
- getParams: function () {
- return $.extend(true, {}, this.params);
- }
+Datas.params = {
+ data: db,
+ data_preprocess: data_preprocess,
+ data_widget: data_widget,
+ mv_lines: mv_lines,
+ auto_reconciliation: auto_reconciliation,
+ data_for_manual_reconciliation_widget: data_for_manual_reconciliation_widget,
+ move_lines_for_manual_reconciliation: move_lines_for_manual_reconciliation,
+ session: session,
+ options: options,
};
+ // this is the main function for this module. Its job is to export (and clone) all data for a test.
+Datas.getParams = function () {
+ return (this.used = $.extend(true, {}, this.params));
+};
+return Datas;
});
odoo.define('account.reconciliation_tests', function (require) {
@@ -992,5 +993,229 @@ QUnit.module('account', {
clientAction.destroy();
});
+
+
+ QUnit.test('Reconciliation pager', function (assert) {
+ assert.expect(6);
+
+ this.params.data['account.bank.statement.line'].records.push(
+ {id: 9, display_name: "add 9"},
+ {id: 10, display_name: "add 10"},
+ {id: 11, display_name: "add 11"},
+ {id: 12, display_name: "add 12"},
+ {id: 13, display_name: "add 13"},
+ {id: 14, display_name: "add 14"},
+ {id: 15, display_name: "add 15"},
+ {id: 16, display_name: "add 16"}
+ );
+
+ var data_preprocess = this.params.data_preprocess;
+ data_preprocess.st_lines_ids.push(9, 10, 11, 12, 13, 14, 15, 16);
+
+ this.params.data_widget.push(
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 9",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 9,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 10",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 10,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 11",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 11,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 12",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 12,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 13",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 13,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 14",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 14,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 15",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 15,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ },
+ {
+ 'st_line': {
+ 'currency_id': 3,
+ 'communication_partner_name': false,
+ 'open_balance_account_id': 287,
+ 'name': "add 16",
+ 'partner_name': "Camptocamp",
+ 'partner_id': 12,
+ 'has_no_partner': false,
+ 'journal_id': 84,
+ 'account_name': "Bank",
+ 'note': "",
+ 'amount': 650.0,
+ 'amount_str': "$ 650.00",
+ 'amount_currency_str': "",
+ 'date': "2017-01-01",
+ 'account_code': "101401",
+ 'ref': "",
+ 'id': 16,
+ 'statement_id': 2
+ },
+ 'reconciliation_proposition': []
+ }
+ );
+
+ var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
+ testUtils.addMockEnvironment(clientAction, {
+ 'data': this.params.data,
+ });
+
+ clientAction.appendTo($('#qunit-fixture'));
+
+ assert.strictEqual(clientAction.$('.o_reconciliation_line').length, 10, "should display 10 lines");
+ assert.strictEqual(clientAction.$('.js_load_more:visible').length, 1, "should display the load more button");
+ clientAction.$('.o_reconcile:visible').click();
+ assert.strictEqual(clientAction.$('.o_reconcile:visible').length, 0, "should remove the reconciled line");
+ assert.strictEqual(clientAction.$('.o_reconciliation_line').length, 10, "should load one line to complete the 10");
+ clientAction.$('.js_load_more').click();
+ assert.strictEqual(clientAction.$('.o_reconciliation_line').length, 11, "should load the last record");
+ assert.strictEqual(clientAction.$('.js_load_more:visible').length, 0, "should hide the load more button");
+
+ clientAction.destroy();
+ });
});
});