Enable Dark Mode!
how-to-use-odoo-19-kpi-indicators-odoo-19-dashboard.jpg
By: Ramsina K

How to Use Odoo 19 KPI Indicators Odoo 19 Dashboard

Technical Odoo 19 Odoo Enterprises Odoo Community

In Odoo 19, KPI (Key Performance Indicator) indicators are real-time metric widgets displayed on module dashboards. They aggregate live data from the database and present it as simple numbers, trends, or comparisons — giving managers an at-a-glance view of business performance.

Odoo 19 dashboards use KPI cards for metrics like:

  • Total confirmed sales orders this month
  • Number of pending delivery orders
  • Outstanding receivables
  • Monthly payroll cost

How KPI Data Flows in Odoo 19

PostgreSQL DB > Python ORM (read_group) > JSON-RPC > OWL Component > Dashboard Card

  1. Python: Aggregates records using read_group() or search_count()
  2. Controller: Exposes computed data via a JSON-RPC route
  3. OWL Component: Fetches and renders the KPI on the dashboard

Building a Custom KPI Dashboard — Full Example

Let's build a simple Sales KPI Dashboard that shows:

  • Total confirmed sales orders
  • Total sales revenue this month

Step 1: Python Model Method

In your custom module, add a method to the sale.order that returns KPI data:

# models/sale_dashboard.py
from odoo import models, fields, api
from datetime import date
class SaleDashboard(models.Model):
    _inherit = 'sale.order'
    @api.model
    def get_kpi_data(self):
        today = date.today()
        first_day = today.replace(day=1)
        # Count confirmed orders this month
        confirmed_orders = self.search_count([
            ('state', 'in', ['sale', 'done']),
            ('date_order', '>=', first_day),
        ])
        # Sum revenue of confirmed orders this month
        result = self.read_group(
            domain=[
                ('state', 'in', ['sale', 'done']),
                ('date_order', '>=', first_day),
            ],
            fields=['amount_total:sum'],
            groupby=[],
        )
        total_revenue = result[0]['amount_total'] if result else 0.0
        return {
            'confirmed_orders': confirmed_orders,
            'total_revenue': total_revenue,
        }

Step 2: Register a Client Action (XML)

<!-- views/dashboard_action.xml -->
<odoo>
    <record id="action_sales_kpi_dashboard" model="ir.actions.client">
        <field name="name">Sales KPI Dashboard</field>
        <field name="tag">sales_kpi_dashboard</field>
    </record>
    <menuitem
        id="menu_sales_kpi_dashboard"
        name="KPI Dashboard"
        parent="sale.sale_menu_root"
        action="action_sales_kpi_dashboard"
        sequence="5"/>
</odoo>

Step 3: OWL Component (JavaScript)

// static/src/js/sales_kpi_dashboard.js
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Component, onWillStart, useState } from "@odoo/owl";
import { loadBundle } from "@web/core/assets";
export class SalesKpiDashboard extends Component {
    static template = "sales_module.SalesKpiDashboard";
    setup() {
        this.orm = useService("orm");
        this.state = useState({
            confirmedOrders: 0,
            totalRevenue: 0.0,
        });
        onWillStart(async () => {
            const data = await this.orm.call(
                "sale.order",
                "get_kpi_data",
                [],
                {}
            );
            this.state.confirmedOrders = data.confirmed_orders;
            this.state.totalRevenue = data.total_revenue.toFixed(2);
        });
    }
}
registry.category("actions").add("sales_kpi_dashboard", SalesKpiDashboard);

Step 4: OWL Template (XML)

<!-- static/src/xml/sales_kpi_dashboard.xml -->
<templates>
    <t t-name="sales_module.SalesKpiDashboard">
        <div class="o_dashboard">
            <div class="o_dashboard_header">
                <h2>Sales KPI Dashboard</h2>
            </div>
            <div class="o_kpi_card_wrapper d-flex gap-4 mt-4">
                <!-- KPI Card 1 -->
                <div class="o_kpi_card card p-4 shadow-sm text-center">
                    <h6 class="text-muted">Confirmed Orders (This Month)</h6>
                    <h2 class="text-primary fw-bold">
                        <t t-esc="state.confirmedOrders"/>
                    </h2>
                </div>
                <!-- KPI Card 2 -->
                <div class="o_kpi_card card p-4 shadow-sm text-center">
                    <h6 class="text-muted">Total Revenue (This Month)</h6>
                    <h2 class="text-success fw-bold">
                        $<t t-esc="state.totalRevenue"/>
                    </h2>
                </div>
            </div>
        </div>
    </t>
</templates>

Step 5: Register Assets in __manifest__.py

# __manifest__.py

{
    'name': 'Sales KPI Dashboard',
    'version': '19.0.1.0.0',
    'depends': ['sale_management'],
    'data': [
        'views/dashboard_action.xml',
    ],
    'assets': {
        'web.assets_backend': [
            'sales_module/static/src/js/sales_kpi_dashboard.js',
            'sales_module/static/src/xml/sales_kpi_dashboard.xml',
        ],
    },
}

Building KPI indicators in Odoo 19 follows a clean three-step pattern:

  • Python > Write a model method using search_count() or read_group() to aggregate data
  • OWL JS > Create a component that calls the method via orm.call() and stores results in useState()
  • OWL XML > Bind the state to HTML template using t-esc directives

KPI dashboards in Odoo help track key data in real time, making it easier to understand performance and take quick decisions.

To read more about How to Configure a Dashboard with Odoo 19, refer to our blog How to Configure a Dashboard with Odoo 19.


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