[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:
@@ -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"
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user