[MERGE] forward port branch 11.0 up to 27c017ad98
This commit is contained in:
@@ -519,7 +519,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}
|
||||
@@ -529,14 +528,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
|
||||
|
||||
@@ -358,18 +358,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)) {
|
||||
@@ -378,10 +381,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) {
|
||||
@@ -395,6 +398,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 = {
|
||||
@@ -1499,5 +1507,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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
<div class="navbar navbar-default navbar-static-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-top-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="/" class="navbar-brand logo">
|
||||
<span t-field="res_company.logo" t-options="{'widget': 'image'}" t-att-alt="'Logo of %s' % res_company.name" t-att-title="res_company.name"/>
|
||||
</a>
|
||||
|
||||
@@ -173,16 +173,8 @@
|
||||
<t t-call-assets="website.assets_editor" t-css="false" groups="website.group_website_publisher"/>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//header//div[hasclass('navbar-header')]" position="replace">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-top-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/" t-if="website" t-field="website.name">My Website</a>
|
||||
</div>
|
||||
<xpath expr="//header//a[hasclass('navbar-brand')]" position="replace">
|
||||
<a class="navbar-brand" href="/" t-if="website" t-field="website.name">My Website</a>
|
||||
</xpath>
|
||||
<xpath expr="//header//ul[@id='top_menu']/li[hasclass('divider')]" position="attributes">
|
||||
<attribute name="t-if">website.user_id != user_id</attribute>
|
||||
|
||||
+6
-3
@@ -3,11 +3,14 @@ decorator==4.0.10
|
||||
docutils==0.12
|
||||
ebaysdk==2.1.5
|
||||
feedparser==5.2.1
|
||||
gevent==1.1.2 ; sys_platform != 'win32'
|
||||
greenlet==0.4.10
|
||||
gevent==1.1.2 ; sys_platform != 'win32' and python_version < '3.7'
|
||||
gevent==1.3.4 ; sys_platform != 'win32' and python_version >= '3.7'
|
||||
greenlet==0.4.10 ; python_version < '3.7'
|
||||
greenlet==0.4.13 ; python_version >= '3.7'
|
||||
html2text==2016.9.19
|
||||
Jinja2==2.8
|
||||
lxml==3.7.1 ; sys_platform != 'win32'
|
||||
lxml==3.7.1 ; sys_platform != 'win32' and python_version < '3.7'
|
||||
lxml==4.2.3 ; sys_platform != 'win32' and python_version >= '3.7'
|
||||
lxml ; sys_platform == 'win32'
|
||||
Mako==1.0.4
|
||||
MarkupSafe==0.23
|
||||
|
||||
Reference in New Issue
Block a user