@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,519 @@
1
+ # Protheus Jobs and Background Processing Patterns
2
+
3
+ Complete reference for implementing background jobs, multi-threading, and scheduled tasks in TOTVS Protheus.
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ Protheus supports background processing through Jobs configured in `appserver.ini` and programmatic thread creation via `StartJob`. Jobs run without a user interface and are used for batch processing, integrations, scheduled tasks, and parallel operations.
10
+
11
+ Key functions:
12
+ - **StartJob()**: Executes a function in a secondary thread without interface.
13
+ - **RpcSetEnv()**: Opens/prepares the Protheus environment (company, branch, tables) in a new thread.
14
+ - **RpcClearEnv()**: Closes/releases the environment and connections opened by `RpcSetEnv`.
15
+ - **LockByName()**: Creates a named semaphore on the license server for concurrency control.
16
+ - **UnlockByName()**: Releases a named semaphore created by `LockByName`.
17
+ - **ConOut()**: Writes messages to the AppServer console log.
18
+ - **FWLogMsg()**: Standardized logging API (recommended replacement for `ConOut` in newer versions).
19
+
20
+ Required includes:
21
+ ```advpl
22
+ #Include "TOTVS.CH"
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 2. OnStart - AppServer.ini Configuration
28
+
29
+ ### 2.1 Basic Job Configuration
30
+
31
+ Jobs are configured in the `appserver.ini` file to run automatically when the Application Server starts.
32
+
33
+ ```ini
34
+ [OnStart]
35
+ JOBS=MYJOB
36
+ REFRESHRATE=120
37
+
38
+ [MYJOB]
39
+ MAIN=U_zJobMain
40
+ ENVIRONMENT=PRODUCAO
41
+ ```
42
+
43
+ | Key | Description |
44
+ |-----|-------------|
45
+ | `JOBS` | Comma-separated list of job section names to execute |
46
+ | `REFRESHRATE` | Interval in seconds to re-execute jobs if not running (default: 60) |
47
+
48
+ ### 2.2 Job Section Keys
49
+
50
+ | Key | Description |
51
+ |-----|-------------|
52
+ | `MAIN` | Function name to execute (e.g., `U_zJobMain`) |
53
+ | `ENVIRONMENT` | Server environment name |
54
+
55
+ ### 2.3 Multiple Jobs
56
+
57
+ ```ini
58
+ [OnStart]
59
+ JOBS=JOB_INTEG,JOB_LOTE,JOB_HTTP
60
+ REFRESHRATE=300
61
+
62
+ [JOB_INTEG]
63
+ MAIN=U_zIntegJob
64
+ ENVIRONMENT=PRODUCAO
65
+
66
+ [JOB_LOTE]
67
+ MAIN=U_zLoteJob
68
+ ENVIRONMENT=PRODUCAO
69
+
70
+ [JOB_HTTP]
71
+ MAIN=HTTP_START
72
+ ENVIRONMENT=PRODUCAO
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 3. StartJob - Thread Creation
78
+
79
+ ### 3.1 Syntax
80
+
81
+ ```advpl
82
+ StartJob(cFuncName, cEnvironment, lWait, parm1, parm2, ..., parm25)
83
+ ```
84
+
85
+ | Parameter | Type | Description |
86
+ |-----------|------|-------------|
87
+ | `cFuncName` | Character | Function name to execute in the new thread |
88
+ | `cEnvironment` | Character | Server environment (use `GetEnvServer()` for current) |
89
+ | `lWait` | Logical | `.T.` waits for thread to finish, `.F.` runs in background |
90
+ | `parm1..parm25` | Any | Up to 25 optional parameters passed to the function |
91
+
92
+ ### 3.2 Basic Usage
93
+
94
+ ```advpl
95
+ // Executa em background sem esperar
96
+ StartJob("U_zProcessa", GetEnvServer(), .F.)
97
+
98
+ // Executa e aguarda conclusao
99
+ StartJob("U_zProcessa", GetEnvServer(), .T.)
100
+
101
+ // Passa parametros
102
+ StartJob("U_zProcessa", GetEnvServer(), .F., "99", "01", "PARAM3")
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 4. RpcSetEnv - Environment Setup
108
+
109
+ ### 4.1 Syntax
110
+
111
+ ```advpl
112
+ lRet := RpcSetEnv(cCodEmp, cCodFil, cUsuario, cSenha, cModulo, cFunName, aTables)
113
+ ```
114
+
115
+ | Parameter | Type | Description |
116
+ |-----------|------|-------------|
117
+ | `cCodEmp` | Character | Company code (e.g., `"99"`) |
118
+ | `cCodFil` | Character | Branch code (e.g., `"01"`) |
119
+
120
+ > **IMPORTANT:** Never use `cFilial`, `cFilAnt` or `cEmpAnt` as variable names — these are **reserved Private variables** maintained by the Protheus framework to track company/branch context. Using them as Local variables will shadow the system variables and cause unpredictable behavior. Use `cCodFil`, `cCodEmp` or similar names instead.
121
+ | `cUsuario` | Character | User login (optional) |
122
+ | `cSenha` | Character | User password (optional) |
123
+ | `cModulo` | Character | Module code, e.g., `"FAT"`, `"CTB"` (default: `"FAT"`) |
124
+ | `cFunName` | Character | Function name for `FunName()` (default: `"RPC"`) |
125
+ | `aTables` | Array | Array of table aliases to open (optional) |
126
+ | **Return** | Logical | `.T.` if environment was prepared successfully |
127
+
128
+ ### 4.2 Basic Usage
129
+
130
+ ```advpl
131
+ If RpcSetEnv("99", "01", "admin", " ", "FAT")
132
+ // Ambiente preparado, executar logica de negocio
133
+ // ...
134
+ RpcClearEnv()
135
+ EndIf
136
+ ```
137
+
138
+ ---
139
+
140
+ ## 5. RpcClearEnv - Environment Cleanup
141
+
142
+ ### 5.1 Syntax
143
+
144
+ ```advpl
145
+ RpcClearEnv()
146
+ ```
147
+
148
+ Closes the environment opened by `RpcSetEnv`, releasing connections and resources. Must always be called after processing is complete.
149
+
150
+ **Important**: Do not call `RpcSetEnv` or `RpcClearEnv` in routines executed from the Protheus menu, as the environment is already open and managed by the system.
151
+
152
+ ---
153
+
154
+ ## 6. LockByName / UnlockByName - Semaphores
155
+
156
+ ### 6.1 LockByName Syntax
157
+
158
+ ```advpl
159
+ lLocked := LockByName(cName, lByCompany, lByBranch)
160
+ ```
161
+
162
+ | Parameter | Type | Description |
163
+ |-----------|------|-------------|
164
+ | `cName` | Character | Semaphore name |
165
+ | `lByCompany` | Logical | `.T.` considers company code in the lock |
166
+ | `lByBranch` | Logical | `.T.` considers branch code in the lock |
167
+ | **Return** | Logical | `.T.` if lock was acquired successfully |
168
+
169
+ ### 6.2 UnlockByName Syntax
170
+
171
+ ```advpl
172
+ UnlockByName(cName, lByCompany, lByBranch)
173
+ ```
174
+
175
+ Parameters are the same as `LockByName`. Releases the named semaphore.
176
+
177
+ ### 6.3 Usage Pattern
178
+
179
+ ```advpl
180
+ // Tenta adquirir o semaforo
181
+ If LockByName("PROC_LOTE_001", .F., .F.)
182
+ // Executa processamento exclusivo
183
+ ProcessaLote()
184
+
185
+ // Libera o semaforo
186
+ UnlockByName("PROC_LOTE_001", .F., .F.)
187
+ Else
188
+ ConOut("Processamento ja em execucao por outra thread.")
189
+ EndIf
190
+ ```
191
+
192
+ ---
193
+
194
+ ## 7. Logging
195
+
196
+ ### 7.1 ConOut
197
+
198
+ Writes a message to the Application Server console log:
199
+
200
+ ```advpl
201
+ ConOut("Inicio do processamento: " + DToC(Date()) + " " + Time())
202
+ ConOut("Registro processado: " + cCodigo)
203
+ ConOut("Erro: " + cMsgErro)
204
+ ```
205
+
206
+ ### 7.2 FWLogMsg (Recommended)
207
+
208
+ Standardized logging API that replaces `ConOut` in newer Protheus versions:
209
+
210
+ ```advpl
211
+ FWLogMsg("INFO", /*cTransactionId*/, "MYJOB", /*cCategory*/, /*cStep*/, "001", "Inicio do processamento", /*aMessage*/, /*nLogLevel*/)
212
+ FWLogMsg("ERROR", /*cTransactionId*/, "MYJOB", /*cCategory*/, /*cStep*/, "002", "Erro no processamento: " + cMsgErro, /*aMessage*/, /*nLogLevel*/)
213
+ ```
214
+
215
+ | Parameter | Description |
216
+ |-----------|-------------|
217
+ | 1st | Severity: `"INFO"`, `"ERROR"`, `"WARNING"` |
218
+ | 2nd | Transaction ID (optional) |
219
+ | 3rd | Message grouper/module ID |
220
+ | 4th | Category (optional) |
221
+ | 5th | Step (optional) |
222
+ | 6th | Message code |
223
+ | 7th | Message text |
224
+ | 8th | Array of additional messages (optional) |
225
+ | 9th | Log level (optional) |
226
+
227
+ ---
228
+
229
+ ## 8. Complete Example - Batch Processing Job
230
+
231
+ This example implements a job that processes pending records in batches, with environment setup, semaphore control, and logging.
232
+
233
+ ```advpl
234
+ #Include "TOTVS.CH"
235
+
236
+ /*/{Protheus.doc} zJobLote
237
+ Job de processamento em lote
238
+ @type User Function
239
+ @author Autor
240
+ @since 01/01/2026
241
+ @version 1.0
242
+ /*/
243
+ User Function zJobLote()
244
+ Local cCodEmp := "99"
245
+ Local cCodFil := "01"
246
+
247
+ ConOut("[zJobLote] Iniciando job: " + DToC(Date()) + " " + Time())
248
+
249
+ // Prepara o ambiente
250
+ If !RpcSetEnv(cCodEmp, cCodFil, "admin", " ", "FAT", "zJobLote")
251
+ ConOut("[zJobLote] ERRO: Falha ao preparar ambiente.")
252
+ Return Nil
253
+ EndIf
254
+
255
+ // Controle de concorrencia via semaforo
256
+ If !LockByName("JOB_PROC_LOTE", .F., .F.)
257
+ ConOut("[zJobLote] Job ja em execucao por outra thread. Saindo.")
258
+ RpcClearEnv()
259
+ Return Nil
260
+ EndIf
261
+
262
+ // Executa o processamento
263
+ Begin Sequence
264
+ fProcessaLote()
265
+ Recover
266
+ ConOut("[zJobLote] ERRO: Excecao durante processamento.")
267
+ End Sequence
268
+
269
+ // Libera semaforo e ambiente
270
+ UnlockByName("JOB_PROC_LOTE", .F., .F.)
271
+ RpcClearEnv()
272
+
273
+ ConOut("[zJobLote] Job finalizado: " + DToC(Date()) + " " + Time())
274
+
275
+ Return Nil
276
+
277
+ // =============================================================
278
+ // Processamento em lote
279
+ // =============================================================
280
+ Static Function fProcessaLote()
281
+ Local cAlias := "ZA9"
282
+ Local aArea := GetArea()
283
+ Local nCount := 0
284
+ Local nErrors := 0
285
+
286
+ DbSelectArea(cAlias)
287
+ DbSetOrder(1)
288
+ DbGoTop()
289
+
290
+ While !Eof()
291
+ If xFilial(cAlias) == ZA9->ZA9_FILIAL .And. ZA9->ZA9_STATUS == "1" // Pendente
292
+
293
+ Begin Transaction
294
+ RecLock(cAlias, .F.)
295
+ ZA9->ZA9_STATUS := "2" // Em processamento
296
+ MsUnlock()
297
+
298
+ // Executa logica de negocio
299
+ If fProcessaRegistro(ZA9->ZA9_CODIGO)
300
+ RecLock(cAlias, .F.)
301
+ ZA9->ZA9_STATUS := "3" // Processado
302
+ ZA9->ZA9_DTPROC := Date()
303
+ ZA9->ZA9_HRPROC := Time()
304
+ MsUnlock()
305
+ nCount++
306
+ Else
307
+ RecLock(cAlias, .F.)
308
+ ZA9->ZA9_STATUS := "9" // Erro
309
+ MsUnlock()
310
+ nErrors++
311
+ EndIf
312
+ End Transaction
313
+
314
+ EndIf
315
+
316
+ DbSkip()
317
+ EndWh
318
+
319
+ ConOut("[zJobLote] Processados: " + cValToChar(nCount) + " | Erros: " + cValToChar(nErrors))
320
+
321
+ RestArea(aArea)
322
+
323
+ Return Nil
324
+
325
+ // =============================================================
326
+ // Processa um registro individual
327
+ // =============================================================
328
+ Static Function fProcessaRegistro(cCodigo)
329
+ Local lRet := .T.
330
+
331
+ // Logica de negocio especifica
332
+ ConOut("[zJobLote] Processando registro: " + cCodigo)
333
+
334
+ // ... implementar logica aqui ...
335
+
336
+ Return lRet
337
+ ```
338
+
339
+ ---
340
+
341
+ ## 9. Multi-Thread Processing with StartJob
342
+
343
+ ### 9.1 Dispatcher Pattern
344
+
345
+ A common pattern is to have a dispatcher function that creates multiple worker threads:
346
+
347
+ ```advpl
348
+ #Include "TOTVS.CH"
349
+
350
+ /*/{Protheus.doc} zDispatcher
351
+ Dispatcher que cria multiplas threads de processamento
352
+ @type User Function
353
+ @author Autor
354
+ @since 01/01/2026
355
+ @version 1.0
356
+ /*/
357
+ User Function zDispatcher()
358
+ Local nThreads := 5
359
+ Local nI := 0
360
+
361
+ ConOut("[zDispatcher] Iniciando " + cValToChar(nThreads) + " threads")
362
+
363
+ // Prepara ambiente para o dispatcher
364
+ If !RpcSetEnv("99", "01", "admin", " ", "FAT", "zDispatcher")
365
+ ConOut("[zDispatcher] ERRO: Falha ao preparar ambiente.")
366
+ Return Nil
367
+ EndIf
368
+
369
+ // Inicia threads de processamento
370
+ For nI := 1 To nThreads
371
+ StartJob("U_zWorker", GetEnvServer(), .F., cValToChar(nI))
372
+ ConOut("[zDispatcher] Thread " + cValToChar(nI) + " iniciada.")
373
+ Next nI
374
+
375
+ RpcClearEnv()
376
+
377
+ Return Nil
378
+
379
+ /*/{Protheus.doc} zWorker
380
+ Worker thread para processamento paralelo
381
+ @type User Function
382
+ @author Autor
383
+ @since 01/01/2026
384
+ @version 1.0
385
+ /*/
386
+ User Function zWorker(cThreadId)
387
+ Default cThreadId := "0"
388
+
389
+ ConOut("[zWorker-" + cThreadId + "] Inicio: " + Time())
390
+
391
+ // Cada thread prepara seu proprio ambiente
392
+ If !RpcSetEnv("99", "01", "admin", " ", "FAT", "zWorker")
393
+ ConOut("[zWorker-" + cThreadId + "] ERRO: Falha ao preparar ambiente.")
394
+ Return Nil
395
+ EndIf
396
+
397
+ // Processamento da thread
398
+ fWorkerProcess(cThreadId)
399
+
400
+ RpcClearEnv()
401
+
402
+ ConOut("[zWorker-" + cThreadId + "] Fim: " + Time())
403
+
404
+ Return Nil
405
+
406
+ Static Function fWorkerProcess(cThreadId)
407
+ Local nI := 0
408
+
409
+ // Exemplo: cada thread processa um lote de registros
410
+ For nI := 1 To 100
411
+ // Processamento individual
412
+ ConOut("[zWorker-" + cThreadId + "] Processando item " + cValToChar(nI))
413
+ Next nI
414
+
415
+ Return Nil
416
+ ```
417
+
418
+ ---
419
+
420
+ ## 10. Job with Interval Processing
421
+
422
+ Pattern for a job that runs periodically and processes data in intervals:
423
+
424
+ ```advpl
425
+ #Include "TOTVS.CH"
426
+
427
+ /*/{Protheus.doc} zJobLoop
428
+ Job com processamento em intervalo (controlado por OnStart/RefreshRate)
429
+ @type User Function
430
+ @author Autor
431
+ @since 01/01/2026
432
+ @version 1.0
433
+ /*/
434
+ User Function zJobLoop()
435
+
436
+ ConOut("[zJobLoop] Executando ciclo: " + DToC(Date()) + " " + Time())
437
+
438
+ If !RpcSetEnv("99", "01", "admin", " ", "FAT", "zJobLoop")
439
+ ConOut("[zJobLoop] ERRO: Falha ao preparar ambiente.")
440
+ Return Nil
441
+ EndIf
442
+
443
+ // Controle de concorrencia
444
+ If !LockByName("JOB_LOOP", .F., .F.)
445
+ ConOut("[zJobLoop] Outra instancia em execucao.")
446
+ RpcClearEnv()
447
+ Return Nil
448
+ EndIf
449
+
450
+ // Processa pendencias
451
+ fProcessaPendencias()
452
+
453
+ UnlockByName("JOB_LOOP", .F., .F.)
454
+ RpcClearEnv()
455
+
456
+ ConOut("[zJobLoop] Ciclo finalizado: " + DToC(Date()) + " " + Time())
457
+ // O RefreshRate do appserver.ini controlara a proxima execucao
458
+
459
+ Return Nil
460
+
461
+ Static Function fProcessaPendencias()
462
+ Local cQuery := ""
463
+ Local cAlias := GetNextAlias()
464
+ Local nCount := 0
465
+
466
+ cQuery := "SELECT ZA9_CODIGO, ZA9_DESCRI "
467
+ cQuery += "FROM " + RetSqlName("ZA9") + " ZA9 "
468
+ cQuery += "WHERE ZA9.D_E_L_E_T_ = ' ' "
469
+ cQuery += "AND ZA9_FILIAL = '" + xFilial("ZA9") + "' "
470
+ cQuery += "AND ZA9_STATUS = '1' "
471
+ cQuery += "ORDER BY ZA9_CODIGO "
472
+
473
+ TCQuery cQuery New Alias (cAlias)
474
+
475
+ While !(cAlias)->(Eof())
476
+ ConOut("[zJobLoop] Processando: " + AllTrim((cAlias)->ZA9_CODIGO))
477
+ // ... logica de processamento ...
478
+ nCount++
479
+ (cAlias)->(DbSkip())
480
+ EndWh
481
+
482
+ (cAlias)->(DbCloseArea())
483
+
484
+ ConOut("[zJobLoop] Total processado: " + cValToChar(nCount))
485
+
486
+ Return Nil
487
+ ```
488
+
489
+ ---
490
+
491
+ ## 11. Best Practices
492
+
493
+ ### 11.1 Environment Management
494
+
495
+ - **Always** call `RpcClearEnv()` after `RpcSetEnv()`, even if an error occurs (use `Begin Sequence` / `Recover`).
496
+ - **Never** call `RpcSetEnv()` or `RpcClearEnv()` in routines executed from the Protheus menu (the environment is already managed by the system).
497
+ - Each thread started by `StartJob` must call its own `RpcSetEnv()` to prepare its environment.
498
+
499
+ ### 11.2 Concurrency Control
500
+
501
+ - Use `LockByName()` / `UnlockByName()` to prevent multiple instances of the same job from running simultaneously.
502
+ - Always release the lock in a `Recover` block or ensure it is released even on errors.
503
+
504
+ ### 11.3 Logging
505
+
506
+ - Use `ConOut()` for basic console logging.
507
+ - Prefer `FWLogMsg()` for standardized, structured logging in production environments.
508
+ - Include timestamps and identifiers in log messages for easier debugging.
509
+
510
+ ### 11.4 Error Handling
511
+
512
+ ```advpl
513
+ Begin Sequence
514
+ // Logica de negocio
515
+ fProcessa()
516
+ Recover
517
+ ConOut("[JOB] Erro critico durante processamento: " + DToC(Date()) + " " + Time())
518
+ End Sequence
519
+ ```