mcpbeat Sign in

Frappe Syntax Hooks Events Agent Skill

> Use when implementing document lifecycle hooks via doc_events in hooks.py, understanding event execution order, or extending/overriding document behavior from another app. Prevents silent hook failures from wrong event names, incorrect execution order assumptions, and broken override chains. Covers doc_events hook syntax, all document events (before_insert, validate, on_submit, etc.), event execution order, extend vs override behavior, cross-app doc_events.

9k tokens
context cost
the whole folder, loaded on every use
5
files
instructions only
0
copies elsewhere
how many repositories repackaged it
158
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-syntax-hooks-events

The instruction itself

35 sections, as written by the author

Document Lifecycle Hooks (doc_events)

Quick Reference: Event Execution Order

Insert (new document)

| Order | Event | Purpose | Can Raise? |

|-------|---------------------|--------------------------------------|------------|

| 1 | before_insert | Set defaults before naming | YES |

| 2 | before_naming | Modify naming logic | YES |

| 3 | autoname | Set the name property | YES |

| 4 | before_validate | Auto-set missing values | YES |

| 5 | validate | Validation logic — throw to abort | YES |

| 6 | before_save | Final mutations before DB write | YES |

| 7 | db_insert | *Internal* — writes row to DB | — |

| 8 | after_insert | Post-insert logic (runs once ever) | YES |

| 9 | on_update | Post-save logic (runs on every save) | YES |

| 10 | on_change | Fires if any field value changed | YES |

Save (existing document)

| Order | Event | Purpose |

|-------|-------------------|-----------------------------------|

| 1 | before_validate | Auto-set missing values |

| 2 | validate | Validation logic — throw to abort |

| 3 | before_save | Final mutations before DB write |

| 4 | db_update | *Internal* — updates row in DB |

| 5 | on_update | Post-save logic |

| 6 | on_change | Fires if any field value changed |

Submit

| Order | Event | Purpose |

|-------|-------------------|------------------------------------|

| 1 | before_validate | Auto-set missing values |

| 2 | validate | Validation logic |

| 3 | before_save | Final mutations before DB write |

| 4 | before_submit | Pre-submit logic — throw to abort |

| 5 | db_update | *Internal* — updates row in DB |

| 6 | on_submit | Post-submit logic (GL entries etc) |

| 7 | on_update | Post-save logic |

| 8 | on_change | Fires if any field value changed |

Cancel

| Order | Event | Purpose |

|-------|-------------------|-------------------------------------|

| 1 | before_cancel | Pre-cancel validation |

| 2 | db_update | *Internal* — updates row in DB |

| 3 | on_cancel | Post-cancel logic (reverse GL etc) |

| 4 | on_change | Fires if any field value changed |

Delete

| Order | Event | Purpose |

|-------|----------------|--------------------------------|

| 1 | on_trash | Pre-delete cleanup |

| 2 | after_delete | Post-delete logic |

Other Operations

| Operation | Events (in order) |

|------------------------|----------------------------------------------------------|

| Rename | before_renameafter_rename |

| Amend | before_insert chain runs on the new amended doc |

| Update After Submit | before_update_after_submitdb_updateon_update_after_submiton_change |


doc_events in hooks.py: Syntax

Basic Structure

# hooks.py
doc_events = {
    "Sales Invoice": {
        "on_submit": "myapp.events.sales_invoice.on_submit",
        "on_cancel": "myapp.events.sales_invoice.on_cancel",
    },
    "Purchase Order": {
        "validate": "myapp.events.purchase_order.validate",
    }
}

Wildcard: Apply to ALL DocTypes

doc_events = {
    "*": {
        "after_insert": "myapp.events.global_handler.after_insert_all",
        "on_update": "myapp.events.global_handler.track_changes",
    }
}

ALWAYS use "*" (string with asterisk) as the key. This fires the handler for every DocType.

Multiple Handlers per Event

doc_events = {
    "Sales Invoice": {
        "on_submit": [
            "myapp.events.accounting.create_gl_entries",
            "myapp.events.notifications.send_invoice_email",
        ]
    }
}

Handler Function Signature

# myapp/events/sales_invoice.py
def on_submit(doc, method=None):
    """
    doc    — the Document instance (e.g., Sales Invoice)
    method — string name of the event (e.g., "on_submit"), or None
    """
    if doc.grand_total > 10000:
        frappe.sendmail(...)

ALWAYS accept method as the second parameter (with default None). Frappe passes it automatically.


Decision Tree: Which Event to Use

"I need to validate data before saving"

→ Use validate. ALWAYS raise frappe.throw() here to block invalid saves.

"I need to set default values automatically"

→ Use before_validate. This runs before validate, so your defaults are set before validation checks.

"I need to run logic only on first creation"

→ Use after_insert. This fires ONLY on insert, NEVER on subsequent saves.

"I need to run logic on every save (insert + update)"

→ Use on_update. This fires on both insert and save operations.

"I need to create linked documents after submit"

→ Use on_submit. NEVER create linked docs in validate — the document is not yet committed.

"I need to reverse linked documents on cancel"

→ Use on_cancel. ALWAYS clean up GL entries, stock ledger entries, and linked docs here.

"I need to modify the document name"

→ Use autoname in the controller, or before_naming for conditional logic.

"I need to prevent deletion under certain conditions"

→ Use on_trash. Raise frappe.throw() to block deletion.

"I need to update a submitted document's fields"

→ Use before_update_after_submit for validation and on_update_after_submit for side effects.

"I need logic that runs only when values actually changed"

→ Use on_change. This fires only when at least one field value differs from the DB state.


doc_events vs Controller Events

Both mechanisms trigger the SAME events. The difference is WHERE you register them.

| Aspect | Controller (class method) | doc_events (hooks.py) |

|---------------------|--------------------------------------|------------------------------------------|

| Location | {doctype}.py controller file | hooks.py in your app |

| Use when | You OWN the DocType | You are EXTENDING another app's DocType |

| Execution | Runs first (controller) | Runs after controller method |

| Multiple apps | Only one controller per DocType | Multiple apps can register handlers |

ALWAYS use doc_events when hooking into a DocType you do NOT own. NEVER modify another app's controller file directly.

Execution Order Within a Single Event

For a given event (e.g., validate):

  • Controller method runs first (def validate(self))
  • doc_events handlers run in app installation order
  • Wildcard "*" handlers run after specific DocType handlers

extend_doctype_class [v16+]

In Frappe v16+, extend_doctype_class provides a cleaner alternative to doc_events for adding methods to existing DocTypes.

hooks.py

extend_doctype_class = {
    "Sales Invoice": [
        "myapp.overrides.sales_invoice.SalesInvoiceExtension"
    ]
}

Extension Class (Mixin)

# myapp/overrides/sales_invoice.py
import frappe

class SalesInvoiceExtension:
    def validate(self):
        """This is called as part of the controller chain."""
        if self.grand_total < 0:
            frappe.throw("Grand total cannot be negative")

    def custom_method(self):
        """Custom methods are also available on the doc instance."""
        return self.items

Key Rules

  • ALWAYS use extend_doctype_class over override_doctype_class in v16+ when multiple apps may extend the same DocType.
  • Multiple apps can extend the same DocType — extensions stack via MRO.
  • Class resolution order follows hooks priority: class Final(App2Mixin, App1Mixin, Original).
  • Extension methods (like validate) run as part of the controller, NOT as separate doc_events handlers.

override_doctype_class [v14+]

Completely replaces the controller class. Use with extreme caution.

# hooks.py
override_doctype_class = {
    "ToDo": "myapp.overrides.todo.CustomToDo"
}
# myapp/overrides/todo.py
from frappe.desk.doctype.todo.todo import ToDo

class CustomToDo(ToDo):
    def validate(self):
        super().validate()  # ALWAYS call super() to preserve original logic
        # Your additions here

NEVER use override_doctype_class if extend_doctype_class is available (v16+). Only ONE app can override a DocType — last-installed app wins, silently breaking other apps.


Multi-App Event Ordering

When multiple apps register doc_events for the same DocType and event:

  • Handlers execute in app installation order (as listed in sites/{site}/site_config.jsoninstalled_apps).
  • The order can be changed via Setup > Installed Applications > Update Hooks Resolution Order.
  • For override_doctype_class, the last-installed app wins (only one override applies).
  • For extend_doctype_class (v16+), all extensions stack cumulatively.

Transaction Behavior

All document events from before_validate through on_change run inside a single database transaction.

  • If ANY event raises an exception, the ENTIRE operation rolls back (including db_insert/db_update).
  • after_insert, on_update, on_submit, on_cancel — all run BEFORE the transaction commits.
  • The transaction commits only AFTER all events complete successfully.
  • after_delete runs after the DELETE statement but still within the request transaction.

NEVER assume data is committed to DB inside any event handler. Other concurrent requests will NOT see your changes until the full request completes.


Critical Rules

  • ALWAYS use frappe.throw() to abort operations — NEVER use raise Exception.
  • NEVER modify doc.name outside of autoname or before_naming.
  • ALWAYS call super().{event}() when overriding controller methods in subclasses.
  • NEVER use doc.save() inside validate or before_save — this causes infinite recursion.
  • ALWAYS use doc.flags.ignore_permissions = True explicitly if your hook needs to bypass permissions — NEVER assume hooks run as Administrator.
  • NEVER put slow operations (API calls, file I/O) in validate — use after_insert or on_update with frappe.enqueue() instead.
  • ALWAYS use doc.flags to communicate between events in the same request (e.g., doc.flags.skip_notification = True).
  • NEVER rely on on_change for critical logic — it only fires when values actually differ from the database state.

See Also

  • Event Execution Order — Detailed Diagrams
  • Working Examples for Common Patterns
  • Anti-Patterns and Common Mistakes
  • frappe-syntax-hooks-config — App-level hooks (scheduler, fixtures, permissions)
  • Official docs: https://docs.frappe.io/framework/user/en/basics/doctypes/controllers

Other skills for the same job

different authors, same section of the catalogue
DOCX
by anthropics
vendor ×16

Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks

7k tokens
PDF
by anthropics
vendor ×16

Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. When Claude needs to fill in a PDF form or programmatically process, generate, or analyze PDF documents at scale.

13k tokens scripts
PPTX
by JayZeeDesign
×15

Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks

308k tokens scripts
Canvas Design
by anthropics
vendor ×13

Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.

1388k tokens
PDF
by anthropics
vendor ×10

Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.

15k tokens scripts
DOCX
by w95
×6

Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). Triggers include: any mention of 'Word doc', 'word document', '.docx', or requests to produce professional documents with formatting like tables of contents, headings, page numbers, or letterheads. Also use when extracting or reorganizing content from .docx files, inserting or replacing images in documents, performing find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a 'report', 'memo', 'letter', 'template', or similar deliverable as a Word or .docx file, use this skill. Do NOT use for PDFs, spreadsheets, Google Docs, or general coding tasks unrelated to document generation.

5k tokens
PPTX
by w95
×4

Use this skill any time a .pptx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates, layouts, speaker notes, or comments. Trigger whenever the user mentions \"deck,\" \"slides,\" \"presentation,\" or references a .pptx filename, regardless of what they plan to do with the content afterward. If a .pptx file needs to be opened, created, or touched, use this skill.

2k tokens
Obsidian Markdown
by ZhanlinCui
×3

Create and edit Obsidian Flavored Markdown with wikilinks, embeds, callouts, properties, and other Obsidian-specific syntax. Use when working with .md files in Obsidian, or when the user mentions wikilinks, callouts, frontmatter, tags, embeds, or Obsidian notes.

3k tokens

How to use it

Copy the folder

Take impertio-studio/frappe-syntax-hooks-events from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.