Frappe_Claude_Skill_Package frappe-syntax-reports
install
source · Clone the upstream repo
git clone https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/source/syntax/frappe-syntax-reports" ~/.claude/skills/openaec-foundation-frappe-claude-skill-package-frappe-syntax-reports && rm -rf "$T"
manifest:
skills/source/syntax/frappe-syntax-reports/SKILL.mdsource content
Reports: Query, Script & Report Builder
Quick Reference
Report Types at a Glance
| Type | Code Required | Use Case | Permission |
|---|---|---|---|
| Report Builder | None | Simple single-DocType listing with filters, group by | Any user |
| Query Report | SQL only | Direct SQL queries, legacy column format | System Manager |
| Script Report (Standard) | Python + JS | Complex logic, charts, summaries, trees | Administrator + Developer Mode |
| Script Report (Custom) | Python in UI | Quick custom reports without app deployment | System Manager |
Script Report execute() Return Values
def execute(filters=None): columns = [...] # List of dicts data = [...] # List of dicts or lists message = "..." # Optional: HTML message above report chart = {...} # Optional: chart configuration report_summary = [...] # Optional: summary cards skip_total_row = False # Optional: suppress auto-total return columns, data, message, chart, report_summary, skip_total_row
Column Definition (Dict Format)
columns = [ { "fieldname": "customer", "label": _("Customer"), "fieldtype": "Link", "options": "Customer", "width": 200 }, { "fieldname": "amount", "label": _("Amount"), "fieldtype": "Currency", "options": "currency", # field in row holding currency code "width": 120 } ]
Query Report Column Format (Legacy String)
SELECT name as "Sales Order:Link/Sales Order:200", customer as "Customer:Link/Customer:180", grand_total as "Total:Currency:120", transaction_date as "Date:Date:100" FROM `tabSales Order` WHERE docstatus = 1
Format:
"Label:Fieldtype/Options:Width" — Options only needed for Link, Dynamic Link, Currency.
Filter Definition (JS)
frappe.query_reports["My Report"] = { 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: "status", label: __("Status"), fieldtype: "Select", options: "\nDraft\nSubmitted\nCancelled" } ] };
Decision Tree: Which Report Type?
Need a report? ├─ Simple list/group of one DocType → Report Builder │ (no code, UI-only, supports Group By with Count/Sum/Avg) ├─ Direct SQL query, no Python logic needed → Query Report │ (SQL in Report doc, column format in aliases) ├─ Complex logic, calculations, charts → Script Report (Standard) │ (Python .py + JS .js files, requires Developer Mode) └─ Quick one-off with Python but no app deploy → Script Report (Custom) (Python in Report doc UI, System Manager can create)
Script Report returns what? ├─ Just data → return columns, data ├─ Data + chart → return columns, data, None, chart ├─ Data + summary → return columns, data, None, None, report_summary ├─ Data + message → return columns, data, message └─ Everything → return columns, data, message, chart, report_summary, skip_total_row
Supported Fieldtypes for Columns
| Fieldtype | Options Required | Notes |
|---|---|---|
| No | Plain text |
| DocType name | Clickable link to document |
| Fieldname holding DocType | Pair with a column containing DocType |
| Currency field or code | Fieldname in row that holds currency |
| No | Decimal number |
| No | Integer |
| No | Shows percentage bar |
| No | Date display |
| No | Date + time |
| No | Boolean checkbox |
| No | Dropdown value |
| No | Long text |
| No | Raw HTML rendering |
Supported Filter Fieldtypes
| Fieldtype | Options | Behavior |
|---|---|---|
| DocType name | Autocomplete from DocType |
| Newline-separated values | Dropdown with fixed options |
| — | Date picker |
| — | Returns list |
| — | Boolean toggle |
| Fieldname of Link filter | Depends on another filter value |
| — | Free text input |
| — | Numeric input |
| DocType name | Multiple value selection |
Chart Data Format
chart = { "data": { "labels": ["Jan", "Feb", "Mar", "Apr"], "datasets": [ {"name": _("Revenue"), "values": [100, 200, 150, 300]}, {"name": _("Expense"), "values": [80, 150, 120, 250]} ] }, "type": "bar", # bar, line, pie, donut, percentage "fieldtype": "Currency", "options": "currency", "currency": "USD", "colors": ["#5e64ff", "#ffa00a"] # Optional custom colors }
Report Summary Format
report_summary = [ { "value": total_revenue, "label": _("Total Revenue"), "datatype": "Currency", "currency": "USD", "indicator": "Green" # Green, Blue, Orange, Red }, { "value": total_count, "label": _("Total Orders"), "datatype": "Int", "indicator": "Blue" } ]
Prepared Reports
For reports processing large datasets, enable Prepared Report to run asynchronously:
- Set
in the Report documentprepared_report = 1 - User clicks "Generate New Report" — runs in background via
enqueue() - Results stored in file; user downloads or views when ready
- ALWAYS use for reports that query > 100k rows or take > 30 seconds
Number Cards
| Source Type | Required Fields | How It Works |
|---|---|---|
| Document Type | , , | SQL aggregate on DocType |
| Report | , , | Pulls value from a report column |
| Custom Method | | Calls a whitelisted Python method |
Custom method signature:
@frappe.whitelist() def get_total_active_users(filters=None): return frappe.db.count("User", {"enabled": 1})
Dashboard Charts
| Source | Configuration | Data Format |
|---|---|---|
| Report | Set , select report | Uses report's chart data |
| Custom | Set , define | Hook returns |
| Group By | Set , pick field | Auto-aggregates by field |
Dashboard Chart Source hook in
hooks.py:
dashboard_chart_source = [ "myapp.dashboard_chart_source.get_chart_data" ]
Critical Rules
- ALWAYS define columns as list of dicts with
,fieldname
,label
. The legacy string format is ONLY for Query Report SQL aliases.fieldtype - NEVER return
forNone
orcolumns
indata
— ALWAYS return empty listsexecute()
.[] - ALWAYS use
for translatable labels in columns and report_summary._(...) - NEVER use
with user-supplied filter values directly in f-strings — ALWAYS pass as parameters:frappe.db.sql
.frappe.db.sql(query, filters, as_dict=True) - ALWAYS set
on the Report document — it controls user access permissions.Reference DocType - NEVER omit
in column definitions — columns without width render poorly.width - ALWAYS match
length todatasets[].values
length in chart data — mismatched lengths cause chart rendering errors.labels
See Also
- references/query-report.md — Complete Query Report API
- references/script-report.md — Script Report JS API
- references/examples.md — Working report examples
- references/anti-patterns.md — Common report mistakes
- references/dashboard.md — Number Cards, Dashboard Charts