Picking List State Machine Documentation
This document describes the complete state machine logic for Picking List.
State Definitions
Picking List has the following 5 states:
| Status Code | Name | Description |
|---|---|---|
ready | Ready | Picking list just created, waiting to start picking |
processing | Processing | Operator is performing picking operations |
done | Done | All orders in the picking list have been shipped |
canceled | Canceled | Picking list has been canceled (can be resumed) |
terminated | Terminated | Picking list has been terminated (some orders canceled or other reasons) |
State Transition Diagram
(State transition diagram picking_list_status.svg is currently unavailable; please refer to the "State Transition Events" table below)
D2 File: docs/state_machines/picking_list_status.d2
State Transition Events
1. start_picking (Start Picking)
Function: Start picking operations
State Transition:
ready→processing
Trigger Timing:
- Operator clicks "Start Picking" button
- First picking item is scanned
Business Logic:
- Record picking start time
- Record picking operator
- Create picking history record
Guard Conditions:
- Picking list status must be
ready - Picking list contains at least one picking item (
has_items?)
2. cancel (Cancel Picking List)
Function: Cancel picking list (can be resumed)
State Transition:
ready→canceledprocessing→canceled
Trigger Timing:
- Operator manually cancels picking list
- Need to pause picking operations
Business Logic:
- Record cancellation time
- Preserve picking list data (can be resumed)
Guard Conditions:
- Picking list status must be
readyorprocessing
3. resume (Resume Picking List)
Function: Resume canceled picking list
State Transition:
canceled→processing
Trigger Timing:
- Operator manually resumes picking list
- Cancellation reason has been resolved
Business Logic:
- Clear cancellation time
- Resume picking operations
Guard Conditions:
- Picking list status must be
canceled
4. complete (Complete Picking)
Function: Complete picking and shipment for all orders
State Transition:
processing→done
Trigger Timing:
- All orders in the picking list have status changed to
done - Automatically triggered by system (listening to order status changes)
- Or manually marked as complete by operator
Business Logic:
- Record completion time
- Create completion history record
- Release picking list resources
Guard Conditions:
- Picking list status must be
processing - All associated orders have
status==done(all_orders_shipped?)
5. terminate (Terminate Picking List)
Function: Terminate picking list (some orders canceled or other abnormal situations)
State Transition:
processing→terminated
Trigger Timing:
- Operator manually terminates
- Some orders canceled causing picking list unable to continue
- System error requires terminating picking process
Business Logic:
- Record termination time
- Record termination reason
- Unbind pending orders
- Release allocated inventory (if applicable)
Guard Conditions:
- Picking list status must be
processing - Requires admin permission or special confirmation
Permission Flag Methods
The following are suggested permission methods for controlling UI button display and feature enablement:
editable?
Description: Whether picking list can be edited (modify order content)
Logic:
def editable?
ready?
endReturns true for states:
ready- Ready state allows modification of orders
pickable?
Description: Whether picking list can start picking
Logic:
def pickable?
ready? && has_items?
endReturns true for states:
ready- Ready state with picking items can start picking
completable?
Description: Whether picking list can be manually completed
Logic:
def completable?
processing? && all_orders_shipped?
endReturns true for states:
processing- Processing and all orders have been shipped
terminatable?
Description: Whether picking list can be terminated
Logic:
def terminatable?
processing?
endReturns true for states:
processing- Processing can be terminated
cancelable?
Description: Whether picking list can be canceled
Logic:
def cancelable?
ready? || processing?
endReturns true for states:
ready- Ready can be canceledprocessing- Processing can be canceled
resumable?
Description: Whether picking list can be resumed
Logic:
def resumable?
canceled?
endReturns true for states:
canceled- Canceled can be resumed
in_progress?
Description: Whether picking list is in progress
Logic:
def in_progress?
processing?
endReturns true for states:
processing- Processing
finished?
Description: Whether picking list has finished (completed, canceled or terminated)
Logic:
def finished?
done? || terminated? || canceled?
endReturns true for states:
done- Completedcanceled- Canceledterminated- Terminated
Business Scenarios
Scenario 1: Normal Picking Flow
- Create Picking List: System automatically creates picking list based on picking rules →
ready - Start Picking: Operator scans first item →
processing - Picking & Shipping: Operator completes picking, parcel, and shipment operations
- Auto Complete: All orders shipped →
done
Scenario 2: Terminate Picking List
Case A: Partial Order Cancellation
- Picking list status is
processing - Several orders are canceled by customers
- Operator evaluates and decides to terminate this picking list
- Click "Terminate" and input reason →
terminated - Remaining valid orders can be reassigned to other picking lists
Case B: System Error
- Picking list status is
processing - Inventory error or system error occurs
- Admin decides to terminate this picking list
- Input termination reason →
terminated - Admin handles order reassignment later
Scenario 3: Partial Order Cancellation (Without Termination)
Case:
- Picking list status is
processing - One order is canceled by customer
Handling Logic:
- Remove canceled order from picking list
- If there are other valid orders, picking list continues normal flow
- If all orders are canceled, system automatically changes picking list to
terminated
Automatic Trigger Checks
System should automatically check and update picking list status in the following situations:
1. After Order Status Change
Check Point: When associated order's status changes
Check Logic:
def check_completion
return unless processing?
if all_orders_done?
complete!
end
end
def check_termination
return unless processing?
# If all orders are canceled, automatically terminate picking list
if orders.all?(&:canceled?)
terminate!
end
end2. After Picking Item Update (Optional)
Check Point: When picking_list_item updates picked_quantity
Check Logic:
# This check is optional, depending on business requirements
def check_all_items_scanned
return unless processing?
if all_items_scanned?
# Can send notification or update progress
notify_all_items_scanned
end
endDatabase Field Description
Current timestamp fields:
| Field Name | Type | Description |
|---|---|---|
status | string | Status (required) |
done_at | datetime | Completion time |
warehouse_id | integer | Warehouse ID |
parent_id | integer | Parent picking list ID (if hierarchical) |
UI Button Display Rules
Button display rules for each state:
| State | Edit | Start Picking | Cancel | Resume | Terminate | Complete |
|---|---|---|---|---|---|---|
ready | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
processing | ❌ | ❌ | ✅ | ❌ | ✅ | ✅* |
done | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
canceled | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
terminated | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Note:
- ✅ indicates button should be displayed and enabled
- ❌ indicates button should be hidden or disabled
- ✅* indicates conditional display (
processingstate shows "Complete" button only when all orders are shipped)
Implementation Suggestions
Using AASM Gem
Recommend using AASM gem to implement state machine, similar to Inbound model:
class PickingList < ApplicationRecord
include AASM
aasm column: :status do
state :ready, initial: true
state :processing
state :done
state :terminated
# Event: Start picking
event :start_picking do
transitions from: :ready, to: :processing
after do
update_column(:started_at, Time.current) if respond_to?(:started_at)
end
end
# Event: Complete
event :complete do
transitions from: :processing, to: :done, guard: :all_orders_done?
after do
update_column(:done_at, Time.current)
end
end
# Event: Terminate
event :terminate do
transitions from: :processing, to: :terminated
after do
update_column(:terminated_at, Time.current) if respond_to?(:terminated_at)
unbind_orders
end
end
end
# Permission methods
def editable?
ready?
end
def pickable?
ready?
end
def completable?
processing? && all_orders_done?
end
def terminatable?
processing?
end
def in_progress?
processing?
end
def finished?
done? || terminated?
end
# Guard methods
def all_orders_done?
orders.where.not(status: 'done').count.zero?
end
# Helper methods
def unbind_orders
orders.update_all(picking_list_id: nil)
end
endUsing Enum (Simplified Version)
If you don't need complex events and callbacks, you can also use Rails built-in enum:
class PickingList < ApplicationRecord
belongs_to :merchant
has_many :picking_list_items, dependent: :destroy
has_many :orders
# Status definition
enum :status, {
ready: 'ready',
processing: 'processing',
done: 'done',
terminated: 'terminated'
}
validates :status, inclusion: { in: statuses.keys }
# Permission methods
def editable?
ready?
end
def pickable?
ready?
end
def completable?
processing? && all_orders_done?
end
def terminatable?
processing?
end
def in_progress?
processing?
end
def finished?
done? || terminated?
end
# State transition methods
def start_picking!
raise AppErrors::Error::AppError.new(AppErrors::Error::INVALID_STATUS) unless pickable?
update!(status: :processing, started_at: Time.current)
end
def complete!
raise AppErrors::Error::AppError.new(AppErrors::Error::INVALID_STATUS) unless completable?
update!(status: :done, done_at: Time.current)
end
def terminate!(reason = nil)
raise AppErrors::Error::AppError.new(AppErrors::Error::INVALID_STATUS) unless terminatable?
update!(
status: :terminated,
terminated_at: Time.current,
terminate_reason: reason
)
unbind_orders
end
# Helper methods
def all_orders_done?
orders.where.not(status: 'done').count.zero?
end
def unbind_orders
orders.update_all(picking_list_id: nil)
end
# Auto-check methods (called when order status changes)
def check_completion
return unless processing?
if all_orders_done?
complete!
end
end
def check_termination
return unless processing?
if orders.all?(&:canceled?)
terminate!('All orders canceled')
end
end
endRelated Documents
- Order State Machine - Order state machine documentation
- Inbound State Machine - Inbound state machine documentation
- Picking List Operations - How operators use picking lists