Enable Dark Mode!
By: Sumaiba

Dynamic Reports in Odoo 15

Functional CRM

Dynamic Report In Odoo 15

Dynamic reports are real-time reports which provide access to up-to-date information. Additionally, it facilitates access to the various reports supported by a dynamic view in Odoo.

You can obtain the required data by using the grouping and filtering tools.

Additionally, users can print reports in XLSX and PDF formats.

Basic Features of Dynamic Reports are;

* Various Filters to Compare

* Option to view source move in the report

* Print reports in PDF and XLSX Format

* Drill-down Approach in Reports

Firstly, create a menu in the XML file in the views directory(views/views.xml).

<?xml version="1.0" encoding="UTF-8"?>
   <record id="purchase_all_report_action" model="ir.actions.client">
       <field name="name">Purchase Report</field>
       <field name="tag">p_r</field>
   <menuitem action="purchase_all_report_action" parent="purchase.purchase_report"
             name="Purchase Report"/>

Here I am creating a record in ir.actions.client model with name as Purchase Report(We can give any name as we want). Additionally, I entered the tag p_r in the tag field, which is the tag used in the widget to load the action or to call the action defined in the JS file when the menu is clicked.

Now let's define the JS file(static/src/js).

odoo.define('purchase_report_generator.purchase_report', function(require) {
  'use strict';
  var AbstractAction = require('web.AbstractAction');
  var core = require('web.core');
  var rpc = require('web.rpc');
  var QWeb = core.qweb;
  var _t = core._t;
  var datepicker = require('web.datepicker');
  var time = require('web.time');
   var framework = require('web.framework');
   var session = require('web.session');
  var PurchaseReport = AbstractAction.extend({
     template: 'PurchaseReport',
     events: {
        'click #apply_filter': 'apply_filter',
        'click #pdf': 'print_pdf',
        'click #xlsx': 'print_xlsx',
        'click .view_purchase_order': 'button_view_order',
        'click .pr-line': 'show_drop_down',

     init: function(parent, action) {
        this._super(parent, action);
        this.report_lines = action.report_lines;
        this.wizard_id = action.context.wizard | null;
     start: function() {
        var self = this;
        self.initial_render = true;
           model: 'dynamic.purchase.report',
           method: 'create',
           args: [{
        }).then(function(res) {
           self.wizard_id = res;

load_data: function(initial_render = true) {
  var self = this;
     model: 'dynamic.purchase.report',
     method: 'purchase_report',
     args: [
  }).then(function(datas) {
     if (initial_render) {
        self.$('.filter_view_pr').html(QWeb.render('PurchaseFilterView', {
           filter_data: datas['filters'],
           placeholder: ' Report Type...',
     if (datas['orders'])
        self.$('.table_view_pr').html(QWeb.render('PurchaseOrderTable', {
           filter: datas['filters'],
           order: datas['orders'],
           report_lines: datas['report_lines'],
           main_lines: datas['report_main_line']

So here we are extending the Abstract Action class. When this action is taken, a template (PurchaseReport) that I've defined will be displayed.

The load data function is called from the start function, and it uses the RPC function to retrieve the data from the python function. The returned value is then passed to the template ("PurchaseReport"), which is then added to the table_view_pr class of the main Template ("PurchaseReport").

Now that the RPC is being called by the python function file, we also need to define the qweb template.  So first, I am creating the python file(models/filename.py).

from odoo import models, fields, api

import io

import json


   from odoo.tools.misc import xlsxwriter
except ImportError:
   import xlsxwriter

class DynamicPurchaseReport(models.Model):
   _name = "dynamic.purchase.report"
   purchase_report = fields.Char(string="Purchase Report")
   date_from = fields.Datetime(string="Date From")
   date_to = fields.Datetime(string="Date to")
   report_type = fields.Selection([
       ('report_by_order', 'Report By Order'),
       ('report_by_order_detail', 'Report By Order Detail'),
       ('report_by_product', 'Report By Product'),
       ('report_by_categories', 'Report By Categories'),
       ('report_by_purchase_representative', 'Report By Purchase Representative'),
       ('repot_by_state', 'Report By State')], default='report_by_order')

   def purchase_report(self, option):
       report_values = self.env['dynamic.purchase.report'].search(
           [('id', '=', option[0])])
       data = {
           'report_type': report_values.report_type,
           'model': self,
       if report_values.date_from:
               'date_from': report_values.date_from,
       if report_values.date_to:
               'date_to': report_values.date_to,
       filters = self.get_filter(option)
       lines = self._get_report_values(data).get('PURCHASE')
       sub_line = self.get_report_child_lines()
       return {
           'name': "Purchase Orders",
           'type': 'ir.actions.client',
           'tag': 's_r',
           'orders': data,
           'filters': filters,
           'report_lines': lines,

After Defined the main python function, The function that executes a straightforward query and adds each row to a list, which is the final list that is returned, can be defined.

def _get_report_sub_lines(self, data, report, date_from, date_to):
   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
       term = 'Where '
       if data.get('date_from'):
           query += "Where l.date_order >= '%s' " % data.get('date_from')
           term = 'AND '
       if data.get('date_to'):
           query += term + "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," \
       report_by_order = self._cr.dictfetchall()

As a result, the RPC call will include this list, and the data is passed to the qweb template.

Let's define the template(In static/src/xml).

   <t t-name="PurchaseReport">
       <div class="">
                   <h1 style="margin: 20px;">Purchase Report</h1>
               <div class="filter_view_pr"/>
               <div class="table_view_pr" style="width: 95%; margin: auto;"/>

   <t t-name="PurchaseOrderTable">
       <div t-if="order.report_type == 'report_by_order'">
           <div class="table_main_view">
               <table cellspacing="0" width="100%">
                       <tr class="table_pr_head">
                           <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>
                       <!--                        <t t-if="order['report_type']='report_by_order'">-->
                       <t t-foreach="report_lines" t-as="dynamic_purchase_report">
                           <tr style="border: 1.5px solid black;" class="pr-line"
                                   <t t-if="dynamic_purchase_report['id']">
                                       <div class="dropdown dropdown-toggle">
                                           <a data-toggle="dropdown" href="#">
                                               <span class="caret"/>

                                                   <t t-esc="dynamic_purchase_report['name']"/>
                                           <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
                                                   <a class="view_purchase_order" tabindex="-1" href="#"
                                                       View Purchase Order
                               <td style="text-align:center;">
                                       <t t-esc="dynamic_purchase_report['date_order']"/>
                               <td style="text-align:center;">
                                       <t t-esc="dynamic_purchase_report['partner']"/>
                               <td style="text-align:center;">
                                       <t t-esc="dynamic_purchase_report['salesman']"/>
                               <td style="text-align:center;">
                                       <t t-esc="dynamic_purchase_report['sum']"/>
                               <td style="text-align:center;">

                                       <t t-esc="dynamic_purchase_report['amount_total']"/>
                               <td style="text-align:center;">
                                       <t t-esc="dynamic_purchase_report['notes']"/>

The data is passed into the qweb template in this manner.

This is how a dynamic report can be defined; you can see the configuration's resulting screenshots below.


Here we have created the menu under the Reports menu in Purchase.

The template will be loaded by adding the appropriate value upon clicking the menu.

Additionally, we can add additional filters to this view and manage the corresponding data by modifying the query defined in accordance with the filter data supplied to the query.

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


Leave a comment




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



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



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

Send Us A Message