From 55d0df62583691f21cd9a5ca4650ab94024d9ebf Mon Sep 17 00:00:00 2001 From: nicolasarana <90768149+nicolasarana@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:41:17 -0300 Subject: [PATCH] =?UTF-8?q?MAT=20-=20"Renovaci=C3=B3n=20:=20No=20enviar=20?= =?UTF-8?q?archivos=20en=20request"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/tm/controller/profesional.ts | 131 ++++++++++++++++++++++++++++++ core/tm/routes/profesional.ts | 23 +++++- 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/core/tm/controller/profesional.ts b/core/tm/controller/profesional.ts index 13dec601f7..57399b76de 100644 --- a/core/tm/controller/profesional.ts +++ b/core/tm/controller/profesional.ts @@ -354,6 +354,137 @@ export async function saveImage(data) { }); } +/** + * Detecta si hubo cambios en formacionGrado que indiquen aprobación + * (papelesVerificados o matriculado pasaron a true) + */ +export function detectaCambiosFormacionGrado(formacionPersistida: any[], formacionNueva: any[]): boolean { + if (!Array.isArray(formacionPersistida) || !Array.isArray(formacionNueva)) { + return false; + } + + for (const nueva of formacionNueva) { + // Comparar por _id para ser robusto ante múltiples formaciones de grado + const persistida = formacionPersistida.find( + (f: any) => f && nueva && String(f._id) === String(nueva._id) + ); + + if (!persistida) { + continue; + } + + // Detectar cambio en papelesVerificados + if (persistida.papelesVerificados === false && nueva.papelesVerificados === true) { + return true; + } + + // Detectar cambio en matriculado + if (persistida.matriculado === false && nueva.matriculado === true) { + return true; + } + } + + return false; +} + +/** + * Promueve imágenes temporales (renovacionOnline) a permanentes + * Copia firma y foto de makeFsFirmaOnline/makeFsImagenOnline a makeFsFirma/makeFs + */ +export async function promoverImagenesTempAPermanentes(idProfesional: string, matricula: number) { + try { + // Copiar firma de temporal a permanente + const firmaOnline = makeFsFirmaOnline(); + const firmaPermanente = makeFsFirma(); + + const metadataOnline = { + 'metadata.idProfesional': idProfesional, + 'metadata.matricula': matricula + }; + const metadataPermanente = { 'metadata.idProfesional': idProfesional }; + + const fileFirmaOnline = await firmaOnline.findOne(metadataOnline); + if (fileFirmaOnline?._id) { + // Leer la firma temporal + const readStream = await firmaOnline.readFile({ _id: fileFirmaOnline._id }); + + // Eliminar firma permanente anterior si existe + const fileFirmaExistente = await firmaPermanente.findOne(metadataPermanente); + if (fileFirmaExistente?._id) { + await new Promise((resolve, reject) => { + firmaPermanente.unlink(fileFirmaExistente._id, (error) => { + if (error) {return reject(error);} + resolve(); + }); + }); + } + + // Escribir firma en permanente + await new Promise((resolve, reject) => { + firmaPermanente.writeFile( + { + filename: 'firma.png', + contentType: fileFirmaOnline.contentType || 'image/jpeg', + metadata: metadataPermanente + }, + readStream, + (error) => { + if (error) {return reject(error);} + resolve(); + } + ); + }); + } + + // Copiar foto de temporal a permanente + const fotoOnline = makeFsImagenOnline(); + const fotoPermanente = makeFs(); + + const fileFotoOnline = await fotoOnline.findOne(metadataOnline); + if (fileFotoOnline?._id) { + // Leer la foto temporal + const readStream = await fotoOnline.readFile({ _id: fileFotoOnline._id }); + + // Eliminar foto permanente anterior si existe + const fileFotoExistente = await fotoPermanente.findOne(metadataPermanente); + if (fileFotoExistente?._id) { + await new Promise((resolve, reject) => { + fotoPermanente.unlink(fileFotoExistente._id, (error) => { + if (error) {return reject(error);} + resolve(); + }); + }); + } + + // Escribir foto en permanente + await new Promise((resolve, reject) => { + fotoPermanente.writeFile( + { + filename: 'foto.png', + contentType: fileFotoOnline.contentType || 'image/jpeg', + metadata: metadataPermanente + }, + readStream, + (error) => { + if (error) {return reject(error);} + resolve(); + } + ); + }); + } + + // Limpiar temporales (se pasa un noop como next para evitar TypeError si falla) + // eslint-disable-next-line no-console + const noopNext = (err: any) => { if (err) { console.error('Error eliminando imágenes temporales:', err); } }; + await deleteFirmaFotoTemporal(idProfesional, matricula, noopNext); + + } catch (error) { + // Log error pero no fallo la operación completa + // eslint-disable-next-line no-console + console.error('Error promoting temporary images to permanent:', error); + } +} + export async function filtrarProfesionalesPorPrestacion(profesionales, prestaciones, organizacionId) { const usuarios = await findUsersByUsername(profesionales.map(p => p.documento)); diff --git a/core/tm/routes/profesional.ts b/core/tm/routes/profesional.ts index 39b0e159ce..f3e6cba869 100644 --- a/core/tm/routes/profesional.ts +++ b/core/tm/routes/profesional.ts @@ -10,7 +10,7 @@ import { PacienteApp } from '../../../modules/mobileApp/schemas/pacienteApp'; import { sendSms } from '../../../utils/roboSender/sendSms'; import { makePattern, toArray } from '../../../utils/utils'; import { streamToBase64 } from '../controller/file-storage'; -import { formacionCero, matriculaCero, migrarTurnos, saveFirma, filtrarProfesionalesPorPrestacion, saveImage, deleteFirmaFotoTemporal } from '../controller/profesional'; +import { formacionCero, matriculaCero, migrarTurnos, saveFirma, filtrarProfesionalesPorPrestacion, saveImage, deleteFirmaFotoTemporal, detectaCambiosFormacionGrado, promoverImagenesTempAPermanentes } from '../controller/profesional'; import { makeFsFirmaAdmin } from '../schemas/firmaAdmin'; import { makeFsFirma } from '../schemas/firmaProf'; import { makeFsFirmaOnline } from '../schemas/firmaRenovacionOnline'; @@ -1355,7 +1355,22 @@ router.patch('/profesionales/:id?', Auth.authenticate(), async (req, res, next) resultado.OtrosDatos = req.body.data; break; case 'updateEstadoGrado': - resultado.formacionGrado = req.body.data; + // Detectar si hay cambios de aprobación (papelesVerificados o matriculado pasaron a true) + const formacionAnterior = resultado.formacionGrado; + const formacionNueva = req.body.data; + + resultado.formacionGrado = formacionNueva; + + // Si hay cambio de aprobación y tenemos matricula, promover imágenes temporales + if (req.body.matricula && detectaCambiosFormacionGrado(formacionAnterior, formacionNueva)) { + try { + await promoverImagenesTempAPermanentes(req.params.id, req.body.matricula); + } catch (error) { + // Log pero no falla el request - la matrícula ya fue aprobada + // eslint-disable-next-line no-console + console.error('Error promoting temporary images:', error); + } + } break; case 'updateEstadoPosGrado': resultado.formacionPosgrado = req.body.data; @@ -1387,7 +1402,9 @@ router.patch('/profesionales/:id?', Auth.authenticate(), async (req, res, next) if (req.body.foto) { resultado.foto = req.body.foto; } - if (req.body.matricula) { + // Nota: Para updateEstadoGrado, las imágenes temporales se promocionan automáticamente + // en el handler. Las siguientes líneas son para operaciones que envíen firmaP e img directamente. + if (req.body.matricula && req.body.op !== 'updateEstadoGrado') { if (req.body.firmaP) { await saveFirma(req.body);