import { Injectable } from '@nestjs/common' import { isNil } from 'lodash' import { BusinessExceptionError } from '../../errors' import { Credit, CreditAuthor, CreditPending, CreditReason, getCreditAuthorType, Products, Subscription, } from '../../models' import { CreditPendingRepository, CreditRepository } from '../../repositories' import { FieldOptions } from '../../repositories/base.repository' export type CreditOpArgs = { patientId?: string reason: CreditReason | any value: number objectModel?: Subscription | any author?: CreditAuthor identifier?: { documentNumber?: string email?: string externalId?: string } product?: Products campaignValue?: string creditId?: string extraArgs?: Partial } export type CreateTransactionResult = { idTransaction?: string status: 'pending' | 'created' } @Injectable() export class CreditService< Model extends Credit = Credit, Repository extends CreditRepository = CreditRepository > { constructor( private readonly creditModel: new (args?) => Model, protected readonly creditRepository: Repository, protected readonly creditPendingRepository: CreditPendingRepository, ) {} async save(credit: Credit, fields: Array): Promise { return this.creditRepository.save(credit, fields) } async getBalance(patientId: string): Promise { return this.creditRepository.getSimpleBalance(patientId) } async getNextValidCreditToUse(patientId: string): Promise { return this.creditRepository.getNextValidCreditToUse(patientId) } async getCount(patientId: string): Promise { return this.creditRepository.getCount(patientId) } async getCompleteBalance(patientId: string): Promise<{ balance: number; currentBalanceSingleBuysCount: number }> { const { balance, nodes } = await this.creditRepository.getComplexBalance(patientId) let countTotal = 0 let currentBalanceSingleBuysCount = 0 for (const item of nodes) { if (item.reason === 'single_buy') currentBalanceSingleBuysCount += 1 countTotal += item.value if (countTotal === balance) break } return { balance, currentBalanceSingleBuysCount } } async createTransaction({ patientId, reason, value, objectModel, author, identifier, product, campaignValue, creditId, extraArgs, }: CreditOpArgs): Promise { if (isNil(patientId)) return { idTransaction: await this.createCreditPending({ reason, value, created_by_type: getCreditAuthorType(author), document_number: identifier?.documentNumber, email: identifier?.email, product, created_by_id: author.id || identifier.externalId, }), status: 'pending', } const credit = this.creditModel['createBy']({ reason, value, objectModel, author, patient_id: patientId, campaign_value: campaignValue, credit_id: creditId, ...extraArgs, }) return { idTransaction: await this.creditRepository.create(credit), status: 'created' } } async hasByDateAndReasons(patientId: string, date: Date, reasons: Array): Promise { const credits = await this.creditRepository.findPatientCreditsByDateAndReasons(patientId, date, reasons) return credits.length > 0 } async freePendingCredit(creditPending: CreditPending, patientId: string): Promise { const transaction = await this.createTransaction({ patientId, reason: creditPending.reason, value: creditPending.value, author: { id: creditPending.created_by_id, type: creditPending.created_by_type }, product: creditPending.product, }) await this.creditPendingRepository.save({ id: creditPending.id, credit_id: transaction.idTransaction }, [ 'credit_id', ]) return transaction } protected async undoCreditTransaction( refundedCredit: Credit, objectModel?: Subscription | any, extraArgs?: Partial, ): Promise { return this.createTransaction({ patientId: refundedCredit.patient_id, reason: 'other', value: refundedCredit.value * -1, author: { type: 'system' }, creditId: refundedCredit.id, extraArgs, objectModel, }) } private async createCreditPending(data: Partial): Promise { if (isNil(data.document_number) && isNil(data.email)) throw new BusinessExceptionError('DocumentNumber or email is required for pending credits.') const creditPending = CreditPending.toInstance(data) return this.creditPendingRepository.create(creditPending) } }