Document Event Hooks in Frappe
Document event hooks allow you to execute custom logic at specific points in the lifecycle of a DocType record. These hooks are tied to events such as creation, saving, submission, or deletion of a document.
Available Document Events
before_insert
Triggered before a new document is inserted into the database.
Use Case:- Validate fields or ensure mandatory fields are populated.
- Set default values.
{ "Sales Invoice": { "before_insert": "custom_app.events.before_insert_sales_invoice" } }
def before_insert_sales_invoice(doc, method): if not doc.customer: frappe.throw("Customer is required before inserting a Sales Invoice.")
after_insert
Triggered after a document is inserted into the database.
Use Case:- Create related documents or perform post-creation actions.
- Notify users about new records.
{ "Sales Invoice": { "after_insert": "custom_app.events.after_insert_sales_invoice" } }
def after_insert_sales_invoice(doc, method): frappe.msgprint(f"Sales Invoice {doc.name} has been created!")
before_validate
Triggered before a document is validated.
Use Case:- Apply custom validation logic.
- Modify fields before standard validation.
{ "Sales Invoice": { "before_validate": "custom_app.events.before_validate_sales_invoice" } }
def before_validate_sales_invoice(doc, method): if doc.discount_amount > doc.total: frappe.throw("Discount cannot be greater than the total amount.")
validate
Triggered during the validation phase, afterbefore_validate
.
Use Case:- Perform additional validation checks.
- Manipulate field values post-validation.
{ "Sales Invoice": { "validate": "custom_app.events.validate_sales_invoice" } }
def validate_sales_invoice(doc, method): if not doc.items: frappe.throw("Sales Invoice must have at least one item.")
before_save
Triggered before a document is saved in the database.
Use Case:- Apply last-minute changes before saving.
- Trigger calculations or custom field updates.
{ "Sales Invoice": { "before_save": "custom_app.events.before_save_sales_invoice" } }
def before_save_sales_invoice(doc, method): doc.additional_notes = "Checked by Admin"
on_update
Triggered after a document is updated in the database.
Use Case:- Perform actions when a document is modified.
- Notify users or trigger workflows.
{ "Sales Invoice": { "on_update": "custom_app.events.on_update_sales_invoice" } }
def on_update_sales_invoice(doc, method): frappe.msgprint(f"Sales Invoice {doc.name} has been updated!")
on_submit
Triggered when a document is submitted.
Use Case:- Trigger financial or stock entries.
- Send notifications or execute workflows.
{ "Sales Invoice": { "on_submit": "custom_app.events.on_submit_sales_invoice" } }
def on_submit_sales_invoice(doc, method): frappe.msgprint(f"Sales Invoice {doc.name} has been submitted.")
before_cancel
Triggered before a document is canceled.
Use Case:- Validate conditions before allowing cancellation.
- Prevent cancellation under specific circumstances.
{ "Sales Invoice": { "before_cancel": "custom_app.events.before_cancel_sales_invoice" } }
def before_cancel_sales_invoice(doc, method): if doc.status == "Paid": frappe.throw("Paid invoices cannot be canceled.")
on_cancel
Triggered after a document is canceled.
Use Case:- Perform cleanup tasks, like reversing linked transactions.
- Notify users about cancellations.
{ "Sales Invoice": { "on_cancel": "custom_app.events.on_cancel_sales_invoice" } }
def on_cancel_sales_invoice(doc, method): frappe.msgprint(f"Sales Invoice {doc.name} has been canceled.")
on_trash
Triggered when a document is deleted.
Use Case:- Handle cleanup tasks like deleting linked records.
{ "Sales Invoice": { "on_trash": "custom_app.events.on_trash_sales_invoice" } }
def on_trash_sales_invoice(doc, method): frappe.msgprint(f"Sales Invoice {doc.name} has been deleted.")
- Handle cleanup tasks like deleting linked records.
before_update_after_submit
Triggered before modifying a submitted document.
Use Case:- Validate changes made to submitted documents.
{ "Sales Invoice": { "before_update_after_submit": "custom_app.events.before_update_after_submit_sales_invoice" } }
def before_update_after_submit_sales_invoice(doc, method): if doc.discount_amount > 1000: frappe.throw("Cannot give discounts higher than 1000 after submission.")
- Validate changes made to submitted documents.
on_update_after_submit
Triggered after modifying a submitted document.
Use Case:- Perform tasks like recalculating reports after modifications.
{ "Sales Invoice": { "on_update_after_submit": "custom_app.events.on_update_after_submit_sales_invoice" } }
def on_update_after_submit_sales_invoice(doc, method): frappe.msgprint(f"Sales Invoice {doc.name} was updated post-submission.")
- Perform tasks like recalculating reports after modifications.
General Structure of a Document Event Hook
doc_events = {
"DocTypeName": {
"event_name": "path.to.method"
}
}
Best Practices
- Modularize Your Code: Keep event handlers in separate files for clarity and maintainability.
- Optimize Performance: Avoid heavy computations or long-running tasks in hooks to prevent delays.
- Error Handling: Use
try
andexcept
blocks to handle unexpected errors gracefully. - Use Frappe APIs: Leverage Frappe’s built-in APIs for operations like querying, updating, and notifying.