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
Was this article helpful?

On this page