I want to create a script that runs before saving to validate whether any item in a Sales Order, Quotation, or Invoice has a quantity smaller than the custom MOQ (Minimum Order Quantity) field I added.
Since server scripts in ERPNext can only reference a single doctype, I’m looking for a way to make the script universal—one that doesn’t explicitly reference a specific doctype but dynamically fetches the current doctype being processed before saving.
If you have a custom app, create a shared Python method that performs the MOQ validation, then call this method from hooks or customizations for the respective doctypes.
In your custom app, create a method in a Python file (e.g., custom_validation.py):
def validate_moq(doc):
for item in doc.items:
if item.qty < item.custom_moq:
frappe.throw(f"Quantity for Item {item.item_code} cannot be less than the Minimum Order Quantity ({item.custom_moq}).")
Thank you for you answer. Your approach, while effective, still relies on explicitly defining the DocTypes in the doc_events dictionary. I am looking for something more universal that is linked to the validate method no matter where it’s called from.
Thank you for your response! I see you’re looking for a universal way to apply the validation without explicitly listing each doctype in the doc_events dictionary.
Unfortunately, due to the way the event handling system works, each doc_event dictionary must reference specific doctypes (e.g., Sales Order, Quotation, Sales Invoice). This is a limitation of how the hooks and server scripts are set up. Since doc_events are registered for each doctype individually, dynamically applying a validation without specifying each doctype is not supported by default.
However, you can simplify your approach by putting the validation logic in a single function (as we discussed earlier), and then call it for any doctype you need. This reduces code duplication, but you will still need to reference each doctype in the doc_events dictionary.
Thank you for your response. I understand that there is indeed some limitations about that. I will try and implement your solution. It’s a little bit more tricky because certain items are used and reused in different modules, so you need to get the exact list of all modules to ensure the script will apply correctly everywhere it’s required.