Overriding ERPNext Doctype Logic

Jurin Liyun
4 min readSep 2, 2023

For those who are new to ERPNext, ERPNext is a free and open-source integrated Enterprise resource planning (ERP) software developed by an Indian software company Frappe Technologies Pvt. Ltd.[2][3] It is built on the MariaDB database system using Frappe, a Python based server-side framework.[4] (source: wikipedia)

You can read more about the details of ERPNext in their website. In this article I will try to give you the glimpse of the possibility that the Frappe team has offered to us regarding customization of their ERP system.

To begin, I would like to give you my scenario why my team need to customize the core.

In one of the organisation that i work with, they required customized workflow that will validate a user that included in a role of multiple users, and in the role, not all users can approve with the same approval value even though they were assigned with the same role. Instead, in the same role admin can set multiple authorization rule for whom they wanted to allow to approve.

In ERPNext there are two built-in ways to do it, namely,

  1. Authorization Rule
    Authorization doctype only allows organization to set what role and who is the user that are authorize to approve sale.

2. Workflow
In workflow, we can only specify the flow of the document how it should behave when the user create until approval of the sales.

States
Rules
Rule Details. Conditional supported

The built-in feature is already good enough for many organizations. But it is always fascinate me when it comes to solving complex problem for certain enterprise organization. What contributes to these complexity, one that i always found was that the legacy of management policies.

My client asked my team to do another level of verification. The system should be able to cater for the person authorization rule in the role that he/she was included. In ERPNext, we have the roles manager, and in each role many people were included. Let me simplify for you.

  1. Given 3 users the role as account user
  2. The user was working in department A
  3. But not all users with the account user role can be authorized with same value. May be 2 users with authorization value of $10,000 and 1 user $4,000.

In the case above, we do some workaround using the role manager which create many different roles for different purpose of authorizations. And this make it even more complicated to manage. So in this case we do another workaround which still uses the built-in but add another layer of authorization namely, “Company Tree” which acted as multilevel authorization just to solve the organization problem.

Let see how we do this.

Company Tree Doctype
Workflow, Doctype, People involved

Now let see the main doc of the sales invoice. How do we override it and add our custom validation. First we create

umserp
|_overrides
|_doctypes
|_sales_invoice.py
from __future__ import unicode_literals
import frappe
import erpnext
from umserp.core.foundations.reporting import render_report
from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice
from frappe import _
from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate, now
from umserp.core.utils.accounting import money_to_words_malay
from umserp.umserp.doctype.module_mapper.module_mapper import validate_financial_period
from umserp.umserp.doctype.company_tree.company_tree import validate_approving_tree_authority
from frappe.model.naming import make_autoname
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
import json
from frappe.model.workflow import get_workflow,get_transitions
from frappe.query_builder import DocType
from frappe.workflow.doctype.workflow_action.workflow_action import get_doc_workflow_state
from frappe.model.workflow import get_workflow_name

# sales_invoice.py
class UMSSalesInvoice(SalesInvoice):
def _validate(self):
super(UMSSalesInvoice,self).validate()
self.convert_amount_into_words()

if self._action == "submit":
self._validate_financial_period() # this is another custom validation for financial period

self._validate_approving_tree_authority() # this is the validation

if self._validate_posting_date():
self.clear_payment_schedule()
self.update_posting_date()
self.set_payment_schedule()
super(UMSSalesInvoice,self).set_missing_values()

The method self._validate_approving_tree_authority() is taken from the company tree doctype. The logic is too long so, I cannot be able to include it in this example.

The last part that we make this codes work for the ERPNext, is to include the overriding class inside the hook.py.

override_doctype_class = {
"Sales Invoice": "umserp.overrides.doctype.sales_invoice.UMSSalesInvoice",
}

Now, the system will automatically recognized our codes as the main code to be read first.

The point that i am making here is that, The ERPNext is a system that empowered by the Frappe framework can be easily customized even for complex rules that one company can has. The requirements of course have to change on certain level, but it would be great if a framework can have such flexibility.

Happy reading.

Notes:
Thanks for my great and enthusiastic team for make everything possible while i can never do it without them. also thanks to the Frappe Team for making such a great framework.

--

--

Jurin Liyun

Developer, Trainer , Consultant, Father of two children