@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.
- package/agents/changelog-generator.md +63 -0
- package/agents/code-generator.md +215 -0
- package/agents/code-reviewer.md +145 -0
- package/agents/debugger.md +83 -0
- package/agents/doc-generator.md +67 -0
- package/agents/docs-reference.md +86 -0
- package/agents/migrator.md +84 -0
- package/agents/process-consultant.md +97 -0
- package/agents/refactorer.md +75 -0
- package/agents/sx-configurator.md +67 -0
- package/commands/changelog.md +66 -0
- package/commands/diagnose.md +67 -0
- package/commands/docs.md +81 -0
- package/commands/document.md +67 -0
- package/commands/explain.md +60 -0
- package/commands/generate.md +111 -0
- package/commands/migrate.md +81 -0
- package/commands/process.md +111 -0
- package/commands/refactor.md +65 -0
- package/commands/review.md +60 -0
- package/commands/sxgen.md +98 -0
- package/commands/test.md +103 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
- package/skills/advpl-code-generation/SKILL.md +163 -0
- package/skills/advpl-code-generation/patterns-fwformbrowse.md +485 -0
- package/skills/advpl-code-generation/patterns-jobs.md +519 -0
- package/skills/advpl-code-generation/patterns-mvc.md +765 -0
- package/skills/advpl-code-generation/patterns-pontos-entrada.md +708 -0
- package/skills/advpl-code-generation/patterns-rest.md +974 -0
- package/skills/advpl-code-generation/patterns-soap.md +639 -0
- package/skills/advpl-code-generation/patterns-treport.md +481 -0
- package/skills/advpl-code-generation/patterns-workflow.md +779 -0
- package/skills/advpl-code-generation/templates-classes.md +1096 -0
- package/skills/advpl-code-review/SKILL.md +72 -0
- package/skills/advpl-code-review/rules-best-practices.md +444 -0
- package/skills/advpl-code-review/rules-modernization.md +290 -0
- package/skills/advpl-code-review/rules-performance.md +333 -0
- package/skills/advpl-code-review/rules-security.md +302 -0
- package/skills/advpl-debugging/SKILL.md +265 -0
- package/skills/advpl-debugging/common-errors.md +1124 -0
- package/skills/advpl-debugging/performance-tips.md +768 -0
- package/skills/advpl-refactoring/SKILL.md +139 -0
- package/skills/advpl-to-tlpp-migration/SKILL.md +293 -0
- package/skills/advpl-to-tlpp-migration/migration-checklist.md +122 -0
- package/skills/advpl-to-tlpp-migration/migration-rules.md +265 -0
- package/skills/changelog-patterns/SKILL.md +99 -0
- package/skills/code-explanation/SKILL.md +66 -0
- package/skills/documentation-patterns/SKILL.md +172 -0
- package/skills/embedded-sql/SKILL.md +379 -0
- package/skills/probat-testing/SKILL.md +226 -0
- package/skills/probat-testing/patterns-unit-tests.md +614 -0
- package/skills/protheus-business/SKILL.md +92 -0
- package/skills/protheus-business/modulo-compras.md +780 -0
- package/skills/protheus-business/modulo-contabilidade.md +874 -0
- package/skills/protheus-business/modulo-estoque.md +876 -0
- package/skills/protheus-business/modulo-faturamento.md +800 -0
- package/skills/protheus-business/modulo-financeiro.md +1015 -0
- package/skills/protheus-business/modulo-fiscal.md +749 -0
- package/skills/protheus-business/modulo-manutencao.md +848 -0
- package/skills/protheus-business/modulo-pcp.md +743 -0
- package/skills/protheus-reference/SKILL.md +119 -0
- package/skills/protheus-reference/native-functions.md +7029 -0
- package/skills/protheus-reference/rest-api-reference.md +1758 -0
- package/skills/protheus-reference/restricted-functions.md +265 -0
- package/skills/protheus-reference/sx-dictionary.md +854 -0
- 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.
|