Como atualizar o cadastro de NCM no Protheus (tabela SYD)

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

Pergunta se deseja prosseguir

Régua de processamento

  • 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)

Mensagem de log

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.

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.

14 Responses

  1. Joao disse:

    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})

  2. renatobermudes disse:

    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)

  3. renatobermudes disse:

    Atílio, boa tarde! Muito obrigado pelo retorno. Você já pegou o tamanho na sx3, ficou muito bom. Abraço.

  4. Rodrigo Campos disse:

    Muito bom!! Está de parabéns, coisas simples e banais que a totvs poderia implementar.

  5. MARCOS ABBUDE disse:

    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.

  6. Wendel disse:

    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?

  7. Maximiliano Pfütz disse:

    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.

Deixe uma resposta

Terminal de Informação