Files
bigcapital/packages/server/src/modules/Items/CreateItem.service.ts
Ahmed Bouhuolia edf12fb87a fix(server): resolve all TypeScript errors without unsafe type casts
Fix 20+ pre-existing TypeScript errors in the server package using
proper type-safe solutions — no `as any`, `as unknown`, or `any` types.

Key changes:
- Replace R.curry with regular curried arrow functions for proper inference
- Add return types to abstract methods (DynamicFilterRoleAbstractor)
- Add field declarations to empty models (ItemWarehouseQuantity)
- Add index signature to IMetadata for dynamic extra columns
- Use explicit field construction instead of pick()+cast patterns
- Convert moment format strings to Date objects where Date type expected
- Make interface properties optional where payloads don't include them
- Use native Array.reduce with proper typing instead of lodash chain

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:52:46 +02:00

124 lines
4.0 KiB
TypeScript

import { Knex } from 'knex';
import { defaultTo } from 'lodash';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Inject, Injectable, Scope } from '@nestjs/common';
import { IItemDTO, IItemEventCreatedPayload } from '@/interfaces/Item';
import { events } from '@/common/events/events';
import { ItemsValidators } from './ItemValidator.service';
import { Item } from './models/Item';
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
import { TenantModelProxy } from '../System/models/TenantBaseModel';
import { CreateItemDto } from './dtos/Item.dto';
@Injectable({ scope: Scope.REQUEST })
export class CreateItemService {
/**
* Constructor for the CreateItemService class.
* @param {EventEmitter2} eventEmitter - Event emitter for publishing item creation events.
* @param {UnitOfWork} uow - Unit of Work for tenant database transactions.
* @param {ItemsValidators} validators - Service for validating item data.
* @param {typeof Item} itemModel - The Item model class for database operations.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly validators: ItemsValidators,
@Inject(Item.name)
private readonly itemModel: TenantModelProxy<typeof Item>,
) {}
/**
* Authorize the creating item.
* @param {number} tenantId
* @param {IItemDTO} itemDTO
*/
async authorize(itemDTO: CreateItemDto) {
// Validate whether the given item name already exists on the storage.
await this.validators.validateItemNameUniquiness(itemDTO.name);
if (itemDTO.categoryId) {
await this.validators.validateItemCategoryExistance(itemDTO.categoryId);
}
if (itemDTO.sellAccountId) {
await this.validators.validateItemSellAccountExistance(
itemDTO.sellAccountId,
);
}
// Validate the income account id existance if the item is sellable.
this.validators.validateIncomeAccountExistance(
itemDTO.sellable,
itemDTO.sellAccountId,
);
if (itemDTO.costAccountId) {
await this.validators.validateItemCostAccountExistance(
itemDTO.costAccountId,
);
}
// Validate the cost account id existance if the item is purchasable.
this.validators.validateCostAccountExistance(
itemDTO.purchasable,
itemDTO.costAccountId,
);
if (itemDTO.inventoryAccountId) {
await this.validators.validateItemInventoryAccountExistance(
itemDTO.inventoryAccountId,
);
}
if (itemDTO.purchaseTaxRateId) {
await this.validators.validatePurchaseTaxRateExistance(
itemDTO.purchaseTaxRateId,
);
}
if (itemDTO.sellTaxRateId) {
await this.validators.validateSellTaxRateExistance(itemDTO.sellTaxRateId);
}
}
/**
* Transforms the item DTO to model.
* @param {CreateItemDto} itemDTO - Item DTO.
* @return {IItem}
*/
private transformNewItemDTOToModel(itemDTO: CreateItemDto) {
return {
...itemDTO,
active: Boolean(defaultTo(itemDTO.active, true)),
quantityOnHand: itemDTO.type === 'inventory' ? 0 : null,
};
}
/**
* Creates a new item.
* @param {IItemDTO} itemDTO
* @return {Promise<number>} - The created item id.
*/
public async createItem(
itemDTO: CreateItemDto,
trx?: Knex.Transaction,
): Promise<number> {
// Authorize the item before creating.
await this.authorize(itemDTO);
// Creates a new item with associated transactions under unit-of-work envirement.
return this.uow.withTransaction<number>(async (trx: Knex.Transaction) => {
const itemInsert = this.transformNewItemDTOToModel(itemDTO);
// Inserts a new item and fetch the created item.
const item = await this.itemModel()
.query(trx)
.insertAndFetch({
...itemInsert,
});
// Triggers `onItemCreated` event.
await this.eventEmitter.emitAsync(events.item.onCreated, {
item,
itemId: item.id,
trx,
} as IItemEventCreatedPayload);
return item.id;
}, trx);
}
}