frappe-agent-architect
Use when designing multi-app Frappe architectures, deciding whether to split functionality into separate apps, or implementing cross-app communication…
Use when implementing the Frappe/ERPNext permission system. Covers roles, user permissions, perm levels, data masking, and permission hooks for v14/v15/v16. Prevents common access control mistakes and security issues. Keywords: permissions, roles, user permissions, perm levels,
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-core-permissions --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/frappe-core-permissionsContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when implementing the Frappe/ERPNext permission system. Covers roles, user permissions, perm levels, data masking, and permission hooks for v14/v15/v16. Prevents common access control mistakes and security issues. Keywords: permissions, roles, user permissions, perm levels,
name: frappe-core-permissions description: > Use when implementing the Frappe/ERPNext permission system. Covers roles, user permissions, perm levels, data masking, and permission hooks for v14/v15/v16. Prevents common access control mistakes and security issues. Keywords: permissions, roles, user permissions, perm levels, data masking,, restrict records, who can see what, department access, row-level, user cannot see document, access denied. access control, security, has_permission. license: MIT compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16." metadata: author: OpenAEC-Foundation version: "2.0"
> Deterministic patterns for the five-layer Frappe permission system.
---
| Layer | Controls | Configured Via | Version | |-------|----------|----------------|---------| | **Role Permissions** | What users CAN do | DocType permissions table | All | | **User Permissions** | WHICH records users see | User Permission DocType | All | | **Perm Levels** | WHICH fields users see/edit | Field `permlevel` property | All | | **Permission Hooks** | Custom deny logic | `hooks.py` | All | | **Data Masking** | Masked field values | Field `mask` property | [v16+] |
---
Need to control access? ├── Who can Create/Read/Write/Delete a DocType? → Role Permissions ├── Which specific records can a user see? → User Permissions ├── Which fields should be hidden? → Perm Levels (permlevel 1+) ├── Which fields show masked values? → Data Masking [v16+] ├── Custom runtime deny logic? → has_permission hook ├── Filter list queries dynamically? → permission_query_conditions hook └── Share one document with one user? → frappe.share Checking permissions in code? ├── Before action → frappe.has_permission() or doc.has_permission() ├── Raise on denial → doc.check_permission() or throw=True ├── System bypass → doc.flags.ignore_permissions = True (ALWAYS document why) └── List query → ALWAYS use frappe.get_list() for user-facing data
---
| Type | API Check | Applies To | |------|-----------|------------| | `read` | `frappe.has_permission(dt, "read")` | All DocTypes | | `write` | `frappe.has_permission(dt, "write")` | All DocTypes | | `create` | `frappe.has_permission(dt, "create")` | All DocTypes | | `delete` | `frappe.has_permission(dt, "delete")` | All DocTypes | | `submit` | `frappe.has_permission(dt, "submit")` | Submittable only | | `cancel` | `frappe.has_permission(dt, "cancel")` | Submittable only | | `amend` | `frappe.has_permission(dt, "amend")` | Submittable only | | `select` | `frappe.has_permission(dt, "select")` | Link fields [v14+] | | `report` | N/A | Report Builder access | | `export` | N/A | Excel/CSV export | | `import` | N/A | Data Import Tool | | `share` | N/A | Share with other users | | `print` | N/A | Print/PDF generation | | `email` | N/A | Send email | | `mask` | Role permission for unmasked view | Data Masking [v16+] |
---
| Role | Assigned To | Notes | |------|-------------|-------| | `Guest` | Everyone (including anonymous) | Public pages | | `All` | All registered users | Basic authenticated access | | `Administrator` | Only the Administrator user | ALWAYS has all permissions | | `Desk User` | System Users only | [v15+] |
---
# DocType-level
frappe.has_permission("Sales Order", "write")
# Document-level (by name or object)
frappe.has_permission("Sales Order", "write", "SO-00001")
frappe.has_permission("Sales Order", "write", doc=doc)
# For specific user
frappe.has_permission("Sales Order", "read", user="john@example.com")
# Throw on denial
frappe.has_permission("Sales Order", "delete", throw=True)
# Debug mode — prints evaluation steps
frappe.has_permission("Sales Order", "read", debug=True)
print(frappe.local.permission_debug_log)doc = frappe.get_doc("Sales Order", "SO-00001")
# Returns bool
if doc.has_permission("write"):
doc.status = "Approved"
doc.save()
# Raises frappe.PermissionError if denied
doc.check_permission("write")from frappe.permissions import get_doc_permissions
perms = get_doc_permissions(doc)
# {'read': 1, 'write': 1, 'create': 0, 'delete': 0, ...}
perms = get_doc_permissions(doc, user="john@example.com")---
Restrict users to specific Link field values (e.g., specific Company, Territory).
from frappe.permissions import add_user_permission, remove_user_permission
# Restrict user to one company
add_user_permission(
doctype="Company",
name="My Company",
user="john@example.com",
is_default=1, # auto-fill in new documents
applicable_for="Sales Order" # only for this DocType (optional)
)
# Remove restriction
remove_user_permission("Company", "My Company", "john@example.com")
# Query current restrictions
from frappe.permissions import get_user_permissions
perms = get_user_permissions("john@example.com")
# {"Company": [{"doc": "My Company", "is_default": 1}], ...}---
Grant access to a single document for a specific user.
from frappe.share import add as add_share, remove as remove_share
add_share("Sales Order", "SO-00001", "jane@example.com",
read=1, write=1, share=0, notify=1)
remove_share("Sales Order", "SO-00001", "jane@example.com")
# Share with everyone
add_share("Sales Order", "SO-00001", everyone=1, read=1)---
Group fields by `permlevel` (0-9). Level 0 MUST be granted before higher levels.
{
"fields": [
{"fieldname": "employee_name", "permlevel": 0},
{"fieldname": "salary", "permlevel": 1}
],
"permissions": [
{"role": "Employee", "permlevel": 0, "read": 1},
{"role": "HR Manager", "permlevel": 0, "read": 1, "write": 1},
{"role"60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations
Repo: Impertio-Studio/Frappe_Claude_Skill_Package
Use when designing multi-app Frappe architectures, deciding whether to split functionality into separate apps, or implementing cross-app communication…
Use when debugging Frappe errors, using bench console for live inspection, analyzing tracebacks, or reading Frappe log files. Prevents wasted debugging time…
Use when receiving vague or unclear ERPNext/Frappe development requests that need interpretation. Transforms requirements like 'make invoice auto-calculate' or…
Use when migrating a Frappe app between major versions, detecting breaking API changes, or resolving post-migration errors. Prevents failed migrations from…
Use when reviewing or validating Frappe/ERPNext code against best practices and common pitfalls. Checks generated code before deployment, validates against all…
Use when building ERPNext/Frappe API integrations (v14/v15/v16) including REST API, RPC API, authentication, webhooks, and rate limiting. Covers external API…