Protheus x TReports Parte 2 — Criando um Objeto de Negócio

Vanessa Ruama
TOTVS Developers
Published in
5 min readMay 15

--

Seleção do Objeto de Negócio

Na primeira parte sobre a Integração TReports x Protheus, falei sobre as configurações iniciais (Quem ainda não deu uma olhada, pode acessar aqui!!). Nesta segunda parte irei falar sobre o Objeto de Negócio.

Então, o que seria esse “Objeto de Negócio” citado anteriormente?

Para a integração com o TReports, foi criada uma nova classe na LIB do Protheus para a preparação dos dados do relatório, mas que inclui também a sua regra de negócio, diferente de uma consulta direta no banco.

O Objeto de Negócio oferece uma gama de vantagens, como paginação automática, tratamento dos dados sensíveis (LGPD), contextualização de Grupo de Empresas/Filiais, recursos para adicionar consultas nos campos do relatório e muitos outros…

Neste artigo irei exemplificar essa criação utilizando a classe IntegratedProvider, logo abaixo:

Como se trata de um fonte TLPP temos alguns recursos utilizados, como Namespace e Annotation.

#include "protheus.ch"
#include "totvs.framework.treports.integratedprovider.th"

namespace custom.produtos.fornecedores

@totvsFrameworkTReportsIntegratedProvider(active=.T., team="SIGACOM", tables="SA5", name="Fornecedores x Produtos", country="ALL", release="12.1.2210")

Com o Annotation totvsFrameworkTReportsIntegratedProvider o Framework consegue ‘enxergar’ a sua classe. Além disso, são utilizadas algumas propriedades para controle do Objeto de Negócio.Para mais informações acessar a documentação completa. (Acessar aqui =D)

Métodos básicos para criação da classe:

class ProdFornTReportsBusinessObject from totvs.framework.treports.integratedprovider.IntegratedProvider
public method new() as object
public method getData() as object
public method getSchema() as object

protected data aFields as array
protected data aStruct as array

endclass

Por estarmos criando uma nova classe, a criação de propriedades para a mesma é livre, depende de como você quer desenvolver seu Objeto de Negócio, neste exemplo criei 2 propriedades para controle dos campos que irei utilizar.

Lembrando que sempre deverá ser herdada a classe do Framework totvs.framework.treports.integratedprovider.IntegratedProvider para utilizarmos os métodos disponíveis para construção do objeto de negócio.

Vamos aos métodos declarados…

New

Aqui temos a instância da classe e já aproveito para declarar quais campos irei utilizar, lembrando que isso não é uma regra, o importante é que ao montar a estrutura dos seus campos, os mesmos sejam definidos.

method new() class ProdFornTReportsBusinessObject
_Super:new()
self:appendArea("Compras")
self:setDisplayName("Fornecedores x Produtos")
self:setDescription("Relatório Fornecedores x Produtos com tratamento LGPD")

self:aFields := {"A5_FILIAL", "A5_FORNECE", "A5_LOJA", "A5_NOMEFOR", "A5_PRODUTO", "A5_NOMPROD", "A5_CODPRF"}
self:aStruct := getStruct(self:aFields)

return self

Criei uma função para montar a estrutura dos campos em um array e ser utilizado depois, a mesma poderia ser criada na mão, mas para facilitar, utilizo algumas funções do Framework para esse preenchimento, deixo a função logo abaixo:

function getStruct(aFlds)
Local aConvFld as array
Local aFldTmp as array
Local cCampo as character
Local cFldQry as character
Local cTipR as character
Local nPos as numeric
Local nC as numeric

aConvFld := {{"C", "string"}, {"D", "date"}, {"N", "number"}, {"L", "boolean"}, {"M", "memo"}}
aFldTmp := {}

for nC := 1 to Len(aFlds)
cFldQry := aFlds[nC]
nPos := AT(".", aFlds[nC]) + 1

if nPos > 0
cCampo := Substr(cFldQry, nPos)
else
cCampo := cFldQry
endif

cTipo := GetSx3Cache(cCampo, "X3_TIPO")

if (nPos := aScan(aConvFld, {|c| c[01] = cTipo})) > 0
cTipR := aConvFld[nPos, 02]
else
cTipR := "string"
endif

AAdd(aFldTmp, {cCampo, FWSX3Util():GetDescription(cCampo), cTipR, FwX3Titulo(Upper(cCampo)), cCampo})
next nC

return (aFldTmp)

Lembrando que os campos memos começaram a ser aceitos a partir da LIB Label 20230403 e os mesmos devem ser declarados como memo na estrutura.

GetSchema

Neste método, temos a construção da estrutura dos campos que irão para o relatório. Como anteriormente já construí um array com essa estrutura, agora preciso apenas deixá-la transparente no método disponível da classe herdada.

method getSchema() as object class ProdFornTReportsBusinessObject
Local nX as numeric

for nX := 1 To Len(self:aStruct)
self:addProperty(self:aStruct[nX][1], self:aStruct[nX][2], self:aStruct[nX][3], self:aStruct[nX][4], self:aStruct[nX][5])
Next nX

return self:oSchema

Até aqui já temos nossa classe e nossos campos, o que falta?

GetData

Chegamos à parte mais interessante: getData. Neste método, temos a construção de toda a regra de negócio e nos retorna todos os dados do relatório.

No meu exemplo irei utilizar uma forma ‘manual’ de construção dessa regra de negócio com o tratamento LGPD.

Lembrando que temos outras formas de construção mais simples, onde o Framework cuida da parte de paginação e execução da query. Para acessar alguns exemplos, olhar a documentação da classe.

method getData(nPage as numeric, oFilter as object) as object class ProdFornTReportsBusinessObject
local cQuery as character
local cAlias as character
local nSkip as numeric
local nCount as numeric
local nX as numeric
local jItems as json
local aPDFields as array

nCount := 0
cQuery := "SELECT A5_FILIAL,A5_PRODUTO,A5_NOMPROD,A5_FORNECE,A5_NOMEFOR,A5_LOJA,A5_CODPRF FROM " + RetSQLName("SA5") + " WHERE D_E_L_E_T_ = ' '"

//Os filtros serão setados na interface do novo TReports
if oFilter:hasFilter()
cQuery += " AND " + oFilter:getSQLExpression()
endif

cAlias := MPSysOpenQuery(cQuery)

if nPage == 1
(cAlias)->(dbGoTop())
else
//Encontra a quantidade de itens que irá pular de acordo com a página atual
nSkip := ((nPage - 1) * self:getPageSize())

(cAlias)->(dbSkip(nSkip))
endif

//Verifica se precisa fazer o tratamento para LGPD
aPDFields := FwProtectedDataUtil():UsrAccessPDField(__cUserID, self:aFields)
lObfuscated := len( aPDFields ) != Len(self:aFields)
while !(cAlias)->(Eof())
jItems := JsonObject():new()

for nX := 1 To Len(self:aStruct)
if lObfuscated .and. aScan(aPDFields, self:aStruct[nX][5]) == 0
jItems[self:aStruct[nX][1]] := FwProtectedDataUtil():ValueAsteriskToAnonymize((cAlias)->&(self:aStruct[nX][5]))
else
jItems[self:aStruct[nX][1]] := (cAlias)->&(self:aStruct[nX][5])
endif
next nX

self:oData:appendData(jItems)

(cAlias)->(DBSkip())
nCount++

//Sai do loop quando chegar no tamanho de itens da página
if nCount == self:getPageSize()
exit
endif
enddo

//Se não for o último registro indica que terá próxima página
self:setHasNext(!(cAlias)->(Eof()))

(cAlias)->(DBCloseArea())

return self:oData

Para campos do tipo data deve ser feito uma conversão para o formato aceito pelo TReports (aaaa-mm-ddThh:mm:ssZ), no momento do appendData. É possível fazer isso com a função FwTimeStamp, tipo 6.

O parâmetro nPage nos traz qual a página que o TReports esta pedindo. Através deste parâmetro e do método getPageSize(), conseguimos fazer a lógica para retornar os itens de acordo com a página que foi pedida.

Neste caso como não defini qual a quantidade de itens da requisição, o default é 100. Portanto, serão enviados 100 itens para o TReports por requisição.

O parâmetro oFilter nos traz algumas informações dos filtros que foram definidos pelo usuário no momento de execução do relatório, ou seja, se você colocar um parâmetro no relatório, o que foi preenchido nele vem nesse objeto.

Além disso, utilizei algumas funções para verificar se o usuário que está imprimindo o relatório pode visualizar o conteúdo do campo (tratamento LGPD). Como os usuários são do Protheus, as funções do Frame agilizam essa descoberta.

Parece que chegamos ao fim da construção do Objeto de Negócio.

Trouxe aqui um exemplo ‘simples’ de como seria essa construção. Cada vez que a integração evolui, novos métodos e formas de construção surgem para ajudar os desenvolvedores.

Temos uma documentação completa sobre a classe de integração, com descrição de cada método, com métodos não utilizados aqui, onde é possível adicionar uma consulta ao campo ou até mesmo dizer que é obrigatório o preenchimento de um campo. (Acessar aqui!!)

A parte 2 acaba por aqui, na próxima parte irei mostrar como consumir esse objeto de negócio no TReports criando um relatório.

Veja este fonte completo aqui: ArtefatosTReportsxProtheus

Documentações:

--

--

Vanessa Ruama
TOTVS Developers

sou Dev na TOTVS, trabalho no framework do ERP Protheus. Busco sempre evoluir na minha área e compartilhar meus conhecimentos.