Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions core/tm/controller/profesional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>((resolve, reject) => {
firmaPermanente.unlink(fileFirmaExistente._id, (error) => {
if (error) {return reject(error);}
resolve();
});
});
}

// Escribir firma en permanente
await new Promise<void>((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<void>((resolve, reject) => {
fotoPermanente.unlink(fileFotoExistente._id, (error) => {
if (error) {return reject(error);}
resolve();
});
});
}

// Escribir foto en permanente
await new Promise<void>((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));

Expand Down
23 changes: 20 additions & 3 deletions core/tm/routes/profesional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Loading