Customizing standard functionality in Frappe and ERPNext is often easy ā hooks, events, and overrides are there for a reason.
However, there are cases where Frappeās official extension points are not enough:
- Some functions arenāt whitelisted.
- Some reports donāt belong to a Doctype (so
override_doctype_class
wonāt work). - Some classes are buried deep and are never exposed for hooks.
In such situations, you can still achieve full control using Monkey Patching ā a powerful technique in Python and JavaScript that allows you to override any function, class, or file at runtime.
Letās understand how you can do this, the right way, using real examples from ERPNext.
Problem Statement: Overriding a Report (General Ledger)
Suppose you want to customize ERPNextās General Ledger report, but:
- You canāt override it from
hooks.py
because itās not tied to a Doctype. - You canāt modify core files (bad practice).
Still, you want full control over both the backend (Python) and frontend (JavaScript) of the report.
Overriding Python Code via __init__.py
Hereās how to override the Python logic without touching core files:
1. Create Your Custom Python File
Path:
sudhanshu_app/custom_script/report/general_ledger/general_ledger.py
In this file:
- Copy the original code from
erpnext/accounts/report/general_ledger/general_ledger.py
. - Modify it according to your business needs.
Example:
def execute(filters=None):
# Your custom logic
return custom_columns, custom_data
2. Monkey Patch via __init__.py
Path:
sudhanshu_app/custom_script/report/general_ledger/__init__.py
Write the following:
import frappe
from sudhanshu_app.custom_script.report.general_ledger.general_ledger import execute as custom_general_ledger
import erpnext.accounts.report.general_ledger.general_ledger
# Replace the original execute function with your custom one
erpnext.accounts.report.general_ledger.general_ledger.execute = custom_general_ledger
Boom! Now whenever General Ledger report is called, your version of execute
runs instead of the original.
No core file touched.
Fully reversible.
Upgradable safely (with minimal merge conflicts).
Overriding JavaScript Code via hooks.py
The frontend (JavaScript) of reports is loaded dynamically in Frappe. You can override it too!
1. Create Your Custom JS File
Path:
sudhanshu_app/public/js/report/general_ledger.js
In this file:
- Copy the full original JavaScript code from
erpnext/public/js/erpnext/accounts_report/general_ledger.js
(or wherever it is). - Make your custom changes.
Example:
frappe.query_reports["General Ledger"] = {
"filters": [...],
"formatter": function (value, row, column, data, default_formatter) {
// Your custom formatter
}
}
2. Include It in hooks.py
In your hooks.py
, add:
app_include_js = [
"/assets/sudhanshu_app/js/report/general_ledger.js"
]
This ensures your JS loads after ERPNextās base JS and replaces the standard report behavior.
Key Takeaways
- Monkey Patching gives you freedom to override any Python function, class, or method in Frappe.
- Overriding JavaScript is as simple as copying and injecting your own custom script via
hooks.py
. - Best Practice: Always comment clearly that this is a monkey patch so your future team (or your future self) knows itās intentional.
- Upgrade-Proofing: Since youāre not editing core files, upgrades are relatively safe ā just review changes in the original files after major updates.
Beyond Reports: What Else Can You Override?
- Custom whitelisted functions
- Server scripts
- API endpoints
- Background jobs
- Page controllers
- Event handlers
- Class methods
Final Thoughts
Monkey patching gives you superpowers, but with great power comes great responsibility.
Use it carefully, transparently, and only when needed ā preferably when standard hooks and extension points arenāt available.
By mastering these techniques, you can unlock full customization potential in any Frappe / ERPNext project ā without ever touching the core!