feat(nestjs): migrate to NestJS

This commit is contained in:
Ahmed Bouhuolia
2025-04-07 11:51:24 +02:00
parent f068218a16
commit 55fcc908ef
3779 changed files with 631 additions and 195332 deletions

View File

@@ -0,0 +1,66 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
ITaxRateActivatedPayload,
ITaxRateActivatingPayload,
} from '../TaxRates.types';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { TaxRateModel } from '../models/TaxRate.model';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class ActivateTaxRateService {
/**
* @param {EventEmitter2} eventEmitter - The event emitter.
* @param {UnitOfWork} uow - The unit of work.
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly validators: CommandTaxRatesValidators,
@Inject(TaxRateModel.name)
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
) {}
/**
* Activates the given tax rate.
* @param {number} taxRateId
* @returns {Promise<ITaxRate>}
*/
public async activateTaxRate(taxRateId: number) {
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
// Validates the tax rate existance.
this.validators.validateTaxRateExistance(oldTaxRate);
// Validates the tax rate inactive.
this.validators.validateTaxRateNotActive(oldTaxRate);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onTaxRateActivating` event.
await this.eventEmitter.emitAsync(events.taxRates.onActivating, {
taxRateId,
trx,
} as ITaxRateActivatingPayload);
const taxRate = await this.taxRateModel()
.query(trx)
.findById(taxRateId)
.patch({ active: true });
// Triggers `onTaxRateCreated` event.
await this.eventEmitter.emitAsync(events.taxRates.onActivated, {
taxRateId,
trx,
} as ITaxRateActivatedPayload);
return taxRate;
});
}
}

View File

@@ -0,0 +1,116 @@
import { Knex } from 'knex';
import { difference } from 'lodash';
// import { IItemEntryDTO } from '@/modules/Items/';
import { ERRORS } from '../constants';
import { TaxRateModel } from '../models/TaxRate.model';
import { Inject } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { ServiceError } from '@/modules/Items/ServiceError';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
@Injectable()
export class CommandTaxRatesValidators {
/**
* @param {TenantModelProxy<typeof TaxRateModel>} taxRateModel - The tax rate model.
*/
constructor(
@Inject(TaxRateModel.name)
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
) {}
/**
* Validates the tax rate existance.
* @param {TaxRate | undefined | null} taxRate
*/
public validateTaxRateExistance(taxRate: TaxRateModel | undefined | null) {
if (!taxRate) {
throw new ServiceError(ERRORS.TAX_RATE_NOT_FOUND);
}
}
/**
* Validates the given tax rate active.
* @param {TaxRateModel} taxRate
*/
public validateTaxRateNotActive(taxRate: TaxRateModel) {
if (taxRate.active) {
throw new ServiceError(ERRORS.TAX_RATE_ALREADY_ACTIVE);
}
}
/**
* Validates the given tax rate inactive.
* @param {TaxRateModel} taxRate
*/
public validateTaxRateNotInactive(taxRate: TaxRateModel) {
if (!taxRate.active) {
throw new ServiceError(ERRORS.TAX_RATE_ALREADY_INACTIVE);
}
}
/**
* Validates the tax code uniquiness.
* @param {number} tenantId
* @param {string} taxCode
* @param {Knex.Transaction} trx -
*/
public async validateTaxCodeUnique(taxCode: string, trx?: Knex.Transaction) {
const foundTaxCode = await this.taxRateModel()
.query(trx)
.findOne({ code: taxCode });
if (foundTaxCode) {
throw new ServiceError(ERRORS.TAX_CODE_NOT_UNIQUE);
}
}
/**
* Validates the tax codes of the given item entries DTO.
* @param {IItemEntryDTO[]} itemEntriesDTO
* @throws {ServiceError}
*/
public async validateItemEntriesTaxCode(itemEntriesDTO: ItemEntryDto[]) {
const filteredTaxEntries = itemEntriesDTO.filter((e) => e.taxCode);
const taxCodes = filteredTaxEntries.map((e) => e.taxCode);
// Can't validate if there is no tax codes.
if (taxCodes.length === 0) return;
const foundTaxCodes = await this.taxRateModel()
.query()
.whereIn('code', taxCodes);
const foundCodes = foundTaxCodes.map((tax) => tax.code);
const notFoundTaxCodes = difference(taxCodes, foundCodes);
if (notFoundTaxCodes.length > 0) {
throw new ServiceError(ERRORS.ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND);
}
}
/**
* Validates the tax rate id of the given item entries DTO.
* @param {ItemEntryDto[]} itemEntriesDTO
* @throws {ServiceError}
*/
public async validateItemEntriesTaxCodeId(itemEntriesDTO: ItemEntryDto[]) {
const filteredTaxEntries = itemEntriesDTO.filter((e) => e.taxRateId);
const taxRatesIds = filteredTaxEntries.map((e) => e.taxRateId);
// Can't validate if there is no tax codes.
if (taxRatesIds.length === 0) return;
const foundTaxCodes = await this.taxRateModel()
.query()
.whereIn('id', taxRatesIds);
const foundTaxRatesIds = foundTaxCodes.map((tax) => tax.id);
const notFoundTaxCodes = difference(taxRatesIds, foundTaxRatesIds);
if (notFoundTaxCodes.length > 0) {
throw new ServiceError(ERRORS.ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND);
}
}
}

View File

@@ -0,0 +1,67 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
ICreateTaxRateDTO,
ITaxRateCreatedPayload,
ITaxRateCreatingPayload,
} from '../TaxRates.types';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
import { TaxRateModel } from '../models/TaxRate.model';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateTaxRateDto } from '../dtos/TaxRate.dto';
@Injectable()
export class CreateTaxRate {
/**
* @param {EventEmitter2} eventEmitter - The event emitter.
* @param {UnitOfWork} uow - The unit of work.
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly validators: CommandTaxRatesValidators,
@Inject(TaxRateModel.name)
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
) {}
/**
* Creates a new tax rate.
* @param {ICreateTaxRateDTO} createTaxRateDTO
*/
public async createTaxRate(
createTaxRateDTO: CreateTaxRateDto,
trx?: Knex.Transaction,
) {
// Validates the tax code uniquiness.
await this.validators.validateTaxCodeUnique(createTaxRateDTO.code, trx);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onTaxRateCreating` event.
await this.eventEmitter.emitAsync(events.taxRates.onCreating, {
createTaxRateDTO,
trx,
} as ITaxRateCreatingPayload);
const taxRate = await this.taxRateModel()
.query(trx)
.insertAndFetch({
...createTaxRateDTO,
});
// Triggers `onTaxRateCreated` event.
await this.eventEmitter.emitAsync(events.taxRates.onCreated, {
createTaxRateDTO,
taxRate,
trx,
} as ITaxRateCreatedPayload);
return taxRate;
}, trx);
}
}

View File

@@ -0,0 +1,58 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
ITaxRateDeletedPayload,
ITaxRateDeletingPayload,
} from '../TaxRates.types';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
import { TaxRateModel } from '../models/TaxRate.model';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class DeleteTaxRateService {
/**
* @param {EventEmitter2} eventEmitter - The event emitter.
* @param {UnitOfWork} uow - The unit of work.
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly validators: CommandTaxRatesValidators,
@Inject(TaxRateModel.name)
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
) {}
/**
* Deletes the given tax rate.
* @param {number} taxRateId
* @returns {Promise<void>}
*/
public async deleteTaxRate(taxRateId: number): Promise<void> {
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
// Validates the tax rate existance.
this.validators.validateTaxRateExistance(oldTaxRate);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onTaxRateDeleting` event.
await this.eventEmitter.emitAsync(events.taxRates.onDeleting, {
oldTaxRate,
trx,
} as ITaxRateDeletingPayload);
await this.taxRateModel().query(trx).findById(taxRateId).delete();
// Triggers `onTaxRateDeleted` event.
await this.eventEmitter.emitAsync(events.taxRates.onDeleted, {
oldTaxRate,
trx,
} as ITaxRateDeletedPayload);
});
}
}

View File

@@ -0,0 +1,123 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { omit } from 'lodash';
import {
IEditTaxRateDTO,
ITaxRateEditedPayload,
ITaxRateEditingPayload,
} from '../TaxRates.types';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
import { TaxRateModel } from '../models/TaxRate.model';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { events } from '@/common/events/events';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditTaxRateDto } from '../dtos/TaxRate.dto';
@Injectable()
export class EditTaxRateService {
/**
* @param {EventEmitter2} eventEmitter - The event emitter.
* @param {UnitOfWork} uow - The unit of work.
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
* @param {TenantModelProxy<typeof TaxRateModel>} taxRateModel - The tax rate model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly validators: CommandTaxRatesValidators,
@Inject(TaxRateModel.name)
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
) {}
/**
* Determines whether the tax rate, name or code have been changed.
* @param {ITaxRate} taxRate
* @param {IEditTaxRateDTO} editTaxRateDTO
* @returns {boolean}
*/
private isTaxRateDTOChanged = (
taxRate: TaxRateModel,
editTaxRateDTO: EditTaxRateDto,
) => {
return (
taxRate.rate !== editTaxRateDTO.rate ||
taxRate.name !== editTaxRateDTO.name ||
taxRate.code !== editTaxRateDTO.code
);
};
/**
* Edits the given tax rate or creates a new if the rate or name have been changed.
* @param {number} tenantId
* @param {ITaxRate} oldTaxRate
* @param {IEditTaxRateDTO} editTaxRateDTO
* @param {Knex.Transaction} trx
* @returns {Promise<ITaxRate>}
*/
private async editTaxRateOrCreate(
oldTaxRate: TaxRateModel,
editTaxRateDTO: EditTaxRateDto,
trx?: Knex.Transaction,
) {
const isTaxDTOChanged = this.isTaxRateDTOChanged(
oldTaxRate,
editTaxRateDTO,
);
if (isTaxDTOChanged) {
// Soft deleting the old tax rate.
await this.taxRateModel().query(trx).findById(oldTaxRate.id).delete();
// Create a new tax rate with new edited data.
return this.taxRateModel()
.query(trx)
.insertAndFetch({
...omit(oldTaxRate, ['id']),
...editTaxRateDTO,
});
} else {
return this.taxRateModel()
.query(trx)
.patchAndFetchById(oldTaxRate.id, {
...editTaxRateDTO,
});
}
}
/**
* Edits the given tax rate.
* @param {number} taxRateId - The tax rate id.
* @param {IEditTaxRateDTO} editTaxRateDTO - The tax rate data to edit.
* @returns {Promise<ITaxRate>}
*/
public async editTaxRate(taxRateId: number, editTaxRateDTO: EditTaxRateDto) {
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
// Validates the tax rate existance.
this.validators.validateTaxRateExistance(oldTaxRate);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onTaxRateEditing` event.
await this.eventEmitter.emitAsync(events.taxRates.onEditing, {
editTaxRateDTO,
trx,
} as ITaxRateEditingPayload);
const taxRate = await this.editTaxRateOrCreate(
oldTaxRate,
editTaxRateDTO,
trx,
);
// Triggers `onTaxRateEdited` event.
await this.eventEmitter.emitAsync(events.taxRates.onEdited, {
editTaxRateDTO,
oldTaxRate,
taxRate,
trx,
} as ITaxRateEditedPayload);
return taxRate;
});
}
}

View File

@@ -0,0 +1,66 @@
import { Inject, Injectable } from '@nestjs/common';
import {
ITaxRateActivatedPayload,
ITaxRateActivatingPayload,
} from '../TaxRates.types';
import { Knex } from 'knex';
import { CommandTaxRatesValidators } from './CommandTaxRatesValidator.service';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { TaxRateModel } from '../models/TaxRate.model';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class InactivateTaxRateService {
/**
* @param {EventEmitter2} eventEmitter - The event emitter.
* @param {UnitOfWork} uow - The unit of work.
* @param {CommandTaxRatesValidators} validators - The tax rates validators.
* @param {typeof TaxRateModel} taxRateModel - The tax rate model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly validators: CommandTaxRatesValidators,
@Inject(TaxRateModel.name)
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>,
) {}
/**
* Edits the given tax rate.
* @param {number} taxRateId
* @returns {Promise<ITaxRate>}
*/
public async inactivateTaxRate(taxRateId: number) {
const oldTaxRate = await this.taxRateModel().query().findById(taxRateId);
// Validates the tax rate existance.
this.validators.validateTaxRateExistance(oldTaxRate);
// Validates the tax rate active.
this.validators.validateTaxRateNotInactive(oldTaxRate);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onTaxRateActivating` event.
await this.eventEmitter.emitAsync(events.taxRates.onInactivating, {
taxRateId,
trx,
} as ITaxRateActivatingPayload);
const taxRate = await this.taxRateModel()
.query(trx)
.findById(taxRateId)
.patch({ active: false });
// Triggers `onTaxRateCreated` event.
await this.eventEmitter.emitAsync(events.taxRates.onInactivated, {
taxRateId,
trx,
} as ITaxRateActivatedPayload);
return taxRate;
});
}
}