import { Injectable } from '@nestjs/common' import { isNil } from 'lodash' import { PlanNotFoundError, UnprocessableEntityError } from '../../../../errors' import { CancellationsReasons, CommonOtherReasons, Patient, PaymentProviders, Plan } from '../../../../models' import { PatientRepository, PlanRepository, SubscriptionRepository } from '../../../../repositories' import { Addon, COLLABORATOR_TELECONSULTATIONS_CREDIT, Person, SubscriptionService, TeleconsultationsCreditService, } from '../../../../services' import { CountryCode } from '../../../../utils/countries' import { CheckSubscription } from './check-subscription' @Injectable() export class SmartfitCheckSubscription implements CheckSubscription { constructor( private readonly patientRepository: PatientRepository, private readonly subscriptionService: SubscriptionService, private readonly subscriptionRepository: SubscriptionRepository, private readonly planRepository: PlanRepository, private readonly teleconsultationsCreditService: TeleconsultationsCreditService, ) {} async check(patient: Patient): Promise { const person = await this.subscriptionService.getPerson(patient.login) if (!person) return false if (!patient.is_smartfit) await this.updatePatientWithPersonData(patient, person) const isCollaborator = await this.checkIfIsCollaborator(patient, person) if (isCollaborator) return isCollaborator const activeAddon = await this.subscriptionService.getActiveAddon(person) if (patient.subscriber && (await this.checkSubscriber(patient, activeAddon))) return true if (!isNil(activeAddon)) return this.checkNonSubscriberWithAddon(patient, activeAddon) return false } private async updatePatientWithPersonData(patient: Patient, person: Person): Promise { await this.patientRepository.save( { id: patient.id, smartfit_token: person.authToken, is_smartfit: !!person.authToken, smartfit_unit_acronym: person.locationAcronym, }, ['smartfit_token', 'is_smartfit', 'smartfit_unit_acronym'], ) } private async checkIfIsCollaborator(patient: Patient, person: Person): Promise { if (patient.collaborator === person.isCollaborator) return patient.collaborator if (person.isCollaborator && !patient.collaborator && patient.hasActiveSubscription) await this.subscriptionService.cancelSubscription({ patient, cancellationReason: { id: CancellationsReasons.OTHER, otherReason: CommonOtherReasons.BECAME_COLLABORATOR }, cancellationBy: { id: patient.id }, }) if (patient.collaborator && !person.isCollaborator) await this.removeCollaboratorCredits(patient) patient.collaborator = person.isCollaborator await this.patientRepository.save(patient, ['collaborator']) if (patient.collaborator) await this.teleconsultationsCreditService.createTransaction({ patientId: patient.id, value: COLLABORATOR_TELECONSULTATIONS_CREDIT, reason: 'collaborator_signup', author: patient, }) return patient.collaborator } private async removeCollaboratorCredits(patient: Patient): Promise { const balance = await this.teleconsultationsCreditService.getCompleteBalance(patient.id) await this.teleconsultationsCreditService.createTransaction({ patientId: patient.id, value: -(balance.balance - balance.currentBalanceSingleBuysCount), reason: 'collaborator_leave', author: { type: 'system' }, }) } private async checkSubscriber(patient: Patient, activeAddon: Addon): Promise { if (!activeAddon) { const subscription = patient.subscription subscription.expiresAt = subscription.expiresAt || new Date() subscription.cancellationRequestAt = subscription.cancellationRequestAt || new Date() subscription.cancellationAt = new Date() await this.subscriptionRepository.cancel(subscription, { id: CancellationsReasons.BY_PROVIDER }, patient.id) return false } if (!patient.subscription.subscribedAt) { const plan = await this.getPlan({ id: patient.subscription.planId, countryCode: patient.country_code as CountryCode, }) patient.subscription.subscribedAt = patient.subscription.createdAt if ( activeAddon?.createdAt && plan.getPaymentProvider(PaymentProviders.SMARTFIT).smartfit_id === activeAddon?.addonId ) patient.subscription.subscribedAt = activeAddon?.createdAt await this.subscriptionRepository.updateSubscribedAt(patient.subscription) } if ( patient.subscription.status === 'in_cancellation' && !isNil(patient.subscription.expiresAt) && patient.subscription.expiresAt <= new Date() ) { const subscription = await this.subscriptionRepository.get(patient.subscription.id) patient.subscription.cancellationAt = new Date() await this.subscriptionRepository.cancel( patient.subscription, { id: subscription.cancellationReasonId, otherReason: subscription.cancellationOtherReason }, subscription.cancellationById, ) return false } return this.updateAddonSubscription(patient, activeAddon) } private async updateAddonSubscription(patient: Patient, addon: Addon): Promise { if (isNil(addon)) return patient.subscriber const plan = await this.getPlan({ id: patient.subscription.planId, countryCode: patient.country_code as CountryCode, }) if ( !(addon.isActive || addon.isInTrial) || plan.getPaymentProvider(PaymentProviders.SMARTFIT).smartfit_id === addon.addonId ) return patient.subscriber await this.subscriptionService.cancelSubscription({ patient, cancellationReason: { id: CancellationsReasons.ADDON_CANCELLED }, cancellationBy: { id: patient.id }, }) const newAddon = await this.getPlan({ addonId: addon.addonId, countryCode: patient.country_code as CountryCode }) await this.subscriptionService.addSubscription({ patient, plan: newAddon, paymentProvider: { provider: PaymentProviders.SMARTFIT }, options: { subscribedAt: addon.createdAt, }, data: {}, }) return true } private async checkNonSubscriberWithAddon(patient: Patient, activeAddon: Addon): Promise { if (isNil(activeAddon)) throw new UnprocessableEntityError('Não foi encontrado uma assinatura ativa') const plan = await this.getPlan({ addonId: activeAddon.addonId, countryCode: patient.country_code as CountryCode }) await this.subscriptionService.addSubscription({ patient, plan, paymentProvider: { provider: PaymentProviders.SMARTFIT }, options: { subscribedAt: activeAddon.createdAt }, data: {}, }) return true } private async getPlan({ id, addonId, countryCode, }: { id?: string addonId?: string countryCode: CountryCode }): Promise { let plan: Plan if (!isNil(id)) plan = await this.planRepository.getWithFeatures(id, countryCode, PaymentProviders.SMARTFIT) else if (!isNil(addonId)) plan = await this.planRepository.findByAddonId(addonId) if (isNil(plan)) throw new PlanNotFoundError(`id: ${id}`) return plan } }