No artigo de hoje vamos demonstrar em como atualizar a tabela SYD, com os dados de NCMs.
A lógica aqui é a seguinte:
- O primeiro passo, é exibido uma pergunta se deseja confirmar
- É feito o download do JSON com as nomenclaturas automaticamente no link https://portalunico.siscomex.gov.br/classif/api/publico/nomenclatura/download/json?perfil=PUBLICO
- Através da classe JSONObject é convertido o texto para uma variável em AdvPL
- A chave Nomenclaturas é atribuída em outra variável (jNomenclat) e é feito um laço de repetição em todas as nomenclaturas (incrementando uma régua de progresso)
- Se a nomenclatura tiver 8 caracteres (sem os pontos) e não existir na SYD, irá acionar o execauto de inclusão (o nome do execauto é MVC_EICA130)
- No final é exibido uma mensagem de log com os sucessos ou falhas (as falhas é gravado um log na pasta \x_logs\ dentro da Protheus Data)
Abaixo o código fonte desenvolvido, conforme a lógica descrita acima.
//Bibliotecas
#Include "TOTVS.ch"
  
/*/{Protheus.doc} User Function zImpSYD
Função para atualizar a tabela de NCM no Protheus
@type  Function
@author Atilio
@since 12/11/2022
@obs A atualização é baseada no JSON disponível para download em:
  https://www.gov.br/receitafederal/pt-br/assuntos/aduana-e-comercio-exterior/classificacao-fiscal-de-mercadorias/download-ncm-nomenclatura-comum-do-mercosul
/*/
  
User Function zImpSYD()
    Local aArea := FWGetArea()
  
    //Se a pergunta for confirmada
    If FWAlertYesNo("Deseja atualizar a tabela de NCMs no Protheus?", "Continua")
        Processa({|| fImporta() }, 'NCMs...')
    EndIf
  
    FWRestArea(aArea)
Return
  
Static Function fImporta()
    Local cLinkDown  := "https://portalunico.siscomex.gov.br/classif/api/publico/nomenclatura/download/json?perfil=PUBLICO;charset=iso-8859-1" //Dica do charset enviada por João
    Local cTxtJson   := ""
    Local cError     := ""
    Local jImport
    Local jNomenclat
    Local jNCMAtu
    Local nNCMAtu    := 0
    Local cCodigo    := ""
    Local cDescric   := ""
    Local aDados     := {}
    Local cLog       := ""
    Local nLinhaErro := 0
	Local nTamDescri := TamSX3("YD_DESC_P")[01]
    //Variáveis para log do ExecAuto
    Private lMSHelpAuto    := .T.
    Private lAutoErrNoFile := .T.
    Private lMsErroAuto    := .F.
  
    //Baixa o JSON disponível
    cTxtJson := HttpGet(cLinkDown)
    //cTxtJson := FWNoAccent(cTxtJson) //Dica do João, será tratado direto na variável cDescric
  
    //Se houver conteúdo
    If ! Empty(cTxtJson)
        jImport := JsonObject():New()
        cError  := jImport:FromJson(cTxtJson)
  
        DbSelectArea("SYD")
        SYD->(DbSetOrder(1)) // YD_FILIAL + YD_TEC + YD_EX_NCM + YD_EX_NBM + YD_DESTAQU
      
        //Se não tiver erro no parse
        If Empty(cError)
            jNomenclat := jImport:GetJsonObject('Nomenclaturas')
  
            //Percorre os NCM
            nTotal := Len(jNomenclat)
            ProcRegua(nTotal)
            For nNCMAtu := 1 To nTotal
                IncProc("Processando registro " + cValToChar(nNCMAtu) + " de " + cValToChar(nTotal) + "...")
  
                //Pegando o NCM atual, o código e a descrição
                jNCMAtu  := jNomenclat[nNCMAtu]
                cCodigo  := jNCMAtu:GetJsonObject("Codigo")
                cDescric := jNCMAtu:GetJsonObject("Descricao")
  
                //Remove caracteres especiais da descrição
				cDescric := FWNoAccent(DecodeUTF8(cDescric, "cp1252")) //Dica do João para tratar a acentuação
                cDescric := StrTran(cDescric, "-", "")
                cDescric := StrTran(cDescric, "<i>", "")
                cDescric := StrTran(cDescric, "</i>", "")
                cDescric := Alltrim(cDescric)
				cDescric := Upper(cDescric) //Dica do João, para gravar tudo em caixa alta padronizado
				cDescric := Left(cDescric, nTamDescri) //Dica do Renato, para gravar somente conforme o tamanho do campo no Dicionário
  
                //Removendo os pontos do código
                cCodigo := Alltrim(cCodigo)
                cCodigo := StrTran(cCodigo, ".", "")
                cCodigo := StrTran(cCodigo, "-", "")
  
                //Se o código tiver 8 caracteres e não conseguir posicionar no registro
                If Len(cCodigo) == 8 .And. ! SYD->(MsSeek(FWxFilial("SYD") + cCodigo))
                    aDados := {}
                    aAdd(aDados, {"YD_TEC",    cCodigo,  Nil})
                    aAdd(aDados, {"YD_DESC_P", cDescric, Nil})
                    aAdd(aDados, {"YD_UNID",   "UN",     Nil})
  
                    //Aciona a inclusão do registro
                    lMsErroAuto := .F.
                    MSExecAuto({|x, y| MVC_EICA130(x, y)}, aDados, 3)
  
                    //Se houve erro, gera o log
                    If lMsErroAuto
                        cPastaErro := '\x_logs\'
                        cNomeErro  := 'erro_syd_cod_' + cCodigo + "_" + dToS(Date()) + '_' + StrTran(Time(), ':', '-') + '.txt'
  
                        //Se a pasta de erro não existir, cria ela
                        If ! ExistDir(cPastaErro)
                            MakeDir(cPastaErro)
                        EndIf
  
                        //Pegando log do ExecAuto, percorrendo e incrementando o texto
                        cTextoErro := ""
                        cTextoErro += "Codigo:    " + cCodigo + CRLF
                        cTextoErro += "Descricao: " + cDescric + CRLF
                        cTextoErro += "--" + CRLF + CRLF
                        aLogErro := GetAutoGRLog()
                        For nLinhaErro := 1 To Len(aLogErro)
                            cTextoErro += aLogErro[nLinhaErro] + CRLF
                        Next
  
                        //Criando o arquivo txt e incrementa o log
                        MemoWrite(cPastaErro + cNomeErro, cTextoErro)
                        cLog += '- Falha ao incluir registro, codigo [' + cCodigo + '], veja o arquivo de log em ' + cPastaErro + cNomeErro + CRLF
                    Else
                        cLog += '+ Sucesso no Execauto no codigo ' + cCodigo + ';' + CRLF
                    EndIf
                EndIf
            Next
  
            //Se tiver log, mostra ele
            If ! Empty(cLog)
                cDirTmp := GetTempPath()
                cArqLog := 'importacao_' + dToS(Date()) + '_' + StrTran(Time(), ':', '-') + '.log'
                MemoWrite(cDirTmp + cArqLog, cLog)
                ShellExecute('OPEN', cArqLog, '', cDirTmp, 1)
            EndIf
  
        Else
            FWAlertError("Houve uma falha na conversão do JSON: " + CRLF + cError, "Erro no Parse")
        EndIf
    EndIf
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.



 
 
 
Efetuei alguns ajustes neste fonte devido a atender minhas necessidades, mas acredito que outros poderam ter o mesmo problema.
Na linha 55 foi necessario alterar o link para este link Local cLinkDown := “https://portalunico.siscomex.gov.br/classif/api/publico/nomenclatura/download/json?perfil=PUBLICO;charset=iso-8859-1”, por causa da codificação em que os acentos estão retornando para o advpl.
Na linha 44 comentei esta parte //cTxtJson := FWNoAccent(cTxtJson), devido não estar retirado as informações de acentos corretamente.
Desta forma foi necessario incluir uma linha logo abaixo da linha 73 foi incluido a tratativa da descrição das NCMS cDescric := FWNoAccent(decodeUTF8(cDescric, “cp1252”)), devido a codificação em que os registros são retornados.
Já na linha 84, incrementei desta forma para ficar um padrão na gravação dos dados, aAdd(aDados, {“YD_DESC_P”, UPPER(cDescric), Nil})
Bom dia João, tudo joia?
Opa, ficamos felizes que nosso singelo exemplo tenha lhe ajudado.
E ficamos honrados pela melhoria que foi elaborada por você e gentilmente disponibilizada.
Um grande abraço.
Boa noite! Além de seguir as orientações do João, precisei limitar o tamanho da variável cDescric devido ao campo YD_DESC_P, que suporta apenas 40 caracteres.
//Limita a descrição para 40 caraceteres
cDescric := SubStr(cDescric, 1, 40)
Bom dia Renato, tudo joia?
Opa, obrigado pela contribuição, atualizamos o exemplo, linhas 37 e 77.
Um grande abraço.
Atílio, boa tarde! Muito obrigado pelo retorno. Você já pegou o tamanho na sx3, ficou muito bom. Abraço.
Bom dia Renato, tudo joia?
Opa, nós que agradecemos pelo comentário e feedback.
Um grande abraço.
Muito bom!! Está de parabéns, coisas simples e banais que a totvs poderia implementar.
Bom dia Rodrigo, tudo joia?
Obrigado pelo comentário e carinho, é muita bondade e generosidade sua.
Um grande abraço.
Boa tarde pessoal, não sou DEV e por isso, apenas compilei o cod. fonte acima. Porém, não funcionou. Não cadastrou os NCM’s na Tabela SYD.
Bom dia Marcos, tudo joia?
Faça esse procedimento:
1. Baixe o zMiniForm disponível nesse link: https://terminaldeinformacao.com/2018/02/13/funcao-para-executar-formulas-protheus-12/
2. Compile ele na sua base usando o VSCode
3. Coloque ele no menu, conforme esse tutorial: https://terminaldeinformacao.com/2023/05/31/como-adicionar-uma-rotina-em-um-menu-do-protheus/
4. Abra o zMiniForm e execute a fórmula: u_zImpSYD()
Tenha uma ótima e abençoada terça feira.
Um grande abraço.
Boa tarde,
Sabem informar se há em algum link o JSON já com as alíquotas para alimentar a SYD com as alíquotas também?
Bom dia Wendel, tudo joia?
Infelizmente desconhecemos algum lugar que tenha as alíquotas de NCM em JSON.
Tenha uma ótima e abençoada sexta feira.
Um forte abraço.
Como faço apara atualizar TIPI e II??
Bom dia Maximiliano, tudo joia?
No JSON que vem do Governo, não tem essa informação.
Você precisaria, ou validar se tem alguma página do Governo, ou algum lugar para se basear para atualizar esse campo conforme os NCM.
Ou alinhar com algum consultor fiscal, para ver se ele teria a lista de NCM com a informação para ser atualizada.
Tenha uma ótima e abençoada sexta feira.
Um forte abraço.