Files
bigcapital/packages/server/src/services/Items/CreateItem.ts
2024-11-28 17:59:09 +02:00

134 lines
3.6 KiB
TypeScript

import { Knex } from 'knex';
import { defaultTo } from 'lodash';
import { Service, Inject } from 'typedi';
import { IItem, IItemDTO, IItemEventCreatedPayload } from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import events from '@/subscribers/events';
import { ItemsValidators } from './ItemValidators';
@Service()
export class CreateItem {
@Inject()
private validators: ItemsValidators;
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private uow: UnitOfWork;
/**
* Authorize the creating item.
* @param {number} tenantId
* @param {IItemDTO} itemDTO
*/
async authorize(tenantId: number, itemDTO: IItemDTO) {
// Validate whether the given item name already exists on the storage.
await this.validators.validateItemNameUniquiness(tenantId, itemDTO.name);
if (itemDTO.categoryId) {
await this.validators.validateItemCategoryExistance(
tenantId,
itemDTO.categoryId
);
}
if (itemDTO.sellAccountId) {
await this.validators.validateItemSellAccountExistance(
tenantId,
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(
tenantId,
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(
tenantId,
itemDTO.inventoryAccountId
);
}
if (itemDTO.purchaseTaxRateId) {
await this.validators.validatePurchaseTaxRateExistance(
tenantId,
itemDTO.purchaseTaxRateId
);
}
if (itemDTO.sellTaxRateId) {
await this.validators.validateSellTaxRateExistance(
tenantId,
itemDTO.sellTaxRateId
);
}
}
/**
* Transforms the item DTO to model.
* @param {IItemDTO} itemDTO - Item DTO.
* @return {IItem}
*/
private transformNewItemDTOToModel(itemDTO: IItemDTO) {
return {
...itemDTO,
active: defaultTo(itemDTO.active, 1),
quantityOnHand: itemDTO.type === 'inventory' ? 0 : null,
};
}
/**
* Creates a new item.
* @param {number} tenantId DTO
* @param {IItemDTO} item
* @return {Promise<IItem>}
*/
public async createItem(
tenantId: number,
itemDTO: IItemDTO,
trx?: Knex.Transaction
): Promise<IItem> {
const { Item } = this.tenancy.models(tenantId);
// Authorize the item before creating.
await this.authorize(tenantId, itemDTO);
// Creates a new item with associated transactions under unit-of-work envirement.
const item = this.uow.withTransaction(
tenantId,
async (trx: Knex.Transaction) => {
// Inserts a new item and fetch the created item.
const item = await Item.query(trx).insertAndFetch({
...this.transformNewItemDTOToModel(itemDTO),
});
// Triggers `onItemCreated` event.
await this.eventPublisher.emitAsync(events.item.onCreated, {
tenantId,
item,
itemId: item.id,
trx,
} as IItemEventCreatedPayload);
return item;
},
trx
);
return item;
}
}