summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Smet <las@openerp.com>2018-02-07 16:47:39 +0100
committerqdp-odoo <qdp@odoo.com>2018-02-07 16:48:53 +0100
commitb10932d8a51ae10d95b776dfcf4ea417ac5ec711 (patch)
treeb48fe8d9e134a66bf99fbbe21dafb8cd63fa0a32
parent197a5f3887801db8c0fa8d25fac4ce439a9e3bb1 (diff)
[IMP] account: allow partial reconciliation with write-off
Suppose you have an invoice of 3000. You want to pay with paypal 3 x 1000 but having of fee of 10 in each transaction. So, you attempt to reconcile 3000 with 3 x (990 + 10) that is not possible with the reconciliation widget. Was PR #17489. Was task: https://www.odoo.com/web#id=23384&view_type=form&model=project.task&action=333&active_id=967&menu_id=4720
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_model.js38
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_renderer.js30
-rw-r--r--addons/account/static/tests/reconciliation_tests.js16
3 files changed, 56 insertions, 28 deletions
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
index 60cba94fffc..9894dc6a9b4 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js
@@ -526,17 +526,37 @@ var StatementModel = BasicModel.extend({
*/
togglePartialReconcile: function (handle) {
var line = this.getLine(handle);
- var props = _.filter(line.reconciliation_proposition, {'invalid': false});
- var prop = props[0];
- if (props.length !== 1 || Math.abs(line.st_line.amount) >= Math.abs(prop.amount)) {
+
+ // Retrieve the toggle proposition
+ var selected;
+ _.each(line.reconciliation_proposition, function (prop) {
+ if (!prop.invalid) {
+ if (((line.balance.amount < 0 || !line.partial_reconcile) && prop.amount > 0 && line.st_line.amount > 0 && line.st_line.amount < prop.amount) ||
+ ((line.balance.amount > 0 || !line.partial_reconcile) && prop.amount < 0 && line.st_line.amount < 0 && line.st_line.amount > prop.amount)) {
+ selected = prop;
+ return false;
+ }
+ }
+ });
+
+ // If no toggled proposition found, reject it
+ if (selected == null)
return $.Deferred().reject();
- }
- prop.partial_reconcile = !prop.partial_reconcile;
- if (!prop.partial_reconcile) {
+
+ // Inverse partial_reconcile value
+ selected.partial_reconcile = !selected.partial_reconcile;
+ if (!selected.partial_reconcile) {
return this._computeLine(line);
}
+
+ // Compute the write_off
+ var format_options = { currency_id: line.st_line.currency_id };
+ selected.write_off_amount = selected.amount + line.balance.amount;
+ selected.write_off_amount_str = field_utils.format.monetary(Math.abs(selected.write_off_amount), {}, format_options);
+ selected.write_off_amount_str = selected.write_off_amount_str.replace('&nbsp;', ' ');
+
return this._computeLine(line).then(function () {
- if (prop.partial_reconcile) {
+ if (selected.partial_reconcile) {
line.balance.amount = 0;
line.balance.type = 1;
line.mode = 'inactive';
@@ -646,6 +666,7 @@ var StatementModel = BasicModel.extend({
return isNaN(prop.id);
}), self._formatToProcessReconciliation.bind(self, line)),
};
+
// If the lines are not fully balanced, create an unreconciled amount.
// line.st_line.currency_id is never false here because its equivalent to
// statement_line.currency_id or statement_line.journal_id.currency_id or statement_line.journal_id.company_id.currency_id (Python-side).
@@ -1078,8 +1099,9 @@ var StatementModel = BasicModel.extend({
// Do not forward port in master. @CSN will change this
var amount = prop.computed_with_tax && -prop.base_amount || -prop.amount;
if (prop.partial_reconcile === true) {
- amount = -line.st_line.amount;
+ amount = -prop.write_off_amount;
}
+
var result = {
name : prop.label,
debit : amount > 0 ? amount : 0,
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
index 15bdc9e0e19..d840872f840 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
@@ -353,7 +353,21 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
// reconciliation_proposition
var $props = this.$('.accounting_view tbody').empty();
- var props = _.filter(state.reconciliation_proposition, {'display': true});
+
+ // loop state propositions
+ var props = [];
+ var nb_debit_props = 0;
+ var nb_credit_props = 0;
+ _.each(state.reconciliation_proposition, function (prop) {
+ if (prop.display) {
+ props.push(prop);
+ if (prop.amount < 0)
+ nb_debit_props += 1;
+ else if (prop.amount > 0)
+ nb_credit_props += 1;
+ }
+ });
+
_.each(props, function (line) {
var $line = $(qweb.render("reconciliation.line.mv_line", {'line': line, 'state': state}));
if (!isNaN(line.id)) {
@@ -361,18 +375,16 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
.appendTo($line.find('.cell_info_popover'))
.attr("data-content", qweb.render('reconciliation.line.mv_line.details', {'line': line}));
}
-
- if ((state.balance.amount_currency !== 0 || line.partial_reconcile) && props.length === 1 &&
- line.already_paid === false &&
- (
- (state.st_line.amount > 0 && state.st_line.amount < props[0].amount) ||
- (state.st_line.amount < 0 && state.st_line.amount > props[0].amount))
- ) {
+ if (line.already_paid === false &&
+ ((state.balance.amount_currency < 0 || line.partial_reconcile) && nb_credit_props == 1
+ && line.amount > 0 && state.st_line.amount > 0 && state.st_line.amount < line.amount) ||
+ ((state.balance.amount_currency > 0 || line.partial_reconcile) && nb_debit_props == 1
+ && line.amount < 0 && state.st_line.amount < 0 && state.st_line.amount > line.amount)) {
var $cell = $line.find(line.amount > 0 ? '.cell_right' : '.cell_left');
var text;
if (line.partial_reconcile) {
text = _t("Undo the partial reconciliation.");
- $cell.text(state.st_line.amount_str);
+ $cell.text(line.write_off_amount_str);
} else {
text = _t("This move's amount is higher than the transaction's amount. Click to register a partial payment and keep the payment balance open.");
}
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
index 1c413c422a2..1146ec50fe1 100644
--- a/addons/account/static/tests/reconciliation_tests.js
+++ b/addons/account/static/tests/reconciliation_tests.js
@@ -775,13 +775,7 @@ QUnit.module('account', {
partner_id: false,
counterpart_aml_dicts:[],
payment_aml_ids: [392],
- new_aml_dicts: [
- {
- "credit": 343.42,
- "debit": 0,
- "name": "Bank fees : Open balance"
- }
- ],
+ new_aml_dicts: [],
}]
], "should call process_reconciliations with partial reconcile values");
}
@@ -810,14 +804,14 @@ QUnit.module('account', {
assert.notOk( widget.$('.cell_left .line_info_button').length, "should not display the partial reconciliation alert");
widget.$('.accounting_view thead td:first').trigger('click');
widget.$('.match .cell_account_code:first').trigger('click');
- assert.equal( widget.$('.accounting_view tbody .cell_left .line_info_button').length, 0, "should not display the partial reconciliation alert");
+ assert.equal( widget.$('.accounting_view tbody .cell_left .line_info_button').length, 1, "should display the partial reconciliation alert");
assert.ok( widget.$('button.btn-primary:not(hidden)').length, "should not display the reconcile button");
assert.ok( widget.$('.text-danger:not(hidden)').length, "should display counterpart alert");
widget.$('.accounting_view .cell_left .line_info_button').trigger('click');
- assert.strictEqual(widget.$('.accounting_view .cell_left .line_info_button').length, 0, "should not display a partial reconciliation alert");
- assert.notOk(widget.$('.accounting_view .cell_left .line_info_button').hasClass('do_partial_reconcile_false'), "should not display the partial reconciliation information");
+ assert.strictEqual(widget.$('.accounting_view .cell_left .line_info_button').length, 1, "should display a partial reconciliation alert");
+ assert.notOk(widget.$('.accounting_view .cell_left .line_info_button').hasClass('do_partial_reconcile_true'), "should display the partial reconciliation information");
assert.ok( widget.$('button.btn-default:not(hidden)').length, "should display the validate button");
- assert.strictEqual( widget.$el.data('mode'), "match", "should be inactive mode");
+ assert.strictEqual( widget.$el.data('mode'), "inactive", "should be inactive mode");
widget.$('button.btn-default:not(hidden)').trigger('click');
clientAction.destroy();