product module

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