Skip to main content
ClaudeWave
Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package /tmp/frappe-core-workflow && cp -r /tmp/frappe-core-workflow/skills/source/core/frappe-core-workflow ~/.claude/skills/frappe-core-workflow
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Workflow Engine

The Frappe Workflow engine is a state machine that controls document lifecycle through configurable states, transitions, and role-based permissions. It governs when and how documents change status, who can perform actions, and what side effects occur on each transition.

## Quick Reference

```
Workflow DocType            → Defines the state machine for a specific DocType
├── states (child table)    → Workflow Document State rows
│   ├── state               → Link to Workflow State
│   ├── doc_status          → 0 (Draft), 1 (Submitted), 2 (Cancelled)
│   ├── allow_edit          → Role that can edit in this state
│   ├── update_field        → Field to update when entering state
│   ├── update_value        → Value to set (literal or expression)
│   └── next_action_email_template → Email Template link
└── transitions (child table) → Workflow Transition rows
    ├── state               → Source state (Link to Workflow State)
    ├── action              → Link to Workflow Action Master
    ├── next_state          → Target state (Link to Workflow State)
    ├── allowed             → Role that can perform this action
    ├── allow_self_approval → Check (default: 1)
    ├── condition           → Python expression (optional)
    └── transition_tasks    → Link to Workflow Transition Tasks
```

### Key Fields on Workflow DocType

| Field | Type | Purpose |
|-------|------|---------|
| `workflow_name` | Data | Unique identifier |
| `document_type` | Link → DocType | Target DocType |
| `is_active` | Check | Only ONE workflow per DocType can be active |
| `workflow_state_field` | Data | Default: `workflow_state` |
| `override_status` | Check | Prevent workflow from overriding list view status |
| `send_email_alert` | Check | Email notifications with next possible actions |

## How the Engine Works

### 1. Activation and Field Creation

When a Workflow is saved with `is_active = 1`:
- All other workflows for the same DocType are deactivated automatically
- A hidden Custom Field (`workflow_state_field`, default `workflow_state`) is created on the target DocType if it does not exist
- The field is type `Link` to `Workflow State`, with `hidden=1`, `allow_on_submit=1`, `no_copy=1`
- Existing documents with empty workflow state get their state set based on their current `docstatus`

### 2. State Resolution

Every document under a workflow has a `workflow_state` field. The engine resolves available transitions by:

1. Reading current `workflow_state` from the document
2. Filtering `workflow.transitions` where `transition.state == current_state`
3. Filtering by user roles: `transition.allowed in frappe.get_roles()`
4. Evaluating `transition.condition` via `frappe.safe_eval()` (if set)
5. Returning matching transitions as available actions

### 3. Applying a Transition

When `apply_workflow(doc, action)` is called:

1. Load document from DB (fresh read)
2. Get available transitions for current user
3. Find transition matching the requested `action`
4. Check self-approval: blocked if `allow_self_approval=0` AND user is document owner
5. Set `workflow_state_field` to `transition.next_state`
6. If `update_field` is set on the target state, update that field
7. Execute transition tasks (sync first, then async via `frappe.enqueue`)
8. Handle docstatus change based on source/target state `doc_status` values
9. Save/Submit/Cancel document accordingly
10. Add workflow comment

## Workflow and DocStatus Interaction

**CRITICAL**: The workflow engine controls docstatus transitions. You NEVER call `doc.submit()` or `doc.cancel()` directly on a workflow-controlled document. The workflow does it.

### DocStatus Transition Rules

| Source doc_status | Target doc_status | Engine Action | Valid? |
|:-:|:-:|---|:-:|
| 0 (Draft) | 0 (Draft) | `doc.save()` | YES |
| 0 (Draft) | 1 (Submitted) | `doc.submit()` | YES |
| 1 (Submitted) | 1 (Submitted) | `doc.save()` | YES |
| 1 (Submitted) | 2 (Cancelled) | `doc.cancel()` | YES |
| 2 (Cancelled) | ANY | BLOCKED | NO |
| 1 (Submitted) | 0 (Draft) | BLOCKED | NO |
| 0 (Draft) | 2 (Cancelled) | BLOCKED | NO |

**ALWAYS** define your states so that docstatus only moves forward: 0→0, 0→1, 1→1, 1→2.
**NEVER** create a transition from a cancelled state or from submitted back to draft.

### Non-Submittable DocTypes

If the target DocType is NOT submittable, ALL states MUST have `doc_status = 0`. The engine validates this and throws an error if any state has `doc_status = 1` or `2` on a non-submittable DocType.

## Workflow States

Workflow State is a separate DocType used as a master list. Each state has:

| Field | Purpose |
|-------|---------|
| `state` | Display name of the state |
| `style` | CSS class for badge display (Primary, Success, Warning, Danger, Info, Inverse) |
| `icon` | Font Awesome icon class |

### State Row Fields (Workflow Document State)

| Field | Purpose |
|-------|---------|
| `state` | Link to Workflow State |
| `doc_status` | Select: 0, 1, or 2 |
| `allow_edit` | Link to Role — ONLY this role can edit the document in this state |
| `update_field` | Field to update when document enters this state |
| `update_value` | Value to set (string or Python expression if `evaluate_as_expression=1`) |
| `is_optional_state` | Check — optional states are skipped in `get_next_possible_transitions` |
| `send_email` | Check (default 1) — send email notification on entering this state |
| `next_action_email_template` | Link to Email Template |
| `message` | Text message for the email notification |

## Workflow Transitions

Each transition row defines one possible action:

| Field | Purpose |
|-------|---------|
| `state` | Source state (MUST exist in states table) |
| `action` | Link to Workflow Action Master (e.g., "Approve", "Reject", "Review") |
| `next_state` | Target state (MUST exist in states table) |
| `allowed` | Link to Role — ONLY users with this role see this action |
| `allow_self_approval` | Check (default 1) — if 0, document owner cannot perform