From ab717b96ac877b9bdddb21bb6fc5863158426de5 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 31 Mar 2025 00:39:00 +0200 Subject: [PATCH] refactor(nestjs): e2e test cases --- .../commands/CreateBankTransaction.service.ts | 3 ++- .../commands/BillDTOTransformer.service.ts | 11 +++++--- .../src/modules/Branches/BranchesSettings.ts | 23 ++++++++++------ .../ActivateBranchesFeature.service.ts | 4 +-- .../BranchTransactionDTOTransform.ts | 14 +++++----- .../ManualJournalDTOTransformer.service.ts | 27 ++++++++++--------- .../CommandCreditNoteDTOTransform.service.ts | 26 ++++++++++-------- .../CreditNotes/dtos/CreditNote.dto.ts | 3 ++- .../commands/CommandExpenseDTO.transformer.ts | 17 +++++++----- .../CreateQuickInventoryAdjustment.service.ts | 4 +-- .../commands/CreateManualJournal.service.ts | 3 ++- .../commands/PaymentReceivedDTOTransformer.ts | 8 +++--- .../SaleEstimateDTOTransformer.service.ts | 10 +++---- .../SaleEstimates/dtos/SaleEstimate.dto.ts | 3 ++- ...ommandSaleInvoiceDTOTransformer.service.ts | 11 ++++---- .../commands/CreateSaleInvoice.service.ts | 1 - .../SaleReceiptDTOTransformer.service.ts | 15 ++++++----- .../commands/SaleReceiptMailNotification.ts | 6 ++--- .../Tenancy/TenancyDB/TenancyDB.module.ts | 15 +++++++++-- .../Tenancy/TenancyModels/Tenancy.module.ts | 3 ++- .../VendorCreditDTOTransform.service.ts | 5 ++-- .../CreateRefundVendorCredit.service.ts | 7 ++--- .../WarehouseTransactionDTOTransform.ts | 26 +++++++++--------- .../Integrations/WarehousesDTOValidators.ts | 6 +++-- .../WarehousesItemsQuantitySynSubscriber.ts | 4 +-- .../modules/Warehouses/WarehousesSettings.ts | 25 ++++++++++------- .../commands/ActivateWarehouses.service.ts | 2 +- .../test/sale-estimates.e2e-spec.ts | 1 + 28 files changed, 168 insertions(+), 115 deletions(-) diff --git a/packages/server-nest/src/modules/BankingTransactions/commands/CreateBankTransaction.service.ts b/packages/server-nest/src/modules/BankingTransactions/commands/CreateBankTransaction.service.ts index 8c1bb19d6..f1244a311 100644 --- a/packages/server-nest/src/modules/BankingTransactions/commands/CreateBankTransaction.service.ts +++ b/packages/server-nest/src/modules/BankingTransactions/commands/CreateBankTransaction.service.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { pick } from 'lodash'; import { Knex } from 'knex'; import * as R from 'ramda'; +import * as composeAsync from 'async/compose'; import { CASHFLOW_TRANSACTION_TYPE } from '../constants'; import { transformCashflowTransactionType } from '../utils'; import { CommandBankTransactionValidator } from './CommandCasflowValidator.service'; @@ -104,7 +105,7 @@ export class CreateBankTransactionService { } : {}), }; - return R.compose(this.branchDTOTransform.transformDTO)( + return composeAsync(this.branchDTOTransform.transformDTO)( initialDTO, ) as BankTransaction; }; diff --git a/packages/server-nest/src/modules/Bills/commands/BillDTOTransformer.service.ts b/packages/server-nest/src/modules/Bills/commands/BillDTOTransformer.service.ts index 3c416562e..ce73c2057 100644 --- a/packages/server-nest/src/modules/Bills/commands/BillDTOTransformer.service.ts +++ b/packages/server-nest/src/modules/Bills/commands/BillDTOTransformer.service.ts @@ -27,6 +27,7 @@ export class BillDTOTransformer { @Inject(ItemEntry.name) private itemEntryModel: TenantModelProxy, + @Inject(Item.name) private itemModel: TenantModelProxy, ) {} @@ -114,12 +115,16 @@ export class BillDTOTransformer { }), userId: authorizedUser.id, }; + + const asyncDto = await composeAsync( + this.branchDTOTransform.transformDTO, + this.warehouseDTOTransform.transformDTO, + )(initialDTO); + return R.compose( // Associates tax amount withheld to the model. this.taxDTOTransformer.assocTaxAmountWithheldFromEntries, - this.branchDTOTransform.transformDTO, - this.warehouseDTOTransform.transformDTO, - )(initialDTO) as Bill; + )(asyncDto) as Bill; } /** diff --git a/packages/server-nest/src/modules/Branches/BranchesSettings.ts b/packages/server-nest/src/modules/Branches/BranchesSettings.ts index e21ad895a..88f88888a 100644 --- a/packages/server-nest/src/modules/Branches/BranchesSettings.ts +++ b/packages/server-nest/src/modules/Branches/BranchesSettings.ts @@ -1,23 +1,30 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; +import { SettingsStore } from '../Settings/SettingsStore'; +import { SETTINGS_PROVIDER } from '../Settings/Settings.types'; +import { Features } from '@/common/types/Features'; @Injectable() export class BranchesSettingsService { + constructor( + @Inject(SETTINGS_PROVIDER) + private readonly settingsStore: () => SettingsStore, + ) {} + /** * Marks multi-branches as activated. */ - public markMultiBranchesAsActivated = () => { - // const settings = this.tenancy.settings(tenantId); + public markMultiBranchesAsActivated = async () => { + const settingsStore = await this.settingsStore(); - // settings.set({ group: 'features', key: Features.BRANCHES, value: 1 }); + settingsStore.set({ group: 'features', key: Features.BRANCHES, value: 1 }); }; /** * Retrieves whether multi-branches is active. */ - public isMultiBranchesActive = () => { - // const settings = this.tenancy.settings(tenantId); + public isMultiBranchesActive = async () => { + const settingsStore = await this.settingsStore(); - // return settings.get({ group: 'features', key: Features.BRANCHES }); - return false; + return settingsStore.get({ group: 'features', key: Features.BRANCHES }); }; } diff --git a/packages/server-nest/src/modules/Branches/commands/ActivateBranchesFeature.service.ts b/packages/server-nest/src/modules/Branches/commands/ActivateBranchesFeature.service.ts index d628458ce..c310bc84e 100644 --- a/packages/server-nest/src/modules/Branches/commands/ActivateBranchesFeature.service.ts +++ b/packages/server-nest/src/modules/Branches/commands/ActivateBranchesFeature.service.ts @@ -49,8 +49,8 @@ export class ActivateBranches { * Activate multi-branches feature. * @returns {Promise} */ - public activateBranches = (): Promise => { - const isActivated = this.branchesSettings.isMultiBranchesActive(); + public activateBranches = async (): Promise => { + const isActivated = await this.branchesSettings.isMultiBranchesActive(); // Throw error if mutli-branches is already activated. this.throwIfMultiBranchesActivated(isActivated); diff --git a/packages/server-nest/src/modules/Branches/integrations/BranchTransactionDTOTransform.ts b/packages/server-nest/src/modules/Branches/integrations/BranchTransactionDTOTransform.ts index 5bdfcb43f..7d622dcbe 100644 --- a/packages/server-nest/src/modules/Branches/integrations/BranchTransactionDTOTransform.ts +++ b/packages/server-nest/src/modules/Branches/integrations/BranchTransactionDTOTransform.ts @@ -10,22 +10,24 @@ export class BranchTransactionDTOTransformer { * Excludes DTO branch id when mutli-warehouses feature is inactive. * @returns {any} */ - private excludeDTOBranchIdWhenInactive = ( + private excludeDTOBranchIdWhenInactive = async < + T extends { branchId?: number }, + >( DTO: T, - ): Omit | T => { - const isActive = this.branchesSettings.isMultiBranchesActive(); + ): Promise | T> => { + const isActive = await this.branchesSettings.isMultiBranchesActive(); return !isActive ? omit(DTO, ['branchId']) : DTO; }; /** - * Transformes the input DTO for branches feature. + * Transforms the input DTO for branches feature. * @param {T} DTO - * @returns {Omit | T} */ - public transformDTO = ( + public transformDTO = async ( DTO: T, - ): Omit | T => { + ): Promise | T> => { return this.excludeDTOBranchIdWhenInactive(DTO); }; } diff --git a/packages/server-nest/src/modules/Branches/integrations/ManualJournals/ManualJournalDTOTransformer.service.ts b/packages/server-nest/src/modules/Branches/integrations/ManualJournals/ManualJournalDTOTransformer.service.ts index bc88fdd8d..1f9cbe374 100644 --- a/packages/server-nest/src/modules/Branches/integrations/ManualJournals/ManualJournalDTOTransformer.service.ts +++ b/packages/server-nest/src/modules/Branches/integrations/ManualJournals/ManualJournalDTOTransformer.service.ts @@ -11,17 +11,18 @@ export class ManualJournalBranchesDTOTransformer { ) {} /** - * - * @param DTO - * @returns + * + * @param DTO + * @returns */ - private excludeDTOBranchIdWhenInactive = ( + private excludeDTOBranchIdWhenInactive = async ( DTO: IManualJournalDTO, - ): IManualJournalDTO => { - const isActive = this.branchesSettings.isMultiBranchesActive(); - - if (isActive) return DTO; + ): Promise => { + const isActive = await this.branchesSettings.isMultiBranchesActive(); + if (isActive) { + return DTO; + } return { ...DTO, entries: DTO.entries.map((e) => omit(e, ['branchId'])), @@ -29,9 +30,11 @@ export class ManualJournalBranchesDTOTransformer { }; /** - * + * */ - public transformDTO = (DTO: IManualJournalDTO): IManualJournalDTO => { - return this.excludeDTOBranchIdWhenInactive(DTO); - }; + public transformDTO = async ( + DTO: IManualJournalDTO, + ): Promise => { + return this.excludeDTOBranchIdWhenInactive(DTO); + }; } diff --git a/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts b/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts index 5990b95af..0748c5cc3 100644 --- a/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts +++ b/packages/server-nest/src/modules/CreditNotes/commands/CommandCreditNoteDTOTransform.service.ts @@ -17,7 +17,11 @@ import { BrandingTemplateDTOTransformer } from '../../PdfTemplate/BrandingTempla import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index'; import { CreditNoteAutoIncrementService } from './CreditNoteAutoIncrement.service'; import { CreditNote } from '../models/CreditNote'; -import { CreateCreditNoteDto, CreditNoteEntryDto, EditCreditNoteDto } from '../dtos/CreditNote.dto'; +import { + CreateCreditNoteDto, + CreditNoteEntryDto, + EditCreditNoteDto, +} from '../dtos/CreditNote.dto'; @Injectable() export class CommandCreditNoteDTOTransform { @@ -33,11 +37,11 @@ export class CommandCreditNoteDTOTransform { private readonly branchDTOTransform: BranchTransactionDTOTransformer, private readonly warehouseDTOTransform: WarehouseTransactionDTOTransform, private readonly brandingTemplatesTransformer: BrandingTemplateDTOTransformer, - private readonly creditNoteAutoIncrement: CreditNoteAutoIncrementService + private readonly creditNoteAutoIncrement: CreditNoteAutoIncrementService, ) {} /** - * Transformes the credit/edit DTO to model. + * Transforms the credit/edit DTO to model. * @param {ICreditNoteNewDTO | ICreditNoteEditDTO} creditNoteDTO * @param {string} customerCurrencyCode - */ @@ -61,10 +65,10 @@ export class CommandCreditNoteDTOTransform { })), )(creditNoteDTO.entries); - // Retreive the next credit note number. + // Retrieves the next credit note number. const autoNextNumber = this.creditNoteAutoIncrement.getNextCreditNumber(); - // Detarmines the credit note number. + // Determines the credit note number. const creditNoteNumber = creditNoteDTO.creditNoteNumber || oldCreditNote?.creditNoteNumber || @@ -84,17 +88,17 @@ export class CommandCreditNoteDTOTransform { refundedAmount: 0, invoicesAmount: 0, }; - const initialAsyncDTO = await composeAsync( + const asyncDto = (await composeAsync( + this.branchDTOTransform.transformDTO, + this.warehouseDTOTransform.transformDTO, + // Assigns the default branding template id to the invoice DTO. this.brandingTemplatesTransformer.assocDefaultBrandingTemplate( 'CreditNote', ), - )(initialDTO); + )(initialDTO)) as CreditNote; - return R.compose( - this.branchDTOTransform.transformDTO, - this.warehouseDTOTransform.transformDTO, - )(initialAsyncDTO) as CreditNote; + return asyncDto; }; /** diff --git a/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts b/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts index 7dfbf4555..f2051c059 100644 --- a/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts +++ b/packages/server-nest/src/modules/CreditNotes/dtos/CreditNote.dto.ts @@ -2,6 +2,7 @@ import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + ArrayMinSize, IsArray, IsBoolean, IsDate, @@ -88,7 +89,7 @@ export class CommandCreditNoteDto { @IsArray() @ValidateNested({ each: true }) @Type(() => CreditNoteEntryDto) - @Min(1) + @ArrayMinSize(1) @ApiProperty({ example: [ { diff --git a/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts b/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts index 81f8a26cf..ca63cfd4b 100644 --- a/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts +++ b/packages/server-nest/src/modules/Expenses/commands/CommandExpenseDTO.transformer.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { omit, sumBy } from 'lodash'; import * as moment from 'moment'; import * as R from 'ramda'; +import * as composeAsync from 'async/compose'; import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform'; import { Expense } from '../models/Expense.model'; import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index'; @@ -48,9 +49,9 @@ export class ExpenseDTOTransformer { * @param {ISystemUser} authorizedUser * @return {IExpense} */ - private expenseDTOToModel( + private async expenseDTOToModel( expenseDTO: CreateExpenseDto | EditExpenseDto, - ): Expense { + ): Promise { const landedCostAmount = this.getExpenseLandedCostAmount(expenseDTO); const totalAmount = this.getExpenseCategoriesTotal(expenseDTO.categories); @@ -71,20 +72,22 @@ export class ExpenseDTOTransformer { } : {}), }; - return R.compose(this.branchDTOTransform.transformDTO)( - initialDTO, - ) as Expense; + const asyncDto = await composeAsync( + this.branchDTOTransform.transformDTO, + )(initialDTO); + + return asyncDto as Expense; } /** - * Transformes the expense create DTO. + * Transforms the expense create DTO. * @param {IExpenseCreateDTO} expenseDTO * @returns {Promise} */ public expenseCreateDTO = async ( expenseDTO: CreateExpenseDto | EditExpenseDto, ): Promise> => { - const initialDTO = this.expenseDTOToModel(expenseDTO); + const initialDTO = await this.expenseDTOToModel(expenseDTO); const tenant = await this.tenancyContext.getTenant(true); return { diff --git a/packages/server-nest/src/modules/InventoryAdjutments/commands/CreateQuickInventoryAdjustment.service.ts b/packages/server-nest/src/modules/InventoryAdjutments/commands/CreateQuickInventoryAdjustment.service.ts index 7d0d0da88..2d8be7376 100644 --- a/packages/server-nest/src/modules/InventoryAdjutments/commands/CreateQuickInventoryAdjustment.service.ts +++ b/packages/server-nest/src/modules/InventoryAdjutments/commands/CreateQuickInventoryAdjustment.service.ts @@ -1,7 +1,7 @@ import { Knex } from 'knex'; import { Inject, Injectable } from '@nestjs/common'; -import * as R from 'ramda'; import * as moment from 'moment'; +import * as composeAsync from 'async/compose'; import { omit } from 'lodash'; import { events } from '@/common/events/events'; import { InventoryAdjustment } from '../models/InventoryAdjustment'; @@ -79,7 +79,7 @@ export class CreateQuickInventoryAdjustmentService { : {}), entries, }; - return R.compose( + return composeAsync( this.warehouseDTOTransform.transformDTO, this.branchDTOTransform.transformDTO, )(initialDTO) as InventoryAdjustment; diff --git a/packages/server-nest/src/modules/ManualJournals/commands/CreateManualJournal.service.ts b/packages/server-nest/src/modules/ManualJournals/commands/CreateManualJournal.service.ts index 4804d2597..7141b7a1f 100644 --- a/packages/server-nest/src/modules/ManualJournals/commands/CreateManualJournal.service.ts +++ b/packages/server-nest/src/modules/ManualJournals/commands/CreateManualJournal.service.ts @@ -3,6 +3,7 @@ import * as moment from 'moment'; import * as R from 'ramda'; import { Knex } from 'knex'; import { Inject, Injectable } from '@nestjs/common'; +import * as composeAsync from 'async/compose'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { IManualJournalDTO, @@ -73,7 +74,7 @@ export class CreateManualJournalService { entries, userId: authorizedUser.id, }; - return R.compose( + return composeAsync( // Omits the `branchId` from entries if multiply branches feature not active. this.branchesDTOTransformer.transformDTO, )(initialDTO) as ManualJournal; diff --git a/packages/server-nest/src/modules/PaymentReceived/commands/PaymentReceivedDTOTransformer.ts b/packages/server-nest/src/modules/PaymentReceived/commands/PaymentReceivedDTOTransformer.ts index e39ced236..7b060b61d 100644 --- a/packages/server-nest/src/modules/PaymentReceived/commands/PaymentReceivedDTOTransformer.ts +++ b/packages/server-nest/src/modules/PaymentReceived/commands/PaymentReceivedDTOTransformer.ts @@ -71,15 +71,15 @@ export class PaymentReceiveDTOTransformer { exchangeRate: paymentReceiveDTO.exchangeRate || 1, entries, }; - const initialAsyncDTO = await composeAsync( + const asyncDto = await composeAsync( + this.branchDTOTransform.transformDTO, + // Assigns the default branding template id to the invoice DTO. this.brandingTemplatesTransformer.assocDefaultBrandingTemplate( 'SaleInvoice', ), )(initialDTO); - return R.compose(this.branchDTOTransform.transformDTO)( - initialAsyncDTO, - ) as PaymentReceived; + return asyncDto; } } diff --git a/packages/server-nest/src/modules/SaleEstimates/commands/SaleEstimateDTOTransformer.service.ts b/packages/server-nest/src/modules/SaleEstimates/commands/SaleEstimateDTOTransformer.service.ts index 3a2600e8a..8ee55f78e 100644 --- a/packages/server-nest/src/modules/SaleEstimates/commands/SaleEstimateDTOTransformer.service.ts +++ b/packages/server-nest/src/modules/SaleEstimates/commands/SaleEstimateDTOTransformer.service.ts @@ -81,17 +81,17 @@ export class SaleEstimateDTOTransformer { deliveredAt: moment().toMySqlDateTime(), }), }; - const initialAsyncDTO = await composeAsync( + const asyncDto = await composeAsync( + this.branchDTOTransform.transformDTO, + this.warehouseDTOTransform.transformDTO, + // Assigns the default branding template id to the invoice DTO. this.brandingTemplatesTransformer.assocDefaultBrandingTemplate( 'SaleEstimate', ), )(initialDTO); - return R.compose( - this.branchDTOTransform.transformDTO, - this.warehouseDTOTransform.transformDTO, - )(initialAsyncDTO); + return asyncDto; } /** diff --git a/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts b/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts index bed501d40..4c6180ed3 100644 --- a/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts +++ b/packages/server-nest/src/modules/SaleEstimates/dtos/SaleEstimate.dto.ts @@ -2,6 +2,7 @@ import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + ArrayMinSize, IsArray, IsBoolean, IsDate, @@ -92,7 +93,7 @@ export class CommandSaleEstimateDto { branchId?: number; @IsArray() - @MinLength(1) + @ArrayMinSize(1) @ValidateNested({ each: true }) @Type(() => SaleEstimateEntryDto) @ApiProperty({ diff --git a/packages/server-nest/src/modules/SaleInvoices/commands/CommandSaleInvoiceDTOTransformer.service.ts b/packages/server-nest/src/modules/SaleInvoices/commands/CommandSaleInvoiceDTOTransformer.service.ts index 5aa4d2aab..29a777766 100644 --- a/packages/server-nest/src/modules/SaleInvoices/commands/CommandSaleInvoiceDTOTransformer.service.ts +++ b/packages/server-nest/src/modules/SaleInvoices/commands/CommandSaleInvoiceDTOTransformer.service.ts @@ -121,17 +121,18 @@ export class CommandSaleInvoiceDTOTransformer { } as SaleInvoice; const initialAsyncDTO = await composeAsync( + this.branchDTOTransform.transformDTO, + this.warehouseDTOTransform.transformDTO, + // Assigns the default branding template id to the invoice DTO. this.brandingTemplatesTransformer.assocDefaultBrandingTemplate( 'SaleInvoice', ), )(initialDTO); - return R.compose( - this.taxDTOTransformer.assocTaxAmountWithheldFromEntries, - this.branchDTOTransform.transformDTO, - this.warehouseDTOTransform.transformDTO, - )(initialAsyncDTO); + return R.compose(this.taxDTOTransformer.assocTaxAmountWithheldFromEntries)( + initialAsyncDTO, + ); } /** 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 175683a56..c491b4e87 100644 --- a/packages/server-nest/src/modules/SaleInvoices/commands/CreateSaleInvoice.service.ts +++ b/packages/server-nest/src/modules/SaleInvoices/commands/CreateSaleInvoice.service.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { Knex } from 'knex'; import { - ISaleInvoiceCreateDTO, ISaleInvoiceCreatedPayload, ISaleInvoiceCreatingPaylaod, } from '../SaleInvoice.types'; diff --git a/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptDTOTransformer.service.ts b/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptDTOTransformer.service.ts index 820f44764..45f5951ba 100644 --- a/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptDTOTransformer.service.ts +++ b/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptDTOTransformer.service.ts @@ -15,7 +15,10 @@ import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-ind import { SaleReceipt } from '../models/SaleReceipt'; import { Customer } from '@/modules/Customers/models/Customer'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; -import { CreateSaleReceiptDto, EditSaleReceiptDto } from '../dtos/SaleReceipt.dto'; +import { + CreateSaleReceiptDto, + EditSaleReceiptDto, +} from '../dtos/SaleReceipt.dto'; @Injectable() export class SaleReceiptDTOTransformer { @@ -96,16 +99,16 @@ export class SaleReceiptDTOTransformer { }), entries, }; - const initialAsyncDTO = await composeAsync( + const asyncDto = await composeAsync( + this.branchDTOTransform.transformDTO, + this.warehouseDTOTransform.transformDTO, + // Assigns the default branding template id to the invoice DTO. this.brandingTemplatesTransformer.assocDefaultBrandingTemplate( 'SaleReceipt', ), )(initialDTO); - return R.compose( - this.branchDTOTransform.transformDTO, - this.warehouseDTOTransform.transformDTO, - )(initialAsyncDTO) as SaleReceipt; + return asyncDto; } } diff --git a/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts b/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts index 75edee509..e5a4196f6 100644 --- a/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts +++ b/packages/server-nest/src/modules/SaleReceipts/commands/SaleReceiptMailNotification.ts @@ -24,6 +24,7 @@ import { SaleReceipt } from '../models/SaleReceipt'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service'; import { Mail } from '@/modules/Mail/Mail'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; +import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; @Injectable() export class SaleReceiptMailNotification { @@ -43,7 +44,7 @@ export class SaleReceiptMailNotification { private readonly tenancyContext: TenancyContext, @Inject(SaleReceipt.name) - private readonly saleReceiptModel: typeof SaleReceipt, + private readonly saleReceiptModel: TenantModelProxy, @InjectQueue(SendSaleReceiptMailQueue) private readonly sendSaleReceiptMailProcess: Queue, @@ -74,7 +75,6 @@ export class SaleReceiptMailNotification { await this.sendSaleReceiptMailProcess.add(SendSaleReceiptMailJob, { ...payload, }); - // Triggers the event `onSaleReceiptPreMailSend`. await this.eventEmitter.emitAsync(events.saleReceipt.onPreMailSend, { saleReceiptId, @@ -90,7 +90,7 @@ export class SaleReceiptMailNotification { public async getMailOptions( saleReceiptId: number, ): Promise { - const saleReceipt = await this.saleReceiptModel + const saleReceipt = await this.saleReceiptModel() .query() .findById(saleReceiptId) .throwIfNotFound(); diff --git a/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts b/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts index 2bfd0a26a..003d2d3f1 100644 --- a/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts +++ b/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts @@ -1,4 +1,5 @@ import knex from 'knex'; +import * as LRUCache from 'lru-cache'; import { Global, Module } from '@nestjs/common'; import { knexSnakeCaseMappers } from 'objection'; import { ClsModule, ClsService } from 'nestjs-cls'; @@ -6,6 +7,8 @@ import { ConfigService } from '@nestjs/config'; import { TENANCY_DB_CONNECTION } from './TenancyDB.constants'; import { UnitOfWork } from './UnitOfWork.service'; +const lruCache = new LRUCache(); + export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({ provide: TENANCY_DB_CONNECTION, global: true, @@ -13,14 +16,19 @@ export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({ inject: [ConfigService, ClsService], useFactory: async (configService: ConfigService, cls: ClsService) => () => { const organizationId = cls.get('organizationId'); + const database = `bigcapital_tenant_${organizationId}`; + const cachedInstance = lruCache.get(database); - return knex({ + if (cachedInstance) { + return cachedInstance; + } + const knexInstance = knex({ client: configService.get('tenantDatabase.client'), connection: { host: configService.get('tenantDatabase.host'), user: configService.get('tenantDatabase.user'), password: configService.get('tenantDatabase.password'), - database: `bigcapital_tenant_${organizationId}`, + database, charset: 'utf8', }, migrations: { @@ -32,6 +40,9 @@ export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({ pool: { min: 0, max: 7 }, ...knexSnakeCaseMappers({ upperCase: true }), }); + lruCache.set(database, knexInstance); + + return knexInstance; }, type: 'function', }); diff --git a/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts b/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts index f66709228..66f3e1b40 100644 --- a/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts +++ b/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts @@ -91,9 +91,10 @@ export function RegisterTenancyModel(model: typeof Model) { provide: model.name, inject: [TENANCY_DB_CONNECTION], global: true, - useFactory: async (tenantKnex: () => Knex) => () => { + useFactory: (tenantKnex: () => Knex) => () => { return model.bindKnex(tenantKnex()); }, + strict: true, type: 'function', }); } diff --git a/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts b/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts index 17b377a98..930704b57 100644 --- a/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts +++ b/packages/server-nest/src/modules/VendorCredit/commands/VendorCreditDTOTransform.service.ts @@ -1,6 +1,7 @@ import * as moment from 'moment'; import { omit } from 'lodash'; import * as R from 'ramda'; +import * as composeAsync from 'async/compose'; import { ERRORS } from '../constants'; import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service'; import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform'; @@ -32,7 +33,7 @@ export class VendorCreditDTOTransformService { ) {} /** - * Transformes the credit/edit vendor credit DTO to model. + * Transforms the credit/edit vendor credit DTO to model. * @param {IVendorCreditCreateDTO | IVendorCreditEditDTO} vendorCreditDTO * @param {string} vendorCurrencyCode - * @param {IVendorCredit} oldVendorCredit - @@ -80,7 +81,7 @@ export class VendorCreditDTOTransformService { openedAt: moment().toMySqlDateTime(), }), }; - return R.compose( + return composeAsync( this.branchDTOTransform.transformDTO, this.warehouseDTOTransform.transformDTO, )(initialDTO) as VendorCredit; diff --git a/packages/server-nest/src/modules/VendorCreditsRefund/commands/CreateRefundVendorCredit.service.ts b/packages/server-nest/src/modules/VendorCreditsRefund/commands/CreateRefundVendorCredit.service.ts index 9d23c6617..f0f236f58 100644 --- a/packages/server-nest/src/modules/VendorCreditsRefund/commands/CreateRefundVendorCredit.service.ts +++ b/packages/server-nest/src/modules/VendorCreditsRefund/commands/CreateRefundVendorCredit.service.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Knex } from 'knex'; import * as R from 'ramda'; +import * as composeAsync from 'async/compose'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { IRefundVendorCreditCreatedPayload, @@ -81,7 +82,7 @@ export class CreateRefundVendorCredit { refundVendorCreditDTO, } as IVendorCreditCreatePayload); - const refundCreditObj = this.transformDTOToModel( + const refundCreditObj = await this.transformDTOToModel( vendorCredit, refundVendorCreditDTO, ); @@ -119,7 +120,7 @@ export class CreateRefundVendorCredit { * @param {RefundVendorCreditDto} vendorCreditDTO * @returns {IRefundVendorCredit} */ - public transformDTOToModel = ( + public transformDTOToModel = async ( vendorCredit: VendorCredit, vendorCreditDTO: RefundVendorCreditDto, ) => { @@ -129,7 +130,7 @@ export class CreateRefundVendorCredit { currencyCode: vendorCredit.currencyCode, exchangeRate: vendorCreditDTO.exchangeRate || 1, }; - return R.compose(this.branchDTOTransform.transformDTO)(initialDTO); + return this.branchDTOTransform.transformDTO(initialDTO); }; /** diff --git a/packages/server-nest/src/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform.ts b/packages/server-nest/src/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform.ts index 82f2b5c8e..4d67bba52 100644 --- a/packages/server-nest/src/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform.ts +++ b/packages/server-nest/src/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform.ts @@ -4,34 +4,32 @@ import { WarehousesSettings } from '../WarehousesSettings'; @Injectable() export class WarehouseTransactionDTOTransform { - constructor( - private readonly warehousesSettings: WarehousesSettings, - ) {} + constructor(private readonly warehousesSettings: WarehousesSettings) {} /** * Excludes DTO warehouse id when mutli-warehouses feature is inactive. * @param {number} tenantId * @returns {Promise | T>} */ - private excludeDTOWarehouseIdWhenInactive = < - T extends { warehouseId?: number } + private excludeDTOWarehouseIdWhenInactive = async < + T extends { warehouseId?: number }, >( - DTO: T - ): Omit | T => { - const isActive = this.warehousesSettings.isMultiWarehousesActive(); + DTO: T, + ): Promise | T> => { + const isActive = await this.warehousesSettings.isMultiWarehousesActive(); return !isActive ? omit(DTO, ['warehouseId']) : DTO; }; /** - * + * * @param {number} tenantId * @param {T} DTO - * @returns {Omit | T} */ - public transformDTO = - (DTO: T): Omit | T => { - return this.excludeDTOWarehouseIdWhenInactive(DTO); - }; + public transformDTO = async ( + DTO: T, + ): Promise | T> => { + return this.excludeDTOWarehouseIdWhenInactive(DTO); + }; } - \ No newline at end of file diff --git a/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesDTOValidators.ts b/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesDTOValidators.ts index 5d65ebb0a..1ccff4ec0 100644 --- a/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesDTOValidators.ts +++ b/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesDTOValidators.ts @@ -19,7 +19,9 @@ export class WarehousesDTOValidators { * Validates the warehouse existance of sale invoice transaction. * @param {IWarehouseTransactionDTO} DTO */ - public validateDTOWarehouseExistance = async (DTO: IWarehouseTransactionDTO) => { + public validateDTOWarehouseExistance = async ( + DTO: IWarehouseTransactionDTO, + ) => { // Validates the sale invoice warehouse id existance. this.validateWarehouseExistanceService.validateWarehouseIdExistance( DTO, @@ -47,7 +49,7 @@ export class WarehousesDTOValidators { public validateDTOWarehouseWhenActive = async ( DTO: IWarehouseTransactionDTO, ): Promise => { - const isActive = this.warehousesSettings.isMultiWarehousesActive(); + const isActive = await this.warehousesSettings.isMultiWarehousesActive(); // Can't continue if the multi-warehouses feature is inactive. if (!isActive) return; diff --git a/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesItemsQuantitySynSubscriber.ts b/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesItemsQuantitySynSubscriber.ts index b3f70f4a4..fadbffdcd 100644 --- a/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesItemsQuantitySynSubscriber.ts +++ b/packages/server-nest/src/modules/Warehouses/Integrations/WarehousesItemsQuantitySynSubscriber.ts @@ -24,7 +24,7 @@ export class WarehousesItemsQuantitySyncSubscriber { inventoryTransactions, trx, }: IInventoryTransactionsCreatedPayload) { - const isActive = this.warehousesSettings.isMultiWarehousesActive(); + const isActive = await this.warehousesSettings.isMultiWarehousesActive(); // Can't continue if the warehouses features is not active. if (!isActive) return; @@ -44,7 +44,7 @@ export class WarehousesItemsQuantitySyncSubscriber { oldInventoryTransactions, trx, }: IInventoryTransactionsDeletedPayload) { - const isActive = this.warehousesSettings.isMultiWarehousesActive(); + const isActive = await this.warehousesSettings.isMultiWarehousesActive(); // Can't continue if the warehouses feature is not active yet. if (!isActive) return; diff --git a/packages/server-nest/src/modules/Warehouses/WarehousesSettings.ts b/packages/server-nest/src/modules/Warehouses/WarehousesSettings.ts index 05b640e11..2ec7c0335 100644 --- a/packages/server-nest/src/modules/Warehouses/WarehousesSettings.ts +++ b/packages/server-nest/src/modules/Warehouses/WarehousesSettings.ts @@ -1,25 +1,32 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; +import { SettingsStore } from '../Settings/SettingsStore'; +import { SETTINGS_PROVIDER } from '../Settings/Settings.types'; +import { Features } from '@/common/types/Features'; @Injectable() export class WarehousesSettings { + constructor( + @Inject(SETTINGS_PROVIDER) + private readonly settingsStore: () => SettingsStore, + ) {} + /** * Marks multi-warehouses as activated. */ - public markMutliwarehoussAsActivated = () => { - // const settings = this.tenancy.settings(tenantId); + public markMutliwarehoussAsActivated = async () => { + const settings = await this.settingsStore(); - // settings.set({ group: 'features', key: Features.WAREHOUSES, value: 1 }); + settings.set({ group: 'features', key: Features.WAREHOUSES, value: 1 }); }; /** - * Detarmines multi-warehouses is active. + * Determines multi-warehouses is active. * @param {number} tenantId * @returns {boolean} */ - public isMultiWarehousesActive = () => { - // const settings = this.tenancy.settings(tenantId); + public isMultiWarehousesActive = async () => { + const settings = await this.settingsStore(); - // return settings.get({ group: 'features', key: Features.WAREHOUSES }); - return true; + return settings.get({ group: 'features', key: Features.WAREHOUSES }); }; } diff --git a/packages/server-nest/src/modules/Warehouses/commands/ActivateWarehouses.service.ts b/packages/server-nest/src/modules/Warehouses/commands/ActivateWarehouses.service.ts index 9d8ce7bae..dee649926 100644 --- a/packages/server-nest/src/modules/Warehouses/commands/ActivateWarehouses.service.ts +++ b/packages/server-nest/src/modules/Warehouses/commands/ActivateWarehouses.service.ts @@ -40,7 +40,7 @@ export class ActivateWarehousesService { * - Mutate inventory transactions with the primary warehouse. */ public async activateWarehouses(): Promise { - const isActivated = this.settings.isMultiWarehousesActive(); + const isActivated = await this.settings.isMultiWarehousesActive(); this.throwIfWarehousesActivated(isActivated); return this.uow.withTransaction(async (trx: Knex.Transaction) => { diff --git a/packages/server-nest/test/sale-estimates.e2e-spec.ts b/packages/server-nest/test/sale-estimates.e2e-spec.ts index 4bd07b51c..2136be2ba 100644 --- a/packages/server-nest/test/sale-estimates.e2e-spec.ts +++ b/packages/server-nest/test/sale-estimates.e2e-spec.ts @@ -39,6 +39,7 @@ describe('Sale Estimates (e2e)', () => { .set('organization-id', orgainzationId) .send({ name: faker.commerce.productName(), + type: 'inventory', sellable: true, purchasable: true, sellAccountId: 1026,