Number Cards display a single aggregated value on the workspace or module page.
Runs an aggregate function on a DocType. Configuration:
| Field | Required | Description |
|---|---|---|
document_type |
Yes | DocType to aggregate |
function |
Yes | Count, Sum, Average, Minimum, Maximum |
aggregate_function_based_on |
For Sum/Avg/Min/Max | Numeric field to aggregate |
filters_json |
No | JSON array of filters |
parent_document_type |
For child tables | Parent DocType name |
Example — Count of open Sales Orders:
{
"document_type": "Sales Order",
"function": "Count",
"filters_json": "[['Sales Order', 'docstatus', '=', 1], ['Sales Order', 'status', '!=', 'Completed']]"
}Example — Sum of outstanding amount:
{
"document_type": "Sales Invoice",
"function": "Sum",
"aggregate_function_based_on": "outstanding_amount",
"filters_json": "[['Sales Invoice', 'docstatus', '=', 1]]"
}Pulls a value from a specific column of a report. Configuration:
| Field | Required | Description |
|---|---|---|
report_name |
Yes | Name of the report |
report_field |
Yes | Column fieldname to extract |
function |
Yes | Sum, Average, etc. applied to column |
filters_json |
No | Report filter values |
Example — Total revenue from a report:
{
"report_name": "Sales Analytics",
"report_field": "total_amount",
"function": "Sum",
"filters_json": "{\"company\": \"My Company\"}"
}Calls a whitelisted Python method that returns a number. Configuration:
| Field | Required | Description |
|---|---|---|
method |
Yes | Dotted path to whitelisted function |
Python method signature:
# In myapp/api.py
import frappe
@frappe.whitelist()
def get_active_user_count(filters=None):
"""Number Card calls this method. MUST return a numeric value."""
return frappe.db.count("User", {"enabled": 1})@frappe.whitelist()
def get_monthly_revenue(filters=None):
"""Accepts optional filters from the Number Card."""
result = frappe.db.sql("""
SELECT SUM(grand_total)
FROM `tabSales Invoice`
WHERE docstatus = 1
AND MONTH(posting_date) = MONTH(CURDATE())
AND YEAR(posting_date) = YEAR(CURDATE())
""")
return result[0][0] or 0| Property | Values | Description |
|---|---|---|
color |
Hex color or None | Card accent color |
show_percentage_stats |
0 or 1 | Show change vs previous period |
stats_time_interval |
Daily, Weekly, Monthly, Yearly | Comparison period |
- Document Type cards: user MUST have read access to the DocType
- Report cards: user MUST have access to the report
- Custom Method cards: user MUST have read access to any DocType (basic desk access)
Dashboard Charts display visual charts on workspaces and module pages.
Uses data from a Script Report or Query Report:
| Field | Required | Description |
|---|---|---|
chart_type |
Yes | "Report" |
report_name |
Yes | Name of source report |
x_field |
Yes | Column for x-axis |
y_axis |
Yes | Column(s) for y-axis |
type |
Yes | Bar, Line, Pie, Donut, Percentage |
filters_json |
No | Report filter values |
is_public |
No | Visible to all users |
timeseries |
No | Enable time-based x-axis |
timespan |
If timeseries | Last Year, Last Quarter, etc. |
time_interval |
If timeseries | Daily, Weekly, Monthly, Quarterly, Yearly |
Auto-aggregates a DocType field:
| Field | Required | Description |
|---|---|---|
chart_type |
Yes | "Group By" |
document_type |
Yes | DocType to query |
group_by_type |
Yes | Count, Sum, Average |
group_by_based_on |
Yes | Field to group by |
aggregate_function_based_on |
For Sum/Avg | Numeric field |
number_of_groups |
No | Limit groups shown (rest = "Other") |
type |
Yes | Bar, Line, Pie, Donut, Percentage |
filters_json |
No | Filter conditions |
Example — Sales Orders by status:
{
"chart_type": "Group By",
"document_type": "Sales Order",
"group_by_type": "Count",
"group_by_based_on": "status",
"type": "Pie",
"filters_json": "[['Sales Order', 'docstatus', '=', 1]]"
}Uses a Python function registered in hooks.py:
In hooks.py:
dashboard_chart_source = [
"myapp.chart_sources.monthly_revenue.get_data"
]Chart source function:
# myapp/chart_sources/monthly_revenue.py
import frappe
from frappe.utils import getdate, add_months
@frappe.whitelist()
def get_data(chart_name=None, chart=None, no_cache=None,
filters=None, from_date=None, to_date=None,
timespan=None, time_interval=None, heatmap_year=None):
"""
MUST return dict with 'labels' and 'datasets' keys.
"""
data = frappe.db.sql("""
SELECT
DATE_FORMAT(posting_date, '%%Y-%%m') as month,
SUM(grand_total) as total
FROM `tabSales Invoice`
WHERE docstatus = 1
GROUP BY month
ORDER BY month
""", as_dict=True)
return {
"labels": [row.month for row in data],
"datasets": [
{"name": "Revenue", "values": [row.total for row in data]}
]
}All Dashboard Charts consume this format:
{
"labels": ["Jan", "Feb", "Mar", "Apr"],
"datasets": [
{
"name": "Dataset 1",
"values": [100, 200, 150, 300]
},
{
"name": "Dataset 2",
"values": [80, 150, 120, 250]
}
]
}For Heatmap charts:
{
"labels": [],
"dataPoints": {
1704067200: 5, # Unix timestamps as keys
1704153600: 3,
1704240000: 8
}
}| Type | Best For |
|---|---|
Bar |
Comparing categories or time periods |
Line |
Showing trends over time |
Pie |
Distribution of a single metric |
Donut |
Same as pie, with center space |
Percentage |
Stacked percentage comparison |
Heatmap |
Activity density over calendar year |
For advanced configuration, use custom_options (JSON string):
{
"colors": ["#5e64ff", "#ffa00a", "#29cd42"],
"barOptions": {
"stacked": 1,
"spaceRatio": 0.5
},
"lineOptions": {
"regionFill": 1,
"dotSize": 4
},
"axisOptions": {
"xIsSeries": 1,
"shortenYAxisNumbers": 1
},
"tooltipOptions": {
"formatTooltipX": "d => d",
"formatTooltipY": "d => d + ' units'"
}
}Dashboards group multiple charts and number cards on a single page.
| Field | Type | Description |
|---|---|---|
module |
Link | Module this dashboard belongs to |
is_default |
Check | Show by default for the module |
charts |
Table | Dashboard Chart child table |
cards |
Table | Number Card child table |
Each row in the Dashboard Chart child table:
{
"chart": "Monthly Revenue", // Dashboard Chart name
"width": "Full" // "Full" or "Half"
}Each row in the Number Card child table:
{
"card": "Active Users Count" // Number Card name
}Charts and Number Cards can also be added to Workspaces (v14+):
- Add a "Chart" or "Number Card" block to the workspace
- Select the Dashboard Chart or Number Card document
- Configure layout position and width
- Dashboard Charts are cached using
cache_sourcedecorator - Cache key:
"chart-data:{chart_name}" - Cache clears on chart document update
- Force refresh with
no_cache=Trueparameter
- ALWAYS return a numeric value from Number Card custom methods — returning
Noneshows "0" but returning a string crashes - ALWAYS ensure Dashboard Chart source functions return
{"labels": [...], "datasets": [...]}format — any other format causes blank charts - NEVER create Number Cards without setting proper permissions — unauthorized users see errors instead of data
- ALWAYS use
@frappe.whitelist()on custom methods for both Number Cards and Dashboard Chart sources - ALWAYS match
datasets[].valueslength tolabelslength in custom chart sources - NEVER rely on Dashboard Chart cache for real-time data — use
no_cache=Trueor set appropriate cache TTL