erp-clinicas-backend-v2/src/modules/patients/services/index.ts
rckrdmrd f8aacbd316 Migración desde erp-clinicas/backend - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:11:57 -06:00

122 lines
3.5 KiB
TypeScript

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource, Repository, ILike } from 'typeorm';
import { Patient } from '../entities';
import { CreatePatientDto, UpdatePatientDto, PatientQueryDto } from '../dto';
export class PatientService {
private repository: Repository<Patient>;
constructor(dataSource: DataSource) {
this.repository = dataSource.getRepository(Patient);
}
async findAll(tenantId: string, query: PatientQueryDto): Promise<{ data: Patient[]; total: number }> {
const { search, status, hasInsurance, page = 1, limit = 20 } = query;
const whereConditions: any = {
tenantId,
};
if (status) {
whereConditions.status = status;
}
if (hasInsurance !== undefined) {
whereConditions.hasInsurance = hasInsurance;
}
const queryBuilder = this.repository.createQueryBuilder('patient')
.where('patient.tenant_id = :tenantId', { tenantId })
.andWhere('patient.deleted_at IS NULL');
if (status) {
queryBuilder.andWhere('patient.status = :status', { status });
}
if (hasInsurance !== undefined) {
queryBuilder.andWhere('patient.has_insurance = :hasInsurance', { hasInsurance });
}
if (search) {
queryBuilder.andWhere(
'(patient.first_name ILIKE :search OR patient.last_name ILIKE :search OR patient.mobile ILIKE :search OR patient.email ILIKE :search)',
{ search: `%${search}%` }
);
}
queryBuilder
.orderBy('patient.last_name', 'ASC')
.addOrderBy('patient.first_name', 'ASC')
.skip((page - 1) * limit)
.take(limit);
const [data, total] = await queryBuilder.getManyAndCount();
return { data, total };
}
async findById(tenantId: string, id: string): Promise<Patient | null> {
return this.repository.findOne({
where: { id, tenantId },
});
}
async findByMobile(tenantId: string, mobile: string): Promise<Patient | null> {
return this.repository.findOne({
where: { mobile, tenantId },
});
}
async create(tenantId: string, dto: CreatePatientDto): Promise<Patient> {
const patient = this.repository.create({
...dto,
tenantId,
dateOfBirth: dto.dateOfBirth ? new Date(dto.dateOfBirth) : undefined,
});
return this.repository.save(patient);
}
async update(tenantId: string, id: string, dto: UpdatePatientDto): Promise<Patient | null> {
const patient = await this.findById(tenantId, id);
if (!patient) {
return null;
}
const updateData: any = { ...dto };
if (dto.dateOfBirth) {
updateData.dateOfBirth = new Date(dto.dateOfBirth);
}
Object.assign(patient, updateData);
return this.repository.save(patient);
}
async softDelete(tenantId: string, id: string): Promise<boolean> {
const patient = await this.findById(tenantId, id);
if (!patient) {
return false;
}
await this.repository.softDelete(id);
return true;
}
async getPatientHistory(tenantId: string, patientId: string): Promise<any> {
// Este metodo devolvera el historial completo del paciente
// incluyendo citas, consultas, prescripciones, etc.
// Por ahora retorna datos basicos
const patient = await this.findById(tenantId, patientId);
if (!patient) {
return null;
}
return {
patient,
appointments: [], // TODO: Integrar con AppointmentService
consultations: [], // TODO: Integrar con ConsultationService
prescriptions: [], // TODO: Integrar con PrescriptionService
};
}
}