UNPKG

eust-conciliation

Version:
142 lines (112 loc) 5.38 kB
/* eslint-disable no-await-in-loop */ /* eslint-disable no-restricted-syntax */ import { Inject } from '@severo-tech/injection-decorator'; import { IConciliationClient, IInvoiceClient, ILogger } from '../../../contracts'; import { Invoice } from '../../../../domain/invoice'; import { CreateEustIssueUseCase } from '../..'; import { Debit } from '../../../../domain/debit'; import { InvoiceInCancellationPeriod, MultipleInvoices } from '../../../../domain/errors'; import { EustIssueType, EustIssueUnit } from '../../../../domain/eust-issue'; import { CombinationsUtil } from '../../../utils'; export abstract class ConciliationBase { @Inject('ZenviaLogger') protected readonly logger!: ILogger; @Inject('Invoice01EUSTClient') private readonly innerInvoiceClient!: IInvoiceClient; @Inject('Conciliation01EUSTClient') private readonly innerConciliationClient!: IConciliationClient; @Inject('CreateEustIssueUseCase') private readonly innerCreateEustIssueUseCase!: CreateEustIssueUseCase; @Inject('CombinationsUtil') private readonly innerCombinationsUtil!: CombinationsUtil; protected async tryToConciliateDebits(startDate: Date, endDate: Date, debits: Debit[], emitterDocument: string, recipientDocument: string, value: number): Promise<void> { try { const invoices = await this.getInvoicesToConciliate(startDate, endDate, emitterDocument, recipientDocument, value); if (invoices?.length) { this.logger.debug(`"ConciliationBase.tryToConciliateDebits" - Invoice Conciliated! UserCNPJ [${recipientDocument}] | TransmitterCNPJ [${emitterDocument}] | Invoice [${JSON.stringify(invoices)}] | Value [${value}]`); await this.conciliateDebits(invoices, debits, false); } } catch (error) { await this.handleError(error, debits); } } protected async tryToConciliateInvoice(startDate: Date, endDate: Date, debits: Debit[], emitterDocument: string, recipientDocument: string): Promise<void> { const invoices = await this.innerInvoiceClient.getUnreconciled({ startDate, endDate, transmitterCNPJ: emitterDocument, userCNPJ: recipientDocument, }); let innerDebits = [...debits]; for (const invoice of invoices) { const combinations = this.innerCombinationsUtil.byValue(innerDebits, +invoice.value); if (combinations.length === 1) { const [match] = combinations; try { await this.validateInvoicesIssueDate([invoice]); await this.conciliateDebits([invoice], match, false); } catch (error) { await this.handleError(error, debits); } innerDebits = innerDebits .filter((debit: Debit): boolean => !match.some((el: Debit): boolean => el.id === debit.id)); } } } protected async conciliateDebits(invoices: Invoice[], debits: Debit[], handleError = true): Promise<void> { try { await this.innerConciliationClient.conciliate(debits, invoices); } catch (error) { if (handleError) await this.handleError(error, debits); throw error; } } protected async getInvoicesToConciliate(startDate: Date, endDate: Date, emitterDocument: string, recipientDocument: string, value: number): Promise<Invoice[]> { const candidates = await this.innerInvoiceClient.getUnreconciled({ startDate, endDate, transmitterCNPJ: emitterDocument, userCNPJ: recipientDocument, }); if (!candidates?.length) { return []; // Cannot conciliate } const invoices = this.processCandidates(candidates, value); await this.validateInvoicesIssueDate(invoices); return invoices; } protected async validateInvoicesIssueDate(invoices: Invoice[]): Promise<void> { const notOlderEnough = invoices.filter(({ createdAt }: Invoice): boolean => !this.isOlderThan24Hours(createdAt)); if (notOlderEnough?.length) { const numbers = notOlderEnough.map(({ number }: Invoice): string => number); throw new InvoiceInCancellationPeriod(`There is(are) ${numbers.length} invoice(s) that is(are) under the Cancellation Period. [${numbers.join(', ')}]`); } } private isOlderThan24Hours(date: Date): boolean { const currentDate = new Date(); const diff = (currentDate.getTime() - date.getTime()) / 1000 / 60 / 60; return diff >= 24; } private processCandidates(candidates: Invoice[], value: number): Invoice[] { const lazyCandidates = candidates.map((el: Invoice) => ({ id: el.id, value: el.value })); const invoiceCombinations = this.innerCombinationsUtil.byValue(lazyCandidates, value, true); if (!invoiceCombinations?.length) { return []; } if (invoiceCombinations.length > 1) { throw new MultipleInvoices(`Found ${invoiceCombinations.length} invoice combinations. User must to do manual conciliation.`); } return invoiceCombinations[0] .map(({ id }): Invoice => candidates.find((el: Invoice): boolean => el.id === id) as Invoice); } protected async handleError(error: any, debits: Debit[]): Promise<void> { await this.innerCreateEustIssueUseCase.execute({ unit: EustIssueUnit.Conciliation, type: error?.code ?? EustIssueType.InternalError, debitIds: debits.map(({ id }: Debit): string => id), description: error?.message, causedBy: error, resolved: !!error?.resolved, }); } }