[FIX] account: reconciliation widget: partial reconcile 2 inv 1 pay
Have two invoices Make a bank statement with one line, which amount is: Amount[Bank] < Amount[Inv1] + Amount[Inv2] AND Amount[Bank] > Amount[Inv1] Click on on the reconcile button on the form view of the bank statement On the reconciliation widget: Click on the first invoice to fully reconcile it with the payment Click on the second to partially reconcile it with the payment Before this commit, there was no way to let the second invoice open and partially reconciled. The only possibility was to make a write-off V10.0 allowed to to do such a flow After this commit, this use case is dealt with again OPW 1864689 closes #25721
This commit is contained in:
@@ -517,7 +517,6 @@ var StatementModel = BasicModel.extend({
|
||||
},
|
||||
/**
|
||||
* Force the partial reconciliation to display the reconciliate button.
|
||||
* This method should only be called when there is onely one proposition.
|
||||
*
|
||||
* @param {string} handle
|
||||
* @returns {Deferred}
|
||||
@@ -527,14 +526,17 @@ var StatementModel = BasicModel.extend({
|
||||
|
||||
// Retrieve the toggle proposition
|
||||
var selected;
|
||||
_.each(line.reconciliation_proposition, function (prop) {
|
||||
var targetLineAmount = line.st_line.amount;
|
||||
line.reconciliation_proposition.every(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)) {
|
||||
if (((line.balance.amount < 0 || !line.partial_reconcile) && prop.amount > 0 && targetLineAmount > 0 && targetLineAmount < prop.amount) ||
|
||||
((line.balance.amount > 0 || !line.partial_reconcile) && prop.amount < 0 && targetLineAmount < 0 && targetLineAmount > prop.amount)) {
|
||||
selected = prop;
|
||||
return false;
|
||||
}
|
||||
targetLineAmount -= prop.amount;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// If no toggled proposition found, reject it
|
||||
|
||||
@@ -359,18 +359,21 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
|
||||
// loop state propositions
|
||||
var props = [];
|
||||
var nb_debit_props = 0;
|
||||
var nb_credit_props = 0;
|
||||
var partialDebitProps = 0;
|
||||
var partialCreditProps = 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;
|
||||
if (prop.amount > 0 && prop.amount > state.st_line.amount) {
|
||||
partialDebitProps++;
|
||||
} else if (prop.amount < 0 && prop.amount < state.st_line.amount) {
|
||||
partialCreditProps++;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var targetLineAmount = state.st_line.amount;
|
||||
_.each(props, function (line) {
|
||||
var $line = $(qweb.render("reconciliation.line.mv_line", {'line': line, 'state': state}));
|
||||
if (!isNaN(line.id)) {
|
||||
@@ -379,10 +382,10 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
.attr("data-content", qweb.render('reconciliation.line.mv_line.details', {'line': line}));
|
||||
}
|
||||
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)) {
|
||||
((state.balance.amount_currency < 0 || line.partial_reconcile)
|
||||
&& line.amount > 0 && state.st_line.amount > 0 && targetLineAmount < line.amount && partialDebitProps <= 1) ||
|
||||
((state.balance.amount_currency > 0 || line.partial_reconcile)
|
||||
&& line.amount < 0 && state.st_line.amount < 0 && targetLineAmount > line.amount && partialCreditProps <= 1)) {
|
||||
var $cell = $line.find(line.amount > 0 ? '.cell_right' : '.cell_left');
|
||||
var text;
|
||||
if (line.partial_reconcile) {
|
||||
@@ -396,6 +399,8 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
.prependTo($cell)
|
||||
.attr("data-content", text);
|
||||
}
|
||||
targetLineAmount -= line.amount;
|
||||
|
||||
$props.append($line);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
odoo.define('account.reconciliation_tests.data', function () {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Debug tip:
|
||||
* To be able to "see" the test in the browser:
|
||||
* var $body = $('body');
|
||||
* $body.addClass('debug');
|
||||
* clientAction.appendTo($body);
|
||||
*/
|
||||
|
||||
var Datas = {};
|
||||
|
||||
var db = {
|
||||
@@ -1490,5 +1498,194 @@ QUnit.module('account', {
|
||||
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Reconciliation: Payment < inv1 + inv2(partial)', function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
/*
|
||||
* One payment: $1175
|
||||
* Two Invoices
|
||||
* The first invoice will be fully reconciled $650
|
||||
* The second invoice will be partially paid with the rest of the payment $999
|
||||
*/
|
||||
|
||||
// modify the second line that is already in db to put it at $999
|
||||
var indexModif = _.findIndex(this.params.mv_lines['[5,"",0,6]'], function (line) {return line.id === 112});
|
||||
this.params.mv_lines['[5,"",0,6]'][indexModif] =
|
||||
{account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
|
||||
total_amount_str: "$ 999.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0003",
|
||||
partner_name: "Agrolait", total_amount_currency_str: "", id: 112, credit: 0.0, journal_id: [1, "Customer Invoices"],
|
||||
amount_str: "$ 999.00", debit: 999.0, account_code: "101200", ref: "", already_paid: false};
|
||||
|
||||
var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
|
||||
testUtils.addMockEnvironment(clientAction, {
|
||||
data: this.params.data,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'process_reconciliations') {
|
||||
assert.deepEqual(args.args,
|
||||
[
|
||||
[5], // Id of the bank statement line
|
||||
|
||||
[{counterpart_aml_dicts:
|
||||
[{name:"INV/2017/0002",
|
||||
debit: 0,
|
||||
credit: 650,
|
||||
counterpart_aml_id: 109},
|
||||
|
||||
{name: "INV/2017/0003",
|
||||
debit: 0,
|
||||
credit: 525,
|
||||
counterpart_aml_id: 112}],
|
||||
|
||||
payment_aml_ids: [],
|
||||
partner_id: 8,
|
||||
new_aml_dicts: []}]
|
||||
], "should call process_reconciliations with partial reconcile values");
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
session: {
|
||||
currencies: {
|
||||
3: {
|
||||
digits: [69, 2],
|
||||
position: "before",
|
||||
symbol: "$"
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
clientAction.appendTo($('#qunit-fixture'));
|
||||
|
||||
// The first reconciliation "line" is where it happens
|
||||
var widget = clientAction.widgets[0];
|
||||
|
||||
// Add first invoice to reconcile fully
|
||||
widget.$('.match .cell_account_code:first').trigger('click');
|
||||
assert.notOk( widget.$('.cell_right .line_info_button').length,
|
||||
"should not display the partial reconciliation alert");
|
||||
|
||||
// Add second invoice to reconcile partially
|
||||
widget.$('.match .cell_account_code:first').trigger('click');
|
||||
var $reconciliationAlert = widget.$('.cell_right .line_info_button');
|
||||
|
||||
assert.ok($reconciliationAlert.length,
|
||||
"should display the partial reconciliation alert");
|
||||
|
||||
$reconciliationAlert.click();
|
||||
|
||||
var $buttonReconcile = widget.$('button.o_reconcile:not(hidden)');
|
||||
|
||||
assert.equal($buttonReconcile.length, 1,
|
||||
'The reconcile button must be visible');
|
||||
|
||||
$buttonReconcile.click();
|
||||
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Reconciliation: payment and 2 partials', function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
/*
|
||||
* One payment: $1175
|
||||
* Two Invoices as Inv1 = 1200; Inv2 = 1200:
|
||||
* Payment < Inv1 AND Payment < Inv2
|
||||
* No partial reconcile is possible, as a write-off of 1225 is necessary
|
||||
*/
|
||||
|
||||
// modify the invoice line to have their amount > payment
|
||||
var indexInv1 = _.findIndex(this.params.mv_lines['[5,"",0,6]'], function (line) {return line.id === 109});
|
||||
this.params.mv_lines['[5,"",0,6]'][indexInv1] =
|
||||
{account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
|
||||
total_amount_str: "$ 1200.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0002", partner_name: "Agrolait",
|
||||
total_amount_currency_str: "", id: 109, credit: 0.0, journal_id: [1, "Customer Invoices"], amount_str: "$ 1200.00", debit: 1200.0,
|
||||
account_code: "101200", ref: "", already_paid: false};
|
||||
|
||||
var indexInv2 = _.findIndex(this.params.mv_lines['[5,"",0,6]'], function (line) {return line.id === 112});
|
||||
this.params.mv_lines['[5,"",0,6]'][indexInv2] =
|
||||
{account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
|
||||
total_amount_str: "$ 1200.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0003",
|
||||
partner_name: "Agrolait", total_amount_currency_str: "", id: 112, credit: 0.0, journal_id: [1, "Customer Invoices"],
|
||||
amount_str: "$ 1200.00", debit: 1200.0, account_code: "101200", ref: "", already_paid: false};
|
||||
|
||||
var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
|
||||
testUtils.addMockEnvironment(clientAction, {
|
||||
data: this.params.data,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'process_reconciliations') {
|
||||
assert.deepEqual(args.args,
|
||||
[
|
||||
[5], // Id of the bank statement line
|
||||
|
||||
[{counterpart_aml_dicts:
|
||||
[{name:"INV/2017/0002",
|
||||
debit: 0,
|
||||
credit: 1200,
|
||||
counterpart_aml_id: 109},
|
||||
|
||||
{name: "INV/2017/0003",
|
||||
debit: 0,
|
||||
credit: 1200,
|
||||
counterpart_aml_id: 112}],
|
||||
|
||||
payment_aml_ids: [],
|
||||
partner_id: 8,
|
||||
new_aml_dicts: [
|
||||
{account_id: 282,
|
||||
credit: 0,
|
||||
debit: 1225,
|
||||
name: 'SAJ/2014/002 and SAJ/2014/003',
|
||||
}
|
||||
]}]
|
||||
], "should call process_reconciliations with new aml dict reconcile values");
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
session: {
|
||||
currencies: {
|
||||
3: {
|
||||
digits: [69, 2],
|
||||
position: "before",
|
||||
symbol: "$"
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
clientAction.appendTo($('#qunit-fixture'));
|
||||
|
||||
// The first reconciliation "line" is where it happens
|
||||
var widget = clientAction.widgets[0];
|
||||
|
||||
// Add first invoice
|
||||
// There should be the opportunity to reconcile partially
|
||||
widget.$('.match .cell_account_code:first').trigger('click');
|
||||
assert.ok(widget.$('.cell_right .line_info_button').length,
|
||||
"should display the partial reconciliation alert");
|
||||
|
||||
// Add second invoice
|
||||
widget.$('.match .cell_account_code:first').trigger('click');
|
||||
assert.notOk(widget.$('.cell_right .line_info_button').length,
|
||||
"should not display the partial reconciliation alert");
|
||||
|
||||
var writeOffCreate = widget.$('div.create');
|
||||
|
||||
assert.equal(writeOffCreate.length, 1,
|
||||
'A write-off creation should be present');
|
||||
|
||||
assert.equal(writeOffCreate.find('input[name=amount]').val(), -1225,
|
||||
'The right amount should be proposed for the write-off');
|
||||
|
||||
writeOffCreate.find('.create_account_id input.ui-autocomplete-input').click();
|
||||
$('ul.ui-autocomplete li a:first').click(); // select first account to do the write off in
|
||||
|
||||
var $buttonReconcile = widget.$('button.o_reconcile:not(hidden)');
|
||||
|
||||
assert.equal($buttonReconcile.length, 1,
|
||||
'The reconcile button must be visible');
|
||||
|
||||
$buttonReconcile.click();
|
||||
|
||||
clientAction.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user