Enable Dark Mode!
how-to-create-a-dynamic-report-in-odoo-18.jpg
By: Ashwin T

How to Create a Dynamic Report in Odoo 18

Technical Odoo 18

In Odoo 18, dynamic reports are powerful and flexible tools that give detailed look into different areas of business. Unlike static reports that show fixed data, dynamic reports allow users to interact with and customize the data in real time by adjusting filters, choosing specific metrics, and modifying visualizations. This makes it easier to explore the data from various perspectives and uncover insights that support smarter, faster decision-making .

Let's walk through the process of creating a dynamic report in Odoo 18.

To get started, create a new menu by defining it in an XML file located in the views directory, typically named views.xml.

Module structure

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
   <!-- Purchase report action and menu -->
   <record id="dynamic_purchase_report_action" model="ir.actions.client">
       <field name="name">Purchase Report</field>
       <field name="tag">purchase_report</field>
   </record>
   <menuitem id="dynamic_purchase_report_menu"
             name=" Dynamic Purchase Report"
             parent="purchase.purchase_report"
             action="dynamic_purchase_report_action"/>
</odoo>

In this configuration, the "Dynamic Purchase Report" menu is added under the Reporting section of the Purchase module.

How to Create a Dynamic Report in Odoo 18-cybrosys

In this step, a record named "Purchase Report" is created under ir.actions.client. The tag field is set to "purchase_report", which matches the widget's tag used to trigger the action when the corresponding menu item is clicked.Next, define the JavaScript file within the static/src/js directory. This file will handle the client-side logic and behavior for the dynamic report interface.

/** @odoo-module */
const { Component } = owl;
import { registry } from "@web/core/registry";
import { download } from "@web/core/network/download";
import { useService } from "@web/core/utils/hooks";
import { useRef, useState } from "@odoo/owl";
import { BlockUI } from "@web/core/ui/block_ui";
const actionRegistry = registry.category("actions");
import { uiService } from "@web/core/ui/ui_service";
// Extending components for adding purchase report class
class PurchaseReport extends Component {
   async setup() {
       super.setup(...arguments);
       this.uiService = useService('ui');
       this.initial_render = true;
       this.orm = useService('orm');
       this.action = useService('action');
       this.start_date = useRef('date_from');
       this.end_date = useRef('date_to');
       this.order_by = useRef('order_by');
       this.state = useState({
           order_line: [],
           data: null,
           order_by : 'report_by_order',
           wizard_id : []
       });
       this.load_data();
   }
   async load_data(wizard_id = null) {
       /**
       * Loads the data for the purchase report.
       */
       let move_lines = ''
       try {
           if(wizard_id == null){
               this.state.wizard_id = await this.orm.create("dynamic.purchase.report",[{}]);
           }
           this.state.data = await this.orm.call("dynamic.purchase.report", "purchase_report", [this.state.wizard_id]);
           if (Array.isArray(this.state.data)) {
               this.state.order_line = this.state.data.map(item => {
                   return item;
               });
           } else if (typeof this.state.data === 'object' && this.state.data !== null) {
               this.state.order_line = this.state.data.report_lines;
           }
       }
       catch (el) {
           window.location.href;
       }
   }
   async applyFilter(ev) {
       let filter_data = {}
       this.state.order_by = this.order_by.el.value
       filter_data.date_from = this.start_date.el.value
       filter_data.date_to = this.end_date.el.value
       filter_data.report_type = this.order_by.el.value
       let data = await this.orm.write("dynamic.purchase.report",this.state.wizard_id, filter_data);
       this.load_data(this.state.wizard_id)
   }
   viewPurchaseOrder(ev) {
       return this.action.doAction({
           type: "ir.actions.act_window",
           res_model: 'purchase.order',
           res_id: parseInt(ev.target.id),
           views: [[false, "form"]],
           target: "current",
       });
   }
   async print_xlsx() {
       /**
       * Generates and downloads an XLSX report for the purchase orders.
       */
       var data = this.state.data
       var action = {
         'data': {
            'model': 'dynamic.purchase.report',
            'options': JSON.stringify(data['orders']),
            'output_format': 'xlsx',
            'report_data': JSON.stringify(data['report_lines']),
            'report_name': 'Purchase Report',
            'dfr_data': JSON.stringify(data),
         },
      };
       this.uiService.block();
       await download({
           url: '/purchase_dynamic_xlsx_reports',
           data: action.data,
           complete: this.uiService.unblock(),
           error: (error) => this.call('crash_manager', 'rpc_error', error),
         });
   }
   async printPdf(ev) {
       /**
       * Generates and displays a PDF report for the purchase orders.
       *
       * @param {Event} ev - The event object triggered by the action.
       * @returns {Promise} - A promise that resolves to the result of the action.
       */
       ev.preventDefault();
       var self = this;
       var action_title = self.props.action.display_name;
       return self.action.doAction({
           'type': 'ir.actions.report',
           'report_type': 'qweb-pdf',
           'report_name': 'purchase_report_generator.purchase_order_report',
           'report_file': 'purchase_report_generator.purchase_order_report',
           'data': {
               'report_data': this.state.data
           },
           'context': {
             'active_model': 'purchase.report',
            'landscape': 1,
            'purchase_order_report': true
         },
         'display_name': 'Purchase Order',
       });
   }
}
PurchaseReport.template = 'PurchaseReport';
actionRegistry.add("purchase_report", PurchaseReport);

In this section, we extend the AbstractAction class to display the PurchaseReport model. The load_data function is called within the start method and uses an RPC call to retrieve data from a corresponding Python function. The data returned is then passed to the PurchaseReport model and rendered through the table_view_pr class, which is part of the main PurchaseReport component.

To support the RPC call from the JavaScript file, a corresponding QWeb model is defined in a Python file located in the models directory, such as models/filename.py.

import json
from odoo import http
from odoo.http import content_disposition, request
from odoo.tools import html_escape

class TBXLSXReportController(http.Controller):
   """ This endpoint generates and provides an XLSX report in response to an
   HTTP POST request. The function uses the provided data to create the
   report and returns it as an XLSX file. """
   @http.route('/purchase_dynamic_xlsx_reports', type='http', auth='user',
               methods=['POST'], csrf=False)
   def get_report_xlsx(self, model, options, output_format, report_data,
                       report_name, dfr_data, **kw):
       """ Endpoint for getting xlsx report """
       uid = request.session.uid
       report_obj = request.env[model].with_user(uid)
       dfr_data = dfr_data
       options = options
       token = 'dummy-because-api-expects-one'
       try:
           if output_format == 'xlsx':
               response = request.make_response(
                   None,
                   headers=[
                       ('Content-Type', 'application/vnd.ms-excel'),
                       ('Content-Disposition',
                        content_disposition(report_name + '.xlsx'))
                   ]
               )
               report_obj.get_purchase_xlsx_report(options, response,
                                                   report_data, dfr_data)
           response.set_cookie('fileToken', token)
           return response
       except Exception as e:
           se = 0
           error = {
               'code': 200,
               'message': 'Odoo Server Error',
               'data': se
           }
           return request.make_response(html_escape(json.dumps(error)))

After defining the main Python function, additional helper functions can be added to execute simple queries. The results from these queries are collected row by row into a list, which is then returned as the final output.

Custom model

def _get_report_sub_lines(self, data):
   """Function for get report value using sql query"""
   report_sub_lines = []
   if data.get('report_type') == 'report_by_order':
       query = """ select l.name,l.date_order, l.partner_id,l.amount_total,
       l.notes,l.user_id,res_partner.name as partner,
       res_users.partner_id as user_partner,sum(purchase_order_line.product_qty),
       l.id as id,(SELECT res_partner.name as salesman FROM res_partner
       WHERE res_partner.id = res_users.partner_id) from purchase_order as l
       left join res_partner on l.partner_id = res_partner.id
       left join res_users on l.user_id = res_users.id
       left join purchase_order_line on l.id = purchase_order_line.order_id
       where 1=1 """
       if data.get('date_from'):
           query += """and l.date_order >= '%s' """ % data.get('date_from')
       if data.get('date_to'):
           query += """ and l.date_order <= '%s' """ % data.get('date_to')
       query += """group by l.user_id,res_users.partner_id,res_partner.name,
                 l.partner_id,l.date_order,l.name,l.amount_total,l.notes,l.id"""
       self._cr.execute(query)
       report_by_order = self._cr.dictfetchall()
       report_sub_lines.append(report_by_order)
   elif data.get('report_type') == 'report_by_order_detail':
       query = """ select l.name,l.date_order,l.partner_id,l.amount_total,
       l.notes, l.user_id,res_partner.name as partner,res_users.partner_id
       as user_partner,sum(purchase_order_line.product_qty),
       purchase_order_line.name as product, purchase_order_line.price_unit,
       purchase_order_line.price_subtotal,l.amount_total,
       purchase_order_line.product_id,product_product.default_code,
       (SELECT res_partner.name as salesman FROM res_partner WHERE
       res_partner.id = res_users.partner_id)from purchase_order as l
       left join res_partner on l.partner_id = res_partner.id
       left join res_users on l.user_id = res_users.id
       left join purchase_order_line on l.id = purchase_order_line.order_id
       left join product_product on purchase_order_line.product_id = product_product.id
       where 1=1 """
       if data.get('date_from'):
           query += """ and l.date_order >= '%s' """ % data.get('date_from')
       if data.get('date_to'):
           query += """ and l.date_order <= '%s' """ % data.get('date_to')
       query += """group by l.user_id,res_users.partner_id,res_partner.name,
       l.partner_id,l.date_order,l.name,l.amount_total,l.notes,
       purchase_order_line.name,purchase_order_line.price_unit,
       purchase_order_line.price_subtotal,l.amount_total,
       purchase_order_line.product_id,product_product.default_code"""
       self._cr.execute(query)
       report_by_order_details = self._cr.dictfetchall()
       report_sub_lines.append(report_by_order_details)
   elif data.get('report_type') == 'report_by_product':
       query = """ select l.amount_total,sum(purchase_order_line.product_qty)
        as qty, purchase_order_line.name as product, purchase_order_line.price_unit,
       product_product.default_code,product_category.name from purchase_order
       as l left join purchase_order_line on l.id = purchase_order_line.order_id
       left join product_product on purchase_order_line.product_id = product_product.id
       left join product_template on purchase_order_line.product_id = product_template.id
       left join product_category on product_category.id = product_template.categ_id
       where 1=1   """
       if data.get('date_from'):
           query += """and l.date_order >= '%s' """ % data.get('date_from')
       if data.get('date_to'):
           query += """ and l.date_order <= '%s' """ % data.get('date_to')
       query += """group by l.amount_total,purchase_order_line.name,
                purchase_order_line.price_unit,purchase_order_line.product_id,
                product_product.default_code,product_template.categ_id,
                product_category.name"""
       self._cr.execute(query)
       report_by_product = self._cr.dictfetchall()
       report_sub_lines.append(report_by_product)
   elif data.get('report_type') == 'report_by_categories':
       query = """ select product_category.name,sum(l.product_qty) as qty,
       sum(l.price_subtotal) as amount_total from purchase_order_line as l
       left join product_template on l.product_id = product_template.id
       left join product_category on product_category.id = product_template.categ_id
       left join purchase_order on l.order_id = purchase_order.id
       where 1=1 """
       if data.get('date_from'):
           query += """and pos_order.date_order >= '%s' """ % data.get(
               'date_from')
       if data.get('date_to'):
           query += """ and pos_order.date_order <= '%s' """ % data.get(
               'date_to')
       query += "group by product_category.name"
       self._cr.execute(query)
       report_by_categories = self._cr.dictfetchall()
       report_sub_lines.append(report_by_categories)
   elif data.get('report_type') == 'report_by_purchase_representative':
       query = """ select res_partner.name,sum(purchase_order_line.product_qty)
        as qty,sum(purchase_order_line.price_subtotal) as amount,count(l.id)
        as order from purchase_order as l left join res_users on
        l.user_id = res_users.id left join res_partner on
        res_users.partner_id = res_partner.id left join purchase_order_line
         on l.id = purchase_order_line.order_id where 1=1 """
       if data.get('date_from'):
           query += """ and l.date_order >= '%s' """ % data.get('date_from')
       if data.get('date_to'):
           query += """ and l.date_order <= '%s' """ % data.get('date_to')
       query += "group by res_partner.name"
       self._cr.execute(query)
       report_by_purchase_representative = self._cr.dictfetchall()
       report_sub_lines.append(report_by_purchase_representative)
   elif data.get('report_type') == 'report_by_state':
       query = """ select l.state,sum(purchase_order_line.product_qty) as
          qty,sum(purchase_order_line.price_subtotal) as amount,
          count(l.id) as order from purchase_order as l
          left join res_users on l.user_id = res_users.id left join
          res_partner on res_users.partner_id = res_partner.id
          left join purchase_order_line on l.id = purchase_order_line.order_id
          where 1=1 """
       if data.get('date_from'):
           query += """and so.date_order >= '%s' """ % data.get('date_from')
       if data.get('date_to'):
           query += """ and so.date_order <= '%s' """ % data.get('date_to')
       query += "group by l.state"
       self._cr.execute(query)
       report_by_state = self._cr.dictfetchall()
       report_sub_lines.append(report_by_state)
   return report_sub_lines
def _get_report_values(self, data):
   """Get report values based on the provided data."""
   docs = data['model']
   if data.get('report_type'):
       report_res = \
           self._get_report_sub_lines(data)[0]
   else:
       report_res = self._get_report_sub_lines(data)
   return {
       'doc_ids': self.ids,
       'docs': docs,
       'PURCHASE': report_res,
   }

As a result, the RPC call will wrap this list and send the data to the QWeb model. Next, define the templates in the static/src/xml directory to handle the structure and display of the report in the UI.

<?xml version="1.0" encoding="UTF-8" ?>
<templates>
   <t t-name="PurchaseReport" owl="1">
       <!-- Section contains a structure for the purchase report, including a filter
       view and a table view. It has div elements for the filter view and table view,
       with respective classes for styling. -->
       <div class="">
           <div>
               <center>
                   <h1 style="margin: 20px;">Purchase Report</h1>
               </center>
           </div>
       </div>
       <div class="print-btns">
           <div class="sub_container_left"
                style="width: 285px; margin-left: 36px;">
               <div class="report_print">
                   <button type="button" class="btn btn-primary" id="pdf"
                           style="float: left; margin-right: 9px;"
                           t-on-click="printPdf">
                       Print (PDF)
                   </button>
                   <button type="button" class="btn btn-primary" id="xlsx"
                           t-on-click="print_xlsx">
                       Export (XLSX)
                   </button>
               </div>
           </div>
           <br/>
           <div class="sub_container_right">
               <div class="dropdown">
                   <button class="btn btn-secondary dropdown-toggle time_range_pr"
                           type="button" id="date_chose"
                           data-bs-toggle="dropdown" aria-expanded="false">
                       <span class="fa fa-calendar" title="Dates" role="img"
                             aria-label="Dates"/>
                       Date Range
                   </button>
                   <div class="dropdown-menu my_custom_dropdown" role="menu"
                        aria-labelledby="date_chose">
                       <div class="form-group">
                           <label class="" for="date_from">Start Date :</label>
                           <div class="input-group date" id="date_from"
                                data-target-input="nearest">
                               <input type="date" name="date_from"
                                      t-ref="date_from"
                                      class="form-control datetimepicker-input"
                                      data-target="#date_from"
                                      t-att-name="prefix"/>
                               <div class="input-group-append"
                                    data-target="#date_from"
                                    data-toggle="datetimepicker"
                                    style="pointer-events: none;">
                               </div>
                           </div>
                           <label class="" for="date_to">End Date :</label>
                           <div class="input-group date" id="date_to"
                                data-target-input="nearest">
                               <input type="date" name="date_to"
                                      t-ref="date_to"
                                      class="form-control datetimepicker-input"
                                      data-target="#date_to"
                                      t-att-name="prefix"/>
                               <div class="input-group-append"
                                    data-target="#date_to"
                                    data-toggle="datetimepicker"
                                    style="pointer-events: none;">
                               </div>
                           </div>
                       </div>
                   </div>
               </div>
               <div class="search-Result-Selection">
                   <div class="dropdown">
                       <a class="btn btn-secondary dropdown-togglereport-type"
                          href="#" role="button" id="dropdownMenuLink"
                          data-bs-toggle="dropdown" aria-expanded="false">
                           <span class="fa fa-book"/>
                           <span class="low_case dropdown-toggle">Report Type
                               :
                           </span>
                       </a>
                       <select id="selection" class="dropdown-menu report_type"
                               aria-labelledby="dropdownMenuLink"
                               t-ref="order_by"
                               name="states[]">
                           <div role="separator" class="dropdown-divider"/>
                           <option value="report_by_order" selected="">Report
                               By Order
                           </option>
                           <option value="report_by_order_detail">Report By
                               Order Detail
                           </option>
                           <option value="report_by_product">Report By
                               Product
                           </option>
                           <option value="report_by_categories">Report By
                               Categories
                           </option>
                           <option value="report_by_purchase_representative">
                               Report By Purchase Representative
                           </option>
                           <option value="report_by_state">Report By
                               State
                           </option>
                       </select>
                       <span id="report_res" t-out="state.order_by"/>
                   </div>
               </div>
               <div class="apply_filter">
                   <button type="button" id="apply_filter"
                           class="btn btn-primary" t-on-click="applyFilter">
                       Apply
                   </button>
               </div>
           </div>
       </div>
       <div class="overflow-auto" style="height: 70vh; padding:10px">
           <div t-if="state.order_by == 'report_by_order'">
               <div class="table_main_view">
                   <table cellspacing="0" width="100%">
                       <thead>
                           <tr class="table_pr_head">
                               <th>Order</th>
                               <th class="mon_fld">Date Order</th>
                               <th class="mon_fld">Customer</th>
                               <th class="mon_fld">Purchase Representative</th>
                               <th class="mon_fld">Total Qty</th>
                               <th class="mon_fld">Amount Total</th>
                               <th class="mon_fld">Note</th>
                           </tr>
                       </thead>
                       <tbody>
                           <t t-foreach="state.order_line"
                              t-as="dynamic_purchase_report"
                              t-key="dynamic_purchase_report_index">
                               <tr style="border: 1.5px solid black;"
                                   class="pr-line"
                                   t-att-data-account-id="dynamic_purchase_report['id']"
                                   t-attf-data-target=".a{{dynamic_purchase_report['id']}}">
                                   <td>
                                       <t t-if="dynamic_purchase_report['id']">
                                           <div class="dropdown dropdown-toggle">
                                               <a data-toggle="dropdown"
                                                  href="#"
                                                  id="table_toggle_btn"
                                                  data-bs-toggle="dropdown"
                                                  aria-expanded="false">
                                                   <span class="caret"/>
                                                   <span>
                                                       <t t-esc="dynamic_purchase_report['name']"/>
                                                   </span>
                                               </a>
                                               <ul class="dropdown-menu"
                                                   role="menu"
                                                   aria-labelledby="table_toggle_btn">
                                                   <li>
                                                       <a class="view_purchase_order"
                                                          tabindex="-1"
                                                          href="#"
                                                          t-att-id="dynamic_purchase_report['id']"
                                                          t-on-click="viewPurchaseOrder">
                                                           View Purchase Order
                                                       </a>
                                                   </li>
                                               </ul>
                                           </div>
                                       </t>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['date_order']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['partner']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['salesman']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['sum']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['amount_total']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['notes']"/>
                                       </span>
                                   </td>
                               </tr>
                           </t>
                       </tbody>
                   </table>
               </div>
           </div>
           <!--Report for order detail-->
           <div t-if="state.order_by == 'report_by_order_detail'">
               <div class="table_main_view">
                   <table cellspacing="0" width="100%">
                       <thead>
                           <tr class="table_pr_head">
                               <th>Order</th>
                               <th class="mon_fld">Date Order</th>
                               <th class="mon_fld">Customer</th>
                               <th class="mon_fld">Purchase Representative</th>
                               <th class="mon_fld">Product Code</th>
                               <th class="mon_fld">Product Name</th>
                               <th class="mon_fld">Price unit</th>
                               <th class="mon_fld">Qty</th>
                               <th class="mon_fld">Price Subtotal</th>
                           </tr>
                       </thead>
                       <tbody>
                           <t t-log="state.order_line"/>
                           <t t-foreach="state.order_line"
                              t-as="dynamic_purchase_report"
                              t-key="dynamic_purchase_report_index">
                               <tr style="border: 1.5px solid black;"
                                   class="pr-line"
                                   t-att-data-account-id="dynamic_purchase_report['id']"
                                   t-attf-data-target=".a{{dynamic_purchase_report['id']}}">
                                   <td>
                                       <div class="dropdown dropdown-toggle">
                                           <a data-toggle="dropdown" href="#"
                                              id="table_toggle_btn"
                                              data-bs-toggle="dropdown"
                                              aria-expanded="false">
                                               <span class="caret"/>
                                               <span>
                                                   <t t-esc="dynamic_purchase_report['name']"/>
                                               </span>
                                           </a>
                                           <ul class="dropdown-menu"
                                               role="menu"
                                               aria-labelledby="table_toggle_btn">
                                               <li>
                                                   <a class="view_purchase_order"
                                                      tabindex="-1" href="#"
                                                      t-att-id="dynamic_purchase_report['id']">
                                                       View Purchase Order
                                                   </a>
                                               </li>
                                           </ul>
                                       </div>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['date_order']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['partner']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['salesman']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['default_code']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['product']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['price_unit']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['sum']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['price_subtotal']"/>
                                       </span>
                                   </td>
                               </tr>
                           </t>
                       </tbody>
                   </table>
               </div>
           </div>
           <!-- Report for product -->
           <div t-if="state.order_by == 'report_by_product'">
               <div class="table_main_view">
                   <table cellspacing="0" width="100%">
                       <thead>
                           <tr class="table_pr_head">
                               <th>Category</th>
                               <th class="mon_fld">Product Code</th>
                               <th class="mon_fld">Product Name</th>
                               <th class="mon_fld">Qty</th>
                               <th class="mon_fld">Amount Total</th>
                           </tr>
                       </thead>
                       <tbody>
                           <t t-foreach="state.order_line"
                              t-as="dynamic_purchase_report"
                              t-key="dynamic_purchase_report_index">
                               <tr style="border: 1.5px solid black;"
                                   class="pr-line"
                                   t-att-data-account-id="dynamic_purchase_report['id']"
                                   t-attf-data-target=".a{{dynamic_purchase_report['id']}}">
                                   <td>
                                       <span>
                                           <t t-esc="dynamic_purchase_report['name']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['default_code']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['product']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['qty']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['amount_total']"/>
                                       </span>
                                   </td>
                               </tr>
                           </t>
                       </tbody>
                   </table>
               </div>
           </div>
           <!-- Report for Categories -->
           <div t-if="state.order_by == 'report_by_categories'">
               <div class="table_main_view">
                   <table cellspacing="0" width="100%">
                       <thead>
                           <tr class="table_pr_head">
                               <th>Category</th>
                               <th class="mon_fld">Qty</th>
                               <th class="mon_fld">Amount Total</th>
                           </tr>
                       </thead>
                       <tbody>
                           <t t-foreach="state.order_line"
                              t-as="dynamic_purchase_report"
                              t-key="dynamic_purchase_report_index">
                               <tr style="border: 1.5px solid black;"
                                   class="pr-line"
                                   t-att-data-account-id="dynamic_purchase_report['id']"
                                   t-attf-data-target=".a{{dynamic_purchase_report['id']}}">
                                   <td style="border: 0px solid black;">
                                       <t t-esc="dynamic_purchase_report['name']"/>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['qty']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['amount_total']"/>
                                       </span>
                                   </td>
                               </tr>
                           </t>
                       </tbody>
                   </table>
               </div>
           </div>
           <!-- Report for purchase_representative -->
           <div t-if="state.order_by == 'report_by_purchase_representative'">
               <div class="table_main_view">
                   <table cellspacing="0" width="100%">
                       <thead>
                           <tr class="table_pr_head">
                               <th>Purchase Representative</th>
                               <th class="mon_fld">Total Order</th>
                               <th class="mon_fld">Total Qty</th>
                               <th class="mon_fld">Total Amount</th>
                           </tr>
                       </thead>
                       <tbody>
                           <t t-foreach="state.order_line"
                              t-as="dynamic_purchase_report"
                              t-key="dynamic_purchase_report_index">
                               <tr style="border: 1.5px solid black;"
                                   class="pr-line"
                                   data-toggle="collapse"
                                   t-att-data-account-id="dynamic_purchase_report['id']"
                                   t-attf-data-target=".a{{dynamic_purchase_report['id']}}">
                                   <td style="border: 0px solid black;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['name']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['order']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['qty']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['amount']"/>
                                       </span>
                                   </td>
                               </tr>
                           </t>
                       </tbody>
                   </table>
               </div>
           </div>
           <!-- Report for State -->
           <div t-if="state.order_by == 'report_by_state'">
               <div class="table_main_view">
                   <table cellspacing="0" width="100%">
                       <thead>
                           <tr class="table_pr_head">
                               <th>State</th>
                               <th class="mon_fld">Total Count</th>
                               <th class="mon_fld">Quantity</th>
                               <th class="mon_fld">Amount</th>
                           </tr>
                       </thead>
                       <tbody>
                           <t t-foreach="state.order_line"
                              t-as="dynamic_purchase_report"
                              t-key="dynamic_purchase_report_index">
                               <tr style="border: 1.5px solid black;"
                                   class="pr-line"
                                   data-toggle="collapse"
                                   t-att-data-account-id="dynamic_purchase_report['id']"
                                   t-attf-data-target=".a{{dynamic_purchase_report['id']}}">
                                   <td style="border: 0px solid black;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['state']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['order']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['qty']"/>
                                       </span>
                                   </td>
                                   <td style="text-align:center;">
                                       <span>
                                           <t t-esc="dynamic_purchase_report['amount']"/>
                                       </span>
                                   </td>
                               </tr>
                           </t>
                       </tbody>
                   </table>
               </div>
           </div>
       </div>
   </t>
</templates>

This overview of the dynamic purchase report includes multiple filter options for refining the displayed data.

How to Create a Dynamic Report in Odoo 18-cybrosys

This view displays the products along with their filtered details, and it also provides an option to filter the data by date range.

Dynamic reporting in Odoo 18 enables businesses to harness the full power of their data through flexible, customizable, and interactive reporting capabilities.

To read more about How to Create a Dynamic Report in Odoo 17, refer to our blog How to Create a Dynamic Report in Odoo 17.


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



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message