---
myst:
html_meta:
"title": "Dashboard Module Development"
"description": "Complete guide to creating custom dashboards in OpenSPP using OWL framework with charts, cards, and real-time data visualization."
"keywords": "OpenSPP, dashboard development, OWL framework, data visualization, charts, metrics, custom dashboard"
---
# Dashboards
In OpenSPP, dashboards provide a powerful way to visualize key metrics and data at a glance. They are built using the Odoo OWL framework, allowing for dynamic, interactive, and real-time reporting. A custom dashboard is a self-contained module that fetches data from the server and presents it using various UI components like charts and summary cards.
This guide will walk you through creating a custom dashboard module from scratch. We will develop the `spp_custom_dashboard` module to build a new dashboard that displays key statistics from the OpenSPP Farmer Registry.
By the end of this guide, you will be able to:
- Understand the structure of a dashboard module.
- Create a server-side method to fetch and prepare data.
- Implement a client-side OWL component for the dashboard.
- Design the dashboard layout using XML templates.
- Use pre-built components like charts and cards.
- Register your dashboard to make it accessible from the Odoo menu.
## Prerequisites
- Solid understanding of Odoo 17 module development, including Python, XML, XPath, and JavaScript (specifically the OWL framework).
- Familiarity with the OpenG2P and OpenSPP core modules, especially `OpenSPP Dashboard (Base)` (`spp_dashboard_base`) and `OpenSPP Farmer Registry (Base)` (`spp_farmer_registry_base`).
- To set up OpenSPP for development, please refer to the {doc}`Development Setup Guide <../setup>`.
## Module Structure
A typical dashboard module follows a standard Odoo module structure. Here's the complete structure of our example module, `spp_custom_dashboard`:
```
spp_custom_dashboard/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ └── res_partner.py # Data-fetching logic
├── static/
│ └── src/
│ └── dashboard/
│ ├── dashboard.js # OWL component for the dashboard
│ └── dashboard.xml # XML template for the dashboard layout
└── views/
└── dashboard_views.xml # Client action and menu item
```
---
## Step-by-Step Guide
### Create the Module Scaffold
Start by creating a new directory for your module (e.g., `spp_custom_dashboard`) and populate it with the basic Odoo module files and the directory structure shown above.
### Define the Manifest (`__manifest__.py`)
The manifest file declares your module's metadata and dependencies. It's crucial to list all the modules your customization will interact with. Our dashboard depends on `spp_dashboard_base` for the core dashboard components and `spp_farmer_registry_base` for the data.
```python
# In: spp_custom_dashboard/__manifest__.py
{
"name": "OpenSPP Custom Dashboard",
"summary": "A custom dashboard to display key metrics from the Farmer Registry.",
"category": "OpenSPP",
"version": "17.0.1.0.0",
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/openspp-modules",
"license": "LGPL-3",
"depends": [
"spp_dashboard_base",
"spp_farmer_registry_base",
],
"data": [
"views/dashboard_views.xml",
],
"assets": {
"web.assets_backend": [
"spp_custom_dashboard/static/src/dashboard/**/*",
],
},
"application": True,
"installable": True,
}
```
### Prepare the Data on the Server (Python)
The dashboard needs data to display. We'll create a method on an existing model to gather and format this data. For our example, we'll extend the `res.partner` model, which represents farms and farmers in the `spp_farmer_registry_base` module.
1. **Create the model file**: In your `models/` directory, create a Python file named `res_partner.py`. Remember to import it in `models/__init__.py`.
2. **Define the data-fetching method**:
- Extend the `res.partner` model.
- Create a method, for example, `get_farmer_dashboard_data`, that will be called from the client-side component.
- This method will search for farms and farmers and aggregate the data into a dictionary.
```python
# In: spp_custom_dashboard/models/res_partner.py
from odoo import api, models
class Partner(models.Model):
_inherit = "res.partner"
@api.model
def get_farmer_dashboard_data(self):
"""Fetch and prepare data for the farmer registry dashboard."""
farm_kind_id = self.env.ref("spp_farmer_registry_base.kind_farm")
farms = self.search([("kind", "=", farm_kind_id.id)])
farmers = self.search([("is_registrant", "=", True), ("is_group", "=", False)])
# Data for a chart: Farms by Type
farm_types = {
"crop": 0,
"livestock": 0,
"aquaculture": 0,
"mixed": 0,
}
for farm in farms:
if farm.details_farm_type in farm_types:
farm_types[farm.details_farm_type] += 1
return {
# Data for CardBoard components
"total_farms": len(farms),
"total_farmers": len(farmers),
# Data for ChartComponent
"farm_types_data": list(farm_types.values()),
"farm_types_labels": [label.capitalize() for label in farm_types.keys()],
}
```
### Create the Dashboard Component (JavaScript)
The client-side component is responsible for calling the server to get the data and rendering the dashboard template.
1. **Create the JavaScript file**: In `static/src/dashboard/`, create `dashboard.js`.
2. **Implement the OWL component**:
- Import `SppDashboard` from the base dashboard module.
- Extend the `SppDashboard` class.
- Use the `onWillStart` lifecycle hook to call your Python method using the `orm` service.
- Store the fetched data in `this.dashboard_data` to make it available in the template.
- Register your new component in the `actions` registry with a unique tag.
```javascript
// In: spp_custom_dashboard/static/src/dashboard/dashboard.js
/** @odoo-module **/
import { SppDashboard } from "@spp_dashboard_base/dashboard/dashboard";
import { registry } from "@web/core/registry";
export class CustomDashboard extends SppDashboard {
setup() {
super.setup();
this.dashboard_title = "Farmer Registry Dashboard";
}
async onWillStart() {
await super.onWillStart();
this.dashboard_data = await this.orm.call(
"res.partner",
"get_farmer_dashboard_data",
[]
);
}
}
CustomDashboard.template = "spp_custom_dashboard.dashboard_page";
registry.category("actions").add("spp_custom_dashboard_tag", CustomDashboard);
```
### Design the Dashboard Layout (XML)
The XML template defines the structure and appearance of your dashboard. It uses the data prepared by the JavaScript component.
1. **Create the XML file**: In `static/src/dashboard/`, create `dashboard.xml`.
2. **Define the template**:
- Create a template with a unique name (e.g., `spp_custom_dashboard.dashboard_page`).
- Use the `CardBoardComponent` and `ChartComponent` provided by `spp_dashboard_base`.
- Pass the data from `dashboard_data` to the components' props.
```xml
```
### Register the Dashboard
Finally, create a client action and a menu item to make your dashboard accessible in the Odoo interface.
1. **Create the view file**: In the `views/` directory, create `dashboard_views.xml`.
2. **Define the action and menu**:
- Create an `ir.actions.client` record. The `tag` must match the one you registered in your JavaScript file (`spp_custom_dashboard_tag`).
- Create a `menuitem` that calls this client action.
```xml
Farmer Dashboard
spp_custom_dashboard_tag
```
### Install and View Your Dashboard
1. Install or upgrade the module through the Apps menu.
2. Refresh your browser.
3. Navigate to the main menu, and you should see a new "Dashboard" menu item. Click it to view your custom dashboard.
## References
For more information on extending OpenSPP modules, refer to:
- [Odoo 17 Developer Documentation](https://www.odoo.com/documentation/17.0/developer/)
- [OpenSPP Dashboard Base Module Source](https://github.com/OpenSPP/openspp-modules/tree/17.0/spp_dashboard_base)