How to override method in frappe?

I think override_doctype_class hook is part of v13 and above.

1 Like

@revant_one @netchampfaris Is there any way to override the method not belong to a class?


You can refer below :


any updates for version 13.

lifesaver! Exactly what I was looking for. I needed to disable the amounts in CoA as it’s using too much computing resource for very big transactions, when we press expand all.

I used the above method (by revant_one) to override the validate method of EmployeeCheckin doctype. It worked perfectly.

But, something else broke. I have a custom script (JS) on after_save of EmployeeCheckin. This code isn’t executed anymore. Can anyone explain why this happens? How do I get some front end code running on after_save?

I was once told (i don’t remember in what thread) that after_save hook will be triggered no matter how the doc is inserted/saved.
So if it is not (as is my case) it might be a bug. Report it on the github.

refer to this app GitHub - aakvatech/CSF_TZ for monkey patching any method

  1. copy and adapt this file into your own custom app
    CSF_TZ/ at master · aakvatech/CSF_TZ · GitHub
  2. create python file under monkey_patches folder like this file.
    CSF_TZ/ at master · aakvatech/CSF_TZ · GitHub

How to override python method which isn’t inside class?

for example, get_leaves_for_period method inside Leave Application package?

I tried:


from import leave_application

def get_leaves_for_period_new(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False):

def override(doc=None, target=None):
    leave_application.get_pending_leaves_for_period = get_pending_leaves_for_period_new

then in

doc_events = {
    "Leave Application": {

        "validate": "",

        "onload": "",

        "refresh": ""


and this approach not working.

did you try this? Hooks

ye, but this is about overriding doctype class, but method get_pending_leaves_for_period inside leave_application is not a class method, it’s a method which is inside but not in Leave Application class.

Did you check this one ?

It uses


frappe.utils.fmt_money =

in the


Yes, also…


import =

and not working

ah… there was a problem somewhere else…

i realised that i override this function again with main one in some other place… (donkey)

sorry, nevermind.

I would like to mention 3 things:

  • My solution was inspired by other one from this thread. Thank you, guys.

  • The other solution used instead of, but that has a side effect: if you uninstall the app, the patches are still loaded (after restart). It may be a bug in frappe, or a feature in python - I don’t know, yet. Using solves the problem.

  • If your patch doesn’t work or works in selected places only, check one more thing. Some functions can be called in more than one namespace. For example:, or frappe.utils.fmt_money (without “data”). That’s because the functions are imported in module’s init file (from [something] import *). I lost a few hours searching for the bug… The solution: assign your custom function to both namespaced versions.


A lot of great information in here. I have my doc_events working.

My question is : Is there an event that is triggered when a NewDoc Entry (like Payment Entry when you add a new entry) is created?

I see that there is a “onload” event and many others, but is there a “oncreated” or “onadded”? To override the validate function as soon as a new doc of that type is made?

I am trying to override the validate function for Payment Entry, I want to override its validate function as soon as a new payment is added, the problem I am having is that most of the events that I am aware of are triggered AFTER the original validate function is ran which does not help overriding.

Thank you for any feedback!

I’ve seen an incomplete event list in this documentation:

Executing Code On Doctype Events (

To make things more complicated, I’m trying to override non-class method get_timesheet_details() in report/

My story:
I’m using Custom App, of which in I overrode erpnext.payroll.doctype.salary_slip.salary_slip.set_time_sheet() method. This is working fine as it is inside the SalarySlip class.

Now, myapp.salary_slip.set_time_sheet() calls Unfortunately I need to override this method too (see this issue)

In myapp/, I import:
from import get_timesheet_details

and therefore it will call the non-overridden get_timesheet_details().

In myapp/
import myapp_app.my_app.billing_summary =

The workaround I’m using is, in myapp/, instead of this:
from import get_timesheet_details

I use:
from my_app.my_app.billing_summary import get_timesheet_details

PS: I’m using monkey patch for the above fixes.

“monkey patch” ?
You can find some inspiration here :


Sure not sustainable way to do it but work

EDIT : Sorry didn’t check previous post that give CSF_TZ repo with is also a good inpiration source for monkey patch

@FHenry hello! I’m using monkey patch already. Sorry I didn’t mention that.

I’ve just edited my reply above.

1 Like

this monkey patch is not same as in CSF_TZ repo and ERPNext开箱即用: ERPNext Out Of Box 开箱即用 -

how it works


# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import importlib

import frappe

patches_loaded = False

__version__ = '13.0.0'

def console(*data):
    frappe.publish_realtime("out_to_console", data, user=frappe.session.user)

def load_monkey_patches():
    global patches_loaded

    if (
        or not getattr(frappe, "conf", None)
        or not "erpnext_oob" in frappe.get_installed_apps()

    for app in frappe.get_installed_apps():
        if app in ['frappe', 'erpnext']: continue

        folder = frappe.get_app_path(app, "monkey_patches")
        if not os.path.exists(folder): continue

        for module_name in os.listdir(folder):
            if not module_name.endswith(".py") or module_name == "":


    patches_loaded = True

connect = frappe.connect

def custom_connect(*args, **kwargs):
    out = connect(*args, **kwargs)

    if frappe.conf.auto_commit_on_many_writes:
        frappe.db.auto_commit_on_many_writes = 1

    return out

frappe.connect = custom_connect

create monkey_patches folder under your app folder.

create any py file, monkey patch like below

import frappe
from frappe.utils import cstr
from frappe.model.base_document import BaseDocument
import json

def get_owner_username(self):
    return frappe.db.get_value('User', self.owner, 'full_name')

def get_submit_username(self):
        ['docstatus', 0, 1]
        if not self.meta.is_submittable:
        filters={'ref_doctype': self.doctype, 'docname':, 'data': ('like', '%docstatus%')}
        version_list = frappe.get_all('Version', filters = filters, fields=['owner','data'], order_by="creation desc")
        for version in version_list:
            data = json.loads(
            found = [f for f in data.get('changed') if f[0] =='docstatus' and f[-1] ==1]
            if found:
                return frappe.db.get_value('User', version.owner, 'full_name')

def _validate_selects(self):
    if frappe.flags.in_import:

    for df in self.meta.get_select_fields():
        if df.fieldname=="naming_series" or not (self.get(df.fieldname) and df.options):

        options = (df.options or "").split("\n")
        options = [o.split(";")[0] for o in options if o]
        # if only empty options
        if not filter(None, options):

        # strip and set
        self.set(df.fieldname, cstr(self.get(df.fieldname)).strip())
        value = self.get(df.fieldname)

        if value not in options and not (frappe.flags.in_test and value.startswith("_T-")):
            # show an elaborate message
            prefix = _("Row #{0}:").format(self.idx) if self.get("parentfield") else ""
            label = _(self.meta.get_label(df.fieldname))
            comma_options = '", "'.join(_(each) for each in options)

            frappe.throw(_('{0} {1} cannot be "{2}". It should be one of "{3}"').format(prefix, label,
                value, comma_options))

BaseDocument.get_owner_username = get_owner_username
BaseDocument.get_submit_username = get_submit_username
BaseDocument._validate_selects = _validate_selects

you can check the source code from the repository.