import { Injectable } from '@nestjs/common' import { addMonths } from 'date-fns' import { BusinessExceptionError } from '../../../errors' import { CreditAuthor, Patient, Plan, PlanFeature, Subscription } from '../../../models' import { PatientRepository, PlanRepository, SubscriptionRepository } from '../../../repositories' import { COLLABORATOR_TELECONSULTATIONS_CREDIT, TeleconsultationsCreditService } from '../../../services' import { chunkedEach } from '../../../utils/async' const CHUNK_SIZE = 50 @Injectable() export class TeleconsultationsCredit { constructor( private readonly teleconsultationsCreditService: TeleconsultationsCreditService, private readonly planRepository: PlanRepository, private readonly subscriptionRepository: SubscriptionRepository, private readonly patientRepository: PatientRepository, ) {} async renewSubscriptionsTeleconsultationsCreditsByPlans(date: Date): Promise { const plans = await this.planRepository.findEnabledWithTeleconsultations() for (const plan of plans) await this.renewTeleconsultationsCreditsForPlan(plan, date) } async renewCollaboratorsTeleconsultationsCredits(criationAnniversaryDate: Date): Promise { const collaborators = await this.patientRepository.findCollaboratorsByCreationAnniversary(criationAnniversaryDate) await chunkedEach(collaborators, CHUNK_SIZE, async (collaborator: Patient) => { try { await this.renewTeleconsultationCreditForCollaborator(collaborator, criationAnniversaryDate) } catch (error) { console.error(error, collaborator) } }) } private async renewTeleconsultationsCreditsForPlan(plan: Plan, date: Date): Promise { const subscriptions = await this.subscriptionRepository.findByTeleconsultationRenew(plan.id, date) const planFeature = plan.getFeature('teleconsultation') await chunkedEach( subscriptions, CHUNK_SIZE, async (subscription: Subscription) => await this.renewSubscripton(subscription, planFeature), ) } private async renewSubscripton(subscription: Subscription, teleconsultationPlanFeature: PlanFeature): Promise { try { await this.renewSubscriptionTeleconsultationsCredits(subscription, teleconsultationPlanFeature.quantity) await this.updateSubscriptionRenewsAt(subscription, teleconsultationPlanFeature.intervalInMonths) } catch (error) { console.error(error, subscription) } } private async updateSubscriptionRenewsAt(subscription: Subscription, intervalInMonths: number): Promise { subscription.teleconsultationRenewsAt = addMonths(new Date(), intervalInMonths) await this.subscriptionRepository.updateRenewsAt(subscription) } private async renewSubscriptionTeleconsultationsCredits(subscription: Subscription, addValue: number): Promise { const common = { patientId: subscription.patientId, objectModel: subscription, author: { type: 'system' } as CreditAuthor, } await this.expiresCreditsNotUsed(common) if (subscription.cancellationAt) return await this.teleconsultationsCreditService.createTransaction({ ...common, value: addValue, reason: 'period_renewal', }) } private async renewTeleconsultationCreditForCollaborator(collaborator: Patient, date: Date): Promise { const hasTransaction = await this.teleconsultationsCreditService.hasByDateAndReasons(collaborator.id, date, [ 'collaborator_signup', 'period_renewal', ]) if (hasTransaction) return const common = { patientId: collaborator.id, author: { type: 'system' } as CreditAuthor, } await this.expiresCreditsNotUsed(common) await this.teleconsultationsCreditService.createTransaction({ ...common, value: COLLABORATOR_TELECONSULTATIONS_CREDIT, reason: 'period_renewal', }) } private async expiresCreditsNotUsed(params: { patientId: string author: CreditAuthor objectModel?: Subscription }): Promise { const { balance, currentBalanceSingleBuysCount } = await this.teleconsultationsCreditService.getCompleteBalance( params.patientId, ) if (balance < 0) throw new BusinessExceptionError( `Teleconsultations credid balance can't be negative! Patient ${params.patientId}`, ) if (balance > 0) await this.teleconsultationsCreditService.createTransaction({ ...params, value: currentBalanceSingleBuysCount - balance, reason: 'end_of_period_expiration', }) } }