@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,779 @@
1
+ # Protheus Workflow and Approval Patterns
2
+
3
+ Complete reference for implementing approval workflows, automatic routines (MsExecAuto), and email notifications in TOTVS Protheus.
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ TOTVS Protheus provides built-in support for document approval workflows through the approval control system (Alcada), automatic routine execution (MsExecAuto/FWMVCRotAuto), and email notifications.
10
+
11
+ Key components:
12
+ - **MsExecAuto**: Executes standard Protheus routines (ExecAuto) programmatically without user interface.
13
+ - **FWMVCRotAuto**: Executes MVC-based routines programmatically.
14
+ - **SCR Table**: Stores documents pending approval (Documentos com Alcada).
15
+ - **SAK Table**: Stores approver definitions and limits.
16
+ - **TMailManager**: Email server communication class (SMTP/POP3).
17
+ - **TMailMessage**: Email message composition and sending.
18
+
19
+ Required includes:
20
+ ```advpl
21
+ #Include "TOTVS.CH"
22
+ ```
23
+
24
+ For MVC operations:
25
+ ```advpl
26
+ #Include "TOTVS.CH"
27
+ #Include "FWMVCDef.ch"
28
+ ```
29
+
30
+ ---
31
+
32
+ ## 2. MsExecAuto - Automatic Routine Execution
33
+
34
+ ### 2.1 Syntax
35
+
36
+ ```advpl
37
+ MsExecAuto({|x,y,z| ROUTINE(x,y,z)}, aCabec, aItens, nOpc)
38
+ ```
39
+
40
+ | Parameter | Type | Description |
41
+ |-----------|------|-------------|
42
+ | Block | Block | Code block calling the target routine |
43
+ | `aCabec` | Array | Header fields array `{{"FIELD", value, Nil}, ...}` |
44
+ | `aItens` | Array | Item lines array (for master-detail routines) |
45
+ | `nOpc` | Numeric | Operation: `3`=Include, `4`=Update, `5`=Delete |
46
+
47
+ ### 2.2 Standard Variables
48
+
49
+ Before calling MsExecAuto, declare these Private variables:
50
+
51
+ ```advpl
52
+ Private lMsErroAuto := .F. // Error flag
53
+ Private lMsHelpAuto := .T. // Suppress Help dialogs
54
+ Private lAutoErrNoFile := .T. // Errors to memory (not file)
55
+ ```
56
+
57
+ ### 2.3 Error Handling
58
+
59
+ ```advpl
60
+ If lMsErroAuto
61
+ // Erro ocorreu
62
+ Local cErro := MostraErro(.F.) // .F. retorna string em vez de exibir dialog
63
+ ConOut("Erro na rotina automatica: " + cErro)
64
+ Else
65
+ ConOut("Rotina executada com sucesso.")
66
+ EndIf
67
+ ```
68
+
69
+ ### 2.4 Include Purchase Request (MATA110)
70
+
71
+ ```advpl
72
+ #Include "TOTVS.CH"
73
+
74
+ /*/{Protheus.doc} zIncSC
75
+ Inclusao de Solicitacao de Compras via MsExecAuto
76
+ @type User Function
77
+ @author Autor
78
+ @since 01/01/2026
79
+ @version 1.0
80
+ /*/
81
+ User Function zIncSC()
82
+ Local aArea := GetArea()
83
+ Local aCabec := {}
84
+ Local aItens := {}
85
+ Local aLinha := {}
86
+
87
+ Private lMsErroAuto := .F.
88
+ Private lMsHelpAuto := .T.
89
+ Private lAutoErrNoFile := .T.
90
+
91
+ // Cabecalho (pode nao ser necessario para todas as rotinas)
92
+ // MATA110 trabalha diretamente nos itens
93
+
94
+ // Item 1
95
+ aLinha := {}
96
+ aAdd(aLinha, {"C1_PRODUTO", "000001", Nil})
97
+ aAdd(aLinha, {"C1_QUANT", 10, Nil})
98
+ aAdd(aLinha, {"C1_DESCRI", "PRODUTO TESTE", Nil})
99
+ aAdd(aLinha, {"C1_UM", "UN", Nil})
100
+ aAdd(aLinha, {"C1_LOCAL", "01", Nil})
101
+ aAdd(aLinha, {"C1_DATPRF", Date() + 30, Nil})
102
+ aAdd(aItens, aLinha)
103
+
104
+ // Item 2
105
+ aLinha := {}
106
+ aAdd(aLinha, {"C1_PRODUTO", "000002", Nil})
107
+ aAdd(aLinha, {"C1_QUANT", 5, Nil})
108
+ aAdd(aLinha, {"C1_DESCRI", "PRODUTO TESTE 2", Nil})
109
+ aAdd(aLinha, {"C1_UM", "UN", Nil})
110
+ aAdd(aLinha, {"C1_LOCAL", "01", Nil})
111
+ aAdd(aLinha, {"C1_DATPRF", Date() + 30, Nil})
112
+ aAdd(aItens, aLinha)
113
+
114
+ // Executa inclusao (opcao 3)
115
+ MsExecAuto({|x, y, z| MATA110(x, y, z)}, aCabec, aItens, 3)
116
+
117
+ If lMsErroAuto
118
+ ConOut("Erro na inclusao da SC: " + MostraErro(.F.))
119
+ Else
120
+ ConOut("Solicitacao de compras incluida com sucesso.")
121
+ EndIf
122
+
123
+ RestArea(aArea)
124
+
125
+ Return Nil
126
+ ```
127
+
128
+ ### 2.5 Include Sales Order (MATA410)
129
+
130
+ ```advpl
131
+ User Function zIncPV()
132
+ Local aCabec := {}
133
+ Local aItens := {}
134
+ Local aLinha := {}
135
+ Local aArea := GetArea()
136
+
137
+ Private lMsErroAuto := .F.
138
+ Private lMsHelpAuto := .T.
139
+ Private lAutoErrNoFile := .T.
140
+
141
+ // Cabecalho do pedido de venda
142
+ aAdd(aCabec, {"C5_TIPO", "N", Nil})
143
+ aAdd(aCabec, {"C5_CLIENTE", "000001", Nil})
144
+ aAdd(aCabec, {"C5_LOJACLI", "01", Nil})
145
+ aAdd(aCabec, {"C5_CONDPAG", "001", Nil})
146
+
147
+ // Item 1
148
+ aLinha := {}
149
+ aAdd(aLinha, {"C6_ITEM", "01", Nil})
150
+ aAdd(aLinha, {"C6_PRODUTO", "000001", Nil})
151
+ aAdd(aLinha, {"C6_QTDVEN", 10, Nil})
152
+ aAdd(aLinha, {"C6_PRCVEN", 50.00, Nil})
153
+ aAdd(aLinha, {"C6_PRUNIT", 50.00, Nil})
154
+ aAdd(aLinha, {"C6_VALOR", 500.00, Nil})
155
+ aAdd(aLinha, {"C6_TES", "501", Nil})
156
+ aAdd(aItens, aLinha)
157
+
158
+ // Executa inclusao
159
+ MsExecAuto({|x, y, z| MATA410(x, y, z)}, aCabec, aItens, 3)
160
+
161
+ If lMsErroAuto
162
+ ConOut("Erro na inclusao do PV: " + MostraErro(.F.))
163
+ Else
164
+ ConOut("Pedido de venda incluido com sucesso.")
165
+ EndIf
166
+
167
+ RestArea(aArea)
168
+
169
+ Return Nil
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 3. FWMVCRotAuto - MVC Automatic Execution
175
+
176
+ ### 3.1 Syntax
177
+
178
+ ```advpl
179
+ lRet := FWMVCRotAuto(oModel, cSource, nOperation, aParams)
180
+ ```
181
+
182
+ | Parameter | Type | Description |
183
+ |-----------|------|-------------|
184
+ | `oModel` | Object | Model object (usually Nil, framework creates internally) |
185
+ | `cSource` | Character | Source program name with ModelDef |
186
+ | `nOperation` | Numeric | Operation constant (MODEL_OPERATION_INSERT, etc.) |
187
+ | `aParams` | Array | Data array: `{{"ModelId", aFieldValues}, {"GridId", aGridLines}}` |
188
+ | **Return** | Logical | `.T.` if operation succeeded |
189
+
190
+ ### 3.2 Include via FWMVCRotAuto
191
+
192
+ ```advpl
193
+ #Include "TOTVS.CH"
194
+ #Include "FWMVCDef.ch"
195
+
196
+ User Function zIncMVC()
197
+ Local aHeader := {}
198
+ Local aItems := {}
199
+ Local aLine := {}
200
+ Local lRet := .F.
201
+ Local oModel := Nil
202
+
203
+ // Cabecalho
204
+ aAdd(aHeader, {"ZA1_NUM", "000001", Nil})
205
+ aAdd(aHeader, {"ZA1_CLIENT", "000001", Nil})
206
+ aAdd(aHeader, {"ZA1_LOJA", "01", Nil})
207
+ aAdd(aHeader, {"ZA1_EMISSA", Date(), Nil})
208
+
209
+ // Item 1
210
+ aLine := {}
211
+ aAdd(aLine, {"ZA2_ITEM", "01", Nil})
212
+ aAdd(aLine, {"ZA2_PROD", "000001", Nil})
213
+ aAdd(aLine, {"ZA2_QUANT", 10, Nil})
214
+ aAdd(aLine, {"ZA2_PRCUNI", 25.50, Nil})
215
+ aAdd(aItems, aLine)
216
+
217
+ // Executa
218
+ lRet := FWMVCRotAuto(oModel, "FATA090", MODEL_OPERATION_INSERT, {;
219
+ {"ZA1MASTER", aHeader}, ;
220
+ {"ZA2DETAIL", aItems} ;
221
+ })
222
+
223
+ If lRet
224
+ ConOut("Registro incluido com sucesso!")
225
+ Else
226
+ ConOut("Erro na inclusao.")
227
+ MostraErro()
228
+ EndIf
229
+
230
+ Return lRet
231
+ ```
232
+
233
+ ---
234
+
235
+ ## 4. Approval Flow - SCR Table
236
+
237
+ ### 4.1 SCR Table Structure (Documentos com Alcada)
238
+
239
+ The SCR table stores documents pending approval in the Protheus approval system.
240
+
241
+ | Field | Type | Description |
242
+ |-------|------|-------------|
243
+ | `CR_FILIAL` | C(2) | Branch |
244
+ | `CR_NUM` | C(50) | Document number |
245
+ | `CR_TIPO` | C(2) | Document type |
246
+ | `CR_USER` | C(6) | User code who created the document |
247
+ | `CR_APROV` | C(6) | Approver code |
248
+ | `CR_GRUPO` | C(6) | Approval group |
249
+ | `CR_NIVEL` | C(2) | Approval level |
250
+ | `CR_STATUS` | C(2) | Approval status |
251
+ | `CR_EMISSAO` | D | Issue date |
252
+ | `CR_TOTAL` | N(14,2) | Total value |
253
+ | `CR_DATALIB` | D | Release/approval date |
254
+ | `CR_OBS` | M(50) | Approval observations |
255
+ | `CR_USERLIB` | C(6) | User who approved |
256
+ | `CR_LIBAPRO` | C(6) | Effective approver of the document |
257
+ | `CR_VALLIB` | N(14,2) | Approved value |
258
+ | `CR_TIPOLIM` | C(1) | Approver limit type |
259
+ | `CR_MOEDA` | N(2) | Currency |
260
+
261
+ ### 4.2 SAK Table Structure (Aprovadores)
262
+
263
+ The SAK table stores approver definitions.
264
+
265
+ | Field | Type | Description |
266
+ |-------|------|-------------|
267
+ | `AK_FILIAL` | C(2) | Branch |
268
+ | `AK_COD` | C(6) | Approver code |
269
+ | `AK_USER` | C(6) | User code |
270
+ | `AK_NOME` | C(40) | Full name |
271
+ | `AK_LIMMIN` | N(14,2) | Minimum approval limit |
272
+ | `AK_LIMMAX` | N(14,2) | Maximum approval limit |
273
+ | `AK_APROSUP` | C(6) | Superior approver code |
274
+ | `AK_LIMITE` | N(14,2) | Approver limit |
275
+ | `AK_TIPO` | C(1) | Limit type |
276
+ | `AK_LOGIN` | C(25) | User login |
277
+
278
+ ### 4.3 Querying Pending Approvals
279
+
280
+ ```advpl
281
+ Static Function fGetPendentes(cAprovador)
282
+ Local cQuery := ""
283
+ Local cAlias := GetNextAlias()
284
+ Local aResult := {}
285
+
286
+ cQuery := "SELECT CR_NUM, CR_TIPO, CR_TOTAL, CR_EMISSAO, CR_USER "
287
+ cQuery += "FROM " + RetSqlName("SCR") + " SCR "
288
+ cQuery += "WHERE SCR.D_E_L_E_T_ = ' ' "
289
+ cQuery += "AND CR_FILIAL = '" + xFilial("SCR") + "' "
290
+ cQuery += "AND CR_APROV = '" + cAprovador + "' "
291
+ cQuery += "AND CR_STATUS = '01' " // Pendente de aprovacao
292
+ cQuery += "ORDER BY CR_EMISSAO, CR_NUM "
293
+
294
+ TCQuery cQuery New Alias (cAlias)
295
+
296
+ While !(cAlias)->(Eof())
297
+ aAdd(aResult, { ;
298
+ AllTrim((cAlias)->CR_NUM), ;
299
+ AllTrim((cAlias)->CR_TIPO), ;
300
+ (cAlias)->CR_TOTAL, ;
301
+ (cAlias)->CR_EMISSAO, ;
302
+ AllTrim((cAlias)->CR_USER) ;
303
+ })
304
+ (cAlias)->(DbSkip())
305
+ EndWh
306
+
307
+ (cAlias)->(DbCloseArea())
308
+
309
+ Return aResult
310
+ ```
311
+
312
+ ---
313
+
314
+ ## 5. Email - TMailManager and TMailMessage
315
+
316
+ ### 5.1 TMailManager - Server Connection
317
+
318
+ **Constructor and methods confirmed on TDN:**
319
+
320
+ | Method | Description |
321
+ |--------|-------------|
322
+ | `TMailManager():New()` | Creates a new mail manager instance |
323
+ | `Init(cServer, cSmtpServer, cUser, cPass, nPop3Port, nSmtpPort)` | Initializes server parameters |
324
+ | `SetUseTLS(lUseTLS)` | Enables TLS encryption |
325
+ | `SetUseSSL(lUseSSL)` | Enables SSL encryption |
326
+ | `SetSmtpTimeOut(nTimeout)` | Sets SMTP timeout in seconds |
327
+ | `SmtpConnect()` | Establishes SMTP connection |
328
+ | `SmtpAuth(cUser, cPass)` | Authenticates on the SMTP server |
329
+ | `SmtpDisconnect()` | Closes the SMTP connection |
330
+ | `GetErrorString(nError)` | Returns error description for the given error code |
331
+
332
+ ### 5.2 TMailMessage - Email Composition
333
+
334
+ **Properties:**
335
+
336
+ | Property | Type | Description |
337
+ |----------|------|-------------|
338
+ | `cFrom` | Character | Sender email address |
339
+ | `cTo` | Character | Recipient email address |
340
+ | `cCc` | Character | CC recipients |
341
+ | `cBcc` | Character | BCC recipients |
342
+ | `cSubject` | Character | Email subject |
343
+ | `cBody` | Character | Email body (HTML or plain text) |
344
+
345
+ **Methods:**
346
+
347
+ | Method | Description |
348
+ |--------|-------------|
349
+ | `TMailMessage():New()` | Creates a new message instance |
350
+ | `Clear()` | Clears all message fields |
351
+ | `Send(oServer)` | Sends the message via the TMailManager server object |
352
+ | `AttachFile(cFilePath)` | Attaches a file to the message |
353
+
354
+ ### 5.3 Complete Email Sending Example
355
+
356
+ ```advpl
357
+ #Include "TOTVS.CH"
358
+
359
+ /*/{Protheus.doc} zSendMail
360
+ Envia email via SMTP usando TMailManager e TMailMessage
361
+ @type User Function
362
+ @author Autor
363
+ @since 01/01/2026
364
+ @version 1.0
365
+ /*/
366
+ User Function zSendMail(cDestinatario, cAssunto, cCorpo)
367
+ Local oServer := Nil
368
+ Local oMessage := Nil
369
+ Local nRet := 0
370
+ Local cSmtp := "smtp.empresa.com"
371
+ Local cUser := "sistema@empresa.com"
372
+ Local cPass := "senha123"
373
+ Local nPort := 587
374
+ Local lRet := .F.
375
+
376
+ // Cria o gerenciador de email
377
+ oServer := TMailManager():New()
378
+ oServer:SetUseTLS(.T.)
379
+
380
+ // Inicializa com dados do servidor SMTP
381
+ nRet := oServer:Init("", cSmtp, cUser, cPass, 0, nPort)
382
+ If nRet <> 0
383
+ ConOut("Erro ao inicializar email: " + oServer:GetErrorString(nRet))
384
+ Return .F.
385
+ EndIf
386
+
387
+ // Configura timeout
388
+ oServer:SetSmtpTimeOut(60)
389
+
390
+ // Conecta ao servidor SMTP
391
+ nRet := oServer:SmtpConnect()
392
+ If nRet <> 0
393
+ ConOut("Erro ao conectar SMTP: " + oServer:GetErrorString(nRet))
394
+ Return .F.
395
+ EndIf
396
+
397
+ // Autentica
398
+ nRet := oServer:SmtpAuth(cUser, cPass)
399
+ If nRet <> 0
400
+ ConOut("Erro na autenticacao SMTP: " + oServer:GetErrorString(nRet))
401
+ oServer:SmtpDisconnect()
402
+ Return .F.
403
+ EndIf
404
+
405
+ // Monta a mensagem
406
+ oMessage := TMailMessage():New()
407
+ oMessage:Clear()
408
+ oMessage:cFrom := cUser
409
+ oMessage:cTo := cDestinatario
410
+ oMessage:cSubject := cAssunto
411
+ oMessage:cBody := cCorpo
412
+
413
+ // Envia
414
+ nRet := oMessage:Send(oServer)
415
+ If nRet <> 0
416
+ ConOut("Erro ao enviar email: " + oServer:GetErrorString(nRet))
417
+ Else
418
+ ConOut("Email enviado com sucesso para: " + cDestinatario)
419
+ lRet := .T.
420
+ EndIf
421
+
422
+ // Desconecta
423
+ oServer:SmtpDisconnect()
424
+
425
+ Return lRet
426
+ ```
427
+
428
+ ### 5.4 Email with Attachment
429
+
430
+ ```advpl
431
+ // Monta mensagem com anexo
432
+ oMessage := TMailMessage():New()
433
+ oMessage:Clear()
434
+ oMessage:cFrom := "sistema@empresa.com"
435
+ oMessage:cTo := "gestor@empresa.com"
436
+ oMessage:cSubject := "Relatorio Diario"
437
+ oMessage:cBody := "<html><body><h2>Relatorio em anexo</h2></body></html>"
438
+
439
+ // Anexa arquivo
440
+ oMessage:AttachFile("/reports/relatorio_diario.pdf")
441
+
442
+ // Envia
443
+ nRet := oMessage:Send(oServer)
444
+ ```
445
+
446
+ ---
447
+
448
+ ## 6. TMailMng - Modern Email Class
449
+
450
+ TMailMng is the newer replacement for TMailManager, offering greater configuration flexibility and not depending on the `appserver.ini` Protocol key.
451
+
452
+ ```advpl
453
+ // TMailMng usa o protocolo definido diretamente no construtor
454
+ // Consulte a documentacao TDN para sintaxe atualizada:
455
+ // https://tdn.totvs.com/display/tec/Classe+TMailMng
456
+ ```
457
+
458
+ **Note**: TMailMng is confirmed on TDN as a valid replacement class. For production implementations, consult the TDN documentation for the most current constructor syntax and method signatures, as they may vary by Protheus version.
459
+
460
+ ---
461
+
462
+ ## 7. Complete Example - Purchase Order Approval Workflow
463
+
464
+ This example implements a complete approval workflow: creating a purchase order, checking approval requirements, and sending email notifications.
465
+
466
+ ```advpl
467
+ #Include "TOTVS.CH"
468
+
469
+ /*/{Protheus.doc} zAprovPC
470
+ Fluxo de aprovacao de Pedido de Compras
471
+ @type User Function
472
+ @author Autor
473
+ @since 01/01/2026
474
+ @version 1.0
475
+ /*/
476
+ User Function zAprovPC(cNumPC)
477
+ Local aArea := GetArea()
478
+ Local lAprov := .F.
479
+ Local cAprov := ""
480
+ Local nTotal := 0
481
+
482
+ // Busca dados do pedido de compras
483
+ DbSelectArea("SC7")
484
+ DbSetOrder(1)
485
+
486
+ If !DbSeek(xFilial("SC7") + cNumPC)
487
+ ConOut("[zAprovPC] Pedido nao encontrado: " + cNumPC)
488
+ RestArea(aArea)
489
+ Return .F.
490
+ EndIf
491
+
492
+ nTotal := fTotalPC(cNumPC)
493
+
494
+ // Busca aprovador com alcada suficiente
495
+ cAprov := fGetAprovador(nTotal)
496
+
497
+ If Empty(cAprov)
498
+ ConOut("[zAprovPC] Nenhum aprovador encontrado para o valor: " + cValToChar(nTotal))
499
+ RestArea(aArea)
500
+ Return .F.
501
+ EndIf
502
+
503
+ // Verifica se ja existe aprovacao pendente na SCR
504
+ If fTemAprovPendente(cNumPC, "PC")
505
+ ConOut("[zAprovPC] Pedido ja possui aprovacao pendente.")
506
+ RestArea(aArea)
507
+ Return .F.
508
+ EndIf
509
+
510
+ // Registra solicitacao de aprovacao na SCR
511
+ fRegistraAprovacao(cNumPC, "PC", cAprov, nTotal)
512
+
513
+ // Envia notificacao por email ao aprovador
514
+ fNotificaAprovador(cAprov, cNumPC, nTotal)
515
+
516
+ ConOut("[zAprovPC] Solicitacao de aprovacao registrada para PC: " + cNumPC)
517
+
518
+ RestArea(aArea)
519
+
520
+ Return .T.
521
+
522
+ // =============================================================
523
+ // Calcula o valor total do pedido de compras
524
+ // =============================================================
525
+ Static Function fTotalPC(cNumPC)
526
+ Local cQuery := ""
527
+ Local cAlias := GetNextAlias()
528
+ Local nTotal := 0
529
+
530
+ cQuery := "SELECT SUM(C7_QUANT * C7_PRECO) AS TOTAL "
531
+ cQuery += "FROM " + RetSqlName("SC7") + " SC7 "
532
+ cQuery += "WHERE SC7.D_E_L_E_T_ = ' ' "
533
+ cQuery += "AND C7_FILIAL = '" + xFilial("SC7") + "' "
534
+ cQuery += "AND C7_NUM = '" + cNumPC + "' "
535
+
536
+ TCQuery cQuery New Alias (cAlias)
537
+
538
+ If !(cAlias)->(Eof())
539
+ nTotal := (cAlias)->TOTAL
540
+ EndIf
541
+
542
+ (cAlias)->(DbCloseArea())
543
+
544
+ Return nTotal
545
+
546
+ // =============================================================
547
+ // Busca aprovador com alcada suficiente
548
+ // =============================================================
549
+ Static Function fGetAprovador(nValor)
550
+ Local cQuery := ""
551
+ Local cAlias := GetNextAlias()
552
+ Local cAprov := ""
553
+
554
+ cQuery := "SELECT AK_COD, AK_NOME, AK_LIMITE "
555
+ cQuery += "FROM " + RetSqlName("SAK") + " SAK "
556
+ cQuery += "WHERE SAK.D_E_L_E_T_ = ' ' "
557
+ cQuery += "AND AK_FILIAL = '" + xFilial("SAK") + "' "
558
+ cQuery += "AND AK_LIMITE >= " + cValToChar(nValor) + " "
559
+ cQuery += "ORDER BY AK_LIMITE "
560
+
561
+ TCQuery cQuery New Alias (cAlias)
562
+
563
+ If !(cAlias)->(Eof())
564
+ cAprov := AllTrim((cAlias)->AK_COD)
565
+ EndIf
566
+
567
+ (cAlias)->(DbCloseArea())
568
+
569
+ Return cAprov
570
+
571
+ // =============================================================
572
+ // Verifica se existe aprovacao pendente
573
+ // =============================================================
574
+ Static Function fTemAprovPendente(cNumDoc, cTipoDoc)
575
+ Local lPendente := .F.
576
+
577
+ DbSelectArea("SCR")
578
+ DbSetOrder(1)
579
+
580
+ If DbSeek(xFilial("SCR") + cNumDoc)
581
+ If AllTrim(SCR->CR_TIPO) == cTipoDoc .And. AllTrim(SCR->CR_STATUS) < "03"
582
+ lPendente := .T.
583
+ EndIf
584
+ EndIf
585
+
586
+ Return lPendente
587
+
588
+ // =============================================================
589
+ // Registra solicitacao de aprovacao
590
+ // =============================================================
591
+ Static Function fRegistraAprovacao(cNumDoc, cTipoDoc, cAprov, nTotal)
592
+
593
+ DbSelectArea("SCR")
594
+
595
+ RecLock("SCR", .T.)
596
+ SCR->CR_FILIAL := xFilial("SCR")
597
+ SCR->CR_NUM := cNumDoc
598
+ SCR->CR_TIPO := cTipoDoc
599
+ SCR->CR_USER := cUserID // Usuario logado
600
+ SCR->CR_APROV := cAprov
601
+ SCR->CR_STATUS := "01" // Pendente
602
+ SCR->CR_EMISSAO := Date()
603
+ SCR->CR_TOTAL := nTotal
604
+ MsUnlock()
605
+
606
+ Return Nil
607
+
608
+ // =============================================================
609
+ // Notifica aprovador por email
610
+ // =============================================================
611
+ Static Function fNotificaAprovador(cAprov, cNumPC, nTotal)
612
+ Local cEmail := ""
613
+ Local cCorpo := ""
614
+
615
+ // Busca email do aprovador
616
+ DbSelectArea("SAK")
617
+ DbSetOrder(1)
618
+
619
+ If DbSeek(xFilial("SAK") + cAprov)
620
+ // Busca email do usuario vinculado ao aprovador
621
+ cEmail := fGetEmailUsuario(AllTrim(SAK->AK_USER))
622
+ EndIf
623
+
624
+ If Empty(cEmail)
625
+ ConOut("[zAprovPC] Email do aprovador nao encontrado: " + cAprov)
626
+ Return Nil
627
+ EndIf
628
+
629
+ // Monta corpo do email
630
+ cCorpo := "<html><body>"
631
+ cCorpo += "<h2>Solicitacao de Aprovacao - Pedido de Compras</h2>"
632
+ cCorpo += "<p>Numero do Pedido: <strong>" + AllTrim(cNumPC) + "</strong></p>"
633
+ cCorpo += "<p>Valor Total: <strong>R$ " + AllTrim(Transform(nTotal, "@E 999,999,999.99")) + "</strong></p>"
634
+ cCorpo += "<p>Data: " + DToC(Date()) + "</p>"
635
+ cCorpo += "<p>Acesse o Protheus para aprovar ou rejeitar este documento.</p>"
636
+ cCorpo += "</body></html>"
637
+
638
+ // Envia email (usando funcao do exemplo anterior)
639
+ U_zSendMail(cEmail, "Aprovacao Pendente - PC " + AllTrim(cNumPC), cCorpo)
640
+
641
+ Return Nil
642
+
643
+ // =============================================================
644
+ // Busca email de um usuario
645
+ // =============================================================
646
+ Static Function fGetEmailUsuario(cUserId)
647
+ Local cEmail := ""
648
+ Local cQuery := ""
649
+ Local cAlias := GetNextAlias()
650
+
651
+ cQuery := "SELECT USR_EMAIL "
652
+ cQuery += "FROM " + RetSqlName("SYS_USR") + " "
653
+ cQuery += "WHERE D_E_L_E_T_ = ' ' "
654
+ cQuery += "AND USR_ID = '" + cUserId + "' "
655
+
656
+ TCQuery cQuery New Alias (cAlias)
657
+
658
+ If !(cAlias)->(Eof())
659
+ cEmail := AllTrim((cAlias)->USR_EMAIL)
660
+ EndIf
661
+
662
+ (cAlias)->(DbCloseArea())
663
+
664
+ Return cEmail
665
+ ```
666
+
667
+ ---
668
+
669
+ ## 8. MsExecAuto with Approval Blocking
670
+
671
+ When a document requires approval, MsExecAuto may block the operation if the approval flow is configured. To handle this:
672
+
673
+ ```advpl
674
+ // Antes do MsExecAuto, verificar se o documento necessita aprovacao
675
+ // e tratar o retorno adequadamente
676
+
677
+ Private lMsErroAuto := .F.
678
+ Private lMsHelpAuto := .T.
679
+ Private lAutoErrNoFile := .T.
680
+
681
+ MsExecAuto({|x, y, z| MATA120(x, y, z)}, aCabec, aItens, 3)
682
+
683
+ If lMsErroAuto
684
+ Local cErro := MostraErro(.F.)
685
+
686
+ // Verifica se o erro e relacionado a aprovacao/alcada
687
+ If "ALCADA" $ Upper(cErro) .Or. "APROV" $ Upper(cErro)
688
+ ConOut("Documento gerado e encaminhado para aprovacao.")
689
+ Else
690
+ ConOut("Erro na geracao do documento: " + cErro)
691
+ EndIf
692
+ EndIf
693
+ ```
694
+
695
+ ---
696
+
697
+ ## 9. FWMVCRotAuto with Approver Validation
698
+
699
+ Example of using FWMVCRotAuto with pre-validation that checks approver permissions:
700
+
701
+ ```advpl
702
+ #Include "TOTVS.CH"
703
+ #Include "FWMVCDef.ch"
704
+
705
+ User Function zMVCAprov()
706
+ Local aHeader := {}
707
+ Local lRet := .F.
708
+ Local oModel := Nil
709
+
710
+ // Verifica se o usuario atual e aprovador
711
+ If !fIsAprovador(cUserID)
712
+ ConOut("Usuario nao possui permissao de aprovador.")
713
+ Return .F.
714
+ EndIf
715
+
716
+ // Posiciona no registro a ser aprovado
717
+ DbSelectArea("ZA1")
718
+ DbSetOrder(1)
719
+ DbSeek(xFilial("ZA1") + "000001")
720
+
721
+ // Campos de aprovacao
722
+ aAdd(aHeader, {"ZA1_STATUS", "A", Nil}) // A = Aprovado
723
+ aAdd(aHeader, {"ZA1_DTAPRO", Date(), Nil})
724
+ aAdd(aHeader, {"ZA1_USRAPR", cUserID, Nil})
725
+
726
+ lRet := FWMVCRotAuto(oModel, "FATA090", MODEL_OPERATION_UPDATE, {;
727
+ {"ZA1MASTER", aHeader} ;
728
+ })
729
+
730
+ If lRet
731
+ ConOut("Documento aprovado com sucesso!")
732
+ // Notifica solicitante
733
+ fNotificaSolicitante("000001", "APROVADO")
734
+ Else
735
+ ConOut("Erro na aprovacao.")
736
+ MostraErro()
737
+ EndIf
738
+
739
+ Return lRet
740
+
741
+ Static Function fIsAprovador(cUser)
742
+ Local lAprov := .F.
743
+
744
+ DbSelectArea("SAK")
745
+ DbSetOrder(1)
746
+
747
+ // Busca aprovador pelo codigo do usuario
748
+ DbGoTop()
749
+ While !Eof()
750
+ If xFilial("SAK") == SAK->AK_FILIAL .And. AllTrim(SAK->AK_USER) == AllTrim(cUser)
751
+ lAprov := .T.
752
+ Exit
753
+ EndIf
754
+ DbSkip()
755
+ EndWh
756
+
757
+ Return lAprov
758
+ ```
759
+
760
+ ---
761
+
762
+ ## 10. Best Practices
763
+
764
+ ### 10.1 MsExecAuto
765
+ - Always declare `lMsErroAuto`, `lMsHelpAuto`, and `lAutoErrNoFile` as Private before calling.
766
+ - Always check `lMsErroAuto` after execution.
767
+ - Use `MostraErro(.F.)` to capture error messages as strings.
768
+ - Use `GetArea()` / `RestArea()` to preserve table positioning.
769
+
770
+ ### 10.2 Approval Flows
771
+ - Use the standard SCR/SAK tables for approval control when possible.
772
+ - Always validate approver limits before allowing approval operations.
773
+ - Log all approval actions for auditing purposes.
774
+
775
+ ### 10.3 Email Notifications
776
+ - Always check the return value of each TMailManager method (0 = success).
777
+ - Always call `SmtpDisconnect()` even when errors occur.
778
+ - Use TLS/SSL for secure email transmission.
779
+ - For high-volume email sending, consider using a background job.