Skip to content

Unleash Powerful Insights: Master the Odoo Purchase Report Wizard in 5 Key Steps!

odoo purchase report wizard

Are you struggling to gain precise, actionable insights from your Odoo purchasing data? Do you find yourself sifting through countless purchase orders, wishing for a more efficient way to track and analyze your procurement activities, especially those managed by specific document managers or buyers? If so, you’ve landed in the right place!

Odoo, as a powerful ERP system, offers robust capabilities, but sometimes, custom reporting is key to unlocking its full potential. This guide will walk you through building and utilizing a transformative tool: the Odoo Purchase Report Wizard. This powerful utility empowers you to filter purchase orders based on selected document managers, providing immediate, focused reports that drive smarter decisions.

We’ll cover the essential steps to develop this custom wizard, from setting up your Odoo module to defining the wizard’s logic and integrating it seamlessly into your system. Get ready to supercharge your procurement analysis and achieve unprecedented clarity in your purchasing processes!

Why an Odoo Purchase Report Wizard is Essential for Your Business

In today’s fast-paced business environment, quick access to accurate data is paramount. A standard list view of purchase orders might give you a general overview, but it often falls short when you need to answer specific questions like:

  • “How many purchase orders did ‘X’ document manager handle last quarter?”
  • “What was the total value of purchases overseen by our senior procurement team?”
  • “Are there any bottlenecks related to specific buyers?”

Without an Odoo Purchase Report Wizard, answering these questions typically involves complex filtering, exporting data to spreadsheets, and manual analysis – a time-consuming and error-prone process. This wizard, however, transforms that tedious task into a quick, intuitive operation directly within Odoo.

Key Benefits of Implementing This Reporting Utility:

  • Data-Driven Decision Making: Get instant reports on specific buyers’ procurement activities.
  • Enhanced Efficiency: Automate data extraction, saving valuable time and resources.
  • Improved Accountability: Easily track performance and workload distribution among document managers.
  • Cost Optimization: Identify purchasing trends and potential areas for savings by analyzing buyer-specific data.
  • User-Friendly Interface: A simple pop-up wizard makes reporting accessible to all authorized users.

Let’s dive into the technical details and build this invaluable tool step-by-step.

Building Your Custom Odoo Module (Foundational Steps)

Before we construct our Odoo Purchase Report Wizard, we need a proper module structure. If you already have a custom module set up, you can integrate these files into it. Otherwise, follow these foundational steps:

  1. Create Your Custom Module Directory:
    Begin by creating a new folder for your custom Odoo module. For instance, you could name it custom_reports_module.

  2. Initialize the Module:
    Inside custom_reports_module, create an empty file named __init__.py. This tells Odoo that this is a Python package.

  3. Define the Module Manifest:
    Also inside custom_reports_module, create a __manifest__.py file. This file contains crucial metadata about your module.

{
    'name': 'Custom Purchase Reports & Enhancements',
    'version': '1.0',
    'depends': ['purchase', 'stock', 'account', 'base'], # Essential dependencies for features covered
    'data': [
        'security/ir.model.access.csv', # Required for access rights
        'views/equipment_category_view.xml', # Example of other views
        'views/product_template_view.xml', # For product enhancements
        'views/purchase_order_line_view.xml', # For purchase line enhancements
        'views/res_partner_view.xml', # For partner/vendor enhancements
        'views/purchase_report_wizard_view.xml', # Our wizard's view!
        'reports/purchase_order_report_inherit.xml', # For custom report modifications
        'views/sale_order_view.xml', # For sales order partner filtering
        'views/account_move_view.xml', # For invoice partner quick create
    ],
    'installable': True,
    'application': True,
    'auto_install': False,
    'license': 'LGPL-3', # It's good practice to define a license
}

Note: We’ve included dependencies and data files for all enhancements discussed in this comprehensive guide, ensuring all parts work seamlessly.

  1. Create Necessary Subdirectories:
    Inside your custom_reports_module directory, create the following subdirectories: models, views, security, and reports.

  2. Initialize Python Files:
    Inside the models directory, create an empty __init__.py file. This will hold references to your Python models.

Step-by-Step: Implementing the Odoo Purchase Report Wizard

Now, let’s focus on the core of our solution: the wizard itself.

1. Defining the Wizard Model

Wizards in Odoo are typically transient models, meaning their data is not permanently stored in the database. They are perfect for temporary input forms like ours.

File: models/purchase_report_wizard.py

from odoo import models, fields, api
from odoo.exceptions import UserError

class PurchaseReportWizard(models.TransientModel):
    _name = 'purchase.report.wizard'
    _description = 'Purchase Order Report Wizard'

    # Field to select the document manager (internal user)
    user_id = fields.Many2one(
        'res.users',
        string='Document Manager',
        domain=[('share', '=', False)], # Ensures only internal users are selectable
        help="Select a document manager to filter purchase orders."
    )

    def action_show_orders(self):
        """
        Action method to display purchase orders based on the selected document manager.
        """
        self.ensure_one() # Ensures the method is called on a single record

        if not self.user_id:
            raise UserError("Please select a Document Manager to generate the report.")

        # Prepare the action to open the purchase order tree view
        action = {
            'type': 'ir.actions.act_window',
            'name': 'Filtered Purchase Orders by Document Manager',
            'res_model': 'purchase.order',
            'view_mode': 'tree,form',
            'domain': [('user_id', '=', self.user_id.id)], # Filter by the selected user
            'context': {'search_default_my_purchases': 1}, # Optional: apply a default filter
            'help': "Displays all purchase orders associated with the selected document manager."
        }
        return action

Explanation:

  • models.TransientModel: Defines this as a temporary model.
  • user_id: A Many2one field linking to res.users. The domain [('share', '=', False)] is crucial here; it restricts the selection to only internal Odoo users, preventing portal or public users from appearing in the list.
  • action_show_orders: This method is triggered when the “Show Orders” button is clicked. It constructs an ir.actions.act_window dictionary, which instructs Odoo to open a new view. The domain [('user_id', '=', self.user_id.id)] is the magic that filters the purchase.order records to only those associated with the chosen user_id.

2. Defining the Wizard View

Next, we need the XML view for our wizard, which provides the user interface.

File: views/purchase_report_wizard_view.xml

<odoo>
    <data>
        <!-- Form view for the Purchase Report Wizard -->
        <record model="ir.ui.view" id="purchase_report_wizard_form_view">
            <field name="name">purchase.report.wizard.form</field>
            <field name="model">purchase.report.wizard</field>
            <field name="arch" type="xml">
                <form string="Purchase Order Report">
                    <group>
                        <field name="user_id" required="1"/> <!-- Ensure a user is selected -->
                    </group>
                    <footer>
                        <!-- Button to trigger the 'action_show_orders' method -->
                        <button name="action_show_orders" string="Show Orders" type="object" class="btn-primary"
                                help="Click to view purchase orders managed by the selected user."/>
                        <!-- Standard cancel button -->
                        <button string="Cancel" class="btn-secondary" special="cancel"/>
                    </footer>
                </form>
            </field>
        </record>

        <!-- Window action to open the wizard -->
        <record model="ir.actions.act_window" id="purchase_report_wizard_action">
            <field name="name">Document Manager Report</field>
            <field name="res_model">purchase.report.wizard</field>
            <field name="view_mode">form</field>
            <field name="target">new</field> <!-- Opens the wizard in a new modal window -->
            <field name="help" type="html">
                <p class="o_view_nocontent_smiling_face">
                    Generate a report of purchase orders filtered by document manager.
                </p>
            </field>
        </record>

        <!-- Menu item to access the wizard -->
        <menuitem id="menu_purchase_report_wizard"
                  name="Document Manager Report"
                  parent="purchase.menu_purchase_reporting" <!-- Places it under Purchase -> Reporting -->
                  action="purchase_report_wizard_action"
                  sequence="10"
                  groups="purchase.group_purchase_user" /> <!-- Restrict access to purchase users -->

    </data>
</odoo>

Explanation:

  • ir.ui.view: Defines the form layout for our wizard.
  • ir.actions.act_window: This action tells Odoo how to open our wizard. target="new" is vital, as it makes the wizard pop up as a modal dialog, providing a better user experience.
  • menuitem: This creates a menu entry for our wizard. We’ve placed it under the “Purchase -> Reporting” menu (parent="purchase.menu_purchase_reporting"), making it easily discoverable for procurement teams. The groups attribute ensures only relevant users can access it.

3. Configuring Access Rights for the Wizard

Security is paramount in Odoo. We need to define who can access and use our Odoo Purchase Report Wizard.

File: security/ir.model.access.csv

Add the following line to your ir.model.access.csv file (or create it if it doesn’t exist).

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_purchase_report_wizard,access_purchase_report_wizard,model_purchase_report_wizard,purchase.group_purchase_user,1,1,1,1

Explanation:

  • This line grants read, write, create, and unlink permissions on the purchase.report.wizard model to users belonging to the purchase.group_purchase_user group (typically, internal Odoo users who work with purchase orders). This ensures only authorized personnel can generate these reports.

4. Registering Python Files (if not already done)

Ensure your Python models for the wizard are registered in your main __init__.py file.

File: models/__init__.py

from . import purchase_report_wizard
# from . import equipment_category (if you're following along with other context elements)
# from . import res_partner
# from . import product_template
# from . import purchase_order
# from . import sale_order
# from . import account_move

Installing and Testing Your Odoo Purchase Report Wizard

  1. Update Module List: In Odoo, activate Developer Mode. Go to the Apps menu, click “Update Apps List”.
  2. Install/Upgrade Your Module: Search for your Custom Purchase Reports & Enhancements module and click “Install” (if new) or “Upgrade” (if you’ve made changes).
  3. Access the Wizard: Navigate to Purchases -> Reporting -> Document Manager Report.
  4. Test the Wizard: A new pop-up window (the wizard) should appear. Select a “Document Manager” from the dropdown and click “Show Orders.” Odoo should then redirect you to a filtered list of purchase orders associated with that specific user.

Congratulations! You’ve successfully implemented your first Odoo Purchase Report Wizard. This analytical tool will significantly streamline your procurement data analysis.


Beyond the Wizard: Enhancing Odoo for Comprehensive Procurement Management

While the Odoo Purchase Report Wizard is a fantastic start, the original context provides several other valuable enhancements that can further optimize your Odoo instance. Let’s integrate some of these to create a truly robust system.

1. Enhancing Vendor Profiles for Better Procurement Control

Managing vendors effectively is crucial. We can add fields to the res.partner model (Odoo’s contact/partner model) to gain more control over vendor-specific settings and product preferences.

File: models/res_partner.py

from odoo import models, fields, api
import re
from odoo.exceptions import ValidationError

class ResPartner(models.Model):
    _inherit = 'res.partner'

    # Boolean to enable/disable product filtering for this vendor
    filter_product_category = fields.Boolean(
        string='Filter Products by Category',
        default=False,
        help="If checked, only 'Preferred Products' will be available for selection in purchase order lines for this vendor."
    )
    # Many2many field for preferred products
    preferred_product_ids = fields.Many2many(
        'product.template',
        string='Preferred Products',
        help="Specify products that are typically sourced from this vendor."
    )
    # Vendor Priority selection field
    priority = fields.Selection([
        ('low', 'Low'),
        ('medium', 'Medium'),
        ('high', 'High'),
        ('critical', 'Critical'),
        ('strategic', 'Strategic')
    ], string='Vendor Priority', default='medium', help="Assign a priority level to this vendor.")

    # ... (mobile number validation methods will go here, see next section)

File: views/res_partner_view.xml

<odoo>
    <data>
        <!-- Extend Partner Form View to add new fields -->
        <record model="ir.ui.view" id="res_partner_product_category_form">
            <field name="name">res.partner.product.category.form</field>
            <field name="model">res.partner</field>
            <field name="inherit_id" ref="base.view_partner_form"/>
            <field name="arch" type="xml">
                <xpath expr="//field[@name='vat']" position="after">
                    <group string="Vendor Specifics" col="2">
                        <field name="filter_product_category"/>
                        <!-- Display preferred_product_ids only if filter_product_category is true -->
                        <field name="preferred_product_ids" widget="many2many_tags"
                               attrs="{'invisible': [('filter_product_category', '=', False)]}"/>
                        <field name="priority"/>
                    </group>
                </xpath>
            </field>
        </record>

        <!-- Extend Partner Search View to add priority group by/filter -->
        <record model="ir.ui.view" id="res_partner_priority_search">
            <field name="name">res.partner.priority.search</field>
            <field name="model">res.partner</field>
            <field name="inherit_id" ref="base.view_res_partner_search_filter"/>
            <field name="arch" type="xml">
                <xpath expr="//filter[@name='supplier']" position="after">
                    <separator/>
                    <filter string="Priority" name="priority" domain="[]" context="{'group_by':'priority'}"/>
                </xpath>
            </field>
        </record>
    </data>
</odoo>

2. Dynamic Product Filtering in Purchase Order Lines

Building on the vendor profile, we can make the product selection in purchase order lines dynamic, filtering products based on the selected vendor’s preferred items if filter_product_category is enabled.

File: models/purchase_order.py (Add these methods to your PurchaseOrderLine class)

from odoo import models, fields, api

class PurchaseOrder(models.Model):
    _inherit = 'purchase.order'

    # Restrict vendor selection to only individual suppliers
    partner_id = fields.Many2one(
        'res.partner', string='Vendor',
        domain=[('is_company', '=', False), ('supplier_rank', '>', 0)], # Individuals who are suppliers
        change_default=True, required=True, tracking=True,
        help="Select the individual vendor for this purchase order."
    )

class PurchaseOrderLine(models.Model):
    _inherit = 'purchase.order.line'

    # Automatically fetch equipment category from product
    equipment_category_id = fields.Many2one(
        'equipment.category',
        string='Equipment Category',
        related='product_id.equipment_category_id',
        readonly=True,
        store=True, # Store the value for better performance in reports
        help="The equipment category associated with the selected product."
    )

    @api.onchange('product_id')
    def _onchange_product_id_set_equipment_category(self):
        """ Automatically set equipment category when product is selected. """
        if self.product_id:
            self.equipment_category_id = self.product_id.equipment_category_id

    @api.onchange('partner_id')
    def _onchange_partner_id_filter_products(self):
        """
        Dynamically filters available products based on the selected vendor's preferences.
        """
        if self.order_id.partner_id and self.order_id.partner_id.filter_product_category:
            # If the vendor has product filtering enabled, restrict to preferred products
            product_domain = [('id', 'in', self.order_id.partner_id.preferred_product_ids.ids)]
            return {'domain': {'product_id': product_domain}}
        else:
            # Otherwise, show all available products
            return {'domain': {'product_id': []}}

Note: The partner_id domain in PurchaseOrder is an important enhancement that ensures you only select individual vendors, not companies. This is a common requirement in many business scenarios.

File: views/purchase_order_line_view.xml (For equipment_category_id in purchase lines)

<odoo>
    <data>
        <!-- Extend Purchase Order Line Form View -->
        <record model="ir.ui.view" id="purchase_order_line_equipment_category_form">
            <field name="name">purchase.order.line.equipment.category.form.inherit</field>
            <field name="model">purchase.order.line</field>
            <field name="inherit_id" ref="purchase.purchase_order_line_form2"/>
            <field name="arch" type="xml">
                <xpath expr="//field[@name='product_id']" position="after">
                    <field name="equipment_category_id" invisible="1"/> <!-- Can be visible or invisible -->
                </xpath>
            </field>
        </record>
    </data>
</odoo>

3. Enforcing Mobile Number Validation

Ensuring data quality is vital. This adds a constraint to res.partner to validate mobile numbers (10 digits, auto-prefix with +). This uses Python’s re module for robust string manipulation.

File: models/res_partner.py (Add these methods to your ResPartner class)

    # ... (existing fields)

    mobile = fields.Char(string='Mobile', help="Mobile number, expected to be 10 digits.")

    def _format_mobile_number(self, mobile):
        """ Cleans and formats the mobile number to '+<10_digits>'. """
        if mobile:
            # Remove spaces, hyphens, plus signs, and parentheses
            clean_mobile = re.sub(r'[\s\-\+\(\)]', '', mobile)
            if len(clean_mobile) == 10: # Only add '+' if it's a valid 10-digit number
                return '+' + clean_mobile
            return clean_mobile # Return cleaned but unformatted if not 10 digits
        return mobile

    @api.constrains('mobile')
    def _check_mobile_number_format(self):
        """ Validates that the mobile number is exactly 10 digits (after cleaning). """
        for record in self:
            if record.mobile:
                clean_mobile = re.sub(r'[\s\-\+\(\)]', '', record.mobile)
                if len(clean_mobile) != 10:
                    raise ValidationError("Mobile number must be exactly 10 digits (excluding formatting characters).")

    @api.model
    def create(self, vals):
        """ Overrides create to format mobile number on creation. """
        if 'mobile' in vals and vals['mobile']:
            vals['mobile'] = self._format_mobile_number(vals['mobile'])
        return super(ResPartner, self).create(vals)

    def write(self, vals):
        """ Overrides write to format mobile number on update. """
        if 'mobile' in vals and vals['mobile']:
            vals['mobile'] = self._format_mobile_number(vals['mobile'])
        return super(ResPartner, self).write(vals)

4. Customizing Purchase Order Reports

Reports are the ultimate output of data. Modifying the standard Purchase Order report to include more details like a sequence number, total quantity, and the purchase representative/team enhances its utility.

File: reports/purchase_order_report_inherit.xml

<odoo>
    <data>
        <template id="report_purchase_order_document_extended" inherit_id="purchase.report_purchaseorder_document">
            <!-- Add Purchase Representative and Team after Payment Terms -->
            <xpath expr="//div[@id='informations']/div[@name='payment_term']" position="after">
                <div t-if="o.user_id" class="col-auto">
                    <strong>Purchase Representative:</strong> <span t-field="o.user_id.name"/>
                </div>
                <div t-if="o.user_id.sale_team_id" class="col-auto">
                    <strong>Purchase Team:</strong> <span t-field="o.user_id.sale_team_id.name"/>
                </div>
            </xpath>

            <!-- Add sequence number column header -->
            <xpath expr="//table[@class='table table-sm o_main_table']/thead/tr/th[1]" position="before">
                <th class="text-left">#</th>
            </xpath>

            <!-- Add sequence number for each line item -->
            <xpath expr="//table[@class='table table-sm o_main_table']/tbody/tr/td[1]" position="before">
                <td class="text-left"><span t-esc="line_index + 1"/></td>
            </xpath>

            <!-- Add Total Quantity at the end of the item lines section -->
            <xpath expr="//table[@class='table table-sm o_main_table']/tfoot" position="after">
                <div class="clearfix">
                    <div id="total" class="row">
                        <div t-if="o.order_line" class="col-6 offset-6">
                            <table class="table table-sm">
                                <tr class="border-black o_total">
                                    <td><strong>Total Quantity:</strong></td>
                                    <td class="text-right">
                                        <span t-esc="sum(line.product_qty for line in o.order_line)"/> Units
                                    </td>
                                </tr>
                            </table>
                        </div>
                    </div>
                </div>
            </xpath>
        </template>
    </data>
</odoo>

5. Streamlining Partner Selection in Sales Orders

Similar to purchase orders, you might want to restrict customer selection in sales orders to only individuals.

File: models/sale_order.py (Create this file if it doesn’t exist, and add to models/__init__.py)

from odoo import models, fields, api

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    partner_id = fields.Many2one(
        'res.partner', string='Customer',
        domain=[('is_company', '=', False), ('customer_rank', '>', 0)], # Individuals who are customers
        change_default=True, index=True, tracking=True, required=True,
        help="Select the individual customer for this sales order."
    )

6. Removing Quick Create/Edit Options for Partners

To maintain data integrity and prevent unintended contact creation, you can disable the “Create” and “Quick Create” options directly from Many2one fields, specifically in customer/vendor fields in invoices.

File: views/account_move_view.xml

<odoo>
    <data>
        <record model="ir.ui.view" id="account_invoice_form_remove_create">
            <field name="name">account.invoice.form.remove.create.inherit</field>
            <field name="model">account.move</field>
            <field name="inherit_id" ref="account.view_move_form"/>
            <field name="arch" type="xml">
                <!-- Apply to customer field in customer invoices -->
                <xpath expr="//field[@name='partner_id']" position="attributes">
                    <attribute name="options">{'no_create': True, 'no_quick_create': True, 'no_edit': True}</attribute>
                </xpath>
            </field>
        </record>
    </data>
</odoo>

Note: The no_edit option is also added to prevent editing directly from the Many2one field, enforcing edits to happen on the partner record itself.

Advanced Insights: Python & SQL for Odoo Developers

The original tutorial also delved into fundamental Python and SQL exercises, which are critical skills for any Odoo developer. These showcase how to manipulate data and extract insights beyond Odoo’s ORM. While we won’t detail the full code here, understanding these concepts is key to advanced reporting and data handling within Odoo, complementing tools like the Odoo Purchase Report Wizard.

  • Python for Data Manipulation: Examples included removing keys from dictionaries and sorting lists, vital for processing data before display or storage. Python’s flexibility allows for complex logic within Odoo’s models. For more on Python’s built-in functions, check the Python Documentation.
  • SQL for Direct Database Queries: Fetching active vendors, filtering purchase orders by date, finding top-selling products, and identifying customers with high-value orders directly using SQL queries demonstrate a deeper level of data extraction. This is particularly useful for complex, aggregate reporting or external integrations. Learn more about SQL on W3Schools SQL Tutorial.

Conclusion: Empower Your Procurement with Odoo Purchase Report Wizard

By following this comprehensive guide, you’ve not only learned to build a powerful Odoo Purchase Report Wizard but also gained insights into several other key Odoo customizations that enhance procurement, vendor management, and overall data quality. This analytical tool will transform how you interact with your purchasing data, enabling faster, more informed decisions and significantly boosting your operational efficiency.

Start leveraging the full power of Odoo today. Implement this wizard, experiment with the additional enhancements, and discover new levels of control and clarity in your business processes. Happy Odoo-ing!


Focus Keyword: Odoo Purchase Report Wizard
SEO Title: Unleash Powerful Insights: Master the Odoo Purchase Report Wizard in 5 Key Steps!
SEO Meta Description: Unlock powerful insights into your purchasing data with the Odoo Purchase Report Wizard. Learn step-by-step how to build and utilize this essential tool for efficient procurement management and data analysis.
URL: yourdomain.com/odoo-purchase-report-wizard-guide


Discover more from teguhteja.id

Subscribe to get the latest posts sent to your email.

Leave a Reply

WP Twitter Auto Publish Powered By : XYZScripts.com