diff --git a/service/csvService.js b/service/csvService.js index 332a7c5..6991de9 100644 --- a/service/csvService.js +++ b/service/csvService.js @@ -117,7 +117,8 @@ function hasAddressOrNumberHeader(headers) { function hasGeoHeaders(headers) { const normalizedHeaders = headers.map(normalizeHeader); - return normalizedHeaders.includes('latitude') && normalizedHeaders.includes('longitude'); + return normalizedHeaders.some(header => header.includes('latitude')) + && normalizedHeaders.some(header => header.includes('longitude')); } function findHeaderRowIndex(rows) { @@ -136,8 +137,8 @@ function resolveColumnIndexes(headers) { idxCep: findFirstHeaderIndex(headers, header => /\bcep\b/.test(header) || header === 'codigo postal'), idxNumero: exactIndex(['numero', 'número', 'num', 'nº', 'n°']), idxEndereco: findFirstHeaderIndex(headers, header => header.includes('endereco') || header.includes('logradouro')), - idxLatitude: exactIndex(['latitude']), - idxLongitude: exactIndex(['longitude']) + idxLatitude: findFirstHeaderIndex(headers, header => header.includes('latitude')), + idxLongitude: findFirstHeaderIndex(headers, header => header.includes('longitude')) }; } @@ -170,13 +171,52 @@ function buildCepPayload(cols, indexes) { return { cep, numero }; } +function parseCoordinate(value) { + const parsed = parseFloat(String(value ?? '').trim().replace(',', '.')); + return Number.isFinite(parsed) ? parsed : NaN; +} + +function buildGeoPayload(cols, indexes) { + const latitude = indexes.idxLatitude >= 0 ? parseCoordinate(cols[indexes.idxLatitude]) : NaN; + const longitude = indexes.idxLongitude >= 0 ? parseCoordinate(cols[indexes.idxLongitude]) : NaN; + + if (isNaN(latitude) || isNaN(longitude)) return null; + return { latitude, longitude }; +} + +async function consultarComFallback(geoPayload, cepPayload) { + let lastError = null; + + if (geoPayload) { + try { + const result = await consultarViabilidade(geoPayload); + if (!result || !result.error) return result; + lastError = new Error(result.error); + } catch (err) { + lastError = err; + } + } + + if (cepPayload) { + try { + const result = await consultarViabilidade(cepPayload); + if (!result || !result.error) return result; + lastError = new Error(result.error); + } catch (err) { + lastError = err; + } + } + + throw lastError || new Error('Linha sem latitude/longitude ou CEP valido'); +} + function cleanCsvValue(value) { const text = String(value ?? '').replace(/[\r\n;]/g, ' '); return text.includes('"') ? text.replace(/"/g, "'") : text; } async function countValidLines(inputPath) { - const dataType = await discoverDataType(inputPath); + await discoverDataType(inputPath); const rows = readRows(inputPath); const headerRowIndex = findHeaderRowIndex(rows); const headers = rows[headerRowIndex] || []; @@ -184,20 +224,16 @@ async function countValidLines(inputPath) { let total = 0; for (const cols of rows.slice(headerRowIndex + 1)) { - if (dataType === 'cep') { - if (buildCepPayload(cols, indexes)) total++; - } else if (dataType === 'geolocalizacao') { - const latitude = indexes.idxLatitude >= 0 ? parseFloat(cols[indexes.idxLatitude]) : NaN; - const longitude = indexes.idxLongitude >= 0 ? parseFloat(cols[indexes.idxLongitude]) : NaN; - if (!isNaN(latitude) && !isNaN(longitude)) total++; - } + const geoPayload = buildGeoPayload(cols, indexes); + const cepPayload = buildCepPayload(cols, indexes); + if (geoPayload || cepPayload) total++; } return total; } async function processCsvFile(jobId, inputPath, originalName) { - const dataType = await discoverDataType(inputPath); + await discoverDataType(inputPath); const rows = readRows(inputPath); const headerRowIndex = findHeaderRowIndex(rows); const headers = rows[headerRowIndex] || []; @@ -210,23 +246,12 @@ async function processCsvFile(jobId, inputPath, originalName) { outStream.write(['Distancia', 'Dedicado', 'Nao Dedicado', 'Erro', ...headers].join(';') + '\n'); for (const cols of rows.slice(headerRowIndex + 1)) { - let dataToSend = {}; - - if (dataType === 'cep') { - dataToSend = buildCepPayload(cols, indexes); - if (!dataToSend) continue; - } else if (dataType === 'geolocalizacao') { - const latitude = indexes.idxLatitude >= 0 ? parseFloat(cols[indexes.idxLatitude]) : NaN; - const longitude = indexes.idxLongitude >= 0 ? parseFloat(cols[indexes.idxLongitude]) : NaN; - - if (isNaN(latitude) || isNaN(longitude)) continue; - dataToSend = { latitude, longitude }; - } else { - continue; - } + const geoPayload = buildGeoPayload(cols, indexes); + const cepPayload = buildCepPayload(cols, indexes); + if (!geoPayload && !cepPayload) continue; try { - const viab = await consultarViabilidade(dataToSend); + const viab = await consultarComFallback(geoPayload, cepPayload); const distancia = viab.distancia ?? (viab.raw && (viab.raw.distancia || viab.raw.distance)) ?? ''; const dedicado = viab.dedicado ? 'Viavel' : 'Nao Viavel'; const naoDedicado = viab.naoDedicado ? 'Viavel' : 'Nao Viavel'; diff --git a/service/viabilidadeService.js b/service/viabilidadeService.js index 8949492..49e23fe 100644 --- a/service/viabilidadeService.js +++ b/service/viabilidadeService.js @@ -55,7 +55,7 @@ function findHeaderRow(rows) { return rows.find(row => { const headers = row.map(normalizeHeader); const hasCepNumero = hasCepHeader(headers) && hasAddressOrNumberHeader(headers); - const hasGeo = headers.includes('latitude') && headers.includes('longitude'); + const hasGeo = hasGeoHeaders(headers); return hasCepNumero || hasGeo; }) || []; } @@ -83,7 +83,7 @@ async function readDelimitedHeaders(filePath) { if (!line.trim()) continue; const headers = line.split(detectDelimiter(line)).map(normalizeHeader); const hasCepNumero = hasCepHeader(headers) && hasAddressOrNumberHeader(headers); - const hasGeo = headers.includes('latitude') && headers.includes('longitude'); + const hasGeo = hasGeoHeaders(headers); if (hasCepNumero || hasGeo) { rl.close(); return headers; @@ -104,6 +104,11 @@ function hasAddressOrNumberHeader(headers) { || header.includes('logradouro')); } +function hasGeoHeaders(headers) { + return headers.some(header => header.includes('latitude')) + && headers.some(header => header.includes('longitude')); +} + async function consultarViabilidade(data) { try { const dataType = await discoverDataType(data); @@ -135,7 +140,7 @@ async function discoverDataType(input) { const hasCepNumero = hasCepHeader(headers) && hasAddressOrNumberHeader(headers); if (hasCepNumero) { return 'cep'; - } else if (headers.includes('latitude') && headers.includes('longitude')) { + } else if (hasGeoHeaders(headers)) { return 'geolocalizacao'; } else { return 'unknown'; @@ -144,7 +149,7 @@ async function discoverDataType(input) { // Trata como objeto de dados if (input.cep && input.numero) { return 'cep'; - } else if (input.latitude && input.longitude) { + } else if (input.latitude !== undefined && input.longitude !== undefined) { return 'geolocalizacao'; } else { return 'unknown';