frappe-enterprise-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Frappe Enterprise Patterns

Frappe 企业级架构模式

Architectural patterns for building production-grade enterprise applications.
用于构建生产级企业应用的架构模式。

When to use

适用场景

  • Building CRM, Helpdesk, HRMS, or similar multi-entity systems
  • Designing SLA-driven workflows
  • Implementing assignment and queue management
  • Building audit trails and activity logs
  • Integrating with external systems (email, telephony, CRM)
  • 构建CRM、Helpdesk、HRMS或类似的多实体系统
  • 设计基于SLA的工作流
  • 实现分配与队列管理
  • 构建审计追踪与活动日志
  • 与外部系统集成(邮件、电话、CRM)

Inputs required

所需输入

  • System type (CRM/Helpdesk/custom)
  • Core entities and relationships
  • SLA requirements
  • Workflow states and transitions
  • Integration points
  • 系统类型(CRM/Helpdesk/自定义)
  • 核心实体及关系
  • SLA要求
  • 工作流状态与转换
  • 集成点

Procedure

实施步骤

0) Design data model

0) 设计数据模型

Start with clear, normalized DocTypes:
Ticket (parent)
├── customer (Link: Customer)
├── assigned_to (Link: User)
├── status (Select: Open, In Progress, Resolved, Closed)
├── priority (Link: Priority)
├── sla (Link: SLA)
├── activities (Table: Ticket Activity)
└── response_by, resolution_by (Datetime)
Key patterns:
  • Use Link fields for relationships
  • Use child tables for activities, timelines, line items
  • Use Dynamic Link when target DocType varies
从清晰、规范化的DocType开始:
Ticket (parent)
├── customer (Link: Customer)
├── assigned_to (Link: User)
├── status (Select: Open, In Progress, Resolved, Closed)
├── priority (Link: Priority)
├── sla (Link: SLA)
├── activities (Table: Ticket Activity)
└── response_by, resolution_by (Datetime)
关键模式:
  • 使用Link字段建立关系
  • 使用子表存储活动、时间线、明细项
  • 当目标DocType变化时使用Dynamic Link

1) Implement state machine

1) 实现状态机

Option A: Workflow DocType
  • Create Workflow with states and role-based transitions
  • Link to your DocType
Option B: docstatus for submission flow
docstatusMeaning
0Draft
1Submitted
2Cancelled
Option C: Custom status field with validation
python
def validate(self):
    allowed = self.get_allowed_transitions()
    if self.status not in allowed:
        frappe.throw(f"Cannot transition to {self.status}")
选项A:Workflow DocType
  • 创建包含状态和基于角色的转换规则的Workflow
  • 关联到你的DocType
选项B:用docstatus处理提交流程
docstatus含义
0草稿
1已提交
2已取消
选项C:带验证的自定义状态字段
python
def validate(self):
    allowed = self.get_allowed_transitions()
    if self.status not in allowed:
        frappe.throw(f"Cannot transition to {self.status}")

2) Set up permissions

2) 设置权限

Row-level filtering:
  • Use User Permissions to restrict by entity
  • Combine with Role Permissions
Always re-check in RPC methods:
python
@frappe.whitelist()
def update_ticket(name, status):
    doc = frappe.get_doc("Ticket", name)
    if not frappe.has_permission("Ticket", "write", doc):
        frappe.throw("Not permitted", frappe.PermissionError)
    doc.status = status
    doc.save()
行级过滤:
  • 使用User Permissions按实体限制访问
  • 结合Role Permissions使用
始终在RPC方法中重新校验:
python
@frappe.whitelist()
def update_ticket(name, status):
    doc = frappe.get_doc("Ticket", name)
    if not frappe.has_permission("Ticket", "write", doc):
        frappe.throw("Not permitted", frappe.PermissionError)
    doc.status = status
    doc.save()

3) Build activity trail

3) 构建活动追踪

Track changes using Activity Log or custom child table:
python
def on_update(self):
    if self.has_value_changed("status"):
        self.append("activities", {
            "action": "Status Change",
            "old_value": self._doc_before_save.status,
            "new_value": self.status,
            "timestamp": frappe.utils.now()
        })
使用Activity Log或自定义子表追踪变更:
python
def on_update(self):
    if self.has_value_changed("status"):
        self.append("activities", {
            "action": "Status Change",
            "old_value": self._doc_before_save.status,
            "new_value": self.status,
            "timestamp": frappe.utils.now()
        })

4) Implement SLA

4) 实现SLA

SLA DocType:
SLA
├── entity_type (Link: DocType)
├── response_time (Duration)
├── resolution_time (Duration)
└── escalation_rules (Table: Escalation Rule)
Apply SLA on creation:
python
def after_insert(self):
    sla = get_applicable_sla(self)
    if sla:
        self.response_by = add_to_date(self.creation, hours=sla.response_time)
        self.resolution_by = add_to_date(self.creation, hours=sla.resolution_time)
        self.db_update()
Monitor breaches (scheduled job):
python
def check_sla_breaches():
    tickets = frappe.get_all("Ticket", 
        filters={"status": ["not in", ["Resolved", "Closed"]]},
        fields=["name", "resolution_by"]
    )
    for t in tickets:
        if frappe.utils.now_datetime() > t.resolution_by:
            mark_sla_breached(t.name)
SLA DocType:
SLA
├── entity_type (Link: DocType)
├── response_time (Duration)
├── resolution_time (Duration)
└── escalation_rules (Table: Escalation Rule)
在创建时应用SLA:
python
def after_insert(self):
    sla = get_applicable_sla(self)
    if sla:
        self.response_by = add_to_date(self.creation, hours=sla.response_time)
        self.resolution_by = add_to_date(self.creation, hours=sla.resolution_time)
        self.db_update()
监控SLA违约(定时任务):
python
def check_sla_breaches():
    tickets = frappe.get_all("Ticket", 
        filters={"status": ["not in", ["Resolved", "Closed"]]},
        fields=["name", "resolution_by"]
    )
    for t in tickets:
        if frappe.utils.now_datetime() > t.resolution_by:
            mark_sla_breached(t.name)

5) Assignment and queues

5) 分配与队列

Round-robin assignment:
python
def assign_next_agent(queue):
    agents = frappe.get_all("Queue Member",
        filters={"queue": queue, "available": 1},
        fields=["user", "current_load"],
        order_by="current_load asc"
    )
    if agents:
        return agents[0].user
    return None
Assignment Rules DocType for automatic assignment.
轮询分配:
python
def assign_next_agent(queue):
    agents = frappe.get_all("Queue Member",
        filters={"queue": queue, "available": 1},
        fields=["user", "current_load"],
        order_by="current_load asc"
    )
    if agents:
        return agents[0].user
    return None
使用Assignment Rules DocType实现自动分配。

6) Notifications and escalations

6) 通知与升级

Configure Notification DocType for:
  • SLA approaching breach
  • Assignment changes
  • Status transitions
  • Customer replies
Escalation chain:
Level 1 (0h): Notify assigned agent
Level 2 (4h): Notify team lead
Level 3 (8h): Notify manager
Level 4 (24h): Notify department head
配置Notification DocType用于:
  • SLA即将违约时通知
  • 分配变更通知
  • 状态转换通知
  • 客户回复通知
升级链:
Level 1 (0h): 通知分配的坐席
Level 2 (4h): 通知团队负责人
Level 3 (8h): 通知经理
Level 4 (24h): 通知部门主管

7) External integrations

7) 外部集成

Centralize in
integrations/
module:
python
undefined
集中在
integrations/
模块中:
python
undefined

my_app/integrations/email_connector.py

my_app/integrations/email_connector.py

def sync_emails(): # Fetch from Email Account # Create Communications # Link to Tickets

**Use background jobs for sync:**
```python
frappe.enqueue(
    "my_app.integrations.email_connector.sync_emails",
    queue="long",
    timeout=600
)
def sync_emails(): # Fetch from Email Account # Create Communications # Link to Tickets

**使用后台任务进行同步:**
```python
frappe.enqueue(
    "my_app.integrations.email_connector.sync_emails",
    queue="long",
    timeout=600
)

Verification

验证项

  • Workflow transitions work for all roles
  • Permissions enforced at API level
  • Activity log captures all changes
  • SLA calculation correct
  • Notifications fire appropriately
  • Integration sync runs without errors
  • 工作流转换对所有角色生效
  • API层面已强制执行权限
  • 活动日志捕获所有变更
  • SLA计算正确
  • 通知正常触发
  • 集成同步运行无错误

Failure modes / debugging

故障模式与调试

  • Permission bypass: Check RPC methods have explicit permission checks
  • SLA not applying: Verify scheduled job is running
  • Activities not logging: Check
    has_value_changed
    usage
  • Notifications not sending: Check Notification rules and email queue
  • 权限绕过:检查RPC方法是否有显式的权限校验
  • SLA未生效:验证定时任务是否在运行
  • 活动未记录:检查
    has_value_changed
    的使用是否正确
  • 通知未发送:检查Notification规则和邮件队列

Escalation

进阶指引

  • For complex permission patterns, see references/advanced-permissions.md
  • For queue optimization, see references/queue-patterns.md
  • For UI/UX patterns →
    frappe-ui-patterns
  • 复杂权限模式请参考references/advanced-permissions.md
  • 队列优化请参考references/queue-patterns.md
  • UI/UX模式请参考
    frappe-ui-patterns

References

参考资源

  • references/workflow-patterns.md - State machine design
  • references/sla-implementation.md - SLA details
  • references/integration-patterns.md - External systems
  • references/workflow-patterns.md - 状态机设计
  • references/sla-implementation.md - SLA细节
  • references/integration-patterns.md - 外部系统集成

Guardrails

注意事项

  • Follow CRM/Helpdesk UI patterns: For CRUD apps, follow
    frappe-ui-patterns
    skill which documents app shell, navigation, list views, and form patterns from official Frappe apps. This includes sidebar layouts, quick filters, Kanban views, and detail panels.
  • Use Frappe UI for frontends: All custom enterprise frontends must use Frappe UI (Vue 3 + TailwindCSS) — never vanilla JS or jQuery
  • Design workflows carefully: Map all states and transitions before implementation; consider rollback paths
  • Handle edge cases: Plan for cancelled, on-hold, and exception states in workflows
  • Test performance early: Run load tests for high-volume DocTypes and complex queries
  • Use background jobs for heavy operations: Never block web requests with long-running tasks
  • Log critical operations: Use
    frappe.log_error()
    and activity logs for auditability
  • 遵循CRM/Helpdesk UI模式:对于CRUD应用,请遵循
    frappe-ui-patterns
    技能中记录的官方Frappe应用的应用外壳、导航、列表视图和表单模式,包括侧边栏布局、快速筛选、看板视图和详情面板。
  • 前端使用Frappe UI:所有自定义企业级前端必须使用Frappe UI(Vue 3 + TailwindCSS)——禁止使用原生JS或jQuery
  • 谨慎设计工作流:在实现前映射所有状态和转换;考虑回滚路径
  • 处理边缘情况:在工作流中规划已取消、暂停和异常状态
  • 尽早测试性能:对高容量DocType和复杂查询进行负载测试
  • 重操作使用后台任务:永远不要用长时间运行的任务阻塞Web请求
  • 记录关键操作:使用
    frappe.log_error()
    和活动日志保证可审计性

Common Mistakes

常见错误

MistakeWhy It FailsFix
Over-complex workflowsHard to maintain, user confusionKeep workflows linear when possible; split complex flows
Missing error handling in integrationsSilent failures, data inconsistencyWrap external calls in try/except; log errors; retry logic
Race conditions in document updatesData corruptionUse
frappe.db.get_value(..., for_update=True)
for locks
SLA without timezone handlingWrong calculations for global usersStore and compare in UTC; use
frappe.utils.convert_utc_to_timezone
Not using queues for bulk operationsTimeouts, memory issuesUse
frappe.enqueue()
for operations on many records
Hardcoded role namesBreaks on role changesUse constants or settings for role names
Custom UI patternsInconsistent UX, user confusionStudy and follow CRM/Helpdesk app shells
Using vanilla JS/jQuery for frontendMaintenance burden, ecosystem mismatchAlways use Frappe UI with Vue 3
错误失败原因修复方案
工作流过于复杂难以维护,用户困惑尽可能保持工作流线性;拆分复杂流程
集成中缺少错误处理静默失败,数据不一致用try/except包裹外部调用;记录错误;添加重试逻辑
文档更新中的竞争条件数据损坏使用
frappe.db.get_value(..., for_update=True)
加锁
SLA未处理时区全球用户计算错误以UTC存储和比较时间;使用
frappe.utils.convert_utc_to_timezone
批量操作未使用队列超时,内存问题对多记录操作使用
frappe.enqueue()
硬编码角色名称角色变更时失效使用常量或配置存储角色名称
自定义UI模式UX不一致,用户困惑学习并遵循CRM/Helpdesk应用外壳
前端使用原生JS/jQuery维护负担,生态不兼容始终使用基于Vue 3的Frappe UI