@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,1096 @@
|
|
|
1
|
+
# TLPP Class Templates
|
|
2
|
+
|
|
3
|
+
Templates for creating classes in TLPP (TOTVS Language Plus Plus) for TOTVS Protheus.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Basic Class Syntax
|
|
8
|
+
|
|
9
|
+
A TLPP class declaration includes the class name, properties (`data`), and methods.
|
|
10
|
+
|
|
11
|
+
```tlpp
|
|
12
|
+
#Include "tlpp-core.th"
|
|
13
|
+
|
|
14
|
+
/*/{Protheus.doc} MinhaClasse
|
|
15
|
+
Descricao da classe
|
|
16
|
+
@type Class
|
|
17
|
+
@author Autor
|
|
18
|
+
@since 01/01/2026
|
|
19
|
+
@version 1.0
|
|
20
|
+
/*/
|
|
21
|
+
Class MinhaClasse
|
|
22
|
+
|
|
23
|
+
// Properties
|
|
24
|
+
Data cNome As Character
|
|
25
|
+
Data nValor As Numeric
|
|
26
|
+
Data dData As Date
|
|
27
|
+
Data lAtivo As Logical
|
|
28
|
+
Data aItens As Array
|
|
29
|
+
Data oRelated As Object
|
|
30
|
+
|
|
31
|
+
// Methods
|
|
32
|
+
Method New() Constructor
|
|
33
|
+
Method Destroy()
|
|
34
|
+
Method GetNome()
|
|
35
|
+
Method SetNome(cNome)
|
|
36
|
+
Method Process()
|
|
37
|
+
|
|
38
|
+
EndClass
|
|
39
|
+
|
|
40
|
+
// Constructor
|
|
41
|
+
Method New() Class MinhaClasse
|
|
42
|
+
::cNome := ""
|
|
43
|
+
::nValor := 0
|
|
44
|
+
::dData := Date()
|
|
45
|
+
::lAtivo := .T.
|
|
46
|
+
::aItens := {}
|
|
47
|
+
::oRelated := Nil
|
|
48
|
+
Return Self
|
|
49
|
+
|
|
50
|
+
// Destructor
|
|
51
|
+
Method Destroy() Class MinhaClasse
|
|
52
|
+
::aItens := {}
|
|
53
|
+
::oRelated := Nil
|
|
54
|
+
Return Nil
|
|
55
|
+
|
|
56
|
+
Method GetNome() Class MinhaClasse
|
|
57
|
+
Return ::cNome
|
|
58
|
+
|
|
59
|
+
Method SetNome(cNome) Class MinhaClasse
|
|
60
|
+
::cNome := cNome
|
|
61
|
+
Return Nil
|
|
62
|
+
|
|
63
|
+
Method Process() Class MinhaClasse
|
|
64
|
+
// Business logic
|
|
65
|
+
Conout("Processing: " + ::cNome)
|
|
66
|
+
Return .T.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Usage:**
|
|
70
|
+
```tlpp
|
|
71
|
+
Local oObj := MinhaClasse():New()
|
|
72
|
+
oObj:SetNome("Teste")
|
|
73
|
+
oObj:Process()
|
|
74
|
+
oObj:Destroy()
|
|
75
|
+
FreeObj(oObj)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 2. Class with Constructor and Destructor
|
|
81
|
+
|
|
82
|
+
The constructor (`New`) initializes all properties and returns `Self`. The destructor (`Destroy`) cleans up resources.
|
|
83
|
+
|
|
84
|
+
```tlpp
|
|
85
|
+
Class PedidoVenda
|
|
86
|
+
|
|
87
|
+
Data cNumero As Character
|
|
88
|
+
Data cCliente As Character
|
|
89
|
+
Data cLoja As Character
|
|
90
|
+
Data dEmissao As Date
|
|
91
|
+
Data nTotal As Numeric
|
|
92
|
+
Data aItens As Array
|
|
93
|
+
Data lFinalizado As Logical
|
|
94
|
+
|
|
95
|
+
Method New(cCliente, cLoja) Constructor
|
|
96
|
+
Method Destroy()
|
|
97
|
+
Method AddItem(cProduto, nQuantidade, nPreco)
|
|
98
|
+
Method Finalizar()
|
|
99
|
+
Method GetTotal()
|
|
100
|
+
|
|
101
|
+
EndClass
|
|
102
|
+
|
|
103
|
+
Method New(cCliente, cLoja) Class PedidoVenda
|
|
104
|
+
Default cCliente := ""
|
|
105
|
+
Default cLoja := "01"
|
|
106
|
+
|
|
107
|
+
::cNumero := GetSXENum("SC5", "C5_NUM")
|
|
108
|
+
::cCliente := cCliente
|
|
109
|
+
::cLoja := cLoja
|
|
110
|
+
::dEmissao := Date()
|
|
111
|
+
::nTotal := 0
|
|
112
|
+
::aItens := {}
|
|
113
|
+
::lFinalizado := .F.
|
|
114
|
+
|
|
115
|
+
Return Self
|
|
116
|
+
|
|
117
|
+
Method Destroy() Class PedidoVenda
|
|
118
|
+
If !::lFinalizado
|
|
119
|
+
RollBackSX8()
|
|
120
|
+
EndIf
|
|
121
|
+
::aItens := {}
|
|
122
|
+
Return Nil
|
|
123
|
+
|
|
124
|
+
Method AddItem(cProduto, nQuantidade, nPreco) Class PedidoVenda
|
|
125
|
+
Local aItem := {}
|
|
126
|
+
|
|
127
|
+
If ::lFinalizado
|
|
128
|
+
Conout("Pedido ja finalizado. Nao e possivel adicionar itens.")
|
|
129
|
+
Return .F.
|
|
130
|
+
EndIf
|
|
131
|
+
|
|
132
|
+
aAdd(aItem, cProduto)
|
|
133
|
+
aAdd(aItem, nQuantidade)
|
|
134
|
+
aAdd(aItem, nPreco)
|
|
135
|
+
aAdd(aItem, nQuantidade * nPreco) // Subtotal
|
|
136
|
+
|
|
137
|
+
aAdd(::aItens, aItem)
|
|
138
|
+
::nTotal += nQuantidade * nPreco
|
|
139
|
+
|
|
140
|
+
Return .T.
|
|
141
|
+
|
|
142
|
+
Method Finalizar() Class PedidoVenda
|
|
143
|
+
If Len(::aItens) == 0
|
|
144
|
+
Conout("Pedido sem itens.")
|
|
145
|
+
Return .F.
|
|
146
|
+
EndIf
|
|
147
|
+
|
|
148
|
+
ConfirmSX8()
|
|
149
|
+
::lFinalizado := .T.
|
|
150
|
+
|
|
151
|
+
Return .T.
|
|
152
|
+
|
|
153
|
+
Method GetTotal() Class PedidoVenda
|
|
154
|
+
Return ::nTotal
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 3. Properties (Data Declarations)
|
|
160
|
+
|
|
161
|
+
Properties are declared with `Data` inside the class block. TLPP supports typed properties.
|
|
162
|
+
|
|
163
|
+
```tlpp
|
|
164
|
+
Class Exemplo
|
|
165
|
+
|
|
166
|
+
// Typed properties
|
|
167
|
+
Data cNome As Character // String
|
|
168
|
+
Data nValor As Numeric // Number (integer or float)
|
|
169
|
+
Data dData As Date // Date
|
|
170
|
+
Data lAtivo As Logical // Boolean
|
|
171
|
+
Data aLista As Array // Array
|
|
172
|
+
Data oObjeto As Object // Object reference
|
|
173
|
+
Data bBloco As Block // Code block
|
|
174
|
+
Data xVariante As Variant // Any type
|
|
175
|
+
|
|
176
|
+
// Properties with initial values (set in constructor)
|
|
177
|
+
Method New() Constructor
|
|
178
|
+
|
|
179
|
+
EndClass
|
|
180
|
+
|
|
181
|
+
Method New() Class Exemplo
|
|
182
|
+
::cNome := ""
|
|
183
|
+
::nValor := 0
|
|
184
|
+
::dData := CToD("")
|
|
185
|
+
::lAtivo := .F.
|
|
186
|
+
::aLista := {}
|
|
187
|
+
::oObjeto := Nil
|
|
188
|
+
::bBloco := {|| .T.}
|
|
189
|
+
::xVariante := Nil
|
|
190
|
+
Return Self
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 4. Method Visibility
|
|
196
|
+
|
|
197
|
+
TLPP supports `public`, `private`, and `protected` visibility for methods.
|
|
198
|
+
|
|
199
|
+
```tlpp
|
|
200
|
+
Class ContaBancaria
|
|
201
|
+
|
|
202
|
+
Data cConta As Character
|
|
203
|
+
Data cAgencia As Character
|
|
204
|
+
Data nSaldo As Numeric
|
|
205
|
+
|
|
206
|
+
// Public methods - accessible from anywhere
|
|
207
|
+
Public Method New(cConta, cAgencia) Constructor
|
|
208
|
+
Public Method Depositar(nValor)
|
|
209
|
+
Public Method Sacar(nValor)
|
|
210
|
+
Public Method GetSaldo()
|
|
211
|
+
Public Method GetExtrato()
|
|
212
|
+
|
|
213
|
+
// Private methods - accessible only within this class
|
|
214
|
+
Private Method ValidarValor(nValor)
|
|
215
|
+
Private Method RegistrarMovimento(cTipo, nValor)
|
|
216
|
+
|
|
217
|
+
// Protected methods - accessible in this class and subclasses
|
|
218
|
+
Protected Method CalcularTarifa(nValor)
|
|
219
|
+
|
|
220
|
+
EndClass
|
|
221
|
+
|
|
222
|
+
Public Method New(cConta, cAgencia) Class ContaBancaria
|
|
223
|
+
::cConta := cConta
|
|
224
|
+
::cAgencia := cAgencia
|
|
225
|
+
::nSaldo := 0
|
|
226
|
+
Return Self
|
|
227
|
+
|
|
228
|
+
Public Method Depositar(nValor) Class ContaBancaria
|
|
229
|
+
If !::ValidarValor(nValor)
|
|
230
|
+
Return .F.
|
|
231
|
+
EndIf
|
|
232
|
+
|
|
233
|
+
::nSaldo += nValor
|
|
234
|
+
::RegistrarMovimento("C", nValor) // Credito
|
|
235
|
+
|
|
236
|
+
Return .T.
|
|
237
|
+
|
|
238
|
+
Public Method Sacar(nValor) Class ContaBancaria
|
|
239
|
+
Local nTarifa := ::CalcularTarifa(nValor)
|
|
240
|
+
|
|
241
|
+
If !::ValidarValor(nValor)
|
|
242
|
+
Return .F.
|
|
243
|
+
EndIf
|
|
244
|
+
|
|
245
|
+
If (nValor + nTarifa) > ::nSaldo
|
|
246
|
+
Conout("Saldo insuficiente")
|
|
247
|
+
Return .F.
|
|
248
|
+
EndIf
|
|
249
|
+
|
|
250
|
+
::nSaldo -= (nValor + nTarifa)
|
|
251
|
+
::RegistrarMovimento("D", nValor) // Debito
|
|
252
|
+
|
|
253
|
+
Return .T.
|
|
254
|
+
|
|
255
|
+
Public Method GetSaldo() Class ContaBancaria
|
|
256
|
+
Return ::nSaldo
|
|
257
|
+
|
|
258
|
+
Public Method GetExtrato() Class ContaBancaria
|
|
259
|
+
// Retorna movimentacoes
|
|
260
|
+
Return {}
|
|
261
|
+
|
|
262
|
+
Private Method ValidarValor(nValor) Class ContaBancaria
|
|
263
|
+
If nValor <= 0
|
|
264
|
+
Conout("Valor deve ser positivo")
|
|
265
|
+
Return .F.
|
|
266
|
+
EndIf
|
|
267
|
+
Return .T.
|
|
268
|
+
|
|
269
|
+
Private Method RegistrarMovimento(cTipo, nValor) Class ContaBancaria
|
|
270
|
+
// Gravar movimentacao no banco de dados
|
|
271
|
+
Conout("Movimento: " + cTipo + " Valor: " + cValToChar(nValor))
|
|
272
|
+
Return Nil
|
|
273
|
+
|
|
274
|
+
Protected Method CalcularTarifa(nValor) Class ContaBancaria
|
|
275
|
+
// Tarifa padrao: 0.1% do valor
|
|
276
|
+
Return nValor * 0.001
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## 5. Inheritance
|
|
282
|
+
|
|
283
|
+
Use the `FROM` keyword to inherit from a parent class. Override methods by redeclaring them.
|
|
284
|
+
|
|
285
|
+
```tlpp
|
|
286
|
+
// Base class
|
|
287
|
+
Class Animal
|
|
288
|
+
|
|
289
|
+
Data cNome As Character
|
|
290
|
+
Data cEspecie As Character
|
|
291
|
+
Data nIdade As Numeric
|
|
292
|
+
|
|
293
|
+
Method New(cNome, cEspecie) Constructor
|
|
294
|
+
Method EmitirSom()
|
|
295
|
+
Method GetInfo()
|
|
296
|
+
|
|
297
|
+
EndClass
|
|
298
|
+
|
|
299
|
+
Method New(cNome, cEspecie) Class Animal
|
|
300
|
+
::cNome := cNome
|
|
301
|
+
::cEspecie := cEspecie
|
|
302
|
+
::nIdade := 0
|
|
303
|
+
Return Self
|
|
304
|
+
|
|
305
|
+
Method EmitirSom() Class Animal
|
|
306
|
+
Conout("...")
|
|
307
|
+
Return Nil
|
|
308
|
+
|
|
309
|
+
Method GetInfo() Class Animal
|
|
310
|
+
Return ::cNome + " (" + ::cEspecie + ") - Idade: " + cValToChar(::nIdade)
|
|
311
|
+
|
|
312
|
+
// Derived class
|
|
313
|
+
Class Cachorro FROM Animal
|
|
314
|
+
|
|
315
|
+
Data cRaca As Character
|
|
316
|
+
|
|
317
|
+
Method New(cNome, cRaca) Constructor
|
|
318
|
+
Method EmitirSom() // Override
|
|
319
|
+
|
|
320
|
+
EndClass
|
|
321
|
+
|
|
322
|
+
Method New(cNome, cRaca) Class Cachorro
|
|
323
|
+
// Call parent constructor
|
|
324
|
+
_Super:New(cNome, "Canino")
|
|
325
|
+
::cRaca := cRaca
|
|
326
|
+
Return Self
|
|
327
|
+
|
|
328
|
+
Method EmitirSom() Class Cachorro
|
|
329
|
+
Conout(::cNome + " diz: Au Au!")
|
|
330
|
+
Return Nil
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Usage:**
|
|
334
|
+
```tlpp
|
|
335
|
+
Local oCachorro := Cachorro():New("Rex", "Labrador")
|
|
336
|
+
oCachorro:EmitirSom() // "Rex diz: Au Au!"
|
|
337
|
+
Conout(oCachorro:GetInfo()) // "Rex (Canino) - Idade: 0" (inherited method)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 6. Static Methods
|
|
343
|
+
|
|
344
|
+
Static methods belong to the class itself, not to instances. They can be called without instantiation.
|
|
345
|
+
|
|
346
|
+
```tlpp
|
|
347
|
+
Class MathHelper
|
|
348
|
+
|
|
349
|
+
Static Method Max(nA, nB)
|
|
350
|
+
Static Method Min(nA, nB)
|
|
351
|
+
Static Method Clamp(nValue, nMin, nMax)
|
|
352
|
+
Static Method Round(nValue, nDecimals)
|
|
353
|
+
|
|
354
|
+
EndClass
|
|
355
|
+
|
|
356
|
+
Static Method Max(nA, nB) Class MathHelper
|
|
357
|
+
Return IIf(nA > nB, nA, nB)
|
|
358
|
+
|
|
359
|
+
Static Method Min(nA, nB) Class MathHelper
|
|
360
|
+
Return IIf(nA < nB, nA, nB)
|
|
361
|
+
|
|
362
|
+
Static Method Clamp(nValue, nMin, nMax) Class MathHelper
|
|
363
|
+
If nValue < nMin
|
|
364
|
+
Return nMin
|
|
365
|
+
ElseIf nValue > nMax
|
|
366
|
+
Return nMax
|
|
367
|
+
EndIf
|
|
368
|
+
Return nValue
|
|
369
|
+
|
|
370
|
+
Static Method Round(nValue, nDecimals) Class MathHelper
|
|
371
|
+
Default nDecimals := 2
|
|
372
|
+
Return Round(nValue, nDecimals)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Usage:**
|
|
376
|
+
```tlpp
|
|
377
|
+
Local nMax := MathHelper():Max(10, 20) // 20
|
|
378
|
+
Local nClamped := MathHelper():Clamp(150, 0, 100) // 100
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## 7. Namespace Usage
|
|
384
|
+
|
|
385
|
+
Namespaces organize classes and avoid name conflicts.
|
|
386
|
+
|
|
387
|
+
```tlpp
|
|
388
|
+
#Include "tlpp-core.th"
|
|
389
|
+
|
|
390
|
+
// Declaring a namespace
|
|
391
|
+
namespace custom.vendas
|
|
392
|
+
|
|
393
|
+
// For REST annotations, add: #Include "tlpp-rest.th"
|
|
394
|
+
// Do NOT use "using namespace tlpp.*" -- use .th includes instead
|
|
395
|
+
// "using namespace" is only for custom/project namespaces (see example below)
|
|
396
|
+
|
|
397
|
+
/*/{Protheus.doc} PedidoService
|
|
398
|
+
Servico de pedidos de venda
|
|
399
|
+
@type Class
|
|
400
|
+
@author Autor
|
|
401
|
+
@since 01/01/2026
|
|
402
|
+
@version 1.0
|
|
403
|
+
/*/
|
|
404
|
+
Class PedidoService
|
|
405
|
+
|
|
406
|
+
Method New() Constructor
|
|
407
|
+
Method CriarPedido(oData)
|
|
408
|
+
Method BuscarPedido(cNumero)
|
|
409
|
+
|
|
410
|
+
EndClass
|
|
411
|
+
|
|
412
|
+
Method New() Class PedidoService
|
|
413
|
+
Return Self
|
|
414
|
+
|
|
415
|
+
Method CriarPedido(oData) Class PedidoService
|
|
416
|
+
// Implementation
|
|
417
|
+
Return .T.
|
|
418
|
+
|
|
419
|
+
Method BuscarPedido(cNumero) Class PedidoService
|
|
420
|
+
// Implementation
|
|
421
|
+
Return Nil
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Using a namespaced class:**
|
|
425
|
+
```tlpp
|
|
426
|
+
using namespace custom.vendas
|
|
427
|
+
|
|
428
|
+
Local oService := PedidoService():New()
|
|
429
|
+
oService:CriarPedido(oData)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## 8. Template: Service Class
|
|
435
|
+
|
|
436
|
+
A Service class encapsulates business logic, keeping it separate from controllers and data access.
|
|
437
|
+
|
|
438
|
+
```tlpp
|
|
439
|
+
#Include "tlpp-core.th"
|
|
440
|
+
|
|
441
|
+
/*/{Protheus.doc} ClienteService
|
|
442
|
+
Servico de regras de negocio para clientes
|
|
443
|
+
@type Class
|
|
444
|
+
@author Autor
|
|
445
|
+
@since 01/01/2026
|
|
446
|
+
@version 1.0
|
|
447
|
+
/*/
|
|
448
|
+
Class ClienteService
|
|
449
|
+
|
|
450
|
+
Data oRepository As Object
|
|
451
|
+
Data aErrors As Array
|
|
452
|
+
Data lHasErrors As Logical
|
|
453
|
+
|
|
454
|
+
Method New(oRepository) Constructor
|
|
455
|
+
Method Destroy()
|
|
456
|
+
|
|
457
|
+
// Business logic
|
|
458
|
+
Method CriarCliente(cNome, cCNPJ, cEmail, cTelefone)
|
|
459
|
+
Method AtualizarCliente(cCodigo, cLoja, oData)
|
|
460
|
+
Method InativarCliente(cCodigo, cLoja)
|
|
461
|
+
Method BuscarPorCNPJ(cCNPJ)
|
|
462
|
+
Method ValidarCNPJ(cCNPJ)
|
|
463
|
+
|
|
464
|
+
// Error handling
|
|
465
|
+
Private Method AddError(cMessage)
|
|
466
|
+
Method GetErrors()
|
|
467
|
+
Method HasErrors()
|
|
468
|
+
Method ClearErrors()
|
|
469
|
+
|
|
470
|
+
EndClass
|
|
471
|
+
|
|
472
|
+
Method New(oRepository) Class ClienteService
|
|
473
|
+
::oRepository := oRepository
|
|
474
|
+
::aErrors := {}
|
|
475
|
+
::lHasErrors := .F.
|
|
476
|
+
Return Self
|
|
477
|
+
|
|
478
|
+
Method Destroy() Class ClienteService
|
|
479
|
+
::oRepository := Nil
|
|
480
|
+
::aErrors := {}
|
|
481
|
+
Return Nil
|
|
482
|
+
|
|
483
|
+
Method CriarCliente(cNome, cCNPJ, cEmail, cTelefone) Class ClienteService
|
|
484
|
+
Local lRet := .T.
|
|
485
|
+
Local oCliente := Nil
|
|
486
|
+
|
|
487
|
+
::ClearErrors()
|
|
488
|
+
|
|
489
|
+
// Validacoes
|
|
490
|
+
If Empty(cNome)
|
|
491
|
+
::AddError("Nome e obrigatorio")
|
|
492
|
+
lRet := .F.
|
|
493
|
+
EndIf
|
|
494
|
+
|
|
495
|
+
If Empty(cCNPJ) .Or. !::ValidarCNPJ(cCNPJ)
|
|
496
|
+
::AddError("CNPJ invalido")
|
|
497
|
+
lRet := .F.
|
|
498
|
+
EndIf
|
|
499
|
+
|
|
500
|
+
// Verifica duplicidade
|
|
501
|
+
If lRet
|
|
502
|
+
oCliente := ::BuscarPorCNPJ(cCNPJ)
|
|
503
|
+
If oCliente <> Nil
|
|
504
|
+
::AddError("Ja existe um cliente com este CNPJ")
|
|
505
|
+
lRet := .F.
|
|
506
|
+
EndIf
|
|
507
|
+
EndIf
|
|
508
|
+
|
|
509
|
+
// Persiste
|
|
510
|
+
If lRet
|
|
511
|
+
lRet := ::oRepository:Create(cNome, cCNPJ, cEmail, cTelefone)
|
|
512
|
+
If !lRet
|
|
513
|
+
::AddError("Erro ao gravar cliente no banco de dados")
|
|
514
|
+
EndIf
|
|
515
|
+
EndIf
|
|
516
|
+
|
|
517
|
+
Return lRet
|
|
518
|
+
|
|
519
|
+
Method AtualizarCliente(cCodigo, cLoja, oData) Class ClienteService
|
|
520
|
+
Local lRet := .T.
|
|
521
|
+
|
|
522
|
+
::ClearErrors()
|
|
523
|
+
|
|
524
|
+
// Verifica se cliente existe
|
|
525
|
+
Local oCliente := ::oRepository:FindById(cCodigo, cLoja)
|
|
526
|
+
If oCliente == Nil
|
|
527
|
+
::AddError("Cliente nao encontrado: " + cCodigo + "/" + cLoja)
|
|
528
|
+
Return .F.
|
|
529
|
+
EndIf
|
|
530
|
+
|
|
531
|
+
// Validacoes de negocio
|
|
532
|
+
If oData["cnpj"] <> Nil .And. !::ValidarCNPJ(oData["cnpj"])
|
|
533
|
+
::AddError("CNPJ invalido")
|
|
534
|
+
lRet := .F.
|
|
535
|
+
EndIf
|
|
536
|
+
|
|
537
|
+
If lRet
|
|
538
|
+
lRet := ::oRepository:Update(cCodigo, cLoja, oData)
|
|
539
|
+
EndIf
|
|
540
|
+
|
|
541
|
+
Return lRet
|
|
542
|
+
|
|
543
|
+
Method InativarCliente(cCodigo, cLoja) Class ClienteService
|
|
544
|
+
Local lRet := .T.
|
|
545
|
+
|
|
546
|
+
::ClearErrors()
|
|
547
|
+
|
|
548
|
+
// Verifica se tem pedidos em aberto
|
|
549
|
+
DbSelectArea("SC5")
|
|
550
|
+
DbSetOrder(3) // Cliente + Loja
|
|
551
|
+
If DbSeek(xFilial("SC5") + cCodigo + cLoja)
|
|
552
|
+
While !Eof() .And. SC5->C5_CLIENTE == cCodigo .And. SC5->C5_LOJACLI == cLoja
|
|
553
|
+
If Empty(SC5->C5_NOTA)
|
|
554
|
+
::AddError("Cliente possui pedidos em aberto. Nao e possivel inativar.")
|
|
555
|
+
Return .F.
|
|
556
|
+
EndIf
|
|
557
|
+
DbSkip()
|
|
558
|
+
EndWh
|
|
559
|
+
EndIf
|
|
560
|
+
|
|
561
|
+
If lRet
|
|
562
|
+
lRet := ::oRepository:Inactivate(cCodigo, cLoja)
|
|
563
|
+
EndIf
|
|
564
|
+
|
|
565
|
+
Return lRet
|
|
566
|
+
|
|
567
|
+
Method BuscarPorCNPJ(cCNPJ) Class ClienteService
|
|
568
|
+
Return ::oRepository:FindByCNPJ(cCNPJ)
|
|
569
|
+
|
|
570
|
+
Method ValidarCNPJ(cCNPJ) Class ClienteService
|
|
571
|
+
Local lValid := .T.
|
|
572
|
+
Local cCNPJClean := ""
|
|
573
|
+
Local nI := 0
|
|
574
|
+
|
|
575
|
+
// Remove formatacao
|
|
576
|
+
For nI := 1 To Len(cCNPJ)
|
|
577
|
+
If SubStr(cCNPJ, nI, 1) $ "0123456789"
|
|
578
|
+
cCNPJClean += SubStr(cCNPJ, nI, 1)
|
|
579
|
+
EndIf
|
|
580
|
+
Next nI
|
|
581
|
+
|
|
582
|
+
If Len(cCNPJClean) <> 14
|
|
583
|
+
lValid := .F.
|
|
584
|
+
EndIf
|
|
585
|
+
|
|
586
|
+
// Use CGC(cCNPJClean) for full validation if available
|
|
587
|
+
Return lValid
|
|
588
|
+
|
|
589
|
+
Private Method AddError(cMessage) Class ClienteService
|
|
590
|
+
aAdd(::aErrors, cMessage)
|
|
591
|
+
::lHasErrors := .T.
|
|
592
|
+
Return Nil
|
|
593
|
+
|
|
594
|
+
Method GetErrors() Class ClienteService
|
|
595
|
+
Return ::aErrors
|
|
596
|
+
|
|
597
|
+
Method HasErrors() Class ClienteService
|
|
598
|
+
Return ::lHasErrors
|
|
599
|
+
|
|
600
|
+
Method ClearErrors() Class ClienteService
|
|
601
|
+
::aErrors := {}
|
|
602
|
+
::lHasErrors := .F.
|
|
603
|
+
Return Nil
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**Usage:**
|
|
607
|
+
```tlpp
|
|
608
|
+
Local oRepo := ClienteRepository():New()
|
|
609
|
+
Local oService := ClienteService():New(oRepo)
|
|
610
|
+
|
|
611
|
+
If oService:CriarCliente("Empresa Teste", "12345678000199", "teste@email.com", "11999999999")
|
|
612
|
+
Conout("Cliente criado com sucesso")
|
|
613
|
+
Else
|
|
614
|
+
Local aErros := oService:GetErrors()
|
|
615
|
+
Local nI := 0
|
|
616
|
+
For nI := 1 To Len(aErros)
|
|
617
|
+
Conout("Erro: " + aErros[nI])
|
|
618
|
+
Next nI
|
|
619
|
+
EndIf
|
|
620
|
+
|
|
621
|
+
oService:Destroy()
|
|
622
|
+
FreeObj(oService)
|
|
623
|
+
oRepo:Destroy()
|
|
624
|
+
FreeObj(oRepo)
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
## 9. Template: Repository Class
|
|
630
|
+
|
|
631
|
+
A Repository class encapsulates data access, providing CRUD operations and query methods.
|
|
632
|
+
|
|
633
|
+
```tlpp
|
|
634
|
+
#Include "tlpp-core.th"
|
|
635
|
+
|
|
636
|
+
/*/{Protheus.doc} ClienteRepository
|
|
637
|
+
Repositorio de acesso a dados de clientes (SA1)
|
|
638
|
+
@type Class
|
|
639
|
+
@author Autor
|
|
640
|
+
@since 01/01/2026
|
|
641
|
+
@version 1.0
|
|
642
|
+
/*/
|
|
643
|
+
Class ClienteRepository
|
|
644
|
+
|
|
645
|
+
Data cAlias As Character
|
|
646
|
+
|
|
647
|
+
Method New() Constructor
|
|
648
|
+
Method Destroy()
|
|
649
|
+
|
|
650
|
+
// CRUD methods
|
|
651
|
+
Method FindById(cCodigo, cLoja)
|
|
652
|
+
Method FindByCNPJ(cCNPJ)
|
|
653
|
+
Method FindAll(nPage, nPageSize, cFiltro)
|
|
654
|
+
Method Create(cNome, cCNPJ, cEmail, cTelefone)
|
|
655
|
+
Method Update(cCodigo, cLoja, oData)
|
|
656
|
+
Method Delete(cCodigo, cLoja)
|
|
657
|
+
Method Inactivate(cCodigo, cLoja)
|
|
658
|
+
|
|
659
|
+
// Query helpers
|
|
660
|
+
Private Method BuildQuery(cWhere, cOrderBy, nLimit, nOffset)
|
|
661
|
+
Private Method RecordToJson()
|
|
662
|
+
|
|
663
|
+
EndClass
|
|
664
|
+
|
|
665
|
+
Method New() Class ClienteRepository
|
|
666
|
+
::cAlias := "SA1"
|
|
667
|
+
Return Self
|
|
668
|
+
|
|
669
|
+
Method Destroy() Class ClienteRepository
|
|
670
|
+
Return Nil
|
|
671
|
+
|
|
672
|
+
// Find a single customer by code and store
|
|
673
|
+
Method FindById(cCodigo, cLoja) Class ClienteRepository
|
|
674
|
+
Local oResult := Nil
|
|
675
|
+
Local aArea := GetArea()
|
|
676
|
+
|
|
677
|
+
DbSelectArea(::cAlias)
|
|
678
|
+
DbSetOrder(1) // Filial + Codigo + Loja
|
|
679
|
+
If DbSeek(xFilial(::cAlias) + PadR(cCodigo, TamSX3("A1_COD")[1]) + PadR(cLoja, TamSX3("A1_LOJA")[1]))
|
|
680
|
+
oResult := ::RecordToJson()
|
|
681
|
+
EndIf
|
|
682
|
+
|
|
683
|
+
RestArea(aArea)
|
|
684
|
+
Return oResult
|
|
685
|
+
|
|
686
|
+
// Find customer by CNPJ
|
|
687
|
+
Method FindByCNPJ(cCNPJ) Class ClienteRepository
|
|
688
|
+
Local oResult := Nil
|
|
689
|
+
Local aArea := GetArea()
|
|
690
|
+
Local cQuery := ""
|
|
691
|
+
Local cTmpAlias := GetNextAlias()
|
|
692
|
+
|
|
693
|
+
cQuery := "SELECT A1_COD, A1_LOJA, A1_NOME, A1_CGC, A1_EMAIL, A1_TEL "
|
|
694
|
+
cQuery += "FROM " + RetSqlName("SA1") + " "
|
|
695
|
+
cQuery += "WHERE D_E_L_E_T_ = ' ' "
|
|
696
|
+
cQuery += "AND A1_FILIAL = '" + xFilial("SA1") + "' "
|
|
697
|
+
cQuery += "AND A1_CGC = '" + cCNPJ + "' "
|
|
698
|
+
|
|
699
|
+
TCQuery cQuery New Alias (cTmpAlias)
|
|
700
|
+
|
|
701
|
+
If !(cTmpAlias)->(Eof())
|
|
702
|
+
oResult := JsonObject():New()
|
|
703
|
+
oResult["codigo"] := AllTrim((cTmpAlias)->A1_COD)
|
|
704
|
+
oResult["loja"] := AllTrim((cTmpAlias)->A1_LOJA)
|
|
705
|
+
oResult["nome"] := AllTrim((cTmpAlias)->A1_NOME)
|
|
706
|
+
oResult["cnpj"] := AllTrim((cTmpAlias)->A1_CGC)
|
|
707
|
+
oResult["email"] := AllTrim((cTmpAlias)->A1_EMAIL)
|
|
708
|
+
oResult["telefone"] := AllTrim((cTmpAlias)->A1_TEL)
|
|
709
|
+
EndIf
|
|
710
|
+
|
|
711
|
+
(cTmpAlias)->(DbCloseArea())
|
|
712
|
+
RestArea(aArea)
|
|
713
|
+
|
|
714
|
+
Return oResult
|
|
715
|
+
|
|
716
|
+
// Find all with pagination and optional filter
|
|
717
|
+
Method FindAll(nPage, nPageSize, cFiltro) Class ClienteRepository
|
|
718
|
+
Local aResult := {}
|
|
719
|
+
Local oItem := Nil
|
|
720
|
+
Local cQuery := ""
|
|
721
|
+
Local cTmpAlias := GetNextAlias()
|
|
722
|
+
|
|
723
|
+
Default nPage := 1
|
|
724
|
+
Default nPageSize := 20
|
|
725
|
+
Default cFiltro := ""
|
|
726
|
+
|
|
727
|
+
cQuery := "SELECT A1_COD, A1_LOJA, A1_NOME, A1_CGC, A1_EMAIL, A1_TEL, A1_MSBLQL "
|
|
728
|
+
cQuery += "FROM " + RetSqlName("SA1") + " "
|
|
729
|
+
cQuery += "WHERE D_E_L_E_T_ = ' ' "
|
|
730
|
+
cQuery += "AND A1_FILIAL = '" + xFilial("SA1") + "' "
|
|
731
|
+
|
|
732
|
+
If !Empty(cFiltro)
|
|
733
|
+
cQuery += "AND (A1_NOME LIKE '%" + cFiltro + "%' OR A1_CGC LIKE '%" + cFiltro + "%') "
|
|
734
|
+
EndIf
|
|
735
|
+
|
|
736
|
+
cQuery += "ORDER BY A1_COD, A1_LOJA "
|
|
737
|
+
cQuery += "OFFSET " + cValToChar((nPage - 1) * nPageSize) + " ROWS "
|
|
738
|
+
cQuery += "FETCH NEXT " + cValToChar(nPageSize) + " ROWS ONLY"
|
|
739
|
+
|
|
740
|
+
TCQuery cQuery New Alias (cTmpAlias)
|
|
741
|
+
|
|
742
|
+
While !(cTmpAlias)->(Eof())
|
|
743
|
+
oItem := JsonObject():New()
|
|
744
|
+
oItem["codigo"] := AllTrim((cTmpAlias)->A1_COD)
|
|
745
|
+
oItem["loja"] := AllTrim((cTmpAlias)->A1_LOJA)
|
|
746
|
+
oItem["nome"] := AllTrim((cTmpAlias)->A1_NOME)
|
|
747
|
+
oItem["cnpj"] := AllTrim((cTmpAlias)->A1_CGC)
|
|
748
|
+
oItem["email"] := AllTrim((cTmpAlias)->A1_EMAIL)
|
|
749
|
+
oItem["telefone"] := AllTrim((cTmpAlias)->A1_TEL)
|
|
750
|
+
oItem["bloqueado"] := AllTrim((cTmpAlias)->A1_MSBLQL) == "1"
|
|
751
|
+
aAdd(aResult, oItem)
|
|
752
|
+
(cTmpAlias)->(DbSkip())
|
|
753
|
+
EndWh
|
|
754
|
+
|
|
755
|
+
(cTmpAlias)->(DbCloseArea())
|
|
756
|
+
|
|
757
|
+
Return aResult
|
|
758
|
+
|
|
759
|
+
// Create a new customer
|
|
760
|
+
Method Create(cNome, cCNPJ, cEmail, cTelefone) Class ClienteRepository
|
|
761
|
+
Local lRet := .T.
|
|
762
|
+
Local aArea := GetArea()
|
|
763
|
+
|
|
764
|
+
Begin Transaction
|
|
765
|
+
DbSelectArea(::cAlias)
|
|
766
|
+
RecLock(::cAlias, .T.)
|
|
767
|
+
SA1->A1_FILIAL := xFilial(::cAlias)
|
|
768
|
+
SA1->A1_COD := GetSXENum(::cAlias, "A1_COD")
|
|
769
|
+
SA1->A1_LOJA := "01"
|
|
770
|
+
SA1->A1_NOME := cNome
|
|
771
|
+
SA1->A1_CGC := cCNPJ
|
|
772
|
+
SA1->A1_EMAIL := IIf(!Empty(cEmail), cEmail, "")
|
|
773
|
+
SA1->A1_TEL := IIf(!Empty(cTelefone), cTelefone, "")
|
|
774
|
+
MsUnlock()
|
|
775
|
+
ConfirmSX8()
|
|
776
|
+
End Transaction
|
|
777
|
+
|
|
778
|
+
RestArea(aArea)
|
|
779
|
+
|
|
780
|
+
Return lRet
|
|
781
|
+
|
|
782
|
+
// Update an existing customer
|
|
783
|
+
Method Update(cCodigo, cLoja, oData) Class ClienteRepository
|
|
784
|
+
Local lRet := .T.
|
|
785
|
+
Local aArea := GetArea()
|
|
786
|
+
|
|
787
|
+
DbSelectArea(::cAlias)
|
|
788
|
+
DbSetOrder(1)
|
|
789
|
+
|
|
790
|
+
If !DbSeek(xFilial(::cAlias) + PadR(cCodigo, TamSX3("A1_COD")[1]) + PadR(cLoja, TamSX3("A1_LOJA")[1]))
|
|
791
|
+
RestArea(aArea)
|
|
792
|
+
Return .F.
|
|
793
|
+
EndIf
|
|
794
|
+
|
|
795
|
+
Begin Transaction
|
|
796
|
+
RecLock(::cAlias, .F.)
|
|
797
|
+
If oData["nome"] <> Nil
|
|
798
|
+
SA1->A1_NOME := oData["nome"]
|
|
799
|
+
EndIf
|
|
800
|
+
If oData["cnpj"] <> Nil
|
|
801
|
+
SA1->A1_CGC := oData["cnpj"]
|
|
802
|
+
EndIf
|
|
803
|
+
If oData["email"] <> Nil
|
|
804
|
+
SA1->A1_EMAIL := oData["email"]
|
|
805
|
+
EndIf
|
|
806
|
+
If oData["telefone"] <> Nil
|
|
807
|
+
SA1->A1_TEL := oData["telefone"]
|
|
808
|
+
EndIf
|
|
809
|
+
MsUnlock()
|
|
810
|
+
End Transaction
|
|
811
|
+
|
|
812
|
+
RestArea(aArea)
|
|
813
|
+
|
|
814
|
+
Return lRet
|
|
815
|
+
|
|
816
|
+
// Soft delete (logical deletion)
|
|
817
|
+
Method Delete(cCodigo, cLoja) Class ClienteRepository
|
|
818
|
+
Local lRet := .T.
|
|
819
|
+
Local aArea := GetArea()
|
|
820
|
+
|
|
821
|
+
DbSelectArea(::cAlias)
|
|
822
|
+
DbSetOrder(1)
|
|
823
|
+
|
|
824
|
+
If !DbSeek(xFilial(::cAlias) + PadR(cCodigo, TamSX3("A1_COD")[1]) + PadR(cLoja, TamSX3("A1_LOJA")[1]))
|
|
825
|
+
RestArea(aArea)
|
|
826
|
+
Return .F.
|
|
827
|
+
EndIf
|
|
828
|
+
|
|
829
|
+
Begin Transaction
|
|
830
|
+
RecLock(::cAlias, .F.)
|
|
831
|
+
SA1->D_E_L_E_T_ := "*"
|
|
832
|
+
MsUnlock()
|
|
833
|
+
End Transaction
|
|
834
|
+
|
|
835
|
+
RestArea(aArea)
|
|
836
|
+
|
|
837
|
+
Return lRet
|
|
838
|
+
|
|
839
|
+
// Inactivate customer (set block flag)
|
|
840
|
+
Method Inactivate(cCodigo, cLoja) Class ClienteRepository
|
|
841
|
+
Local lRet := .T.
|
|
842
|
+
Local aArea := GetArea()
|
|
843
|
+
|
|
844
|
+
DbSelectArea(::cAlias)
|
|
845
|
+
DbSetOrder(1)
|
|
846
|
+
|
|
847
|
+
If !DbSeek(xFilial(::cAlias) + PadR(cCodigo, TamSX3("A1_COD")[1]) + PadR(cLoja, TamSX3("A1_LOJA")[1]))
|
|
848
|
+
RestArea(aArea)
|
|
849
|
+
Return .F.
|
|
850
|
+
EndIf
|
|
851
|
+
|
|
852
|
+
Begin Transaction
|
|
853
|
+
RecLock(::cAlias, .F.)
|
|
854
|
+
SA1->A1_MSBLQL := "1"
|
|
855
|
+
MsUnlock()
|
|
856
|
+
End Transaction
|
|
857
|
+
|
|
858
|
+
RestArea(aArea)
|
|
859
|
+
|
|
860
|
+
Return lRet
|
|
861
|
+
|
|
862
|
+
// Build query string helper
|
|
863
|
+
Private Method BuildQuery(cWhere, cOrderBy, nLimit, nOffset) Class ClienteRepository
|
|
864
|
+
Local cQuery := ""
|
|
865
|
+
|
|
866
|
+
Default cWhere := ""
|
|
867
|
+
Default cOrderBy := "A1_COD, A1_LOJA"
|
|
868
|
+
Default nLimit := 0
|
|
869
|
+
Default nOffset := 0
|
|
870
|
+
|
|
871
|
+
cQuery := "SELECT * FROM " + RetSqlName(::cAlias) + " "
|
|
872
|
+
cQuery += "WHERE D_E_L_E_T_ = ' ' "
|
|
873
|
+
cQuery += "AND A1_FILIAL = '" + xFilial(::cAlias) + "' "
|
|
874
|
+
|
|
875
|
+
If !Empty(cWhere)
|
|
876
|
+
cQuery += "AND " + cWhere + " "
|
|
877
|
+
EndIf
|
|
878
|
+
|
|
879
|
+
cQuery += "ORDER BY " + cOrderBy + " "
|
|
880
|
+
|
|
881
|
+
If nLimit > 0
|
|
882
|
+
cQuery += "OFFSET " + cValToChar(nOffset) + " ROWS "
|
|
883
|
+
cQuery += "FETCH NEXT " + cValToChar(nLimit) + " ROWS ONLY"
|
|
884
|
+
EndIf
|
|
885
|
+
|
|
886
|
+
Return cQuery
|
|
887
|
+
|
|
888
|
+
// Convert current SA1 record to JsonObject
|
|
889
|
+
Private Method RecordToJson() Class ClienteRepository
|
|
890
|
+
Local oJson := JsonObject():New()
|
|
891
|
+
|
|
892
|
+
oJson["codigo"] := AllTrim(SA1->A1_COD)
|
|
893
|
+
oJson["loja"] := AllTrim(SA1->A1_LOJA)
|
|
894
|
+
oJson["nome"] := AllTrim(SA1->A1_NOME)
|
|
895
|
+
oJson["cnpj"] := AllTrim(SA1->A1_CGC)
|
|
896
|
+
oJson["email"] := AllTrim(SA1->A1_EMAIL)
|
|
897
|
+
oJson["telefone"] := AllTrim(SA1->A1_TEL)
|
|
898
|
+
oJson["bloqueado"] := AllTrim(SA1->A1_MSBLQL) == "1"
|
|
899
|
+
|
|
900
|
+
Return oJson
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
## 10. Template: DTO Class (Data Transfer Object)
|
|
906
|
+
|
|
907
|
+
A DTO class carries data between layers. It includes serialization (toJson/fromJson) and validation.
|
|
908
|
+
|
|
909
|
+
```tlpp
|
|
910
|
+
#Include "tlpp-core.th"
|
|
911
|
+
|
|
912
|
+
/*/{Protheus.doc} ClienteDTO
|
|
913
|
+
Data Transfer Object para dados de cliente
|
|
914
|
+
@type Class
|
|
915
|
+
@author Autor
|
|
916
|
+
@since 01/01/2026
|
|
917
|
+
@version 1.0
|
|
918
|
+
/*/
|
|
919
|
+
Class ClienteDTO
|
|
920
|
+
|
|
921
|
+
Data cCodigo As Character
|
|
922
|
+
Data cLoja As Character
|
|
923
|
+
Data cNome As Character
|
|
924
|
+
Data cCNPJ As Character
|
|
925
|
+
Data cEmail As Character
|
|
926
|
+
Data cTelefone As Character
|
|
927
|
+
Data cEndereco As Character
|
|
928
|
+
Data cMunicipio As Character
|
|
929
|
+
Data cEstado As Character
|
|
930
|
+
Data cCEP As Character
|
|
931
|
+
Data lAtivo As Logical
|
|
932
|
+
Data aErrors As Array
|
|
933
|
+
|
|
934
|
+
Method New() Constructor
|
|
935
|
+
Method FromJson(oJson)
|
|
936
|
+
Method ToJson()
|
|
937
|
+
Method Validate()
|
|
938
|
+
Method IsValid()
|
|
939
|
+
Method GetErrors()
|
|
940
|
+
|
|
941
|
+
Static Method FromRecord(cAlias)
|
|
942
|
+
|
|
943
|
+
EndClass
|
|
944
|
+
|
|
945
|
+
Method New() Class ClienteDTO
|
|
946
|
+
::cCodigo := ""
|
|
947
|
+
::cLoja := ""
|
|
948
|
+
::cNome := ""
|
|
949
|
+
::cCNPJ := ""
|
|
950
|
+
::cEmail := ""
|
|
951
|
+
::cTelefone := ""
|
|
952
|
+
::cEndereco := ""
|
|
953
|
+
::cMunicipio := ""
|
|
954
|
+
::cEstado := ""
|
|
955
|
+
::cCEP := ""
|
|
956
|
+
::lAtivo := .T.
|
|
957
|
+
::aErrors := {}
|
|
958
|
+
Return Self
|
|
959
|
+
|
|
960
|
+
// Populate DTO from a JsonObject
|
|
961
|
+
Method FromJson(oJson) Class ClienteDTO
|
|
962
|
+
If oJson == Nil
|
|
963
|
+
Return Self
|
|
964
|
+
EndIf
|
|
965
|
+
|
|
966
|
+
If oJson["codigo"] <> Nil
|
|
967
|
+
::cCodigo := oJson["codigo"]
|
|
968
|
+
EndIf
|
|
969
|
+
If oJson["loja"] <> Nil
|
|
970
|
+
::cLoja := oJson["loja"]
|
|
971
|
+
EndIf
|
|
972
|
+
If oJson["nome"] <> Nil
|
|
973
|
+
::cNome := oJson["nome"]
|
|
974
|
+
EndIf
|
|
975
|
+
If oJson["cnpj"] <> Nil
|
|
976
|
+
::cCNPJ := oJson["cnpj"]
|
|
977
|
+
EndIf
|
|
978
|
+
If oJson["email"] <> Nil
|
|
979
|
+
::cEmail := oJson["email"]
|
|
980
|
+
EndIf
|
|
981
|
+
If oJson["telefone"] <> Nil
|
|
982
|
+
::cTelefone := oJson["telefone"]
|
|
983
|
+
EndIf
|
|
984
|
+
If oJson["endereco"] <> Nil
|
|
985
|
+
::cEndereco := oJson["endereco"]
|
|
986
|
+
EndIf
|
|
987
|
+
If oJson["municipio"] <> Nil
|
|
988
|
+
::cMunicipio := oJson["municipio"]
|
|
989
|
+
EndIf
|
|
990
|
+
If oJson["estado"] <> Nil
|
|
991
|
+
::cEstado := oJson["estado"]
|
|
992
|
+
EndIf
|
|
993
|
+
If oJson["cep"] <> Nil
|
|
994
|
+
::cCEP := oJson["cep"]
|
|
995
|
+
EndIf
|
|
996
|
+
If oJson["ativo"] <> Nil
|
|
997
|
+
::lAtivo := oJson["ativo"]
|
|
998
|
+
EndIf
|
|
999
|
+
|
|
1000
|
+
Return Self
|
|
1001
|
+
|
|
1002
|
+
// Serialize DTO to JsonObject
|
|
1003
|
+
Method ToJson() Class ClienteDTO
|
|
1004
|
+
Local oJson := JsonObject():New()
|
|
1005
|
+
|
|
1006
|
+
oJson["codigo"] := ::cCodigo
|
|
1007
|
+
oJson["loja"] := ::cLoja
|
|
1008
|
+
oJson["nome"] := ::cNome
|
|
1009
|
+
oJson["cnpj"] := ::cCNPJ
|
|
1010
|
+
oJson["email"] := ::cEmail
|
|
1011
|
+
oJson["telefone"] := ::cTelefone
|
|
1012
|
+
oJson["endereco"] := ::cEndereco
|
|
1013
|
+
oJson["municipio"] := ::cMunicipio
|
|
1014
|
+
oJson["estado"] := ::cEstado
|
|
1015
|
+
oJson["cep"] := ::cCEP
|
|
1016
|
+
oJson["ativo"] := ::lAtivo
|
|
1017
|
+
|
|
1018
|
+
Return oJson
|
|
1019
|
+
|
|
1020
|
+
// Validate all required fields
|
|
1021
|
+
Method Validate() Class ClienteDTO
|
|
1022
|
+
::aErrors := {}
|
|
1023
|
+
|
|
1024
|
+
If Empty(::cNome)
|
|
1025
|
+
aAdd(::aErrors, "Campo 'nome' e obrigatorio")
|
|
1026
|
+
EndIf
|
|
1027
|
+
|
|
1028
|
+
If Empty(::cCNPJ)
|
|
1029
|
+
aAdd(::aErrors, "Campo 'cnpj' e obrigatorio")
|
|
1030
|
+
ElseIf Len(StrTran(StrTran(StrTran(::cCNPJ, ".", ""), "-", ""), "/", "")) <> 14
|
|
1031
|
+
aAdd(::aErrors, "CNPJ deve conter 14 digitos")
|
|
1032
|
+
EndIf
|
|
1033
|
+
|
|
1034
|
+
If !Empty(::cEmail) .And. At("@", ::cEmail) == 0
|
|
1035
|
+
aAdd(::aErrors, "Email invalido")
|
|
1036
|
+
EndIf
|
|
1037
|
+
|
|
1038
|
+
If !Empty(::cEstado) .And. Len(::cEstado) <> 2
|
|
1039
|
+
aAdd(::aErrors, "Estado deve conter 2 caracteres (UF)")
|
|
1040
|
+
EndIf
|
|
1041
|
+
|
|
1042
|
+
Return Len(::aErrors) == 0
|
|
1043
|
+
|
|
1044
|
+
Method IsValid() Class ClienteDTO
|
|
1045
|
+
Return Len(::aErrors) == 0
|
|
1046
|
+
|
|
1047
|
+
Method GetErrors() Class ClienteDTO
|
|
1048
|
+
Return ::aErrors
|
|
1049
|
+
|
|
1050
|
+
// Static factory: create DTO from a positioned SA1 record
|
|
1051
|
+
Static Method FromRecord(cAlias) Class ClienteDTO
|
|
1052
|
+
Local oDTO := ClienteDTO():New()
|
|
1053
|
+
|
|
1054
|
+
Default cAlias := "SA1"
|
|
1055
|
+
|
|
1056
|
+
oDTO:cCodigo := AllTrim(SA1->A1_COD)
|
|
1057
|
+
oDTO:cLoja := AllTrim(SA1->A1_LOJA)
|
|
1058
|
+
oDTO:cNome := AllTrim(SA1->A1_NOME)
|
|
1059
|
+
oDTO:cCNPJ := AllTrim(SA1->A1_CGC)
|
|
1060
|
+
oDTO:cEmail := AllTrim(SA1->A1_EMAIL)
|
|
1061
|
+
oDTO:cTelefone := AllTrim(SA1->A1_TEL)
|
|
1062
|
+
oDTO:cEndereco := AllTrim(SA1->A1_END)
|
|
1063
|
+
oDTO:cMunicipio := AllTrim(SA1->A1_MUN)
|
|
1064
|
+
oDTO:cEstado := AllTrim(SA1->A1_EST)
|
|
1065
|
+
oDTO:cCEP := AllTrim(SA1->A1_CEP)
|
|
1066
|
+
oDTO:lAtivo := SA1->A1_MSBLQL <> "1"
|
|
1067
|
+
|
|
1068
|
+
Return oDTO
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
**Usage:**
|
|
1072
|
+
```tlpp
|
|
1073
|
+
// From JSON (API request)
|
|
1074
|
+
Local oJson := JsonObject():New()
|
|
1075
|
+
oJson:FromJson(cBody)
|
|
1076
|
+
|
|
1077
|
+
Local oDTO := ClienteDTO():New()
|
|
1078
|
+
oDTO:FromJson(oJson)
|
|
1079
|
+
|
|
1080
|
+
If oDTO:Validate()
|
|
1081
|
+
// Use oDTO data
|
|
1082
|
+
oService:CriarCliente(oDTO:cNome, oDTO:cCNPJ, oDTO:cEmail, oDTO:cTelefone)
|
|
1083
|
+
Else
|
|
1084
|
+
Local aErros := oDTO:GetErrors()
|
|
1085
|
+
// Return validation errors
|
|
1086
|
+
EndIf
|
|
1087
|
+
|
|
1088
|
+
// From database record
|
|
1089
|
+
DbSelectArea("SA1")
|
|
1090
|
+
DbSetOrder(1)
|
|
1091
|
+
If DbSeek(xFilial("SA1") + cCodigo + cLoja)
|
|
1092
|
+
Local oDTOFromDB := ClienteDTO():FromRecord("SA1")
|
|
1093
|
+
Local oRespJson := oDTOFromDB:ToJson()
|
|
1094
|
+
cResponse := oRespJson:ToJson()
|
|
1095
|
+
EndIf
|
|
1096
|
+
```
|