diff --git a/packages/server/src/interfaces/Import.ts b/packages/server/src/interfaces/Import.ts index 4f6d2e069..cf25a79fb 100644 --- a/packages/server/src/interfaces/Import.ts +++ b/packages/server/src/interfaces/Import.ts @@ -3,6 +3,6 @@ import { ImportFilePreviewPOJO } from "@/services/Import/interfaces"; export interface IImportFileCommitedEventPayload { tenantId: number; - importId: number; + importId: string; meta: ImportFilePreviewPOJO; } \ No newline at end of file diff --git a/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts b/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts index eba643481..7220be765 100644 --- a/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts +++ b/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts @@ -100,7 +100,6 @@ export class TriggerRecognizedTransactions { private async triggerRecognizeTransactionsOnImportCommitted({ tenantId, importId, - meta, }: IImportFileCommitedEventPayload) { const importFile = await Import.query().findOne({ importId }); const batch = importFile.paramsParsed.batch; diff --git a/packages/server/src/services/Import/ImportALS.ts b/packages/server/src/services/Import/ImportALS.ts new file mode 100644 index 000000000..bfa69eb9f --- /dev/null +++ b/packages/server/src/services/Import/ImportALS.ts @@ -0,0 +1,104 @@ +import { Service } from 'typedi'; +import { AsyncLocalStorage } from 'async_hooks'; + +@Service() +export class ImportAls { + private als: AsyncLocalStorage>; + + constructor() { + this.als = new AsyncLocalStorage(); + } + /** + * Runs a callback function within the context of a new AsyncLocalStorage store. + * @param callback The function to be executed within the AsyncLocalStorage context. + * @returns The result of the callback function. + */ + public run(callback: () => T): T { + return this.als.run(new Map(), callback); + } + + /** + * Runs a callback function in preview mode within the AsyncLocalStorage context. + * @param callback The function to be executed in preview mode. + * @returns The result of the callback function. + */ + public runPreview(callback: () => T): T { + return this.run(() => { + this.markAsImport(); + this.markAsImportPreview(); + return callback(); + }); + } + + /** + * Runs a callback function in commit mode within the AsyncLocalStorage context. + * @param {() => T} callback - The function to be executed in commit mode. + * @returns {T} The result of the callback function. + */ + public runCommit(callback: () => T): T { + return this.run(() => { + this.markAsImport(); + this.markAsImportCommit(); + return callback(); + }); + } + + /** + * Retrieves the current AsyncLocalStorage store. + * @returns The current store or undefined if not in a valid context. + */ + public getStore(): Map | undefined { + return this.als.getStore(); + } + + /** + * Marks the current context as an import operation. + * @param flag Boolean flag to set or unset the import status. Defaults to true. + */ + public markAsImport(flag: boolean = true): void { + const store = this.getStore(); + store?.set('isImport', flag); + } + + /** + * Marks the current context as an import commit operation. + * @param flag Boolean flag to set or unset the import commit status. Defaults to true. + */ + public markAsImportCommit(flag: boolean = true): void { + const store = this.getStore(); + store?.set('isImportCommit', flag); + } + + /** + * Marks the current context as an import preview operation. + * @param {boolean} flag - Boolean flag to set or unset the import preview status. Defaults to true. + */ + public markAsImportPreview(flag: boolean = true): void { + const store = this.getStore(); + store?.set('isImportPreview', flag); + } + + /** + * Checks if the current context is an import operation. + * @returns {boolean} True if the context is an import operation, false otherwise. + */ + public isImport(): boolean { + return !!this.getStore()?.get('isImport'); + } + + /** + * Checks if the current context is an import commit operation. + * @returns {boolean} True if the context is an import commit operation, false otherwise. + */ + public isImportCommit(): boolean { + return !!this.getStore()?.get('isImportCommit'); + } + + /** + * Checks if the current context is an import preview operation. + * @returns {boolean} True if the context is an import preview operation, false otherwise. + */ + public isImportPreview(): boolean { + return !!this.getStore()?.get('isImportPreview'); + } +} diff --git a/packages/server/src/services/Import/ImportFilePreview.ts b/packages/server/src/services/Import/ImportFilePreview.ts index b25c00a0e..e8b566bbe 100644 --- a/packages/server/src/services/Import/ImportFilePreview.ts +++ b/packages/server/src/services/Import/ImportFilePreview.ts @@ -2,6 +2,7 @@ import { Inject, Service } from 'typedi'; import HasTenancyService from '../Tenancy/TenancyService'; import { ImportFilePreviewPOJO } from './interfaces'; import { ImportFileProcess } from './ImportFileProcess'; +import { ImportAls } from './ImportALS'; @Service() export class ImportFilePreview { @@ -11,13 +12,31 @@ export class ImportFilePreview { @Inject() private importFile: ImportFileProcess; + @Inject() + private importAls: ImportAls; + + /** + * Preview the imported file results before commiting the transactions. + * @param {number} tenantId - + * @param {string} importId - + * @returns {Promise} + */ + public async preview( + tenantId: number, + importId: string + ): Promise { + return this.importAls.runPreview>(() => + this.previewAlsRun(tenantId, importId) + ); + } + /** * Preview the imported file results before commiting the transactions. * @param {number} tenantId * @param {number} importId * @returns {Promise} */ - public async preview( + public async previewAlsRun( tenantId: number, importId: string ): Promise { diff --git a/packages/server/src/services/Import/ImportFileProcessCommit.ts b/packages/server/src/services/Import/ImportFileProcessCommit.ts index 689b956c7..f9cb640fc 100644 --- a/packages/server/src/services/Import/ImportFileProcessCommit.ts +++ b/packages/server/src/services/Import/ImportFileProcessCommit.ts @@ -5,6 +5,7 @@ import { ImportFileProcess } from './ImportFileProcess'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; import { IImportFileCommitedEventPayload } from '@/interfaces/Import'; +import { ImportAls } from './ImportALS'; @Service() export class ImportFileProcessCommit { @@ -14,16 +15,34 @@ export class ImportFileProcessCommit { @Inject() private importFile: ImportFileProcess; + @Inject() + private importAls: ImportAls; + @Inject() private eventPublisher: EventPublisher; + /** + * Commits the imported file under ALS. + * @param {number} tenantId + * @param {string} importId + * @returns {Promise} + */ + public commit( + tenantId: number, + importId: string + ): Promise { + return this.importAls.runCommit>(() => + this.commitAlsRun(tenantId, importId) + ); + } + /** * Commits the imported file. * @param {number} tenantId * @param {number} importId * @returns {Promise} */ - public async commit( + public async commitAlsRun( tenantId: number, importId: string ): Promise { diff --git a/packages/server/src/subscribers/Inventory/Inventory.ts b/packages/server/src/subscribers/Inventory/Inventory.ts index 9061a6d81..f45a1a8f7 100644 --- a/packages/server/src/subscribers/Inventory/Inventory.ts +++ b/packages/server/src/subscribers/Inventory/Inventory.ts @@ -10,6 +10,7 @@ import { } from '@/interfaces'; import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks'; import { SaleInvoicesCost } from '@/services/Sales/Invoices/SalesInvoicesCost'; +import { ImportAls } from '@/services/Import/ImportALS'; @Service() export default class InventorySubscriber { @@ -25,6 +26,9 @@ export default class InventorySubscriber { @Inject('agenda') private agenda: any; + @Inject() + private importAls: ImportAls; + /** * Attaches events with handlers. */ @@ -88,7 +92,10 @@ export default class InventorySubscriber { inventoryTransactions, trx, }: IInventoryTransactionsCreatedPayload) => { - const inventoryItemsIds = map(inventoryTransactions, 'itemId'); + const inImportPreviewScope = this.importAls.isImportPreview(); + + // Avoid running the cost items job if the async process is in import preview. + if (inImportPreviewScope) return; await this.saleInvoicesCost.computeItemsCostByInventoryTransactions( tenantId,