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.

6 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.

Deixe uma resposta para Dan (Daniel Atilio)Cancelar resposta

Terminal de Informação