[IMP] account: add searchpanel on account.account and account.move.line

Task 2008956

* Add the root of an account (first 2 digits/char) as a searchable model.
* Create a custom searchpanel for this account root on account.move.line
* Add the account_root searchpanel to the account.account view too.
This commit is contained in:
wan
2019-09-04 09:00:03 +00:00
parent 16c32f0c25
commit 737eaf9333
11 changed files with 91 additions and 7 deletions
+35 -1
View File
@@ -8,7 +8,7 @@ from odoo.osv import expression
from odoo.tools.float_utils import float_round as round, float_compare
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.exceptions import UserError, ValidationError
from odoo import api, fields, models, _
from odoo import api, fields, models, _, tools
from odoo.tests.common import Form
TYPE_TAX_USE = [
@@ -225,6 +225,7 @@ class AccountAccount(models.Model):
default=lambda self: self.env.company)
tag_ids = fields.Many2many('account.account.tag', 'account_account_account_tag', string='Tags', help="Optional tags you may want to assign for custom reporting")
group_id = fields.Many2one('account.group')
root_id = fields.Many2one('account.root', compute='_compute_account_root', store=True)
opening_debit = fields.Monetary(string="Opening debit", compute='_compute_opening_debit_credit', inverse='_set_opening_debit', help="Opening debit value for this account.")
opening_credit = fields.Monetary(string="Opening credit", compute='_compute_opening_debit_credit', inverse='_set_opening_credit', help="Opening credit value for this account.")
@@ -242,6 +243,14 @@ class AccountAccount(models.Model):
if record.tax_ids:
raise UserError(_('An Off-Balance account can not have taxes'))
@api.depends('code')
def _compute_account_root(self):
# this computes the first 2 digits of the account.
# This field should have been a char, but the aim is to use it in a side panel view with hierarchy, and it's only supported by many2one fields so far.
# So instead, we make it a many2one to a psql view with what we need as records.
for record in self:
record.root_id = record.code and (ord(record.code[0]) * 1000 + ord(record.code[1])) or False
@api.model
def _search_new_account_code(self, company, digits, prefix):
for num in range(1, 10000):
@@ -543,6 +552,31 @@ class AccountGroup(models.Model):
return self.browse(group_ids).name_get()
class AccountRoot(models.Model):
_name = 'account.root'
_description = 'Account codes first 2 digits'
_auto = False
name = fields.Char()
parent_id = fields.Many2one('account.root')
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute('''
CREATE OR REPLACE VIEW %s AS (
SELECT DISTINCT ASCII(code) * 1000 + ASCII(SUBSTRING(code,2,1)) AS id,
LEFT(code,2) AS name,
ASCII(code) AS parent_id
FROM account_account WHERE code IS NOT NULL
UNION ALL
SELECT DISTINCT ASCII(code) AS id,
LEFT(code,1) AS name,
NULL::int AS parent_id
FROM account_account WHERE code IS NOT NULL
)''' % (self._table,)
)
class AccountJournalGroup(models.Model):
_name = 'account.journal.group'
_description = "Account Journal Group"
+1
View File
@@ -2142,6 +2142,7 @@ class AccountMoveLine(models.Model):
index=True, ondelete="cascade",
domain=[('deprecated', '=', False)])
account_internal_type = fields.Selection(related='account_id.user_type_id.type', string="Internal Type", store=True, readonly=True)
account_root_id = fields.Many2one(related='account_id.root_id', string="Account Root", store=True, readonly=True)
sequence = fields.Integer(default=10)
name = fields.Char(string='Label')
quantity = fields.Float(string='Quantity',
+1
View File
@@ -57,6 +57,7 @@ class AccountAccountTemplate(models.Model):
"to define chart templates that extend another and complete it with few new accounts (You don't need to define the whole structure that is common to both several times).")
tag_ids = fields.Many2many('account.account.tag', 'account_account_template_account_tag', string='Account tag', help="Optional tags you may want to assign for custom reporting")
group_id = fields.Many2one('account.group')
root_id = fields.Many2one('account.root')
@api.depends('name', 'code')
def name_get(self):
@@ -13,6 +13,8 @@ access_account_account_user,account.account user,model_account_account,base.grou
access_account_account_partner_manager,account.account partner manager,model_account_account,base.group_partner_manager,1,0,0,0
access_account_group_manager,account.group,model_account_group,account.group_account_manager,1,1,1,1
access_account_group,account.group,model_account_group,account.group_account_user,1,0,0,0
access_account_root_manager,account.root,model_account_root,account.group_account_manager,1,0,0,0
access_account_root,account.root,model_account_root,account.group_account_user,1,0,0,0
access_account_tax,account.tax,model_account_tax,account.group_account_invoice,1,0,0,0
access_account_account_template,account.account.template,model_account_account_template,account.group_account_manager,1,1,1,1
access_account_chart_template,account.chart.template,model_account_chart_template,account.group_account_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
13 access_account_account_partner_manager account.account partner manager model_account_account base.group_partner_manager 1 0 0 0
14 access_account_group_manager account.group model_account_group account.group_account_manager 1 1 1 1
15 access_account_group account.group model_account_group account.group_account_user 1 0 0 0
16 access_account_root_manager account.root model_account_root account.group_account_manager 1 0 0 0
17 access_account_root account.root model_account_root account.group_account_user 1 0 0 0
18 access_account_tax account.tax model_account_tax account.group_account_invoice 1 0 0 0
19 access_account_account_template account.account.template model_account_account_template account.group_account_manager 1 1 1 1
20 access_account_chart_template account.chart.template model_account_chart_template account.group_account_manager 1 1 1 1
@@ -0,0 +1,14 @@
.o_search_panel.account_root {
flex: 0 0 48px;
padding: 6px;
.o_search_panel_section_header {
display: none;
}
.list-group-item span.o_search_panel_label_title {
display: contents;
}
.o_search_panel_category_value header {
margin-left: 0;
padding-left: 0;
}
}
+1
View File
@@ -14,6 +14,7 @@
<link rel="stylesheet" type="text/scss" href="/account/static/src/scss/account_reconciliation.scss"/>
<link rel="stylesheet" type="text/scss" href="/account/static/src/scss/account_journal_dashboard.scss"/>
<link rel="stylesheet" type="text/scss" href="/account/static/src/scss/account_dashboard.scss"/>
<link rel="stylesheet" type="text/scss" href="/account/static/src/scss/account_searchpanel.scss"/>
<link rel="stylesheet" href="/account/static/src/scss/section_and_note_backend.scss"/>
<link rel="stylesheet" type="text/scss" href="/account/static/src/scss/account_activity.scss"/>
@@ -311,6 +311,8 @@
('name', 'ilike', self), ('ref', 'ilike', self), ('account_id', 'ilike', self), ('partner_id', 'ilike', self)]"/>
<field name="date"/>
<field name="account_id"/>
<field name="account_root_id"/>
<field name="account_internal_type"/>
<field name="partner_id"/>
<field name="journal_id"/>
<field name="move_id" string="Journal Entry" filter_domain="[
@@ -347,6 +349,20 @@
</search>
</field>
</record>
<record id="view_account_move_line_filter_with_root_selection" model="ir.ui.view">
<field name="name">account.move.line.search</field>
<field name="model">account.move.line</field>
<field name="mode">primary</field>
<field name="priority">20</field>
<field name="inherit_id" ref="account.view_account_move_line_filter"/>
<field name="arch" type="xml">
<xpath expr="//group" position="after">
<searchpanel class="account_root">
<field name="account_root_id" icon="fa-filter"/>
</searchpanel>
</xpath>
</field>
</record>
<!-- account.move (Journal Entry) -->
@@ -1056,6 +1072,7 @@
<field name="res_model">account.move.line</field>
<field name="domain">[('display_type', 'not in', ('line_section', 'line_note'))]</field>
<field name="view_id" ref="view_move_line_tree_grouped_general"/>
<field name="search_view_id" ref="view_account_move_line_filter_with_root_selection"/>
<field name="view_mode">tree,pivot,graph,form,kanban</field>
</record>
@@ -1065,6 +1082,7 @@
<field name="res_model">account.move.line</field>
<field name="domain">[('display_type', 'not in', ('line_section', 'line_note'))]</field>
<field name="view_id" ref="view_move_line_tree_grouped_partner"/>
<field name="search_view_id" ref="view_account_move_line_filter_with_root_selection"/>
<field name="view_mode">tree,pivot,graph,form,kanban</field>
</record>
+3
View File
@@ -96,6 +96,9 @@
<group expand="0" string="Group By">
<filter string="Account Type" name="accounttype" domain="" context="{'group_by':'user_type_id'}"/>
</group>
<searchpanel class="account_root">
<field name="root_id" icon="fa-filter"/>
</searchpanel>
</search>
</field>
</record>
@@ -205,11 +205,11 @@ var AbstractView = Factory.extend({
if (this.withSearchPanel) {
var spProto = this.config.SearchPanel.prototype;
var viewInfo = this.controlPanelParams.viewInfo;
var sections = spProto.computeSearchPanelParams(viewInfo, this.viewType);
if (sections) {
this.searchPanelParams.sections = sections;
var searchPanelParams = spProto.computeSearchPanelParams(viewInfo, this.viewType);
if (searchPanelParams.sections) {
this.searchPanelParams.sections = searchPanelParams.sections;
this.rendererParams.withSearchPanel = true;
spDef = Promise.resolve(cpDef).then(this._createSearchPanel.bind(this, parent));
spDef = Promise.resolve(cpDef).then(this._createSearchPanel.bind(this, parent, searchPanelParams));
}
}
@@ -283,7 +283,7 @@ var AbstractView = Factory.extend({
* @param {Widget} parent
* @returns {Promise<SearchPanel>} resolved when the searchPanel is ready
*/
_createSearchPanel: async function (parent) {
_createSearchPanel: async function (parent, params) {
var defaultValues = {};
Object.keys(this.loadParams.context).forEach((key) => {
let match = /^searchpanel_default_(.*)$/.exec(key);
@@ -299,6 +299,7 @@ var AbstractView = Factory.extend({
searchDomain: controlPanelDomain,
sections: this.searchPanelParams.sections,
state: this.searchPanelParams.state,
classes: params.classes || [],
});
this.controllerParams.searchPanel = searchPanel;
this.controllerParams.controlPanelDomain = controlPanelDomain;
@@ -99,6 +99,7 @@ var SearchPanel = Widget.extend({
this.defaultValues = params.defaultValues || {};
this.fields = params.fields;
this.model = params.model;
this.className = params.classes.concat(['o_search_panel']).join(' ');
this.searchDomain = params.searchDomain;
},
/**
@@ -157,6 +158,7 @@ var SearchPanel = Widget.extend({
*/
computeSearchPanelParams: function (viewInfo, viewType) {
var searchPanelSections;
var classes;
if (viewInfo) {
var arch = viewUtils.parseArch(viewInfo.arch);
viewType = viewType === 'list' ? 'tree' : viewType;
@@ -167,13 +169,19 @@ var SearchPanel = Widget.extend({
if (attrs.view_types) {
viewTypes = attrs.view_types.split(',');
}
if (attrs.class) {
classes = attrs.class.split(' ');
}
if (viewTypes.indexOf(viewType) !== -1) {
searchPanelSections = _processSearchPanelNode(node, viewInfo.fields);
}
}
});
}
return searchPanelSections;
return {
sections: searchPanelSections,
classes: classes,
}
},
/**
* Export the current state (categories and filters) of the searchpanel.
+1
View File
@@ -11,6 +11,7 @@
<rng:element name="searchpanel">
<rng:ref name="overload"/>
<rng:optional><rng:attribute name="view_types"/></rng:optional>
<rng:optional><rng:attribute name="class"/></rng:optional>
<rng:zeroOrMore>
<rng:ref name="field" />
</rng:zeroOrMore>