Criando o código GTIN de produto via AdvPL usando API do GS1

No artigo de hoje, vamos demonstrar em como consumir a API do GS1, para criar o código GTIN do Produto e já popular as informações das tabelas SB1 e SB5.

Esse fonte foi desenvolvido pelo grande Vinicius Reies ( https://www.linkedin.com/in/vreies ).

 

Basicamente é necessário:

  1. Um parâmetro, de nome MV_X_GS1EM com o eMail de acesso para gerar o Token no GS1
  2. Um parâmetro, de nome MV_X_GS1PS com a senha desse email para gerar o Token no GS1
  3. Um parâmetro, de nome MV_X_GS1AU com a string da autenticação Basic usada no GS1
  4. Um parâmetro, de nome MV_X_GS1ID com o client_id no GS1

 

Nisso a lógica funciona da seguinte forma:

  1. É recebido o código de um produto
  2. Se esse produto não tiver ainda o Código GTIN (B1_CODGTIN)
  3. Ai é acionado a geração do Token através de uma função chamada fRestTken() usando FWRest
  4. Após gerar o Token, ai é montado uma string com as informações do cadastro de produtos (isso precisa ser revisado por cada empresa, conforme regras de negócio)
  5. Essa string é enviada à API do GS1 através da classe FWRest
  6. Se deu tudo certo, a informação vai ser gravada no B1_CODGTIN e se houver no B5_2CODBAR (nesse exemplo foi usado RecLock, mas você poderia adaptar e usar MsExecAuto)

 

Segue abaixo o código fonte conforme a lógica descrita acima:

//Bibliotecas
#Include "Totvs.ch"

/*/{Protheus.doc} zSB1Gtin
Verificação e atribuição de GTIN para produtos
@type user function
@author Vinicius
@since 06/01/2025
@version version
@param cCodProd, character, codigo do produto
/*/

User Function zSB1Gtin(cCodProd)
    Local aArea         := FWGetArea()
    Local cToken        := "" // Variável que armazena o token
    Private cRetorno    := "" // Variável para validar retorno de erro

    // Abrindo a tabela SB1
    DbSelectArea("SB1")
    SB1->(DbSetOrder(1)) // Filial + Código

    // Verificando existência e posicionando através do código do produto
    If SB1->(MsSeek(FWxFilial("SB1") + cCodProd))

        // Verificando se B1_CODGTIN está vazio
        If Empty(SB1->B1_CODGTIN)

            // Requisição para pegar token
            cToken := fRestTken() 

            if !Empty(cToken)
                // Requisição para aplicar cadastro
                fRestCad(cToken)
            EndIf
        Else
            // Código Gtin já existente
            cRetorno := "Campo B1_CODGTIN já existia para o produto: " + SB1->B1_CODGTIN
        EndIf
    Else
       cRetorno := "Produto não encontrado"
    EndIf

    FWRestArea(aArea)
Return cRetorno

// Função para requisição REST do token
Static Function fRestTken()
    Local cJsonText     := ""                                    // JSON para access_token
    Local cToken        := ""                                    // Variável para armazenar o token
    Local cResultado    := ""                                    // Armazena resulta da requisição
    Local cErro         := ""                                    // Armazena o erro
    Local aHeader       := {}                                    // Header para montar a requisição REST
    Local oRestUrl      := FWRest():New("http://api.gs1br.org/") // URL para access_token. (testes: http://api-hml.gs1br.org/oauth/access-token) (produção: http://api.gs1br.org/oauth/access-token) 
    Local cEmailGs1     := u_zGetMV("MV_X_GS1EM")                // Email de acesso para requisição do token
    Local cSenhaGs1     := u_zGetMV("MV_X_GS1PS")                // Senha de acesso para requisição do token
	Local cBasicGs1     := u_zGetMV("MV_X_GS1AU")                // String em Base64 da Autenticação Basic no GS1
    Local jResponse                                              // Response do json
    Local aArray        := {}                                    // Array de objetos do retorno de erro de login
    Local cMsgErro      := ""                                    // Variável string para erro de login.

    // Adiciona os cabeçalhos necessários para a requisição
	aAdd(aHeader, 'Content-Type: application/json')
    aAdd(aHeader, 'Authorization: Basic ' + cBasicGs1)

    // Setando o path do diretório
    oRestUrl:setPath("oauth/access-token")

    // JSON para pegar o access_token (Ambiente de produção)
    cJsonText := '{' + CRLF
    cJsonText += ' "grant_type":"password",' + CRLF
    cJsonText += ' "username":"' + cEmailGs1 + '",' + CRLF
    cJsonText += ' "password":"' + cSenhaGs1 + '"' + CRLF
    cJsonText += '}' + CRLF

    // Setando os parâmetros da variável para o objeto
	oRestUrl:SetPostParams(cJsonText)

    // Realiza a requisição POST
    If oRestUrl:Post(aHeader)
        cResultado := oRestUrl:GetResult()

        //Pega o JSON de resposta, e pega o token
        jResponse := JsonObject():New()
        jResponse:FromJson(cResultado)

        //Pega o Token de acesso
        cToken := Iif( ValType(jResponse['access_token']) != "U", jResponse['access_token'], "")
    Else
        cErro := oRestUrl:GetResult()
        If Empty(cErro)
            cErro := oRestUrl:GetLastError()
        Else
            //Pega o JSON de resposta, e pega o token
            jResponse := JsonObject():New()
            jResponse:FromJson(cErro)

            // Falha na requisição
            aArray := jResponse:GetJsonObject('errors')
            If Len(aArray) > 0
                cMsgErro := aArray[1]:GetJsonObject('message')
            EndIf
        EndIf

        cRetorno := "Falha na obtenção do token -" + cMsgErro
    EndIf
Return cToken

// Função para requisição REST cadastro
Static Function fRestCad(cToken)
    Local cJsonText     := ""                                       // Json para access_token
    Local aHeader       := {}                                       // Header para montar a requisição REST
    Local oRestUrl      := FWRest():New("https://api.gs1br.org/")   // Url para cadastro (testes: https://api-hml.gs1br.org/gs1/v2/products) (produção: https://api.gs1br.org/gs1/v2/products)
    Local cB1Desc       := Alltrim(StrTran(SB1->B1_DESC, '"', ''))  // Variável para limpar descrição do produto
    Local cResultado    := ""                                       // Resultado da requisição
    Local cGtin         := ""                                       // Variável para armazenar o Gtin do produto
    Local jResponse     := {}                                       // Response do json
	Local cBasicGs1     := u_zGetMV("MV_X_GS1AU")                   // String em Base64 da Autenticação Basic no GS1
	Local cCliIdGs1     := u_zGetMV("MV_X_GS1ID")                   // String com o client_id
    Local cMessage      := ""                                       // Variável para armazenar erro de Gateway
	aAdd(aHeader, 'Content-Type: application/json')
    aAdd(aHeader, 'Authorization: Basic ' + cBasicGs1)
    aAdd(aHeader, 'client_id: ' + cCliIdGs1)
    aAdd(aHeader, 'access_token:' + cToken)

    // setando o path do diretório
    oRestUrl:setPath("gs1/v2/products")

    // JSON para cadastrar produto
    cJsonText := '{ ' + CRLF
    cJsonText += '   "company":{ ' + CRLF
    cJsonText += '      "cad":"A64812" ' + CRLF
    cJsonText += '   }, ' + CRLF
    cJsonText += '   "gtinStatusCode":"ACTIVE", ' + CRLF
    cJsonText += '   "tradeItemDescriptionInformationLang":[ ' + CRLF
    cJsonText += '      { ' + CRLF
    cJsonText += '         "tradeItemDescription":"' + cB1Desc + '", ' + CRLF // SB1->B1_DESC
    cJsonText += '         "languageCode":"pt-BR", ' + CRLF
    cJsonText += '         "default":true ' + CRLF
    cJsonText += '      } ' + CRLF
    cJsonText += '   ], ' + CRLF
    cJsonText += '   "gs1TradeItemIdentificationKey":{ ' + CRLF
    cJsonText += '      "gs1TradeItemIdentificationKeyCode":"GTIN_13" ' + CRLF
    cJsonText += '   }, ' + CRLF
    cJsonText += '   "referencedFileInformations":[ ' + CRLF
    cJsonText += '      { ' + CRLF
    cJsonText += '         "uniformResourceIdentifier":"http://site.com.br/produtos/' + Alltrim(SB1->B1_COD) + '.jpg", ' + CRLF
    cJsonText += '         "referencedFileTypeCode":"PLANOGRAM", ' + CRLF
    cJsonText += '         "featuredFile":true ' + CRLF
    cJsonText += '      } ' + CRLF
    cJsonText += '   ], ' + CRLF
    cJsonText += '   "tradeItemMeasurements":{ ' + CRLF
    cJsonText += '      "netContent":{ ' + CRLF
    cJsonText += '         "measurementUnitCode":"GRM", ' + CRLF
    cJsonText += '         "value":' + cValToChar(SB1->B1_PESO) + CRLF // cValToChar(SB1->B1_PESO)
    cJsonText += '      } ' + CRLF
    cJsonText += '   }, ' + CRLF
    cJsonText += '   "tradeItemWeight":{ ' + CRLF
    cJsonText += '      "grossWeight":{ ' + CRLF
    cJsonText += '         "value":' + cValToChar(SB1->B1_PESBRU) + ',' + CRLF // cValToChar(SB1->B1_PESBRU)
    cJsonText += '         "measurementUnitCode":"KGM" ' + CRLF
    cJsonText += '      } ' + CRLF
    cJsonText += '   }, ' + CRLF
    cJsonText += '   "brandNameInformationLang":[ ' + CRLF
    cJsonText += '      { ' + CRLF
    cJsonText += '         "brandName":"' + Alltrim(Posicione("SA2", 1, FWxFilial("SA2") + SB1->B1_PROC, "A2_NOME")) + '", ' + CRLF // Posicione("SA2", 1, FWxFilial("SA2") + SB1->B1_PROC, "A2_NOME")
    cJsonText += '         "languageCode":"pt-BR", ' + CRLF
    cJsonText += '         "default":true ' + CRLF
    cJsonText += '      } ' + CRLF
    cJsonText += '   ], ' + CRLF
    cJsonText += '   "tradeItemClassification":{ ' + CRLF
    cJsonText += '      "additionalTradeItemClassifications":[ ' + CRLF
    cJsonText += '         { ' + CRLF
    cJsonText += '            "additionalTradeItemClassificationSystemCode":"NCM", ' + CRLF
    cJsonText += '            "additionalTradeItemClassificationCodeValue":"' + Alltrim(Transform(SB1->B1_POSIPI, PesqPict("SB1", "B1_POSIPI"))) + '"' + CRLF // SB1->B1_POSIPI Alltrim(Transform(SB1->B1_POSIPI, PesqPict("SB1", "B1_POSIPI")))
    cJsonText += '         }, ' + CRLF
    cJsonText += '         { ' + CRLF
    cJsonText += '            "additionalTradeItemClassificationCodeValue":"' + Alltrim(Transform(SB1->B1_CEST, PesqPict("SB1", "B1_CEST"))) + '", ' + CRLF // SB1->B1_CEST
    cJsonText += '            "additionalTradeItemClassificationSystemCode":"CEST" ' + CRLF
    cJsonText += '         } ' + CRLF
    cJsonText += '      ], ' + CRLF
    cJsonText += '      "gpcCategoryCode":"77000000" ' + CRLF
    cJsonText += '   }, ' + CRLF
    cJsonText += '   "tradeItem":{ ' + CRLF
    cJsonText += '      "targetMarket":{ ' + CRLF
    cJsonText += '         "targetMarketCountryCodes":[ ' + CRLF
    cJsonText += '            "076" ' + CRLF
    cJsonText += '         ] ' + CRLF
    cJsonText += '      }, ' + CRLF
    cJsonText += '      "tradeItemUnitDescriptorCode":"PALLET" ' + CRLF
    cJsonText += '   }, ' + CRLF
    cJsonText += '   "acceptResponsibility":true, ' + CRLF
    cJsonText += '   "shareDataIndicator":true, ' + CRLF
    cJsonText += '   "placeOfProductActivity":{ ' + CRLF
    cJsonText += '      "countryOfOrigin":{ ' + CRLF
    cJsonText += '         "countryCode":"076", ' + CRLF
    cJsonText += '         "countrySubdivisionCodes":[ ' + CRLF
    cJsonText += '             ' + CRLF
    cJsonText += '         ] ' + CRLF
    cJsonText += '      } ' + CRLF
    cJsonText += '   } ' + CRLF
    cJsonText += '} ' + CRLF

    // Setando os parâmetros da variável para o objeto
	oRestUrl:SetPostParams(cJsonText)
    jResponse := JsonObject():New()

    // Realiza a requisição POST
    If oRestUrl:Post(aHeader)
        cResultado := oRestUrl:GetResult()

        // Pega o JSON de resposta
        jResponse:FromJson(cResultado)

        // Acessa o valor do campo "gtin" dentro do objeto aninhado
        If ValType(jResponse["product"]) != "U" .AND. ValType(jResponse["product"]["gs1TradeItemIdentificationKey"]) != "U"
            cGtin := Iif(ValType(jResponse["product"]["gs1TradeItemIdentificationKey"]["gtin"]) != "U", jResponse["product"]["gs1TradeItemIdentificationKey"]["gtin"], "")

            //Atualiza o produto
            RecLock("SB1", .F.)
                SB1->B1_CODGTIN := cGtin
            SB1->(MsUnlock())
            
            //Atualiza o complemento do produto
            DbSelectArea("SB5")
            SB5->(DbSetOrder(1))

            If SB5->(MsSeek(FWxFilial("SB5") + SB1->B1_COD))
                RecLock("SB5", .F.)
                    SB5->B5_2CODBAR := cGtin
                SB5->(MsUnlock())
            EndIf

            cRetorno := "GTIN cadastrado com sucesso: " + cGtin 
        EndIf
    Else

        cErro := oRestUrl:GetResult()

        jResponse:FromJson(cErro)

        If Empty(cErro)
            cErro := oRestUrl:GetLastError()
        EndIf
            
        If "GATEWAY" $ Upper(cErro)
            cMessage := "(Produto não possui imagem na url site.com.br)"
			
		ElseIf !Empty(jResponse["data"]["validations"][1]["message"])
            cMessage := DecodeUTF8(jResponse["data"]["validations"][1]["message"])

        EndIf
        
        cRetorno := "Houve uma falha na geração do Gtin. " + cMessage 

    EndIf 
Return 

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

Deixe uma resposta

Terminal de Informação