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>
113 lines
3.5 KiB
TypeScript
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) }));
|
|
}
|
|
}
|