UNPKG

d-danfe

Version:
426 lines (391 loc) 12.5 kB
const handlebars = require('handlebars') const NFe = require('d-nfe') const TEMPLATE_DANFE = __dirname + '/template-danfe.hbs' const fs = require('fs') /** * Retorna <valor> especificado com máscara do CPF. * * @param {string} valor * @return {string} */ function mascaraCPF(valor) { var retorno var grupo01 = valor.substring(0, 3) retorno = grupo01 var grupo02 = valor.substring(3, 6) if (grupo02 !== '') { retorno += '.' + grupo02 } var grupo03 = valor.substring(6, 9) if (grupo03 !== '') { retorno += '.' + grupo03 } var grupo04 = valor.substring(9) if (grupo04 !== '') { retorno += '-' + grupo04 } return retorno } /** * Retorna <valor> especificado com máscara do CNPJ. * * @param {string} valor * @return {string} */ function mascaraCNPJ(valor) { var retorno var grupo01 = valor.substring(0, 2) retorno = grupo01 var grupo02 = valor.substring(2, 5) if (grupo02 !== '') { retorno += '.' + grupo02 } var grupo03 = valor.substring(5, 8) if (grupo03 !== '') { retorno += '.' + grupo03 } var grupo04 = valor.substring(8, 12) if (grupo04 !== '') { retorno += '/' + grupo04 } var grupo05 = valor.substring(12) if (grupo05 !== '') { retorno += '-' + grupo05 } return retorno } /** * Retorna <numero> especificado formatado de acordo com seu tipo (cpf ou cnpj). * * @param {string} numero * @return {string} */ function formataInscricaoNacional(numero) { if (numero) { if (numero.length === 11) { return mascaraCPF(numero) } if (numero.length === 14) { return mascaraCNPJ(numero) } } return numero } /** * Formata data de acordo com <dt> esoecificado. * <dt> é no formato UTC, YYYY-MM-DDThh:mm:ssTZD (https://www.w3.org/TR/NOTE-datetime) * * @param {string} dt * @return {string} */ function formataData(dt) { dt = dt ? dt.toString() : '' if (!dt) { return '' } if (dt && dt.length === 10) { dt += 'T00:00:00+00:00' } var [data, hora] = dt.split('T') var [hora, utc] = hora.split(/[-+]/) var [ano, mes, dia] = data.split('-') var [hora, min, seg] = hora.split(':') var [utchora, utcmin] = utc ? utc.split(':') : ['', ''] return dia.padStart(2, '0') + '/' + mes.toString().padStart(2, '0') + '/' + ano } function formataHora(dt) { if (dt) { var data = new Date(dt) return data.getHours().toString().padStart(2, '0') + ':' + (data.getMinutes().toString().padStart(2, '0')) + ':' + data.getSeconds().toString().padStart(2, '0') } return '' } /** * Retorna o valor formatado em moeda de acordo com <numero> e <decimais> especificados. * * @param {number} numero * @param {number} decimais * @return {string} */ function formataMoeda(numero, decimais) { decimais = decimais || 4 var symbol = '' var decimal = ',' var thousand = '.' var negative = numero < 0 ? '-' : '' var i = parseInt(numero = Math.abs(+numero || 0).toFixed(decimais), 10) + '' var j = 0 decimais = !isNaN(decimais = Math.abs(decimais)) ? decimais : 2 symbol = symbol !== undefined ? symbol : '$' thousand = thousand || ',' decimal = decimal || '.' j = (j = i.length) > 3 ? j % 3 : 0 return symbol + negative + (j ? i.substr(0, j) + thousand : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand) + (decimais ? decimal + Math.abs(numero - i).toFixed(decimais).slice(2) : '') }; /** * Retorna objeto representando os dados da <entidade> especificada. * * @param {Object} entidade djf-nfe * @return {Object} */ function dadosEntidade(entidade) { if (entidade) { return { nome: entidade.nome(), fantasia: entidade.fantasia(), ie: entidade.inscricaoEstadual(), ie_st: entidade.inscricaoEstadualST(), inscricao_municipal: entidade.inscricaoMunicipal(), inscricao_nacional: formataInscricaoNacional(entidade.inscricaoNacional()), telefone: entidade.telefone() } } return {} } /** * Retorna objeto representando os dados do <endereco> especificado. * * @param {Object} endereco djf-nfe * @return {Object} */ function endereco(endereco) { if (endereco) { return { endereco: endereco.logradouro(), numero: endereco.numero(), complemento: endereco.complemento(), bairro: endereco.bairro(), municipio: endereco.municipio(), cep: endereco.cep(), uf: endereco.uf() } } return {} } /** * Retorna a <cahve> da NFE formata. * Formatação: grupos de 4 números separados por espaço. * @param {string} chave * @return {string} */ function formataChave(chave) { var out = '' if (chave && chave.length === 44) { for (var i = 0; i < chave.split('').length; i++) { if (i % 4 === 0) { out += ' ' + chave.charAt(i) } else { out += chave.charAt(i) } } return out } return chave } /** * Retorna array de objetos com os dados dos itens de acordo com <nfe> especificado. * * @param {<object>} nfe djf-nfe * @return {array} */ function itens(nfe) { var itens = [] var nrItens = nfe.nrItens() for (var i = 1; i <= nrItens; i++) { var row = nfe.item(i) var item = { codigo: row.codigo(), descricao: row.descricao(), ncm: row.ncm(), cst: row.origem() + '' + row.cst(), cfop: row.cfop(), unidade: row.unidadeComercial(), quantidade: formataMoeda(row.quantidadeComercial()), valor: formataMoeda(row.valorUnitario()), desconto: formataMoeda(row.valorDesconto()), total: formataMoeda(row.valorProdutos()), base_calculo: formataMoeda(row.baseCalculoIcms()), icms: formataMoeda(row.valorIcms()), ipi: formataMoeda(row.valorIPI()), porcentagem_icms: formataMoeda(row.porcetagemIcms(), 2), porcentagem_ipi: formataMoeda(row.porcentagemIPI(), 2) } itens.push(item) } return itens } /** * Retorna array de objetos com os dados das duplicatas de acordo com <nfe> especificado * * @param {object} nfe djf-nfe * @return {array} */ function duplicatas(nfe) { var dups = [] if (nfe.cobranca() && nfe.cobranca().nrDuplicatas() > 0) { var quant = nfe.cobranca().nrDuplicatas() for (var i = 1; i <= quant; i++) { var dup = nfe.cobranca().duplicata(i) dups.push({ numero: dup.numeroDuplicata(), vencimento: formataData(dup.vencimentoDuplicata()), valor: formataMoeda(dup.valorDuplicata(), 2) }) } } return dups } /** * Retorna os dados da observação de acordo com <nfe> especificado. * * @param {object} nfe djf-nfe * @return {string} */ function observacoes(nfe) { var quant = nfe.nrObservacoes() var result = '' for (var i = 1; i <= quant; i++) { result += '\n' + nfe.observacao(i).texto() } return result } /** * Retorna o template html do Danfe preenchido com os dados em <data> especificado. * Retorna vazio se não gerado. * @param {object} data * @return {string} */ function renderHtml(data) { if (!data) return '' handlebars.registerHelper('ifCond', function (v1, v2, options) { if (v1.length > v2) { return options.fn(this) } return options.inverse(this) }) const template = fs.readFileSync(TEMPLATE_DANFE, 'utf8') return handlebars.compile(template)(data) } /** * Retorna objeto com os dados do template de acordo com <nfe> especificado. * * @param {object} nfe djf-nfe * @return {object} */ function getTemplateData(nfe, storeID) { if (!nfe) { return null } var data = { operacao: nfe.tipoOperacao(), natureza: nfe.naturezaOperacao(), numero: nfe.nrNota(), serie: nfe.serie(), chave: formataChave(nfe.chave()), protocolo: nfe.protocolo(), data_protocolo: formataData(nfe.dataHoraRecebimento()) + ' ' + formataHora(nfe.dataHoraRecebimento()), destinatario: Object.assign(dadosEntidade(nfe.destinatario()), endereco(nfe.destinatario())), emitente: Object.assign(dadosEntidade(nfe.emitente()), endereco(nfe.emitente())), data_emissao: formataData(nfe.dataEmissao()), data_saida: formataData(nfe.dataEntradaSaida()), base_calculo_icms: formataMoeda(nfe.total().baseCalculoIcms(), 2), imposto_icms: formataMoeda(nfe.total().valorIcms(), 2), base_calculo_icms_st: formataMoeda(nfe.total().baseCalculoIcmsST(), 2), imposto_icms_st: formataMoeda(nfe.total().valorIcmsST(), 2), imposto_tributos: formataMoeda(nfe.total().valorTotalTributos(), 2), total_produtos: formataMoeda(nfe.total().valorProdutos(), 2), total_frete: formataMoeda(nfe.total().valorFrete(), 2), total_seguro: formataMoeda(nfe.total().valorSeguro(), 2), total_desconto: formataMoeda(nfe.total().valorDesconto(), 2), total_despesas: formataMoeda(nfe.total().valorOutrasDespesas(), 2), total_ipi: formataMoeda(nfe.total().valorIPI(), 2), total_nota: formataMoeda(nfe.total().valorNota(), 2), transportador: Object.assign(dadosEntidade(nfe.transportador()), endereco(nfe.transportador())), informacoes_fisco: nfe.informacoesFisco(), informacoes_complementares: nfe.informacoesComplementares(), observacao: observacoes(nfe), modalidade_frete: nfe.modalidadeFrete(), modalidade_frete_texto: nfe.modalidadeFreteTexto(), itens: itens(nfe), duplicatas: duplicatas(nfe), valor_ii: formataMoeda(nfe.valorII(), 2), valor_pis: formataMoeda(nfe.valorPIS(), 2), valor_cofins: formataMoeda(nfe.valorCOFINS(), 2), codigo_barras: nfe.chave(), valor_combate_pobreza: formataMoeda(nfe.valorCombatePobreza(), 2), valor_icms_uf_remetente: formataMoeda(nfe.valorIcmsUfRemetente(), 2), valor_icms_uf_destinatario: formataMoeda(nfe.valorIcmsUfDestinatario(), 2), storeID } if (nfe.transporte().volume()) { let volume = nfe.transporte().volume() data.volume_quantidade = formataMoeda(volume.quantidadeVolumes()) data.volume_especie = volume.especie() data.volume_marca = volume.marca() data.volume_numeracao = volume.numeracao() data.volume_pesoBruto = formataMoeda(volume.pesoBruto()) data.volume_pesoLiquido = formataMoeda(volume.pesoLiquido()) } if (nfe.transporte().veiculo()) { data.veiculo_placa = nfe.transporte().veiculo().placa() data.veiculo_placa_uf = nfe.transporte().veiculo().uf() data.veiculo_antt = nfe.transporte().veiculo().antt() } if (nfe.servico()) { data.total_servico = formataMoeda(nfe.servico().valorTotalServicoNaoIncidente()) data.total_issqn = formataMoeda(nfe.servico().valorTotalISS()) data.base_calculo_issqn = formataMoeda(nfe.servico().baseCalculo()) } return data } /** * Retorna modelo Danfe de acordo com objeto <nfe> especificado. * * @param {<type>} nfe djf-nfe * @return {Object} { description_of_the_return_value } */ function model(nfe, storeID) { return { toHtml: () => renderHtml(getTemplateData(nfe, storeID)) } } /** * Retorna modelo Danfe de acordo com objeto <nfe> especificado. * * @param {object} nfe djf-nfe * @return {<object>} */ module.exports.fromNFe = function (nfe) { if (!nfe || typeof nfe.nrNota !== 'function') { return model(null) } return model(nfe) } /** * Retorna modelo Danfe de acordo com <xml> especificado. * * @param {string} xml * @return {<object>} */ module.exports.fromXML = function (xml, storeID) { if (!xml || typeof xml !== 'string') { return model(null) } return model(NFe(xml), storeID) } /** * Retorna modelo Danfe de acordo com <filePath> especificado. * * @param {string} filePath * @return {<object>} */ module.exports.fromFile = function (filePath, storeID) { var content = '' if (!filePath || typeof filePath !== 'string') { return model(null) } try { content = fs.readFileSync(filePath, 'utf8') } catch (err) { throw new Error('File not found: ' + filePath + ' => ' + err.message) } return module.exports.fromXML(content, storeID) }