[FIX] account: fix various bugs in reconciliation
* partial reconcile, display, compute, process_reconciliations * more information in tooltip * add domain to filter partner
This commit is contained in:
committed by
Aaron Bohy
parent
2a40c78a8b
commit
97c6dc7792
@@ -402,9 +402,22 @@ var StatementModel = BasicModel.extend({
|
||||
*/
|
||||
togglePartialReconcile: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
var prop = _.find(line.reconciliation_proposition, {'invalid': false});
|
||||
var props = _.filter(line.reconciliation_proposition, {'invalid': false});
|
||||
if (props.length !== 1) {
|
||||
return $.Deferred().reject();
|
||||
}
|
||||
var prop = props[0];
|
||||
prop.partial_reconcile = !prop.partial_reconcile;
|
||||
return this._computeLine(line);
|
||||
if (!prop.partial_reconcile) {
|
||||
return this._computeLine(line);
|
||||
}
|
||||
return this._computeLine(line).then(function () {
|
||||
if (prop.partial_reconcile) {
|
||||
line.balance.amount = 0;
|
||||
line.balance.type = 1;
|
||||
line.mode = 'inactive';
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Change the value of the editable proposition line or create a new one.
|
||||
@@ -472,9 +485,15 @@ var StatementModel = BasicModel.extend({
|
||||
ids.push(line.id);
|
||||
values.push({
|
||||
"partner_id": line.st_line.partner_id,
|
||||
"counterpart_aml_dicts": _.map(_.filter(props, function (prop) {return !isNaN(prop.id) && !prop.already_paid;}), self._formatToProcessReconciliation),
|
||||
"payment_aml_ids": _.map(_.filter(props, function (prop) {return !isNaN(prop.id) && prop.already_paid;}), self._formatToProcessReconciliation),
|
||||
"new_aml_dicts": _.map(_.filter(props, function (prop) {return isNaN(prop.id);}), self._formatToProcessReconciliation),
|
||||
"counterpart_aml_dicts": _.map(_.filter(props, function (prop) {
|
||||
return !isNaN(prop.id) || prop.partial_reconcile;
|
||||
}), self._formatToProcessReconciliation.bind(self, line)),
|
||||
"payment_aml_ids": _.pluck(_.filter(props, function (prop) {
|
||||
return !isNaN(prop.id) && !prop.partial_reconcile;
|
||||
}), 'id'),
|
||||
"new_aml_dicts": _.map(_.filter(props, function (prop) {
|
||||
return isNaN(prop.id);
|
||||
}), self._formatToProcessReconciliation.bind(self, line)),
|
||||
});
|
||||
line.reconciled = true;
|
||||
self.valuenow++;
|
||||
@@ -514,8 +533,8 @@ var StatementModel = BasicModel.extend({
|
||||
}
|
||||
|
||||
line.reconciliation_proposition.push(prop);
|
||||
_.each(line.reconciliation_proposition, function (line) {
|
||||
line.partial_reconcile = false;
|
||||
_.each(line.reconciliation_proposition, function (prop) {
|
||||
prop.partial_reconcile = false;
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -627,13 +646,6 @@ var StatementModel = BasicModel.extend({
|
||||
'account_code': self.accounts[line.st_line.open_balance_account_id],
|
||||
};
|
||||
line.balance.type = line.balance.amount ? (line.balance.amount > 0 && line.st_line.partner_id ? 0 : -1) : 1;
|
||||
|
||||
if (line.balance.type === -1) {
|
||||
var props = _.filter(line.reconciliation_proposition, {'invalid': false});
|
||||
if (props.length === 1 && props[0].partial_reconcile) {
|
||||
line.balance.type = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -678,6 +690,7 @@ var StatementModel = BasicModel.extend({
|
||||
prop.amount = prop.debit || -prop.credit;
|
||||
prop.label = prop.name;
|
||||
prop.account_id = self._formatNameGet(prop.account_id || line.account_id);
|
||||
prop.is_partially_reconciled = prop.amount_str !== prop.total_amount_str;
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -791,7 +804,7 @@ var StatementModel = BasicModel.extend({
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
_isValid: function (prop) {
|
||||
return prop.account_id && prop.amount && prop.label && !!prop.label.length;
|
||||
return !isNaN(prop.id) || prop.account_id && prop.amount && prop.label && !!prop.label.length;
|
||||
},
|
||||
/**
|
||||
* Fetch 'account.bank.statement.line' propositions.
|
||||
@@ -804,9 +817,11 @@ var StatementModel = BasicModel.extend({
|
||||
*/
|
||||
_performMoveLine: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
var excluded_ids = _.flatten(_.map(this.lines, function (line) {
|
||||
return _.filter(_.pluck(line.reconciliation_proposition, 'id'), _.isNumber);
|
||||
}));
|
||||
var excluded_ids = _.compact(_.flatten(_.map(this.lines, function (line) {
|
||||
return _.map(line.reconciliation_proposition, function (prop) {
|
||||
return !prop.partial_reconcile && _.isNumber(prop.id) ? prop.id : null;
|
||||
});
|
||||
})));
|
||||
var filter = line.filter || "";
|
||||
var offset = line.offset;
|
||||
var limit = 6;
|
||||
@@ -821,17 +836,25 @@ var StatementModel = BasicModel.extend({
|
||||
* format the proposition to send information server side
|
||||
*
|
||||
* @private
|
||||
* @param {object} line
|
||||
* @param {object} proposition
|
||||
* @returns {object}
|
||||
*/
|
||||
_formatToProcessReconciliation: function (prop) {
|
||||
_formatToProcessReconciliation: function (line, prop) {
|
||||
var amount = prop.partial_reconcile ? -line.st_line.amount : prop.amount;
|
||||
var result = {
|
||||
'name' : prop.label,
|
||||
'debit' : prop.amount > 0 ? prop.amount : 0,
|
||||
'credit' : prop.amount < 0 ? -prop.amount : 0,
|
||||
'account_id' : prop.account_id.id,
|
||||
'journal_id' : prop.journal_id && prop.journal_id.id,
|
||||
name : prop.label,
|
||||
debit : amount > 0 ? amount : 0,
|
||||
credit : amount < 0 ? -amount : 0,
|
||||
};
|
||||
if (!isNaN(prop.id)) {
|
||||
result.counterpart_aml_id = prop.id;
|
||||
} else {
|
||||
result.account_id = prop.account_id.id;
|
||||
if (prop.journal_id) {
|
||||
result.journal_id = prop.account_id.id;
|
||||
}
|
||||
}
|
||||
if (!isNaN(prop.id)) result.counterpart_aml_id = prop.id;
|
||||
if (prop.analytic_account_id) result.analytic_account_id = prop.analytic_account_id.id;
|
||||
if (prop.tax_id) result.tax_ids = [[4, prop.tax_id.id, null]];
|
||||
@@ -970,7 +993,7 @@ var ManualModel = StatementModel.extend({
|
||||
});
|
||||
} else {
|
||||
var mv_line_ids = _.pluck(_.filter(props, function (prop) {return !isNaN(prop.id);}), 'id');
|
||||
var new_mv_line_dicts = _.map(_.filter(props, function (prop) {return isNaN(prop.id);}), self._formatToProcessReconciliation);
|
||||
var new_mv_line_dicts = _.map(_.filter(props, function (prop) {return isNaN(prop.id);}), self._formatToProcessReconciliation.bind(self, line));
|
||||
process_reconciliations.push({
|
||||
id: null,
|
||||
type: null,
|
||||
@@ -1127,9 +1150,11 @@ var ManualModel = StatementModel.extend({
|
||||
*/
|
||||
_performMoveLine: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
var excluded_ids = _.flatten(_.map(this.lines, function (line) {
|
||||
return _.filter(_.pluck(line.reconciliation_proposition, 'id'), _.isNumber);
|
||||
}));
|
||||
var excluded_ids = _.compact(_.flatten(_.map(this.lines, function (line) {
|
||||
return _.map(line.reconciliation_proposition, function (prop) {
|
||||
return !prop.partial_reconcile && _.isNumber(prop.id) ? prop.id : null;
|
||||
});
|
||||
})));
|
||||
var filter = line.filter || "";
|
||||
var offset = line.offset;
|
||||
var limit = 6;
|
||||
|
||||
@@ -194,7 +194,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
'click .accounting_view tbody .mv_line td': '_onSelectProposition',
|
||||
'click .o_reconcile_models button': '_onQuickCreateProposition',
|
||||
'click .create .add_line': '_onCreateProposition',
|
||||
'click .accounting_view .cell_right .line_info_button': '_onTogglePartialReconcile',
|
||||
'click .accounting_view .line_info_button.fa-exclamation-triangle': '_onTogglePartialReconcile',
|
||||
'click .reconcile_model_create': '_onCreateReconcileModel',
|
||||
'click .reconcile_model_edit': '_onEditReconcileModel',
|
||||
},
|
||||
@@ -290,12 +290,18 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
.attr("data-content", qweb.render('reconciliation.line.mv_line.details', {'line': line}));
|
||||
}
|
||||
|
||||
if (state.balance.amount < 0 && props.length === 1) {
|
||||
var text = !line.partial_reconcile ?
|
||||
_t("This move's amount is higher than the transaction's amount. Click to register a partial payment and keep the payment balance open."):
|
||||
_t("Undo the partial reconciliation.");
|
||||
if ((state.balance.amount !== 0 || line.partial_reconcile) && props.length === 1) {
|
||||
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);
|
||||
} 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.");
|
||||
}
|
||||
|
||||
$('<span class="do_partial_reconcile_'+(!line.partial_reconcile)+' line_info_button fa fa-exclamation-triangle"/>')
|
||||
.prependTo($line.find('.cell_right'))
|
||||
.prependTo($cell)
|
||||
.attr("data-content", text);
|
||||
}
|
||||
$props.append($line);
|
||||
@@ -353,7 +359,11 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
type: 'many2one',
|
||||
name: 'partner_id',
|
||||
value: [partnerID, partnerName]
|
||||
}]);
|
||||
}], {
|
||||
partner_id: {
|
||||
domain: [["parent_id", "=", false], "|", ["customer", "=", true], ["supplier", "=", true]]
|
||||
}
|
||||
});
|
||||
return this.model.get(recordID);
|
||||
},
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
vertical-align: top;
|
||||
padding-right: 10px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="reconciliation.line.balance">
|
||||
<tr t-if="state.balance.amount">
|
||||
<tr t-if="state.balance.amount && !(state.reconciliation_proposition[0] || {}).partial_reconcile">
|
||||
<td class="cell_action"><span class="toggle_create fa fa-play"></span></td>
|
||||
<td class="cell_account_code"><t t-esc="state.balance.account_code"/></td>
|
||||
<td class="cell_due_date"></td>
|
||||
@@ -211,7 +211,8 @@
|
||||
<tr t-if="line.partner_id"><td>Partner</td><td><t t-esc="line.partner_name"/></td></tr>
|
||||
<tr><td>Date</td><td><t t-esc="line.date"/></td></tr>
|
||||
<tr><td>Due Date</td><td><t t-esc="line.date_maturity === false ? line.date : line.date_maturity"/></td></tr>
|
||||
<tr><td><t t-if="line.is_partially_reconciled">Residual</t><t t-if="! line.is_partially_reconciled">Amount</t></td><td>
|
||||
<tr><td>Amount</td><td><t t-raw="line.total_amount_str"/><t t-if="line.total_amount_currency_str"> (<t t-esc="line.total_amount_currency_str"/>)</t></td></tr>
|
||||
<tr t-if="line.is_partially_reconciled"><td>Residual</td><td>
|
||||
<t t-raw="line.amount_str"/><t t-if="line.amount_currency_str"> (<t t-esc="line.amount_currency_str"/>)</t>
|
||||
</td></tr>
|
||||
<tr class="one_line_info" t-if='line.already_paid'>
|
||||
|
||||
@@ -7,14 +7,16 @@ var data = {
|
||||
id: {string: "ID", type: 'integer'},
|
||||
display_name: {string: "Displayed name", type: 'char'},
|
||||
image: {string: "image", type: 'integer'},
|
||||
customer: {string: "customer", type: 'boolean'},
|
||||
supplier: {string: "supplier", type: 'boolean'},
|
||||
},
|
||||
records: [
|
||||
{id: 1, display_name: "partner 1", image: 'AAA'},
|
||||
{id: 2, display_name: "partner 2", image: 'BBB'},
|
||||
{id: 3, display_name: "partner 3", image: 'CCC'},
|
||||
{id: 4, display_name: "partner 4", image: 'DDD'},
|
||||
{id: 8, display_name: "Agrolait", image: 'EEE'},
|
||||
{id: 12, display_name: "Camptocamp", image: 'FFF'},
|
||||
{id: 1, display_name: "partner 1", image: 'AAA', customer: true},
|
||||
{id: 2, display_name: "partner 2", image: 'BBB', customer: true},
|
||||
{id: 3, display_name: "partner 3", image: 'CCC', customer: true},
|
||||
{id: 4, display_name: "partner 4", image: 'DDD', customer: true},
|
||||
{id: 8, display_name: "Agrolait", image: 'EEE', customer: true},
|
||||
{id: 12, display_name: "Camptocamp", image: 'FFF', supplier: true},
|
||||
],
|
||||
mark_as_reconciled: function () {
|
||||
return $.when();
|
||||
@@ -132,14 +134,15 @@ var data = {
|
||||
return $.when(auto_reconciliation);
|
||||
},
|
||||
process_reconciliations: function (args) {
|
||||
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) {
|
||||
return data.mv_line_ids.indexOf(mv_line.id) === -1;
|
||||
});
|
||||
}
|
||||
var datas = args[1];
|
||||
var ids = _.flatten(_.pluck(_.pluck(datas, 'counterpart_aml_dicts'), 'counterpart_aml_id'));
|
||||
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) {
|
||||
return ids.indexOf(mv_line.id) === -1;
|
||||
});
|
||||
}
|
||||
return $.when();
|
||||
},
|
||||
@@ -554,11 +557,11 @@ QUnit.module('account', {
|
||||
|
||||
|
||||
QUnit.test('Reconciliation basic data', function (assert) {
|
||||
assert.expect(20);
|
||||
assert.expect(13);
|
||||
|
||||
var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
|
||||
testUtils.addMockEnvironment(clientAction, {
|
||||
'data': this.params.data,
|
||||
'data': this.params.data
|
||||
});
|
||||
|
||||
clientAction.appendTo($('#qunit-fixture'));
|
||||
@@ -591,24 +594,57 @@ QUnit.module('account', {
|
||||
clientAction.widgets[3].$('.accounting_view thead td:first').trigger('click');
|
||||
assert.strictEqual(clientAction.widgets[3].$el.data('mode'), 'create', "should switch to 'create' mode instead 'match' mode when 'match' mode is empty");
|
||||
|
||||
widget = clientAction.widgets[2];
|
||||
|
||||
assert.notOk( widget.$('.cell_right .line_info_button').length, "should not display the partial reconciliation alert");
|
||||
assert.ok( widget.$('button.btn-primary:not(hidden)').length, "should display the reconcile button");
|
||||
widget.$('.accounting_view thead td:first').trigger('click');
|
||||
widget.$('.match td.cell_label:first').trigger('click');
|
||||
assert.ok( widget.$('.text-danger:not(hidden)').length, "should display counterpart alert");
|
||||
widget.$('.accounting_view tbody td.cell_label:first').trigger('click');
|
||||
assert.ok( widget.$('.text-danger:not(hidden)').length, "should display counterpart alert");
|
||||
assert.strictEqual(widget.$('.cell_right .line_info_button').length, 1, "should display a partial reconciliation alert");
|
||||
widget.$('.cell_right .line_info_button').trigger('click');
|
||||
assert.ok(widget.$('.cell_right .line_info_button').hasClass('do_partial_reconcile_false'), "should display the partial reconciliation information");
|
||||
assert.ok( widget.$('button.btn-default:not(hidden)').length, "should display the validate button");
|
||||
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('Reconciliation partial', function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
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, [
|
||||
[6],
|
||||
[{
|
||||
partner_id: false,
|
||||
counterpart_aml_dicts:[{
|
||||
name: "BNK1/2017/0002: SUPP.OUT/2017/0002",
|
||||
debit: 32.58,
|
||||
credit: 0,
|
||||
counterpart_aml_id: 392
|
||||
}],
|
||||
payment_aml_ids: [],
|
||||
new_aml_dicts: []
|
||||
}]
|
||||
], "should call process_reconciliations with partial reconcile values");
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
|
||||
clientAction.appendTo($('body'));
|
||||
|
||||
var widget = clientAction.widgets[1];
|
||||
|
||||
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, 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, 1, "should display a partial reconciliation alert");
|
||||
assert.ok(widget.$('.accounting_view .cell_left .line_info_button').hasClass('do_partial_reconcile_false'), "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'), "inactive", "should be inactive mode");
|
||||
widget.$('button.btn-default:not(hidden)').trigger('click');
|
||||
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Reconciliation title', function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user