Monkey Patching in Frappe: Override Core Logic the Clean Way
Learn how to safely override core functionality in Frappe and ERPNext using monkey patching
🔍 What is Monkey Patching?
Monkey patching means replacing an existing function or method with your version while the app is running.
In Python (which Frappe is built on), this is easy because everything is an object. That means we can load a function from the system and then swap it with our version.
With this, you can:
- Replace a function in Frappe or ERPNext without touching the original files
- Add your business logic
- Make system-wide changes without breaking future updates
Why Should You Use Monkey Patching?
Monkey patching isn’t always the first option, but it’s super helpful when:
🔁 You want custom logic
Maybe you want to change how a Sales Invoice is validated or add a message before it submits.
You want to keep your changes safe
If you edit core files directly, those changes will disappear when you update. Monkey patching keeps your custom logic in your own app — so updates won't break your system.
There's no hook or event
Frappe gives us a lot of ways to customize (like doc_events
), but sometimes there’s no official way to override a method. Monkey patching solves that.
🧠 How Does Monkey Patching Work in Frappe?
There are just a few simple steps:
✅ Step 1: Create your custom logic
You write your own version of the method you want to change.
✅ Step 2: Replace the original method with your version
You tell Frappe, “Hey, use my function instead of the built-in one.”
✅ Step 3: Make sure this replacement happens when the app starts
You do this by linking your patch file in your app’s hooks.py
, so it loads automatically.
Keep It Clean: Use One File for All Patches
Here’s a best practice: keep all your monkey patches in one place.
Create a folder like this in your custom app:
kotlinCopyEditcustom_app/├── monkey_patches/│ └── __init__.py ← this is where all your patch code goes├── hooks.py
Why use one file?
- ✅ Everything is easy to find
- ✅ No confusion about where patches are
- ✅ It’s easier to debug and update later
When Not to Use Monkey Patching
Monkey patching is powerful — but it’s not always the best choice.
Don’t use monkey patching if:
- There's already a built-in hook (
doc_events
,override_whitelisted_methods
, etc). - You can subclass a controller or use a custom Doctype instead
- You want to avoid future maintenance headaches
- You’re not sure if a method will change often in future updates
Always check if there's a cleaner way before monkey patching.
Summary
Monkey patching is a great tool when:
- You want to customize Frappe or ERPNext behavior
- You can’t use standard hooks
- You want your changes to survive updates
But — keep it clean:
- Put all patches in one file (
monkey_patches/__init__.py
) - Link that file in
hooks.py
so it loads when the app starts
With this setup, you can safely customize your system without ever touching core files.
Example: Monkey Patching ERPNext Tax Functions (No Extra Files)
In this example, we wanted to customize how ERPNext handles tax breakup display (both HTML and data), without modifying any core files.
✅ Patch Directly from hooks.py
Instead of keeping monkey patch logic in a separate patch file, we wrote it directly in hooks.py
:
import erpnext.controllers.taxes_and_totalsimport india_compliance.gst_india.overrides.transactionimport your_custom_app.overrides.custom_taxes as custom_taxes# Apply monkey patcheserpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_html = custom_taxes.custom_get_itemised_tax_breakup_htmlerpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data = custom_taxes.custom_get_itemised_tax_breakup_dataindia_compliance.gst_india.overrides.transaction.get_itemised_tax_breakup_data = custom_taxes.custom_get_itemised_tax_breakup_data
This makes the patch apply automatically when the app is installed or the server restarts — no need to touch doc_events
or add a custom __init__.py
.
✅ Write Your Custom Functions
Then, in your app's code (e.g. custom_app/overrides/custom_taxes.py
), define the new behavior for tax functions. These will now override ERPNext’s default logic.
Why This Works
- Hook files run early during boot, so the patch is guaranteed to be in place before any document or request needs the function.
- It keeps the patch logic visible and central — easy to track.
- No core changes, no app conflicts, and upgrade-safe.
So, in summary:
- Monkey patch logic = in
hooks.py
- Custom function = in your own app's Python file
- No extra hooks or
doc_events
needed
can You Help Me To Fix it
I have Tried This it is Working In Local / server but Failed In Frappe Cloud some times it is working Some times not working