@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,639 @@
1
+ # Protheus SOAP Web Service Patterns
2
+
3
+ Patterns for exposing and consuming SOAP Web Services in TOTVS Protheus. SOAP in Protheus is exclusive to `.prw` files — there is no TLPP equivalent.
4
+
5
+ ---
6
+
7
+ ## 1. Exposing SOAP Web Services (WsService)
8
+
9
+ The `WsService` approach defines SOAP services with typed attributes and methods. The Protheus AppServer automatically generates the WSDL.
10
+
11
+ ### 1.1 Includes
12
+
13
+ ```advpl
14
+ #Include "TOTVS.CH"
15
+ #Include "APWebSrv.ch"
16
+ ```
17
+
18
+ **Both includes are required** for exposing SOAP services.
19
+
20
+ ### 1.2 Service Declaration
21
+
22
+ ```advpl
23
+ #Include "TOTVS.CH"
24
+ #Include "APWebSrv.ch"
25
+
26
+ /*/{Protheus.doc} zWSClientes
27
+ Web Service SOAP para operacoes com clientes
28
+ @type WsService
29
+ @author Autor
30
+ @since 01/01/2026
31
+ @version 1.0
32
+ /*/
33
+ WsService zWSClientes Description "WebService de Clientes"
34
+
35
+ // Atributos de entrada/saida
36
+ WsData cViewRece As String
37
+ WsData cViewSend As String
38
+ WsData cNewRece As String
39
+ WsData cNewSend As String
40
+ WsData cDelRece As String
41
+ WsData cDelSend As String
42
+
43
+ // Metodos expostos
44
+ WsMethod ViewCli Description "Consultar cliente por codigo ou CGC"
45
+ WsMethod NewCli Description "Incluir novo cliente"
46
+ WsMethod DelCli Description "Excluir cliente por codigo"
47
+
48
+ EndWsService
49
+ ```
50
+
51
+ ### 1.3 WsData Types
52
+
53
+ | Type | ADVPL Equivalent | Example |
54
+ |------|-----------------|---------|
55
+ | `String` | Character | `WsData cNome As String` |
56
+ | `Integer` | Numeric (integer) | `WsData nQtd As Integer` |
57
+ | `Float` | Numeric (decimal) | `WsData nValor As Float` |
58
+ | `Boolean` | Logical | `WsData lAtivo As Boolean` |
59
+ | `Date` | Date | `WsData dEmissao As Date` |
60
+
61
+ ### 1.4 WsMethod Syntax
62
+
63
+ ```advpl
64
+ WsMethod <MethodName> WsReceive <param1>, <param2> WsSend <paramOut> WsService <ServiceName>
65
+ ```
66
+
67
+ - `WsReceive` — attributes that the method receives (input)
68
+ - `WsSend` — attribute that the method returns (output)
69
+ - `WsService` — name of the service this method belongs to
70
+ - All parameters must be declared as `WsData` in the service
71
+
72
+ ### 1.5 Method Implementation: Query (ViewCli)
73
+
74
+ ```advpl
75
+ /*/{Protheus.doc} WsMethod ViewCli
76
+ Consulta informacoes de um cliente por codigo ou CGC
77
+ @type WsMethod
78
+ @author Autor
79
+ @since 01/01/2026
80
+ @param cViewRece, Caractere, Codigo ou CGC do cliente
81
+ @return cViewSend, Caractere, JSON com dados do cliente
82
+ /*/
83
+ WsMethod ViewCli WsReceive cViewRece WsSend cViewSend WsService zWSClientes
84
+ Local aArea := FWGetArea()
85
+ Local lRet := .T.
86
+ Local cBusca := AllTrim(::cViewRece)
87
+ Local nIndice := 0
88
+ Local cCGC := ""
89
+ Local cMascCPF := "@R 999.999.999-99"
90
+ Local cMascCNPJ := "@R 99.999.999/9999-99"
91
+ Local jResponse := JsonObject():New()
92
+
93
+ // Normalizar entrada (remover pontuacao)
94
+ cBusca := StrTran(cBusca, ".", "")
95
+ cBusca := StrTran(cBusca, "/", "")
96
+ cBusca := StrTran(cBusca, "-", "")
97
+
98
+ // Determinar indice de busca
99
+ If Len(cBusca) == 14 .Or. Len(cBusca) == 11
100
+ nIndice := 3 // A1_FILIAL + A1_CGC
101
+ Else
102
+ nIndice := 1 // A1_FILIAL + A1_COD + A1_LOJA
103
+ EndIf
104
+
105
+ DbSelectArea("SA1")
106
+ SA1->(DbSetOrder(nIndice))
107
+
108
+ If SA1->(MsSeek(FWxFilial("SA1") + cBusca))
109
+ cCGC := AllTrim(SA1->A1_CGC)
110
+
111
+ jResponse["status"] := "Cliente encontrado"
112
+ jResponse["codigo"] := AllTrim(SA1->A1_COD) + AllTrim(SA1->A1_LOJA)
113
+ jResponse["nome"] := AllTrim(SA1->A1_NOME)
114
+ jResponse["email"] := AllTrim(SA1->A1_EMAIL)
115
+
116
+ If Len(cCGC) == 14
117
+ jResponse["cnpj"] := AllTrim(Transform(cCGC, cMascCNPJ))
118
+ ElseIf Len(cCGC) == 11
119
+ jResponse["cpf"] := AllTrim(Transform(cCGC, cMascCPF))
120
+ EndIf
121
+ Else
122
+ jResponse["status"] := "Cliente nao encontrado com a chave fornecida"
123
+ EndIf
124
+
125
+ ::cViewSend := jResponse:ToJson()
126
+
127
+ FreeObj(jResponse)
128
+ FWRestArea(aArea)
129
+ Return lRet
130
+ ```
131
+
132
+ ### 1.6 Method Implementation: Create (NewCli)
133
+
134
+ ```advpl
135
+ /*/{Protheus.doc} WsMethod NewCli
136
+ Inclui um novo cliente via JSON
137
+ @type WsMethod
138
+ @author Autor
139
+ @since 01/01/2026
140
+ @param cNewRece, Caractere, JSON com dados do cliente
141
+ @return cNewSend, Caractere, JSON com resultado da inclusao
142
+ /*/
143
+ WsMethod NewCli WsReceive cNewRece WsSend cNewSend WsService zWSClientes
144
+ Local aArea := FWGetArea()
145
+ Local lRet := .T.
146
+ Local jRecebe := JsonObject():New()
147
+ Local jResp := JsonObject():New()
148
+ Local cError := ""
149
+ Local aDados := {}
150
+ Local aLogAuto := {}
151
+ Local cErrorLog := ""
152
+ Local nLinha := 0
153
+ Private lMsHelpAuto := .T.
154
+ Private lAutoErrNoFile := .T.
155
+ Private lMsErroAuto := .F.
156
+
157
+ // Parse do JSON de entrada
158
+ cError := jRecebe:FromJson(::cNewRece)
159
+
160
+ If !Empty(cError)
161
+ jResp["errorId"] := "NEW001"
162
+ jResp["error"] := "Erro no parse do JSON"
163
+ jResp["solution"] := "Verifique a estrutura do JSON enviado"
164
+ ::cNewSend := jResp:ToJson()
165
+ FreeObj(jRecebe)
166
+ FreeObj(jResp)
167
+ FWRestArea(aArea)
168
+ Return lRet
169
+ EndIf
170
+
171
+ // Validacao de campos obrigatorios
172
+ If Empty(jRecebe:GetJsonObject("cod")) .Or. ;
173
+ Empty(jRecebe:GetJsonObject("loja")) .Or. ;
174
+ Empty(jRecebe:GetJsonObject("nome")) .Or. ;
175
+ Empty(jRecebe:GetJsonObject("tipo"))
176
+
177
+ jResp["errorId"] := "NEW002"
178
+ jResp["error"] := "Campos obrigatorios ausentes"
179
+ jResp["solution"] := "Envie: cod, loja, nome, tipo"
180
+ ::cNewSend := jResp:ToJson()
181
+ FreeObj(jRecebe)
182
+ FreeObj(jResp)
183
+ FWRestArea(aArea)
184
+ Return lRet
185
+ EndIf
186
+
187
+ // Montar array para MsExecAuto
188
+ aAdd(aDados, {"A1_COD", jRecebe:GetJsonObject("cod"), Nil})
189
+ aAdd(aDados, {"A1_LOJA", jRecebe:GetJsonObject("loja"), Nil})
190
+ aAdd(aDados, {"A1_NOME", jRecebe:GetJsonObject("nome"), Nil})
191
+ aAdd(aDados, {"A1_TIPO", jRecebe:GetJsonObject("tipo"), Nil})
192
+
193
+ If !Empty(jRecebe:GetJsonObject("end"))
194
+ aAdd(aDados, {"A1_END", jRecebe:GetJsonObject("end"), Nil})
195
+ EndIf
196
+ If !Empty(jRecebe:GetJsonObject("mun"))
197
+ aAdd(aDados, {"A1_MUN", jRecebe:GetJsonObject("mun"), Nil})
198
+ EndIf
199
+ If !Empty(jRecebe:GetJsonObject("est"))
200
+ aAdd(aDados, {"A1_EST", jRecebe:GetJsonObject("est"), Nil})
201
+ EndIf
202
+
203
+ // Executar inclusao via ExecAuto
204
+ MsExecAuto({|x, y| CRMA980(x, y)}, aDados, 3)
205
+
206
+ If lMsErroAuto
207
+ aLogAuto := GetAutoGrLog()
208
+ For nLinha := 1 To Len(aLogAuto)
209
+ cErrorLog += aLogAuto[nLinha] + CRLF
210
+ Next nLinha
211
+
212
+ Conout("zWSClientes:NewCli - Erro: " + cErrorLog)
213
+
214
+ jResp["errorId"] := "NEW003"
215
+ jResp["error"] := "Erro na inclusao do registro"
216
+ jResp["solution"] := cErrorLog
217
+ Else
218
+ jResp["note"] := "Registro incluido com sucesso"
219
+ EndIf
220
+
221
+ ::cNewSend := jResp:ToJson()
222
+
223
+ FreeObj(jRecebe)
224
+ FreeObj(jResp)
225
+ FWRestArea(aArea)
226
+ Return lRet
227
+ ```
228
+
229
+ ### 1.7 Method Implementation: Delete (DelCli)
230
+
231
+ ```advpl
232
+ /*/{Protheus.doc} WsMethod DelCli
233
+ Exclui um cliente por codigo
234
+ @type WsMethod
235
+ @author Autor
236
+ @since 01/01/2026
237
+ @param cDelRece, Caractere, Codigo do cliente (cod+loja)
238
+ @return cDelSend, Caractere, JSON com resultado da exclusao
239
+ /*/
240
+ WsMethod DelCli WsReceive cDelRece WsSend cDelSend WsService zWSClientes
241
+ Local aArea := FWGetArea()
242
+ Local lRet := .T.
243
+ Local cBusca := AllTrim(::cDelRece)
244
+ Local jResp := JsonObject():New()
245
+ Local aDados := {}
246
+ Private lMsHelpAuto := .T.
247
+ Private lAutoErrNoFile := .T.
248
+ Private lMsErroAuto := .F.
249
+
250
+ DbSelectArea("SA1")
251
+ SA1->(DbSetOrder(1))
252
+
253
+ If !SA1->(MsSeek(FWxFilial("SA1") + cBusca))
254
+ jResp["errorId"] := "DEL001"
255
+ jResp["error"] := "Cliente nao encontrado: " + cBusca
256
+ ::cDelSend := jResp:ToJson()
257
+ FreeObj(jResp)
258
+ FWRestArea(aArea)
259
+ Return lRet
260
+ EndIf
261
+
262
+ aAdd(aDados, {"A1_COD", SA1->A1_COD, Nil})
263
+ aAdd(aDados, {"A1_LOJA", SA1->A1_LOJA, Nil})
264
+
265
+ MsExecAuto({|x, y| CRMA980(x, y)}, aDados, 5)
266
+
267
+ If lMsErroAuto
268
+ jResp["errorId"] := "DEL002"
269
+ jResp["error"] := "Erro na exclusao do registro"
270
+ Else
271
+ jResp["note"] := "Registro excluido com sucesso"
272
+ EndIf
273
+
274
+ ::cDelSend := jResp:ToJson()
275
+
276
+ FreeObj(jResp)
277
+ FWRestArea(aArea)
278
+ Return lRet
279
+ ```
280
+
281
+ ### 1.8 Accessing the WSDL
282
+
283
+ After compiling the service and starting the AppServer, the WSDL is available at:
284
+
285
+ ```
286
+ http://<server>:<port>/ws/<ServiceName>.apw?WSDL
287
+ ```
288
+
289
+ Example:
290
+ ```
291
+ http://localhost:8091/ws/ZWSCLIENTES.apw?WSDL
292
+ ```
293
+
294
+ The WSDL index page listing all services:
295
+ ```
296
+ http://localhost:8091/ws/WSINDEX.apw
297
+ ```
298
+
299
+ ### 1.9 appserver.ini Configuration
300
+
301
+ ```ini
302
+ [HTTPSERVER]
303
+ Enable=1
304
+ Port=8091
305
+
306
+ [HTTPENV]
307
+ Environment=ENVIRONMENT_NAME
308
+ ```
309
+
310
+ ---
311
+
312
+ ## 2. Consuming SOAP Web Services (TWsdlManager)
313
+
314
+ Use `TWsdlManager` to call external SOAP services from ADVPL.
315
+
316
+ ### 2.1 Include
317
+
318
+ ```advpl
319
+ #Include "TOTVS.CH"
320
+ ```
321
+
322
+ Only `TOTVS.CH` is needed — `TWsdlManager` is a native Protheus class.
323
+
324
+ ### 2.2 Basic Flow: Load WSDL, Call Operation, Read Response
325
+
326
+ ```advpl
327
+ #Include "TOTVS.CH"
328
+
329
+ /*/{Protheus.doc} ConsumeWS
330
+ Exemplo de consumo de Web Service SOAP via TWsdlManager
331
+ @type User Function
332
+ @author Autor
333
+ @since 01/01/2026
334
+ @version 1.0
335
+ /*/
336
+ User Function ConsumeWS()
337
+ Local oWsdl := TWsdlManager():New()
338
+ Local cUrl := "http://127.0.0.1:8091/ws/ZWSCLIENTES.apw?WSDL"
339
+ Local cResp := ""
340
+ Local cMsgWs := ""
341
+ Local lRet := .F.
342
+
343
+ // Configurar timeout (segundos)
344
+ oWsdl:nTimeout := 120
345
+
346
+ // Desabilitar verificacao de certificado SSL (apenas para testes)
347
+ oWsdl:bNoCheckPeerCert := .T.
348
+
349
+ // Carregar WSDL
350
+ If !oWsdl:ParseURL(cUrl)
351
+ Conout("Erro ao carregar WSDL: " + oWsdl:cError)
352
+ Return lRet
353
+ EndIf
354
+
355
+ // Selecionar operacao
356
+ If !oWsdl:SetOperation("VIEWCLI")
357
+ Conout("Erro ao selecionar operacao: " + oWsdl:cError)
358
+ Return lRet
359
+ EndIf
360
+
361
+ // Montar envelope SOAP
362
+ cMsgWs := '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://localhost/">'
363
+ cMsgWs += ' <soapenv:Header/>'
364
+ cMsgWs += ' <soapenv:Body>'
365
+ cMsgWs += ' <ws:VIEWCLI>'
366
+ cMsgWs += ' <ws:CVIEWRECE>000001</ws:CVIEWRECE>'
367
+ cMsgWs += ' </ws:VIEWCLI>'
368
+ cMsgWs += ' </soapenv:Body>'
369
+ cMsgWs += '</soapenv:Envelope>'
370
+
371
+ // Enviar requisicao
372
+ If oWsdl:SendSoapMsg(cMsgWs)
373
+ cResp := oWsdl:GetParsedResponse()
374
+
375
+ If !Empty(cResp)
376
+ Conout("Resposta: " + cResp)
377
+ lRet := .T.
378
+ Else
379
+ Conout("Resposta vazia")
380
+ EndIf
381
+ Else
382
+ Conout("Erro ao enviar SOAP: " + oWsdl:cError)
383
+ EndIf
384
+
385
+ Return lRet
386
+ ```
387
+
388
+ ### 2.3 Listing Available Operations
389
+
390
+ ```advpl
391
+ User Function ListOps()
392
+ Local oWsdl := TWsdlManager():New()
393
+ Local cUrl := "http://127.0.0.1:8091/ws/ZWSCLIENTES.apw?WSDL"
394
+ Local aOps := {}
395
+ Local nI := 0
396
+
397
+ oWsdl:nTimeout := 60
398
+
399
+ If !oWsdl:ParseURL(cUrl)
400
+ Conout("Erro: " + oWsdl:cError)
401
+ Return
402
+ EndIf
403
+
404
+ // Listar operacoes do servico
405
+ Conout("Operacoes disponiveis:")
406
+ aOps := oWsdl:ListOperations()
407
+ For nI := 1 To Len(aOps)
408
+ Conout(" " + aOps[nI])
409
+ Next nI
410
+
411
+ Return
412
+ ```
413
+
414
+ ### 2.4 Error Handling
415
+
416
+ ```advpl
417
+ User Function ConsumeWSSafe()
418
+ Local oWsdl := TWsdlManager():New()
419
+ Local cUrl := "http://api.externa.com/service?WSDL"
420
+ Local cResp := ""
421
+ Local cFault := ""
422
+ Local cMsgWs := ""
423
+
424
+ oWsdl:nTimeout := 30
425
+ oWsdl:bNoCheckPeerCert := .T.
426
+
427
+ // Carregar WSDL com tratamento
428
+ If !oWsdl:ParseURL(cUrl)
429
+ FWLogMsg("ERROR", , "ConsumeWS", "ParseURL", , , ;
430
+ "Falha ao carregar WSDL: " + cUrl + " - " + oWsdl:cError)
431
+ Return .F.
432
+ EndIf
433
+
434
+ If !oWsdl:SetOperation("OPERACAO")
435
+ FWLogMsg("ERROR", , "ConsumeWS", "SetOperation", , , ;
436
+ "Operacao nao encontrada: OPERACAO")
437
+ Return .F.
438
+ EndIf
439
+
440
+ // Montar e enviar
441
+ cMsgWs := fMontaEnvelope()
442
+
443
+ If !oWsdl:SendSoapMsg(cMsgWs)
444
+ FWLogMsg("ERROR", , "ConsumeWS", "SendSoapMsg", , , ;
445
+ "Falha no envio SOAP: " + oWsdl:cError)
446
+ Return .F.
447
+ EndIf
448
+
449
+ // Verificar SOAP Fault via propriedades
450
+ If !Empty(oWsdl:cFaultCode)
451
+ FWLogMsg("ERROR", , "ConsumeWS", "SoapFault", , , ;
452
+ "SOAP Fault: [" + oWsdl:cFaultCode + "] " + oWsdl:cFaultString)
453
+ Return .F.
454
+ EndIf
455
+
456
+ // Ler resposta
457
+ cResp := oWsdl:GetParsedResponse()
458
+
459
+ If Empty(cResp)
460
+ FWLogMsg("WARN", , "ConsumeWS", "Response", , , ;
461
+ "Resposta vazia do servico")
462
+ Return .F.
463
+ EndIf
464
+
465
+ // Processar resposta (pode ser XML ou texto)
466
+ Conout("Resposta: " + cResp)
467
+
468
+ Return .T.
469
+
470
+ Static Function fMontaEnvelope()
471
+ Local cMsg := ""
472
+ cMsg := '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://target/">'
473
+ cMsg += ' <soapenv:Header/>'
474
+ cMsg += ' <soapenv:Body>'
475
+ cMsg += ' <ws:OPERACAO>'
476
+ cMsg += ' <ws:PARAMETRO>valor</ws:PARAMETRO>'
477
+ cMsg += ' </ws:OPERACAO>'
478
+ cMsg += ' </soapenv:Body>'
479
+ cMsg += '</soapenv:Envelope>'
480
+ Return cMsg
481
+ ```
482
+
483
+ ### 2.5 TWsdlManager Key Properties and Methods
484
+
485
+ | Property/Method | Type | Description |
486
+ |-----------------|------|-------------|
487
+ | `nTimeout` | Property | Timeout in seconds (default: 120) |
488
+ | `bNoCheckPeerCert` | Property | `.T.` to skip SSL certificate validation |
489
+ | `cError` | Property | Last error message |
490
+ | `ParseURL(cUrl)` | Method | Load WSDL from URL. Returns `.T.`/`.F.` |
491
+ | `ParseFile(cPath)` | Method | Load WSDL from local file. Returns `.T.`/`.F.` |
492
+ | `SetOperation(cOp)` | Method | Select the operation to call. Returns `.T.`/`.F.` |
493
+ | `SendSoapMsg(cXml)` | Method | Send SOAP envelope. Returns `.T.`/`.F.` |
494
+ | `GetParsedResponse()` | Method | Get the parsed response body |
495
+ | `GetSoapResponse()` | Method | Get the raw SOAP XML response |
496
+ | `GetSoapMsg()` | Method | Get the SOAP message that will be/was sent |
497
+ | `ListOperations()` | Method | List operations for the current service |
498
+ | `SetPort(cPort)` | Method | Select a service port from the WSDL |
499
+ | `SetValue(cParam, cValue)` | Method | Set input parameter value |
500
+ | `SimpleInput(cField)` | Method | Navigate to a simple input field |
501
+ | `cFaultCode` | Property | SOAP Fault code (check after SendSoapMsg) |
502
+ | `cFaultSubCode` | Property | SOAP Fault sub-code |
503
+ | `cFaultString` | Property | SOAP Fault description message |
504
+ | `cFaultActor` | Property | SOAP Fault actor |
505
+ | `lProcResp` | Property | `.T.` to auto-process response (default `.T.`) |
506
+
507
+ ---
508
+
509
+ ## 3. SOAP vs REST — When to Use Each
510
+
511
+ | Aspect | SOAP (WsService) | REST (WsRestFul / TLPP) |
512
+ |--------|-------------------|------------------------|
513
+ | Protocol | XML over HTTP | JSON over HTTP |
514
+ | Contract | WSDL (strict) | Documentation (flexible) |
515
+ | Use case | Enterprise integration, legacy systems, NFe/NFSe | Modern APIs, mobile, SPA |
516
+ | Complexity | Higher (XML, envelope, WSDL) | Lower (JSON, HTTP verbs) |
517
+ | Performance | Heavier (XML parsing) | Lighter (JSON) |
518
+ | TLPP support | No (`.prw` only) | Yes (`.prw` and `.tlpp`) |
519
+ | Error format | SOAP Fault (XML) | HTTP status + JSON |
520
+ | When to choose | Integrating with systems that require SOAP (banks, government, NFe) | New APIs, internal integration, mobile apps |
521
+
522
+ **Rule of thumb:** Use REST for new development. Use SOAP only when the external system requires it.
523
+
524
+ ---
525
+
526
+ ## 4. Common Errors and Troubleshooting
527
+
528
+ ### 4.1 WSDL Not Found (404)
529
+
530
+ **Error:** `http://server:port/ws/SERVICE.apw?WSDL` returns 404
531
+
532
+ **Causes:**
533
+ - Service not compiled into RPO
534
+ - `[HTTPSERVER]` not configured in `appserver.ini`
535
+ - Service name in URL does not match `WsService` declaration (case-sensitive)
536
+
537
+ **Solution:**
538
+ ```ini
539
+ ; appserver.ini
540
+ [HTTPSERVER]
541
+ Enable=1
542
+ Port=8091
543
+ ```
544
+ Recompile the `.prw` file and restart the AppServer.
545
+
546
+ ### 4.2 ParseURL Fails
547
+
548
+ **Error:** `oWsdl:ParseURL()` returns `.F.`
549
+
550
+ **Causes:**
551
+ - Network unreachable or timeout
552
+ - Invalid WSDL URL
553
+ - SSL certificate issue
554
+
555
+ **Solution:**
556
+ ```advpl
557
+ // Increase timeout
558
+ oWsdl:nTimeout := 300
559
+
560
+ // Disable SSL check for testing
561
+ oWsdl:bNoCheckPeerCert := .T.
562
+
563
+ // Try loading from local file instead
564
+ If !oWsdl:ParseURL(cUrl)
565
+ Conout("ParseURL failed: " + oWsdl:cError)
566
+ // Save WSDL manually and use ParseFile
567
+ // oWsdl:ParseFile("C:\temp\service.wsdl")
568
+ EndIf
569
+ ```
570
+
571
+ ### 4.3 SOAP Fault Response
572
+
573
+ **Error:** The service returns a SOAP Fault instead of the expected response.
574
+
575
+ **Solution:**
576
+ ```advpl
577
+ If oWsdl:SendSoapMsg(cMsgWs)
578
+ // Check SOAP Fault via properties (NOT via GetSoapFault which does not exist)
579
+ If !Empty(oWsdl:cFaultCode)
580
+ Conout("SOAP Fault Code: " + oWsdl:cFaultCode)
581
+ Conout("SOAP Fault Msg: " + oWsdl:cFaultString)
582
+ // Also check the raw response for details
583
+ Conout("Raw XML: " + oWsdl:GetSoapResponse())
584
+ Else
585
+ cResp := oWsdl:GetParsedResponse()
586
+ EndIf
587
+ EndIf
588
+ ```
589
+
590
+ ### 4.4 Empty Response
591
+
592
+ **Error:** `GetParsedResponse()` returns empty string.
593
+
594
+ **Causes:**
595
+ - Wrong operation name
596
+ - Missing or wrong parameters in the envelope
597
+ - Server error not returned as SOAP Fault
598
+
599
+ **Solution:**
600
+ ```advpl
601
+ // Check the raw SOAP response
602
+ Local cRawXml := oWsdl:GetSoapMsg()
603
+ Conout("Raw response: " + cRawXml)
604
+
605
+ // Verify operation name matches exactly (case-sensitive)
606
+ // Verify namespace in envelope matches the WSDL target namespace
607
+ ```
608
+
609
+ ### 4.5 SSL Certificate Error
610
+
611
+ **Error:** Connection fails with SSL/TLS error.
612
+
613
+ **Solution:**
614
+ ```advpl
615
+ // For testing only — disable certificate verification
616
+ oWsdl:bNoCheckPeerCert := .T.
617
+
618
+ // For production — configure certificates in appserver.ini:
619
+ // [SSLConfigure]
620
+ // CertificateClient=cert\client.pem
621
+ // KeyClient=cert\client.key
622
+ ```
623
+
624
+ ---
625
+
626
+ ## 5. Best Practices
627
+
628
+ | Practice | Description |
629
+ |----------|-------------|
630
+ | Always use `FWGetArea()` / `FWRestArea()` | Preserve work area state in every WsMethod |
631
+ | Validate input before processing | Check required fields and data format |
632
+ | Return structured responses | Use JSON via `JsonObject` for consistent output |
633
+ | Log errors with context | Use `FWLogMsg` or `Conout` with service/method name |
634
+ | Set appropriate timeout | Default 120s may be too long or short — adjust per service |
635
+ | Handle SOAP Faults explicitly | Always check `cFaultCode`/`cFaultString` properties after `SendSoapMsg()` |
636
+ | Use `MsExecAuto` for data operations | Triggers standard Protheus validations and workflows |
637
+ | Do NOT use `Private` variables in new code | Exception: `lMsHelpAuto`, `lAutoErrNoFile`, `lMsErroAuto` required by `MsExecAuto` |
638
+ | Name services with `z` prefix | Convention for custom services: `zWSClientes`, `zWSPedidos` |
639
+ | Keep methods focused | One operation per method — avoid methods that do multiple things |