ERPNext CustomizationPython

How to Override Standard DocType Methods in Frappe/ERPNext

Override Exiting Python Methods

Learn how to dynamically override or 'monkey patch' core DocType methods in Frappe and ERPNext using a custom Python script, demonstrated with the Sales Invoice DocType.

In Frappe development, there are times when you need to alter the core behavior of a standard DocType beyond what is possible with standard hooks. This snippet demonstrates a powerful technique known as 'monkey patching', where you dynamically replace a DocType's class methods with your own custom functions at runtime.

This approach allows for deep customization of core logic, such as modifying the cancellation process of a Sales Invoice. However, it should be used with caution as it can have wide-ranging effects and may need updates if the underlying core code changes.

custom_app/custom_module/sales_invoice_override.pypython
1# In custom_app/custom_module/sales_invoice_override.py
2import frappe
3from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice
4
5def shoutout(self):
6 print("Yay!")
7
8def before_cancel(self):
9 self.shoutout()
10 # Note: The original 'update_time_sheet' method might not exist on all versions.
11 # This demonstrates calling another custom or existing method.
12 # In a real scenario, you might call the original method or implement new logic.
13 frappe.msgprint("Custom cancel logic executed!")
14
15def apply_sales_invoice_patch():
16 SalesInvoice.shoutout = shoutout
17 SalesInvoice.before_cancel = before_cancel

Understanding This Code

What It Does

This script dynamically adds a new method (`shoutout`) and overrides an existing method (`before_cancel`) on the standard `SalesInvoice` DocType class. All Sales Invoice document instances will use this new logic globally after the patch is applied.

When To Use

Use this technique when you need to change the fundamental behavior of a core DocType method that doesn't have a suitable server-side hook. It's ideal for injecting custom validation, logic, or integrations directly into a DocType's workflow.

Prerequisites

  • A custom Frappe app must be created.
  • Working knowledge of Python classes and functions.
  • Bench command-line interface for restarting the server.

Key Concepts

Important ideas to understand in this code

Monkey Patching

Monkey patching is the process of dynamically modifying classes or modules at runtime. In this context, we are not editing the ERPNext source code file, but instead assigning our custom functions to the `SalesInvoice` class in memory after it has been loaded.

Learn more

DocType Classes

Every DocType in Frappe is backed by a Python class. For the 'Sales Invoice' DocType, the class is `SalesInvoice`. By modifying this class, we change the behavior of all documents of that type.

Learn more

Execution via Hooks

For a patch to be effective, it must be executed when the Frappe application starts. This is typically done by calling the patch function (e.g., `apply_sales_invoice_patch`) from a startup hook like `on_startup` in your custom app's `hooks.py` file.

Learn more

Step-by-Step Tutorial

Follow along to understand how this code works

1

Create the Python File

Inside your custom Frappe app, create a new Python file. A good practice is to organize such overrides in a dedicated module, for example: `my_app/overrides/sales_invoice.py`.

python
# my_app/overrides/sales_invoice.py

# ... your code will go here
Next Step
2

Define Custom Methods

Write the Python functions that will serve as your new or replacement methods. The first argument must always be `self`, which refers to the document instance.

python
import frappe
from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice

def shoutout(self):
	print("Yay!")

def before_cancel(self):
	self.shoutout()
	frappe.msgprint("Custom cancel logic executed!")
Next Step
3

Create the Patching Function

Create a wrapper function that performs the assignment of your custom methods to the DocType class. This keeps the patching logic organized.

python
def apply_sales_invoice_patch():
	SalesInvoice.shoutout = shoutout
	SalesInvoice.before_cancel = before_cancel
Next Step
4

Execute the Patch via Hooks

To ensure your patch is applied every time the server starts, you must call the patching function from your app's `hooks.py` file using the `on_startup` hook.

python
# my_app/hooks.py

on_startup = "my_app.overrides.sales_invoice.apply_sales_invoice_patch"
Next Step
5

Restart the Bench

Server-side Python code changes are not loaded automatically. You must restart the bench for the new hooks and code to take effect.

bash
bench restart

Common Issues & Solutions

Troubleshoot problems you might encounter