Skill125 repo starsupdated 2mo ago
frappe-syntax-hooks-events
>
Install in Claude Code
Copygit clone --depth 1 https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package /tmp/frappe-syntax-hooks-events && cp -r /tmp/frappe-syntax-hooks-events/skills/source/syntax/frappe-syntax-hooks-events ~/.claude/skills/frappe-syntax-hooks-eventsThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# 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_rename` → `after_rename` |
| Amend | `before_insert` chain runs on the new amended doc |
| Update After Submit | `before_update_after_submit` → `db_update` → `on_update_after_submit` → `on_change` |
---
## doc_events in hooks.py: Syntax
### Basic Structure
```python
# 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
```python
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
```python
doc_events = {
"Sales Invoice": {
"on_submit": [
"myapp.events.accounting.create_gl_entries",
"myapp.events.notifications.send_invoice_email",
]
}
}
```
### Handler Function Signature
```python
# 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"
→