Files
bigcapital/packages/server/src/services/Items/EditItem.ts

162 lines
4.5 KiB
TypeScript

import { Knex } from 'knex';
import { Service, Inject } from 'typedi';
import {
IItem,
IItemDTO,
IItemEditDTO,
IItemEventEditedPayload,
} from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import { ItemsValidators } from './ItemValidators';
import events from '@/subscribers/events';
@Service()
export class EditItem {
@Inject()
private validators: ItemsValidators;
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private uow: UnitOfWork;
/**
* Authorize the editing item.
* @param {number} tenantId
* @param {IItemEditDTO} itemDTO
* @param {IItem} oldItem
*/
async authorize(tenantId: number, itemDTO: IItemEditDTO, oldItem: IItem) {
// Validate edit item type from inventory type.
this.validators.validateEditItemFromInventory(itemDTO, oldItem);
// Validate edit item type to inventory type.
await this.validators.validateEditItemTypeToInventory(
tenantId,
oldItem,
itemDTO
);
// Validate whether the given item name already exists on the storage.
await this.validators.validateItemNameUniquiness(
tenantId,
itemDTO.name,
oldItem.id
);
// Validate the item category existance on the storage,
if (itemDTO.categoryId) {
await this.validators.validateItemCategoryExistance(
tenantId,
itemDTO.categoryId
);
}
// Validate the sell account existance on the storage.
if (itemDTO.sellAccountId) {
await this.validators.validateItemSellAccountExistance(
tenantId,
itemDTO.sellAccountId
);
}
// Validate the cost account existance on the storage.
if (itemDTO.costAccountId) {
await this.validators.validateItemCostAccountExistance(
tenantId,
itemDTO.costAccountId
);
}
// Validate the inventory account existance onthe storage.
if (itemDTO.inventoryAccountId) {
await this.validators.validateItemInventoryAccountExistance(
tenantId,
itemDTO.inventoryAccountId
);
}
// Validate the purchase tax rate id existance.
if (itemDTO.purchaseTaxRateId) {
await this.validators.validatePurchaseTaxRateExistance(
tenantId,
itemDTO.purchaseTaxRateId
);
}
// Validate the sell tax rate id existance.
if (itemDTO.sellTaxRateId) {
await this.validators.validateSellTaxRateExistance(
tenantId,
itemDTO.sellTaxRateId
);
}
// Validate inventory account should be modified in inventory item
// has inventory transactions.
await this.validators.validateItemInvnetoryAccountModified(
tenantId,
oldItem,
itemDTO
);
}
/**
* Transformes edit item DTO to model.
* @param {IItemDTO} itemDTO - Item DTO.
* @param {IItem} oldItem -
*/
private transformEditItemDTOToModel(itemDTO: IItemDTO, oldItem: IItem) {
return {
...itemDTO,
...(itemDTO.type === 'inventory' && oldItem.type !== 'inventory'
? {
quantityOnHand: 0,
}
: {}),
};
}
/**
* Edits the item metadata.
* @param {number} tenantId
* @param {number} itemId
* @param {IItemDTO} itemDTO
*/
public async editItem(tenantId: number, itemId: number, itemDTO: IItemDTO) {
const { Item } = this.tenancy.models(tenantId);
// Validates the given item existance on the storage.
const oldItem = await Item.query().findById(itemId).throwIfNotFound();
// Authorize before editing item.
await this.authorize(tenantId, itemDTO, oldItem);
// Transform the edit item DTO to model.
const itemModel = this.transformEditItemDTOToModel(itemDTO, oldItem);
// Edits the item with associated transactions under unit-of-work envirement.
const newItem = this.uow.withTransaction(
tenantId,
async (trx: Knex.Transaction) => {
// Updates the item on the storage and fetches the updated once.
const newItem = await Item.query(trx).patchAndFetchById(itemId, {
...itemModel,
});
// Edit event payload.
const eventPayload: IItemEventEditedPayload = {
tenantId,
item: newItem,
oldItem,
itemId: newItem.id,
trx,
};
// Triggers `onItemEdited` event.
await this.eventPublisher.emitAsync(events.item.onEdited, eventPayload);
return newItem;
}
);
return newItem;
}
}