Skip to main content

Introduction: What Is a Script Report in Frappe?

A script report in Frappe Framework v15 is a report powered by Python code instead of raw SQL. It allows developers to write server-side logic to fetch, process, and format report data dynamically.

Script reports are ideal when:

  • Business logic is complex
  • Data requires conditional processing
  • Permissions and workflows must be enforced
  • SQL alone is not sufficient

Why Use Script Reports Instead of Query Reports?

Script reports are preferred when you need:

  • Complex calculations and transformations
  • Conditional data generation
  • Multi-source data aggregation
  • Permission-aware logic
  • Cleaner and safer abstraction over SQL

They trade raw performance for flexibility and maintainability.

Types of Reports in Frappe v15 (Context)

Frappe supports:

  • Query Reports – SQL-based, read-only
  • Script Reports – Python-based logic
  • Report Builder Reports – UI-driven, basic

This guide focuses exclusively on Script Reports.

How Script Reports Work in Frappe v15

Answer:
Script reports execute a Python function that returns:

  1. Column definitions
  2. Row data

Frappe calls this function whenever the report is loaded or filtered, ensuring real-time, permission-safe results.

How to Create a Script Report (Step-by-Step)

Step 1: Create a New Report

Go to Report List → New and set:

  • Report Name
  • Ref DocType
  • Report Type: Script Report

Save the report.

Step 2: Create the Report Python File

Frappe automatically links the report to a Python module located at:

app_name/app_name/report/report_name/report_name.py

This file contains the report execution logic.

Basic Script Report Structure (Frappe v15)

Every script report must expose an execute function.

def execute(filters=None):
columns = get_columns()
data = get_data(filters)
return columns, data

This structure is mandatory in Frappe v15.

Defining Columns in a Script Report

Columns are defined as dictionaries.

def get_columns():
return [
{
"label": "Customer",
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer",
"width": 180,
},
{
"label": "Total Amount",
"fieldname": "total",
"fieldtype": "Currency",
"width": 120,
}
]

Each column definition controls display, data type, and formatting.

Fetching Data in Script Reports

Using Frappe ORM (Recommended)

import frappe
def get_data(filters):
return frappe.get_all(
"Sales Invoice",
filters={"docstatus": 1},
fields=["customer", "grand_total as total"]
)

This ensures:

  • Permission enforcement
  • Cleaner abstraction
  • Upgrade safety

Using Filters in Script Reports

Filters are passed automatically to the execute function.

def get_data(filters):
conditions = {}
if filters.get("customer"):
conditions["customer"] = filters["customer"]
return frappe.get_all(
"Sales Invoice",
filters=conditions,
fields=["customer", "grand_total"]
)

Filters must be defined in the Report form.

Adding Filters to the Report

In the Filters section of the report:

  • Define fieldname
  • Select fieldtype
  • Set options (if required)

Frappe handles filter injection automatically.

Permissions and Security in Script Reports

Script reports:

  • Respect user permissions
  • Follow DocType access rules
  • Obey role-based visibility
  • Prevent unauthorized data exposure

This makes them safer than raw SQL reports.

Best Practices for Script Reports

Use ORM instead of raw SQL
Improves readability and permission safety.

Keep logic modular
Separate column and data logic.

Avoid heavy loops
Optimize for large datasets.

Validate filters
Never trust unvalidated input.

Common Issues and Troubleshooting

Report loads but shows no data

  • Check filter logic
  • Verify DocType permissions
  • Confirm returned data structure

Columns not rendering

  • Ensure fieldname matches data keys
  • Validate column dictionary structure

Performance issues

  • Limit fetched fields
  • Use indexed filters
  • Avoid nested loops

Advanced Use Case: Aggregated Script Report

def get_data(filters):
return frappe.db.sql("""
SELECT customer, SUM(grand_total) as total
FROM `tabSales Invoice`
WHERE docstatus = 1
GROUP BY customer
""", as_dict=True)

Use raw SQL only when necessary and with caution.

Industry Relevance and Use Cases

Script reports are widely used for:

  • MIS and management dashboards
  • Financial analysis
  • Operational performance tracking
  • Custom business intelligence

They are essential for ERPNext enterprise reporting.

Technical Prerequisites

  • Frappe Framework Version 15
  • Python knowledge
  • Understanding of DocTypes and permissions
  • Access to Report and DocType modules

Conclusion

Script reports in Frappe Framework v15 provide maximum flexibility for building dynamic, permission-aware reports using Python. They are ideal for complex business logic where SQL-based query reports fall short.
For ERPNext and Frappe developers, script reports are a core reporting capability that enables advanced analytics while maintaining security and maintainability.

Rating: 5 / 5 (1 votes)