Back to skills
extension
Category: Development & EngineeringNo API key required

frappe-state-machine-helper

Generate state machine logic for Frappe DocTypes. Use when implementing complex status workflows, state transitions, or document lifecycle management.

personAuthor: jakexiaohubgithub

Frappe State Machine Helper

Generate state machine logic for managing complex document states and transitions in Frappe DocTypes.

When to Use This Skill

Claude should invoke this skill when:

  • User wants to implement document status management
  • User needs state transition logic
  • User mentions state machine, status workflow, or document lifecycle
  • User wants to validate state transitions
  • User needs state-dependent behavior

Capabilities

1. State Transition Logic

Order Status State Machine:

class SalesOrder(Document):
    def validate(self):
        self.validate_state_transition()

    def validate_state_transition(self):
        """Validate allowed state transitions"""
        if not self.is_new():
            old_status = frappe.db.get_value('Sales Order', self.name, 'status')

            # Define allowed transitions
            allowed_transitions = {
                'Draft': ['Pending', 'Cancelled'],
                'Pending': ['Confirmed', 'Cancelled'],
                'Confirmed': ['In Progress', 'Cancelled'],
                'In Progress': ['Completed', 'On Hold'],
                'On Hold': ['In Progress', 'Cancelled'],
                'Completed': [],  # Terminal state
                'Cancelled': []   # Terminal state
            }

            if old_status != self.status:
                allowed = allowed_transitions.get(old_status, [])
                if self.status not in allowed:
                    frappe.throw(
                        _(f'Cannot transition from {old_status} to {self.status}')
                    )

    def on_submit(self):
        self.status = 'Confirmed'

    def on_cancel(self):
        self.status = 'Cancelled'

2. State-Dependent Actions

Actions Based on State:

class PaymentEntry(Document):
    def validate(self):
        if self.status == 'Draft':
            self.validate_draft_entry()
        elif self.status == 'Submitted':
            self.validate_submitted_entry()

    def before_submit(self):
        # Actions before state change
        if self.payment_type == 'Pay':
            self.validate_sufficient_balance()

        self.status = 'Submitted'

    def on_cancel(self):
        # Reverse actions
        if self.status == 'Submitted':
            self.reverse_gl_entries()

        self.status = 'Cancelled'

References

State Management Examples:

  • Sales Invoice: https://github.com/frappe/erpnext/blob/develop/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
  • Purchase Order: https://github.com/frappe/erpnext/blob/develop/erpnext/buying/doctype/purchase_order/purchase_order.py