const crypto = require('crypto'); const { INVIMA_DATASETS } = require('../APIinvima'); const cleanText = (value) => { if (value === null || value === undefined) return null; const text = String(value).trim(); if (!text) return null; if (text.toLowerCase() === 'null') return null; return text; }; const pickFirst = (row, keys) => { for (const key of keys) { const value = cleanText(row[key]); if (value) return value; } return null; }; const parseDateFlexible = (value) => { const text = cleanText(value); if (!text) return null; const slashDate = text.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/); if (slashDate) { const month = slashDate[1].padStart(2, '0'); const day = slashDate[2].padStart(2, '0'); return `${slashDate[3]}-${month}-${day}`; } const parsed = new Date(text); if (Number.isNaN(parsed.getTime())) return null; return parsed.toISOString().slice(0, 10); }; const buildUid = (datasetKey, rawParts, row) => { const parts = rawParts.map(cleanText).filter(Boolean); const base = parts.length ? `${datasetKey}|${parts.join('|')}` : `${datasetKey}|${JSON.stringify(row)}`; return crypto.createHash('sha1').update(base).digest('hex'); }; const baseRecord = (dataset, row) => ({ source_dataset_key: dataset.key, source_dataset_id: dataset.id, source_dataset_name: dataset.name, categoria_linea: dataset.defaultCategory, expediente: null, registro_sanitario: null, producto: null, titular: null, estado_registro: null, fecha_expedicion: null, fecha_vencimiento: null, modalidad: null, grupo: null, marca: null, principio_activo: null, forma_farmaceutica: null, presentacion_comercial: null, atc: null, rol_nombre: null, rol_tipo: null, ciudad_titular: null, pais_titular: null, fabricante: null, importador: null, vigencia: null, extra: row }); const buildAdapter = (datasetKey, normalize) => { const dataset = INVIMA_DATASETS[datasetKey]; if (!dataset) { throw new Error(`Dataset no configurado en APIinvima.json: ${datasetKey}`); } return { ...dataset, normalize }; }; const invimaAdapters = { dispositivos: buildAdapter('dispositivos', (row) => { const record = baseRecord(INVIMA_DATASETS.dispositivos, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = pickFirst(row, ['registro_sanitario']); record.producto = pickFirst(row, ['prodcuto', 'producto']); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.estado_registro); record.fecha_vencimiento = parseDateFlexible(row.fecha_vencimiento); record.modalidad = cleanText(row.modalidad); record.grupo = cleanText(row.grupo); record.marca = cleanText(row.marca); record.rol_nombre = cleanText(row.nombre_rol); record.rol_tipo = cleanText(row.rol); record.ciudad_titular = cleanText(row.ciudad_titular); record.pais_titular = cleanText(row.pais_titular); record.vigencia = cleanText(row.estado_registro); return { ...record, source_uid: buildUid( 'dispositivos', [row.registro_sanitario, row.expediente, row.prodcuto, row.nombre_rol, row.modalidad], row ) }; }), rs_nso: buildAdapter('rs_nso', (row) => { const record = baseRecord(INVIMA_DATASETS.rs_nso, row); record.categoria_linea = cleanText(row.grupo) || INVIMA_DATASETS.rs_nso.defaultCategory; record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.rsynso); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.estadoregistro); record.fecha_expedicion = parseDateFlexible(row.fechaexpedicion); record.fecha_vencimiento = parseDateFlexible(row.fechavencimiento); record.modalidad = cleanText(row.modalidad); record.grupo = cleanText(row.grupo); record.rol_nombre = cleanText(row.nombrerol); record.rol_tipo = cleanText(row.rol); record.vigencia = cleanText(row.estadoregistro); return { ...record, source_uid: buildUid('rs_nso', [row.rsynso, row.expediente, row.producto, row.nombrerol], row) }; }), medicamentos: buildAdapter('medicamentos', (row) => { const record = baseRecord(INVIMA_DATASETS.medicamentos, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.registrosanitario); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.estadoregistro); record.fecha_expedicion = parseDateFlexible(row.fechaexpedicion); record.fecha_vencimiento = parseDateFlexible(row.fechavencimiento); record.modalidad = cleanText(row.modalidad); record.principio_activo = cleanText(row.principioactivo); record.forma_farmaceutica = cleanText(row.formafarmaceutica); record.presentacion_comercial = cleanText(row.descripcioncomercial); record.atc = cleanText(row.atc); record.rol_nombre = cleanText(row.nombrerol); record.rol_tipo = cleanText(row.tiporol); record.vigencia = cleanText(row.estadocum) || cleanText(row.estadoregistro); return { ...record, source_uid: buildUid( 'medicamentos', [row.registrosanitario, row.expedientecum, row.consecutivocum, row.producto], row ) }; }), cum_vigentes: buildAdapter('cum_vigentes', (row) => { const record = baseRecord(INVIMA_DATASETS.cum_vigentes, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.registrosanitario); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.estadoregistro); record.fecha_expedicion = parseDateFlexible(row.fechaexpedicion); record.modalidad = cleanText(row.modalidad); record.principio_activo = cleanText(row.principioactivo); record.forma_farmaceutica = cleanText(row.formafarmaceutica); record.presentacion_comercial = cleanText(row.descripcioncomercial); record.atc = cleanText(row.atc); record.rol_nombre = cleanText(row.nombrerol); record.rol_tipo = cleanText(row.tiporol); record.vigencia = cleanText(row.estadocum) || cleanText(row.estadoregistro); return { ...record, source_uid: buildUid( 'cum_vigentes', [row.registrosanitario, row.expedientecum, row.consecutivocum, row.producto], row ) }; }), homeopaticos: buildAdapter('homeopaticos', (row) => { const record = baseRecord(INVIMA_DATASETS.homeopaticos, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.registro_sanitario); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.estado_registro); record.fecha_expedicion = parseDateFlexible(row.fecha_expedicion); record.modalidad = cleanText(row.modalidad); record.marca = cleanText(row.marca); record.principio_activo = cleanText(row.principio_activo); record.forma_farmaceutica = cleanText(row.forma_farmaceutica); record.presentacion_comercial = cleanText(row.presentacioncomercial); record.rol_tipo = cleanText(row.rol); record.ciudad_titular = cleanText(row.ciudad_titular); record.vigencia = cleanText(row.estado_registro); return { ...record, source_uid: buildUid('homeopaticos', [row.registro_sanitario, row.expediente, row.producto], row) }; }), fitoterapeuticos: buildAdapter('fitoterapeuticos', (row) => { const record = baseRecord(INVIMA_DATASETS.fitoterapeuticos, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.registro_sanitario); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.fecha_expedicion = parseDateFlexible(row.fecha_expedicion); record.fecha_vencimiento = parseDateFlexible(row.fecha_vencimiento); record.modalidad = cleanText(row.modalidad); record.marca = cleanText(row.marca); record.principio_activo = cleanText(row.principio_activo); record.forma_farmaceutica = cleanText(row.forma_farmaceutica); record.presentacion_comercial = cleanText(row.presentacioncomercial); record.rol_tipo = cleanText(row.rol); record.ciudad_titular = cleanText(row.ciudad_titular); record.vigencia = cleanText(row.fecha_vencimiento); return { ...record, source_uid: buildUid('fitoterapeuticos', [row.registro_sanitario, row.expediente, row.producto], row) }; }), suplementos: buildAdapter('suplementos', (row) => { const record = baseRecord(INVIMA_DATASETS.suplementos, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.registro_sanitario); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.estado_registro); record.fecha_expedicion = parseDateFlexible(row.fecha_expedicion); record.fecha_vencimiento = parseDateFlexible(row.fecha_vencimiento); record.modalidad = cleanText(row.modalidad); record.marca = cleanText(row.marca); record.forma_farmaceutica = cleanText(row.forma_farmaceutica); record.presentacion_comercial = cleanText(row.presentacioncomercial); record.ciudad_titular = cleanText(row.ciudad_titular); record.vigencia = cleanText(row.estado_registro); return { ...record, source_uid: buildUid('suplementos', [row.registro_sanitario, row.expediente, row.producto], row) }; }), vacunas: buildAdapter('vacunas', (row) => { const record = baseRecord(INVIMA_DATASETS.vacunas, row); record.expediente = cleanText(row.expediente); record.registro_sanitario = cleanText(row.registro_sanitario); record.producto = cleanText(row.producto); record.titular = cleanText(row.titular); record.estado_registro = cleanText(row.vigencia); record.modalidad = cleanText(row.via_de_administraci_n); record.principio_activo = cleanText(row.principio_activo); record.forma_farmaceutica = cleanText(row.forma_farmac_utica); record.presentacion_comercial = cleanText(row.presentaci_n_comercial); record.atc = cleanText(row.atc); record.fabricante = cleanText(row.fabricante); record.importador = cleanText(row.importador); record.vigencia = cleanText(row.vigencia); return { ...record, source_uid: buildUid('vacunas', [row.consec, row.registro_sanitario, row.expediente, row.producto], row) }; }) }; const adapterKeys = Object.keys(invimaAdapters); module.exports = { invimaAdapters, adapterKeys, cleanText, parseDateFlexible, pickFirst };