Enable Dark Mode!
how-to-add-a-branch-filter-to-accounting-reports-in-odoo-19.jpg
By: Abhijith CK

How to add a Branch Filter to Accounting Reports in Odoo 19

Technical Odoo 19 Accounting

Custom report creation in Odoo 19 is an essential skill for any developer dealing with multi-site environments. This blog will take you step-by-step through the development process of developing a custom Odoo 19 module named my_custom_module. The goal of this module is to include a custom Branch filtering feature in the Partner Ledger report and improve SQL queries for maximum efficiency in reporting.

Step 1: Module Structure and Manifest Configuration

First, we need to set up the directory structure for our module. We set up the root directory and the necessary manifest file:

  • Directory name: my_custom_module
  • Dependencies: Should depend on 'account' (Core Accounting) and 'account_reports' (Enterprise Reporting Framework).

My_custom_module

+-- __init__.py
+-- __manifest__.py
+-- models/
¦   +-- __init__.py
¦   +-- branch.py
¦   +-- account_move.py
¦   +-- account_report.py
¦   +-- account_partner_ledger.py
+-- security/
¦   +-- ir.model.access.csv
+-- data/
¦   +-- branch_data.xml
+-- views/
¦   +-- branch_views.xml
¦   +-- account_move_views.xml
¦   +-- account_report_views.xml
+-- static/
    +-- src/
        +-- xml/
            +-- branch_filter.xml

Step 2: Defining the Branch Model

Next is the process of defining our models. The `branch.branch` model will be defined within the file `models/branch.py`.

models/branch.py snippet:

class BranchBranch(models.Model):
    _name = 'branch.branch'
    _description = 'Branch'
    name = fields.Char(string='Branch Name', required=True)
    code = fields.Char(string='Branch Code', required=True)
    location = fields.Char(string='Location')
    company_id = fields.Many2one('res.company', string='Company',
                                 default=lambda self: self.env.company)
    active = fields.Boolean(string='Active', default=True)
    note = fields.Text(string='Notes')
    _code_company_uniq = models.Constraint(
        'unique(code, company_id)',
        'Branch code must be unique per company!'
    )

Step 3: Extending Invoices with Automated Defaults

The `account.move` model is extended through the addition of a Many2one relation to the `branch.branch` model. This way, we have automatic creation of branches when creating an invoice/journal entry.

models/account_move.py:

from odoo import api, models, fields

class AccountMove(models.Model):
    _inherit = 'account.move'
    branch_id = fields.Many2one('branch.branch', string='Branch',
                                 help='Branch, that is associated with this invoice')
    @api.model
    def default_get(self, fields_list):
        res = super().default_get(fields_list)
        if 'branch_id' in fields_list and not res.get('branch_id'):
            branch = self.env['branch.branch'].search([('company_id', '=', self.env.company.id)], limit=1)
            if branch:
                res['branch_id'] = branch.id
        return res

class AccountMoveLine(models.Model):
    _inherit = 'account.move.line'
    branch_id = fields.Many2one(related='move_id.branch_id', string='Branch',
                                store=True, readonly=True)

Step 4: Custom Report Options and Domain Logic

The use of Odoo's ‘account.report’ module, which is inherited by ‘models.account_report.py’, makes it possible for us to create the filtering domain and options.

models/account_report.py:

class AccountReport(models.Model):
    _inherit = 'account.report'
    def _init_options_branch(self, options, previous_options):
        previous_options = previous_options or {}
        options['branch'] = True
        previous_branch_ids = previous_options.get('branch_ids') or []
        selected_branch_ids = [int(branch) for branch in previous_branch_ids]
        selected_branches = selected_branch_ids and self.env['branch.branch'].with_context(active_test=False).search([('id', 'in', selected_branch_ids)]) or self.env['branch.branch']
        options['selected_branch_ids'] = selected_branches.mapped('name')
        options['branch_ids'] = selected_branches.ids
    def _get_options_branch_domain(self, options):
        if options.get('branch_ids'):
            branch_ids = [int(branch) for branch in options['branch_ids']]
            return Domain([('branch_id', 'in', branch_ids)])
        return Domain.TRUE
    def _get_options_domain(self, options, date_scope) -> Domain:
        domain = super()._get_options_domain(options, date_scope)
        branch_domain = self._get_options_branch_domain(options)
        return domain & branch_domain

Step 5: Extending the Partner Ledger SQL Query

Since our Partner Ledger report runs with simple SQL queries, we inherit ‘account.partner.ledger.report.handler’ to retrieve the branch name from the query itself.

models/account_partner_ledger.py:

class AccountPartnerLedgerReportHandler(models.AbstractModel):
    _inherit = 'account.partner.ledger.report.handler'
    def _get_additional_column_aml_values(self):
        res = super()._get_additional_column_aml_values()
        return SQL('%s (SELECT name FROM branch_branch WHERE id = account_move_line.branch_id) AS branch_name,', res)

Step 6: OWL XML Template & View Integration

We expand upon the reporting options model by building on the customizable filters model. In ‘static/src/xml/branch_filter.xml’:

static/src/xml/branch_filter.xml:

<templates xml:space="preserve">
    <t t-inherit="account_reports.AccountReportFiltersCustomizable" t-inherit-mode="extension">
        <xpath expr="//div[@id='filter_partner']" position="after">
            <t t-if="'branch' in controller.cachedFilterOptions">
                <div id="filter_branch" class="filter_branch">
                    <Dropdown
                        menuClass="'account_report_filter branch'"
                    >
                        <button class="btn btn-secondary">
                            <i class="fa fa-folder-open me-1"/>Branches
                        </button>
                        <t t-set-slot="content">
                            <div class="dropdown-item gap-2 align-items-center">
                                <label>Branches</label>
                                <MultiRecordSelector t-props="getMultiRecordSelectorProps('branch.branch', 'branch_ids')"/>
                            </div>
                        </t>
                    </Dropdown>
                </div>
            </t>
        </xpath>
    </t>
</templates>

For the column insertion feature, we create the column and insert it into the Partner Ledger report through the views xml:

views/account_report_views.xml:

<record id="partner_ledger_report_branch_column" model="account.report.column">
            <field name="name">Branch</field>
            <field name="expression_label">branch_name</field>
            <field name="figure_type">string</field>
            <field name="sequence">0</field>
        </record>
        <record id="account_reports.partner_ledger_report" model="account.report">
            <field name="column_ids" eval="[(4, ref('partner_ledger_report_branch_column'))]"/>
        </record>

Having completed all these procedures, start the Odoo server once again and refresh the modules list in the database. Your module can now be installed. After its installation, accessing the Partner Ledger report will bring you to the new ‘Branches’ selector on the filtering menu at the top and the new ‘Branch’ column at the very beginning of the report.

To read more about How to Add Filters in the Accounting Report Odoo 19, refer to our blog How to Add Filters in the Accounting Report Odoo 19.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



WhatsApp