diff --git a/src/modules/foods/dto/create-food.dto.ts b/src/modules/foods/dto/create-food.dto.ts index 0ac227d..a27ccf5 100644 --- a/src/modules/foods/dto/create-food.dto.ts +++ b/src/modules/foods/dto/create-food.dto.ts @@ -1 +1,87 @@ -export class CreateFoodDto {} +import { IsBoolean, IsOptional, IsString, IsNumber, IsArray } from 'class-validator'; + +export class CreateFoodDto { + @IsOptional() + @IsBoolean() + breakfast?: boolean; + @IsOptional() + @IsBoolean() + tue?: boolean; + @IsOptional() + @IsBoolean() + wed?: boolean; + @IsOptional() + @IsBoolean() + thu?: boolean; + @IsOptional() + @IsBoolean() + fri?: boolean; + @IsOptional() + @IsString() + titleFa?: string; + @IsOptional() + @IsString() + titleEn?: string; + @IsOptional() + @IsString() + contentFa?: string; + @IsOptional() + @IsString() + contentEn?: string; + @IsOptional() + @IsNumber() + price?: number; + @IsOptional() + @IsNumber() + points?: number; + @IsOptional() + @IsNumber() + prepareTime?: number; + @IsOptional() + @IsBoolean() + sat?: boolean; + @IsOptional() + @IsBoolean() + sun?: boolean; + @IsOptional() + @IsBoolean() + mon?: boolean; + @IsOptional() + @IsBoolean() + noon?: boolean; + @IsOptional() + @IsBoolean() + dinner?: boolean; + @IsOptional() + @IsBoolean() + isPickup?: boolean; + @IsOptional() + @IsNumber() + stock?: number; + @IsOptional() + @IsNumber() + stockDefault?: number; + @IsOptional() + @IsBoolean() + isActive?: boolean; + @IsOptional() + @IsArray() + @IsString({ each: true }) + images?: string[]; + @IsOptional() + @IsBoolean() + inPlaceServe?: boolean; + @IsOptional() + @IsBoolean() + pickupServe?: boolean; + @IsOptional() + @IsNumber() + rate?: number; + @IsOptional() + @IsNumber() + discount?: number; + @IsOptional() + @IsArray() + @IsString({ each: true }) + categoryIds?: string[]; +} diff --git a/src/modules/foods/entities/category.ts b/src/modules/foods/entities/category.ts new file mode 100644 index 0000000..db756fb --- /dev/null +++ b/src/modules/foods/entities/category.ts @@ -0,0 +1,12 @@ +import { Entity, Property, ManyToMany, Collection } from '@mikro-orm/core'; +import { Foods } from './food.entity'; +import { BaseEntity } from 'src/common/entities/base.entity'; + +@Entity({ tableName: 'categories' }) +export class Category extends BaseEntity { + @Property({ nullable: true }) + title?: string; + + @ManyToMany(() => Foods, food => food.categories) + foods = new Collection(this); +} diff --git a/src/modules/foods/entities/food.entity.ts b/src/modules/foods/entities/food.entity.ts index 3a8e2ed..406e627 100644 --- a/src/modules/foods/entities/food.entity.ts +++ b/src/modules/foods/entities/food.entity.ts @@ -1 +1,76 @@ -export class Food {} +import { Collection, Entity, ManyToMany, Property } from '@mikro-orm/core'; +import { Category } from './category'; +import { BaseEntity } from 'src/common/entities/base.entity'; + +@Entity({ tableName: 'foods' }) +export class Foods extends BaseEntity { + @ManyToMany(() => Category) + categories = new Collection(this); + + @Property({ nullable: true }) + titleFa?: string; + + @Property({ nullable: true }) + titleEn?: string; + + @Property({ type: 'text', nullable: true }) + contentFa?: string; + + @Property({ type: 'text', nullable: true }) + contentEn?: string; + + @Property({ type: 'decimal', precision: 10, scale: 2, nullable: true }) + price?: number; + + @Property({ type: 'int', nullable: true }) + points?: number; + + @Property({ type: 'int', nullable: true }) + prepareTime?: number; // in minutes + + @Property({ type: 'boolean', default: false }) + sat: boolean = false; + + @Property({ type: 'boolean', default: false }) + sun: boolean = false; + + @Property({ type: 'boolean', default: false }) + mon: boolean = false; + + @Property({ type: 'boolean', default: false }) + breakfast: boolean = false; + + @Property({ type: 'boolean', default: false }) + noon: boolean = false; + + @Property({ type: 'boolean', default: false }) + dinner: boolean = false; + + @Property({ type: 'boolean', default: false }) + isPickup: boolean = false; + + @Property({ type: 'int', default: 0 }) + stock: number = 0; + + @Property({ type: 'int', default: 0 }) + stockDefault: number = 0; + + @Property({ type: 'boolean', default: true }) + isActive: boolean = true; + + // you can store image URLs as JSON array + @Property({ type: 'json', nullable: true }) + images?: string[]; + + @Property({ type: 'boolean', default: false }) + inPlaceServe: boolean = false; + + @Property({ type: 'boolean', default: false }) + pickupServe: boolean = false; + + @Property({ type: 'float', default: 0 }) + rate: number = 0; + + @Property({ type: 'float', default: 0 }) + discount: number = 0; +} diff --git a/src/modules/foods/foods.controller.ts b/src/modules/foods/foods.controller.ts index bf170a8..81bfd4a 100644 --- a/src/modules/foods/foods.controller.ts +++ b/src/modules/foods/foods.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; -import { FoodsService } from './foods.service'; +import { FoodsService } from './providers/foods.service'; import { CreateFoodDto } from './dto/create-food.dto'; import { UpdateFoodDto } from './dto/update-food.dto'; diff --git a/src/modules/foods/foods.module.ts b/src/modules/foods/foods.module.ts index 9c282c2..2710730 100644 --- a/src/modules/foods/foods.module.ts +++ b/src/modules/foods/foods.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { FoodsService } from './foods.service'; +import { FoodsService } from './providers/foods.service'; import { FoodsController } from './foods.controller'; @Module({ diff --git a/src/modules/foods/foods.service.ts b/src/modules/foods/foods.service.ts deleted file mode 100644 index 162b32e..0000000 --- a/src/modules/foods/foods.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { CreateFoodDto } from './dto/create-food.dto'; -import { UpdateFoodDto } from './dto/update-food.dto'; - -@Injectable() -export class FoodsService { - create(createFoodDto: CreateFoodDto) { - return 'This action adds a new food'; - } - - findAll() { - return `This action returns all foods`; - } - - findOne(id: number) { - return `This action returns a #${id} food`; - } - - update(id: number, updateFoodDto: UpdateFoodDto) { - return `This action updates a #${id} food`; - } - - remove(id: number) { - return `This action removes a #${id} food`; - } -} diff --git a/src/modules/foods/providers/foods.service.ts b/src/modules/foods/providers/foods.service.ts new file mode 100644 index 0000000..ce93af8 --- /dev/null +++ b/src/modules/foods/providers/foods.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; +import { CreateFoodDto } from '../dto/create-food.dto'; +import { FoodRepository } from '../repositories/food.repository'; +import { CategoryRepository } from '../repositories/category.repository'; +import { EntityManager } from '@mikro-orm/postgresql'; +import { Foods } from '../entities/food.entity'; + +@Injectable() +export class FoodsService { + constructor( + private readonly foodRepository: FoodRepository, + private readonly categoryRepository: CategoryRepository, + private readonly em: EntityManager, + ) {} + + async create(createFoodDto: CreateFoodDto): Promise { + const { categoryIds, ...rest } = createFoodDto; + + // create an empty food entity and assign dto properties to avoid strict typing issues + const food = new Foods(); + this.em.assign(food, rest as Partial); + + // attach categories if provided + if (categoryIds && categoryIds.length) { + const categories = await this.categoryRepository.find({ id: { $in: categoryIds } }); + categories.forEach(cat => food.categories.add(cat)); + } + + await this.em.persistAndFlush(food); + return food; + } + + findAll() { + return `This action returns all foods`; + } + + findOne(id: number) { + return `This action returns a #${id} food`; + } + + update(id: number) { + return `This action updates a #${id} food`; + } + + remove(id: number) { + return `This action removes a #${id} food`; + } +} diff --git a/src/modules/foods/repositories/category.repository.ts b/src/modules/foods/repositories/category.repository.ts new file mode 100644 index 0000000..07b7d03 --- /dev/null +++ b/src/modules/foods/repositories/category.repository.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { Category } from '../entities/category'; + +@Injectable() +export class CategoryRepository extends EntityRepository { + constructor(readonly em: EntityManager) { + super(em, Category); + } +} diff --git a/src/modules/foods/repositories/food.repository.ts b/src/modules/foods/repositories/food.repository.ts new file mode 100644 index 0000000..2477e6b --- /dev/null +++ b/src/modules/foods/repositories/food.repository.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { Foods } from '../entities/food.entity'; + +@Injectable() +export class FoodRepository extends EntityRepository { + constructor(readonly em: EntityManager) { + super(em, Foods); + } +}