import { ICreditNoteOpenedPayload, ICreditNoteOpeningPayload, } from '../types/CreditNotes.types'; import { Knex } from 'knex'; import { Inject, Injectable } from '@nestjs/common'; import { ERRORS } from '../constants'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { CreditNote } from '../models/CreditNote'; import { events } from '@/common/events/events'; import { ServiceError } from '@/modules/Items/ServiceError'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; @Injectable() export class OpenCreditNoteService { /** * @param {EventEmitter2} eventPublisher - The event publisher. * @param {UnitOfWork} uow - The unit of work. * @param {typeof CreditNote} creditNoteModel - The credit note model. */ constructor( private readonly eventPublisher: EventEmitter2, private readonly uow: UnitOfWork, @Inject(CreditNote.name) private readonly creditNoteModel: TenantModelProxy, ) {} /** * Opens the given credit note. * @param {number} creditNoteId - * @returns {Promise} */ public openCreditNote = async (creditNoteId: number): Promise => { // Retrieve the sale invoice or throw not found service error. const oldCreditNote = await this.creditNoteModel() .query() .findById(creditNoteId) .throwIfNotFound(); // Throw service error if the credit note is already open. this.throwErrorIfAlreadyOpen(oldCreditNote); // Triggers `onCreditNoteOpen` event. this.eventPublisher.emitAsync(events.creditNote.onOpen, { creditNoteId, oldCreditNote, }); // Sales the credit note transactions with associated entries. return this.uow.withTransaction(async (trx: Knex.Transaction) => { const eventPayload = { oldCreditNote, trx, } as ICreditNoteOpeningPayload; // Triggers `onCreditNoteOpening` event. await this.eventPublisher.emitAsync( events.creditNote.onOpening, eventPayload, ); // Saves the credit note graph to the storage. const creditNote = await this.creditNoteModel() .query(trx) .updateAndFetchById(creditNoteId, { openedAt: new Date(), }); // Triggers `onCreditNoteOpened` event. await this.eventPublisher.emitAsync(events.creditNote.onOpened, { ...eventPayload, creditNote, } as ICreditNoteOpenedPayload); return creditNote; }); }; /** * Throws an error if the given credit note is already open. * @param {CreditNote} creditNote - */ public throwErrorIfAlreadyOpen = (creditNote: CreditNote) => { if (creditNote.openedAt) { throw new ServiceError(ERRORS.CREDIT_NOTE_ALREADY_OPENED); } }; }