@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.
Files changed (117) hide show
  1. package/CLAUDE.md +201 -0
  2. package/README.md +371 -0
  3. package/dist/commands/add.d.ts +23 -0
  4. package/dist/commands/add.js +303 -0
  5. package/dist/commands/code.d.ts +11 -0
  6. package/dist/commands/code.js +72 -0
  7. package/dist/commands/create-object.d.ts +6 -0
  8. package/dist/commands/create-object.js +293 -0
  9. package/dist/commands/create-report.d.ts +6 -0
  10. package/dist/commands/create-report.js +154 -0
  11. package/dist/commands/diff.d.ts +4 -0
  12. package/dist/commands/diff.js +238 -0
  13. package/dist/commands/download.d.ts +4 -0
  14. package/dist/commands/download.js +374 -0
  15. package/dist/commands/layout.d.ts +12 -0
  16. package/dist/commands/layout.js +83 -0
  17. package/dist/commands/record.d.ts +21 -0
  18. package/dist/commands/record.js +483 -0
  19. package/dist/commands/run-poc.d.ts +3 -0
  20. package/dist/commands/run-poc.js +18 -0
  21. package/dist/commands/setup.d.ts +3 -0
  22. package/dist/commands/setup.js +66 -0
  23. package/dist/commands/start-poc.d.ts +3 -0
  24. package/dist/commands/start-poc.js +55 -0
  25. package/dist/commands/sync-docs.d.ts +3 -0
  26. package/dist/commands/sync-docs.js +27 -0
  27. package/dist/commands/translate.d.ts +13 -0
  28. package/dist/commands/translate.js +401 -0
  29. package/dist/commands/upload.d.ts +3 -0
  30. package/dist/commands/upload.js +150 -0
  31. package/dist/commands/workflow-info.d.ts +13 -0
  32. package/dist/commands/workflow-info.js +161 -0
  33. package/dist/components/ConflictResolver.d.ts +12 -0
  34. package/dist/components/ConflictResolver.js +77 -0
  35. package/dist/components/DiffView.d.ts +11 -0
  36. package/dist/components/DiffView.js +101 -0
  37. package/dist/components/DownloadProgress.d.ts +11 -0
  38. package/dist/components/DownloadProgress.js +29 -0
  39. package/dist/components/RecordPreview.d.ts +11 -0
  40. package/dist/components/RecordPreview.js +91 -0
  41. package/dist/components/SetupForm.d.ts +8 -0
  42. package/dist/components/SetupForm.js +56 -0
  43. package/dist/components/UploadProgress.d.ts +13 -0
  44. package/dist/components/UploadProgress.js +42 -0
  45. package/dist/diff/adapters/index.d.ts +8 -0
  46. package/dist/diff/adapters/index.js +18 -0
  47. package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
  48. package/dist/diff/adapters/objectsAdapter.js +177 -0
  49. package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
  50. package/dist/diff/adapters/reportsAdapter.js +212 -0
  51. package/dist/diff/adapters/types.d.ts +19 -0
  52. package/dist/diff/adapters/types.js +2 -0
  53. package/dist/diff/engine.d.ts +19 -0
  54. package/dist/diff/engine.js +57 -0
  55. package/dist/diff/types.d.ts +34 -0
  56. package/dist/diff/types.js +110 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.js +117 -0
  59. package/dist/types/index.d.ts +18 -0
  60. package/dist/types/index.js +2 -0
  61. package/dist/utils/api.d.ts +85 -0
  62. package/dist/utils/api.js +1031 -0
  63. package/dist/utils/auth.d.ts +4 -0
  64. package/dist/utils/auth.js +22 -0
  65. package/dist/utils/bfySplitter.d.ts +12 -0
  66. package/dist/utils/bfySplitter.js +151 -0
  67. package/dist/utils/docs.d.ts +16 -0
  68. package/dist/utils/docs.js +186 -0
  69. package/dist/utils/errorLogger.d.ts +6 -0
  70. package/dist/utils/errorLogger.js +29 -0
  71. package/dist/utils/files.d.ts +14 -0
  72. package/dist/utils/files.js +772 -0
  73. package/dist/utils/lockManager.d.ts +15 -0
  74. package/dist/utils/lockManager.js +126 -0
  75. package/dist/utils/resourceHandlers.d.ts +50 -0
  76. package/dist/utils/resourceHandlers.js +684 -0
  77. package/dist/utils/resourceMapping.d.ts +32 -0
  78. package/dist/utils/resourceMapping.js +210 -0
  79. package/dist/utils/singleResourceDownload.d.ts +14 -0
  80. package/dist/utils/singleResourceDownload.js +261 -0
  81. package/dist/utils/summaryGenerator.d.ts +2 -0
  82. package/dist/utils/summaryGenerator.js +183 -0
  83. package/dist/utils/uploadHandler.d.ts +31 -0
  84. package/dist/utils/uploadHandler.js +263 -0
  85. package/docs/AI_API.md +93 -0
  86. package/docs/CLAUDE.md +216 -0
  87. package/docs/PROJECT_SPECIFIC.md +1 -0
  88. package/docs/RECORD_COMMAND.md +262 -0
  89. package/docs/WORKFLOW_API.md +480 -0
  90. package/docs/bfy-splitting.md +126 -0
  91. package/docs/cli-commands.md +333 -0
  92. package/docs/examples/README.md +95 -0
  93. package/docs/examples/order-system.md +147 -0
  94. package/docs/examples/product-catalog.md +195 -0
  95. package/docs/examples/reports.md +187 -0
  96. package/docs/excel-export.md +216 -0
  97. package/docs/field-types/README.md +29 -0
  98. package/docs/field-types/calculated.md +147 -0
  99. package/docs/field-types/code-mappings.md +84 -0
  100. package/docs/field-types/custom.md +340 -0
  101. package/docs/object-specs/README.md +136 -0
  102. package/docs/object-specs/code-parameters.md +151 -0
  103. package/docs/object-specs/creating.md +203 -0
  104. package/docs/object-specs/js-code-examples.md +208 -0
  105. package/docs/object-specs/js-field-updates.md +168 -0
  106. package/docs/objects/README.md +89 -0
  107. package/docs/objects/creating.md +127 -0
  108. package/docs/page-layout.md +361 -0
  109. package/docs/permissions.md +260 -0
  110. package/docs/reports.md +197 -0
  111. package/docs/state-machines.md +544 -0
  112. package/docs/tasks/create-object.md +81 -0
  113. package/docs/translations.md +346 -0
  114. package/docs/twig-helpers.md +283 -0
  115. package/docs/webservices.md +159 -0
  116. package/docs/workspaces.md +176 -0
  117. 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`