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 for monkey patching any method

  1. copy and adapt this file into your own custom app
  2. create python file under monkey_patches folder like this file.

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?

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

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.

1 Like