refactor: migrate item categories to nestjs

This commit is contained in:
Ahmed Bouhuolia
2024-12-20 10:40:35 +02:00
parent 83dfaa00fd
commit 1f32a7c59a
18 changed files with 128 additions and 22 deletions

View File

@@ -1,7 +1,8 @@
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './modules/App/App.module';
import { ClsMiddleware } from 'nestjs-cls';
import { AppModule } from './modules/App/App.module';
import './utils/moment-mysql';
async function bootstrap() {
const app = await NestFactory.create(AppModule);

View File

@@ -33,6 +33,7 @@ import { TransformerInjectable } from '../Transformer/TransformerInjectable.serv
import { TransformerModule } from '../Transformer/Transformer.module';
import { AccountsModule } from '../Accounts/Accounts.module';
import { ExpensesModule } from '../Expenses/Expenses.module';
import { ItemCategoryModule } from '../ItemCategories/ItemCategory.module';
@Module({
imports: [
@@ -87,6 +88,7 @@ import { ExpensesModule } from '../Expenses/Expenses.module';
TenancyDatabaseModule,
TenancyModelsModule,
ItemsModule,
ItemCategoryModule,
AccountsModule,
ExpensesModule,
],
@@ -106,7 +108,6 @@ import { ExpensesModule } from '../Expenses/Expenses.module';
},
AppService,
JwtStrategy,
],
})
export class AppModule {

View File

@@ -6,17 +6,25 @@ import { PublishExpense } from './commands/PublishExpense.service';
import { ExpensesController } from './Expenses.controller';
import { ExpensesApplication } from './ExpensesApplication.service';
import { GetExpenseService } from './queries/GetExpense.service';
import { ExpenseDTOTransformer } from './commands/CommandExpenseDTO.transformer';
import { CommandExpenseValidator } from './commands/CommandExpenseValidator.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
@Module({
imports: [],
controllers: [ExpensesController],
providers: [
CreateExpense,
ExpenseDTOTransformer,
CommandExpenseValidator,
EditExpense,
DeleteExpense,
PublishExpense,
GetExpenseService,
ExpensesApplication,
TenancyContext,
TransformerInjectable
],
})
export class ExpensesModule {}

View File

@@ -7,7 +7,6 @@ import {
IExpenseEditDTO,
} from '../interfaces/Expenses.interface';
// import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
// import { TenantMetadata } from '@/system/models';
import { Injectable } from '@nestjs/common';
import { Expense } from '../models/Expense.model';
import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index';
@@ -86,7 +85,7 @@ export class ExpenseDTOTransformer {
*/
public expenseCreateDTO = async (
expenseDTO: IExpenseCreateDTO,
): Promise<Expense> => {
): Promise<Partial<Expense>> => {
const initialDTO = this.expenseDTOToModel(expenseDTO);
const tenant = await this.tenancyContext.getTenant(true);

View File

@@ -6,7 +6,7 @@ import {
IExpenseCreatingPayload,
} from '../interfaces/Expenses.interface';
import { CommandExpenseValidator } from './CommandExpenseValidator.service';
import { ExpenseDTOTransformer } from './ExpenseDTOTransformer';
import { ExpenseDTOTransformer } from './CommandExpenseDTO.transformer';
import { Account } from '@/modules/Accounts/models/Account.model';
import { Expense } from '@/modules/Expenses/models/Expense.model';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';

View File

@@ -7,7 +7,7 @@ import {
} from '../interfaces/Expenses.interface';
import { CommandExpenseValidator } from './CommandExpenseValidator.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ExpenseDTOTransformer } from './ExpenseDTOTransformer';
import { ExpenseDTOTransformer } from './CommandExpenseDTO.transformer';
// import { EntriesService } from '@/services/Entries';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Account } from '@/modules/Accounts/models/Account.model';

View File

@@ -1,9 +1,11 @@
import { Injectable } from '@nestjs/common';
import { IItemCategoryOTD } from './ItemCategory.interfaces';
import { CreateItemCategoryService } from './commands/CreateItemCategory.service';
import { DeleteItemCategoryService } from './commands/DeleteItemCategory.service';
import { EditItemCategoryService } from './commands/EditItemCategory.service';
import { GetItemCategoryService } from './queries/GetItemCategory.service';
@Injectable()
export class ItemCategoryApplication {
constructor(
private readonly createItemCategoryService: CreateItemCategoryService,

View File

@@ -9,8 +9,10 @@ import {
} from '@nestjs/common';
import { ItemCategoryApplication } from './ItemCategory.application';
import { IItemCategoryOTD } from './ItemCategory.interfaces';
import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('item-categories')
@PublicRoute()
export class ItemCategoryController {
constructor(
private readonly itemCategoryApplication: ItemCategoryApplication,

View File

@@ -6,6 +6,9 @@ import { EditItemCategoryService } from './commands/EditItemCategory.service';
import { GetItemCategoryService } from './queries/GetItemCategory.service';
import { ItemCategoryApplication } from './ItemCategory.application';
import { ItemCategoryController } from './ItemCategory.controller';
import { CommandItemCategoryValidatorService } from './commands/CommandItemCategoryValidator.service';
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
@Module({
imports: [TenancyDatabaseModule],
@@ -16,6 +19,9 @@ import { ItemCategoryController } from './ItemCategory.controller';
GetItemCategoryService,
DeleteItemCategoryService,
ItemCategoryApplication,
CommandItemCategoryValidatorService,
TransformerInjectable,
TenancyContext
],
})
export class ItemCategoryModule {}

View File

@@ -1,10 +1,11 @@
import { Account } from '@/modules/Accounts/models/Account.model';
import { ItemCategory } from '../models/ItemCategory.model';
import { Inject } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { ServiceError } from '@/modules/Items/ServiceError';
import { ERRORS } from '../constants';
import { ACCOUNT_ROOT_TYPE, ACCOUNT_TYPE } from '@/constants/accounts';
@Injectable()
export class CommandItemCategoryValidatorService {
constructor(
@Inject(ItemCategory.name)

View File

@@ -26,12 +26,15 @@ export class CreateItemCategoryService {
* Transforms OTD to model object.
* @param {IItemCategoryOTD} itemCategoryOTD
* @param {ISystemUser} authorizedUser
* @returns {ItemCategory}
*/
private transformOTDToObject(
itemCategoryOTD: IItemCategoryOTD,
authorizedUser: SystemUser,
): ItemCategory {
return { ...itemCategoryOTD, userId: authorizedUser.id };
): Partial<ItemCategory> {
return {
...itemCategoryOTD,
// userId: authorizedUser.id
};
}
/**
* Inserts a new item category.
@@ -58,10 +61,8 @@ export class CreateItemCategoryService {
itemCategoryOTD.inventoryAccountId,
);
}
const itemCategoryObj = this.transformOTDToObject(
itemCategoryOTD,
authorizedUser,
);
const itemCategoryObj = this.transformOTDToObject(itemCategoryOTD);
// Creates item category under unit-of-work evnirement.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Inserts the item category.

View File

@@ -10,12 +10,14 @@ import { SystemUser } from '@/modules/System/models/SystemUser';
import { Knex } from 'knex';
import { ItemCategory } from '../models/ItemCategory.model';
import { Inject } from '@nestjs/common';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
export class EditItemCategoryService {
constructor(
private readonly uow: UnitOfWork,
private readonly validator: CommandItemCategoryValidatorService,
private readonly eventEmitter: EventEmitter2,
private readonly tenancyContext: TenancyContext,
@Inject(ItemCategory.name)
private readonly itemCategoryModel: typeof ItemCategory,
) {}
@@ -29,7 +31,7 @@ export class EditItemCategoryService {
public async editItemCategory(
itemCategoryId: number,
itemCategoryOTD: IItemCategoryOTD,
): Promise<IItemCategory> {
): Promise<ItemCategory> {
// Retrieve the item category from the storage.
const oldItemCategory = await this.itemCategoryModel
.query()
@@ -52,11 +54,14 @@ export class EditItemCategoryService {
itemCategoryOTD.inventoryAccountId,
);
}
// Retrieves the authorized user.
const authorizedUser = await this.tenancyContext.getSystemUser();
const itemCategoryObj = this.transformOTDToObject(
itemCategoryOTD,
authorizedUser,
);
//
// Creates item category under unit-of-work evnirement.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
//
const itemCategory = await ItemCategory.query().patchAndFetchById(

View File

@@ -5,6 +5,15 @@ import { Model, mixin } from 'objection';
// import ItemCategorySettings from './ItemCategory.Settings';
export class ItemCategory extends BaseModel {
name!: string;
description!: string;
costAccountId!: number;
sellAccountId!: number;
inventoryAccountId!: number;
userId!: number;
/**
* Table name.
*/
@@ -58,6 +67,6 @@ export class ItemCategory extends BaseModel {
* Model meta.
*/
// static get meta() {
// return ItemCategorySettings;
// return ItemCategorySettings;
// }
}

View File

@@ -6,8 +6,19 @@ import { Item } from '../../../modules/Items/models/Item';
import { Account } from '@/modules/Accounts/models/Account.model';
import { ItemEntry } from '@/modules/Items/models/ItemEntry';
import { AccountTransaction } from '@/modules/Accounts/models/AccountTransaction.model';
import { Expense } from '@/modules/Expenses/models/Expense.model';
import ExpenseCategory from '@/modules/Expenses/models/ExpenseCategory.model';
import { ItemCategory } from '@/modules/ItemCategories/models/ItemCategory.model';
const models = [Item, Account, ItemEntry, AccountTransaction];
const models = [
Item,
Account,
ItemEntry,
AccountTransaction,
Expense,
ExpenseCategory,
ItemCategory,
];
const modelProviders = models.map((model) => {
return {

View File

@@ -0,0 +1,14 @@
import * as moment from 'moment';
// Extends moment prototype to add a new method to format date to MySQL datetime format.
moment.prototype.toMySqlDateTime = function () {
return this.format('YYYY-MM-DD HH:mm:ss');
};
declare global {
namespace moment {
interface Moment {
toMySqlDateTime(): string;
}
}
}

View File

@@ -0,0 +1,49 @@
import * as request from 'supertest';
import { faker } from '@faker-js/faker';
import { app } from './init-app-test';
describe('Item Categories(e2e)', () => {
it('/item-categories (POST)', () => {
return request(app.getHttpServer())
.post('/item-categories')
.set('organization-id', '4064541lv40nhca')
.send({
name: faker.person.fullName(),
description: faker.lorem.sentence(),
})
.expect(201);
});
it('/item-categories/:id (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/item-categories')
.set('organization-id', '4064541lv40nhca')
.send({
name: faker.person.fullName(),
description: faker.lorem.sentence(),
});
const itemCategoryId = response.body.id;
return request(app.getHttpServer())
.get(`/item-categories/${itemCategoryId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/item-categories/:id (DELETE)', async () => {
const response = await request(app.getHttpServer())
.post('/item-categories')
.set('organization-id', '4064541lv40nhca')
.send({
name: faker.person.fullName(),
description: faker.lorem.sentence(),
});
const itemCategoryId = response.body.id;
return request(app.getHttpServer())
.delete(`/item-categories/${itemCategoryId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
});

View File

@@ -1,8 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { faker } from '@faker-js/faker';
import { AppModule } from '../src/modules/App/App.module';
import { app } from './init-app-test';
describe('Items (e2e)', () => {

View File

@@ -20,7 +20,7 @@ export class CreateExpense {
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
private eventPublisher: EventPublisher;
@Inject()
private uow: UnitOfWork;