erp-clinicas-backend-v2/src/modules/insurance/services/insurance-company.service.ts
Adrian Flores Cortes 1b38818354 [CL-010] feat: Implement insurance module for medical insurance management
Implements complete insurance module with:
- Insurance company catalog (private, government, HMO, PPO)
- Insurance plans with coverage details, deductibles, copays
- Patient insurance policies with verification workflow
- Insurance claims with full lifecycle (draft->submitted->approved/denied/paid)
- Preauthorization requests with approval workflow
- Coverage verification endpoint
- EOB (Explanation of Benefits) tracking
- Aging reports for claims management

Entities: InsuranceCompany, InsurancePlan, PatientInsurance, InsuranceClaim, Preauthorization
Services: Full CRUD + workflow operations for each entity
Controller: 50+ REST endpoints for all insurance operations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 19:59:47 -06:00

113 lines
3.5 KiB
TypeScript

import { DataSource, Repository } from 'typeorm';
import { InsuranceCompany } from '../entities';
import { CreateInsuranceCompanyDto, UpdateInsuranceCompanyDto, InsuranceCompanyQueryDto } from '../dto';
export class InsuranceCompanyService {
private repository: Repository<InsuranceCompany>;
constructor(dataSource: DataSource) {
this.repository = dataSource.getRepository(InsuranceCompany);
}
async findAll(tenantId: string, query: InsuranceCompanyQueryDto): Promise<{ data: InsuranceCompany[]; total: number }> {
const { search, type, status, page = 1, limit = 50 } = query;
const queryBuilder = this.repository.createQueryBuilder('company')
.where('company.tenant_id = :tenantId', { tenantId })
.andWhere('company.deleted_at IS NULL');
if (type) {
queryBuilder.andWhere('company.type = :type', { type });
}
if (status) {
queryBuilder.andWhere('company.status = :status', { status });
}
if (search) {
queryBuilder.andWhere(
'(company.code ILIKE :search OR company.name ILIKE :search OR company.legal_name ILIKE :search)',
{ search: `%${search}%` }
);
}
queryBuilder
.orderBy('company.name', 'ASC')
.skip((page - 1) * limit)
.take(limit);
const [data, total] = await queryBuilder.getManyAndCount();
return { data, total };
}
async findById(tenantId: string, id: string): Promise<InsuranceCompany | null> {
return this.repository.findOne({
where: { id, tenantId },
relations: ['plans'],
});
}
async findByCode(tenantId: string, code: string): Promise<InsuranceCompany | null> {
return this.repository.findOne({
where: { code, tenantId },
});
}
async findActive(tenantId: string): Promise<InsuranceCompany[]> {
return this.repository.find({
where: { tenantId, status: 'active' },
order: { name: 'ASC' },
});
}
async create(tenantId: string, dto: CreateInsuranceCompanyDto): Promise<InsuranceCompany> {
const existing = await this.findByCode(tenantId, dto.code);
if (existing) {
throw new Error(`Insurance company with code ${dto.code} already exists`);
}
const company = this.repository.create({
...dto,
tenantId,
});
return this.repository.save(company);
}
async update(tenantId: string, id: string, dto: UpdateInsuranceCompanyDto): Promise<InsuranceCompany | null> {
const company = await this.findById(tenantId, id);
if (!company) return null;
if (dto.code && dto.code !== company.code) {
const existing = await this.findByCode(tenantId, dto.code);
if (existing) {
throw new Error(`Insurance company with code ${dto.code} already exists`);
}
}
Object.assign(company, dto);
return this.repository.save(company);
}
async softDelete(tenantId: string, id: string): Promise<boolean> {
const company = await this.findById(tenantId, id);
if (!company) return false;
await this.repository.softDelete(id);
return true;
}
async getByType(tenantId: string): Promise<{ type: string; count: number }[]> {
const result = await this.repository.createQueryBuilder('company')
.select('company.type', 'type')
.addSelect('COUNT(*)', 'count')
.where('company.tenant_id = :tenantId', { tenantId })
.andWhere('company.deleted_at IS NULL')
.andWhere('company.status = :status', { status: 'active' })
.groupBy('company.type')
.orderBy('company.type', 'ASC')
.getRawMany();
return result.map(r => ({ type: r.type, count: parseInt(r.count, 10) }));
}
}