import { Injectable } from '@nestjs/common' import { debug, Debugger } from 'debug' import { isNil } from 'lodash' import { CancellationsReasons, Patient, PaymentProviders, Plan, SubscriptionSmartfit } from '../../../models' import { NotFoundError } from '../../../errors' import { PatientRepository, PlanRepository, SubscriptionRepository, SubscriptionSmartfitRepository, } from '../../../repositories' import { AccountService, SubscriptionService } from '../../../services' @Injectable() export class Concilliation { private debug: Debugger = debug(this.constructor.name) private plans: { [key: string]: { addonId: string; plan: Plan } } = {} constructor( private readonly patientRepository: PatientRepository, private readonly planRepository: PlanRepository, private readonly subscriptionRepository: SubscriptionRepository, private readonly subscriptionSmartfitRepository: SubscriptionSmartfitRepository, private readonly subscription: SubscriptionService, private readonly account: AccountService, ) {} async run(): Promise { const report = await this.getLastReport() for (const row of report) { try { let patient = await this.getPatient(row) if (isNil(patient)) patient = await this.account.createPatientIfStudentOrCollaborator( row[Patient.loginField(row.countryCode)], null, ) if (await this.checkIfIsCancelled(patient, row)) continue await this.checkIfIsActive(patient, row) } catch (error) { this.debug(`Concilliation error: ${row.documentNumber}: ${error.message}`) } } } private async getLastReport(): Promise { return this.subscriptionSmartfitRepository.getReport() } private async checkIfIsCancelled(patient: Patient, infoAddon: SubscriptionSmartfit): Promise { if (isNil(infoAddon.cancellationRequestedAt)) return false if (!patient.subscriber) return false const plan = await this.getPlan(patient.subscription.planId) if (plan.getPaymentProvider(PaymentProviders.SMARTFIT).smartfit_id !== infoAddon.addonId) return true const subscription = await this.subscriptionRepository.get(patient.subscription.id) subscription.fillCancellationDataFromSmartfit(infoAddon) await this.subscriptionRepository.save(subscription, ['cancellationRequestAt', 'cancellationAt', 'trialExpiresAt']) return true } private async checkIfIsActive(patient: Patient, infoAddon: SubscriptionSmartfit): Promise { if (!isNil(infoAddon.cancellationRequestedAt)) return false if (patient.subscriber) { const plan = await this.getPlan(patient.subscription.planId) if (plan.getPaymentProvider(PaymentProviders.SMARTFIT).smartfit_id === infoAddon.addonId) return true await this.subscription.cancelSubscription({ patient, cancellationReason: { id: CancellationsReasons.PLAN_REPLACEMENT }, cancellationBy: { id: patient.id }, }) } await this.subscription.addSubscription({ patient, plan: await this.getPlanByAddonId(infoAddon.addonId), paymentProvider: { provider: PaymentProviders.SMARTFIT }, data: {}, options: { trialExpiresAt: infoAddon.trialExpiresAt, }, }) return true } private async getPlan(id: string): Promise { if (!isNil(this.plans[id])) return this.plans[id].plan const plan = await this.planRepository.get(id) if (isNil(plan)) throw new NotFoundError(`Plano ${id} não encontrado`) this.addCachedPlan(plan) return plan } private async getPlanByAddonId(addonId: string): Promise { let plan = Object.values(this.plans).find(plan => plan.addonId === addonId)?.plan if (!isNil(plan)) return plan plan = await this.planRepository.findByAddonId(addonId) if (isNil(plan)) throw new NotFoundError(`Plano com addon ${addonId} não encontrado`) this.addCachedPlan(plan) return plan } private getPatient(row: SubscriptionSmartfit): Promise { return Patient.loginField(row.countryCode) === 'document_number' ? this.patientRepository.findByDocumentNumber(row.documentNumber) : this.patientRepository.findByEmail(row.email) } private addCachedPlan(plan: Plan): void { this.plans[plan.id] = { addonId: plan.getPaymentProvider(PaymentProviders.SMARTFIT).smartfit_id, plan } } }