@netoalmanca/advpl-sensei 1.1.0

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 (70) hide show
  1. package/agents/changelog-generator.md +63 -0
  2. package/agents/code-generator.md +215 -0
  3. package/agents/code-reviewer.md +145 -0
  4. package/agents/debugger.md +83 -0
  5. package/agents/doc-generator.md +67 -0
  6. package/agents/docs-reference.md +86 -0
  7. package/agents/migrator.md +84 -0
  8. package/agents/process-consultant.md +97 -0
  9. package/agents/refactorer.md +75 -0
  10. package/agents/sx-configurator.md +67 -0
  11. package/commands/changelog.md +66 -0
  12. package/commands/diagnose.md +67 -0
  13. package/commands/docs.md +81 -0
  14. package/commands/document.md +67 -0
  15. package/commands/explain.md +60 -0
  16. package/commands/generate.md +111 -0
  17. package/commands/migrate.md +81 -0
  18. package/commands/process.md +111 -0
  19. package/commands/refactor.md +65 -0
  20. package/commands/review.md +60 -0
  21. package/commands/sxgen.md +98 -0
  22. package/commands/test.md +103 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +143 -0
  26. package/dist/index.js.map +1 -0
  27. package/package.json +30 -0
  28. package/skills/advpl-code-generation/SKILL.md +163 -0
  29. package/skills/advpl-code-generation/patterns-fwformbrowse.md +485 -0
  30. package/skills/advpl-code-generation/patterns-jobs.md +519 -0
  31. package/skills/advpl-code-generation/patterns-mvc.md +765 -0
  32. package/skills/advpl-code-generation/patterns-pontos-entrada.md +708 -0
  33. package/skills/advpl-code-generation/patterns-rest.md +974 -0
  34. package/skills/advpl-code-generation/patterns-soap.md +639 -0
  35. package/skills/advpl-code-generation/patterns-treport.md +481 -0
  36. package/skills/advpl-code-generation/patterns-workflow.md +779 -0
  37. package/skills/advpl-code-generation/templates-classes.md +1096 -0
  38. package/skills/advpl-code-review/SKILL.md +72 -0
  39. package/skills/advpl-code-review/rules-best-practices.md +444 -0
  40. package/skills/advpl-code-review/rules-modernization.md +290 -0
  41. package/skills/advpl-code-review/rules-performance.md +333 -0
  42. package/skills/advpl-code-review/rules-security.md +302 -0
  43. package/skills/advpl-debugging/SKILL.md +265 -0
  44. package/skills/advpl-debugging/common-errors.md +1124 -0
  45. package/skills/advpl-debugging/performance-tips.md +768 -0
  46. package/skills/advpl-refactoring/SKILL.md +139 -0
  47. package/skills/advpl-to-tlpp-migration/SKILL.md +293 -0
  48. package/skills/advpl-to-tlpp-migration/migration-checklist.md +122 -0
  49. package/skills/advpl-to-tlpp-migration/migration-rules.md +265 -0
  50. package/skills/changelog-patterns/SKILL.md +99 -0
  51. package/skills/code-explanation/SKILL.md +66 -0
  52. package/skills/documentation-patterns/SKILL.md +172 -0
  53. package/skills/embedded-sql/SKILL.md +379 -0
  54. package/skills/probat-testing/SKILL.md +226 -0
  55. package/skills/probat-testing/patterns-unit-tests.md +614 -0
  56. package/skills/protheus-business/SKILL.md +92 -0
  57. package/skills/protheus-business/modulo-compras.md +780 -0
  58. package/skills/protheus-business/modulo-contabilidade.md +874 -0
  59. package/skills/protheus-business/modulo-estoque.md +876 -0
  60. package/skills/protheus-business/modulo-faturamento.md +800 -0
  61. package/skills/protheus-business/modulo-financeiro.md +1015 -0
  62. package/skills/protheus-business/modulo-fiscal.md +749 -0
  63. package/skills/protheus-business/modulo-manutencao.md +848 -0
  64. package/skills/protheus-business/modulo-pcp.md +743 -0
  65. package/skills/protheus-reference/SKILL.md +119 -0
  66. package/skills/protheus-reference/native-functions.md +7029 -0
  67. package/skills/protheus-reference/rest-api-reference.md +1758 -0
  68. package/skills/protheus-reference/restricted-functions.md +265 -0
  69. package/skills/protheus-reference/sx-dictionary.md +854 -0
  70. package/skills/sx-configuration/SKILL.md +184 -0
@@ -0,0 +1,765 @@
1
+ # Protheus MVC Patterns
2
+
3
+ Complete reference for implementing Model-View-Controller structures in TOTVS Protheus.
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ The MVC pattern in Protheus is **not** the same as web-based MVC (such as Laravel or Rails). In Protheus, MVC is a framework provided by the TOTVS library (`FWFormModel`, `FWFormView`) that separates:
10
+
11
+ - **Model** (`ModelDef`): Business rules, field validations, data structure, triggers, and database persistence.
12
+ - **View** (`ViewDef`): Screen layout, panels, grids, and visual components. The View never accesses the database directly.
13
+ - **MenuDef**: Defines the browsable interface and the available operations (include, edit, delete, view, print, copy).
14
+
15
+ Key characteristics:
16
+ - The Model is completely independent of the View. You can execute the Model programmatically without any screen (via `FWMVCRotAuto`).
17
+ - The View always references a Model. It never contains business logic.
18
+ - Protheus MVC enforces data integrity through pre/post validation blocks, triggers, and commit/cancel blocks.
19
+ - All three definitions (`MenuDef`, `ModelDef`, `ViewDef`) are exported as `Static Function` and discovered by the framework at runtime.
20
+
21
+ Required includes:
22
+ ```advpl
23
+ #Include "TOTVS.CH"
24
+ #Include "FWMVCDef.ch"
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 2. MenuDef
30
+
31
+ The `MenuDef` function defines the browse grid and the toolbar operations. It returns an array of `MENUDEF_STR` items.
32
+
33
+ ```advpl
34
+ /*/{Protheus.doc} MenuDef
35
+ Definicao do menu MVC
36
+ @type Static Function
37
+ @author Autor
38
+ @since 01/01/2026
39
+ @version 1.0
40
+ /*/
41
+ Static Function MenuDef()
42
+ Local aRotina := {}
43
+
44
+ ADD OPTION aRotina TITLE "Visualizar" ACTION "VIEWDEF.MODEXEMPLO" OPERATION MODEL_OPERATION_VIEW ACCESS 0
45
+ ADD OPTION aRotina TITLE "Incluir" ACTION "VIEWDEF.MODEXEMPLO" OPERATION MODEL_OPERATION_INSERT ACCESS 0
46
+ ADD OPTION aRotina TITLE "Alterar" ACTION "VIEWDEF.MODEXEMPLO" OPERATION MODEL_OPERATION_UPDATE ACCESS 0
47
+ ADD OPTION aRotina TITLE "Excluir" ACTION "VIEWDEF.MODEXEMPLO" OPERATION MODEL_OPERATION_DELETE ACCESS 0
48
+ ADD OPTION aRotina TITLE "Imprimir" ACTION "VIEWDEF.MODEXEMPLO" OPERATION MODEL_OPERATION_PRINT ACCESS 0
49
+ ADD OPTION aRotina TITLE "Copiar" ACTION "VIEWDEF.MODEXEMPLO" OPERATION MODEL_OPERATION_COPY ACCESS 0
50
+
51
+ Return aRotina
52
+ ```
53
+
54
+ **Operation constants** (from `FWMVCDef.ch`):
55
+ | Constant | Value | Description |
56
+ |----------|-------|-------------|
57
+ | `MODEL_OPERATION_VIEW` | 1 | View record (read-only) |
58
+ | `MODEL_OPERATION_INSERT` | 3 | Include new record |
59
+ | `MODEL_OPERATION_UPDATE` | 4 | Alter existing record |
60
+ | `MODEL_OPERATION_DELETE` | 5 | Delete record |
61
+ | `MODEL_OPERATION_PRINT` | 8 | Print record |
62
+ | `MODEL_OPERATION_COPY` | 9 | Copy record |
63
+
64
+ ---
65
+
66
+ ## 3. ModelDef
67
+
68
+ The `ModelDef` function creates the business model using `FWFormModel`. It defines tables, fields, relationships, validations, and persistence logic.
69
+
70
+ ### 3.1 Basic Structure
71
+
72
+ ```advpl
73
+ /*/{Protheus.doc} ModelDef
74
+ Definicao do modelo de dados MVC
75
+ @type Static Function
76
+ @author Autor
77
+ @since 01/01/2026
78
+ @version 1.0
79
+ /*/
80
+ Static Function ModelDef()
81
+ Local oModel := Nil
82
+ Local oStMaster := FWFormStruct(1, "SA1") // Master structure
83
+ Local oStDetail := FWFormStruct(1, "SA2") // Detail structure
84
+
85
+ oModel := MPFormModel():New("MODEXEMPLO", /*bPreValid*/, /*bPosValid*/, /*bCommit*/, /*bCancel*/)
86
+ oModel:SetDescription("Cadastro de Clientes com Enderecos")
87
+
88
+ // Master (header) fields
89
+ oModel:AddFields("SA1MASTER", /*owner*/, oStMaster, /*bPreValid*/, /*bPosValid*/, /*bLoad*/)
90
+ oModel:SetPrimaryKey({"A1_FILIAL", "A1_COD", "A1_LOJA"})
91
+
92
+ // Detail (grid) fields
93
+ oModel:AddGrid("SA2DETAIL", "SA1MASTER", oStDetail, /*bLinePre*/, /*bLinePost*/, /*bPreValid*/, /*bPosValid*/, /*bLoad*/)
94
+ oModel:SetRelation("SA2DETAIL", {{"A2_FILIAL", "xFilial('SA2')"}, {"A2_COD", "A1_COD"}, {"A2_LOJA", "A1_LOJA"}}, SA2->(IndexKey(1)))
95
+
96
+ // Primary key for the grid
97
+ oModel:GetModel("SA2DETAIL"):SetUniqueLine({"A2_ITEM"})
98
+
99
+ Return oModel
100
+ ```
101
+
102
+ ### 3.2 AddFields (Master/Header)
103
+
104
+ ```advpl
105
+ oModel:AddFields(cId, cOwner, oStruct, bPreValid, bPosValid, bLoad)
106
+ ```
107
+
108
+ | Parameter | Type | Description |
109
+ |-----------|------|-------------|
110
+ | `cId` | Character | Unique identifier for the field group |
111
+ | `cOwner` | Character | Owner model ID (Nil for master) |
112
+ | `oStruct` | Object | `FWFormStruct` object for the table |
113
+ | `bPreValid` | Block | Pre-validation block: `{|oModel| lValid}` |
114
+ | `bPosValid` | Block | Post-validation block: `{|oModel| lValid}` |
115
+ | `bLoad` | Block | Custom data load block |
116
+
117
+ ### 3.3 AddGrid (Detail/Items)
118
+
119
+ ```advpl
120
+ oModel:AddGrid(cId, cOwner, oStruct, bLinePre, bLinePost, bPreValid, bPosValid, bLoad)
121
+ ```
122
+
123
+ | Parameter | Type | Description |
124
+ |-----------|------|-------------|
125
+ | `cId` | Character | Unique identifier for the grid |
126
+ | `cOwner` | Character | Owner model ID (the master ID) |
127
+ | `oStruct` | Object | `FWFormStruct` object for the table |
128
+ | `bLinePre` | Block | Line pre-validation: `{|oGridModel, nLine, cAction, cField| lValid}` |
129
+ | `bLinePost` | Block | Line post-validation: `{|oGridModel, nLine| lValid}` |
130
+ | `bPreValid` | Block | Grid pre-validation: `{|oModel| lValid}` |
131
+ | `bPosValid` | Block | Grid post-validation: `{|oModel| lValid}` |
132
+ | `bLoad` | Block | Custom data load block |
133
+
134
+ ### 3.4 SetPrimaryKey
135
+
136
+ Defines which fields compose the primary key for the master.
137
+
138
+ ```advpl
139
+ oModel:SetPrimaryKey({"A1_FILIAL", "A1_COD", "A1_LOJA"})
140
+ ```
141
+
142
+ ### 3.5 SetDescription
143
+
144
+ ```advpl
145
+ oModel:SetDescription("Descricao do modelo")
146
+ ```
147
+
148
+ ### 3.6 Triggers
149
+
150
+ Use `FWFormStruct:AddTrigger` to define field triggers that auto-fill other fields when a field value changes.
151
+
152
+ ```advpl
153
+ oStMaster:AddTrigger( ;
154
+ "A1_CEP", ; // Field that triggers
155
+ "A1_MUN", ; // Field to be filled
156
+ {|| .T.}, ; // Condition block
157
+ {|| Posicione("SYR", 1, xFilial("SYR") + M->A1_CEP, "YR_MUN")} ; // Value block
158
+ )
159
+ ```
160
+
161
+ ### 3.7 Pre and Post Validation Blocks
162
+
163
+ **Pre-validation** executes before the data is committed (useful for confirming with the user):
164
+ ```advpl
165
+ oModel := MPFormModel():New("MODEXEMPLO", ;
166
+ {|oModel| PreValidacao(oModel)}, ; // bPreValid
167
+ {|oModel| PosValidacao(oModel)}, ; // bPosValid
168
+ {|oModel| Comitar(oModel)}, ; // bCommit
169
+ {|oModel| Cancelar(oModel)} ; // bCancel
170
+ )
171
+
172
+ Static Function PreValidacao(oModel)
173
+ Local lRet := .T.
174
+ // Validacoes antes do commit
175
+ Return lRet
176
+
177
+ Static Function PosValidacao(oModel)
178
+ Local lRet := .T.
179
+ // Validacoes apos preenchimento
180
+ Return lRet
181
+ ```
182
+
183
+ ### 3.8 Commit and Cancel Blocks
184
+
185
+ **Commit block** (custom persistence logic, replaces default FWFormCommit):
186
+ ```advpl
187
+ Static Function Comitar(oModel)
188
+ FWFormCommit(oModel) // Gravacao padrao
189
+ // Logica adicional pos-gravacao
190
+ Return Nil
191
+
192
+ Static Function Cancelar(oModel)
193
+ FWFormCancel(oModel) // Cancelamento padrao
194
+ Return Nil
195
+ ```
196
+
197
+ ### 3.9 Complete ModelDef Example (SA1 Master / SA2 Detail)
198
+
199
+ ```advpl
200
+ Static Function ModelDef()
201
+ Local oModel := Nil
202
+ Local oStSA1 := FWFormStruct(1, "SA1")
203
+ Local oStSA2 := FWFormStruct(1, "SA2")
204
+
205
+ // Remove campos que nao serao usados
206
+ oStSA1:RemoveField("A1_ZORDER")
207
+
208
+ // Cria modelo
209
+ oModel := MPFormModel():New("MODEXEMPLO", ;
210
+ {|oMdl| PreValidModel(oMdl)}, ;
211
+ {|oMdl| PosValidModel(oMdl)}, ;
212
+ {|oMdl| CommitModel(oMdl)}, ;
213
+ {|oMdl| CancelModel(oMdl)} ;
214
+ )
215
+ oModel:SetDescription("Cadastro de Clientes com Enderecos")
216
+
217
+ // Master - Cabecalho (SA1)
218
+ oModel:AddFields("SA1MASTER", /*cOwner*/, oStSA1)
219
+ oModel:SetPrimaryKey({"A1_FILIAL", "A1_COD", "A1_LOJA"})
220
+
221
+ // Detail - Grid de enderecos (SA2)
222
+ oModel:AddGrid("SA2DETAIL", "SA1MASTER", oStSA2, ;
223
+ /*bLinePre*/, ;
224
+ {|oGridMdl, nLine| LinePostSA2(oGridMdl, nLine)}, ;
225
+ /*bPreValid*/, ;
226
+ /*bPosValid*/ ;
227
+ )
228
+
229
+ oModel:SetRelation("SA2DETAIL", ;
230
+ {{"A2_FILIAL", "xFilial('SA2')"}, {"A2_COD", "A1_COD"}, {"A2_LOJA", "A1_LOJA"}}, ;
231
+ SA2->(IndexKey(1)) ;
232
+ )
233
+ oModel:GetModel("SA2DETAIL"):SetUniqueLine({"A2_ITEM"})
234
+
235
+ Return oModel
236
+
237
+ Static Function PreValidModel(oModel)
238
+ Local lRet := .T.
239
+ Local oMdlSA1 := oModel:GetModel("SA1MASTER")
240
+ Local cNome := oMdlSA1:GetValue("A1_NOME")
241
+
242
+ If Empty(cNome)
243
+ Help(Nil, Nil, "PREVAL", Nil, "Nome do cliente e obrigatorio.", 1, 0)
244
+ lRet := .F.
245
+ EndIf
246
+
247
+ Return lRet
248
+
249
+ Static Function PosValidModel(oModel)
250
+ Return .T.
251
+
252
+ Static Function CommitModel(oModel)
253
+ FWFormCommit(oModel)
254
+ Return Nil
255
+
256
+ Static Function CancelModel(oModel)
257
+ FWFormCancel(oModel)
258
+ Return Nil
259
+
260
+ Static Function LinePostSA2(oGridModel, nLine)
261
+ Local lRet := .T.
262
+ Local cEnder := oGridModel:GetValue("A2_ENDER")
263
+
264
+ If Empty(cEnder)
265
+ Help(Nil, Nil, "LINEPOST", Nil, "Endereco e obrigatorio.", 1, 0)
266
+ lRet := .F.
267
+ EndIf
268
+
269
+ Return lRet
270
+ ```
271
+
272
+ ---
273
+
274
+ ## 4. ViewDef
275
+
276
+ The `ViewDef` function creates the visual layout using `FWFormView`. It references the Model and arranges fields into panels, boxes, and grids.
277
+
278
+ ### 4.1 Basic Structure
279
+
280
+ ```advpl
281
+ /*/{Protheus.doc} ViewDef
282
+ Definicao da interface visual MVC
283
+ @type Static Function
284
+ @author Autor
285
+ @since 01/01/2026
286
+ @version 1.0
287
+ /*/
288
+ Static Function ViewDef()
289
+ Local oView := Nil
290
+ Local oModel := FWLoadModel("MODEXEMPLO")
291
+ Local oStSA1 := FWFormStruct(2, "SA1")
292
+ Local oStSA2 := FWFormStruct(2, "SA2")
293
+
294
+ oView := FWFormView():New()
295
+ oView:SetModel(oModel)
296
+
297
+ // Header panel
298
+ oView:AddField("VIEW_SA1", oStSA1, "SA1MASTER")
299
+
300
+ // Detail grid
301
+ oView:AddGrid("VIEW_SA2", oStSA2, "SA2DETAIL")
302
+
303
+ // Layout boxes
304
+ oView:CreateHorizontalBox("SUPERIOR", 50) // 50% da tela
305
+ oView:CreateHorizontalBox("INFERIOR", 50) // 50% da tela
306
+
307
+ // Bind views to boxes
308
+ oView:SetOwnerView("VIEW_SA1", "SUPERIOR")
309
+ oView:SetOwnerView("VIEW_SA2", "INFERIOR")
310
+
311
+ // Titles
312
+ oView:EnableTitleView("VIEW_SA1", "Dados do Cliente")
313
+ oView:EnableTitleView("VIEW_SA2", "Enderecos")
314
+
315
+ Return oView
316
+ ```
317
+
318
+ ### 4.2 Key Methods
319
+
320
+ | Method | Description |
321
+ |--------|-------------|
322
+ | `SetModel(oModel)` | Binds the view to a model |
323
+ | `AddField(cId, oStruct, cModelId)` | Adds a field panel linked to a model group |
324
+ | `AddGrid(cId, oStruct, cModelId)` | Adds a grid panel linked to a model grid |
325
+ | `CreateHorizontalBox(cId, nPercentual)` | Creates a horizontal box occupying N% of space |
326
+ | `CreateVerticalBox(cId, nPercentual)` | Creates a vertical box occupying N% of space |
327
+ | `SetOwnerView(cViewId, cBoxId)` | Places a view element inside a box |
328
+ | `EnableTitleView(cViewId, cTitle)` | Shows a title bar above the view element |
329
+
330
+ ### 4.3 FWFormStruct for View
331
+
332
+ When creating `FWFormStruct` for the View, use parameter `2` (view mode) instead of `1` (model mode):
333
+
334
+ ```advpl
335
+ Local oStView := FWFormStruct(2, "SA1") // 2 = View structure
336
+ Local oStModel := FWFormStruct(1, "SA1") // 1 = Model structure
337
+ ```
338
+
339
+ ### 4.4 Advanced Layout with Nested Boxes
340
+
341
+ ```advpl
342
+ // Three-panel layout
343
+ oView:CreateHorizontalBox("TOP", 30)
344
+ oView:CreateHorizontalBox("MIDDLE", 40)
345
+ oView:CreateHorizontalBox("BOTTOM", 30)
346
+
347
+ // Split top into two vertical columns
348
+ oView:CreateVerticalBox("TOP_LEFT", 50, "TOP")
349
+ oView:CreateVerticalBox("TOP_RIGHT", 50, "TOP")
350
+
351
+ oView:SetOwnerView("VIEW_DADOS", "TOP_LEFT")
352
+ oView:SetOwnerView("VIEW_CONTATO", "TOP_RIGHT")
353
+ oView:SetOwnerView("VIEW_ITENS", "MIDDLE")
354
+ oView:SetOwnerView("VIEW_OBS", "BOTTOM")
355
+ ```
356
+
357
+ ### 4.5 Complete ViewDef Example (matching the ModelDef above)
358
+
359
+ ```advpl
360
+ Static Function ViewDef()
361
+ Local oView := Nil
362
+ Local oModel := FWLoadModel("MODEXEMPLO")
363
+ Local oStSA1 := FWFormStruct(2, "SA1")
364
+ Local oStSA2 := FWFormStruct(2, "SA2")
365
+
366
+ oView := FWFormView():New()
367
+ oView:SetModel(oModel)
368
+
369
+ oView:AddField("VIEW_SA1", oStSA1, "SA1MASTER")
370
+ oView:AddGrid("VIEW_SA2", oStSA2, "SA2DETAIL")
371
+
372
+ oView:CreateHorizontalBox("BOXMASTER", 40)
373
+ oView:CreateHorizontalBox("BOXDETAIL", 60)
374
+
375
+ oView:SetOwnerView("VIEW_SA1", "BOXMASTER")
376
+ oView:SetOwnerView("VIEW_SA2", "BOXDETAIL")
377
+
378
+ oView:EnableTitleView("VIEW_SA1", "Dados do Cliente")
379
+ oView:EnableTitleView("VIEW_SA2", "Enderecos do Cliente")
380
+
381
+ Return oView
382
+ ```
383
+
384
+ ---
385
+
386
+ ## 5. Complete Example - Master-Detail CRUD (ZA1/ZA2)
387
+
388
+ Full working MVC for a custom orders (ZA1) and order items (ZA2) structure.
389
+
390
+ ```advpl
391
+ #Include "TOTVS.CH"
392
+ #Include "FWMVCDef.ch"
393
+
394
+ /*/{Protheus.doc} FATA090
395
+ Cadastro de Pedidos customizado com MVC
396
+ @type User Function
397
+ @author Autor
398
+ @since 01/01/2026
399
+ @version 1.0
400
+ /*/
401
+ User Function FATA090()
402
+ Local oBrowse := Nil
403
+
404
+ oBrowse := FWMBrowse():New()
405
+ oBrowse:SetAlias("ZA1")
406
+ oBrowse:SetDescription("Cadastro de Pedidos")
407
+ oBrowse:Activate()
408
+
409
+ Return Nil
410
+
411
+ // =============================================================
412
+ // MenuDef
413
+ // =============================================================
414
+ Static Function MenuDef()
415
+ Local aRotina := {}
416
+
417
+ ADD OPTION aRotina TITLE "Visualizar" ACTION "VIEWDEF.FATA090" OPERATION MODEL_OPERATION_VIEW ACCESS 0
418
+ ADD OPTION aRotina TITLE "Incluir" ACTION "VIEWDEF.FATA090" OPERATION MODEL_OPERATION_INSERT ACCESS 0
419
+ ADD OPTION aRotina TITLE "Alterar" ACTION "VIEWDEF.FATA090" OPERATION MODEL_OPERATION_UPDATE ACCESS 0
420
+ ADD OPTION aRotina TITLE "Excluir" ACTION "VIEWDEF.FATA090" OPERATION MODEL_OPERATION_DELETE ACCESS 0
421
+ ADD OPTION aRotina TITLE "Imprimir" ACTION "VIEWDEF.FATA090" OPERATION MODEL_OPERATION_PRINT ACCESS 0
422
+ ADD OPTION aRotina TITLE "Copiar" ACTION "VIEWDEF.FATA090" OPERATION MODEL_OPERATION_COPY ACCESS 0
423
+
424
+ Return aRotina
425
+
426
+ // =============================================================
427
+ // ModelDef
428
+ // =============================================================
429
+ Static Function ModelDef()
430
+ Local oModel := Nil
431
+ Local oStZA1 := FWFormStruct(1, "ZA1")
432
+ Local oStZA2 := FWFormStruct(1, "ZA2")
433
+
434
+ // Trigger: ao preencher produto, buscar descricao e preco
435
+ oStZA2:AddTrigger( ;
436
+ "ZA2_PROD", ;
437
+ "ZA2_DESC", ;
438
+ {|| .T.}, ;
439
+ {|| Posicione("SB1", 1, xFilial("SB1") + M->ZA2_PROD, "B1_DESC")} ;
440
+ )
441
+ oStZA2:AddTrigger( ;
442
+ "ZA2_PROD", ;
443
+ "ZA2_PRCUNI", ;
444
+ {|| .T.}, ;
445
+ {|| Posicione("SB1", 1, xFilial("SB1") + M->ZA2_PROD, "B1_PRV1")} ;
446
+ )
447
+
448
+ // Cria modelo
449
+ oModel := MPFormModel():New("FATA090", ;
450
+ {|oMdl| PreValidPed(oMdl)}, ;
451
+ {|oMdl| PosValidPed(oMdl)}, ;
452
+ {|oMdl| CommitPed(oMdl)}, ;
453
+ {|oMdl| CancelPed(oMdl)} ;
454
+ )
455
+ oModel:SetDescription("Cadastro de Pedidos")
456
+
457
+ // Master - Cabecalho do pedido (ZA1)
458
+ oModel:AddFields("ZA1MASTER", /*cOwner*/, oStZA1)
459
+ oModel:SetPrimaryKey({"ZA1_FILIAL", "ZA1_NUM"})
460
+
461
+ // Detail - Itens do pedido (ZA2)
462
+ oModel:AddGrid("ZA2DETAIL", "ZA1MASTER", oStZA2, ;
463
+ /*bLinePre*/, ;
464
+ {|oGridMdl, nLine| LinePostZA2(oGridMdl, nLine)} ;
465
+ )
466
+
467
+ oModel:SetRelation("ZA2DETAIL", ;
468
+ {{"ZA2_FILIAL", "xFilial('ZA2')"}, {"ZA2_NUM", "ZA1_NUM"}}, ;
469
+ ZA2->(IndexKey(1)) ;
470
+ )
471
+ oModel:GetModel("ZA2DETAIL"):SetUniqueLine({"ZA2_ITEM"})
472
+
473
+ Return oModel
474
+
475
+ // Pre-validacao geral do modelo
476
+ Static Function PreValidPed(oModel)
477
+ Local lRet := .T.
478
+ Local oMaster := oModel:GetModel("ZA1MASTER")
479
+ Local cCliente := oMaster:GetValue("ZA1_CLIENT")
480
+
481
+ If Empty(cCliente)
482
+ Help(Nil, Nil, "PEDVAL", Nil, "Cliente e obrigatorio.", 1, 0)
483
+ lRet := .F.
484
+ EndIf
485
+
486
+ Return lRet
487
+
488
+ // Pos-validacao geral do modelo
489
+ Static Function PosValidPed(oModel)
490
+ Local lRet := .T.
491
+ Local oGrid := oModel:GetModel("ZA2DETAIL")
492
+ Local nI := 0
493
+ Local nTotal := 0
494
+
495
+ // Verifica se existe pelo menos um item
496
+ For nI := 1 To oGrid:Length()
497
+ oGrid:GoLine(nI)
498
+ If !oGrid:IsDeleted()
499
+ nTotal += oGrid:GetValue("ZA2_TOTAL")
500
+ EndIf
501
+ Next nI
502
+
503
+ If nTotal <= 0
504
+ Help(Nil, Nil, "PEDVAL", Nil, "O pedido deve ter pelo menos um item com valor.", 1, 0)
505
+ lRet := .F.
506
+ EndIf
507
+
508
+ Return lRet
509
+
510
+ // Commit customizado
511
+ Static Function CommitPed(oModel)
512
+ FWFormCommit(oModel)
513
+
514
+ // Logica adicional apos gravacao
515
+ Local oMaster := oModel:GetModel("ZA1MASTER")
516
+ Local cNumPed := oMaster:GetValue("ZA1_NUM")
517
+ Conout("Pedido gravado: " + cNumPed)
518
+
519
+ Return Nil
520
+
521
+ // Cancel
522
+ Static Function CancelPed(oModel)
523
+ FWFormCancel(oModel)
524
+ Return Nil
525
+
526
+ // Validacao de linha do grid
527
+ Static Function LinePostZA2(oGridModel, nLine)
528
+ Local lRet := .T.
529
+ Local nQuant := oGridModel:GetValue("ZA2_QUANT")
530
+ Local nPrcUn := oGridModel:GetValue("ZA2_PRCUNI")
531
+
532
+ If nQuant <= 0
533
+ Help(Nil, Nil, "ITEMVAL", Nil, "Quantidade deve ser maior que zero.", 1, 0)
534
+ lRet := .F.
535
+ EndIf
536
+
537
+ If nPrcUn <= 0
538
+ Help(Nil, Nil, "ITEMVAL", Nil, "Preco unitario deve ser maior que zero.", 1, 0)
539
+ lRet := .F.
540
+ EndIf
541
+
542
+ // Calcula total da linha
543
+ If lRet
544
+ oGridModel:SetValue("ZA2_TOTAL", nQuant * nPrcUn)
545
+ EndIf
546
+
547
+ Return lRet
548
+
549
+ // =============================================================
550
+ // ViewDef
551
+ // =============================================================
552
+ Static Function ViewDef()
553
+ Local oView := Nil
554
+ Local oModel := FWLoadModel("FATA090")
555
+ Local oStZA1 := FWFormStruct(2, "ZA1")
556
+ Local oStZA2 := FWFormStruct(2, "ZA2")
557
+
558
+ oView := FWFormView():New()
559
+ oView:SetModel(oModel)
560
+
561
+ oView:AddField("VIEW_ZA1", oStZA1, "ZA1MASTER")
562
+ oView:AddGrid("VIEW_ZA2", oStZA2, "ZA2DETAIL")
563
+
564
+ oView:CreateHorizontalBox("BOXCAB", 35)
565
+ oView:CreateHorizontalBox("BOXITEM", 65)
566
+
567
+ oView:SetOwnerView("VIEW_ZA1", "BOXCAB")
568
+ oView:SetOwnerView("VIEW_ZA2", "BOXITEM")
569
+
570
+ oView:EnableTitleView("VIEW_ZA1", "Cabecalho do Pedido")
571
+ oView:EnableTitleView("VIEW_ZA2", "Itens do Pedido")
572
+
573
+ Return oView
574
+ ```
575
+
576
+ ---
577
+
578
+ ## 6. FWMVCRotAuto - Batch/Programmatic Execution
579
+
580
+ `FWMVCRotAuto` allows you to execute MVC operations programmatically, without user interface. This is essential for integrations, batch processing, and automated data entry.
581
+
582
+ ### 6.1 Include Operation
583
+
584
+ ```advpl
585
+ #Include "TOTVS.CH"
586
+ #Include "FWMVCDef.ch"
587
+
588
+ User Function IncPedAuto()
589
+ Local aHeader := {}
590
+ Local aItems := {}
591
+ Local aLine := {}
592
+ Local oModel := Nil
593
+ Local lRet := .F.
594
+
595
+ // Cabecalho (master fields)
596
+ aAdd(aHeader, {"ZA1_NUM", "000001", Nil})
597
+ aAdd(aHeader, {"ZA1_CLIENT", "000001", Nil})
598
+ aAdd(aHeader, {"ZA1_LOJA", "01", Nil})
599
+ aAdd(aHeader, {"ZA1_EMISSA", Date(), Nil})
600
+
601
+ // Item 1
602
+ aLine := {}
603
+ aAdd(aLine, {"ZA2_ITEM", "01", Nil})
604
+ aAdd(aLine, {"ZA2_PROD", "000001", Nil})
605
+ aAdd(aLine, {"ZA2_QUANT", 10, Nil})
606
+ aAdd(aLine, {"ZA2_PRCUNI", 25.50, Nil})
607
+ aAdd(aItems, aLine)
608
+
609
+ // Item 2
610
+ aLine := {}
611
+ aAdd(aLine, {"ZA2_ITEM", "02", Nil})
612
+ aAdd(aLine, {"ZA2_PROD", "000002", Nil})
613
+ aAdd(aLine, {"ZA2_QUANT", 5, Nil})
614
+ aAdd(aLine, {"ZA2_PRCUNI", 100.00, Nil})
615
+ aAdd(aItems, aLine)
616
+
617
+ // Executa inclusao
618
+ lRet := FWMVCRotAuto(oModel, "FATA090", MODEL_OPERATION_INSERT, {;
619
+ {"ZA1MASTER", aHeader}, ;
620
+ {"ZA2DETAIL", aItems} ;
621
+ })
622
+
623
+ If lRet
624
+ Conout("Pedido incluido com sucesso!")
625
+ Else
626
+ Conout("Erro na inclusao do pedido.")
627
+ // Recuperar mensagens de erro
628
+ AutoGrLog("Erro na inclusao automatica")
629
+ MostraErro()
630
+ EndIf
631
+
632
+ Return lRet
633
+ ```
634
+
635
+ ### 6.2 Update Operation
636
+
637
+ ```advpl
638
+ User Function AltPedAuto()
639
+ Local aHeader := {}
640
+ Local lRet := .F.
641
+ Local oModel := Nil
642
+
643
+ // Posiciona no registro a ser alterado
644
+ DbSelectArea("ZA1")
645
+ DbSetOrder(1)
646
+ DbSeek(xFilial("ZA1") + "000001")
647
+
648
+ // Campos a alterar
649
+ aAdd(aHeader, {"ZA1_CLIENT", "000002", Nil})
650
+ aAdd(aHeader, {"ZA1_LOJA", "01", Nil})
651
+
652
+ lRet := FWMVCRotAuto(oModel, "FATA090", MODEL_OPERATION_UPDATE, {;
653
+ {"ZA1MASTER", aHeader} ;
654
+ })
655
+
656
+ If lRet
657
+ Conout("Pedido alterado com sucesso!")
658
+ Else
659
+ Conout("Erro na alteracao.")
660
+ MostraErro()
661
+ EndIf
662
+
663
+ Return lRet
664
+ ```
665
+
666
+ ### 6.3 Delete Operation
667
+
668
+ ```advpl
669
+ User Function DelPedAuto()
670
+ Local lRet := .F.
671
+ Local oModel := Nil
672
+
673
+ // Posiciona no registro a ser excluido
674
+ DbSelectArea("ZA1")
675
+ DbSetOrder(1)
676
+ DbSeek(xFilial("ZA1") + "000001")
677
+
678
+ lRet := FWMVCRotAuto(oModel, "FATA090", MODEL_OPERATION_DELETE)
679
+
680
+ If lRet
681
+ Conout("Pedido excluido com sucesso!")
682
+ Else
683
+ Conout("Erro na exclusao.")
684
+ MostraErro()
685
+ EndIf
686
+
687
+ Return lRet
688
+ ```
689
+
690
+ ### 6.4 Parameter Array Structure
691
+
692
+ The parameter array for `FWMVCRotAuto` follows this structure:
693
+
694
+ ```
695
+ {
696
+ { cModelId, aFieldValues }, // Master
697
+ { cGridId, aGridLines } // Detail (optional)
698
+ }
699
+ ```
700
+
701
+ Where each field value is:
702
+ ```
703
+ { cFieldName, xValue, cLookupKey }
704
+ ```
705
+
706
+ | Element | Type | Description |
707
+ |---------|------|-------------|
708
+ | `cFieldName` | Character | SX3 field name |
709
+ | `xValue` | Any | Value to set |
710
+ | `cLookupKey` | Character/Nil | Nil for direct value, or lookup key |
711
+
712
+ ### 6.5 Error Handling in RotAuto
713
+
714
+ ```advpl
715
+ Private lMsErroAuto := .F.
716
+ Private lMsHelpAuto := .T.
717
+ Private lAutoErrNoFile := .T.
718
+
719
+ lRet := FWMVCRotAuto(oModel, "FATA090", MODEL_OPERATION_INSERT, aParams)
720
+
721
+ If lMsErroAuto
722
+ // Erros ocorreram
723
+ Local cErro := MostraErro(.F.) // .F. para retornar string em vez de exibir dialog
724
+ Conout("Erros: " + cErro)
725
+ EndIf
726
+ ```
727
+
728
+ ---
729
+
730
+ ## 7. Legacy AxCadastro - Comparison
731
+
732
+ Before MVC, Protheus used `AxCadastro` (also known as Modelo1, Modelo2, Modelo3) for CRUD screens.
733
+
734
+ | Feature | MVC (FWFormModel) | Legacy (AxCadastro/Modelo2/3) |
735
+ |---------|-------------------|-------------------------------|
736
+ | Architecture | Model + View separated | All-in-one function |
737
+ | Testability | Model can run without UI | Requires screen interaction |
738
+ | Batch execution | FWMVCRotAuto | MsExecAuto / ExecAuto |
739
+ | Master-detail | Built-in with AddGrid | MSGetDados / GetDados arrays |
740
+ | Validation | Pre/Post blocks in Model | aRotina + validations mixed |
741
+ | Complexity | Higher initial setup | Simpler for basic CRUD |
742
+ | Reusability | High (Model reusable) | Low (tightly coupled) |
743
+
744
+ **Legacy AxCadastro example (for reference only - prefer MVC for new code):**
745
+ ```advpl
746
+ User Function FATA001()
747
+ Private cCadastro := "SA1"
748
+ Private aRotina := {}
749
+
750
+ aAdd(aRotina, {"Pesquisar", "AxPesqui", 0, 1})
751
+ aAdd(aRotina, {"Visualizar", "AxVisual", 0, 2})
752
+ aAdd(aRotina, {"Incluir", "AxInclui", 0, 3})
753
+ aAdd(aRotina, {"Alterar", "AxAltera", 0, 4})
754
+ aAdd(aRotina, {"Excluir", "AxDeleta", 0, 5})
755
+
756
+ DbSelectArea(cCadastro)
757
+ DbSetOrder(1)
758
+ DbGoTop()
759
+
760
+ MBrowse(6, 1, 22, 75, cCadastro)
761
+
762
+ Return Nil
763
+ ```
764
+
765
+ **Recommendation:** Always use MVC (`FWFormModel` / `FWFormView`) for new development. Legacy patterns should only be maintained, not created.