refactor: dynamic list to nestjs

This commit is contained in:
Ahmed Bouhuolia
2025-01-12 18:22:48 +02:00
parent ddaea20d16
commit 270b421a6c
117 changed files with 4232 additions and 1493 deletions

View File

@@ -8,7 +8,14 @@ import { CloseSaleReceipt } from './commands/CloseSaleReceipt.service';
import { DeleteSaleReceipt } from './commands/DeleteSaleReceipt.service';
import { GetSaleReceipt } from './queries/GetSaleReceipt.service';
import { EditSaleReceipt } from './commands/EditSaleReceipt.service';
import { ISaleReceiptDTO, ISaleReceiptState } from './types/SaleReceipts.types';
import {
ISaleReceiptDTO,
ISaleReceiptState,
ISalesReceiptsFilter,
} from './types/SaleReceipts.types';
import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
import { SaleReceipt } from './models/SaleReceipt';
import { IPaginationMeta } from '@/interfaces/Model';
@Injectable()
export class SaleReceiptApplication {
@@ -17,7 +24,7 @@ export class SaleReceiptApplication {
private editSaleReceiptService: EditSaleReceipt,
private getSaleReceiptService: GetSaleReceipt,
private deleteSaleReceiptService: DeleteSaleReceipt,
// private getSaleReceiptsService: GetSaleReceipts,
private getSaleReceiptsService: GetSaleReceiptsService,
private closeSaleReceiptService: CloseSaleReceipt,
private getSaleReceiptPdfService: SaleReceiptsPdfService,
// private saleReceiptNotifyBySmsService: SaleReceiptNotifyBySms,
@@ -74,20 +81,16 @@ export class SaleReceiptApplication {
/**
* Retrieve sales receipts paginated and filterable list.
* @param {number} tenantId
* @param {ISalesReceiptsFilter} filterDTO
* @returns
*/
// public async getSaleReceipts(
// tenantId: number,
// filterDTO: ISalesReceiptsFilter,
// ): Promise<{
// data: ISaleReceipt[];
// pagination: IPaginationMeta;
// filterMeta: IFilterMeta;
// }> {
// return this.getSaleReceiptsService.getSaleReceipts(tenantId, filterDTO);
// }
public async getSaleReceipts(filterDTO: ISalesReceiptsFilter): Promise<{
data: SaleReceipt[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}> {
return this.getSaleReceiptsService.getSaleReceipts(filterDTO);
}
/**
* Closes the given sale receipt.
@@ -106,9 +109,7 @@ export class SaleReceiptApplication {
* @returns
*/
public getSaleReceiptPdf(tenantId: number, saleReceiptId: number) {
return this.getSaleReceiptPdfService.saleReceiptPdf(
saleReceiptId,
);
return this.getSaleReceiptPdfService.saleReceiptPdf(saleReceiptId);
}
/**

View File

@@ -24,6 +24,8 @@ import { SaleReceiptGLEntriesSubscriber } from './subscribers/SaleReceiptGLEntri
import { SaleReceiptGLEntries } from './ledger/SaleReceiptGLEntries';
import { LedgerModule } from '../Ledger/Ledger.module';
import { AccountsModule } from '../Accounts/Accounts.module';
import { SaleReceiptInventoryTransactionsSubscriber } from './inventory/SaleReceiptWriteInventoryTransactions';
import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
@Module({
controllers: [SaleReceiptsController],
@@ -53,7 +55,9 @@ import { AccountsModule } from '../Accounts/Accounts.module';
SaleReceiptBrandingTemplate,
SaleReceiptIncrement,
SaleReceiptGLEntries,
SaleReceiptGLEntriesSubscriber
SaleReceiptGLEntriesSubscriber,
SaleReceiptInventoryTransactionsSubscriber,
GetSaleReceiptsService
],
})
export class SaleReceiptsModule {}

View File

@@ -0,0 +1,66 @@
import { Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { InventoryService } from '@/modules/InventoryCost/Inventory';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { SaleReceipt } from '../models/SaleReceipt';
@Injectable()
export class SaleReceiptInventoryTransactions {
constructor(
private readonly itemsEntriesService: ItemsEntriesService,
private readonly inventoryService: InventoryService,
) {}
/**
* Records the inventory transactions from the given bill input.
* @param {Bill} bill - Bill model object.
* @param {number} billId - Bill id.
* @return {Promise<void>}
*/
public async recordInventoryTransactions(
saleReceipt: SaleReceipt,
override?: boolean,
trx?: Knex.Transaction,
): Promise<void> {
// Loads the inventory items entries of the given sale invoice.
const inventoryEntries =
await this.itemsEntriesService.filterInventoryEntries(
saleReceipt.entries,
);
const transaction = {
transactionId: saleReceipt.id,
transactionType: 'SaleReceipt',
transactionNumber: saleReceipt.receiptNumber,
exchangeRate: saleReceipt.exchangeRate,
date: saleReceipt.receiptDate,
direction: 'OUT',
entries: inventoryEntries,
createdAt: saleReceipt.createdAt,
warehouseId: saleReceipt.warehouseId,
};
return this.inventoryService.recordInventoryTransactionsFromItemsEntries(
transaction,
override,
trx,
);
}
/**
* Reverts the inventory transactions of the given bill id.
* @param {number} tenantId - Tenant id.
* @param {number} billId - Bill id.
* @return {Promise<void>}
*/
public async revertInventoryTransactions(
receiptId: number,
trx?: Knex.Transaction,
) {
return this.inventoryService.deleteInventoryTransactions(
receiptId,
'SaleReceipt',
trx,
);
}
}

View File

@@ -0,0 +1,69 @@
import { OnEvent } from '@nestjs/event-emitter';
import {
ISaleReceiptCreatedPayload,
ISaleReceiptEditedPayload,
ISaleReceiptEventDeletedPayload,
} from '../types/SaleReceipts.types';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { SaleReceiptInventoryTransactions } from './SaleReceiptInventoryTransactions';
@Injectable()
export class SaleReceiptInventoryTransactionsSubscriber {
constructor(
private readonly saleReceiptInventory: SaleReceiptInventoryTransactions
) {}
/**
* Handles the writing inventory transactions once the receipt created.
* @param {ISaleReceiptCreatedPayload} payload -
*/
@OnEvent(events.saleReceipt.onCreated)
public async handleWritingInventoryTransactions({
saleReceipt,
trx,
}: ISaleReceiptCreatedPayload) {
// Can't continue if the sale receipt is not closed yet.
if (!saleReceipt.closedAt) return null;
await this.saleReceiptInventory.recordInventoryTransactions(
saleReceipt,
false,
trx
);
};
/**
* Rewriting the inventory transactions once the sale invoice be edited.
* @param {ISaleReceiptEditedPayload} payload -
*/
@OnEvent(events.saleReceipt.onEdited)
public async handleRewritingInventoryTransactions({
saleReceipt,
trx,
}: ISaleReceiptEditedPayload) {
// Can't continue if the sale receipt is not closed yet.
if (!saleReceipt.closedAt) return null;
await this.saleReceiptInventory.recordInventoryTransactions(
saleReceipt,
true,
trx
);
};
/**
* Handles deleting the inventory transactions once the receipt deleted.
* @param {ISaleReceiptEventDeletedPayload} payload -
*/
@OnEvent(events.saleReceipt.onDeleted)
public async handleDeletingInventoryTransactions({
saleReceiptId,
trx,
}: ISaleReceiptEventDeletedPayload) {
await this.saleReceiptInventory.revertInventoryTransactions(
saleReceiptId,
trx
);
};
}

View File

@@ -0,0 +1,73 @@
import * as R from 'ramda';
import { SaleReceiptTransformer } from './SaleReceiptTransformer';
import { Inject, Injectable } from '@nestjs/common';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { ISalesReceiptsFilter } from '../types/SaleReceipts.types';
import { SaleReceipt } from '../models/SaleReceipt';
import { IPaginationMeta } from '@/interfaces/Model';
interface GetSaleReceiptsSettings {
fetchEntriesGraph?: boolean;
}
@Injectable()
export class GetSaleReceiptsService {
constructor(
private readonly transformer: TransformerInjectable,
private readonly dynamicListService: DynamicListService,
@Inject(SaleReceipt.name)
private readonly saleReceiptModel: typeof SaleReceipt,
) {}
/**
* Retrieve sales receipts paginated and filterable list.
* @param {number} tenantId
* @param {ISaleReceiptFilter} salesReceiptsFilter
*/
public async getSaleReceipts(filterDTO: ISalesReceiptsFilter): Promise<{
data: SaleReceipt[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}> {
// Parses the stringified filter roles.
const filter = this.parseListFilterDTO(filterDTO);
// Dynamic list service.
const dynamicFilter = await this.dynamicListService.dynamicList(
SaleReceipt,
filter,
);
const { results, pagination } = await this.saleReceiptModel
.query()
.onBuild((builder) => {
builder.withGraphFetched('depositAccount');
builder.withGraphFetched('customer');
builder.withGraphFetched('entries.item');
dynamicFilter.buildQuery()(builder);
filterDTO?.filterQuery && filterDTO?.filterQuery(builder);
})
.pagination(filter.page - 1, filter.pageSize);
// Transformes the estimates models to POJO.
const salesEstimates = await this.transformer.transform(
results,
new SaleReceiptTransformer(),
);
return {
data: salesEstimates,
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};
}
/**
* Parses the sale invoice list filter DTO.
* @param filterDTO
* @returns
*/
private parseListFilterDTO(filterDTO) {
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
}
}

View File

@@ -1,84 +0,0 @@
// import * as R from 'ramda';
// import {
// IFilterMeta,
// IPaginationMeta,
// ISaleReceipt,
// ISalesReceiptsFilter,
// } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Inject, Service } from 'typedi';
// import { SaleReceiptTransformer } from './SaleReceiptTransformer';
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
// interface GetSaleReceiptsSettings {
// fetchEntriesGraph?: boolean;
// }
// @Service()
// export class GetSaleReceipts {
// @Inject()
// private tenancy: HasTenancyService;
// @Inject()
// private transformer: TransformerInjectable;
// @Inject()
// private dynamicListService: DynamicListingService;
// /**
// * Retrieve sales receipts paginated and filterable list.
// * @param {number} tenantId
// * @param {ISaleReceiptFilter} salesReceiptsFilter
// */
// public async getSaleReceipts(
// tenantId: number,
// filterDTO: ISalesReceiptsFilter
// ): Promise<{
// data: ISaleReceipt[];
// pagination: IPaginationMeta;
// filterMeta: IFilterMeta;
// }> {
// const { SaleReceipt } = this.tenancy.models(tenantId);
// // Parses the stringified filter roles.
// const filter = this.parseListFilterDTO(filterDTO);
// // Dynamic list service.
// const dynamicFilter = await this.dynamicListService.dynamicList(
// tenantId,
// SaleReceipt,
// filter
// );
// const { results, pagination } = await SaleReceipt.query()
// .onBuild((builder) => {
// builder.withGraphFetched('depositAccount');
// builder.withGraphFetched('customer');
// builder.withGraphFetched('entries.item');
// dynamicFilter.buildQuery()(builder);
// filterDTO?.filterQuery && filterDTO?.filterQuery(builder);
// })
// .pagination(filter.page - 1, filter.pageSize);
// // Transformes the estimates models to POJO.
// const salesEstimates = await this.transformer.transform(
// tenantId,
// results,
// new SaleReceiptTransformer()
// );
// return {
// data: salesEstimates,
// pagination,
// filterMeta: dynamicFilter.getResponseMeta(),
// };
// }
// /**
// * Parses the sale invoice list filter DTO.
// * @param filterDTO
// * @returns
// */
// private parseListFilterDTO(filterDTO) {
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
// }
// }