Criando consulta customizada desde o back-end via API até o front-end com Angular e POUI

Vamos criar uma tela de consulta totalmente customizada que possamos usar no ERP Datasul da TOTVs. Vamos criar uma consulta de cidades neste guia.

Primeiramente vamos criar a API que fará a coleta dos dados no sistema usando os recursos apresentados n

este link: https://tdn.totvs.com/display/public/FRAMJOI/Desenvolvimento+de+APIs+para+o+produto+Datasul

Também vamos usar o programa criado pelo Rubinhos e disponibilizado no GIT da TOTVs neste endereço: https://github.com/totvs/CoffeeAndCodeJOI/blob/master/dts-thf/back-end/fnd/api/v1/idiomas.p

Em seguida vamos criar uma tela de consulta com Angular e POUI e tentar inserir no ERP Datasul baseados no guia deste endereço: https://po-ui.io/guides/getting-started

Criando a API para consulta

Vamos criar uma procedure chamada api_cidade.p, segue o código:

USING Progress.Json.ObjectModel.*.
USING Progress.Json.ObjectModel.ObjectModelParser.
USING Progress.Json.ObjectModel.JsonArray.
USING Progress.Json.ObjectModel.JsonObject.

{utp/ut-api.i}
{utp/ut-api-utils.i}
{utp/ut-api-action.i piGet GET /cidade/~*}
{utp/ut-api-action.i piGetAll GET /~*}
{utp/ut-api-notfound.i}

define temp-table ttCidade no-undo
    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".

/*------------------------------------------------------------------------------
 Retorna a lista de cidades.
------------------------------------------------------------------------------*/
PROCEDURE piGetAll:
    DEFINE INPUT  PARAMETER jsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER jsonOutput AS JsonObject NO-UNDO.
    
    DEFINE VARIABLE jsonRetorno AS JsonArray NO-UNDO.
    
    EMPTY TEMP-TABLE ttCidade.
    EMPTY TEMP-TABLE RowErrors.
    
    for each mgcad.cidade no-lock
        by cidade:
        create ttCidade.
        buffer-copy mgcad.cidade to ttCidade.
    end.
    
    ASSIGN jsonRetorno = NEW JsonArray().
    jsonRetorno:Read(TEMP-TABLE ttCidade:HANDLE).
    
    RUN createJsonResponse(
        INPUT jsonRetorno, 
        INPUT TABLE RowErrors, 
        INPUT FALSE, 
        OUTPUT jsonOutput).
END PROCEDURE.
/*------------------------------------------------------------------------------
 Retorna os dados da cidade informada por parametro (Parametro de URL)
------------------------------------------------------------------------------*/
PROCEDURE piGet:
    DEFINE INPUT  PARAMETER jsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER jsonOutput AS JsonObject NO-UNDO.
    DEFINE VARIABLE jsonRetorno AS JsonArray NO-UNDO.
    define variable oRequestParser as JsonAPIRequestParser no-undo.
    define variable oQueryParams as JsonObject no-undo.

    DEFINE VARIABLE vAux AS CHARACTER NO-UNDO.

    EMPTY TEMP-TABLE ttCidade.
    EMPTY TEMP-TABLE RowErrors.

    ASSIGN
        oQueryParams = NEW JsonObject()
        jsonRetorno = NEW JsonArray().

    oRequestParser = NEW JsonAPIRequestParser(jsonInput).
    assign oQueryParams = oRequestParser:getQueryParams().
 

    /* se vazio o GetJsonText() retorna 2 */
    if length(oQueryParams:GetJsonText()) <= 2 then do:
        CREATE RowErrors.
        ASSIGN
            RowErrors.ErrorNumber      = 42424
            RowErrors.ErrorType        = "error"
            RowErrors.ErrorDescription = "Nenhum parâmetro de busca informado"
            RowErrors.ErrorSubType     = "ERROR".        
    end.
    else do:
        if oQueryParams:has("cidade") then do:
            /* getQueryParams retorna JsonArray, vamos estrair a informação na primeira posição */
            assign jsonRetorno = oQueryParams:GetJsonArray("cidade").
            assign vAux = string(jsonRetorno:GetJsonText(1)).

            for each mgcad.cidade no-lock
                where mgcad.cidade.cidade = vAux
                by cidade:
                create ttCidade.
                buffer-copy mgcad.cidade to ttCidade.
            end.

            /* Se for filtrar estado */
            /* :has é case sensitive */
            if oQueryParams:has("estado") then do:
                assign jsonRetorno = oQueryParams:GetJsonArray("estado").
                assign vAux = string(jsonRetorno:GetJsonText(1)).
                for each ttCidade
                    where ttCidade.estado <> vAux
                    :
                    delete ttCidade.
                end.
            end.
            /* Se for filtrar estado */
            if oQueryParams:has("pais") then do:
                assign jsonRetorno = oQueryParams:GetJsonArray("pais").
                assign vAux = string(jsonRetorno:GetJsonText(1)).
                for each ttCidade
                    where ttCidade.pais <> vAux
                    :
                    delete ttCidade.
                end.
            end.
        end.
    end.

    if not can-find(first ttCidade) then do:
        CREATE RowErrors.
        ASSIGN
            RowErrors.ErrorNumber      = 42424
            RowErrors.ErrorType        = "error"
            RowErrors.ErrorDescription = "Nenhum registro localizado"
            RowErrors.ErrorSubType     = "ERROR".
    end.
    else
        jsonRetorno:Read(TEMP-TABLE ttCidade:HANDLE).

    if not can-find(first RowErrors) then do:
        RUN createJsonResponse(
            INPUT jsonRetorno, 
            INPUT TABLE RowErrors, 
            INPUT FALSE, 
            OUTPUT jsonOutput).
    end.
    else do:
        RUN createJsonResponse(NEW JsonObject(), INPUT TABLE RowErrors, INPUT FALSE, OUTPUT jsonOutput).
    end.
END PROCEDURE.

Resultado da consulta realizado pelo Postman:

O próximo passo é criar a tela de consulta de dados .

Porem antes vem um detalhe importante. Ao tentar fazer uma consulta pode estar tendo um retorno de falha de login.

Acontece pois a API vai tentar logar o usuário antes da consulta, para conseguir acessar deve ser informado um usuário e senha cadastrados no sistema Datasul, e a senha deve estar convertida para SHA1

DEFINE VARIABLE testPass AS CHARACTER NO-UNDO.
testPass = BASE64-ENCODE(SHA1-DIGEST(LC("testPassword"))).

O resultado desta conversão será “i7YRj4/Wk1rQh2o740pxfTJwj/0=”

Para realizar o teste no Postman por exemplo devemos configurar Basic Auth em Autorization:

11 thoughts on “Criando consulta customizada desde o back-end via API até o front-end com Angular e POUI

  1. Boa tarde Fabiano, tudo bem?
    Assunto muito interessante, porém o primeiro link é apenas para pessoal da TOTVS, franqueados ou parceiros, não sei como conseguir acesso a ele.

      1. Opa, já me deu uma luz, estou tendo alguns problemas, mas acredito que seja configuração de produto, abri um chamado na TOTVS pra ver se eles me auxiliam, por enquanto, muito obrigado =)

  2. Muito bom seu tutorial, como estou iniciando nesta jornada (PO-UI x Progress) não entendi bem onde ficarão os programas da sua API.
    Por exemplo, se eu quiser construir algo para o módulo de cadastros, devo colocar meu .R na pasta : ..\totvs\datasul\dts-12\ERP\ems2\cdp\api\v2 ?

    E no caso acima o seu API_CIDADE.P está onde ?

    1. Bom dia,
      desculpe a demora em responder, estranhamente não fui notificado do comentário.
      Por padrão adotamos que os customizados recebem a nomeclatura dms, então temos um banco de dados mgdms, as tabelas recebem dm_ no inicio e no propatch os compilados ficam na pasta dms/ pra baixo.
      Estou colocando todas as APIs feitas por mim no seguinte diretório: …\erp\fnd\dms\api\v1

      1. Bom dia Fabiano, tudo certo?
        Para consumir essas APIs eu vou sempre precisar criar uma pasta customizada dentro da pasta fnd ou pastas padrões datasul? Porque aqui na empresa utilizamos uma pasta _custom que fica depois da ERP, no caso ERP\_custom e aqui salvamos nossos customizados, gostaria de saber se consigo criar uma pasta api\v1 dentro dessa custom e utilizar as APIs, estou tentando de todas as formas e só consegui com a pasta customizada dentro da fnd mesmo.

  3. Olá Fabiano! Obrigada pelo tutorial. Consegui dar meus primeiros passos e compartilho aqui 🙂

    1) Caminho dos programas: temos a pasta “esp” de específico que também está no Propath do AppServer. Ficou assim: esp\fnd\api\v1\api_cidade.r. Na chamada via Postman ficou assim: http://servidor:porta/api/fnd/v1/api_cidade?cidade=CURITIBA&estado=PR&pais=Brasil
    2) Postman: não tive falha no login, em “Authorization” informei o tipo “Basic Auth” e usuário e senha do Datasul sem a necessidade de converter para SHA1
    Abraços!

  4. Não consigo fazer funcionar…
    quando chamo http://cairo:8180/api/sec/api/v1/api_cidade ele devolve o erro abaixo

    {“detailedMessage”:”Method not found”,”code”:”1″,”message”:”BAD_REQUEST”,”type”:”error”}

    porem acredito que não seja problema de propath, por se eu colocar o caminho errado… ele da erro..
    http://cairo:8180/api/sec/api/v1/api_cidadeXXX

    {“detailedMessage”:”ERROR condition: ** \”sec\/api\/v1\/api_cidadeXXX\” nao foi encontrado. (293) (7211)”,”code”:500,”message”:”Internal server error”}

    alguem consegue me dar uma luz ? k

  5. Boa tarde, alguém pode me dizer o motivo deste erro abaixo:

    {
    “detailedMessage”: “Method not found”,
    “code”: “1”,
    “message”: “BAD_REQUEST”,
    “type”: “error”
    }

    1. Olá,
      pode ser um destes motivos:
      O método utilizado como GET, POST PUT não foi definido no definitions
      O nome do método na URL está diferente do método criado no definitions:
      {utp/ut-api-action.i piGet GET /cidade/~*}
      URL: servidor:porta/dts/datasul-rest/xxx/pi_Get/?cidade=XXXX
      Ou a URL pode estar errada.

      Boa sorte..

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.