crete inventory with food creation and update
This commit is contained in:
@@ -99,6 +99,12 @@ export class CreateFoodDto {
|
|||||||
@ApiPropertyOptional({ example: 0 })
|
@ApiPropertyOptional({ example: 0 })
|
||||||
discount?: number;
|
discount?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0)
|
||||||
|
@Type(() => Number)
|
||||||
|
@ApiProperty({ example: 50 })
|
||||||
|
dailyStock: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { RestRepository } from 'src/modules/restaurants/repositories/rest.reposi
|
|||||||
import { CacheService } from '../../utils/cache.service';
|
import { CacheService } from '../../utils/cache.service';
|
||||||
import { Favorite } from '../entities/favorite.entity';
|
import { Favorite } from '../entities/favorite.entity';
|
||||||
import { MealType } from '../interface/food.interface';
|
import { MealType } from '../interface/food.interface';
|
||||||
|
import { Inventory } from 'src/modules/inventory/entities/inventory.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FoodService {
|
export class FoodService {
|
||||||
@@ -25,8 +26,8 @@ export class FoodService {
|
|||||||
private readonly cacheService: CacheService,
|
private readonly cacheService: CacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async create(restId: string, createFoodDto: CreateFoodDto): Promise<Food> {
|
async create(restId: string, createFoodDto: CreateFoodDto) {
|
||||||
const { categoryId, ...rest } = createFoodDto;
|
const { categoryId, dailyStock = 0, ...rest } = createFoodDto;
|
||||||
const restaurant = await this.restRepository.findOne({ id: restId });
|
const restaurant = await this.restRepository.findOne({ id: restId });
|
||||||
if (!restaurant) {
|
if (!restaurant) {
|
||||||
throw new NotFoundException(RestMessage.NOT_FOUND);
|
throw new NotFoundException(RestMessage.NOT_FOUND);
|
||||||
@@ -36,6 +37,7 @@ export class FoodService {
|
|||||||
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { food, inventory } = await this.em.transactional(async em => {
|
||||||
// prepare data with defaults for required fields so repository.create typing is satisfied
|
// prepare data with defaults for required fields so repository.create typing is satisfied
|
||||||
const data: RequiredEntityData<Food> = {
|
const data: RequiredEntityData<Food> = {
|
||||||
desc: rest.desc,
|
desc: rest.desc,
|
||||||
@@ -58,17 +60,24 @@ export class FoodService {
|
|||||||
category: category,
|
category: category,
|
||||||
};
|
};
|
||||||
|
|
||||||
const food = this.foodRepository.create(data);
|
const food = em.create(Food, data);
|
||||||
if (!food) {
|
const newInventoryRecord = em.create(Inventory, {
|
||||||
throw new Error('Failed to create food entity');
|
food: food,
|
||||||
}
|
availableStock: dailyStock,
|
||||||
|
totalStock: dailyStock,
|
||||||
|
});
|
||||||
|
|
||||||
await this.em.persistAndFlush(food);
|
await em.flush();
|
||||||
|
return { food, inventory: newInventoryRecord };
|
||||||
|
});
|
||||||
|
|
||||||
// Invalidate cache for the restaurant
|
// Re-load created entities with the primary EM to ensure they're attached
|
||||||
// await this.invalidateRestaurantFoodsCache(restaurant.slug);
|
const savedFood = food?.id
|
||||||
|
? await this.foodRepository.findOne({ id: food.id }, { populate: ['category', 'restaurant'] })
|
||||||
|
: null;
|
||||||
|
const savedInventory = inventory?.id ? await this.em.findOne(Inventory, { id: inventory.id }) : inventory;
|
||||||
|
|
||||||
return food;
|
return { food: savedFood ?? food, inventory: savedInventory ?? inventory };
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(restId: string, dto: FindFoodsDto) {
|
findAll(restId: string, dto: FindFoodsDto) {
|
||||||
@@ -157,13 +166,15 @@ export class FoodService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(restId: string, id: string, dto: Partial<CreateFoodDto>): Promise<Food> {
|
async update(restId: string, id: string, dto: Partial<CreateFoodDto>): Promise<Food> {
|
||||||
|
const { categoryId, dailyStock, ...rest } = dto;
|
||||||
const food = await this.foodRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['restaurant'] });
|
const food = await this.foodRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['restaurant'] });
|
||||||
if (!food) {
|
if (!food) {
|
||||||
throw new NotFoundException(FoodMessage.NOT_FOUND);
|
throw new NotFoundException(FoodMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// attach new categories if provided (adds, does not attempt to preserve/remove existing ones)
|
// attach new categories if provided (adds, does not attempt to preserve/remove existing ones)
|
||||||
if (dto.categoryId) {
|
if (categoryId) {
|
||||||
const category = await this.categoryRepository.findOne({ id: dto.categoryId, restaurant: { id: restId } });
|
const category = await this.categoryRepository.findOne({ id: categoryId, restaurant: { id: restId } });
|
||||||
|
|
||||||
if (!category) {
|
if (!category) {
|
||||||
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
||||||
@@ -172,17 +183,37 @@ export class FoodService {
|
|||||||
this.em.assign(food, { category: category });
|
this.em.assign(food, { category: category });
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign other fields from DTO
|
// assign other fields from DTO (excluding dailyStock handled below)
|
||||||
const { categoryId, ...rest } = dto;
|
|
||||||
void categoryId;
|
|
||||||
this.em.assign(food, rest);
|
this.em.assign(food, rest);
|
||||||
|
|
||||||
await this.em.persistAndFlush(food);
|
// Persist food and update/create related inventory atomically
|
||||||
|
await this.em.transactional(async em => {
|
||||||
|
await em.persistAndFlush(food);
|
||||||
|
|
||||||
// Invalidate cache for the restaurant
|
if (typeof dailyStock !== 'undefined') {
|
||||||
// await this.invalidateRestaurantFoodsCache(food.restaurant.slug);
|
const inventoryRecord = await em.findOne(Inventory, { food: food });
|
||||||
|
if (inventoryRecord) {
|
||||||
|
inventoryRecord.totalStock = dailyStock;
|
||||||
|
inventoryRecord.availableStock = dailyStock;
|
||||||
|
} else {
|
||||||
|
em.create(Inventory, {
|
||||||
|
food: food,
|
||||||
|
availableStock: dailyStock,
|
||||||
|
totalStock: dailyStock,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return food;
|
await em.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-load the food to ensure populated relations and stable instance
|
||||||
|
const savedFood = await this.foodRepository.findOne({ id: food.id }, { populate: ['category', 'restaurant'] });
|
||||||
|
|
||||||
|
// Invalidate cache for the restaurant if present (optional)
|
||||||
|
// await this.invalidateRestaurantFoodsCache(savedFood?.restaurant?.slug);
|
||||||
|
|
||||||
|
return savedFood ?? food;
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(restId: string, id: string): Promise<void> {
|
async remove(restId: string, id: string): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user