How to Create Custom Dashboard Charts in ERPNext: A Complete Developer Guide
Creating custom dashboard charts in ERPNext requires a Python or JavaScript source module that returns structured data, a Dashboard Chart document that references that source, and placement within a Dashboard document.
ERPNext, the open-source ERP from Frappe, renders analytics dashboards by reading Dashboard Chart documents linked to executable data sources. Understanding the process for creating custom dashboard charts in ERPNext enables developers to surface real-time business metrics—from warehouse stock levels to sales order trends—directly within the native interface without modifying core framework files.
Architecture Overview: The Four-Step Rendering Flow
ERPNext constructs dashboards through a strictly defined pipeline. According to the frappe/erpnext source code, the system executes four distinct phases when rendering a chart:
- Source Definition: A Python file (or JavaScript module) queries the database and returns a JSON structure containing
labels,values, and optionaloptions. - Document Configuration: A Dashboard Chart document (stored in the database or shipped as a JSON fixture) specifies metadata including
chart_type,time_interval, and thesourcepath. - Dashboard Assignment: A Dashboard document maintains an array of chart references that determines which charts appear and their layout width (Half or Full).
- Client Rendering: When a user loads the dashboard, ERPNext calls the source method via
frappe.get_attr(Python) orfrappe.call(JavaScript), then renders the visualization using ChartJS.
Key implementation files include the default dashboard definitions in erpnext/setup/setup_wizard/data/dashboard_charts.py and example source modules like erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py.
Step 1: Define the Chart Source Module
The chart source is the data layer. It lives in a folder named after your chart within the dashboard_chart_source directory of your app.
Python Source Modules
Place your Python file at {app}/{module}/dashboard_chart_source/{chart_name}/{chart_name}.py. The module must expose a top-level get function that accepts a filters argument and returns a dictionary.
Required return structure:
labels: List of strings for the X-axis or category names.values: List of integers or floats corresponding to the data points.options: Dictionary specifying ChartJS configuration (e.g.,type,color).
Example: Warehouse-wise stock value (based on erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py):
import frappe
def get(filters=None):
data = frappe.db.sql("""
SELECT warehouse, SUM(actual_qty) as qty
FROM `tabBin`
WHERE posting_date BETWEEN %(from)s AND %(to)s
GROUP BY warehouse
""", filters, as_dict=True)
return {
"labels": [d.warehouse for d in data],
"values": [d.qty for d in data],
"options": {"type": "pie"}
}
JavaScript Source Modules
Alternatively, you can define the source in JavaScript at the same path with a .js extension. ERPNext invokes this via frappe.call to the corresponding Python method:
// my_app/dashboard_chart_source/monthly_sales/monthly_sales.js
frappe.call({
method: "my_app.dashboard_chart_source.monthly_sales.monthly_sales.get",
args: { filters: filters },
callback: function(r) {
// r.message contains {labels, values, options}
}
});
Step 2: Create the Dashboard Chart Document
The Dashboard Chart document acts as the bridge between your data source and the UI. You can create this via the desk interface (Setup → Dashboard Chart) or ship it as a JSON fixture in your custom app.
Critical fields:
source: Must match the folder name of your source module (e.g.,"Warehouse Wise Stock Value").chart_type: Set to"Custom"when using your own source.timespanandtime_interval: Control default date filtering.filters_json: Stringified JSON object passed to yourgetfunction.
Fixture example (save as my_app/dashboard_chart/monthly_sales/monthly_sales.json):
{
"doctype": "Dashboard Chart",
"chart_name": "Monthly Sales Orders",
"time_interval": "Monthly",
"timespan": "Last Year",
"chart_type": "Custom",
"source": "Monthly Sales Orders",
"filters_json": "{}",
"type": "Line",
"width": "Half"
}
When installed, this document tells ERPNext to execute the get function from the monthly_sales_orders source module.
Step 3: Add the Chart to a Dashboard
Dashboard documents contain an ordered list of charts. To display your custom chart, append a reference object to the charts array in the target dashboard definition.
Dashboard definition example (from erpnext/setup/setup_wizard/data/dashboard_charts.py):
{
"doctype": "Dashboard",
"dashboard_name": "Sales",
"charts": [
{"chart": "Monthly Sales Orders", "width": "Full"},
{"chart": "Top Customers", "width": "Half"}
]
}
You can also assign charts dynamically via the UI by navigating to Setup → Dashboard, selecting the target dashboard, and dragging the new chart into the layout.
Complete Implementation Example: Monthly Sales Orders
The following example demonstrates an end-to-end custom chart that displays monthly sales order counts for the last 12 months.
1. Create the source module at my_app/dashboard_chart_source/monthly_sales_orders/monthly_sales_orders.py:
import frappe
from frappe.utils import getdate, add_months
def get(filters=None):
end_date = getdate()
start_date = add_months(end_date, -12)
data = frappe.db.sql("""
SELECT DATE_FORMAT(posting_date, '%Y-%m') as month, COUNT(*) as cnt
FROM `tabSales Order`
WHERE posting_date BETWEEN %(start)s AND %(end)s
GROUP BY month
ORDER BY month
""", {"start": start_date, "end": end_date}, as_dict=True)
return {
"labels": [d.month for d in data],
"values": [d.cnt for d in data],
"options": {"type": "line", "color": "#5e72e4"}
}
2. Create the fixture at my_app/dashboard_chart/monthly_sales_orders.json:
{
"doctype": "Dashboard Chart",
"chart_name": "Monthly Sales Orders",
"time_interval": "Monthly",
"timespan": "Last Year",
"chart_type": "Custom",
"source": "Monthly Sales Orders",
"filters_json": "{}",
"type": "Line",
"width": "Half"
}
3. Add to a dashboard by including it in your app's fixtures or manually in Setup → Dashboard.
Summary
- Source Module: Build a Python file with a
get(filters)function returninglabels,values, andoptions, or use JavaScript for client-side data fetching. - Dashboard Chart Document: Create a JSON fixture or UI document referencing the source folder name and specifying chart metadata.
- Dashboard Assignment: Insert the chart reference into a Dashboard document's
chartsarray, setting the width toHalforFull. - Core References: Default dashboards are defined in
erpnext/setup/setup_wizard/data/dashboard_charts.py, while rendering utilities reside inerpnext/controllers/trends.py.
Frequently Asked Questions
What is the difference between a Chart Source and a Dashboard Chart?
A Chart Source is the executable code (Python or JavaScript) that queries the database and returns raw data. A Dashboard Chart is the configuration document that references that source, stores user-defined filters, and determines visual properties like chart type and color scheme.
Can I use JavaScript instead of Python for chart data sources?
Yes. While Python sources are preferred for database queries due to security and performance, JavaScript sources are supported for client-side logic. Place a .js file alongside your Python module, or use frappe.call to invoke a whitelisted Python method from the client.
How do I filter data based on user input or date ranges?
The get function receives a filters dictionary parameter populated from the filters_json field of the Dashboard Chart document. Users can modify these filters via the chart's filter UI, and ERPNext passes the updated values to your source function on each refresh.
Where are default ERPNext dashboard charts defined?
Default charts for standard modules (Accounts, Sales, Stock) are defined in erpnext/setup/setup_wizard/data/dashboard_charts.py. This file contains the initial Dashboard and Dashboard Chart documents created during the setup wizard, which you can reference as templates for your custom implementations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →