From 92cd7432f549bdd1505307c6da2cb954fb017d16 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 8 Feb 2026 15:06:52 +0330 Subject: [PATCH] product module --- src/common/decorators/index.ts | 2 +- src/common/decorators/rest-id.decorator.ts | 14 +- .../admin/controllers/admin.controller.ts | 14 +- .../cart/controllers/cart.controller.ts | 22 +-- .../contact/controllers/contact.controller.ts | 12 +- .../coupons/controllers/coupon.controller.ts | 8 +- src/modules/coupons/entities/coupon.entity.ts | 8 +- .../controllers/delivery.controller.ts | 16 +- src/modules/foods/entities/food.entity.ts | 2 +- .../controllers/notifications.controller.ts | 24 +-- .../orders/controllers/orders.controller.ts | 20 +- .../pager/controllers/pager.controller.ts | 8 +- .../controllers/payments.controller.ts | 10 +- .../controllers/category.controller.ts | 14 +- .../controllers/product.controller.ts | 53 +++-- .../products/dto/create-product.dto.ts | 43 +++- src/modules/products/dto/find-products.dto.ts | 2 +- .../products/dto/update-product.dto.ts | 4 +- .../products/entities/category.entity.ts | 4 +- .../products/entities/product.entity.ts | 16 +- .../products/entities/variant.entity.ts | 20 ++ .../products/providers/product.service.ts | 185 +++++++++++------- .../repositories/product.repository.ts | 2 +- .../review/controllers/review.controller.ts | 12 +- .../roles/controllers/roles.controller.ts | 12 +- .../shops/controllers/schedule.controller.ts | 12 +- .../shops/controllers/shops.controller.ts | 8 +- .../shops/providers/schedule.service.ts | 6 +- src/modules/shops/providers/shops.service.ts | 41 ++-- .../shops/repositories/rest.repository.ts | 2 +- src/modules/shops/shops.module.ts | 8 +- .../users/controllers/users.controller.ts | 14 +- 32 files changed, 359 insertions(+), 259 deletions(-) create mode 100644 src/modules/products/entities/variant.entity.ts diff --git a/src/common/decorators/index.ts b/src/common/decorators/index.ts index 6a12fec..0d1399c 100644 --- a/src/common/decorators/index.ts +++ b/src/common/decorators/index.ts @@ -1,3 +1,3 @@ export { UserId } from './user-id.decorator'; -export { RestId } from './rest-id.decorator'; +export { ShopId } from './rest-id.decorator'; export { RateLimit } from './rate-limit.decorator'; diff --git a/src/common/decorators/rest-id.decorator.ts b/src/common/decorators/rest-id.decorator.ts index 295fc72..9d0f2e3 100644 --- a/src/common/decorators/rest-id.decorator.ts +++ b/src/common/decorators/rest-id.decorator.ts @@ -2,17 +2,17 @@ import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; import type { Request } from 'express'; /** - * Decorator to extract restId from the authenticated request. - * Must be used after AdminAuthGuard or AuthGuard that sets request.restId. + * Decorator to extract shopId from the authenticated request. + * Must be used after AdminAuthGuard or AuthGuard that sets request.shopId. * * @example * @Get('/shops') * @UseGuards(AdminAuthGuard) - * getRestaurants(@RestId() restId: string) { - * return this.restaurantService.findById(restId); + * getRestaurants(@shopId() shopId: string) { + * return this.restaurantService.findById(shopId); * } */ -export const RestId = createParamDecorator((data: unknown, ctx: ExecutionContext): string => { - const request = ctx.switchToHttp().getRequest(); - return request.restId || ''; +export const ShopId = createParamDecorator((data: unknown, ctx: ExecutionContext): string => { + const request = ctx.switchToHttp().getRequest(); + return request.shopId || ''; }); diff --git a/src/modules/admin/controllers/admin.controller.ts b/src/modules/admin/controllers/admin.controller.ts index c54ef64..fb98612 100644 --- a/src/modules/admin/controllers/admin.controller.ts +++ b/src/modules/admin/controllers/admin.controller.ts @@ -16,13 +16,13 @@ import { CreateMyRestaurantAdminDto } from '../dto/create-my-shop-admin.dto'; @ApiTags('admin') @Controller() export class AdminController { - constructor(private readonly adminService: AdminService) {} + constructor(private readonly adminService: AdminService) { } @Get('admin/admins') @UseGuards(AdminAuthGuard) @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'admin' }) - async getAll(@RestId() restId: string) { + async getAll(@ShopId() restId: string) { const admin = await this.adminService.findAllByRestaurantId(restId); return admin; } @@ -31,7 +31,7 @@ export class AdminController { @UseGuards(AdminAuthGuard) @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'admin' }) - async getMe(@AdminId() adminId: string, @RestId() restId: string) { + async getMe(@AdminId() adminId: string, @ShopId() restId: string) { const admin = await this.adminService.findById(adminId, restId); return admin; } @@ -42,7 +42,7 @@ export class AdminController { @HttpCode(HttpStatus.CREATED) @ApiOperation({ summary: 'Create a new admin' }) @ApiBody({ type: CreateAdminDto }) - async create(@Body() dto: CreateAdminDto, @RestId() restId: string) { + async create(@Body() dto: CreateAdminDto, @ShopId() restId: string) { const admin = await this.adminService.createAdminForMyRestaurant(restId, dto); return admin; } @@ -53,7 +53,7 @@ export class AdminController { @ApiOperation({ summary: 'Update an admin' }) @ApiParam({ name: 'adminId', description: 'Admin ID' }) @ApiBody({ type: UpdateAdminDto }) - update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto, @RestId() restId: string): Promise { + update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto, @ShopId() restId: string): Promise { return this.adminService.update(adminId, restId, dto); } @@ -62,7 +62,7 @@ export class AdminController { @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'Get an admin by ID' }) @ApiParam({ name: 'id', description: 'Admin ID' }) - getById(@Param('adminId') adminId: string, @RestId() restId: string): Promise { + getById(@Param('adminId') adminId: string, @ShopId() restId: string): Promise { return this.adminService.findById(adminId, restId); } @@ -71,7 +71,7 @@ export class AdminController { @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'Delete an admin by ID' }) @ApiParam({ name: 'id', description: 'Admin ID' }) - deleteById(@Param('adminId') adminId: string, @RestId() restId: string): Promise { + deleteById(@Param('adminId') adminId: string, @ShopId() restId: string): Promise { return this.adminService.remove(adminId, restId); } diff --git a/src/modules/cart/controllers/cart.controller.ts b/src/modules/cart/controllers/cart.controller.ts index de9d959..f910fa6 100644 --- a/src/modules/cart/controllers/cart.controller.ts +++ b/src/modules/cart/controllers/cart.controller.ts @@ -5,7 +5,7 @@ import { ApplyCouponDto } from '../dto/apply-coupon.dto'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiBody, ApiParam, ApiHeader } from '@nestjs/swagger'; import { AuthGuard } from '../../../auth/guards/auth.guard'; import { UserId } from 'src/common/decorators/user-id.decorator'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { SetAllCartParmsDto } from '../dto/set-all-cart-params.dto'; import { API_HEADER_SLUG } from 'src/common/constants/index'; @@ -14,12 +14,12 @@ import { API_HEADER_SLUG } from 'src/common/constants/index'; @ApiTags('cart') @Controller('public/cart') export class CartController { - constructor(private readonly cartService: CartService) {} + constructor(private readonly cartService: CartService) { } @Get() @ApiOperation({ summary: 'Get cart for current user and shop' }) @ApiHeader(API_HEADER_SLUG) - async findOne(@UserId() userId: string, @RestId() restaurantId: string) { + async findOne(@UserId() userId: string, @ShopId() restaurantId: string) { return this.cartService.getOrCreateCart(userId, restaurantId); } @@ -27,7 +27,7 @@ export class CartController { @ApiOperation({ summary: 'Increment item quantity in cart by 1' }) @ApiHeader(API_HEADER_SLUG) @ApiParam({ name: 'foodId', description: 'Product ID to increment in cart' }) - async incrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) { + async incrementItem(@UserId() userId: string, @ShopId() restaurantId: string, @Param('foodId') foodId: string) { return this.cartService.incrementItem(userId, restaurantId, foodId, 1); } @@ -35,7 +35,7 @@ export class CartController { @ApiOperation({ summary: 'Decrement item quantity in cart by 1 (removes item if quantity reaches 0)' }) @ApiHeader(API_HEADER_SLUG) @ApiParam({ name: 'foodId', description: 'Product ID to decrement in cart' }) - async decrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) { + async decrementItem(@UserId() userId: string, @ShopId() restaurantId: string, @Param('foodId') foodId: string) { return this.cartService.decrementItem(userId, restaurantId, foodId); } @@ -45,7 +45,7 @@ export class CartController { @ApiBody({ type: BulkAddItemsToCartDto }) bulkAddItems( @UserId() userId: string, - @RestId() restaurantId: string, + @ShopId() restaurantId: string, @Body() bulkAddItemsDto: BulkAddItemsToCartDto, ) { return this.cartService.bulkAddItems(userId, restaurantId, bulkAddItemsDto); @@ -55,14 +55,14 @@ export class CartController { @ApiOperation({ summary: 'Remove item from cart' }) @ApiHeader(API_HEADER_SLUG) @ApiParam({ name: 'foodId', description: 'Product ID in the cart' }) - async removeItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) { + async removeItem(@UserId() userId: string, @ShopId() restaurantId: string, @Param('foodId') foodId: string) { return this.cartService.removeItem(userId, restaurantId, foodId); } @Delete() @ApiOperation({ summary: 'Clear entire cart' }) @ApiHeader(API_HEADER_SLUG) - async clearCart(@UserId() userId: string, @RestId() restaurantId: string) { + async clearCart(@UserId() userId: string, @ShopId() restaurantId: string) { return this.cartService.clearCart(userId, restaurantId); } @@ -70,14 +70,14 @@ export class CartController { @ApiOperation({ summary: 'Apply coupon to cart' }) @ApiHeader(API_HEADER_SLUG) @ApiBody({ type: ApplyCouponDto }) - async applyCoupon(@UserId() userId: string, @RestId() restaurantId: string, @Body() applyCouponDto: ApplyCouponDto) { + async applyCoupon(@UserId() userId: string, @ShopId() restaurantId: string, @Body() applyCouponDto: ApplyCouponDto) { return this.cartService.applyCoupon(userId, restaurantId, applyCouponDto); } @Delete('coupon') @ApiOperation({ summary: 'Remove coupon from cart' }) @ApiHeader(API_HEADER_SLUG) - async removeCoupon(@UserId() userId: string, @RestId() restaurantId: string) { + async removeCoupon(@UserId() userId: string, @ShopId() restaurantId: string) { return this.cartService.removeCoupon(userId, restaurantId); } @@ -86,7 +86,7 @@ export class CartController { @ApiOperation({ summary: 'Set all cart params' }) @ApiHeader(API_HEADER_SLUG) @ApiBody({ type: SetAllCartParmsDto }) - setAllCartParams(@UserId() userId: string, @RestId() restaurantId: string, @Body() dto: SetAllCartParmsDto) { + setAllCartParams(@UserId() userId: string, @ShopId() restaurantId: string, @Body() dto: SetAllCartParmsDto) { return this.cartService.setAllCartParams(userId, restaurantId, dto); } } diff --git a/src/modules/contact/controllers/contact.controller.ts b/src/modules/contact/controllers/contact.controller.ts index 83736fa..155e617 100644 --- a/src/modules/contact/controllers/contact.controller.ts +++ b/src/modules/contact/controllers/contact.controller.ts @@ -20,7 +20,7 @@ import { OptionalAuthGuard } from 'src/modules/auth/guards/optinalAuth.guard'; import { API_HEADER_SLUG } from 'src/common/constants'; import { Permission } from 'src/common/enums/permission.enum'; import { Permissions } from 'src/common/decorators/permissions.decorator'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { RestSlug } from 'src/common/decorators/rest-slug.decorator'; @ApiTags('contact') @@ -32,7 +32,7 @@ export class ContactController { @Post('public/contact') @ApiOperation({ summary: 'Create a new contact (public endpoint)' }) @ApiHeader(API_HEADER_SLUG) - async create(@Body() createContactDto: CreateContactDto, @RestSlug() slug: string, @RestId() restId?: string) { + async create(@Body() createContactDto: CreateContactDto, @RestSlug() slug: string, @ShopId() restId?: string) { return this.contactService.create(createContactDto, restId, slug); } @@ -48,7 +48,7 @@ export class ContactController { @ApiQuery({ name: 'orderBy', required: false, type: String }) @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) @ApiQuery({ name: 'status', required: false, enum: ['new', 'seen'] }) - async findAllPaginated(@RestId() restId: string, @Query() dto: FindContactsDto) { + async findAllPaginated(@ShopId() restId: string, @Query() dto: FindContactsDto) { return this.contactService.findAllPaginatedAdmin(dto, restId); } @@ -58,7 +58,7 @@ export class ContactController { @Get('admin/contact/:id') @ApiOperation({ summary: 'Get contact details by ID (admin only)' }) @ApiParam({ name: 'id', description: 'Contact ID' }) - async findOne(@RestId() restId: string, @Param('id') id: string) { + async findOne(@ShopId() restId: string, @Param('id') id: string) { return this.contactService.findOne(id, restId); } @@ -68,7 +68,7 @@ export class ContactController { @Patch('admin/contact/:id/status') @ApiOperation({ summary: 'Update contact status (admin only)' }) @ApiParam({ name: 'id', description: 'Contact ID' }) - async updateStatus(@RestId() restId: string, @Param('id') id: string, @Body() dto: UpdateContactStatusDto) { + async updateStatus(@ShopId() restId: string, @Param('id') id: string, @Body() dto: UpdateContactStatusDto) { return this.contactService.updateStatus(id, dto, restId); } @@ -78,7 +78,7 @@ export class ContactController { @Delete('admin/contact/:id') @ApiOperation({ summary: 'Hard delete a contact (admin only)' }) @ApiParam({ name: 'id', description: 'Contact ID' }) - async remove(@RestId() restId: string, @Param('id') id: string) { + async remove(@ShopId() restId: string, @Param('id') id: string) { await this.contactService.remove(id, restId); } diff --git a/src/modules/coupons/controllers/coupon.controller.ts b/src/modules/coupons/controllers/coupon.controller.ts index 81634e7..6012366 100644 --- a/src/modules/coupons/controllers/coupon.controller.ts +++ b/src/modules/coupons/controllers/coupon.controller.ts @@ -39,7 +39,7 @@ export class CouponController { @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) @ApiQuery({ name: 'type', required: false, enum: ['PERCENTAGE', 'FIXED'] }) @ApiQuery({ name: 'isActive', required: false, type: Boolean }) - getMyCoupons(@Query() dto: FindCouponsDto, @RestId() restId: string) { + getMyCoupons(@Query() dto: FindCouponsDto, @ShopId() restId: string) { return this.couponService.findAll(restId, dto); } /*** Admin ***/ @@ -50,7 +50,7 @@ export class CouponController { @ApiOperation({ summary: 'Create a new coupon' }) @ApiCreatedResponse({ description: 'The coupon has been successfully created.', type: CreateCouponDto }) @ApiBody({ type: CreateCouponDto }) - create(@Body() createCouponDto: CreateCouponDto, @RestId() restId: string) { + create(@Body() createCouponDto: CreateCouponDto, @ShopId() restId: string) { return this.couponService.create(restId, createCouponDto); } @@ -66,7 +66,7 @@ export class CouponController { @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) @ApiQuery({ name: 'type', required: false, enum: ['PERCENTAGE', 'FIXED'] }) @ApiQuery({ name: 'isActive', required: false, type: Boolean }) - findAll(@Query() dto: FindCouponsDto, @RestId() restId: string) { + findAll(@Query() dto: FindCouponsDto, @ShopId() restId: string) { return this.couponService.findAll(restId, dto); } @@ -106,7 +106,7 @@ export class CouponController { @Permissions(Permission.MANAGE_COUPONS) @Get('admin/coupons/generate-code') @ApiOperation({ summary: 'generate a coupon code' }) - generateCouponCode(@RestId() restId: string) { + generateCouponCode(@ShopId() restId: string) { return this.couponService.generateCouponCode(restId); } } diff --git a/src/modules/coupons/entities/coupon.entity.ts b/src/modules/coupons/entities/coupon.entity.ts index a5439bc..05e1027 100644 --- a/src/modules/coupons/entities/coupon.entity.ts +++ b/src/modules/coupons/entities/coupon.entity.ts @@ -1,6 +1,6 @@ import { Entity, Index, ManyToOne, Property, Enum, Unique } from '@mikro-orm/core'; import { BaseEntity } from '../../../../common/entities/base.entity'; -import { Shop } from ../../../shops/entities/shop.entity'; +import { Shop } from ../../../ shops / entities / shop.entity'; import { normalizePhone } from '../../../utils/phone.util'; import { CouponType } from '../interface/coupon'; @@ -25,13 +25,13 @@ export class Coupon extends BaseEntity { @Enum(() => CouponType) type!: CouponType; - @Property({ type: 'decimal', precision: 10, scale: 2 }) + @Property({ type: 'decimal', precision: 10, scale: 0 }) value!: number; // Discount amount or percentage - @Property({ type: 'decimal', precision: 10, scale: 2, nullable: true }) + @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true }) maxDiscount?: number; // Maximum discount for percentage coupons - @Property({ type: 'decimal', precision: 10, scale: 2, nullable: true }) + @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true }) minOrderAmount?: number; // Minimum order amount to use coupon @Property({ type: 'int', nullable: true }) diff --git a/src/modules/delivery/controllers/delivery.controller.ts b/src/modules/delivery/controllers/delivery.controller.ts index 6235983..400c69c 100644 --- a/src/modules/delivery/controllers/delivery.controller.ts +++ b/src/modules/delivery/controllers/delivery.controller.ts @@ -13,7 +13,7 @@ import { import { DeliveryService } from '../providers/delivery.service'; import { CreateDeliveryDto } from '../dto/create-delivery.dto'; import { UpdateDeliveryDto } from '../dto/update-delivery.dto'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { Delivery } from '../entities/delivery.entity'; @@ -24,14 +24,14 @@ import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('Delivery') @Controller() export class DeliveryController { - constructor(private readonly deliveryService: DeliveryService) {} + constructor(private readonly deliveryService: DeliveryService) { } @UseGuards(AuthGuard) @ApiBearerAuth() @Get('public/delivery-methods/shop') @ApiOperation({ summary: 'Get shop delivery methods' }) @ApiHeader(API_HEADER_SLUG) - findAllDeliveryMethods(@RestId() restId: string) { + findAllDeliveryMethods(@ShopId() restId: string) { return this.deliveryService.findAllForRestaurantId(restId); } @@ -42,7 +42,7 @@ export class DeliveryController { @Post('admin/delivery-methods/shop') @ApiOperation({ summary: 'Create a delivery method for a shop' }) @ApiBody({ type: CreateDeliveryDto }) - create(@Body() createDto: CreateDeliveryDto, @RestId() restId: string) { + create(@Body() createDto: CreateDeliveryDto, @ShopId() restId: string) { return this.deliveryService.create(restId, createDto); } @@ -51,7 +51,7 @@ export class DeliveryController { @Permissions(Permission.MANAGE_DELIVERY) @Get('admin/delivery-methods/shop') @ApiOperation({ summary: 'Get the shop delivery methods' }) - findRestaurantDeliveryMethods(@RestId() restId: string) { + findRestaurantDeliveryMethods(@ShopId() restId: string) { return this.deliveryService.findAllForRestaurantId(restId); } @@ -61,7 +61,7 @@ export class DeliveryController { @Get('admin/delivery-methods/shop/:deliveryId') @ApiOperation({ summary: 'Get a shop delivery method by delivery ID' }) @ApiParam({ name: 'deliveryId', description: 'Delivery method ID' }) - findOne(@Param('deliveryId') deliveryId: string, @RestId() restId: string) { + findOne(@Param('deliveryId') deliveryId: string, @ShopId() restId: string) { return this.deliveryService.findOne(restId, deliveryId); } @@ -72,7 +72,7 @@ export class DeliveryController { @ApiOperation({ summary: 'Update a shop delivery method' }) @ApiParam({ name: 'deliveryId', description: 'Delivery method ID' }) @ApiBody({ type: UpdateDeliveryDto }) - update(@Param('deliveryId') deliveryId: string, @Body() updateDto: UpdateDeliveryDto, @RestId() restId: string) { + update(@Param('deliveryId') deliveryId: string, @Body() updateDto: UpdateDeliveryDto, @ShopId() restId: string) { return this.deliveryService.update(restId, deliveryId, updateDto); } @@ -82,7 +82,7 @@ export class DeliveryController { @Delete('admin/delivery-methods/shop/:deliveryId') @ApiOperation({ summary: 'Delete a shop delivery method' }) @ApiParam({ name: 'deliveryId', description: 'Delivery method ID' }) - remove(@Param('deliveryId') deliveryId: string, @RestId() restId: string) { + remove(@Param('deliveryId') deliveryId: string, @ShopId() restId: string) { return this.deliveryService.remove(restId, deliveryId); } } diff --git a/src/modules/foods/entities/food.entity.ts b/src/modules/foods/entities/food.entity.ts index 96c18e5..2e1b029 100644 --- a/src/modules/foods/entities/food.entity.ts +++ b/src/modules/foods/entities/food.entity.ts @@ -32,7 +32,7 @@ export class Product extends BaseEntity { @Property({ type: 'json', nullable: true }) content?: string[]; - @Property({ type: 'decimal', precision: 10, scale: 2, nullable: true }) + @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true }) price?: number; @Property({ type: 'int', nullable: true }) diff --git a/src/modules/notifications/controllers/notifications.controller.ts b/src/modules/notifications/controllers/notifications.controller.ts index f6754ba..6bb87f6 100644 --- a/src/modules/notifications/controllers/notifications.controller.ts +++ b/src/modules/notifications/controllers/notifications.controller.ts @@ -38,7 +38,7 @@ export class NotificationsController { @ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' }) async getUserNotifications( @UserId() userId: string, - @RestId() restaurantId: string, + @ShopId() restaurantId: string, @Query('limit') limit?: number, @Query('cursor') cursor?: string, @Query('status') status?: 'seen' | 'unseen', @@ -57,7 +57,7 @@ export class NotificationsController { @Get('public/notifications/unseen-count') @ApiOperation({ summary: 'Get unseen notifications count for user' }) @ApiHeader(API_HEADER_SLUG) - async getUserUnseenCount(@UserId() userId: string, @RestId() restaurantId: string) { + async getUserUnseenCount(@UserId() userId: string, @ShopId() restaurantId: string) { const count = await this.notificationService.countUnseenByUserAndRestaurant(userId, restaurantId); return { count }; } @@ -67,7 +67,7 @@ export class NotificationsController { @Put('public/notifications/:id') @ApiOperation({ summary: 'Read a notification ' }) @ApiParam({ name: 'id', description: 'Notification ID' }) - async readNotificationUser(@RestId() restaurantId: string, @Param('id') id: string, @UserId() userId: string) { + async readNotificationUser(@ShopId() restaurantId: string, @Param('id') id: string, @UserId() userId: string) { await this.notificationService.readNotificationAsUser(id, userId, restaurantId); return { message: NotificationMessage.READ_SUCCESS }; } @@ -76,7 +76,7 @@ export class NotificationsController { @ApiBearerAuth() @Put('public/notifications/read/all') @ApiOperation({ summary: 'Read all notification ' }) - async readAllNotificationUser(@RestId() restaurantId: string, @UserId() userId: string) { + async readAllNotificationUser(@ShopId() restaurantId: string, @UserId() userId: string) { await this.notificationService.readAllNotifsAsUser(userId, restaurantId); return { message: NotificationMessage.READ_SUCCESS }; } @@ -96,7 +96,7 @@ export class NotificationsController { @ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' }) async getAdminNotifications( @AdminId() adminId: string, - @RestId() restaurantId: string, + @ShopId() restaurantId: string, @Query('limit') limit?: number, @Query('cursor') cursor?: string, @Query('status') status?: 'seen' | 'unseen', @@ -109,7 +109,7 @@ export class NotificationsController { @ApiBearerAuth() @Get('admin/notifications/unseen-count') @ApiOperation({ summary: 'Get unseen notifications count for admin' }) - async getAdminUnseenCount(@AdminId() adminId: string, @RestId() restaurantId: string) { + async getAdminUnseenCount(@AdminId() adminId: string, @ShopId() restaurantId: string) { const count = await this.notificationService.countUnseenByRestaurant(adminId, restaurantId); return { count }; } @@ -119,7 +119,7 @@ export class NotificationsController { @Put('admin/notifications/:id') @ApiOperation({ summary: 'Read a notification ' }) @ApiParam({ name: 'id', description: 'Notification ID' }) - async readNotificationAdmin(@RestId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) { + async readNotificationAdmin(@ShopId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) { await this.notificationService.readNotificationAdmin(id, adminId, restaurantId); return { message: NotificationMessage.READ_SUCCESS }; } @@ -128,7 +128,7 @@ export class NotificationsController { @ApiBearerAuth() @Put('admin/notifications/read/all') @ApiOperation({ summary: 'Read all notification ' }) - async readAllNotificationAdmin(@RestId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) { + async readAllNotificationAdmin(@ShopId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) { await this.notificationService.readAllNotifsAsAdmin(adminId, restaurantId); return { message: NotificationMessage.READ_SUCCESS }; } @@ -138,7 +138,7 @@ export class NotificationsController { @Permissions(Permission.MANAGE_SETTINGS) @Get('admin/notification-preferences') @ApiOperation({ summary: 'Get all notification preferences for a shop' }) - async getPreferences(@RestId() restaurantId: string) { + async getPreferences(@ShopId() restaurantId: string) { return this.preferenceService.findByRestaurant(restaurantId); } @@ -149,7 +149,7 @@ export class NotificationsController { @ApiOperation({ summary: 'Update notification channels' }) @ApiParam({ name: 'id', description: 'Notification preference ID' }) async updatePreference( - @RestId() restaurantId: string, + @ShopId() restaurantId: string, @Param('id') preferenceId: string, @Body() dto: UpdatePreferenceDto, ) { @@ -162,7 +162,7 @@ export class NotificationsController { @Post('admin/notification-preferences') @ApiOperation({ summary: 'Create notification preference' }) async createPreference( - @RestId() restaurantId: string, + @ShopId() restaurantId: string, @Body() dto: CreatePreferenceDto, ) { return this.preferenceService.create(restaurantId, dto); @@ -172,7 +172,7 @@ export class NotificationsController { @ApiBearerAuth() @Get('admin/notifications/sms-usage') @ApiOperation({ summary: 'Get SMS usage for my shop' }) - async getRestaurantSmsUsage(@RestId() restaurantId: string) { + async getRestaurantSmsUsage(@ShopId() restaurantId: string) { const smsCount = await this.notificationService.getSmsCountByRestaurantId(restaurantId); return { smsCount }; } diff --git a/src/modules/orders/controllers/orders.controller.ts b/src/modules/orders/controllers/orders.controller.ts index b6734dd..8eaa649 100644 --- a/src/modules/orders/controllers/orders.controller.ts +++ b/src/modules/orders/controllers/orders.controller.ts @@ -3,7 +3,7 @@ import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiHeader, ApiBody } fr import { OrdersService } from '../providers/orders.service'; import { AuthGuard } from '../../../auth/guards/auth.guard'; import { UserId } from '../../../../common/decorators/user-id.decorator'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { AdminAuthGuard } from '../../../auth/guards/adminAuth.guard'; import { FindOrdersDto } from '../dto/find-orders.dto'; import { OrderStatus } from '../interface/order.interface'; @@ -22,7 +22,7 @@ export class OrdersController { @Post('public/checkout') @ApiHeader(API_HEADER_SLUG) @ApiOperation({ summary: 'Checkout : create order and payment record' }) - checkout(@UserId() userId: string, @RestId() restaurantId: string) { + checkout(@UserId() userId: string, @ShopId() restaurantId: string) { return this.ordersService.checkout(userId, restaurantId); } @@ -30,7 +30,7 @@ export class OrdersController { @Get('public/orders') @ApiHeader(API_HEADER_SLUG) @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAll(@RestId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) { + findAll(@ShopId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) { return this.ordersService.findAllForUser(restId, dto, userId); } @@ -39,7 +39,7 @@ export class OrdersController { @ApiParam({ name: 'orderId', description: 'Order ID' }) @ApiHeader(API_HEADER_SLUG) @Get('public/orders/:orderId') - findOne(@Param('orderId') orderId: string, @RestId() restId: string) { + findOne(@Param('orderId') orderId: string, @ShopId() restId: string) { return this.ordersService.findOne(orderId, restId); } @@ -58,7 +58,7 @@ export class OrdersController { @Body() dto: UpdateOrderStatusDto, @Param('id') orderId: string, @Param('status') status: OrderStatus, - @RestId() restId: string, + @ShopId() restId: string, ) { return this.ordersService.changeOrderStatus(orderId, restId, status, 'user', dto?.desc); } @@ -68,7 +68,7 @@ export class OrdersController { @Permissions(Permission.MANAGE_ORDERS) @Get('admin/orders') @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAllAdmin(@RestId() restId: string, @Query() dto: FindOrdersDto) { + findAllAdmin(@ShopId() restId: string, @Query() dto: FindOrdersDto) { return this.ordersService.findAllForAdmin(restId, dto); } @@ -77,7 +77,7 @@ export class OrdersController { @ApiOperation({ summary: 'Get an order By id for User' }) @ApiParam({ name: 'orderId', description: 'Order ID' }) @Get('admin/orders/:orderId') - findOneAsAdmin(@Param('orderId') orderId: string, @RestId() restId: string) { + findOneAsAdmin(@Param('orderId') orderId: string, @ShopId() restId: string) { return this.ordersService.findOne(orderId, restId); } @@ -96,7 +96,7 @@ export class OrdersController { @Param('orderId') orderId: string, @Body() dto: UpdateOrderStatusDto, @Param('status') status: OrderStatus, - @RestId() restId: string, + @ShopId() restId: string, ) { return this.ordersService.changeOrderStatus(orderId, restId, status, 'admin', dto?.desc); } @@ -105,7 +105,7 @@ export class OrdersController { @Permissions(Permission.VIEW_REPORTS) @ApiOperation({ summary: 'Get Stats for report page' }) @Get('admin/orders/stats') - findStats(@RestId() restId: string) { + findStats(@ShopId() restId: string) { return this.ordersService.getStats(restId); } @@ -114,7 +114,7 @@ export class OrdersController { @ApiOperation({ summary: 'Get product sales pie chart data for last month' }) @ApiHeader(API_HEADER_SLUG) @Get('admin/orders/product-sales-pie-chart') - getFoodSalesPieChart(@RestId() restId: string) { + getFoodSalesPieChart(@ShopId() restId: string) { return this.ordersService.getFoodSalesPieChart(restId); } } diff --git a/src/modules/pager/controllers/pager.controller.ts b/src/modules/pager/controllers/pager.controller.ts index 2f87fa1..f2a56b3 100644 --- a/src/modules/pager/controllers/pager.controller.ts +++ b/src/modules/pager/controllers/pager.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Post, Body, UseGuards, Res, Req, Patch, Param } from ' import { PagerService } from '../providers/pager.service'; import { CreatePagerDto } from '../dto/create-pager.dto'; import { OptionalAuthGuard } from '../../../auth/guards/optinalAuth.guard'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { UserId } from 'src/common/decorators/user-id.decorator'; import { RestSlug } from 'src/common/decorators/rest-slug.decorator'; import { ApiTags, ApiOperation, ApiBody, ApiHeader, ApiBearerAuth, ApiParam } from '@nestjs/swagger'; @@ -31,7 +31,7 @@ export class PagerController { @Req() request: FastifyRequest, @Res() reply: FastifyReply, @RestSlug() slug: string, - @RestId() restId?: string, + @ShopId() restId?: string, @UserId() userId?: string, ) { // Convert empty strings to undefined for cleaner validation @@ -79,7 +79,7 @@ export class PagerController { @ApiBearerAuth() @Get('admin/pager') @ApiOperation({ summary: 'Get all pager requests for the shop' }) - async findAllAdmin(@RestId() restId: string) { + async findAllAdmin(@ShopId() restId: string) { return this.pagerService.findAllByRestaurantId(restId); } @@ -92,7 +92,7 @@ export class PagerController { @ApiBody({ type: UpdatePagerStatusDto }) async updateStatus( @Param('pagerId') pagerId: string, - @RestId() restId: string, + @ShopId() restId: string, @AdminId() adminId: string, @Body() dto: UpdatePagerStatusDto, ) { diff --git a/src/modules/payments/controllers/payments.controller.ts b/src/modules/payments/controllers/payments.controller.ts index dcecda6..ed65d9d 100644 --- a/src/modules/payments/controllers/payments.controller.ts +++ b/src/modules/payments/controllers/payments.controller.ts @@ -35,7 +35,7 @@ export class PaymentsController { @ApiOperation({ summary: 'Get the shop payment methods' }) @ApiHeader(API_HEADER_SLUG) @ApiNotFoundResponse({ description: 'Shop payment methods not found' }) - getTheRestaurantPaymentMethods(@RestId() restId: string) { + getTheRestaurantPaymentMethods(@ShopId() restId: string) { return this.paymentMethodService.findByRestaurant(restId); } @@ -44,7 +44,7 @@ export class PaymentsController { @Get('public/payments') @ApiOperation({ summary: 'Get all the shop payments for user' }) @ApiHeader(API_HEADER_SLUG) - findAllByRestaurant(@UserId() userId: string, @RestId() restId: string) { + findAllByRestaurant(@UserId() userId: string, @ShopId() restId: string) { return this.paymentsService.findAllPaymentsByRestaurantId(restId, userId); } @@ -73,7 +73,7 @@ export class PaymentsController { @ApiBearerAuth() @Get('admin/payments/methods') @ApiOperation({ summary: 'Get shop all payment methods' }) - findByRestaurant(@RestId() restId: string) { + findByRestaurant(@ShopId() restId: string) { return this.paymentMethodService.findByRestaurant(restId); } @@ -83,7 +83,7 @@ export class PaymentsController { @Post('admin/payments/methods') @ApiOperation({ summary: 'Create a new shop payment method' }) @ApiBody({ type: CreatePaymentMethodDto }) - createRestaurantPaymentMethod(@RestId() restId: string, @Body() createPaymentMethodDto: CreatePaymentMethodDto) { + createRestaurantPaymentMethod(@ShopId() restId: string, @Body() createPaymentMethodDto: CreatePaymentMethodDto) { return this.paymentMethodService.create(restId, createPaymentMethodDto); } @@ -137,7 +137,7 @@ export class PaymentsController { @Get('admin/payments/chart') @ApiOperation({ summary: 'Get payment chart data with date and period filters' }) @ApiHeader(API_HEADER_SLUG) - getPaymentChart(@Query() query: PaymentChartDto, @RestId() restId: string) { + getPaymentChart(@Query() query: PaymentChartDto, @ShopId() restId: string) { return this.paymentsService.getChartData(query, restId); } } diff --git a/src/modules/products/controllers/category.controller.ts b/src/modules/products/controllers/category.controller.ts index b982fe5..797d91e 100644 --- a/src/modules/products/controllers/category.controller.ts +++ b/src/modules/products/controllers/category.controller.ts @@ -42,17 +42,17 @@ export class CategoryController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Post('admin/categories') - create(@Body() dto: CreateCategoryDto, @RestId() restId: string) { + create(@Body() dto: CreateCategoryDto, @ShopId() restId: string) { return this.categoryService.create(restId, dto); } @Permissions(Permission.MANAGE_CATEGORIES) @ApiOperation({ summary: 'my shop categories' }) @UseGuards(AdminAuthGuard) @ApiBearerAuth() - @Get('admin/categories') - findAll(@RestId() restId: string) { + @Get('admin/categories') + findAll(@ShopId() restId: string) { return this.categoryService.findAllByRestaurantId(restId); - } + } @Permissions(Permission.MANAGE_CATEGORIES) @UseGuards(AdminAuthGuard) @@ -60,7 +60,7 @@ export class CategoryController { @Get('admin/categories/:id') @ApiOperation({ summary: 'Get category' }) @ApiParam({ name: 'id', required: true, type: String }) - findOne(@Param('id') id: string, @RestId() restId: string) { + findOne(@Param('id') id: string, @ShopId() restId: string) { return this.categoryService.findOne(restId, id); } @@ -71,7 +71,7 @@ export class CategoryController { @ApiOperation({ summary: 'Update category' }) @ApiParam({ name: 'id' }) @ApiBody({ type: UpdateCategoryDto }) - update(@Param('id') id: string, @Body() dto: UpdateCategoryDto, @RestId() restId: string) { + update(@Param('id') id: string, @Body() dto: UpdateCategoryDto, @ShopId() restId: string) { return this.categoryService.update(restId, id, dto); } @@ -82,7 +82,7 @@ export class CategoryController { @ApiOperation({ summary: 'Delete category' }) @ApiParam({ name: 'id' }) @ApiOkResponse({ description: 'Deleted' }) - remove(@Param('id') id: string, @RestId() restId: string) { + remove(@Param('id') id: string, @ShopId() restId: string) { return this.categoryService.remove(restId, id); } diff --git a/src/modules/products/controllers/product.controller.ts b/src/modules/products/controllers/product.controller.ts index 27f2f7d..06c9730 100644 --- a/src/modules/products/controllers/product.controller.ts +++ b/src/modules/products/controllers/product.controller.ts @@ -1,8 +1,8 @@ import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } from '@nestjs/common'; -import { FoodService } from '../providers/product.service'; -import { CreateFoodDto } from '../dto/create-product.dto'; +import { ProductService } from '../providers/product.service'; +import { CreateProductDto } from '../dto/create-product.dto'; import { UpdateFoodDto } from '../dto/update-product.dto'; -import { FindFoodsDto } from ../../../products.dto'; +import { FindProductsDto } from '../dto/find-products.dto'; import { ApiTags, ApiOperation, @@ -13,7 +13,7 @@ import { ApiHeader, } from '@nestjs/swagger'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { RestId, UserId } from 'src/common/decorators'; +import { ShopId, UserId } from 'src/common/decorators'; import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; import { OptionalAuthGuard } from 'src/modules/auth/guards/optinalAuth.guard'; import { API_HEADER_SLUG } from 'src/common/constants'; @@ -23,35 +23,34 @@ import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('products') @Controller() export class FoodController { - constructor(private readonly foodsService: FoodService) { } + constructor(private readonly productService: ProductService) { } @Get('public/products/shop/:slug') @ApiOperation({ summary: 'Get all products by shop slug' }) @ApiHeader(API_HEADER_SLUG) @ApiParam({ name: 'slug', required: true, description: 'Shop Slug' }) findAllByRestaurant(@Param('slug') slug: string) { - return this.foodsService.findByWeekDateAndMealType(slug); + return this.productService.findByShop(slug); } - @Get('public/products/:foodId') + @Get('public/products/:id') @UseGuards(OptionalAuthGuard) @ApiHeader(API_HEADER_SLUG) @ApiBearerAuth() @ApiOperation({ summary: 'Get a product by id' }) - @ApiParam({ name: 'foodId', required: true }) - findPublicFoodById(@Param('foodId') foodId: string, @UserId() userId?: string): Promise { - const a = this.foodsService.findPublicById(foodId, userId); + findPublicFoodById(@Param('id') productId: string, @UserId() userId?: string): Promise { + const a = this.productService.findPublicById(productId, userId); return a; } - @Post('public/products/favorite/:foodId') + @Post('public/products/:id/favorite') @UseGuards(AuthGuard) @ApiHeader(API_HEADER_SLUG) @ApiBearerAuth() @ApiOperation({ summary: 'toggle a product as favorite' }) - @ApiParam({ name: 'foodId', required: true }) - setAsFavorute(@Param('foodId') foodId: string, @UserId() userId: string) { - return this.foodsService.toggleFavorite(userId, foodId); + @ApiParam({ name: 'productId', required: true }) + setAsFavorute(@Param('productId') productId: string, @UserId() userId: string) { + return this.productService.toggleFavorite(userId, productId); } @Get('public/products/favorite') @@ -59,8 +58,8 @@ export class FoodController { @ApiHeader(API_HEADER_SLUG) @ApiBearerAuth() @ApiOperation({ summary: 'get my favorites' }) - getMyFavorites(@UserId() userId: string, @RestId() restId: string) { - return this.foodsService.getMyFavorites(userId, restId); + getMyFavorites(@UserId() userId: string, @ShopId() restId: string) { + return this.productService.getMyFavorites(userId, restId); } /* ---------------------------------- Admin ---------------------------------- */ @@ -69,9 +68,8 @@ export class FoodController { @Permissions(Permission.MANAGE_FOODS) @Post('admin/products') @ApiOperation({ summary: 'Create a new product' }) - @ApiBody({ type: CreateFoodDto }) - create(@Body() createFoodDto: CreateFoodDto, @RestId() restId: string) { - return this.foodsService.create(restId, createFoodDto); + create(@Body() createFoodDto: CreateProductDto, @ShopId() restId: string) { + return this.productService.create(restId, createFoodDto); } @UseGuards(AdminAuthGuard) @@ -86,8 +84,8 @@ export class FoodController { @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) @ApiQuery({ name: 'categoryId', required: false, type: String }) @ApiQuery({ name: 'isActive', required: false, type: Boolean }) - async findAll(@Query() dto: FindFoodsDto, @RestId() restId: string) { - const result = await this.foodsService.findAll(restId, dto); + async findAll(@Query() dto: FindProductsDto, @ShopId() restId: string) { + const result = await this.productService.findAll(restId, dto); return result; } @@ -97,8 +95,8 @@ export class FoodController { @Get('admin/products/:id') @ApiOperation({ summary: 'Get a product by id' }) @ApiParam({ name: 'id', required: true }) - findById(@Param('id') id: string, @RestId() restId: string) { - return this.foodsService.findAdminById(restId, id); + findById(@Param('id') id: string, @ShopId() restId: string) { + return this.productService.findAdminById(restId, id); } @UseGuards(AdminAuthGuard) @@ -108,16 +106,15 @@ export class FoodController { @ApiOperation({ summary: 'Update a product' }) @ApiParam({ name: 'id', required: true }) @ApiBody({ type: UpdateFoodDto }) - update(@Param('id') id: string, @Body() updateFoodDto: UpdateFoodDto, @RestId() restId: string) { - return this.foodsService.update(restId, id, updateFoodDto); + update(@Param('id') id: string, @Body() updateFoodDto: UpdateFoodDto, @ShopId() restId: string) { + return this.productService.update(restId, id, updateFoodDto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Permissions(Permission.MANAGE_FOODS) @Delete('admin/products/:id') @ApiOperation({ summary: 'Delete (soft) a product' }) - @ApiParam({ name: 'id', required: true }) - remove(@Param('id') id: string, @RestId() restId: string) { - return this.foodsService.remove(restId, id); + remove(@Param('id') id: string, @ShopId() restId: string) { + return this.productService.remove(restId, id); } } diff --git a/src/modules/products/dto/create-product.dto.ts b/src/modules/products/dto/create-product.dto.ts index cd814db..5d279a7 100644 --- a/src/modules/products/dto/create-product.dto.ts +++ b/src/modules/products/dto/create-product.dto.ts @@ -14,7 +14,25 @@ import { } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -export class CreateFoodDto { +export class CreateVariantDto { + @IsString() + @ApiProperty() + id: string + + @IsString() + @ApiProperty() + value: string + + @IsNumber() + @ApiProperty() + price: number + + @IsNumber() + @ApiProperty() + stock: number +} + +export class CreateProductDto { @IsNotEmpty() @IsString() @ApiProperty() @@ -25,16 +43,27 @@ export class CreateFoodDto { @ApiPropertyOptional({ example: 'قرمه سبزی' }) title?: string; + @IsOptional() + @IsString() + @ApiPropertyOptional({ example: 'مثلا رنگ ،سایز' }) + attribute?: string; + + @IsOptional() + @ApiPropertyOptional({ + type: CreateVariantDto, + example: [{ + value: 'قرمز', + price: 100000, + stock: 20 + }] + }) + variants: CreateVariantDto[] + @IsOptional() @IsString() @ApiPropertyOptional({ example: 'توضیحات غذا' }) desc?: string; - @IsOptional() - @IsArray() - @IsString({ each: true }) - @ApiPropertyOptional({ type: [String] }) - content?: string[]; @IsOptional() @IsNumber() @@ -67,7 +96,7 @@ export class CreateFoodDto { @Min(0) @Type(() => Number) @ApiProperty({ example: 50 }) - dailyStock: number; + stock: number; @IsOptional() @IsBoolean() diff --git a/src/modules/products/dto/find-products.dto.ts b/src/modules/products/dto/find-products.dto.ts index 19cb94a..204b782 100644 --- a/src/modules/products/dto/find-products.dto.ts +++ b/src/modules/products/dto/find-products.dto.ts @@ -2,7 +2,7 @@ import { IsOptional, IsNumber, IsString, IsIn, IsBoolean } from 'class-validator import { ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -export class FindFoodsDto { +export class FindProductsDto { @IsOptional() @IsNumber() @Type(() => Number) diff --git a/src/modules/products/dto/update-product.dto.ts b/src/modules/products/dto/update-product.dto.ts index 82a8617..08475fd 100644 --- a/src/modules/products/dto/update-product.dto.ts +++ b/src/modules/products/dto/update-product.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/swagger'; -import { CreateFoodDto } from './create-product.dto'; +import { CreateProductDto } from './create-product.dto'; -export class UpdateFoodDto extends PartialType(CreateFoodDto) {} +export class UpdateFoodDto extends PartialType(CreateProductDto) { } diff --git a/src/modules/products/entities/category.entity.ts b/src/modules/products/entities/category.entity.ts index ff57a63..692fed1 100644 --- a/src/modules/products/entities/category.entity.ts +++ b/src/modules/products/entities/category.entity.ts @@ -1,7 +1,7 @@ import { Entity, Index, Property, Collection, OneToMany, ManyToOne } from '@mikro-orm/core'; import { Product } from './product.entity'; import { BaseEntity } from '../../../../common/entities/base.entity'; -import { Shop } from ../../../shops/entities/shop.entity'; +import { Shop } from '../../../shops/entities/shop.entity'; @Entity({ tableName: 'categories' }) @Index({ properties: ['shop', 'isActive'] }) @@ -24,4 +24,4 @@ export class Category extends BaseEntity { @Property({ type: 'int', nullable: true }) order?: number; -} +} \ No newline at end of file diff --git a/src/modules/products/entities/product.entity.ts b/src/modules/products/entities/product.entity.ts index 8e9c6d0..2c9235a 100644 --- a/src/modules/products/entities/product.entity.ts +++ b/src/modules/products/entities/product.entity.ts @@ -1,9 +1,10 @@ import { Cascade, Collection, Entity, Index, ManyToOne, OneToMany, Property, OneToOne } from '@mikro-orm/core'; import { Category } from './category.entity'; -import { BaseEntity } from '../../../../common/entities/base.entity'; -import { Shop } from '../../../../modules/shops/entities/shop.entity'; +import { BaseEntity } from '../../../common/entities/base.entity'; +import { Shop } from '../../../modules/shops/entities/shop.entity'; import { Review } from 'src/modules/review/entities/review.entity'; import { Favorite } from './favorite.entity'; +import { Variant } from './variant.entity'; @Entity({ tableName: 'products' }) @Index({ properties: ['shop', 'isActive'] }) @@ -19,6 +20,11 @@ export class Product extends BaseEntity { @OneToMany(() => Review, review => review.product, { cascade: [Cascade.ALL], orphanRemoval: true }) reviews = new Collection(this); + @OneToMany(() => Variant, (variant) => variant.product, { cascade: [Cascade.ALL], orphanRemoval: true }) + variants = new Collection(this) + + @Property({ type: 'string', nullable: true }) + attribute?: string @OneToMany(() => Favorite, favorite => favorite.product) favorites = new Collection(this); @@ -29,12 +35,6 @@ export class Product extends BaseEntity { @Property({ type: 'text', nullable: true }) desc?: string; - @Property({ type: 'json', nullable: true }) - content?: string[]; - - @Property({ type: 'decimal', precision: 10, scale: 2, nullable: true }) - price?: number; - @Property({ type: 'int', nullable: true }) order?: number; diff --git a/src/modules/products/entities/variant.entity.ts b/src/modules/products/entities/variant.entity.ts new file mode 100644 index 0000000..31c129f --- /dev/null +++ b/src/modules/products/entities/variant.entity.ts @@ -0,0 +1,20 @@ +import { Entity, Index, Property, ManyToOne, ManyToMany, Collection } from '@mikro-orm/core'; +import { Product } from './product.entity'; +import { BaseEntity } from '../../../../common/entities/base.entity'; + +@Entity({ tableName: 'variants' }) +@Index({ properties: ['product', 'isActive'] }) +export class Variant extends BaseEntity { + @ManyToOne(() => Product) + product!: Product; + + @Property({ type: 'string' }) + value: string + + @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true }) + price?: number; + + @Property({ type: 'int', default: 0 }) + stock: number = 0; + +} diff --git a/src/modules/products/providers/product.service.ts b/src/modules/products/providers/product.service.ts index 92f9348..91c5ad0 100644 --- a/src/modules/products/providers/product.service.ts +++ b/src/modules/products/providers/product.service.ts @@ -1,32 +1,32 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { CreateFoodDto } from '../dto/create-product.dto'; -import { FindFoodsDto } from ../../..products.dto'; -import { FoodRepository } from '../repositories/product.repository'; +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { CreateProductDto } from '../dto/create-product.dto'; +import { FindProductsDto } from '../dto/find-products.dto'; +import { ProductRepository } from '../repositories/product.repository'; import { CategoryRepository } from '../repositories/category.repository'; import { EntityManager } from '@mikro-orm/postgresql'; import { RequiredEntityData, FilterQuery } from '@mikro-orm/core'; import { Product } from '../entities/product.entity'; -import { CategoryMessage, FoodMessage, RestMessage } from 'src/common/enums/message.enum'; -import { RestRepository } from ../../..shops/repositories/rest.repository'; -import { CacheService } from '../../../utils/cache.service'; +import { CategoryMessage, ProductMessage, RestMessage } from 'src/common/enums/message.enum'; import { Favorite } from '../entities/favorite.entity'; +import { Variant } from '../entities/variant.entity'; +import { ShopService } from 'src/modules/shops/providers/shops.service'; @Injectable() -export class FoodService { +export class ProductService { constructor( - private readonly foodRepository: FoodRepository, + private readonly productRepository: ProductRepository, private readonly categoryRepository: CategoryRepository, - private readonly restRepository: RestRepository, + private readonly shopService: ShopService, private readonly em: EntityManager, ) { } - async create(restId: string, createFoodDto: CreateFoodDto) { - const { categoryId, ...rest } = createFoodDto; - const shop = await this.restRepository.findOne({ id: restId }); + async create(shopId: string, dto: CreateProductDto) { + const { categoryId, variants, ...rest } = dto; + const shop = await this.shopService.findOrFail(shopId); if (!shop) { throw new NotFoundException(RestMessage.NOT_FOUND); } - const category = await this.categoryRepository.findOne({ id: categoryId, shop: { id: restId } }); + const category = await this.categoryRepository.findOne({ id: categoryId, shop: { id: shopId } }); if (!category) { throw new NotFoundException(CategoryMessage.NOT_FOUND); } @@ -41,7 +41,7 @@ export class FoodService { order: rest.order ?? null, // map single-title/content DTO to localized fields title: rest.title, - content: rest.content, + attribute: rest.attribute, // numeric/array fields price: rest.price ?? 0, images: rest.images ?? undefined, @@ -50,31 +50,41 @@ export class FoodService { }; const product = em.create(Product, data); + + em.persist(product) + + for (const variant of variants) { + const variantRecord = em.create(Variant, { + product, + value: variant.value, + stock: variant.stock, + price: variant.price, + }) + + em.persist(variantRecord) + } + await em.flush(); return product; }); - // Re-load created entities with the primary EM to ensure they're attached - const savedFood = product?.id - ? await this.foodRepository.findOne({ id: product.id }, { populate: ['category', 'shop'] }) - : null; - return savedFood ?? product; + + return product; } - findAll(restId: string, dto: FindFoodsDto) { - return this.foodRepository.findAllPaginated(restId, dto); + findAll(restId: string, dto: FindProductsDto) { + return this.productRepository.findAllPaginated(restId, dto); } - /** - * Public product detail (only active products are visible). - */ - async findPublicById(foodId: string, userId?: string): Promise { - const product = await this.foodRepository.findOne({ id: foodId, isActive: true }, { populate: ['category'] }); - if (!product) throw new NotFoundException(FoodMessage.NOT_FOUND); + + async findPublicById(productId: string, userId?: string): Promise { + + const product = await this.findOrFail(productId) + let isFavorite = false; if (userId) { - isFavorite = (await this.em.count(Favorite, { user: { id: userId }, product: { id: foodId } })) > 0; + isFavorite = (await this.em.count(Favorite, { user: { id: userId }, product: { id: productId } })) > 0; } return { ...product, @@ -82,44 +92,33 @@ export class FoodService { }; } - /** - * Admin product detail (scoped to the authenticated shop). - */ - async findAdminById(restId: string, id: string): Promise { - const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['category'] }); - if (!product) throw new NotFoundException(FoodMessage.NOT_FOUND); + async findAdminById(shopId: string, productId: string): Promise { + const product = await this.findOrFail(productId) + + if (product.shop.id !== shopId) throw new NotFoundException(ProductMessage.NOT_FOUND); return product; } - /** - * Find active products for a shop. - * @param slug - Shop slug identifier - * @returns Array of active products - */ - async findByWeekDateAndMealType(slug: string): Promise { - const shop = await this.restRepository.findOne({ slug }); - if (!shop) { - throw new NotFoundException(RestMessage.NOT_FOUND); - } + + async findByShop(slug: string): Promise { + const shop = await this.shopService.findOrFailBySlug( slug ); const queryFilter: FilterQuery = { shop: { slug }, isActive: true, } as unknown as FilterQuery; - return this.foodRepository.find(queryFilter, { + return this.productRepository.find(queryFilter, { populate: ['category'], orderBy: { order: 'asc' } }); } - async update(restId: string, id: string, dto: Partial): Promise { - const { categoryId, ...rest } = dto; - const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['shop'] }); - if (!product) { - throw new NotFoundException(FoodMessage.NOT_FOUND); - } + async update(restId: string, productId: string, dto: Partial): Promise { + const { categoryId, variants, ...rest } = dto; + + const product = await this.findOrFail(productId) // attach new categories if provided (adds, does not attempt to preserve/remove existing ones) if (categoryId) { @@ -132,44 +131,80 @@ export class FoodService { this.em.assign(product, { category: category }); } + if (variants) { + // Get existing variants for this product + const existingVariants = product.variants + + // Create a map of existing variants by id for easy lookup + const existingVariantsMap = new Map(existingVariants.map((v: any) => [v.id, v])); + + // Track which existing variants are being updated (to avoid deletion) + const updatedVariantIds = new Set(); + + for (const variant of variants) { + const { id, value, stock, price } = variant; + + if (id) { + // Update existing variant + const existingVariant = existingVariantsMap.get(id); + if (!existingVariant) { + throw new NotFoundException('Variant not found'); + } + + // Update the variant fields + this.em.assign(existingVariant, { value, stock, price }); + updatedVariantIds.add(id); + } else { + // Create new variant + const variantRecord = this.em.create(Variant, { + product, + value, + stock, + price, + }); + this.em.persist(variantRecord); + } + } + + // Delete variants that exist but were not included in the update + for (const existingVariant of existingVariants) { + if (!updatedVariantIds.has((existingVariant as any).id)) { + this.em.remove(existingVariant); + } + } + } // assign other fields from DTO (excluding dailyStock handled below) this.em.assign(product, rest); await this.em.persistAndFlush(product); - // Re-load the product to ensure populated relations and stable instance - const savedFood = await this.foodRepository.findOne({ id: product.id }, { populate: ['category', 'shop'] }); - // Invalidate cache for the shop if present (optional) - // await this.invalidateRestaurantFoodsCache(savedFood?.shop?.slug); - - return savedFood ?? product; + return product; } async remove(restId: string, id: string): Promise { - const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['shop'] }); - if (!product) { - throw new NotFoundException(FoodMessage.NOT_FOUND); + const product = await this.findOrFail(id) + + if (product.shop.id !== restId) { + throw new BadRequestException("Product doesnt belongs to you") } - // const restaurantSlug = product.shop.slug; product.deletedAt = new Date(); - await this.em.persistAndFlush(product); - // Invalidate cache for the shop - // await this.invalidateRestaurantFoodsCache(restaurantSlug); + await this.em.flush(); + } - async toggleFavorite(userId: string, foodId: string) { + async toggleFavorite(userId: string, productId: string) { - const favorite = await this.em.findOne(Favorite, { user: userId, product: foodId }); + const favorite = await this.em.findOne(Favorite, { user: userId, product: productId }); if (favorite) { return this.em.removeAndFlush(favorite); } const newFavorite = this.em.create(Favorite, { user: userId, - product: foodId, + product: productId, }); return this.em.persistAndFlush(newFavorite); } @@ -179,10 +214,14 @@ export class FoodService { return this.em.find(Favorite, { user: userId, product: { shop: { id: restId } } }, { populate: ['product'] }); } /** - * Invalidate cache for shop products + * Helper */ - // private async invalidateRestaurantFoodsCache(slug: string): Promise { - // const cacheKey = `${this.FOODS_BY_RESTAURANT_CACHE_KEY_PREFIX}${slug}`; - // await this.cacheService.del(cacheKey); - // } + async findOrFail(productId: string) { + const product = await this.productRepository.findOne({ id: productId, isActive: true }, + { populate: ['category', 'variants', 'shop'] }); + + if (!product) throw new NotFoundException(ProductMessage.NOT_FOUND); + + return product + } } diff --git a/src/modules/products/repositories/product.repository.ts b/src/modules/products/repositories/product.repository.ts index 2d98924..2ec5434 100644 --- a/src/modules/products/repositories/product.repository.ts +++ b/src/modules/products/repositories/product.repository.ts @@ -15,7 +15,7 @@ type FindFoodsOpts = { }; @Injectable() -export class FoodRepository extends EntityRepository { +export class ProductRepository extends EntityRepository { constructor(readonly em: EntityManager) { super(em, Product); } diff --git a/src/modules/review/controllers/review.controller.ts b/src/modules/review/controllers/review.controller.ts index 40c2e0f..95e0048 100644 --- a/src/modules/review/controllers/review.controller.ts +++ b/src/modules/review/controllers/review.controller.ts @@ -18,7 +18,7 @@ import { import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { UserId } from 'src/common/decorators/user-id.decorator'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { ReviewStatus } from '../enums/review-status.enum'; import { Permissions } from 'src/common/decorators/permissions.decorator'; import { Permission } from 'src/common/enums/permission.enum'; @@ -27,7 +27,7 @@ import { API_HEADER_SLUG } from 'src/common/constants'; @ApiTags('review') @Controller() export class ReviewController { - constructor(private readonly reviewService: ReviewService) {} + constructor(private readonly reviewService: ReviewService) { } @UseGuards(AuthGuard) @ApiBearerAuth() @@ -57,7 +57,7 @@ export class ReviewController { @Get('public/reviews/restuarant/:restuarantSlug') @ApiOperation({ summary: 'Get all reviews of restuarant by slug(public - only approved)' }) @ApiHeader(API_HEADER_SLUG) - @ApiParam({ name: 'restuarantSlug', required: true, type: String }) + @ApiParam({ name: 'restuarantSlug', required: true, type: String }) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'limit', required: false, type: Number }) @ApiQuery({ name: 'orderBy', required: false, type: String }) @@ -113,7 +113,7 @@ export class ReviewController { @ApiQuery({ name: 'status', required: false, enum: ReviewStatus }) @ApiQuery({ name: 'orderBy', required: false, type: String }) @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) - findAllAdmin(@Query() dto: FindReviewsDto, @RestId() restId: string) { + findAllAdmin(@Query() dto: FindReviewsDto, @ShopId() restId: string) { return this.reviewService.findAll({ ...dto, restId: restId }); } @@ -134,7 +134,7 @@ export class ReviewController { @Get('admin/reviews/:id') @ApiOperation({ summary: 'review detail' }) @ApiParam({ name: 'id', required: true }) - reviewDetail(@Param('id') reviewId: string, @RestId() restaurantId: string) { + reviewDetail(@Param('id') reviewId: string, @ShopId() restaurantId: string) { return this.reviewService.findById(reviewId, restaurantId); } @@ -148,7 +148,7 @@ export class ReviewController { changeStatus( @Param('id') reviewId: string, @Body() changeStatusDto: ChangeStatusDto, - @RestId() restaurantId: string, + @ShopId() restaurantId: string, ) { return this.reviewService.changeStatus(reviewId, changeStatusDto.status, restaurantId); } diff --git a/src/modules/roles/controllers/roles.controller.ts b/src/modules/roles/controllers/roles.controller.ts index 5618ca5..ec0cbb7 100644 --- a/src/modules/roles/controllers/roles.controller.ts +++ b/src/modules/roles/controllers/roles.controller.ts @@ -26,7 +26,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Get all through shop roles with pagination and filters' }) - findAll(@RestId() restId: string) { + findAll(@ShopId() restId: string) { return this.roleService.findAllGeneralAndRestaurantRoles(restId); } @@ -35,7 +35,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Get all permissions that the admin has' }) - async findAllPermissions(@AdminId() adminId: string, @RestId() restId: string) { + async findAllPermissions(@AdminId() adminId: string, @ShopId() restId: string) { const adminPermissionNames = await this.permissionService.getAdminFullPermissions(adminId, restId); return adminPermissionNames; @@ -49,7 +49,7 @@ export class RolesController { @ApiBearerAuth() @ApiOperation({ summary: 'Create a new role' }) @ApiBody({ type: CreateRoleDto }) - create(@Body() dto: CreateRoleDto, @RestId() restId: string) { + create(@Body() dto: CreateRoleDto, @ShopId() restId: string) { return this.roleService.createRestaurantRole(dto, restId); } @@ -58,7 +58,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Get a specific role by ID' }) - findOne(@Param('id') id: string, @RestId() restId: string) { + findOne(@Param('id') id: string, @ShopId() restId: string) { return this.roleService.findOne(restId, id); } @@ -68,7 +68,7 @@ export class RolesController { @ApiBearerAuth() @ApiOperation({ summary: 'Update a role' }) @ApiBody({ type: UpdateRoleDto }) - update(@Param('id') id: string, @Body() dto: UpdateRoleDto, @RestId() restId: string) { + update(@Param('id') id: string, @Body() dto: UpdateRoleDto, @ShopId() restId: string) { return this.roleService.update(restId, id, dto); } @@ -77,7 +77,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Delete a role' }) - remove(@Param('id') id: string, @RestId() restId: string) { + remove(@Param('id') id: string, @ShopId() restId: string) { return this.roleService.remove(restId, id); } diff --git a/src/modules/shops/controllers/schedule.controller.ts b/src/modules/shops/controllers/schedule.controller.ts index f1a82b5..f9d099a 100644 --- a/src/modules/shops/controllers/schedule.controller.ts +++ b/src/modules/shops/controllers/schedule.controller.ts @@ -17,7 +17,7 @@ import { UpdateScheduleDto } from '../dto/update-schedule.dto'; import { FindSchedulesDto } from '../dto/find-schedules.dto'; import { Schedule } from '../entities/schedule.entity'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { RestId } from 'src/common/decorators/rest-id.decorator'; +import { ShopId } from 'src/common/decorators/rest-id.decorator'; import { Permissions } from 'src/common/decorators/permissions.decorator'; import { Permission } from 'src/common/enums/permission.enum'; import { API_HEADER_SLUG } from 'src/common/constants'; @@ -45,7 +45,7 @@ export class ScheduleController { @ApiOperation({ summary: 'Create a new schedule' }) @ApiBody({ type: CreateScheduleDto }) @ApiCreatedResponse({ description: 'Schedule created successfully', type: Schedule }) - create(@Body() createScheduleDto: CreateScheduleDto, @RestId() restId: string) { + create(@Body() createScheduleDto: CreateScheduleDto, @ShopId() restId: string) { return this.scheduleService.create(restId, createScheduleDto); } @@ -63,7 +63,7 @@ export class ScheduleController { @ApiOkResponse({ description: 'List of schedules', type: [Schedule] }) findAll( @Query(new ValidationPipe({ transform: true, whitelist: true })) query: FindSchedulesDto, - @RestId() restId: string, + @ShopId() restId: string, ) { return this.scheduleService.findAll(restId, query); } @@ -76,7 +76,7 @@ export class ScheduleController { @ApiParam({ name: 'id', description: 'Schedule ID' }) @ApiOkResponse({ description: 'Schedule details', type: Schedule }) @ApiNotFoundResponse({ description: 'Schedule not found' }) - findOne(@Param('id') id: string, @RestId() restId: string) { + findOne(@Param('id') id: string, @ShopId() restId: string) { return this.scheduleService.findOne(id, restId); } @@ -89,7 +89,7 @@ export class ScheduleController { @ApiBody({ type: UpdateScheduleDto }) @ApiOkResponse({ description: 'Schedule updated successfully', type: Schedule }) @ApiNotFoundResponse({ description: 'Schedule not found' }) - update(@Param('id') id: string, @Body() updateScheduleDto: UpdateScheduleDto, @RestId() restId: string) { + update(@Param('id') id: string, @Body() updateScheduleDto: UpdateScheduleDto, @ShopId() restId: string) { return this.scheduleService.update(id, restId, updateScheduleDto); } @@ -101,7 +101,7 @@ export class ScheduleController { @ApiParam({ name: 'id', description: 'Schedule ID' }) @ApiOkResponse({ description: 'Schedule deleted successfully' }) @ApiNotFoundResponse({ description: 'Schedule not found' }) - remove(@Param('id') id: string, @RestId() restId: string) { + remove(@Param('id') id: string, @ShopId() restId: string) { return this.scheduleService.remove(id, restId); } } diff --git a/src/modules/shops/controllers/shops.controller.ts b/src/modules/shops/controllers/shops.controller.ts index f047a4e..9c0a377 100644 --- a/src/modules/shops/controllers/shops.controller.ts +++ b/src/modules/shops/controllers/shops.controller.ts @@ -1,9 +1,9 @@ import { Controller, Get, Post, Body, Patch, Param, UseGuards, Delete, Query } from '@nestjs/common'; -import { RestaurantsService } from ../../../shops.service'; +import { RestaurantsService } from ../../../ shops.service'; import { CreateRestaurantDto } from '../dto/create-shop.dto'; import { UpdateRestaurantDto } from '../dto/update-shop.dto'; import { UpgradeSubscriptionDto } from '../dto/upgrade-subscription.dto'; -import { FindRestaurantsDto } from ../../../shops.dto'; +import { FindRestaurantsDto } from ../../../ shops.dto'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { ApiBearerAuth, @@ -41,7 +41,7 @@ export class RestaurantsController { @Permissions(Permission.UPDATE_RESTAURANT) @ApiOperation({ summary: 'Get shop by ID from request' }) @Get('admin/shops/my-shop') - async findOne(@RestId() restId: string) { + async findOne(@ShopId() restId: string) { return await this.restaurantsService.findOne(restId); } @@ -51,7 +51,7 @@ export class RestaurantsController { @ApiOperation({ summary: 'Update a shop' }) @ApiBody({ type: UpdateRestaurantDto }) @Patch('admin/shops/my-shop') - updateMyRestaurant(@RestId() restId: string, @Body() updateRestaurantDto: UpdateRestaurantDto) { + updateMyRestaurant(@ShopId() restId: string, @Body() updateRestaurantDto: UpdateRestaurantDto) { return this.restaurantsService.update(restId, updateRestaurantDto); } diff --git a/src/modules/shops/providers/schedule.service.ts b/src/modules/shops/providers/schedule.service.ts index 4b465f0..3bf769b 100644 --- a/src/modules/shops/providers/schedule.service.ts +++ b/src/modules/shops/providers/schedule.service.ts @@ -6,7 +6,7 @@ import { ScheduleRepository } from '../repositories/schedule.repository'; import { CreateScheduleDto } from '../dto/create-schedule.dto'; import { UpdateScheduleDto } from '../dto/update-schedule.dto'; import { FindSchedulesDto } from '../dto/find-schedules.dto'; -import { RestRepository } from '../repositories/rest.repository'; +import { shopRepository } from '../repositories/rest.repository'; import { Shop } from '../entities/shop.entity'; import { RestMessage } from 'src/common/enums/message.enum'; // import { RestMessage } from 'src/common/enums/message.enum'; @@ -15,9 +15,9 @@ import { RestMessage } from 'src/common/enums/message.enum'; export class ScheduleService { constructor( private readonly scheduleRepository: ScheduleRepository, - private readonly restRepository: RestRepository, + private readonly restRepository: shopRepository, private readonly em: EntityManager, - ) {} + ) { } async create(restId: string, createScheduleDto: CreateScheduleDto): Promise { const rest = await this.restRepository.findOne({ id: restId }); diff --git a/src/modules/shops/providers/shops.service.ts b/src/modules/shops/providers/shops.service.ts index 847bc4d..6cd55d7 100644 --- a/src/modules/shops/providers/shops.service.ts +++ b/src/modules/shops/providers/shops.service.ts @@ -4,9 +4,9 @@ import { UpdateRestaurantDto } from '../dto/update-shop.dto'; import { UpgradeSubscriptionDto } from '../dto/upgrade-subscription.dto'; import { Shop } from '../entities/shop.entity'; import { EntityManager } from '@mikro-orm/postgresql'; -import { RestRepository } from '../repositories/rest.repository'; +import { shopRepository } from '../repositories/rest.repository'; import { RestMessage } from 'src/common/enums/message.enum'; -import { FindRestaurantsDto } from ../../..shops.dto'; +import { FindRestaurantsDto } from '../providers/shops.dto'; import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; import { PlanEnum } from '../interface/plan.interface'; import { Admin } from '../../../admin/entities/admin.entity'; @@ -18,8 +18,8 @@ import { NotificationPreference } from '../../../notifications/entities/notifica import { notificationPreferencesData } from '../../../../seeders/data/notification-preferences.data'; import { Order } from '../../../orders/entities/order.entity'; import { Coupon } from '../../../coupons/entities/coupon.entity'; -import { Category } from ../../..products/entities/category.entity'; -import { Product } from ../../..products/entities/product.entity'; +import { Category } from ../../..products / entities / category.entity'; +import { Product } from ../../..products / entities / product.entity'; import { Delivery } from '../../../delivery/entities/delivery.entity'; import { PaymentMethod } from '../../../payments/entities/payment-method.entity'; import { Pager } from '../../../pager/entities/pager.entity'; @@ -30,10 +30,10 @@ import { Notification } from '../../../notifications/entities/notification.entit @Injectable() -export class RestaurantsService { +export class ShopService { constructor( private readonly em: EntityManager, - private readonly restRepository: RestRepository, + private readonly shopRepository: shopRepository, ) { } async setupRestuarant(dto: CreateRestaurantDto): Promise { @@ -107,7 +107,7 @@ export class RestaurantsService { } async findAll(dto: FindRestaurantsDto): Promise> { - return this.restRepository.findAllPaginated({ + return this.shopRepository.findAllPaginated({ page: dto.page, limit: dto.limit, search: dto.search, @@ -129,7 +129,7 @@ export class RestaurantsService { } async findOne(id: string): Promise { - const shop = await this.restRepository.findOne({ id }); + const shop = await this.shopRepository.findOne({ id }); if (!shop) { throw new NotFoundException(RestMessage.NOT_FOUND); @@ -140,7 +140,7 @@ export class RestaurantsService { async findOneBySubscriptionId(subscriptionId: string): Promise { console.log('subscriptionId', subscriptionId) - const shop = await this.restRepository.findOne({ subscriptionId }); + const shop = await this.shopRepository.findOne({ subscriptionId }); console.log('shop', shop) if (!shop) { throw new NotFoundException(RestMessage.NOT_FOUND); @@ -155,7 +155,7 @@ export class RestaurantsService { // TODO : it must be done inside transaction async update(id: string, dto: UpdateRestaurantDto): Promise { - const shop = await this.restRepository.findOne({ id: id }); + const shop = await this.shopRepository.findOne({ id: id }); if (!shop) { throw new NotFoundException(RestMessage.NOT_FOUND); @@ -186,7 +186,7 @@ export class RestaurantsService { } - this.restRepository.assign(shop, dto); + this.shopRepository.assign(shop, dto); await this.em.persistAndFlush(shop); @@ -195,7 +195,7 @@ export class RestaurantsService { async remove(id: string) { // Soft delete the shop by setting isActive to false - const shop = await this.restRepository.findOne({ id }); + const shop = await this.shopRepository.findOne({ id }); if (!shop) { throw new NotFoundException(RestMessage.NOT_FOUND); @@ -208,7 +208,7 @@ export class RestaurantsService { } async upgradeSubscription(subscriptionId: string, dto: UpgradeSubscriptionDto): Promise { - const shop = await this.restRepository.findOne({ subscriptionId }); + const shop = await this.shopRepository.findOne({ subscriptionId }); if (!shop) { throw new NotFoundException(RestMessage.NOT_FOUND); @@ -278,4 +278,19 @@ export class RestaurantsService { }); } + async findOrFail(shopId: string) { + const shop = await this.shopRepository.findOne({ id: shopId }, { populate: ['deliveries'] }) + if (!shop) { + throw new BadRequestException("Shop not found") + } + return shop + } + + async findOrFailBySlug(shopSlug: string) { + const shop = await this.shopRepository.findOne({ slug: shopSlug }, { populate: ['deliveries'] }) + if (!shop) { + throw new BadRequestException("Shop not found") + } + return shop + } } diff --git a/src/modules/shops/repositories/rest.repository.ts b/src/modules/shops/repositories/rest.repository.ts index 7782ce9..4f7d11a 100644 --- a/src/modules/shops/repositories/rest.repository.ts +++ b/src/modules/shops/repositories/rest.repository.ts @@ -16,7 +16,7 @@ type FindRestaurantsOpts = { }; @Injectable() -export class RestRepository extends EntityRepository { +export class shopRepository extends EntityRepository { constructor(readonly em: EntityManager) { super(em, Shop); } diff --git a/src/modules/shops/shops.module.ts b/src/modules/shops/shops.module.ts index 347def3..c45ef6c 100644 --- a/src/modules/shops/shops.module.ts +++ b/src/modules/shops/shops.module.ts @@ -3,7 +3,7 @@ import { ShopsService } from './providers/shops.service'; import { ShopsController } from './controllers/shops.controller'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Shop } from './entities/shop.entity'; -import { RestRepository } from './repositories/rest.repository'; +import { shopRepository } from './repositories/rest.repository'; import { ScheduleRepository } from './repositories/schedule.repository'; import { Schedule } from './entities/schedule.entity'; import { ScheduleService } from './providers/schedule.service'; @@ -14,8 +14,8 @@ import { AuthModule } from '../auth/auth.module'; @Module({ controllers: [ShopsController, ScheduleController], - providers: [ShopsService, RestRepository, ScheduleRepository, ScheduleService, ShopCrone], + providers: [ShopsService, shopRepository, ScheduleRepository, ScheduleService, ShopCrone], imports: [MikroOrmModule.forFeature([Shop, Schedule]), JwtModule, forwardRef(() => AuthModule)], - exports: [RestRepository, ScheduleRepository, ScheduleService], + exports: [shopRepository, ScheduleRepository, ScheduleService], }) -export class ShopsModule {} +export class ShopsModule { } diff --git a/src/modules/users/controllers/users.controller.ts b/src/modules/users/controllers/users.controller.ts index a32a6de..b884dcc 100644 --- a/src/modules/users/controllers/users.controller.ts +++ b/src/modules/users/controllers/users.controller.ts @@ -22,7 +22,7 @@ export class UsersController { constructor( private readonly userService: UserService, private readonly walletService: WalletService, - ) {} + ) { } @UseGuards(AuthGuard) @ApiBearerAuth() @@ -39,7 +39,7 @@ export class UsersController { @ApiHeader(API_HEADER_SLUG) @ApiBody({ type: UpdateUserDto }) @Patch('public/user/update') - async updateUser(@UserId() userId: string, @RestId() restId: string, @Body() dto: UpdateUserDto) { + async updateUser(@UserId() userId: string, @ShopId() restId: string, @Body() dto: UpdateUserDto) { const user = await this.userService.updateUser(userId, restId, dto); return user; } @@ -60,7 +60,7 @@ export class UsersController { @ApiOperation({ summary: 'Get User Wallet' }) @ApiHeader(API_HEADER_SLUG) @Get('public/user/wallet/balance') - async getUserWalletBalance(@UserId() userId: string, @RestId() restId: string) { + async getUserWalletBalance(@UserId() userId: string, @ShopId() restId: string) { const wallet = await this.walletService.getUserCurrentWalletBalance(userId, restId); return wallet; } @@ -70,7 +70,7 @@ export class UsersController { @ApiOperation({ summary: 'Get User Wallet' }) @ApiHeader(API_HEADER_SLUG) @Get('public/user/points/balance') - async getUserWallet(@UserId() userId: string, @RestId() restId: string) { + async getUserWallet(@UserId() userId: string, @ShopId() restId: string) { const wallet = await this.walletService.getUserCurrentPoinrBalance(userId, restId); return wallet; } @@ -82,7 +82,7 @@ export class UsersController { @Get('public/user/wallet/transactions') async getUserWalletTransactions( @UserId() userId: string, - @RestId() restId: string, + @ShopId() restId: string, @Query(new ValidationPipe({ transform: true, whitelist: true })) query: FindWalletTransactionsDto, ) { @@ -159,7 +159,7 @@ export class UsersController { @ApiOperation({ summary: 'Convert user score (points) to wallet balance' }) @ApiHeader(API_HEADER_SLUG) @Post('public/user/convert-score-to-wallet') - async convertScoreToWallet(@UserId() userId: string, @RestId() restId: string) { + async convertScoreToWallet(@UserId() userId: string, @ShopId() restId: string) { await this.userService.convertScoreToWallet(userId, restId); return { message: UserSuccessMessage.SCORE_CONVERTED_SUCCESS, @@ -176,7 +176,7 @@ export class UsersController { async findAllRestaurantUsers( @Query(new ValidationPipe({ transform: true, whitelist: true })) query: FindUsersDto, - @RestId() restId: string, + @ShopId() restId: string, ) { return this.userService.findAll(restId, query); }