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

  1. Open the list view — say Production Batch.
  2. Switch to Report view: the toggle at the top right of the list, or add /view/report to the URL.
  3. 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. - ChartMenu → Toggle Chart to add one above the table.
  4. 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

  1. Awesome Bar → report+ Add Report.
  2. Fill in: - Report NameCuts Per Table Per Week - Report TypeQuery Report - Ref DocType — the DocType it is mostly about (Cutting Order); this decides who can see it - Module — where it is filed - Is StandardNo
  3. Write SQL in the Query box.
  4. 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)
Caution

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.

  1. Create a Report as above, but set Report Type = Script Report.
  2. In the script, build columns and data and assign the pair to result:
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, Average or Group 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.

WhatsApp