Olá, passei um tempo tentando aprender a desenvolver APIs customizadas no padrão da TOTVS para executar telas em HTML no ERP Datasul, más tive muita dificuldade em encontrar conteúdo claro e prático sobre o assunto na internet.
Por fim o pessoal da comunidade 4each.com.br me convenceu a usar os recursos que já tenho algum conhecimento.
Então vou tentar escrever um artigo onde teremos uma API com método GET bem simples só para listar as cidades cadastradas.
E depois vou tentar complementar fazendo uma tela de consulta com Angular e PO-UI.
Sobre APIs e telas em HTML customizadas Datasul caso esteja interessado em desenvolver as APIs e telas em HTML customizadas para o ERP Datasul segue abaixo um compilado de links que encontrei e considerei útil:
- Guia desenvolvimento TOTVS:
- Introdução ao Angular:
- Construindo Web API para o Datasul em Progress:
- Construindo aplicações web para o Datasul em THF :
- Observação: Foi substituído pelo https://po-ui.io/
- https://www.facebook.com/totvsdevelopers/videos/1713104225447659
- Cadastrando uma nova aplicação web no menu Datasul:
- Documentação da técnica no TDN:
- Documentação do Portinari:
- Modelo Angular e cadastrar no menu:
- Artigos desenvolvimento com THF:
- Página de APIs da Totvs:
- Consumir API Totvs e criar tela CRUD:
Configurar WebSpeed
Antes de iniciar, eu trabalho com o WebSpeed rodando no IIS, sempre que preciso configurar algo recorro a esse guia da Progress e da TOTVS:
https://knowledgebase.progress.com/articles/Article/P125146
https://tdn.totvs.com/pages/releaseview.action?pageId=189317595
O código
O fonte gerado para este exemplo está disponível no GitHub:
https://github.com/fabianofss/OpenEdge_Progress/blob/main/API/api_cidades.p
Agora vou explicar alguns pontos em detalhes.
Iniciamos o código importando uma classe da Progress para manipular Json
USING Progress.Json.ObjectModel.*.
Definimos algumas variáveis que vamos usar para pegar os parâmetros passado pela URL:
define variable p_filtro as logical no-undo.
define variable p_cidade as character no-undo.
…
vamos definir uma tabela temporária para manipular os dados que serão retornados pela API observe que para as colunas que possuem nome muito longo ou palavras separadas por traço usei o serialize-name para ajustar o nome do campo no Json que será gerado.
Também criei os campos CallBack e mgsRetorno para retornar uma mensagem caso aconteça algum erro previsto.
define temp-table ttCidade
field cidade like mgcad.cidade.cidade
field sigla like mgcad.cidade.sigla
field estado like mgcad.cidade.estado
field pais like mgcad.cidade.pais
field cdn-munpio-ibge like mgcad.cidade.cdn-munpio-ibge serialize-name "cdnMunpioIbge"
field CallBack as character
field mgsRetorno as character.
Aqui estamos lendo os parâmetros e populando as variáveis:
PROCEDURE LerParametros :
assign
p_cidade = get-value(trim("cidade"))
p_sigla = get-value(trim("sigla"))
p_estado = get-value(trim("estado"))
p_pais = get-value(trim("pais"))
p_filtro = logical(get-value(trim("filtro"))).
Vamos usar o parâmetro p_filtro para controle, se ele for false então buscamos todas as cidades,se for true vamos fazer o filtro das cidades.
A procedure outputHeader usamos para configurar o header da resposta da API, se o Access-Control-Allow-Origin não for configurado pode gerar erro no Angular:
PROCEDURE outputHeader :
output-http-header("Access-Control-Allow-Origin", "*").
output-content-type ("application/json":U).
END PROCEDURE.
E process fazemos uso dos parâmetros capturados pela URL realizamos as operações
PROCEDURE process :
if p_filtro = false then do:
for each mgcad.cidade
no-lock:
create ttCidade.
buffer-copy mgcad.cidade to ttCidade.
end.
end.
else do:
/* Buscar cidade pelo nome */
if p_cidade <> "" then do:
find first mgcad.cidade no-lock
where mgcad.cidade.cidade = p_cidade
no-error.
if available mgcad.cidade then do:
create ttCidade.
buffer-copy mgcad.cidade to ttCidade.
end.
end.
Usamos o CallBack e o mgsRetorno para retornar um erro caso nenhuma cidade seja localizada.
if not available ttCidade then do:
create ttCidade.
assign
ttCidade.CallBack = "erro"
ttCidade.mgsRetorno = "Nenhuma cidade localizada!".
next.
end.
Criamos a função LongcharToObject para converter um Lonhchar em Objeto Json, vamos usar este objeto para concatenar em outro objeto antes de dar o retorno da API:
FUNCTION LongcharToObject RETURNS JsonObject ( input jsonChar as longchar ) :
/* função que transforma um longchar em objeto json */
define variable jsonInput as JsonObject no-undo.
define variable objParse as ObjectModelParser no-undo.
define variable jsonOutput as JsonObject no-undo.
assign
jsonOutput = new JsonObject()
objParse = new ObjectModelParser()
jsonOutput = cast(objParse:Parse(jsonChar), JsonObject).
delete object objParse.
return jsonOutput.
END FUNCTION.
E por fim, na procedure process-web-request transformamos o conteúdo da tabela temporária ttCidade em um Json longchar para que a API retorne o conteúdo.
PROCEDURE process-web-request :
define variable jsonObjeto as JsonArray no-undo.
define variable jsonTemp AS JsonObject NO-UNDO.
DEFINE VARIABLE cJSON AS LONGCHAR NO-UNDO.
DEFINE VARIABLE lOK AS LOGICAL NO-UNDO.
/*
* Output the MIME header and set up the object as state-less or state-aware.
* This is required if any HTML is to be returned to the browser.
*/
RUN outputHeader.
find first ttCidade no-lock no-error.
ASSIGN lOK = TEMP-TABLE ttCidade:WRITE-JSON("LONGCHAR", cJSON).
assign jsonObjeto = LongcharToObject(cJSON):GetJsonArray("ttCidade").
jsonObjeto:Write(cJSON).
jsonTemp = NEW JSONObject().
jsonTemp:ADD("hasNext", false).
jsonTemp:ADD("items", jsonObjeto).
jsonTemp:Write(cJSON).
/* o prefixo cb significa CallBack */
{&OUT} STRING(cJSON).
END PROCEDURE.
Observe que neste momento em que converto o conteúdo da ttCidade em Json já poderia ter feito o retorno deste conteúdo. Porém para ficar um pouco mais próximo do padrão adotado pela TOTVS criei um segundo Json e inseri o conteúdo convertido dentro do campo items.
O resultado será esse:
Testando filtro de cidades no navegador:
Testando no Postman, agora filtrando por estado:
Como seria a parametrização dentro do progress?