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(' ', ' '); + 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();