crete inventory with food creation and update
This commit is contained in:
@@ -99,6 +99,12 @@ export class CreateFoodDto {
|
||||
@ApiPropertyOptional({ example: 0 })
|
||||
discount?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ example: 50 })
|
||||
dailyStock: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
|
||||
@@ -11,6 +11,7 @@ import { RestRepository } from 'src/modules/restaurants/repositories/rest.reposi
|
||||
import { CacheService } from '../../utils/cache.service';
|
||||
import { Favorite } from '../entities/favorite.entity';
|
||||
import { MealType } from '../interface/food.interface';
|
||||
import { Inventory } from 'src/modules/inventory/entities/inventory.entity';
|
||||
|
||||
@Injectable()
|
||||
export class FoodService {
|
||||
@@ -25,8 +26,8 @@ export class FoodService {
|
||||
private readonly cacheService: CacheService,
|
||||
) {}
|
||||
|
||||
async create(restId: string, createFoodDto: CreateFoodDto): Promise<Food> {
|
||||
const { categoryId, ...rest } = createFoodDto;
|
||||
async create(restId: string, createFoodDto: CreateFoodDto) {
|
||||
const { categoryId, dailyStock = 0, ...rest } = createFoodDto;
|
||||
const restaurant = await this.restRepository.findOne({ id: restId });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException(RestMessage.NOT_FOUND);
|
||||
@@ -36,39 +37,47 @@ export class FoodService {
|
||||
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
// prepare data with defaults for required fields so repository.create typing is satisfied
|
||||
const data: RequiredEntityData<Food> = {
|
||||
desc: rest.desc,
|
||||
isActive: rest.isActive ?? true,
|
||||
inPlaceServe: rest.inPlaceServe ?? false,
|
||||
pickupServe: rest.pickupServe ?? false,
|
||||
discount: rest.discount ?? 0,
|
||||
isSpecialOffer: rest.isSpecialOffer ?? false,
|
||||
// map single-title/content DTO to localized fields
|
||||
title: rest.title,
|
||||
content: rest.content,
|
||||
// numeric/array fields
|
||||
price: rest.price ?? 0,
|
||||
prepareTime: rest.prepareTime ?? 0,
|
||||
images: rest.images ?? undefined,
|
||||
// new fields
|
||||
weekDays: rest.weekDays ?? [0, 1, 2, 3, 4, 5, 6],
|
||||
mealTypes: rest.mealTypes ?? [],
|
||||
restaurant: restaurant,
|
||||
category: category,
|
||||
};
|
||||
const { food, inventory } = await this.em.transactional(async em => {
|
||||
// prepare data with defaults for required fields so repository.create typing is satisfied
|
||||
const data: RequiredEntityData<Food> = {
|
||||
desc: rest.desc,
|
||||
isActive: rest.isActive ?? true,
|
||||
inPlaceServe: rest.inPlaceServe ?? false,
|
||||
pickupServe: rest.pickupServe ?? false,
|
||||
discount: rest.discount ?? 0,
|
||||
isSpecialOffer: rest.isSpecialOffer ?? false,
|
||||
// map single-title/content DTO to localized fields
|
||||
title: rest.title,
|
||||
content: rest.content,
|
||||
// numeric/array fields
|
||||
price: rest.price ?? 0,
|
||||
prepareTime: rest.prepareTime ?? 0,
|
||||
images: rest.images ?? undefined,
|
||||
// new fields
|
||||
weekDays: rest.weekDays ?? [0, 1, 2, 3, 4, 5, 6],
|
||||
mealTypes: rest.mealTypes ?? [],
|
||||
restaurant: restaurant,
|
||||
category: category,
|
||||
};
|
||||
|
||||
const food = this.foodRepository.create(data);
|
||||
if (!food) {
|
||||
throw new Error('Failed to create food entity');
|
||||
}
|
||||
const food = em.create(Food, data);
|
||||
const newInventoryRecord = em.create(Inventory, {
|
||||
food: food,
|
||||
availableStock: dailyStock,
|
||||
totalStock: dailyStock,
|
||||
});
|
||||
|
||||
await this.em.persistAndFlush(food);
|
||||
await em.flush();
|
||||
return { food, inventory: newInventoryRecord };
|
||||
});
|
||||
|
||||
// Invalidate cache for the restaurant
|
||||
// await this.invalidateRestaurantFoodsCache(restaurant.slug);
|
||||
// Re-load created entities with the primary EM to ensure they're attached
|
||||
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) {
|
||||
@@ -157,13 +166,15 @@ export class FoodService {
|
||||
}
|
||||
|
||||
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'] });
|
||||
if (!food) {
|
||||
throw new NotFoundException(FoodMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
// attach new categories if provided (adds, does not attempt to preserve/remove existing ones)
|
||||
if (dto.categoryId) {
|
||||
const category = await this.categoryRepository.findOne({ id: dto.categoryId, restaurant: { id: restId } });
|
||||
if (categoryId) {
|
||||
const category = await this.categoryRepository.findOne({ id: categoryId, restaurant: { id: restId } });
|
||||
|
||||
if (!category) {
|
||||
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
||||
@@ -172,17 +183,37 @@ export class FoodService {
|
||||
this.em.assign(food, { category: category });
|
||||
}
|
||||
|
||||
// assign other fields from DTO
|
||||
const { categoryId, ...rest } = dto;
|
||||
void categoryId;
|
||||
// assign other fields from DTO (excluding dailyStock handled below)
|
||||
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
|
||||
// await this.invalidateRestaurantFoodsCache(food.restaurant.slug);
|
||||
if (typeof dailyStock !== 'undefined') {
|
||||
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> {
|
||||
|
||||
Reference in New Issue
Block a user