diff --git a/docker-compose.yml b/docker-compose.yml index 3cd95670d..a5c6e96e3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,8 @@ services: context: ./docker/redis expose: - "6379" + ports: + - "6379:6379" volumes: - redis:/data deploy: diff --git a/packages/server-nest/package.json b/packages/server-nest/package.json index 079aa991a..d99359c1f 100644 --- a/packages/server-nest/package.json +++ b/packages/server-nest/package.json @@ -24,7 +24,7 @@ "@bigcapital/pdf-templates": "*", "@bigcapital/utils": "*", "@nestjs/bull": "^10.2.1", - "@nestjs/bullmq": "^10.2.1", + "@nestjs/bullmq": "^10.2.2", "@nestjs/cache-manager": "^2.2.2", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", @@ -44,7 +44,7 @@ "axios": "^1.6.0", "bluebird": "^3.7.2", "bull": "^4.16.3", - "bullmq": "^5.21.1", + "bullmq": "^5.25.6", "cache-manager": "^6.1.1", "cache-manager-redis-store": "^3.0.1", "class-transformer": "^0.5.1", @@ -58,6 +58,7 @@ "knex": "^3.1.0", "lamda": "^0.4.1", "lodash": "^4.17.21", + "mathjs": "^9.4.0", "moment": "^2.30.1", "mysql": "^2.18.1", "mysql2": "^3.11.3", @@ -85,8 +86,7 @@ "uuid": "^10.0.0", "xlsx": "^0.18.5", "yup": "^0.28.1", - "zod": "^3.23.8", - "mathjs": "^9.4.0" + "zod": "^3.23.8" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -94,10 +94,10 @@ "@nestjs/testing": "^10.0.0", "@types/express": "^5.0.0", "@types/jest": "^29.5.2", + "@types/mathjs": "^6.0.12", "@types/node": "^20.3.1", "@types/supertest": "^6.0.0", "@types/yup": "^0.29.13", - "@types/mathjs": "^6.0.12", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "eslint": "^9.0.0", diff --git a/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.module.ts b/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.module.ts index c7c2c6997..c8f609229 100644 --- a/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.module.ts +++ b/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.module.ts @@ -12,7 +12,6 @@ import { ValidateDeleteBankAccountTransactions } from './commands/ValidateDelete import { BankTransactionGLEntriesService } from './commands/BankTransactionGLEntries'; import { BankingTransactionsApplication } from './BankingTransactionsApplication.service'; import { AutoIncrementOrdersModule } from '../AutoIncrementOrders/AutoIncrementOrders.module'; -import { LedgerModule } from '../Ledger/Ledger.module'; import { DeleteCashflowTransaction } from './commands/DeleteCashflowTransaction.service'; import { CreateBankTransactionService } from './commands/CreateBankTransaction.service'; import { GetBankTransactionService } from './queries/GetBankTransaction.service'; @@ -24,6 +23,7 @@ import { BankingTransactionsController } from './BankingTransactions.controller' import { GetBankAccountsService } from './queries/GetBankAccounts.service'; import { DynamicListModule } from '../DynamicListing/DynamicList.module'; import { BankAccount } from './models/BankAccount'; +import { LedgerModule } from '../Ledger/Ledger.module'; const models = [ RegisterTenancyModel(UncategorizedBankTransaction), diff --git a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.controller.ts b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.controller.ts index bc116d919..65b08fc3a 100644 --- a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.controller.ts +++ b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.controller.ts @@ -1,13 +1,14 @@ -import { Controller, Get } from '@nestjs/common'; -import { Headers, Query, Res } from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { Response } from 'express'; +import { Controller, Get, Headers, Query, Res } from '@nestjs/common'; import { IGeneralLedgerSheetQuery } from './GeneralLedger.types'; import { GeneralLedgerApplication } from './GeneralLedgerApplication'; import { AcceptType } from '@/constants/accept-type'; -import { Response } from 'express'; -import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { PublicRoute } from '@/modules/Auth/Jwt.guard'; @Controller('/reports/general-ledger') @ApiTags('reports') +@PublicRoute() export class GeneralLedgerController { constructor( private readonly generalLedgerApplication: GeneralLedgerApplication, diff --git a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.ts b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.ts index 4b8e6904f..af5febfc8 100644 --- a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.ts +++ b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedger.ts @@ -37,7 +37,7 @@ export class GeneralLedgerSheet extends R.compose(FinancialSheetStructure)( constructor( query: IGeneralLedgerSheetQuery, repository: GeneralLedgerRepository, - i18n, + i18n: I18nService, ) { super(); diff --git a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerRepository.ts b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerRepository.ts index 9a2581f42..cd6c03456 100644 --- a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerRepository.ts +++ b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerRepository.ts @@ -1,21 +1,19 @@ import * as moment from 'moment'; import * as R from 'ramda'; -import { - IGeneralLedgerSheetQuery, - -} from './GeneralLedger.types'; +import { IGeneralLedgerSheetQuery } from './GeneralLedger.types'; import { flatten, isEmpty, uniq } from 'lodash'; import { ModelObject } from 'objection'; import { Account } from '@/modules/Accounts/models/Account.model'; import { AccountTransaction } from '@/modules/Accounts/models/AccountTransaction.model'; import { Contact } from '@/modules/Contacts/models/Contact'; import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository'; -import { Inject } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; import { transformToMap } from '@/utils/transform-to-key'; import { Ledger } from '@/modules/Ledger/Ledger'; import { TenantModel } from '@/modules/System/models/TenantModel'; +@Injectable({ scope: Scope.TRANSIENT }) export class GeneralLedgerRepository { public filter: IGeneralLedgerSheetQuery; public accounts: Account[]; @@ -42,6 +40,12 @@ export class GeneralLedgerRepository { @Inject(AccountRepository) private readonly accountRepository: AccountRepository; + @Inject(AccountTransaction.name) + private readonly accountTransactionModel: typeof AccountTransaction; + + @Inject(Contact.name) + private readonly contactModel: typeof Contact; + @Inject(TenancyContext) private readonly tenancyContext: TenancyContext; @@ -79,24 +83,21 @@ export class GeneralLedgerRepository { */ public async initAccounts() { // @ts-ignore - this.accounts = await this.accountRepository - .all() - .orderBy('name', 'ASC'); + this.accounts = await this.accountRepository.all().orderBy('name', 'ASC'); } /** * Initialize the accounts graph. */ public async initAccountsGraph() { - this.accountsGraph = - await this.repositories.accountRepository.getDependencyGraph(); + this.accountsGraph = await this.accountRepository.getDependencyGraph(); } /** * Initialize the contacts. */ public async initContacts() { - this.contacts = await this.repositories.contactRepository.all(); + this.contacts = await this.contactModel.query(); this.contactsById = transformToMap(this.contacts, 'id'); } @@ -104,17 +105,23 @@ export class GeneralLedgerRepository { * Initialize the G/L transactions from/to the given date. */ public async initTransactions() { - this.transactions = await this.repositories.transactionsRepository - .journal({ - fromDate: this.filter.fromDate, - toDate: this.filter.toDate, - branchesIds: this.filter.branchesIds, - }) - .orderBy('date', 'ASC') + this.transactions = await this.accountTransactionModel + .query() .onBuild((query) => { + query.modify( + 'filterDateRange', + this.filter.fromDate, + this.filter.toDate, + ); + if (!isEmpty(this.filter.branchesIds)) { + query.modify('filterByBranches', this.filter.branchesIds); + } + query.orderBy('date', 'ASC'); + if (this.filter.accountsIds?.length > 0) { query.whereIn('accountId', this.accountNodesIncludeTransactions); } + query.withGraphFetched('account'); }); // Transform array transactions to journal collection. this.transactionsLedger = Ledger.fromTransactions(this.transactions); @@ -124,17 +131,23 @@ export class GeneralLedgerRepository { * Initialize the G/L accounts opening balance. */ public async initAccountsOpeningBalance() { - // Retreive opening balance credit/debit sumation. - this.openingBalanceTransactions = - await this.repositories.transactionsRepository.journal({ - toDate: moment(this.filter.fromDate).subtract(1, 'day'), - sumationCreditDebit: true, - branchesIds: this.filter.branchesIds, - }); + // Retrieves opening balance credit/debit sumation. + this.openingBalanceTransactions = await this.accountTransactionModel + .query() + .onBuild((query) => { + const toDate = moment(this.filter.fromDate).subtract(1, 'day'); + query.modify('sumationCreditDebit'); + query.modify('filterDateRange', null, toDate); + + if (!isEmpty(this.filter.branchesIds)) { + query.modify('filterByBranches', this.filter.branchesIds); + } + query.withGraphFetched('account'); + }); // Accounts opening transactions. this.openingBalanceTransactionsLedger = Ledger.fromTransactions( - this.openingBalanceTransactions + this.openingBalanceTransactions, ); } @@ -149,7 +162,7 @@ export class GeneralLedgerRepository { const childrenNodeIds = this.filter.accountsIds?.map( (accountId: number) => { return this.accountsGraph.dependenciesOf(accountId); - } + }, ); const nodeIds = R.concat(this.filter.accountsIds, childrenNodeIds); @@ -175,7 +188,7 @@ export class GeneralLedgerRepository { this.accountNodeInclude = R.compose( R.uniq, R.flatten, - R.concat(this.filter.accountsIds) + R.concat(this.filter.accountsIds), )(nodeIds); } } diff --git a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerService.ts b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerService.ts index 2fe8c0959..28143d3c1 100644 --- a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerService.ts +++ b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/GeneralLedgerService.ts @@ -1,4 +1,4 @@ -import * as moment from 'moment'; +import { I18nService } from 'nestjs-i18n'; import { GeneralLedgerMeta } from './GeneralLedgerMeta'; import { GeneralLedgerRepository } from './GeneralLedgerRepository'; import { EventEmitter2 } from '@nestjs/event-emitter'; @@ -10,8 +10,6 @@ import { IGeneralLedgerMeta, IGeneralLedgerSheetQuery, } from './GeneralLedger.types'; -import { I18nService } from 'nestjs-i18n'; -import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; @Injectable() export class GeneralLedgerService { @@ -52,8 +50,7 @@ export class GeneralLedgerService { const meta = await this.generalLedgerMeta.meta(filter); // Triggers `onGeneralLedgerViewed` event. - await this.eventEmitter.emitAsync(events.reports.onGeneralLedgerViewed, { - }); + await this.eventEmitter.emitAsync(events.reports.onGeneralLedgerViewed, {}); return { data: reportData, diff --git a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/_utils.ts b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/_utils.ts index b77182e9c..896133870 100644 --- a/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/_utils.ts +++ b/packages/server-nest/src/modules/FinancialStatements/modules/GeneralLedger/_utils.ts @@ -1,3 +1,5 @@ +import * as moment from 'moment'; + /** * Calculate the running balance. * @param {number} amount - Transaction amount. diff --git a/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.application.ts b/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.application.ts index adfb440eb..0b9a36227 100644 --- a/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.application.ts +++ b/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.application.ts @@ -81,7 +81,7 @@ export class SaleEstimatesApplication { /** * Deliver the given sale estimate. - * @param {number} saleEstimateId + * @param {number} saleEstimateId - Sale estimate id. * @returns {Promise} */ public deliverSaleEstimate(saleEstimateId: number) { diff --git a/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.module.ts b/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.module.ts index aa112aab9..83a5e2bef 100644 --- a/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.module.ts +++ b/packages/server-nest/src/modules/SaleEstimates/SaleEstimates.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { BullModule } from '@nestjs/bull'; import { TenancyContext } from '../Tenancy/TenancyContext.service'; import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module'; import { TransformerInjectable } from '../Transformer/TransformerInjectable.service'; @@ -33,7 +34,7 @@ import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy. import { TemplateInjectableModule } from '../TemplateInjectable/TemplateInjectable.module'; import { SaleEstimatePdfTemplate } from '../SaleInvoices/queries/SaleEstimatePdfTemplate.service'; import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module'; -// import { SaleEstimateNotifyBySms } from './commands/SaleEstimateSmsNotify'; +import { SendSaleEstimateMailQueue } from './types/SaleEstimates.types'; @Module({ imports: [ @@ -43,7 +44,8 @@ import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module'; MailModule, ChromiumlyTenancyModule, TemplateInjectableModule, - PdfTemplatesModule + PdfTemplatesModule, + BullModule.registerQueue({ name: SendSaleEstimateMailQueue }), ], controllers: [SaleEstimatesController], providers: [ @@ -73,7 +75,6 @@ import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module'; SendSaleEstimateMail, GetSaleEstimatePdf, SaleEstimatePdfTemplate - // SaleEstimateNotifyBySms, ], }) export class SaleEstimatesModule {} diff --git a/packages/server-nest/src/modules/SaleEstimates/commands/SendSaleEstimateMail.ts b/packages/server-nest/src/modules/SaleEstimates/commands/SendSaleEstimateMail.ts index eea8c24e9..7ba24e966 100644 --- a/packages/server-nest/src/modules/SaleEstimates/commands/SendSaleEstimateMail.ts +++ b/packages/server-nest/src/modules/SaleEstimates/commands/SendSaleEstimateMail.ts @@ -1,3 +1,5 @@ +import { InjectQueue } from '@nestjs/bullmq'; +import { Queue } from 'bull'; import { Inject, Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification'; @@ -14,6 +16,8 @@ import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils'; import { ISaleEstimateMailPresendEvent, SaleEstimateMailOptionsDTO, + SendSaleEstimateMailJob, + SendSaleEstimateMailQueue, } from '../types/SaleEstimates.types'; import { SaleEstimateMailOptions } from '../types/SaleEstimates.types'; import { Mail } from '@/modules/Mail/Mail'; @@ -38,6 +42,8 @@ export class SendSaleEstimateMail { @Inject(SaleEstimate.name) private readonly saleEstimateModel: typeof SaleEstimate, + @InjectQueue(SendSaleEstimateMailQueue) + private readonly sendEstimateMailQueue: Queue, ) {} /** @@ -54,7 +60,7 @@ export class SendSaleEstimateMail { saleEstimateId, messageOptions, }; - // await this.agenda.now('sale-estimate-mail-send', payload); + await this.sendEstimateMailQueue.add(SendSaleEstimateMailJob, payload); // Triggers `onSaleEstimatePreMailSend` event. await this.eventPublisher.emitAsync(events.saleEstimate.onPreMailSend, { diff --git a/packages/server-nest/src/modules/SaleEstimates/processes/SendSaleEstimateMail.process.ts b/packages/server-nest/src/modules/SaleEstimates/processes/SendSaleEstimateMail.process.ts new file mode 100644 index 000000000..76c235b2c --- /dev/null +++ b/packages/server-nest/src/modules/SaleEstimates/processes/SendSaleEstimateMail.process.ts @@ -0,0 +1,19 @@ +import { Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; +import { + SendSaleEstimateMailJob, + SendSaleEstimateMailQueue, +} from '../types/SaleEstimates.types'; +import { SendSaleEstimateMail } from '../commands/SendSaleEstimateMail'; + +@Processor(SendSaleEstimateMailQueue) +export class SendSaleEstimateMailProcess { + constructor(private readonly sendEstimateMailService: SendSaleEstimateMail) {} + + @Process(SendSaleEstimateMailJob) + async handleSendMail(job: Job) { + const { saleEstimateId, messageOptions } = job.data; + + await this.sendEstimateMailService.sendMail(saleEstimateId, messageOptions); + } +} diff --git a/packages/server-nest/src/modules/SaleEstimates/types/SaleEstimates.types.ts b/packages/server-nest/src/modules/SaleEstimates/types/SaleEstimates.types.ts index 4c46c49c9..5fa9690db 100644 --- a/packages/server-nest/src/modules/SaleEstimates/types/SaleEstimates.types.ts +++ b/packages/server-nest/src/modules/SaleEstimates/types/SaleEstimates.types.ts @@ -8,6 +8,9 @@ import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/Dynam import { CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types'; import { CommonMailOptions } from '@/modules/MailNotification/MailNotification.types'; +export const SendSaleEstimateMailQueue = 'SendSaleEstimateMailProcessor'; +export const SendSaleEstimateMailJob = 'SendSaleEstimateMailProcess'; + export interface ISaleEstimateDTO { customerId: number; exchangeRate?: number; @@ -122,3 +125,8 @@ export interface ISaleEstimateMailPresendEvent { export interface ISaleEstimateState { defaultTemplateId: number; } + +export interface ISendSaleEstimateMailProcessData { + saleEstimateId: number; + messageOptions: SaleEstimateMailOptionsDTO; +} diff --git a/packages/server-nest/src/modules/SaleInvoices/SaleInvoice.types.ts b/packages/server-nest/src/modules/SaleInvoices/SaleInvoice.types.ts index 69c0d3da9..5878fd745 100644 --- a/packages/server-nest/src/modules/SaleInvoices/SaleInvoice.types.ts +++ b/packages/server-nest/src/modules/SaleInvoices/SaleInvoice.types.ts @@ -322,3 +322,9 @@ export interface InvoicePdfTemplateAttributes { export interface ISaleInvocieState { defaultTemplateId: number; } + + +export interface SaleInvoiceSendMailData { + saleInvoiceId: number; + messageOptions: SendInvoiceMailDTO; +} \ No newline at end of file diff --git a/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.application.ts b/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.application.ts index 5ca651226..f6aa0857f 100644 --- a/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.application.ts +++ b/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.application.ts @@ -8,8 +8,6 @@ import { GetSaleInvoicesPayable } from './queries/GetSaleInvoicesPayable.service import { WriteoffSaleInvoice } from './commands/WriteoffSaleInvoice.service'; import { SaleInvoicePdf } from './queries/SaleInvoicePdf.service'; import { GetInvoicePaymentsService } from './queries/GetInvoicePayments.service'; -// import { SaleInvoiceNotifyBySms } from './SaleInvoiceNotifyBySms'; -// import { SendInvoiceMailReminder } from './commands/SendSaleInvoiceMailReminder'; import { GetSaleInvoiceState } from './queries/GetSaleInvoiceState.service'; import { GetSaleInvoiceMailState } from './queries/GetSaleInvoiceMailState.service'; import { @@ -39,7 +37,6 @@ export class SaleInvoiceApplication { private getSaleInvoiceStateService: GetSaleInvoiceState, private sendSaleInvoiceMailService: SendSaleInvoiceMail, private getSaleInvoiceMailStateService: GetSaleInvoiceMailState, - // private invoiceSms: SaleInvoiceNotifyBySms, ) {} /** @@ -191,57 +188,9 @@ export class SaleInvoiceApplication { ); } - /** - * - * @param {number} tenantId - * @param {number} saleInvoiceId - * @param {InvoiceNotificationType} invoiceNotificationType - */ - // public notifySaleInvoiceBySms = async ( - // tenantId: number, - // saleInvoiceId: number, - // invoiceNotificationType: InvoiceNotificationType, - // ) => { - // return this.invoiceSms.notifyBySms( - // tenantId, - // saleInvoiceId, - // invoiceNotificationType, - // ); - // }; - - /** - * Retrieves the SMS details of the given invoice. - * @param {number} tenantId - Tenant id. - * @param {number} saleInvoiceId - Sale invoice id. - */ - // public getSaleInvoiceSmsDetails = async ( - // tenantId: number, - // saleInvoiceId: number, - // invoiceSmsDetailsDTO: ISaleInvoiceSmsDetailsDTO, - // ): Promise => { - // return this.invoiceSms.smsDetails( - // tenantId, - // saleInvoiceId, - // invoiceSmsDetailsDTO, - // ); - // }; - - /** - * Retrieves the metadata of invoice mail reminder. - * @param {number} tenantId - * @param {number} saleInvoiceId - * @returns {} - */ - // public getSaleInvoiceMailReminder(tenantId: number, saleInvoiceId: number) { - // return this.sendInvoiceReminderService.getMailOption( - // tenantId, - // saleInvoiceId, - // ); - // } - /** * Retrieves the default mail options of the given sale invoice. - * @param {number} saleInvoiceid + * @param {number} saleInvoiceid - Sale invoice id. * @returns {Promise} */ public getSaleInvoiceMailState( diff --git a/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.module.ts b/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.module.ts index 30df3e959..cfa8e6469 100644 --- a/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.module.ts +++ b/packages/server-nest/src/modules/SaleInvoices/SaleInvoices.module.ts @@ -44,6 +44,9 @@ import { InventoryCostModule } from '../InventoryCost/InventoryCost.module'; import { SendSaleInvoiceMailCommon } from './commands/SendInvoiceInvoiceMailCommon.service'; import { DynamicListModule } from '../DynamicListing/DynamicList.module'; import { MailNotificationModule } from '../MailNotification/MailNotification.module'; +import { SendSaleInvoiceMailProcessor } from './processors/SendSaleInvoiceMail.processor'; +import { BullModule } from '@nestjs/bull'; +import { SendSaleInvoiceQueue } from './constants'; @Module({ imports: [ @@ -60,6 +63,7 @@ import { MailNotificationModule } from '../MailNotification/MailNotification.mod MailNotificationModule, InventoryCostModule, DynamicListModule, + BullModule.registerQueue({ name: SendSaleInvoiceQueue }), ], controllers: [SaleInvoicesController], providers: [ @@ -95,6 +99,7 @@ import { MailNotificationModule } from '../MailNotification/MailNotification.mod GetSaleInvoicesService, GetSaleInvoiceMailState, SendSaleInvoiceMailCommon, + SendSaleInvoiceMailProcessor ], exports: [GetSaleInvoice], }) diff --git a/packages/server-nest/src/modules/SaleInvoices/commands/CreateSaleInvoice.service.ts b/packages/server-nest/src/modules/SaleInvoices/commands/CreateSaleInvoice.service.ts index c4a825ee6..265e157af 100644 --- a/packages/server-nest/src/modules/SaleInvoices/commands/CreateSaleInvoice.service.ts +++ b/packages/server-nest/src/modules/SaleInvoices/commands/CreateSaleInvoice.service.ts @@ -115,19 +115,16 @@ export class CreateSaleInvoice { /** * Transformes create DTO to model. - * @param {number} tenantId - * @param {ICustomer} customer - * @param {ISaleInvoiceCreateDTO} saleInvoiceDTO - */ private transformCreateDTOToModel = async ( customer: Customer, saleInvoiceDTO: ISaleInvoiceCreateDTO, - // authorizedUser: SystemUser, ) => { return this.transformerDTO.transformDTOToModel( customer, saleInvoiceDTO, - // authorizedUser, ); }; } diff --git a/packages/server-nest/src/modules/SaleInvoices/commands/SendSaleInvoiceMail.ts b/packages/server-nest/src/modules/SaleInvoices/commands/SendSaleInvoiceMail.ts index 2f9aa6589..9ae608b99 100644 --- a/packages/server-nest/src/modules/SaleInvoices/commands/SendSaleInvoiceMail.ts +++ b/packages/server-nest/src/modules/SaleInvoices/commands/SendSaleInvoiceMail.ts @@ -1,13 +1,19 @@ import { Injectable } from '@nestjs/common'; +import { Queue } from 'bullmq'; +import { InjectQueue } from '@nestjs/bullmq'; import { SaleInvoicePdf } from '../queries/SaleInvoicePdf.service'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon.service'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { events } from '@/common/events/events'; import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils'; -import { SaleInvoiceMailOptions, SendInvoiceMailDTO } from '../SaleInvoice.types'; +import { + SaleInvoiceMailOptions, + SendInvoiceMailDTO, +} from '../SaleInvoice.types'; import { ISaleInvoiceMailSend } from '../SaleInvoice.types'; import { Mail } from '@/modules/Mail/Mail'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service'; +import { SendSaleInvoiceMailJob, SendSaleInvoiceQueue } from '../constants'; @Injectable() export class SendSaleInvoiceMail { @@ -22,13 +28,13 @@ export class SendSaleInvoiceMail { private readonly invoiceMail: SendSaleInvoiceMailCommon, private readonly eventEmitter: EventEmitter2, private readonly mailTransporter: MailTransporter, + @InjectQueue(SendSaleInvoiceQueue) private readonly sendInvoiceQueue: Queue, ) {} /** * Sends the invoice mail of the given sale invoice. - * @param {number} tenantId - * @param {number} saleInvoiceId - * @param {SendInvoiceMailDTO} messageDTO + * @param {number} saleInvoiceId - Sale invoice id. + * @param {SendInvoiceMailDTO} messageDTO - Message DTO. */ public async triggerMail( saleInvoiceId: number, @@ -38,8 +44,7 @@ export class SendSaleInvoiceMail { saleInvoiceId, messageOptions, }; - // await this.agenda.now('sale-invoice-mail-send', payload); - + await this.sendInvoiceQueue.add(SendSaleInvoiceMailJob, payload); // Triggers the event `onSaleInvoicePreMailSend`. await this.eventEmitter.emitAsync(events.saleInvoice.onPreMailSend, { saleInvoiceId, diff --git a/packages/server-nest/src/modules/SaleInvoices/constants.ts b/packages/server-nest/src/modules/SaleInvoices/constants.ts index 42c783f10..58e6413c1 100644 --- a/packages/server-nest/src/modules/SaleInvoices/constants.ts +++ b/packages/server-nest/src/modules/SaleInvoices/constants.ts @@ -1,5 +1,8 @@ // import config from '@/config'; +export const SendSaleInvoiceQueue = 'SendSaleInvoiceQueue'; +export const SendSaleInvoiceMailJob = 'SendSaleInvoiceMailJob'; + const BASE_URL = 'http://localhost:3000'; export const DEFAULT_INVOICE_MAIL_SUBJECT = diff --git a/packages/server-nest/src/modules/SaleInvoices/processors/SendSaleInvoiceMail.processor.ts b/packages/server-nest/src/modules/SaleInvoices/processors/SendSaleInvoiceMail.processor.ts new file mode 100644 index 000000000..d12cfd6b1 --- /dev/null +++ b/packages/server-nest/src/modules/SaleInvoices/processors/SendSaleInvoiceMail.processor.ts @@ -0,0 +1,30 @@ +import { JOB_REF, Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; +import { SendSaleInvoiceMailJob, SendSaleInvoiceQueue } from '../constants'; +import { SendSaleInvoiceMail } from '../commands/SendSaleInvoiceMail'; +import { Inject, Scope } from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import { UseCls } from 'nestjs-cls'; + +@Processor({ + name: SendSaleInvoiceQueue, + scope: Scope.REQUEST, +}) +export class SendSaleInvoiceMailProcessor { + constructor( + private readonly sendSaleInvoiceMail: SendSaleInvoiceMail, + @Inject(REQUEST) private readonly request: Request, + @Inject(JOB_REF) private readonly jobRef: Job, + ) {} + + @Process(SendSaleInvoiceMailJob) + async handleSendInvoice() { + const { messageOptions, saleInvoiceId } = this.jobRef.data; + + try { + await this.sendSaleInvoiceMail.sendMail(saleInvoiceId, messageOptions); + } catch (error) { + console.log(error); + } + } +} diff --git a/packages/server-nest/src/modules/SaleReceipts/SaleReceiptApplication.service.ts b/packages/server-nest/src/modules/SaleReceipts/SaleReceiptApplication.service.ts index 267d32b8b..100df8b62 100644 --- a/packages/server-nest/src/modules/SaleReceipts/SaleReceiptApplication.service.ts +++ b/packages/server-nest/src/modules/SaleReceipts/SaleReceiptApplication.service.ts @@ -13,6 +13,7 @@ import { ISaleReceiptState, ISalesReceiptsFilter, SaleReceiptMailOpts, + SaleReceiptMailOptsDTO, } from './types/SaleReceipts.types'; import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service'; import { SaleReceipt } from './models/SaleReceipt'; @@ -29,7 +30,6 @@ export class SaleReceiptApplication { private getSaleReceiptsService: GetSaleReceiptsService, private closeSaleReceiptService: CloseSaleReceipt, private getSaleReceiptPdfService: SaleReceiptsPdfService, - // private saleReceiptNotifyBySmsService: SaleReceiptNotifyBySms, private getSaleReceiptStateService: GetSaleReceiptState, private saleReceiptNotifyByMailService: SaleReceiptMailNotification, ) {} @@ -147,16 +147,15 @@ export class SaleReceiptApplication { * @param {SaleReceiptMailOptsDTO} messageOpts * @returns {Promise} */ - // public sendSaleReceiptMail( - // saleReceiptId: number, - // messageOpts: SaleReceiptMailOptsDTO, - // ): Promise { - // return this.saleReceiptNotifyByMailService.triggerMail( - // tenantId, - // saleReceiptId, - // messageOpts, - // ); - // } + public sendSaleReceiptMail( + saleReceiptId: number, + messageOpts: SaleReceiptMailOptsDTO, + ): Promise { + return this.saleReceiptNotifyByMailService.triggerMail( + saleReceiptId, + messageOpts, + ); + } /** * Retrieves the default mail options of the given sale receipt. diff --git a/packages/server-nest/src/modules/SaleReceipts/SaleReceipts.module.ts b/packages/server-nest/src/modules/SaleReceipts/SaleReceipts.module.ts index 60f9242ad..255be3d42 100644 --- a/packages/server-nest/src/modules/SaleReceipts/SaleReceipts.module.ts +++ b/packages/server-nest/src/modules/SaleReceipts/SaleReceipts.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { BullModule } from '@nestjs/bull'; import { SaleReceiptApplication } from './SaleReceiptApplication.service'; import { CreateSaleReceipt } from './commands/CreateSaleReceipt.service'; import { EditSaleReceipt } from './commands/EditSaleReceipt.service'; @@ -30,8 +31,10 @@ import { SaleReceiptMailNotification } from './commands/SaleReceiptMailNotificat import { SaleReceiptInventoryTransactions } from './inventory/SaleReceiptInventoryTransactions'; import { InventoryCostModule } from '../InventoryCost/InventoryCost.module'; import { DynamicListModule } from '../DynamicListing/DynamicList.module'; -import { MailModule } from '../Mail/Mail.module'; import { MailNotificationModule } from '../MailNotification/MailNotification.module'; +import { SendSaleReceiptMailProcess } from './processes/SendSaleReceiptMail.process'; +import { MailModule } from '../Mail/Mail.module'; +import { SendSaleReceiptMailQueue } from './constants'; @Module({ controllers: [SaleReceiptsController], @@ -48,7 +51,8 @@ import { MailNotificationModule } from '../MailNotification/MailNotification.mod InventoryCostModule, DynamicListModule, MailModule, - MailNotificationModule + MailNotificationModule, + BullModule.registerQueue({ name: SendSaleReceiptMailQueue }), ], providers: [ TenancyContext, @@ -70,6 +74,7 @@ import { MailNotificationModule } from '../MailNotification/MailNotification.mod SaleReceiptMailNotification, SaleReceiptInventoryTransactions, SaleReceiptInventoryTransactionsSubscriber, + SendSaleReceiptMailProcess, ], }) export class SaleReceiptsModule {} diff --git a/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts b/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts index fe064ef18..3bc47e857 100644 --- a/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts +++ b/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts @@ -1,6 +1,10 @@ +import { InjectQueue } from '@nestjs/bull'; +import { Queue } from 'bullmq'; import { DEFAULT_RECEIPT_MAIL_CONTENT, DEFAULT_RECEIPT_MAIL_SUBJECT, + SendSaleReceiptMailJob, + SendSaleReceiptMailQueue, } from '../constants'; import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils'; import { transformReceiptToMailDataArgs } from '../utils'; @@ -36,14 +40,16 @@ export class SaleReceiptMailNotification { private readonly mailTransporter: MailTransporter, @Inject(SaleReceipt.name) - private readonly saleReceiptModel: typeof SaleReceipt + private readonly saleReceiptModel: typeof SaleReceipt, + + @InjectQueue(SendSaleReceiptMailQueue) + private readonly sendSaleReceiptMailProcess: Queue, ) {} /** * Sends the receipt mail of the given sale receipt. - * @param {number} tenantId - * @param {number} saleReceiptId - * @param {SaleReceiptMailOptsDTO} messageDTO + * @param {number} saleReceiptId - Sale receipt id. + * @param {SaleReceiptMailOptsDTO} messageDTO - Message DTOs. */ public async triggerMail( saleReceiptId: number, @@ -53,7 +59,7 @@ export class SaleReceiptMailNotification { saleReceiptId, messageOpts: messageOptions, }; - // await this.agenda.now('sale-receipt-mail-send', payload); + this.sendSaleReceiptMailProcess.add(SendSaleReceiptMailJob, { ...payload }); // Triggers the event `onSaleReceiptPreMailSend`. await this.eventEmitter.emitAsync(events.saleReceipt.onPreMailSend, { @@ -70,7 +76,8 @@ export class SaleReceiptMailNotification { public async getMailOptions( saleReceiptId: number, ): Promise { - const saleReceipt = await this.saleReceiptModel.query() + const saleReceipt = await this.saleReceiptModel + .query() .findById(saleReceiptId) .throwIfNotFound(); diff --git a/packages/server-nest/src/modules/SaleReceipts/constants.ts b/packages/server-nest/src/modules/SaleReceipts/constants.ts index b1a1c3898..871688b2d 100644 --- a/packages/server-nest/src/modules/SaleReceipts/constants.ts +++ b/packages/server-nest/src/modules/SaleReceipts/constants.ts @@ -14,6 +14,9 @@ Amount : {Receipt Amount}

`; +export const SendSaleReceiptMailQueue = 'SendSaleReceiptMailQueue'; +export const SendSaleReceiptMailJob = 'SendSaleReceiptMailJob'; + export const ERRORS = { SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND', DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND', diff --git a/packages/server-nest/src/modules/SaleReceipts/processes/SendSaleReceiptMail.process.ts b/packages/server-nest/src/modules/SaleReceipts/processes/SendSaleReceiptMail.process.ts new file mode 100644 index 000000000..d77c504b8 --- /dev/null +++ b/packages/server-nest/src/modules/SaleReceipts/processes/SendSaleReceiptMail.process.ts @@ -0,0 +1,18 @@ +import { Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; +import { SendSaleReceiptMailQueue } from '../constants'; +import { SaleReceiptMailNotification } from '../commands/SaleReceiptMailNotification'; + +@Processor(SendSaleReceiptMailQueue) +export class SendSaleReceiptMailProcess { + constructor( + private readonly saleReceiptMailNotification: SaleReceiptMailNotification, + ) {} + + @Process(SendSaleReceiptMailQueue) + async handleSendMailJob(job: Job) { + const { messageOpts, saleReceiptId } = job.data; + + await this.saleReceiptMailNotification.sendMail(saleReceiptId, messageOpts); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecaa78f4f..392e37acd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -497,7 +497,7 @@ importers: specifier: ^10.2.1 version: 10.2.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(bull@4.16.4) '@nestjs/bullmq': - specifier: ^10.2.1 + specifier: ^10.2.2 version: 10.2.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(bullmq@5.25.6) '@nestjs/cache-manager': specifier: ^2.2.2 @@ -557,7 +557,7 @@ importers: specifier: ^4.16.3 version: 4.16.4 bullmq: - specifier: ^5.21.1 + specifier: ^5.25.6 version: 5.25.6 cache-manager: specifier: ^6.1.1