[FIX] account: reconciliation model support tags in widget

Activate analytic tags
Create an reconciliation model on which you set at least one tag
Create a bank statement and a line
Click on reconcile (or access the reconciliation widget by another means)

Quick Create a writeoff line
Click on the button corresponding to the reconciliation model

Before this commit:
The many2many tags was buggy, there was one empty tag, corresponding to nothing
clicking on the field triggered errors
validating the reconciliation by clicking on reconcile triggered a server traceback
in the real backend, opening the tag form view triggered an access error

This was because the data were not formatted the right way to be passed to the m2m_tag_field through
notifyChanges

After this commit, the reconciliation model's tag renders and behaves well
Validating the reconciliation works

OPW 2033350

As a side note, in the future, the reconciliation widget Model should fully implement basicModel
or not at all. Some of the heavy plumbing that the ReconciliationModel does (fetch records, data...)
is imitating what fetchRecord does

closes odoo/odoo#34838

Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
This commit is contained in:
Lucas Perais (lpe)
2019-07-16 07:22:16 +00:00
parent 1bd434da34
commit 1112ca05ab
2 changed files with 117 additions and 23 deletions
@@ -384,14 +384,7 @@ var StatementModel = BasicModel.extend({
if (context && context.active_model === 'account.journal' && context.active_ids) {
domainReconcile.push(['journal_id', 'in', [false].concat(context.active_ids)]);
}
var def_reconcileModel = this._rpc({
model: 'account.reconcile.model',
method: 'search_read',
domain: domainReconcile,
})
.then(function (reconcileModels) {
self.reconcileModels = reconcileModels;
});
var def_reconcileModel = this._loadReconciliationModel({domainReconcile: domainReconcile});
var def_account = this._rpc({
model: 'account.account',
method: 'search_read',
@@ -411,7 +404,59 @@ var StatementModel = BasicModel.extend({
return self._formatLine(self.statement.lines);
});
},
_readAnalyticTags: function (params) {
var self = this;
this.analyticTags = {};
if (!params || !params.res_ids || !params.res_ids.length) {
return $.when();
}
var fields = (params && params.fields || []).concat(['id', 'display_name']);
return this._rpc({
model: 'account.analytic.tag',
method: 'read',
args: [
params.res_ids,
fields,
],
}).then(function (tags) {
for (var i=0; i<tags.length; i++) {
var tag = tags[i];
self.analyticTags[tag.id] = tag;
}
});
},
_loadReconciliationModel: function (params) {
var self = this;
return this._rpc({
model: 'account.reconcile.model',
method: 'search_read',
domain: params.domainReconcile || [],
})
.then(function (reconcileModels) {
var analyticTagIds = [];
for (var i=0; i<reconcileModels.length; i++) {
var modelTags = reconcileModels[i].analytic_tag_ids || [];
for (var j=0; j<modelTags.length; j++) {
if (analyticTagIds.indexOf(modelTags[j]) === -1) {
analyticTagIds.push(modelTags[j]);
}
}
}
return self._readAnalyticTags({res_ids: analyticTagIds}).then(function () {
for (var i=0; i<reconcileModels.length; i++) {
var recModel = reconcileModels[i];
var analyticTagData = [];
var modelTags = reconcileModels[i].analytic_tag_ids || [];
for (var j=0; j<modelTags.length; j++) {
var tagId = modelTags[j];
analyticTagData.push([tagId, self.analyticTags[tagId].display_name])
}
recModel.analytic_tag_ids = analyticTagData;
}
self.reconcileModels = reconcileModels;
});
});
},
_loadTaxes: function(){
var self = this;
self.taxes = {};
@@ -949,7 +994,7 @@ var StatementModel = BasicModel.extend({
_formatMany2ManyTags: function (value) {
var res = [];
for (var i=0, len=value.length; i<len; i++) {
res[i] = {data: {'id': value[i][0], 'display_name': value[i][1]}};
res[i] = {'id': value[i][0], 'display_name': value[i][1]};
}
return res;
},
@@ -1290,15 +1335,7 @@ var ManualModel = StatementModel.extend({
if (company_ids) {
domainReconcile.push(['company_id', 'in', company_ids]);
}
var def_reconcileModel = this._rpc({
model: 'account.reconcile.model',
method: 'search_read',
domain: domainReconcile,
})
.then(function (reconcileModels) {
self.reconcileModels = reconcileModels;
});
var def_reconcileModel = this._loadReconciliationModel({domainReconcile: domainReconcile});
var def_taxes = this._loadTaxes();
return $.when(def_reconcileModel, def_account, def_taxes).then(function () {
@@ -136,8 +136,14 @@ var db = {
]
},
'account.analytic.tag': {
fields: {},
records: []
fields: {
id: {string: 'id', type: 'integer'},
display_name: {string: 'display_name', type: 'char'},
},
records: [
{id: 1, display_name: 'Come together'},
{id: 2, display_name: 'Right now'},
],
},
'account.bank.statement': {
fields: {},
@@ -179,7 +185,8 @@ var db = {
second_amount: {string: "Second Amount", type: 'float', digits:0, help:"Fixed amount will count as a debit if it is negative, as a credit if it is positive.", default:100.0},
second_tax_id: {string: "Second Tax", type: 'many2one', relation:'account.tax', domain:[('type_tax_use', '=', 'purchase')]},
second_analytic_account_id: {string: "Second Analytic Account", type: 'many2one', relation:'account.analytic.account'},
match_journal_ids: {string: "Journal Ids", type: 'many2many', relation: 'account.journal'}
match_journal_ids: {string: "Journal Ids", type: 'many2many', relation: 'account.journal'},
analytic_tag_ids: {string: 'Analytic tags', type: 'many2many', relation: 'account.analytic.tag'},
},
records: [
{'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 4, 'analytic_account_id': false, 'display_name': "Int\u00e9rrets", 'rule_type': 'writeoff_button', 'second_tax_id': false, 'has_second_line': false, 'journal_id': false, 'label': false, 'second_label': false, 'second_account_id': false, 'account_id': 282, 'company_id': [1, "Demo SPRL"], 'tax_id': false, 'amount_type': "fixed", 'name': "Int\u00e9rrets", 'amount': 0.0, 'second_amount': 100.0, 'match_journal_ids': []},
@@ -187,7 +194,7 @@ var db = {
{'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 5, 'analytic_account_id': false, 'display_name': "Fs bank", 'rule_type': 'writeoff_button', 'second_tax_id': false, 'has_second_line': false, 'journal_id': false, 'label': false, 'second_label': false, 'second_account_id': false, 'account_id': 284, 'company_id': [1, "Demo SPRL"], 'tax_id': false, 'amount_type': "percentage", 'name': "Fs bank", 'amount': 100.0, 'second_amount': 100.0},
{'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 8, 'analytic_account_id': false, 'display_name': "Caisse Sand.", 'rule_type': 'writeoff_button', 'second_tax_id': false, 'has_second_line': false, 'journal_id': false, 'label': "Caisse Sand.", 'second_label': false, 'second_account_id': false, 'account_id': 308, 'company_id': [1, "Demo SPRL"], 'tax_id': false, 'amount_type': "percentage", 'name': "Caisse Sand.", 'amount': 100.0, 'second_amount': 100.0, 'match_journal_ids': []},
{'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 3, 'analytic_account_id': false, 'display_name': "ATOS", 'rule_type': 'writeoff_button', 'second_tax_id': 7, 'has_second_line': true, 'journal_id': false, 'label': "ATOS Banque", 'second_label': "ATOS Frais", 'second_account_id': 286, 'account_id': 285, 'company_id': [1, "Demo SPRL"], 'tax_id': 6, 'amount_type': "percentage", 'name': "ATOS", 'amount': 97.5, 'second_amount': 2.5},
{'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 10, 'analytic_account_id': false, 'display_name': "Double", 'rule_type': 'writeoff_button', 'second_tax_id': false, 'has_second_line': true, 'journal_id': false, 'label': "Double Banque", 'second_label': "Double Frais", 'second_account_id': 286, 'account_id': 285, 'company_id': [1, "Demo SPRL"], 'tax_id': false, 'amount_type': "percentage", 'name': "Double", 'amount': 97.5, 'second_amount': 2.5, 'match_journal_ids': []},
{'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 10, 'analytic_account_id': false, 'display_name': "Double", 'rule_type': 'writeoff_button', 'second_tax_id': false, 'has_second_line': true, 'journal_id': false, 'label': "Double Banque", 'second_label': "Double Frais", 'second_account_id': 286, 'account_id': 285, 'company_id': [1, "Demo SPRL"], 'tax_id': false, 'amount_type': "percentage", 'name': "Double", 'amount': 97.5, 'second_amount': 2.5, 'match_journal_ids': [], 'analytic_tag_ids': [1,2]},
]
},
'account.reconciliation.widget': {
@@ -589,6 +596,7 @@ var ReconciliationRenderer = require('account.ReconciliationRenderer');
var demoData = require('account.reconciliation_tests.data');
var testUtils = require('web.test_utils');
var testUtilsDom = require('web.test_utils_dom');
QUnit.module('account', {
beforeEach: function () {
@@ -1768,5 +1776,54 @@ QUnit.module('account', {
clientAction.destroy();
});
QUnit.test('Reconciliation Models handle analytic tags', function (assert) {
assert.expect(6);
var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
testUtils.addMockEnvironment(clientAction, {
data: this.params.data,
mockRPC: function (route, args) {
if (args.method === 'process_bank_statement_line') {
var new_aml_dicts = args.args[1][0].new_aml_dicts;
assert.strictEqual(new_aml_dicts.length, 2);
// I personnally judge the following use case rotten, since
// the first and the second line wouldn't have the same tags
assert.deepEqual(new_aml_dicts[0].analytic_tag_ids, [[6, null, [1, 2]]]);
assert.deepEqual(new_aml_dicts[1].analytic_tag_ids, [[6, null, [2]]]);
}
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];
testUtilsDom.click(widget.$('.toggle_create'));
testUtilsDom.click(widget.$('.quick_add button:contains("Double")'));
assert.containsN(widget, '.create_analytic_tag_ids .o_field_many2manytags .badge', 2,
'Two tags are loaded');
assert.containsOnce(widget, '.create_analytic_tag_ids .o_field_many2manytags .badge:contains("Come together")',
'Tags should have a name');
assert.containsOnce(widget, '.create_analytic_tag_ids .o_field_many2manytags .badge:contains("Right now")',
'Tags should have a name');
testUtilsDom.click(widget.$('.create_analytic_tag_ids .o_field_many2manytags .badge a.o_delete:first()'));
testUtilsDom.click(widget.$('.o_reconcile'));
clientAction.destroy();
});
});
});