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

  1. 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.")
    

  1. 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!")
    

  1. 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.")
    

  1. validate
    Triggered during the validation phase, after before_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.")
    

  1. 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"
    

  1. 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!")
    

  1. 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.")
    

  1. 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.")
    

  1. 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.")
    

  1. 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.")
    

  1. 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.")
    

  1. 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.")
    

General Structure of a Document Event Hook

doc_events = {
    "DocTypeName": {
        "event_name": "path.to.method"
    }
}

Best Practices

  1. Modularize Your Code: Keep event handlers in separate files for clarity and maintainability.
  2. Optimize Performance: Avoid heavy computations or long-running tasks in hooks to prevent delays.
  3. Error Handling: Use try and except blocks to handle unexpected errors gracefully.
  4. Use Frappe APIs: Leverage Frappe’s built-in APIs for operations like querying, updating, and notifying.
Discard
Save
This page has been updated since your last edit. Your draft may contain outdated content. Load Latest Version
Was this article helpful?

On this page

Review Changes ← Back to Content
Message Status Space Raised By Last update on