diff options
| author | wan <wan@odoo.com> | 2019-08-28 13:54:41 +0000 |
|---|---|---|
| committer | wan <wan@odoo.com> | 2019-09-04 09:00:03 +0000 |
| commit | 737eaf93330b7c4e5ff16311c92f1ad17be0cf72 (patch) | |
| tree | 6f2520e675791f3af0cdd84252bab393b3a62717 | |
| parent | 16c32f0c25f82b308b50e55e2127a2e6d139f7bf (diff) | |
[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.
| -rw-r--r-- | addons/account/models/account.py | 36 | ||||
| -rw-r--r-- | addons/account/models/account_move.py | 1 | ||||
| -rw-r--r-- | addons/account/models/chart_template.py | 1 | ||||
| -rw-r--r-- | addons/account/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/account/static/src/scss/account_searchpanel.scss | 14 | ||||
| -rw-r--r-- | addons/account/views/account.xml | 1 | ||||
| -rw-r--r-- | addons/account/views/account_move_views.xml | 18 | ||||
| -rw-r--r-- | addons/account/views/account_view.xml | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/abstract_view.js | 11 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/search_panel.js | 10 | ||||
| -rw-r--r-- | odoo/addons/base/rng/search_view.rng | 1 |
11 files changed, 91 insertions, 7 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 83d9cb05c1f..f833430b4af 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -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" diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 52cf708c80e..604aae8d046 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -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', diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 86a3ca5822e..2170a3ff632 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -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): diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index 1925e05545f..f310f756c9c 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -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 diff --git a/addons/account/static/src/scss/account_searchpanel.scss b/addons/account/static/src/scss/account_searchpanel.scss new file mode 100644 index 00000000000..affa8898732 --- /dev/null +++ b/addons/account/static/src/scss/account_searchpanel.scss @@ -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; + } +} diff --git a/addons/account/views/account.xml b/addons/account/views/account.xml index 4bfc5afe7cc..5b3eff87c8e 100644 --- a/addons/account/views/account.xml +++ b/addons/account/views/account.xml @@ -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"/> diff --git a/addons/account/views/account_move_views.xml b/addons/account/views/account_move_views.xml index 770072b2cb3..a244fca68bc 100644 --- a/addons/account/views/account_move_views.xml +++ b/addons/account/views/account_move_views.xml @@ -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> diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index c5f7657c623..ed5afea3286 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -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> diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index fc8e73136d8..d581f84308a 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -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; diff --git a/addons/web/static/src/js/views/search_panel.js b/addons/web/static/src/js/views/search_panel.js index f09ef071f79..32bfda12594 100644 --- a/addons/web/static/src/js/views/search_panel.js +++ b/addons/web/static/src/js/views/search_panel.js @@ -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. diff --git a/odoo/addons/base/rng/search_view.rng b/odoo/addons/base/rng/search_view.rng index 08776e8e206..dfa6059da2c 100644 --- a/odoo/addons/base/rng/search_view.rng +++ b/odoo/addons/base/rng/search_view.rng @@ -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> |
