Creating Reports
GarmentFlow ships a lot of reports, and you will still want your own — the question your accountant asks every month, the list your buyer wants every Monday.
There are three ways to build one. Start at the top and only move down when you must.
| Kind | Built with | Needs |
|---|---|---|
| Report Builder | Clicking in a list view | Nothing |
| Query Report | One SQL statement | SQL |
| Script Report | Python | Python, and server scripts enabled |
1. Report Builder
For "show me these columns, filtered like this, grouped like that", from a single DocType.
Building one
- Open the list view — say Production Batch.
- Switch to Report view: the toggle at the top right of the list, or add
/view/reportto the URL. - You now have a spreadsheet-style grid. Shape it:
- Columns — use the column header menus to add, remove and resize. Pick from a linked document lets you pull a column from a linked record, so you can show the Production Order's customer alongside the batch.
- Filters — the filter area at the top.
status = In Progress,end_date < today. - Group By — group rows and get counts or sums per group. - Sort — click a column header. - Totals — the menu offers a totals row. - Chart — Menu → Toggle Chart to add one above the table. - Menu → Save, and give it a name.
Your report now appears under that DocType's reports and in the Awesome Bar.
Sharing it
A saved report belongs to you until you share it:
- Menu → Share to give named users or roles access.
- Or open the Report record itself (Awesome Bar →
report) and add roles to it.
Exporting and scheduling
- Menu → Export for CSV or Excel.
- To have it emailed on a schedule, create an Auto Email Report: Awesome Bar →
auto email report→ + Add, choose your report, the recipients, the frequency and the format.
That combination — Report Builder plus Auto Email Report — covers a surprising share of "can I get this every Monday" requests, with no code at all.
2. Query Report
When the answer spans several tables, or needs arithmetic the builder can't do.
Creating one
- Awesome Bar →
report→ + Add Report. - Fill in:
- Report Name —
Cuts Per Table Per Week- Report Type —Query Report- Ref DocType — the DocType it is mostly about (Cutting Order); this decides who can see it - Module — where it is filed - Is Standard —No - Write SQL in the Query box.
- Save, then open it from the Awesome Bar.
Table and column names
Frappe's tables are the DocType name prefixed with tab, in backticks:
`tabCutting Order`
`tabProduction Batch`
`tabBatch Operation Step`
Columns are fieldnames. Every DocType also has name, owner, creation, modified, modified_by, docstatus and idx.
Column headers do the formatting
A Query Report reads the column alias to decide the heading, the type, and the width:
Label:Fieldtype/Options:Width
So:
SELECT
co.production_unit AS "Cutting Unit:Link/Production Unit:160",
co.name AS "Cutting Order:Link/Cutting Order:140",
co.date AS "Date:Date:100",
co.length AS "Length (m):Float:100",
COUNT(coi.name) AS "Items:Int:80"
FROM `tabCutting Order` co
LEFT JOIN `tabCutting Order Item` coi ON coi.parent = co.name
WHERE co.docstatus < 2
GROUP BY co.name
ORDER BY co.date DESC
Link/Production Unit makes the cell clickable through to the record. Use it — a report you can click into is worth several you can't.
Types you can use: Data, Int, Float, Currency, Percent, Date, Datetime, Check, Link/<DocType>.
Filters
Add rows to the Filters table on the Report — each with a fieldname, label and type — then reference them in the SQL as %(fieldname)s:
WHERE co.docstatus < 2
AND co.date BETWEEN %(from_date)s AND %(to_date)s
AND (%(production_unit)s IS NULL OR co.production_unit = %(production_unit)s)
Always use %(name)s placeholders. Never paste a filter value into the SQL string — that is a SQL injection, and it is exactly as dangerous on an internal system as an external one.
Permissions
A Query Report shows whatever the SQL returns; it does not apply record-level permissions. Restrict it by role on the Report record, and don't expose costs in a report a role shouldn't see just because the SQL was convenient.
3. Script Report
For anything requiring real logic: a running balance, a pivot with dynamic columns, data assembled from several sources.
Script Reports need server scripts enabled — see Server scripts.
- Create a Report as above, but set Report Type =
Script Report. - In the script, build
columnsanddataand assign the pair toresult:
columns = [
{"label": "Production Order", "fieldname": "production_order",
"fieldtype": "Link", "options": "Production Order", "width": 160},
{"label": "Planned", "fieldname": "planned", "fieldtype": "Float", "width": 100},
{"label": "Produced", "fieldname": "produced", "fieldtype": "Float", "width": 100},
{"label": "Variance %", "fieldname": "variance", "fieldtype": "Percent", "width": 110},
]
data = []
orders = frappe.db.get_list(
"Production Order",
filters={"status": ["in", ["In Progress", "Completed"]]},
fields=["name", "total_planned_qty"],
limit=500,
)
for o in orders:
produced = frappe.db.count("Production Batch", {"production_order": o.name, "status": "Completed"})
planned = o.total_planned_qty or 0
data.append({
"production_order": o.name,
"planned": planned,
"produced": produced,
"variance": ((produced - planned) / planned * 100) if planned else 0,
})
result = [columns, data]
Filters set on the Report arrive in a filters dict:
unit = filters.get("production_unit")
Two practical rules: always limit your queries (a report that pulls every row will time out on a real database), and query in bulk — one query returning 500 rows, not 500 queries in a loop.
Charts, number cards and dashboards
Reports answer questions. Dashboards show state.
Dashboard Chart — Awesome Bar → dashboard chart → + Add:
- Chart Type:
Count,Sum,AverageorGroup By. - Document Type, the field to aggregate, filters, and a time span.
- Chart style, then Add to Dashboard.
Number Card — a single figure: a count or sum with filters. GarmentFlow's own KPI tiles are number cards, so yours sit alongside them.
Dashboard — a page holding charts and cards. Create one, add your charts, share it with a role.
To put a chart or card on a workspace, open the workspace, click Edit, and add it there.
Choosing
| Question | Use |
|---|---|
| "All batches at Stitching, with the customer" | Report Builder |
| "Cut orders per table per week" | Query Report |
| "Planned vs produced with a computed variance" | Query Report or Script Report |
| "Running WIP balance per operation over time" | Script Report |
| "How many orders are overdue, on a dashboard" | Number Card |
If you are about to write SQL, check the existing reports first. WIP Aging, Operation Cost Variance and the subcontracting reports already answer a lot of what people set out to rebuild.
Troubleshooting
| Symptom | Cause |
|---|---|
| "No permission" | No role on the Report record, or the wrong Ref DocType |
| Empty result | Filters excluding everything, or docstatus — draft is 0, submitted 1, cancelled 2 |
| Times out | No limit, or a query inside a loop |
| Numbers disagree with a screen | You counted a header field where the screen derives from the ledger. See Architecture |
| Column shows raw text | The alias is missing its :Fieldtype |
What to do next
Continue to Configuring notifications to have the system tell people rather than waiting to be asked.