import { Injectable, NotFoundException } from '@nestjs/common'; import { CreateFoodDto } from '../dto/create-food.dto'; import { FindFoodsDto } from '../dto/find-foods.dto'; import { FoodRepository } from '../repositories/food.repository'; import { CategoryRepository } from '../repositories/category.repository'; import { EntityManager } from '@mikro-orm/postgresql'; import { RequiredEntityData } from '@mikro-orm/core'; import { Food } from '../entities/food.entity'; import { CategoryMessage, FoodMessage, RestMessage } from 'src/common/enums/message.enum'; import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository'; @Injectable() export class FoodService { constructor( private readonly foodRepository: FoodRepository, private readonly categoryRepository: CategoryRepository, private readonly restRepository: RestRepository, private readonly em: EntityManager, ) {} async create(restId: string, createFoodDto: CreateFoodDto): Promise { const { categoryId, ...rest } = createFoodDto; const restaurant = await this.restRepository.findOne({ id: restId }); if (!restaurant) { throw new NotFoundException(RestMessage.NOT_FOUND); } const category = await this.categoryRepository.findOne({ id: categoryId }); if (!category) { throw new NotFoundException(CategoryMessage.NOT_FOUND); } // prepare data with defaults for required fields so repository.create typing is satisfied const data: RequiredEntityData = { // boolean/day flags sat: rest.sat ?? false, sun: rest.sun ?? false, mon: rest.mon ?? false, breakfast: rest.breakfast ?? false, noon: rest.noon ?? false, dinner: rest.dinner ?? false, // pickup/stock stock: rest.stock ?? 0, stockDefault: rest.stockDefault ?? 0, isActive: rest.isActive ?? true, inPlaceServe: rest.inPlaceServe ?? false, pickupServe: rest.pickupServe ?? false, discount: rest.discount ?? 0, // map single-title/content DTO to localized fields title: rest.title, content: rest.content, // numeric/array fields price: rest.price ?? 0, points: rest.points ?? 0, prepareTime: rest.prepareTime ?? 0, images: rest.images ?? undefined, restaurant: restaurant, category: category }; const food = this.foodRepository.create(data); if (!food) { throw new Error('Failed to create food entity'); } await this.em.persistAndFlush(food); return food; } findAll(restId: string, dto: FindFoodsDto) { return this.foodRepository.findAllPaginated(restId, dto); } async findById(id: string): Promise { const food = await this.foodRepository.findOne({ id }, { populate: ['category'] }); if (!food) { throw new NotFoundException(FoodMessage.NOT_FOUND); } return food; } async findAllByRestaurant(slug: string): Promise { const restaurant = await this.restRepository.findOne({ slug }); if (!restaurant) { throw new NotFoundException(RestMessage.NOT_FOUND); } return this.foodRepository.find({ restaurant: { slug }, isActive: true }, { populate: ['category'] }); } async update(id: string, dto: Partial): Promise { const food = await this.foodRepository.findOne({ id }); 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 }); if (!category) { throw new NotFoundException(CategoryMessage.NOT_FOUND); } this.em.assign(food, { category: category }); } await this.em.persistAndFlush(food); return food; } async remove(id: string) { const food = await this.foodRepository.findOne({ id }); if (!food) { throw new NotFoundException(FoodMessage.NOT_FOUND); } food.deletedAt = new Date(); await this.em.persistAndFlush(food); } }