Skill125 estrellas del repoactualizado 2mo ago
frappe-syntax-hooks
>
Instalar en Claude Code
Copiargit clone --depth 1 https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package /tmp/frappe-syntax-hooks && cp -r /tmp/frappe-syntax-hooks/skills/source/syntax/frappe-syntax-hooks ~/.claude/skills/frappe-syntax-hooksDespués abre una sesión nueva de Claude Code; el skill carga automáticamente.
Definición
SKILL.md
# Frappe Configuration Hooks (hooks.py)
Configuration hooks in hooks.py enable custom apps to extend Frappe/ERPNext
behavior. This skill covers ALL non-document-event hooks. For `doc_events`
(validate, on_submit, on_update, etc.), see **frappe-syntax-hooks-events**.
## Quick Reference: Hook Categories
| Category | Key Hooks | Reference |
|----------|-----------|-----------|
| App metadata | `app_name`, `app_title`, `required_apps` | Below |
| Frontend assets | `app_include_js/css`, `web_include_js/css` | Below |
| Install/migrate | `before_install`, `after_install`, `after_migrate` | Below |
| Scheduler | `hourly`, `daily`, `cron`, `*_long` | [scheduler-events.md](references/scheduler-events.md) |
| Session/auth | `on_login`, `on_logout`, `auth_hooks` | [bootinfo.md](references/bootinfo.md) |
| Request middleware | `before_request`, `after_request` | [request-lifecycle.md](references/request-lifecycle.md) |
| Permissions | `permission_query_conditions`, `has_permission` | [permissions.md](references/permissions.md) |
| DocType overrides | `override_doctype_class`, `doctype_js` | [overrides.md](references/overrides.md) |
| Website/portal | `website_route_rules`, `portal_menu_items` | [request-lifecycle.md](references/request-lifecycle.md) |
| File handling | `before_write_file`, `write_file` | Below |
| Email | `override_email_send`, `default_mail_footer` | Below |
| PDF | `pdf_header_html`, `pdf_footer_html` | Below |
| Jinja | `jinja.methods`, `jinja.filters` | Below |
| Boot/client data | `extend_bootinfo`, `notification_config` | [bootinfo.md](references/bootinfo.md) |
| Data/fixtures | `fixtures`, `global_search_doctypes` | Below |
| Method overrides | `override_whitelisted_methods`, `standard_queries` | [overrides.md](references/overrides.md) |
---
## Decision Tree: Which Hook Do I Need?
```
What do you want to achieve?
|
+-- ADD JS/CSS to desk or portal?
| +-- Desk --> app_include_js / app_include_css
| +-- Portal --> web_include_js / web_include_css
| +-- Specific form --> doctype_js
| +-- List view --> doctype_list_js
|
+-- RUN periodic background tasks?
| +-- < 5 min execution --> hourly / daily / weekly / monthly
| +-- 5-25 min execution --> hourly_long / daily_long / etc.
| +-- Exact time needed --> cron
| See: frappe-syntax-hooks > scheduler-events.md
|
+-- SEND data to client at page load?
| +-- extend_bootinfo
|
+-- MODIFY controller of existing DocType?
| +-- v16+ --> extend_doctype_class (RECOMMENDED)
| +-- v14/v15 --> override_doctype_class (last app wins)
|
+-- MODIFY API endpoint?
| +-- override_whitelisted_methods
|
+-- CUSTOMIZE permissions?
| +-- List filtering --> permission_query_conditions
| +-- Document-level --> has_permission
|
+-- REACT to document save/submit/delete?
| +-- See frappe-syntax-hooks-events skill
|
+-- EXPORT/IMPORT configuration?
| +-- fixtures
|
+-- SETUP on install or migrate?
| +-- after_install / after_migrate
|
+-- ADD custom Jinja functions?
| +-- jinja.methods / jinja.filters
|
+-- CUSTOMIZE website routing?
| +-- website_route_rules
| See: request-lifecycle.md for full routing pipeline
|
+-- INTERCEPT every request/response?
| +-- before_request / after_request
| See: request-lifecycle.md for lifecycle flow
|
+-- CUSTOM page rendering?
| +-- page_renderer hook
| See: request-lifecycle.md for renderer architecture
```
---
## 1. App Metadata Hooks
ALWAYS include these in every hooks.py:
```python
app_name = "myapp"
app_title = "My App"
app_publisher = "My Company"
app_description = "Custom ERPNext extensions"
app_email = "info@mycompany.com"
app_license = "MIT"
required_apps = ["erpnext"] # Declare dependencies
```
---
## 2. Frontend Asset Injection
```python
# Desk (backend UI) assets — loaded on EVERY desk page
app_include_js = "/assets/myapp/js/myapp.min.js" # string or list
app_include_css = "/assets/myapp/css/myapp.min.css"
# Website/portal assets — loaded on EVERY web page
web_include_js = "/assets/myapp/js/web.min.js"
web_include_css = "/assets/myapp/css/web.min.css"
# Web form specific assets
webform_include_js = {"My Web Form": "public/js/my_webform.js"}
webform_include_css = {"My Web Form": "public/css/my_webform.css"}
# Form script extensions (extend OTHER apps' forms)
doctype_js = {"Sales Invoice": "public/js/sales_invoice.js"}
# List view script extensions
doctype_list_js = {"Sales Invoice": "public/js/sales_invoice_list.js"}
# Custom sounds
sounds = [{"name": "alert", "src": "/assets/myapp/sounds/alert.mp3", "volume": 0.5}]
```
NEVER put heavy libraries in `app_include_js` — they load on every page.
---
## 3. Installation & Migration Lifecycle
```python
before_install = "myapp.setup.before_install"
after_install = "myapp.setup.after_install"
after_sync = "myapp.setup.after_sync" # After fixture sync
before_migrate = "myapp.setup.before_migrate"
after_migrate = "myapp.setup.after_migrate"
before_uninstall = "myapp.setup.before_uninstall"
after_uninstall = "myapp.setup.after_uninstall"
before_tests = "myapp.setup.seed_test_data"
```
All accept a single dotted-path string. The function receives no arguments.
---
## 4. Scheduler Events
See [scheduler-events.md](references/scheduler-events.md) for full reference.
```python
scheduler_events = {
"all": ["myapp.tasks.every_minute"], # ~60s interval
"hourly": ["myapp.tasks.hourly_check"], # default queue, 5 min timeout
"daily": ["myapp.tasks.daily_report"],
"weekly": ["myapp.tasks.weekly_cleanup"],
"monthly": ["myapp.tasks.monthly_summary"],
"daily_long": ["myapp.tasks.heavy_sync"], # long queue, 25 min timeout
"cron": {
"0 9 * * 1-5": ["myapp.tasks.weekday_morning"] # cron expression
}
}
```
ALWAYS run `bench --site sitename migrate` after changing scheduler_events.
NEVER define task functions with arguments — they receive none.
---
## 5. Session & Authentication Hooks
```python
on_login = "myapp.auth.on_login"