@rglabs/butterfly 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +201 -0
- package/README.md +371 -0
- package/dist/commands/add.d.ts +23 -0
- package/dist/commands/add.js +303 -0
- package/dist/commands/code.d.ts +11 -0
- package/dist/commands/code.js +72 -0
- package/dist/commands/create-object.d.ts +6 -0
- package/dist/commands/create-object.js +293 -0
- package/dist/commands/create-report.d.ts +6 -0
- package/dist/commands/create-report.js +154 -0
- package/dist/commands/diff.d.ts +4 -0
- package/dist/commands/diff.js +238 -0
- package/dist/commands/download.d.ts +4 -0
- package/dist/commands/download.js +374 -0
- package/dist/commands/layout.d.ts +12 -0
- package/dist/commands/layout.js +83 -0
- package/dist/commands/record.d.ts +21 -0
- package/dist/commands/record.js +483 -0
- package/dist/commands/run-poc.d.ts +3 -0
- package/dist/commands/run-poc.js +18 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.js +66 -0
- package/dist/commands/start-poc.d.ts +3 -0
- package/dist/commands/start-poc.js +55 -0
- package/dist/commands/sync-docs.d.ts +3 -0
- package/dist/commands/sync-docs.js +27 -0
- package/dist/commands/translate.d.ts +13 -0
- package/dist/commands/translate.js +401 -0
- package/dist/commands/upload.d.ts +3 -0
- package/dist/commands/upload.js +150 -0
- package/dist/commands/workflow-info.d.ts +13 -0
- package/dist/commands/workflow-info.js +161 -0
- package/dist/components/ConflictResolver.d.ts +12 -0
- package/dist/components/ConflictResolver.js +77 -0
- package/dist/components/DiffView.d.ts +11 -0
- package/dist/components/DiffView.js +101 -0
- package/dist/components/DownloadProgress.d.ts +11 -0
- package/dist/components/DownloadProgress.js +29 -0
- package/dist/components/RecordPreview.d.ts +11 -0
- package/dist/components/RecordPreview.js +91 -0
- package/dist/components/SetupForm.d.ts +8 -0
- package/dist/components/SetupForm.js +56 -0
- package/dist/components/UploadProgress.d.ts +13 -0
- package/dist/components/UploadProgress.js +42 -0
- package/dist/diff/adapters/index.d.ts +8 -0
- package/dist/diff/adapters/index.js +18 -0
- package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
- package/dist/diff/adapters/objectsAdapter.js +177 -0
- package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
- package/dist/diff/adapters/reportsAdapter.js +212 -0
- package/dist/diff/adapters/types.d.ts +19 -0
- package/dist/diff/adapters/types.js +2 -0
- package/dist/diff/engine.d.ts +19 -0
- package/dist/diff/engine.js +57 -0
- package/dist/diff/types.d.ts +34 -0
- package/dist/diff/types.js +110 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +117 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/api.d.ts +85 -0
- package/dist/utils/api.js +1031 -0
- package/dist/utils/auth.d.ts +4 -0
- package/dist/utils/auth.js +22 -0
- package/dist/utils/bfySplitter.d.ts +12 -0
- package/dist/utils/bfySplitter.js +151 -0
- package/dist/utils/docs.d.ts +16 -0
- package/dist/utils/docs.js +186 -0
- package/dist/utils/errorLogger.d.ts +6 -0
- package/dist/utils/errorLogger.js +29 -0
- package/dist/utils/files.d.ts +14 -0
- package/dist/utils/files.js +772 -0
- package/dist/utils/lockManager.d.ts +15 -0
- package/dist/utils/lockManager.js +126 -0
- package/dist/utils/resourceHandlers.d.ts +50 -0
- package/dist/utils/resourceHandlers.js +684 -0
- package/dist/utils/resourceMapping.d.ts +32 -0
- package/dist/utils/resourceMapping.js +210 -0
- package/dist/utils/singleResourceDownload.d.ts +14 -0
- package/dist/utils/singleResourceDownload.js +261 -0
- package/dist/utils/summaryGenerator.d.ts +2 -0
- package/dist/utils/summaryGenerator.js +183 -0
- package/dist/utils/uploadHandler.d.ts +31 -0
- package/dist/utils/uploadHandler.js +263 -0
- package/docs/AI_API.md +93 -0
- package/docs/CLAUDE.md +216 -0
- package/docs/PROJECT_SPECIFIC.md +1 -0
- package/docs/RECORD_COMMAND.md +262 -0
- package/docs/WORKFLOW_API.md +480 -0
- package/docs/bfy-splitting.md +126 -0
- package/docs/cli-commands.md +333 -0
- package/docs/examples/README.md +95 -0
- package/docs/examples/order-system.md +147 -0
- package/docs/examples/product-catalog.md +195 -0
- package/docs/examples/reports.md +187 -0
- package/docs/excel-export.md +216 -0
- package/docs/field-types/README.md +29 -0
- package/docs/field-types/calculated.md +147 -0
- package/docs/field-types/code-mappings.md +84 -0
- package/docs/field-types/custom.md +340 -0
- package/docs/object-specs/README.md +136 -0
- package/docs/object-specs/code-parameters.md +151 -0
- package/docs/object-specs/creating.md +203 -0
- package/docs/object-specs/js-code-examples.md +208 -0
- package/docs/object-specs/js-field-updates.md +168 -0
- package/docs/objects/README.md +89 -0
- package/docs/objects/creating.md +127 -0
- package/docs/page-layout.md +361 -0
- package/docs/permissions.md +260 -0
- package/docs/reports.md +197 -0
- package/docs/state-machines.md +544 -0
- package/docs/tasks/create-object.md +81 -0
- package/docs/translations.md +346 -0
- package/docs/twig-helpers.md +283 -0
- package/docs/webservices.md +159 -0
- package/docs/workspaces.md +176 -0
- package/package.json +59 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# State Machines
|
|
2
|
+
|
|
3
|
+
This document explains how to work with state machines in Butterfly.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
State machines define workflow states, transitions, roles, and actions for managing record lifecycles. When you create a new state machine, the system automatically creates default components.
|
|
8
|
+
|
|
9
|
+
## Default State and Role Creation
|
|
10
|
+
|
|
11
|
+
When a new state machine is created, the system **automatically creates**:
|
|
12
|
+
|
|
13
|
+
1. **Default State**: A state named `"Created"` (system_name: `created`)
|
|
14
|
+
2. **Default Role**: A role named `"General"`
|
|
15
|
+
|
|
16
|
+
> **Important:** You don't need to manually create these. If you want to change the default state or role names, simply update them after creation.
|
|
17
|
+
|
|
18
|
+
### Updating Default State Name
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_states__edit
|
|
22
|
+
Content-Type: application/x-www-form-urlencoded
|
|
23
|
+
|
|
24
|
+
id=<STATE_ID>&name=New State Name&system_name=new_state_name
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Updating Default Role Name
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_roles__edit
|
|
31
|
+
Content-Type: application/x-www-form-urlencoded
|
|
32
|
+
|
|
33
|
+
id=<ROLE_ID>&name=New Role Name
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Binding a State Machine to an Object
|
|
37
|
+
|
|
38
|
+
To use a state machine with an object, add a field with type `state` and set the state machine ID as the parameter (`val_1`).
|
|
39
|
+
|
|
40
|
+
### Adding a State Field to an Object
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
POST /admin/ajax/cms_object/operation?do=object_specs__add
|
|
44
|
+
Content-Type: application/x-www-form-urlencoded
|
|
45
|
+
|
|
46
|
+
object_id=<OBJECT_ID>&name=Status&column_name=state&type=state&val_1=<STATE_MACHINE_ID>&edit_position=1&edit_order_no=1
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Parameters for `state` field type:**
|
|
50
|
+
|
|
51
|
+
| Parameter | Description |
|
|
52
|
+
|-----------|-------------|
|
|
53
|
+
| `val_1` | State machine ID to bind |
|
|
54
|
+
|
|
55
|
+
**Recommended positioning:**
|
|
56
|
+
|
|
57
|
+
| Parameter | Value | Description |
|
|
58
|
+
|-----------|-------|-------------|
|
|
59
|
+
| `edit_position` | `1` | Right column (sidebar) |
|
|
60
|
+
| `edit_order_no` | `1` | Top of the column |
|
|
61
|
+
|
|
62
|
+
**Example:**
|
|
63
|
+
```
|
|
64
|
+
POST /admin/ajax/cms_object/operation?do=object_specs__add
|
|
65
|
+
Content-Type: application/x-www-form-urlencoded
|
|
66
|
+
|
|
67
|
+
object_id=128&name=Order Status&column_name=state&type=state&val_1=5&edit_position=1&edit_order_no=1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This creates a "state" column in your object that:
|
|
71
|
+
- Displays the current state from the bound state machine
|
|
72
|
+
- Shows available transitions based on user roles
|
|
73
|
+
- Tracks state history for the record
|
|
74
|
+
|
|
75
|
+
> **Best Practice:** Place the state field in the right column (`edit_position=1`) at the top (`edit_order_no=1`) so it's always visible and accessible in the sidebar.
|
|
76
|
+
|
|
77
|
+
> **Note:** The column name is typically `state`, but you can use any column name.
|
|
78
|
+
|
|
79
|
+
## Tables Involved
|
|
80
|
+
|
|
81
|
+
| Table | Purpose |
|
|
82
|
+
|-------|---------|
|
|
83
|
+
| `bfy_state_machines` | Main state machine definition |
|
|
84
|
+
| `bfy_state_machine_states` | States within a state machine |
|
|
85
|
+
| `bfy_state_machine_roles` | Roles that can perform transitions |
|
|
86
|
+
| `bfy_state_machine_transitions` | Transitions between states |
|
|
87
|
+
| `bfy_state_machine_transition_specs` | Form fields shown during transitions |
|
|
88
|
+
| `bfy_sm_actions` | Reusable actions |
|
|
89
|
+
| `bfy_sm_transition_actions` | Actions triggered by transitions |
|
|
90
|
+
| `bfy_sm_action_specs` | Form fields for actions |
|
|
91
|
+
|
|
92
|
+
## Directory Structure
|
|
93
|
+
|
|
94
|
+
When downloading state machines, the CLI creates:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
bfy_state_machines/
|
|
98
|
+
└── [state_machine_name]/
|
|
99
|
+
├── state_machine.json # Main state machine data
|
|
100
|
+
├── states/
|
|
101
|
+
│ └── [state_name]/
|
|
102
|
+
│ └── state.json
|
|
103
|
+
├── roles/
|
|
104
|
+
│ └── [role_name]/
|
|
105
|
+
│ ├── role.json
|
|
106
|
+
│ └── validation_code.bfy # If validation code exists
|
|
107
|
+
├── transitions/
|
|
108
|
+
│ └── [transition_name]/
|
|
109
|
+
│ ├── transition.json
|
|
110
|
+
│ ├── action_code.bfy # If action code exists
|
|
111
|
+
│ ├── validation_code.bfy # If validation code exists
|
|
112
|
+
│ └── specs/ # Transition form fields
|
|
113
|
+
│ └── [spec_name]/
|
|
114
|
+
│ └── spec.json
|
|
115
|
+
├── actions/
|
|
116
|
+
│ └── [action_name]/
|
|
117
|
+
│ ├── action.json
|
|
118
|
+
│ ├── code.bfy # Action code
|
|
119
|
+
│ └── specs/ # Action form fields
|
|
120
|
+
│ └── [spec_name]/
|
|
121
|
+
│ └── spec.json
|
|
122
|
+
└── transition_actions/
|
|
123
|
+
└── [mapping_name].json # Transition-action mappings
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## State Machine Table (`bfy_state_machines`)
|
|
127
|
+
|
|
128
|
+
| Column | Type | Required | Description |
|
|
129
|
+
|--------|------|----------|-------------|
|
|
130
|
+
| `name` | string | Yes | Display name of the state machine |
|
|
131
|
+
| `bfy_state_machine_state_id` | int | No | Default/initial state ID |
|
|
132
|
+
| `bfy_workspace_id` | int | No | Workspace ID |
|
|
133
|
+
|
|
134
|
+
### Creating a State Machine
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machines__add
|
|
138
|
+
Content-Type: application/x-www-form-urlencoded
|
|
139
|
+
|
|
140
|
+
name=Order Status
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
> **Note:** After creation, a default state "Created" and role "General" are automatically added.
|
|
144
|
+
|
|
145
|
+
## States Table (`bfy_state_machine_states`)
|
|
146
|
+
|
|
147
|
+
| Column | Type | Required | Description |
|
|
148
|
+
|--------|------|----------|-------------|
|
|
149
|
+
| `bfy_state_machine_id` | int | No | Parent state machine ID |
|
|
150
|
+
| `name` | string | Yes | Display name |
|
|
151
|
+
| `system_name` | string | No | System identifier (slug) |
|
|
152
|
+
|
|
153
|
+
### Creating Additional States
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_states__add
|
|
157
|
+
Content-Type: application/x-www-form-urlencoded
|
|
158
|
+
|
|
159
|
+
bfy_state_machine_id=<SM_ID>&name=Processing&system_name=processing
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Roles Table (`bfy_state_machine_roles`)
|
|
163
|
+
|
|
164
|
+
| Column | Type | Required | Description |
|
|
165
|
+
|--------|------|----------|-------------|
|
|
166
|
+
| `bfy_state_machine_id` | int | No | Parent state machine ID |
|
|
167
|
+
| `name` | string | Yes | Display name |
|
|
168
|
+
| `validation_code` | string | No | Twig code to validate if user has this role |
|
|
169
|
+
|
|
170
|
+
### Creating Additional Roles
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_roles__add
|
|
174
|
+
Content-Type: application/x-www-form-urlencoded
|
|
175
|
+
|
|
176
|
+
bfy_state_machine_id=<SM_ID>&name=Admin
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Role Validation Code
|
|
180
|
+
|
|
181
|
+
The `validation_code` field contains Twig code that determines if the current user has this role:
|
|
182
|
+
|
|
183
|
+
```twig
|
|
184
|
+
{# Example: Check if user is admin #}
|
|
185
|
+
{% if user.role == 'admin' %}
|
|
186
|
+
{{ true }}
|
|
187
|
+
{% endif %}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Transitions Table (`bfy_state_machine_transitions`)
|
|
191
|
+
|
|
192
|
+
| Column | Type | Required | Description |
|
|
193
|
+
|--------|------|----------|-------------|
|
|
194
|
+
| `bfy_state_machine_id` | int | No | Parent state machine ID |
|
|
195
|
+
| `name` | string | Yes | Display name |
|
|
196
|
+
| `from_bfy_state_machine_state_id` | int | No | Source state ID |
|
|
197
|
+
| `to_bfy_state_machine_state_id` | int | No | Target state ID |
|
|
198
|
+
| `bfy_state_machine_role_id` | int | No | Required role ID |
|
|
199
|
+
| `action_code` | string | No | Twig code executed on transition (for single-use actions) |
|
|
200
|
+
| `validation_code` | string | No | Twig code to validate transition |
|
|
201
|
+
|
|
202
|
+
### Action Code vs Reusable Actions
|
|
203
|
+
|
|
204
|
+
When adding logic to transitions, choose between:
|
|
205
|
+
|
|
206
|
+
| Approach | When to Use | Location |
|
|
207
|
+
|----------|-------------|----------|
|
|
208
|
+
| `action_code` | Action is used in **only one transition** | Inline in `bfy_state_machine_transitions.action_code` |
|
|
209
|
+
| `bfy_sm_actions` | Action is **reused across multiple transitions** | Separate record in `bfy_sm_actions`, linked via `bfy_sm_transition_actions` |
|
|
210
|
+
|
|
211
|
+
**Use `action_code` when:**
|
|
212
|
+
- The logic is specific to a single transition
|
|
213
|
+
- You don't need to reuse the same logic elsewhere
|
|
214
|
+
- The action is simple and self-contained
|
|
215
|
+
|
|
216
|
+
**Use `bfy_sm_actions` when:**
|
|
217
|
+
- The same action needs to run on multiple transitions
|
|
218
|
+
- You want to maintain the logic in one place (DRY principle)
|
|
219
|
+
- The action is complex and benefits from being modular
|
|
220
|
+
|
|
221
|
+
### Accessing Transition Spec Values in Action Code
|
|
222
|
+
|
|
223
|
+
When a transition has specs (form fields defined via `bfy_state_machine_transition_specs`), the values entered by the user during the transition are accessible in `action_code` using `params.XXX`:
|
|
224
|
+
|
|
225
|
+
```twig
|
|
226
|
+
{# Access transition spec values using params.column_name #}
|
|
227
|
+
{% set rejectionReason = params.rejection_reason %}
|
|
228
|
+
{% set notifyCustomer = params.notify_customer %}
|
|
229
|
+
|
|
230
|
+
{# Example: Use spec values to update the record #}
|
|
231
|
+
{{ setValue('rejection_reason', params.rejection_reason) }}
|
|
232
|
+
|
|
233
|
+
{# Example: Conditional logic based on spec value #}
|
|
234
|
+
{% if params.notify_customer == '1' %}
|
|
235
|
+
{# Send notification logic here #}
|
|
236
|
+
{% endif %}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
> **Important:** `params.XXX` is used to access transition spec values in `action_code`. This is different from `getParameter('XXX')` which accesses POST/GET parameters from HTTP requests elsewhere in the system.
|
|
240
|
+
|
|
241
|
+
### Transition Validation Code
|
|
242
|
+
|
|
243
|
+
The `validation_code` field in transitions determines whether a transition button should be shown and if the transition can be executed. Use `{{ info.XXX }}` to access current record data.
|
|
244
|
+
|
|
245
|
+
#### Validation Functions
|
|
246
|
+
|
|
247
|
+
| Function | Description |
|
|
248
|
+
|----------|-------------|
|
|
249
|
+
| *(no function)* | Transition is valid and button is shown |
|
|
250
|
+
| `{{ notValid() }}` | Transition is not valid, button is hidden |
|
|
251
|
+
| `{{ validRequire('Message') }}` | Button is shown but disabled with message; something must be fulfilled first |
|
|
252
|
+
| `{{ validHidden() }}` | Transition is valid but button is hidden; can only be triggered programmatically |
|
|
253
|
+
|
|
254
|
+
#### Examples
|
|
255
|
+
|
|
256
|
+
**Simple condition - hide button if already processed:**
|
|
257
|
+
```twig
|
|
258
|
+
{% if info.is_processed == 1 %}
|
|
259
|
+
{{ notValid() }}
|
|
260
|
+
{% endif %}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Require a field to be filled before transition:**
|
|
264
|
+
```twig
|
|
265
|
+
{% if info.assigned_to is empty %}
|
|
266
|
+
{{ validRequire('Please assign a user first') }}
|
|
267
|
+
{% endif %}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Hidden transition (triggered from external code/API):**
|
|
271
|
+
```twig
|
|
272
|
+
{{ validHidden() }}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Complex validation with multiple conditions:**
|
|
276
|
+
```twig
|
|
277
|
+
{% if info.total_amount <= 0 %}
|
|
278
|
+
{{ validRequire('Order must have items') }}
|
|
279
|
+
{% elseif info.shipping_address is empty %}
|
|
280
|
+
{{ validRequire('Shipping address required') }}
|
|
281
|
+
{% elseif info.is_cancelled == 1 %}
|
|
282
|
+
{{ notValid() }}
|
|
283
|
+
{% endif %}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
> **Note:** If no validation function is called, the transition is considered valid and the button will be displayed.
|
|
287
|
+
|
|
288
|
+
### Creating a Transition
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_transitions__add
|
|
292
|
+
Content-Type: application/x-www-form-urlencoded
|
|
293
|
+
|
|
294
|
+
bfy_state_machine_id=<SM_ID>&name=Start Processing&from_bfy_state_machine_state_id=<FROM_STATE_ID>&to_bfy_state_machine_state_id=<TO_STATE_ID>&bfy_state_machine_role_id=<ROLE_ID>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Workflow Example
|
|
298
|
+
|
|
299
|
+
### 1. Create State Machine
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machines__add
|
|
303
|
+
name=Order Workflow
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Response includes the new state machine ID. Default "Created" state and "General" role are auto-created.
|
|
307
|
+
|
|
308
|
+
### 2. Add Additional States
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
# Add "Processing" state
|
|
312
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_states__add
|
|
313
|
+
bfy_state_machine_id=1&name=Processing&system_name=processing
|
|
314
|
+
|
|
315
|
+
# Add "Completed" state
|
|
316
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_states__add
|
|
317
|
+
bfy_state_machine_id=1&name=Completed&system_name=completed
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 3. Add Roles (Optional)
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
# Add "Manager" role
|
|
324
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_roles__add
|
|
325
|
+
bfy_state_machine_id=1&name=Manager
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 4. Create Transitions
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
# Transition from Created to Processing
|
|
332
|
+
POST /admin/ajax/cms_object/operation?do=bfy_state_machine_transitions__add
|
|
333
|
+
bfy_state_machine_id=1&name=Start Processing&from_bfy_state_machine_state_id=1&to_bfy_state_machine_state_id=2&bfy_state_machine_role_id=1
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 5. Download to Local
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
butterfly-cli download -t bfy_state_machines -n "Order Workflow"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Triggering State Transitions Programmatically
|
|
343
|
+
|
|
344
|
+
State transitions can be triggered via the `butterfly-cli record edit` command by passing a special JSON structure to the `state` field.
|
|
345
|
+
|
|
346
|
+
### Automatic Transitions (from State 0)
|
|
347
|
+
|
|
348
|
+
When a record is created or saved, certain transitions are triggered automatically based on the `from_bfy_state_machine_state_id` value:
|
|
349
|
+
|
|
350
|
+
| From State ID | To State ID | Trigger Condition |
|
|
351
|
+
|---------------|-------------|-------------------|
|
|
352
|
+
| `0` | Default state ID | **On record creation** - Automatically triggered when a new record is added |
|
|
353
|
+
| `0` | `0` | **On every save** - Executed on all save operations (insert and update) |
|
|
354
|
+
|
|
355
|
+
> **Note:** Transitions with `from_bfy_state_machine_state_id = 0` act as hooks that run automatically without manual triggering.
|
|
356
|
+
|
|
357
|
+
### Manual Transition via CLI
|
|
358
|
+
|
|
359
|
+
To manually trigger a state transition on an existing record, use `butterfly-cli record edit` with a JSON-encoded `state` field:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
butterfly-cli record edit <table_name> --id <RECORD_ID> --data '{"state": "{\"state_transition_id\":<TRANSITION_ID>,\"params\":{}}"}'
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**State field structure:**
|
|
366
|
+
|
|
367
|
+
| Field | Type | Description |
|
|
368
|
+
|-------|------|-------------|
|
|
369
|
+
| `state_transition_id` | int | The ID of the transition to execute (from `bfy_state_machine_transitions.id`) |
|
|
370
|
+
| `params` | object | Key-value pairs for transition spec fields (`column_name: value`) |
|
|
371
|
+
|
|
372
|
+
### Example: Transition without Parameters
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
butterfly-cli record edit orders --id 123 --data '{"state": "{\"state_transition_id\":2,\"params\":{}}"}'
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Example: Transition with Parameters
|
|
379
|
+
|
|
380
|
+
If the transition has spec fields (form fields shown during transition), pass their values in `params`:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
butterfly-cli record edit orders --id 123 --data '{"state": "{\"state_transition_id\":5,\"params\":{\"rejection_reason\":\"Out of stock\",\"notify_customer\":\"1\"}}"}'
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
In this example:
|
|
387
|
+
- `state_transition_id: 5` - The transition to execute
|
|
388
|
+
- `rejection_reason` and `notify_customer` - Transition spec column names with their values
|
|
389
|
+
|
|
390
|
+
### Finding Transition IDs
|
|
391
|
+
|
|
392
|
+
Transition IDs can be found in:
|
|
393
|
+
1. **Local files**: `butterfly-resources/bfy_state_machines/[name]/transitions/[transition]/transition.json`
|
|
394
|
+
2. **Database**: Query `bfy_state_machine_transitions` table
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
# Using CLI to get transition details
|
|
398
|
+
butterfly-cli record get bfy_state_machine_transitions --column bfy_state_machine_id --value <SM_ID>
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## CLI Commands
|
|
402
|
+
|
|
403
|
+
### Download State Machines
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
# Download all state machines
|
|
407
|
+
butterfly-cli download -t bfy_state_machines
|
|
408
|
+
|
|
409
|
+
# Download specific state machine
|
|
410
|
+
butterfly-cli download -t bfy_state_machines -n "Order Workflow"
|
|
411
|
+
|
|
412
|
+
# Download with cleanup
|
|
413
|
+
butterfly-cli download -t bfy_state_machines --cleanup
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Upload Changes
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
butterfly-cli upload <path>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Upload state machine files (state_machine.json, state.json, role.json, transition.json, code files) to sync changes to the platform.
|
|
423
|
+
|
|
424
|
+
## CRUD Context in State Machine Code
|
|
425
|
+
|
|
426
|
+
Within state machine transition code (`action_code`), validation code (`validation_code`), and action code, you have access to the current CRUD operation context using the `getCrud()` function.
|
|
427
|
+
|
|
428
|
+
### Available Functions
|
|
429
|
+
|
|
430
|
+
| Function | Description | Return Type |
|
|
431
|
+
|----------|-------------|-------------|
|
|
432
|
+
| `getCrud()` | Returns the current CRUD operation object | object |
|
|
433
|
+
| `getCrud().get_difference()` | Returns changed fields with old and new values | array |
|
|
434
|
+
| `getCrud().getOldData('column_name')` | Returns the previous value of a specific column | mixed |
|
|
435
|
+
| `getValue('column_name')` | Returns the current value of a column | mixed |
|
|
436
|
+
|
|
437
|
+
### Detecting Changes with `get_difference()`
|
|
438
|
+
|
|
439
|
+
The `get_difference()` method returns an array of changed fields. Each changed field contains `old_data` and `new_data` keys:
|
|
440
|
+
|
|
441
|
+
```twig
|
|
442
|
+
{% set changes = getCrud().get_difference() %}
|
|
443
|
+
|
|
444
|
+
{# Example return value when 'title' and 'status' changed: #}
|
|
445
|
+
{# {
|
|
446
|
+
"title": {"old_data": "Old Title", "new_data": "New Title"},
|
|
447
|
+
"status": {"old_data": "draft", "new_data": "published"}
|
|
448
|
+
}
|
|
449
|
+
#}
|
|
450
|
+
|
|
451
|
+
{# If no changes, returns empty array: {} #}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Common Use Cases
|
|
455
|
+
|
|
456
|
+
#### 1. Update modification timestamp when any field changes
|
|
457
|
+
|
|
458
|
+
```twig
|
|
459
|
+
{% set changes = getCrud().get_difference() %}
|
|
460
|
+
{% if changes|length > 0 %}
|
|
461
|
+
{{ setValue('modification_date', 'now'|date('Y-m-d H:i:s')) }}
|
|
462
|
+
{{ setValue('updated_by', currentUser('id')) }}
|
|
463
|
+
{% endif %}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
#### 2. Check if a specific field was changed
|
|
467
|
+
|
|
468
|
+
```twig
|
|
469
|
+
{% set changes = getCrud().get_difference() %}
|
|
470
|
+
{% if changes.status is defined %}
|
|
471
|
+
{# Status field was modified #}
|
|
472
|
+
{% set oldStatus = changes.status.old_data %}
|
|
473
|
+
{% set newStatus = changes.status.new_data %}
|
|
474
|
+
|
|
475
|
+
{# Log the status change or trigger notifications #}
|
|
476
|
+
{% endif %}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### 3. Compare old and current values using getOldData()
|
|
480
|
+
|
|
481
|
+
```twig
|
|
482
|
+
{% set oldPrice = getCrud().getOldData('price') %}
|
|
483
|
+
{% set currentPrice = getValue('price') %}
|
|
484
|
+
|
|
485
|
+
{% if oldPrice != currentPrice %}
|
|
486
|
+
{# Price was changed, log the change #}
|
|
487
|
+
{% set response = crud()
|
|
488
|
+
.table('price_history')
|
|
489
|
+
.insert({
|
|
490
|
+
"product_id": getValue('id'),
|
|
491
|
+
"old_price": oldPrice,
|
|
492
|
+
"new_price": currentPrice,
|
|
493
|
+
"changed_at": 'now'|date('Y-m-d H:i:s')
|
|
494
|
+
})
|
|
495
|
+
%}
|
|
496
|
+
{% endif %}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### 4. Prevent transition if certain fields haven't changed
|
|
500
|
+
|
|
501
|
+
```twig
|
|
502
|
+
{# In validation_code - prevent completion if no work was done #}
|
|
503
|
+
{% set changes = getCrud().get_difference() %}
|
|
504
|
+
{% if changes.result is not defined and changes.notes is not defined %}
|
|
505
|
+
{{ errorMessage('Please fill in result or notes before completing this task.') }}
|
|
506
|
+
{% endif %}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### 5. Track who made changes
|
|
510
|
+
|
|
511
|
+
```twig
|
|
512
|
+
{% set changes = getCrud().get_difference() %}
|
|
513
|
+
{% if changes|length > 0 %}
|
|
514
|
+
{{ setValue('last_modified_by', currentUser('id')) }}
|
|
515
|
+
{{ setValue('last_modified_at', 'now'|date('Y-m-d H:i:s')) }}
|
|
516
|
+
|
|
517
|
+
{# Optionally log all changes #}
|
|
518
|
+
{% for field, change in changes %}
|
|
519
|
+
{% set log = crud()
|
|
520
|
+
.table('audit_log')
|
|
521
|
+
.insert({
|
|
522
|
+
"record_id": getValue('id'),
|
|
523
|
+
"field_name": field,
|
|
524
|
+
"old_value": change.old_data,
|
|
525
|
+
"new_value": change.new_data,
|
|
526
|
+
"changed_by": currentUser('id'),
|
|
527
|
+
"changed_at": 'now'|date('Y-m-d H:i:s')
|
|
528
|
+
})
|
|
529
|
+
%}
|
|
530
|
+
{% endfor %}
|
|
531
|
+
{% endif %}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Important Notes
|
|
535
|
+
|
|
536
|
+
- `getCrud()` is only available within state machine context (transitions, actions, validation code)
|
|
537
|
+
- `get_difference()` returns an empty array `{}` if no fields were modified
|
|
538
|
+
- `getOldData()` returns the value before the current save operation
|
|
539
|
+
- `getValue()` returns the current/new value being saved
|
|
540
|
+
- These functions are particularly useful for audit trails, conditional logic, and automatic field updates
|
|
541
|
+
|
|
542
|
+
## Related
|
|
543
|
+
|
|
544
|
+
- [Main CLAUDE.md](./CLAUDE.md) - Full CLI documentation
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Object Generation Prompt
|
|
2
|
+
|
|
3
|
+
You are a database architect for Butterfly Platform. Create an object definition based on the user's request.
|
|
4
|
+
|
|
5
|
+
## CRITICAL RULES
|
|
6
|
+
|
|
7
|
+
### 1. Check Existing Tables First
|
|
8
|
+
- Look in `butterfly-resources/objects/app/` for existing application tables
|
|
9
|
+
- Look in `butterfly-resources/objects/butterfly/` for core/system tables
|
|
10
|
+
- Use existing tables for dropdowns/autocomplete references
|
|
11
|
+
|
|
12
|
+
### 2. Output Format: CSV with backtick (`) separator
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
[OBJECT]
|
|
16
|
+
name`table_name`database_alias`has_item`seo`menu_path`section_title`auto_increment_column_name`left_column_size
|
|
17
|
+
Display Name`table_name`default`0``Menu::icon>SubMenu::icon`Section Title`id`1/2
|
|
18
|
+
|
|
19
|
+
[SPECS]
|
|
20
|
+
name`column_name`type`val_1`val_2`val_3`val_4`val_5`column_size`required`tab_title`tab_placement`list_column`search_column`is_displayed_in_filter
|
|
21
|
+
Field Name`column_name`type`0`0`0`0`0`1/2`1``0`1`1`0
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 3. Tab Title Rules
|
|
25
|
+
- Use empty string for default tab (NOT "Genel", "General", etc.)
|
|
26
|
+
- Only use specific tab names for additional grouped tabs
|
|
27
|
+
|
|
28
|
+
### 4. Column Size Rules
|
|
29
|
+
- Single word field names: can use `1/3` or `1/4`
|
|
30
|
+
- Two+ word field names: must use `1/2` or `1`
|
|
31
|
+
|
|
32
|
+
### 5. Field Types Reference
|
|
33
|
+
|
|
34
|
+
| Type | val_1 | val_2 | val_3 | val_4 | val_5 |
|
|
35
|
+
|------|-------|-------|-------|-------|-------|
|
|
36
|
+
| `string` | 0 | 0 | 0 | 0 | 0 |
|
|
37
|
+
| `textarea` | 0 | 0 | 0 | 0 | 0 |
|
|
38
|
+
| `integer` | 0 | 0 | 0 | 0 | 0 |
|
|
39
|
+
| `float` | 0 | 0 | 0 | 0 | 0 |
|
|
40
|
+
| `money` | 0 | 0 | 0 | 0 | 0 |
|
|
41
|
+
| `date` | 0 | 0 | 0 | 0 | 0 |
|
|
42
|
+
| `datetime` | 0 | 0 | 0 | 0 | 0 |
|
|
43
|
+
| `checkbox` | 0 | 0 | 0 | 0 | 0 |
|
|
44
|
+
| `dropdown` | table_name | display_column | value_column | filter or 0 | 0 |
|
|
45
|
+
| `autocomplete` | table_name | display_column | value_column | filter or 0 | 0 |
|
|
46
|
+
| `from_list` | "key1:Label1\|key2:Label2" | 0 | 0 | 0 | 0 |
|
|
47
|
+
| `file_upload` | upload | 0 | 0 | 0 | 0 |
|
|
48
|
+
| `calculated` | twig_code | 0 | 0 | 0 | 0 |
|
|
49
|
+
| `status` | 0 | 0 | 0 | 0 | 0 |
|
|
50
|
+
|
|
51
|
+
### 6. Menu Path Format
|
|
52
|
+
Format: `Category::icon>SubCategory::icon`
|
|
53
|
+
Use Lucide icons: https://lucide.dev/icons
|
|
54
|
+
|
|
55
|
+
Common icons:
|
|
56
|
+
- Settings: `settings`, `sliders`
|
|
57
|
+
- Finance: `wallet`, `coins`, `banknote`
|
|
58
|
+
- Inventory: `package`, `boxes`, `warehouse`
|
|
59
|
+
- Sales: `shopping-cart`, `receipt`
|
|
60
|
+
- Master Data: `database`, `folder`
|
|
61
|
+
- Users: `users`, `user`
|
|
62
|
+
|
|
63
|
+
### 7. Layout Rules
|
|
64
|
+
- `tab_placement`: 0 = left column, 1 = right column
|
|
65
|
+
- `list_column`: 1 = show in listing table
|
|
66
|
+
- `search_column`: 1 = searchable field
|
|
67
|
+
- `is_displayed_in_filter`: 1 = show in filter bar
|
|
68
|
+
|
|
69
|
+
### 8. Important Notes
|
|
70
|
+
- Table names: plural, snake_case (e.g., `purchase_orders`)
|
|
71
|
+
- Column names: singular, snake_case (e.g., `order_date`)
|
|
72
|
+
- Foreign keys end with `_id` (e.g., `customer_id`)
|
|
73
|
+
- System fields (id, created_at, updated_at) are auto-created, don't add them
|
|
74
|
+
- Use Turkish characters properly (İ, Ş, Ü, Ğ, Ç, Ö) if source is Turkish
|
|
75
|
+
|
|
76
|
+
## Task
|
|
77
|
+
|
|
78
|
+
1. Read the user's request
|
|
79
|
+
2. Check existing tables in `butterfly-resources/objects/` for related tables
|
|
80
|
+
3. Generate a single object CSV definition
|
|
81
|
+
4. Write the CSV to `butterfly-resources/.temp/new_object.csv`
|