reports
Skill68 starsChanged 9 days agoMITView on GitHub
Create reports in Frappe including Report Builder, Query Reports (SQL), and Script Reports (Python + JS). Use when building data analysis views, dashboards, or custom reporting features.
7 other projects use a version of this.
Adopted by
Most recently taken up by
- garrytan/gbraintheir own version, first seen Oct 17, 2026
- DenchHQ/ironclawtheir own version, first seen Oct 8, 2026
- DenchHQ/DenchClawtheir own version, first seen Oct 8, 2026
- hurttlocker/o8their own version, first seen Oct 4, 2026
- msrv-tech/skillstheir own version, first seen Oct 2, 2026
- supermodo/skillstheir own version, first seen Sep 26, 2026
Used with
Projects with it also use ship.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
AWSSkillship
Finalize dev work on an issue branch — update docs, push, and open a PR.
Also in 3 projects that use this
Other versions7 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 7
- garrytan/gbrain★ 32ksame name
- DenchHQ/DenchClaw★ 1.7ksame name
- DenchHQ/ironclaw★ 1.7ksame name
- msrv-tech/skills★ 60same name
- hurttlocker/o8★ 24same name
- supermodo/skills★ 1same name
- yuntian05/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository13 other files it gives its agents.
- api-developmentSkill
- app-developmentSkill
- desk-customizationSkill
- doctype-developmentSkill
- enterprise-patternsSkill
- frappe-managerSkill
- frontend-developmentSkill
- printing-templatesSkill
- project-triageSkill
- routerSkill
- testingSkill
- ui-patternsSkill
- web-formsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. 0 are common, 9 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 5 edited versions we read keep these.
9
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 38
Create via UI with no code:
- Distinctive: line 42
Add columns, filters, sorting, and grouping via the builder UI
- Distinctive: line 48
Create Report → Type = "Query Report"
- Very specific: line 80
Filter variables: Use %(filter_name)s for parameterized queries.
- Very specific: line 241
Create sales_summary.html in the report folder for a custom print layout:
- Very specific: line 277
Report not found: Check module path and is_standard setting.
- Very specific: line 278
SQL syntax error: Test query in bench --site <site> mariadb first
- Very specific: line 279
No data returned: Check docstatus filter; verify filters match data
- Distinctive: line 280
Permission denied: Verify Reference DocType permissions for the user's role
- Distinctive: line 281
Slow query: Add indexes; use Query Builder.
- Distinctive: line 295
Validate filters: Check filter values before building queries.
- Distinctive: line 296
Handle empty results: Always handle case where query returns no data.
- Distinctive: line 298
Limit result sets: Add LIMIT clause or pagination for large datasets
- Distinctive: line 299
Check permissions in execute: Verify user has permission to see the data
“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Frappe Reports
Build reports using Report Builder, Query Reports (SQL), or Script Reports (Python + JS).
When to use
- Creating data analysis or summary reports
- Building SQL-based query reports
- Implementing complex reports with Python logic and JS UI
- Adding custom filters, formatters, and charts to reports
- Creating printable report formats
Inputs required
- Report purpose and data requirements
- Source DocType(s) for the report
- Filter requirements
- Column definitions (fields, types, formatting)
- Whether report is standard (app-bundled) or custom (site-specific)
Procedure
0) Choose report type
| Type | Complexity | Code Required | Best For |
| Report Builder | Low | None | Simple field selection, grouping, sorting |
| Query Report | Medium | SQL only | Direct SQL queries, joins, aggregations |
| Script Report | High | Python + JS | Complex logic, computed fields, dynamic filters |
1) Report Builder
Create via UI with no code:
- Navigate to the Report list → New Report
- Select Reference DocType
- Choose Report Type = "Report Builder"
- Add columns, filters, sorting, and grouping via the builder UI
2) Query Report
Reports using raw SQL queries:
- Create Report → Type = "Query Report"
- Set Reference DocType (controls permissions)
- Write SQL query
SELECT
`tabSales Order`.name AS "Sales Order:Link/Sales Order:200",
`tabSales Order`.customer AS "Customer:Link/Customer:200",
`tabSales Order`.transaction_date AS "Date:Date:120",
`tabSales Order`.grand_total AS "Grand Total:Currency:150",
`tabSales Order`.status AS "Status:Data:100"
FROM `tabSales Order`
WHERE `tabSales Order`.docstatus = 1
{% if filters.company %}
AND `tabSales Order`.company = %(company)s
{% endif %}
{% if filters.from_date %}
AND `tabSales Order`.transaction_date >= %(from_date)s
{% endif %}
ORDER BY `tabSales Order`.transaction_date DESCColumn format in SELECT: "Label:Fieldtype/Options:Width"
| Fieldtype | Example |
| Link | "Customer:Link/Customer:200" |
| Currency | "Amount:Currency:150" |
| Date | "Date:Date:120" |
| Int | "Quantity:Int:100" |
| Data | "Status:Data:100" |
Filter variables: Use %(filter_name)s for parameterized queries.
3) Script Report (standard)
For app-bundled reports with full Python + JS control:
Create the report structure:
my_app/
└── my_module/
└── report/
└── sales_summary/
├── sales_summary.json # Report metadata
├── sales_summary.py # Python data logic
└── sales_summary.js # JS filters and UIPython script (sales_summary.py):
import frappe
from frappe import _
def execute(filters=None):
columns = get_columns()
data = get_data(filters)
chart = get_chart(data)
return columns, data, None, chart
def get_columns():
return [
{
"label": _("Customer"),
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer",
"width": 200
},
{
"label": _("Total Orders"),
"fieldname": "total_orders",
"fieldtype": "Int",
"width": 120
},
{
"label": _("Total Amount"),
"fieldname": "total_amount",
"fieldtype": "Currency",
"width": 150
},
{
"label": _("Average Order"),
"fieldname": "avg_order",
"fieldtype": "Currency",
"width": 150
}
]
def get_data(filters):
conditions = get_conditions(filters)
data = frappe.db.sql("""
SELECT
customer,
COUNT(name) as total_orders,
SUM(grand_total) as total_amount,
AVG(grand_total) as avg_order
FROM `tabSales Order`
WHERE docstatus = 1 {conditions}
GROUP BY customer
ORDER BY total_amount DESC
""".format(conditions=conditions), filters, as_dict=True)
return data
def get_conditions(filters):
conditions = ""
if filters.get("company"):
conditions += " AND company = %(company)s"
if filters.get("from_date"):
conditions += " AND transaction_date >= %(from_date)s"
if filters.get("to_date"):
conditions += " AND transaction_date <= %(to_date)s"
return conditions
def get_chart(data):
if not data:
return None
return {
"data": {
"labels": [d.customer for d in data[:10]],
"datasets": [{
"name": _("Total Amount"),
"values": [d.total_amount for d in data[:10]]
}]
},
"type": "bar"
}JavaScript script (sales_summary.js):
frappe.query_reports["Sales Summary"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1)
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.datetime.get_today()
}
],
onload(report) {
// Custom initialization
},
formatter(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
// Highlight high-value customers
if (column.fieldname === "total_amount" && data.total_amount > 100000) {
value = `<span style="color: green; font-weight: bold">${value}</span>`;
}
return value;
}
};Report JSON (sales_summary.json):
{
"name": "Sales Summary",
"doctype": "Report",
"report_type": "Script Report",
"ref_doctype": "Sales Order",
"module": "My Module",
"is_standard": "Yes",
"disabled": 0
}4) Add report print format
Create sales_summary.html in the report folder for a custom print layout:
<h2>Sales Summary Report</h2>
<table class="table table-bordered">
<tr>
<th>Customer</th>
<th>Orders</th>
<th>Total</th>
</tr>
{% for row in data %}
<tr>
<td>{{ row.customer }}</td>
<td>{{ row.total_orders }}</td>
<td>{{ frappe.format(row.total_amount, {fieldtype: 'Currency'}) }}</td>
</tr>
{% endfor %}
</table>5) Register report in hooks (optional)
Reports are auto-discovered if they follow the standard directory structure. No hooks.py entry is needed for standard reports.
Verification
- Report appears in Report list
- Filters work correctly and affect results
- Columns display with proper formatting
- Chart renders (if applicable)
- Permissions respected (only authorized users see data)
- Print format works
- Performance acceptable for expected data volume
Failure modes / debugging
- Report not found: Check module path and
is_standardsetting; runbench migrate - SQL syntax error: Test query in
bench --site <site> mariadbfirst - No data returned: Check
docstatusfilter; verify filters match data - Permission denied: Verify Reference DocType permissions for the user's role
- Slow query: Add indexes; use Query Builder; limit result set
Escalation
- For DocType schema →
doctype-development - For API endpoints (report data via API) →
api-development - For Desk UI customization →
desk-customization
References
- references/reports.md — Report types, creation, and examples
Guardrails
- Validate filters: Check filter values before building queries; handle empty/invalid input
- Handle empty results: Always handle case where query returns no data; show appropriate message
- Use
frappe.db.escape(): Escape user input in SQL queries to prevent injection - Limit result sets: Add LIMIT clause or pagination for large datasets
- Check permissions in execute: Verify user has permission to see the data
Common Mistakes
| Mistake | Why It Fails | Fix |
| SQL injection via filters | Security vulnerability | Use frappe.db.escape() or Query Builder with parameters |
| Missing permission checks | Unauthorized data access | Verify frappe.has_permission() or filter by allowed records |
| Unbounded queries | Timeouts, memory issues | Add LIMIT, use pagination, or filter by date range |
| Wrong column fieldtype | Formatting issues | Match column fieldtype to data (Currency, Date, etc.) |
| Not handling None in aggregations | Errors or wrong totals | Use COALESCE() or IFNULL() in SQL |
Hardcoded docstatus assumptions | Missing draft/cancelled records | Explicitly filter docstatus based on report needs |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

