UNPKG

@jackiemacklein/nettz-utils

Version:

Serviços de imagem, e-mail, códigos de barras, utilitários numéricos e componentes React para apps Node.js com TypeScript

179 lines (178 loc) 6.65 kB
"use strict"; /** * @author Jackiê Macklein * @company Onside tecnologia/Nettz * @copyright Todos direitos reservados. * @description Serviço para análise e conversão de códigos de barras */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BarcodeType = void 0; exports.analyzeBarcode = analyzeBarcode; exports.convertLineToBarcode = convertLineToBarcode; exports.validateDVModulo11 = validateDVModulo11; exports.checkBarcodeType = checkBarcodeType; var BarcodeType; (function (BarcodeType) { BarcodeType["BOLETO_BANCARIO"] = "boleto_bancario"; BarcodeType["TRIBUTO"] = "tributo"; BarcodeType["FORMATO_INVALIDO"] = "formato_invalido"; })(BarcodeType || (exports.BarcodeType = BarcodeType = {})); function convertTributoLineToBarcode(line) { const clean = line.replace(/\D/g, ""); if (clean.length !== 48) throw new Error("Linha digitável de tributo deve ter 48 dígitos"); // remove os 4 DVs de campo return (clean.substring(0, 11) + // bloco 1 clean.substring(12, 23) + // bloco 2 clean.substring(24, 35) + // bloco 3 clean.substring(36, 47) // bloco 4 ); // → 44 dígitos } function modulo10(num) { let soma = 0, peso = 2; for (let i = num.length - 1; i >= 0; i--) { let r = +num[i] * peso; if (r > 9) r -= 9; soma += r; peso = peso === 2 ? 1 : 2; } const resto = soma % 10; return resto === 0 ? 0 : 10 - resto; } function validateDVTributo(barcode) { const dv = +barcode[3]; // 4.º dígito const corpo = barcode.slice(0, 3) + barcode.slice(4); // 43 dígitos const idValor = barcode[2]; // 3.º dígito const calc = idValor === "6" || idValor === "7" ? modulo10(corpo) : calculateModulo11Boleto(corpo); // já existe na sua lib return dv === calc; } /** * Função para analisar um código de barras e retornar um resultado * @param code - Código de barras a ser analisado * @returns Resultado da análise do código de barras */ function analyzeBarcode(code) { const cleanCode = code.replace(/\D/g, ""); if (![44, 47, 48].includes(cleanCode.length)) return { isValid: false, message: "Formato inválido", type: BarcodeType.FORMATO_INVALIDO, }; const type = checkBarcodeType(cleanCode); // ---------------- BOLETO BANCÁRIO --------------------------------------- if (type === BarcodeType.BOLETO_BANCARIO) { const barcode = cleanCode.length === 47 ? convertLineToBarcode(cleanCode) : cleanCode; if (!validateDVModulo11(barcode)) return { isValid: false, message: "Dígito verificador inválido", type }; const fatorVenc = +barcode.substring(5, 9); const base = new Date(1997, 9, 7); // 07/10/1997 const venc = new Date(base.getTime() + fatorVenc * 86400000); const valor = +barcode.substring(9, 19) / 100; return { isValid: true, type, value: valor.toFixed(2), dueDate: isNaN(venc.getTime()) ? null : venc.toLocaleDateString("pt-BR"), }; } // ----------------- TRIBUTO ---------------------------------------------- let barcode = cleanCode; if (cleanCode.length === 48) barcode = convertTributoLineToBarcode(cleanCode); if (!validateDVTributo(barcode)) return { isValid: false, message: "Dígito verificador inválido", type }; const valor = +barcode.substring(4, 15) / 100; // pos. 05-15 → 11 dígitos // algumas entidades gravam AAAAMMDD em pos. 20-27 (campo livre) let dueDate = null; const dataCampoLivre = barcode.substring(19, 27); if (/^\d{8}$/.test(dataCampoLivre) && dataCampoLivre !== "00000000") { const y = +dataCampoLivre.slice(0, 4); const m = +dataCampoLivre.slice(4, 6) - 1; const d = +dataCampoLivre.slice(6, 8); const dObj = new Date(y, m, d); if (!isNaN(dObj.getTime())) dueDate = dObj.toLocaleDateString("pt-BR"); } return { isValid: true, type: BarcodeType.TRIBUTO, value: valor.toFixed(2), dueDate, message: dueDate ? undefined : "Tributo não possui vencimento definido no código", }; } /** * Função para converter uma linha digitável para um código de barras * @param line - Linha digitável a ser convertida * @returns Código de barras convertido */ function convertLineToBarcode(line) { const clean = line.replace(/\D/g, ""); const bloco1 = clean.substring(0, 9); const bloco2 = clean.substring(10, 20); const bloco3 = clean.substring(21, 31); const bloco5 = clean.substring(33, 47); // Fator + Valor const codeWithoutDV = bloco1.substring(0, 4) + // banco + moeda bloco5 + // fator + valor bloco1.substring(4) + // restante do campo 1 bloco2 + // campo 2 completo bloco3; // campo 3 completo const dv = calculateModulo11Boleto(codeWithoutDV); return codeWithoutDV.substring(0, 4) + dv + codeWithoutDV.substring(4); } /** * Função para calcular o dígito verificador do código de barras * @param code - Código de barras a ser calculado * @returns Dígito verificador calculado */ function calculateModulo11Boleto(code) { const reversed = code.split("").reverse(); let soma = 0; let peso = 2; for (const digito of reversed) { soma += parseInt(digito) * peso; peso = peso === 9 ? 2 : peso + 1; } const resto = soma % 11; if (resto === 0 || resto === 1) return 0; if (resto === 10) return 1; return 11 - resto; } /** * Função para validar o dígito verificador do código de barras * @param code - Código de barras a ser validado * @returns true se o dígito verificador for válido, false caso contrário */ function validateDVModulo11(code) { const dv = parseInt(code.charAt(4)); const base = code.substring(0, 4) + code.substring(5); const calculated = calculateModulo11Boleto(base); return dv === calculated; } /** * Função para verificar o tipo de código de barras * @param code - Código de barras a ser verificado * @returns Tipo de código de barras */ function checkBarcodeType(code) { const clean = code.replace(/\D/g, ""); if (clean.length === 44 || clean.length === 47) { return clean.startsWith("8") ? BarcodeType.TRIBUTO : BarcodeType.BOLETO_BANCARIO; } else if (clean.length === 48) { return BarcodeType.TRIBUTO; } else { return BarcodeType.FORMATO_INVALIDO; } }