remove inventory
This commit is contained in:
@@ -22,7 +22,6 @@ import { NotificationsModule } from './modules/notifications/notifications.modul
|
|||||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
import { PagerModule } from './modules/pager/pager.module';
|
import { PagerModule } from './modules/pager/pager.module';
|
||||||
import { ContactModule } from './modules/contact/contact.module';
|
import { ContactModule } from './modules/contact/contact.module';
|
||||||
import { InventoryModule } from './modules/inventory/inventory.module';
|
|
||||||
import { IconsModule } from './modules/icons/icons.module';
|
import { IconsModule } from './modules/icons/icons.module';
|
||||||
import { CacheModule } from '@nestjs/cache-manager';
|
import { CacheModule } from '@nestjs/cache-manager';
|
||||||
import { cacheConfig } from './config/cache.config';
|
import { cacheConfig } from './config/cache.config';
|
||||||
@@ -57,7 +56,6 @@ import { cacheConfig } from './config/cache.config';
|
|||||||
EventEmitterModule.forRoot(),
|
EventEmitterModule.forRoot(),
|
||||||
PagerModule,
|
PagerModule,
|
||||||
ContactModule,
|
ContactModule,
|
||||||
InventoryModule,
|
|
||||||
IconsModule,
|
IconsModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
|
|||||||
@@ -753,13 +753,6 @@ export const enum DeliveryMessage {
|
|||||||
DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد',
|
DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum InventoryMessage {
|
|
||||||
AVAILABLE_STOCK_EXCEEDS_TOTAL = 'موجودی موجود نمیتواند از موجودی کل بیشتر باشد',
|
|
||||||
PRODUCT_NOT_FOUND = 'محصول یافت نشد',
|
|
||||||
PRODUCT_NOT_BELONGS_TO_SHOP = 'محصول به این فروشگاه تعلق ندارد',
|
|
||||||
SHOP_NOT_FOUND = 'فروشگاه یافت نشد',
|
|
||||||
PRODUCTS_NOT_FOUND = 'محصولات یافت نشدند',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const enum PagerMessage {
|
export const enum PagerMessage {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Permission } from '../roles/entities/permission.entity';
|
|||||||
import { RolePermission } from '../roles/entities/rolePermission.entity';
|
import { RolePermission } from '../roles/entities/rolePermission.entity';
|
||||||
import { AdminController } from './controllers/admin.controller';
|
import { AdminController } from './controllers/admin.controller';
|
||||||
import { UtilsModule } from '../utils/utils.module';
|
import { UtilsModule } from '../utils/utils.module';
|
||||||
import { RestaurantsModule } from ../../..shops.module';
|
import { ShopsModule } from '../../../shops/shops.module';
|
||||||
import { AdminRole } from './entities/adminRole.entity';
|
import { AdminRole } from './entities/adminRole.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Entity, Index, ManyToOne, Unique } from '@mikro-orm/core';
|
import { Entity, Index, ManyToOne, Unique } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { Role } from '../../../roles/entities/role.entity';
|
import { Role } from '../../../roles/entities/role.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from '../../../shops/entities/shop.entity';
|
||||||
import { Admin } from './admin.entity';
|
import { Admin } from './admin.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'admin_roles' })
|
@Entity({ tableName: 'admin_roles' })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { InjectRepository } from '@mikro-orm/nestjs';
|
|||||||
import { EntityRepository } from '@mikro-orm/core';
|
import { EntityRepository } from '@mikro-orm/core';
|
||||||
import { Admin } from '../entities/admin.entity';
|
import { Admin } from '../entities/admin.entity';
|
||||||
import { Role } from '../../../roles/entities/role.entity';
|
import { Role } from '../../../roles/entities/role.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { CacheService } from '../../../utils/cache.service';
|
import { CacheService } from '../../../utils/cache.service';
|
||||||
import { CreateMyRestaurantAdminDto } from '../dto/create-my-shop-admin.dto';
|
import { CreateMyRestaurantAdminDto } from '../dto/create-my-shop-admin.dto';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
import { Admin } from '../entities/admin.entity';
|
import { Admin } from '../entities/admin.entity';
|
||||||
import { AdminRole } from '../entities/adminRole.entity';
|
import { AdminRole } from '../entities/adminRole.entity';
|
||||||
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../../shops/repositories/rest.repository';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { normalizePhone } from '../../../utils/phone.util';
|
import { normalizePhone } from '../../../utils/phone.util';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { JwtModule } from '@nestjs/jwt';
|
|||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { AdminModule } from '../admin/admin.module';
|
import { AdminModule } from '../admin/admin.module';
|
||||||
import { TokensService } from './services/tokens.service';
|
import { TokensService } from './services/tokens.service';
|
||||||
import { RestaurantsModule } from ../../..shops.module';
|
import { RestaurantsModule } from ../../../shops.module';
|
||||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||||
import { User } from '../users/entities/user.entity';
|
import { User } from '../users/entities/user.entity';
|
||||||
import { AdminAuthGuard } from './guards/adminAuth.guard';
|
import { AdminAuthGuard } from './guards/adminAuth.guard';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { SmsService } from '../../../notifications/services/sms.service';
|
|||||||
import { UserService } from '../../../users/providers/user.service';
|
import { UserService } from '../../../users/providers/user.service';
|
||||||
import { randomInt } from 'crypto';
|
import { randomInt } from 'crypto';
|
||||||
import { TokensService } from './tokens.service';
|
import { TokensService } from './tokens.service';
|
||||||
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../../shops/repositories/rest.repository';
|
||||||
import { AuthMessage, RestMessage } from 'src/common/enums/message.enum';
|
import { AuthMessage, RestMessage } from 'src/common/enums/message.enum';
|
||||||
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
||||||
import { AdminLoginTransformer } from '../transformers/admin-login.transformer';
|
import { AdminLoginTransformer } from '../transformers/admin-login.transformer';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import dayjs from 'dayjs';
|
|||||||
import { AuthMessage } from '../../../../common/enums/message.enum';
|
import { AuthMessage } from '../../../../common/enums/message.enum';
|
||||||
import { RefreshToken, RefreshTokenType } from '../entities/refresh-token.entity';
|
import { RefreshToken, RefreshTokenType } from '../entities/refresh-token.entity';
|
||||||
import { IAdminTokenPayload, ITokenPayload } from '../interfaces/IToken-payload';
|
import { IAdminTokenPayload, ITokenPayload } from '../interfaces/IToken-payload';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TokensService {
|
export class TokensService {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Admin } from '../../../admin/entities/admin.entity';
|
import type { Admin } from '../../../admin/entities/admin.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
export interface AdminLoginResponse {
|
export interface AdminLoginResponse {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { User } from '../../../users/entities/user.entity';
|
import type { User } from '../../../users/entities/user.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
export interface UserLoginResponse {
|
export interface UserLoginResponse {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
import { Product } from ../../../products/entities/product.entity';
|
||||||
import { Cart, CartItem } from '../interfaces/cart.interface';
|
import { Cart, CartItem } from '../interfaces/cart.interface';
|
||||||
import { CartValidationService } from './cart-validation.service';
|
import { CartValidationService } from './cart-validation.service';
|
||||||
import { CartCalculationService } from './cart-calculation.service';
|
import { CartCalculationService } from './cart-calculation.service';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
import { Product } from ../../../products/entities/product.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { UserAddress } from '../../../users/entities/user-address.entity';
|
import { UserAddress } from '../../../users/entities/user-address.entity';
|
||||||
import { User } from '../../../users/entities/user.entity';
|
import { User } from '../../../users/entities/user.entity';
|
||||||
import { PaymentMethod } from '../../../payments/entities/payment-method.entity';
|
import { PaymentMethod } from '../../../payments/entities/payment-method.entity';
|
||||||
@@ -23,10 +23,10 @@ export class CartValidationService {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate product exists, belongs to shop, has inventory, and check stock
|
* Validate product exists and belongs to shop
|
||||||
*/
|
*/
|
||||||
async validateAndGetFood(foodId: string, restaurantId: string, quantity: number): Promise<Product> {
|
async validateAndGetFood(foodId: string, restaurantId: string, quantity: number): Promise<Product> {
|
||||||
const product = await this.em.findOne(Product, { id: foodId }, { populate: ['shop', 'inventory'] });
|
const product = await this.em.findOne(Product, { id: foodId }, { populate: ['shop'] });
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new NotFoundException(CartMessage.FOOD_NOT_FOUND);
|
throw new NotFoundException(CartMessage.FOOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
@@ -35,23 +35,9 @@ export class CartValidationService {
|
|||||||
throw new BadRequestException(CartMessage.FOOD_NOT_BELONGS_TO_RESTAURANT);
|
throw new BadRequestException(CartMessage.FOOD_NOT_BELONGS_TO_RESTAURANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!product.inventory) {
|
|
||||||
throw new BadRequestException(CartMessage.FOOD_NO_INVENTORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.validateStock(product, quantity);
|
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate stock availability for product
|
|
||||||
*/
|
|
||||||
validateStock(product: Product, quantity: number): void {
|
|
||||||
const availableStock = product.inventory!.availableStock;
|
|
||||||
if (availableStock < quantity) {
|
|
||||||
throw new BadRequestException(CartMessage.INSUFFICIENT_STOCK + product.title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user or throw if not found
|
* Get user or throw if not found
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Entity, Enum, ManyToOne, Property } from '@mikro-orm/core';
|
import { Entity, Enum, ManyToOne, Property } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { ContactScope, ContactStatusEnum } from '../interface/interface';
|
import { ContactScope, ContactStatusEnum } from '../interface/interface';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'contacts' })
|
@Entity({ tableName: 'contacts' })
|
||||||
export class Contact extends BaseEntity {
|
export class Contact extends BaseEntity {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
|||||||
import { ContactStatusEnum } from '../interface/interface';
|
import { ContactStatusEnum } from '../interface/interface';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { ContactScope } from '../interface/interface';
|
import { ContactScope } from '../interface/interface';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ContactService {
|
export class ContactService {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { CouponController } from './controllers/coupon.controller';
|
|||||||
import { CouponRepository } from './repositories/coupon.repository';
|
import { CouponRepository } from './repositories/coupon.repository';
|
||||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||||
import { Coupon } from './entities/coupon.entity';
|
import { Coupon } from './entities/coupon.entity';
|
||||||
import { RestaurantsModule } from ../../..shops.module';
|
import { RestaurantsModule } from ../../../shops.module';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { CreateCouponDto } from '../dto/create-coupon.dto';
|
|||||||
import { UpdateCouponDto } from '../dto/update-coupon.dto';
|
import { UpdateCouponDto } from '../dto/update-coupon.dto';
|
||||||
import { FindCouponsDto } from '../dto/find-coupons.dto';
|
import { FindCouponsDto } from '../dto/find-coupons.dto';
|
||||||
import { CouponRepository } from '../repositories/coupon.repository';
|
import { CouponRepository } from '../repositories/coupon.repository';
|
||||||
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../../shops/repositories/rest.repository';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { Coupon } from '../entities/coupon.entity';
|
import { Coupon } from '../entities/coupon.entity';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { MikroOrmModule } from '@mikro-orm/nestjs';
|
|||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { DeliveryController } from './controllers/delivery.controller';
|
import { DeliveryController } from './controllers/delivery.controller';
|
||||||
import { DeliveryService } from './providers/delivery.service';
|
import { DeliveryService } from './providers/delivery.service';
|
||||||
import { RestaurantsModule } from ../../..shops.module';
|
import { RestaurantsModule } from ../../../shops.module';
|
||||||
import { Delivery } from './entities/delivery.entity';
|
import { Delivery } from './entities/delivery.entity';
|
||||||
import { DeliveryRepository } from './repositories/delivery.repository';
|
import { DeliveryRepository } from './repositories/delivery.repository';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Delivery } from '../entities/delivery.entity';
|
|||||||
import { DeliveryRepository } from '../repositories/delivery.repository';
|
import { DeliveryRepository } from '../repositories/delivery.repository';
|
||||||
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 { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../../shops/repositories/rest.repository';
|
||||||
import { DeliveryMethodEnum } from '../interface/delivery';
|
import { DeliveryMethodEnum } from '../interface/delivery';
|
||||||
import { DeliveryMessage } from 'src/common/enums/message.enum';
|
import { DeliveryMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ 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 { Inventory } from 'src/modules/inventory/entities/inventory.entity';
|
|
||||||
import { Favorite } from './favorite.entity';
|
import { Favorite } from './favorite.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'products' })
|
@Entity({ tableName: 'products' })
|
||||||
@@ -20,11 +19,6 @@ 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);
|
||||||
|
|
||||||
@OneToOne(() => Inventory, {
|
|
||||||
mappedBy: 'product',
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
inventory?: Inventory;
|
|
||||||
|
|
||||||
@OneToMany(() => Favorite, favorite => favorite.product)
|
@OneToMany(() => Favorite, favorite => favorite.product)
|
||||||
favorites = new Collection<Favorite>(this);
|
favorites = new Collection<Favorite>(this);
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsArray, ValidateNested, ArrayMinSize, IsString, IsNumber, Min } from 'class-validator';
|
|
||||||
import { Type } from 'class-transformer';
|
|
||||||
|
|
||||||
export class BulkReserveFoodItemDto {
|
|
||||||
@ApiProperty({ example: 'product-123', description: 'Product ID' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
|
||||||
foodId!: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 5, description: 'Quantity to reserve' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsNumber()
|
|
||||||
@Min(1)
|
|
||||||
@Type(() => Number)
|
|
||||||
quantity!: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BulkReserveFoodDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'Array of product reservations to create',
|
|
||||||
type: [BulkReserveFoodItemDto],
|
|
||||||
example: [
|
|
||||||
{ foodId: 'product-123', quantity: 5 },
|
|
||||||
{ foodId: 'product-789', quantity: 3 },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsArray()
|
|
||||||
@ArrayMinSize(1, { message: 'At least one item is required' })
|
|
||||||
@ValidateNested({ each: true })
|
|
||||||
@Type(() => BulkReserveFoodItemDto)
|
|
||||||
items!: BulkReserveFoodItemDto[];
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsArray, ValidateNested, ArrayMinSize } from 'class-validator';
|
|
||||||
import { Type } from 'class-transformer';
|
|
||||||
import { SetStockDto } from './set-stock.dto';
|
|
||||||
|
|
||||||
export class BulkSetStockItemDto extends SetStockDto {
|
|
||||||
@ApiProperty({ example: 'product-123', description: 'Product ID' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
foodId!: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BulkSetStockDto {
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'Array of stock items to set',
|
|
||||||
type: [BulkSetStockItemDto],
|
|
||||||
example: [
|
|
||||||
{ foodId: 'product-123', totalStock: 100, availableStock: 80 },
|
|
||||||
{ foodId: 'product-456', totalStock: 50, availableStock: 45 },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsArray()
|
|
||||||
@ArrayMinSize(1, { message: 'At least one item is required' })
|
|
||||||
@ValidateNested({ each: true })
|
|
||||||
@Type(() => BulkSetStockItemDto)
|
|
||||||
items!: BulkSetStockItemDto[];
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export class CreateInventoryDto {}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNumber, Min } from 'class-validator';
|
|
||||||
import { Type } from 'class-transformer';
|
|
||||||
|
|
||||||
export class SetStockDto {
|
|
||||||
@ApiProperty({ example: 100, description: 'Total stock quantity' })
|
|
||||||
@IsNumber()
|
|
||||||
@Min(0)
|
|
||||||
@Type(() => Number)
|
|
||||||
totalStock!: number;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 80, description: 'Available stock quantity' })
|
|
||||||
@IsNumber()
|
|
||||||
@Min(0)
|
|
||||||
@Type(() => Number)
|
|
||||||
availableStock!: number;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Cascade, Entity, Property, OneToOne } from '@mikro-orm/core';
|
|
||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
|
||||||
import { Product } from '../../../products/entities/product.entity';
|
|
||||||
|
|
||||||
@Entity({ tableName: 'inventory' })
|
|
||||||
export class Inventory extends BaseEntity {
|
|
||||||
@OneToOne(() => Product, {
|
|
||||||
cascade: [Cascade.ALL],
|
|
||||||
orphanRemoval: true,
|
|
||||||
})
|
|
||||||
product!: Product;
|
|
||||||
|
|
||||||
@Property({ type: 'int' })
|
|
||||||
totalStock!: number;
|
|
||||||
|
|
||||||
@Property({ type: 'int' })
|
|
||||||
availableStock!: number;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export enum ReservationStatus {
|
|
||||||
ACTIVE = 'active',
|
|
||||||
CONFIRMED = 'confirmed',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import { Controller, Body, Patch, Param, Post, UseGuards } from '@nestjs/common';
|
|
||||||
import { InventoryService } from './inventory.service';
|
|
||||||
import { SetStockDto } from './dto/set-stock.dto';
|
|
||||||
import { BulkSetStockDto } from './dto/bulk-set-stock.dto';
|
|
||||||
import { ApiTags, ApiOperation, ApiBody, ApiParam, ApiBearerAuth } from '@nestjs/swagger';
|
|
||||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
|
||||||
import { RestId } from 'src/common/decorators';
|
|
||||||
import { Inventory } from './entities/inventory.entity';
|
|
||||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
|
||||||
import { Permission } from 'src/common/enums/permission.enum';
|
|
||||||
|
|
||||||
@ApiTags('inventory')
|
|
||||||
@UseGuards(AdminAuthGuard)
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@Permissions(Permission.MANAGE_FOODS)
|
|
||||||
@Controller()
|
|
||||||
export class InventoryController {
|
|
||||||
constructor(private readonly inventoryService: InventoryService) { }
|
|
||||||
|
|
||||||
@Patch('admin/inventory/product/:foodId/stock')
|
|
||||||
@ApiOperation({ summary: 'Set available and total stock for a product item' })
|
|
||||||
@ApiParam({ name: 'foodId', description: 'Product ID' })
|
|
||||||
@ApiBody({ type: SetStockDto })
|
|
||||||
setStockForFood(
|
|
||||||
@Param('foodId') foodId: string,
|
|
||||||
@RestId() restaurantId: string,
|
|
||||||
@Body() setStockDto: SetStockDto,
|
|
||||||
): Promise<Inventory> {
|
|
||||||
return this.inventoryService.setStockForFood(foodId, restaurantId, setStockDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post('admin/inventory/products/stock/bulk')
|
|
||||||
@ApiOperation({ summary: 'Bulk set available and total stock for multiple product items' })
|
|
||||||
@ApiBody({ type: BulkSetStockDto })
|
|
||||||
bulkSetStockForFoods(@RestId() restaurantId: string, @Body() bulkSetStockDto: BulkSetStockDto): Promise<Inventory[]> {
|
|
||||||
return this.inventoryService.bulkSetStockForFoods(restaurantId, bulkSetStockDto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
|
||||||
import { InventoryService } from './inventory.service';
|
|
||||||
import { InventoryController } from './inventory.controller';
|
|
||||||
import { Inventory } from './entities/inventory.entity';
|
|
||||||
import { AuthModule } from '../auth/auth.module';
|
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [MikroOrmModule.forFeature([Inventory]), AuthModule, JwtModule],
|
|
||||||
controllers: [InventoryController],
|
|
||||||
providers: [InventoryService],
|
|
||||||
exports: [InventoryService],
|
|
||||||
})
|
|
||||||
export class InventoryModule {}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
|
||||||
import { SetStockDto } from './dto/set-stock.dto';
|
|
||||||
import { BulkSetStockDto } from './dto/bulk-set-stock.dto';
|
|
||||||
import { BulkReserveFoodDto } from './dto/bulk-reserve-product.dto';
|
|
||||||
import { Inventory } from './entities/inventory.entity';
|
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class InventoryService {
|
|
||||||
constructor(private readonly em: EntityManager) {}
|
|
||||||
|
|
||||||
async setStockForFood(foodId: string, restaurantId: string, setStockDto: SetStockDto): Promise<Inventory> {
|
|
||||||
// Validate that availableStock doesn't exceed totalStock
|
|
||||||
if (setStockDto.availableStock > setStockDto.totalStock) {
|
|
||||||
throw new BadRequestException('Available stock cannot exceed total stock');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find product and verify it belongs to the shop
|
|
||||||
const product = await this.em.findOne(Product, { id: foodId }, { populate: ['shop'] });
|
|
||||||
if (!product) {
|
|
||||||
throw new NotFoundException(`Product with ID ${foodId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (product.shop.id !== restaurantId) {
|
|
||||||
throw new BadRequestException(`Product does not belong to shop ${restaurantId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find or create inventory record
|
|
||||||
let inventory = await this.em.findOne(Inventory, {
|
|
||||||
product: { id: foodId, shop: { id: restaurantId } },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!inventory) {
|
|
||||||
// Create new inventory record
|
|
||||||
const shop = await this.em.findOne(Shop, { id: restaurantId });
|
|
||||||
if (!shop) {
|
|
||||||
throw new NotFoundException(`Shop with ID ${restaurantId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory = this.em.create(Inventory, {
|
|
||||||
product,
|
|
||||||
totalStock: setStockDto.totalStock,
|
|
||||||
availableStock: setStockDto.availableStock,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Update existing inventory record
|
|
||||||
inventory.totalStock = setStockDto.totalStock;
|
|
||||||
inventory.availableStock = setStockDto.availableStock;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.em.flush();
|
|
||||||
|
|
||||||
return inventory;
|
|
||||||
}
|
|
||||||
|
|
||||||
async bulkSetStockForFoods(restaurantId: string, bulkSetStockDto: BulkSetStockDto): Promise<Inventory[]> {
|
|
||||||
const { items } = bulkSetStockDto;
|
|
||||||
// Validate all items first
|
|
||||||
for (const item of items) {
|
|
||||||
if (item.availableStock > item.totalStock) {
|
|
||||||
throw new BadRequestException(`Available stock cannot exceed total stock for product ${item.foodId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all product IDs
|
|
||||||
const foodIds = items.map(item => item.foodId);
|
|
||||||
|
|
||||||
// Load all products in one query
|
|
||||||
const products = await this.em.find(Product, { id: { $in: foodIds } }, { populate: ['shop'] });
|
|
||||||
|
|
||||||
// Verify all products exist and belong to the shop
|
|
||||||
const foodMap = new Map<string, Product>();
|
|
||||||
for (const product of products) {
|
|
||||||
if (product.shop.id !== restaurantId) {
|
|
||||||
throw new BadRequestException(`Product ${product.id} does not belong to shop ${restaurantId}`);
|
|
||||||
}
|
|
||||||
foodMap.set(product.id, product);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for missing products
|
|
||||||
const missingFoodIds = foodIds.filter(id => !foodMap.has(id));
|
|
||||||
if (missingFoodIds.length > 0) {
|
|
||||||
throw new NotFoundException(`Products not found: ${missingFoodIds.join(', ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load all existing inventories in one query
|
|
||||||
const existingInventories = await this.em.find(Inventory, {
|
|
||||||
product: { id: { $in: foodIds }, shop: { id: restaurantId } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const inventoryMap = new Map<string, Inventory>();
|
|
||||||
for (const inventory of existingInventories) {
|
|
||||||
inventoryMap.set(inventory.product.id, inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get shop once
|
|
||||||
const shop = await this.em.findOne(Shop, { id: restaurantId });
|
|
||||||
if (!shop) {
|
|
||||||
throw new NotFoundException(`Shop with ID ${restaurantId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process all items
|
|
||||||
const results: Inventory[] = [];
|
|
||||||
for (const item of items) {
|
|
||||||
const product = foodMap.get(item.foodId)!;
|
|
||||||
let inventory = inventoryMap.get(item.foodId);
|
|
||||||
|
|
||||||
if (!inventory) {
|
|
||||||
// Create new inventory record
|
|
||||||
inventory = this.em.create(Inventory, {
|
|
||||||
product,
|
|
||||||
totalStock: item.totalStock,
|
|
||||||
availableStock: item.availableStock,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Update existing inventory record
|
|
||||||
inventory.totalStock = item.totalStock;
|
|
||||||
inventory.availableStock = item.availableStock;
|
|
||||||
}
|
|
||||||
|
|
||||||
results.push(inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush all changes at once
|
|
||||||
await this.em.flush();
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deductFromInventory(em: EntityManager, bulkReserveFoodDto: BulkReserveFoodDto): Promise<Inventory[]> {
|
|
||||||
return em.transactional(async em => {
|
|
||||||
const { items } = bulkReserveFoodDto;
|
|
||||||
// Get all unique product IDs
|
|
||||||
const foodIds = [...new Set(items.map(item => item.foodId))];
|
|
||||||
|
|
||||||
// Load all products in one query
|
|
||||||
const products = await em.find(Product, { id: { $in: foodIds } });
|
|
||||||
|
|
||||||
// Verify all products exist and belong to the shop
|
|
||||||
const foodMap = new Map<string, Product>();
|
|
||||||
for (const product of products) {
|
|
||||||
foodMap.set(product.id, product);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for missing products
|
|
||||||
const missingFoodIds = foodIds.filter(id => !foodMap.has(id));
|
|
||||||
if (missingFoodIds.length > 0) {
|
|
||||||
throw new NotFoundException(`Products not found: ${missingFoodIds.join(', ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load all existing inventories in one query
|
|
||||||
const existingInventories = await em.find(Inventory, {
|
|
||||||
product: { id: { $in: foodIds } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const inventoryMap = new Map<string, Inventory>();
|
|
||||||
for (const inventory of existingInventories) {
|
|
||||||
inventoryMap.set(inventory.product.id, inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate stock availability and create reservations
|
|
||||||
const inventories: Inventory[] = [];
|
|
||||||
for (const item of items) {
|
|
||||||
const inventory = inventoryMap.get(item.foodId);
|
|
||||||
|
|
||||||
// Check if inventory exists
|
|
||||||
if (!inventory) {
|
|
||||||
throw new NotFoundException(`Inventory not found for product ${item.foodId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if available stock is sufficient
|
|
||||||
if (inventory.availableStock < item.quantity) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
`Insufficient stock for product ${item.foodId}. Available: ${inventory.availableStock}, Requested: ${item.quantity}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
inventory.availableStock -= item.quantity;
|
|
||||||
inventories.push(inventory);
|
|
||||||
em.persist(inventory);
|
|
||||||
}
|
|
||||||
return inventories;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async restoreToInventory(em: EntityManager, bulkRestoreDto: BulkReserveFoodDto): Promise<Inventory[]> {
|
|
||||||
return em.transactional(async em => {
|
|
||||||
const { items } = bulkRestoreDto;
|
|
||||||
|
|
||||||
const foodIds = [...new Set(items.map(item => item.foodId))];
|
|
||||||
|
|
||||||
const products = await em.find(Product, { id: { $in: foodIds } });
|
|
||||||
const foodMap = new Map<string, Product>();
|
|
||||||
for (const product of products) {
|
|
||||||
foodMap.set(product.id, product);
|
|
||||||
}
|
|
||||||
|
|
||||||
const missingFoodIds = foodIds.filter(id => !foodMap.has(id));
|
|
||||||
if (missingFoodIds.length > 0) {
|
|
||||||
throw new NotFoundException(`Products not found: ${missingFoodIds.join(', ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingInventories = await em.find(Inventory, {
|
|
||||||
product: { id: { $in: foodIds } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const inventoryMap = new Map<string, Inventory>();
|
|
||||||
for (const inventory of existingInventories) {
|
|
||||||
inventoryMap.set(inventory.product.id, inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
const inventories: Inventory[] = [];
|
|
||||||
for (const item of items) {
|
|
||||||
const inventory = inventoryMap.get(item.foodId);
|
|
||||||
if (!inventory) {
|
|
||||||
throw new NotFoundException(`Inventory not found for product ${item.foodId}`);
|
|
||||||
}
|
|
||||||
inventory.availableStock += item.quantity;
|
|
||||||
inventories.push(inventory);
|
|
||||||
em.persist(inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
return inventories;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Entity, Property, ManyToOne, Unique } from '@mikro-orm/core';
|
import { Entity, Property, ManyToOne, 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 { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||||
import { NotifChannelEnum } from '../interfaces/notification.interface';
|
import { NotifChannelEnum } from '../interfaces/notification.interface';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Entity, Property, ManyToOne, Enum } from '@mikro-orm/core';
|
import { Entity, Property, ManyToOne, Enum } 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 { User } from '../../../users/entities/user.entity';
|
import { User } from '../../../users/entities/user.entity';
|
||||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||||
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Entity, Property, ManyToOne, PrimaryKey } from '@mikro-orm/core';
|
import { Entity, Property, ManyToOne, PrimaryKey } from '@mikro-orm/core';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'sms_logs' })
|
@Entity({ tableName: 'sms_logs' })
|
||||||
export class SmsLog {
|
export class SmsLog {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { OnEvent } from '@nestjs/event-emitter';
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
import { SmsSentEvent } from '../events/sms.events';
|
import { SmsSentEvent } from '../events/sms.events';
|
||||||
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../../shops/repositories/rest.repository';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { SmsLog } from '../entities/smsLogs.entity';
|
import { SmsLog } from '../entities/smsLogs.entity';
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { SmsProcessor } from './processors/sms.processor';
|
|||||||
import { PushProcessor } from './processors/push.processor';
|
import { PushProcessor } from './processors/push.processor';
|
||||||
import { NotificationsController } from './controllers/notifications.controller';
|
import { NotificationsController } from './controllers/notifications.controller';
|
||||||
import { User } from '../users/entities/user.entity';
|
import { User } from '../users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { NotificationQueueNameEnum } from './constants/queue';
|
import { NotificationQueueNameEnum } from './constants/queue';
|
||||||
@@ -26,7 +26,7 @@ import { NotificationCrone } from './crone/notification.crone';
|
|||||||
import { SmsService } from './services/sms.service';
|
import { SmsService } from './services/sms.service';
|
||||||
import { SmsLog } from './entities/smsLogs.entity';
|
import { SmsLog } from './entities/smsLogs.entity';
|
||||||
import { SmsLogRepository } from './repositories/sms-log.repository';
|
import { SmsLogRepository } from './repositories/sms-log.repository';
|
||||||
import { RestaurantsModule } from ../../..shops.module';
|
import { RestaurantsModule } from ../../../shops.module';
|
||||||
import { SmsListeners } from './listeners/sms.listeners';
|
import { SmsListeners } from './listeners/sms.listeners';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { NotificationPreference } from '../entities/notification-preference.entity';
|
import { NotificationPreference } from '../entities/notification-preference.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
||||||
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
||||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { EntityManager } from '@mikro-orm/postgresql';
|
|||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { Payment } from '../../../payments/entities/payment.entity';
|
import { Payment } from '../../../payments/entities/payment.entity';
|
||||||
import { PaymentMethodEnum, PaymentStatusEnum } from '../../../payments/interface/payment';
|
import { PaymentMethodEnum, PaymentStatusEnum } from '../../../payments/interface/payment';
|
||||||
import { InventoryService } from '../../../inventory/inventory.service';
|
|
||||||
import { OrderStatus } from '../interface/order.interface';
|
import { OrderStatus } from '../interface/order.interface';
|
||||||
import { Order } from '../entities/order.entity';
|
import { Order } from '../entities/order.entity';
|
||||||
import { OrderStatusChangedEvent } from '../events/order.events';
|
import { OrderStatusChangedEvent } from '../events/order.events';
|
||||||
@@ -15,7 +14,6 @@ export class OrdersCrone {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
private readonly inventoryService: InventoryService,
|
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@@ -63,23 +61,13 @@ export class OrdersCrone {
|
|||||||
|
|
||||||
if (payment.order) {
|
if (payment.order) {
|
||||||
payment.order.status = OrderStatus.CANCELED;
|
payment.order.status = OrderStatus.CANCELED;
|
||||||
|
|
||||||
// prepare restore payload
|
|
||||||
const items = (payment.order as any).items || [];
|
|
||||||
const restorePayload = {
|
|
||||||
items: items.map((it: any) => ({ foodId: it.product.id, quantity: it.quantity })),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (restorePayload.items.length > 0) {
|
|
||||||
await this.inventoryService.restoreToInventory(em, restorePayload);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
em.persist(payment);
|
em.persist(payment);
|
||||||
if (payment.order) em.persist(payment.order);
|
if (payment.order) em.persist(payment.order);
|
||||||
await em.flush();
|
await em.flush();
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Marked payment ${payment.id} and order ${payment.order?.id} as failed and restored inventory`,
|
`Marked payment ${payment.id} and order ${payment.order?.id} as failed`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Entity, Index, ManyToOne, Property } from '@mikro-orm/core';
|
import { Entity, Index, ManyToOne, Property } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { Order } from './order.entity';
|
import { Order } from './order.entity';
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
import { Product } from ../../../products/entities/product.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'order_items' })
|
@Entity({ tableName: 'order_items' })
|
||||||
@Index({ properties: ['order'] })
|
@Index({ properties: ['order'] })
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { OrderCouponDetail, OrderStatus } from '../interface/order.interface';
|
import { OrderCouponDetail, OrderStatus } from '../interface/order.interface';
|
||||||
import { User } from '../../../users/entities/user.entity';
|
import { User } from '../../../users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { PaymentMethod } from '../../../payments/entities/payment-method.entity';
|
import { PaymentMethod } from '../../../payments/entities/payment-method.entity';
|
||||||
import { OrderItem } from './order-item.entity';
|
import { OrderItem } from './order-item.entity';
|
||||||
import { Delivery } from '../../../delivery/entities/delivery.entity';
|
import { Delivery } from '../../../delivery/entities/delivery.entity';
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { OrdersController } from './controllers/orders.controller';
|
|||||||
import { Order } from './entities/order.entity';
|
import { Order } from './entities/order.entity';
|
||||||
import { OrderItem } from './entities/order-item.entity';
|
import { OrderItem } from './entities/order-item.entity';
|
||||||
import { User } from '../users/entities/user.entity';
|
import { User } from '../users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
import { Product } from ../../../products/entities/product.entity';
|
||||||
import { UserAddress } from '../users/entities/user-address.entity';
|
import { UserAddress } from '../users/entities/user-address.entity';
|
||||||
import { PaymentMethod } from '../payments/entities/payment-method.entity';
|
import { PaymentMethod } from '../payments/entities/payment-method.entity';
|
||||||
import { CartModule } from '../cart/cart.module';
|
import { CartModule } from '../cart/cart.module';
|
||||||
@@ -19,7 +19,6 @@ import { OrderListeners } from './listeners/order.listeners';
|
|||||||
import { OrdersCrone } from './crone/order.crone';
|
import { OrdersCrone } from './crone/order.crone';
|
||||||
import { AdminModule } from '../admin/admin.module';
|
import { AdminModule } from '../admin/admin.module';
|
||||||
import { NotificationsModule } from '../notifications/notifications.module';
|
import { NotificationsModule } from '../notifications/notifications.module';
|
||||||
import { InventoryModule } from '../inventory/inventory.module';
|
|
||||||
import { UserModule } from '../users/user.module';
|
import { UserModule } from '../users/user.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -32,7 +31,6 @@ import { UserModule } from '../users/user.module';
|
|||||||
JwtModule,
|
JwtModule,
|
||||||
AdminModule,
|
AdminModule,
|
||||||
NotificationsModule,
|
NotificationsModule,
|
||||||
InventoryModule,
|
|
||||||
forwardRef(() => UserModule)
|
forwardRef(() => UserModule)
|
||||||
],
|
],
|
||||||
controllers: [OrdersController],
|
controllers: [OrdersController],
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { EntityManager } from '@mikro-orm/postgresql';
|
|||||||
import { Order } from '../entities/order.entity';
|
import { Order } from '../entities/order.entity';
|
||||||
import { OrderItem } from '../entities/order-item.entity';
|
import { OrderItem } from '../entities/order-item.entity';
|
||||||
import { User } from '../../../users/entities/user.entity';
|
import { User } from '../../../users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from '../../../shops/entities/shop.entity';
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
import { Product } from '../../../products/entities/product.entity';
|
||||||
import { CartService } from '../../../cart/providers/cart.service';
|
import { CartService } from '../../../cart/providers/cart.service';
|
||||||
import { OrderStatus, OrderUserAddress, OrderCarAddress } from '../interface/order.interface';
|
import { OrderStatus, OrderUserAddress, OrderCarAddress } from '../interface/order.interface';
|
||||||
import { PaymentMethodEnum, PaymentStatusEnum } from '../../../payments/interface/payment';
|
import { PaymentMethodEnum, PaymentStatusEnum } from '../../../payments/interface/payment';
|
||||||
@@ -17,8 +17,6 @@ import { OrderRepository } from '../repositories/order.repository';
|
|||||||
import { FindOrdersDto } from '../dto/find-orders.dto';
|
import { FindOrdersDto } from '../dto/find-orders.dto';
|
||||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
import { Payment } from 'src/modules/payments/entities/payment.entity';
|
import { Payment } from 'src/modules/payments/entities/payment.entity';
|
||||||
import { InventoryService } from 'src/modules/inventory/inventory.service';
|
|
||||||
import { BulkReserveFoodDto } from 'src/modules/inventory/dto/bulk-reserve-product.dto';
|
|
||||||
import { StatusTransitionRef } from '../interface/order.interface';
|
import { StatusTransitionRef } from '../interface/order.interface';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
|
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
|
||||||
@@ -55,7 +53,6 @@ export class OrdersService {
|
|||||||
private readonly cartService: CartService,
|
private readonly cartService: CartService,
|
||||||
private readonly orderRepository: OrderRepository,
|
private readonly orderRepository: OrderRepository,
|
||||||
private readonly paymentsService: PaymentsService,
|
private readonly paymentsService: PaymentsService,
|
||||||
private readonly inventoryService: InventoryService,
|
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@@ -116,14 +113,6 @@ export class OrdersService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
em.persist(payment);
|
em.persist(payment);
|
||||||
// reserve stock based on payment method.
|
|
||||||
const bulkReserveFoodDto: BulkReserveFoodDto = {
|
|
||||||
items: validated.orderItemsData.map(item => ({
|
|
||||||
foodId: item.product.id,
|
|
||||||
quantity: item.quantity,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
await this.inventoryService.deductFromInventory(em, bulkReserveFoodDto);
|
|
||||||
await em.flush();
|
await em.flush();
|
||||||
this.logger.debug(`Order ${order.id} created for user ${userId} (shop ${restaurantId})`);
|
this.logger.debug(`Order ${order.id} created for user ${userId} (shop ${restaurantId})`);
|
||||||
return order;
|
return order;
|
||||||
@@ -426,15 +415,13 @@ export class OrdersService {
|
|||||||
const orderItemsData: OrderItemData[] = [];
|
const orderItemsData: OrderItemData[] = [];
|
||||||
|
|
||||||
for (const cartItem of cart.items) {
|
for (const cartItem of cart.items) {
|
||||||
const product = await this.em.findOne(Product, { id: cartItem.foodId }, { populate: ['shop', 'inventory'] });
|
const product = await this.em.findOne(Product, { id: cartItem.foodId }, { populate: ['shop'] });
|
||||||
if (!product) throw new NotFoundException(OrderMessage.FOOD_NOT_FOUND);
|
if (!product) throw new NotFoundException(OrderMessage.FOOD_NOT_FOUND);
|
||||||
|
|
||||||
if (product.shop.id !== restaurantId) {
|
if (product.shop.id !== restaurantId) {
|
||||||
throw new BadRequestException(OrderMessage.FOOD_NOT_BELONGS_TO_RESTAURANT);
|
throw new BadRequestException(OrderMessage.FOOD_NOT_BELONGS_TO_RESTAURANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assertFoodHasSufficientStock(product, cartItem.quantity);
|
|
||||||
|
|
||||||
orderItemsData.push({
|
orderItemsData.push({
|
||||||
product,
|
product,
|
||||||
quantity: cartItem.quantity,
|
quantity: cartItem.quantity,
|
||||||
@@ -446,12 +433,6 @@ export class OrdersService {
|
|||||||
return orderItemsData;
|
return orderItemsData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertFoodHasSufficientStock(product: Product, quantity: number) {
|
|
||||||
const availableStock = product.inventory?.availableStock ?? 0;
|
|
||||||
if (availableStock < quantity) {
|
|
||||||
throw new BadRequestException(OrderMessage.FOOD_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async getStats(restId: string) {
|
async getStats(restId: string) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Entity, ManyToOne, Property, Enum } from '@mikro-orm/core';
|
import { Entity, ManyToOne, Property, Enum } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { User } from '../../../users/entities/user.entity';
|
import { User } from '../../../users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { PagerStatus } from '../interface/pager';
|
import { PagerStatus } from '../interface/pager';
|
||||||
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { BadRequestException, Injectable, NotFoundException, Inject, forwardRef
|
|||||||
import { CreatePagerDto } from '../dto/create-pager.dto';
|
import { CreatePagerDto } from '../dto/create-pager.dto';
|
||||||
import { User } from '../../../users/entities/user.entity';
|
import { User } from '../../../users/entities/user.entity';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { Pager } from '../entities/pager.entity';
|
import { Pager } from '../entities/pager.entity';
|
||||||
import { PagerStatus } from '../interface/pager';
|
import { PagerStatus } from '../interface/pager';
|
||||||
import { ulid } from 'ulid';
|
import { ulid } from 'ulid';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Entity, Enum, Index, ManyToOne, Property, Unique } from '@mikro-orm/core';
|
import { Entity, Enum, Index, ManyToOne, Property, 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 { PaymentGatewayEnum, PaymentMethodEnum } from '../interface/payment';
|
import { PaymentGatewayEnum, PaymentMethodEnum } from '../interface/payment';
|
||||||
|
|
||||||
@Entity({ tableName: 'payment_methods' })
|
@Entity({ tableName: 'payment_methods' })
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export interface IPaymentVerifyData {
|
|||||||
cardPan?: string;
|
cardPan?: string;
|
||||||
raw: Record<string, any>;
|
raw: Record<string, any>;
|
||||||
}
|
}
|
||||||
////////////////////////// Zarinpal API v4 //////////////////////////
|
../../../../../../////// Zarinpal API v../../../../../../////////
|
||||||
export interface IZarinpalRequestPayment {
|
export interface IZarinpalRequestPayment {
|
||||||
amount: number;
|
amount: number;
|
||||||
merchant_id: string;
|
merchant_id: string;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { MikroOrmModule } from '@mikro-orm/nestjs';
|
|||||||
import { PaymentMethod } from './entities/payment-method.entity';
|
import { PaymentMethod } from './entities/payment-method.entity';
|
||||||
import { PaymentMethodRepository } from './repositories/payment-method.repository';
|
import { PaymentMethodRepository } from './repositories/payment-method.repository';
|
||||||
import { PaymentMethodService } from './services/payment-method.service';
|
import { PaymentMethodService } from './services/payment-method.service';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from '../../../shops/entities/shop.entity';
|
||||||
import { PaymentsController } from './controllers/payments.controller';
|
import { PaymentsController } from './controllers/payments.controller';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
@@ -12,7 +12,6 @@ import { Payment } from './entities/payment.entity';
|
|||||||
import { ZarinpalGateway } from './gateways/zarinpal.gateway';
|
import { ZarinpalGateway } from './gateways/zarinpal.gateway';
|
||||||
import { GatewayManager } from './gateways/gateway.manager';
|
import { GatewayManager } from './gateways/gateway.manager';
|
||||||
import { PaymentRepository } from './repositories/payment.repository';
|
import { PaymentRepository } from './repositories/payment.repository';
|
||||||
import { InventoryModule } from '../inventory/inventory.module';
|
|
||||||
import { PaymentListeners } from './listeners/payment.listeners';
|
import { PaymentListeners } from './listeners/payment.listeners';
|
||||||
import { AdminModule } from '../admin/admin.module';
|
import { AdminModule } from '../admin/admin.module';
|
||||||
import { NotificationsModule } from '../notifications/notifications.module';
|
import { NotificationsModule } from '../notifications/notifications.module';
|
||||||
@@ -20,7 +19,7 @@ import { OrdersModule } from '../orders/orders.module';
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [MikroOrmModule.forFeature([PaymentMethod, Payment, Shop]),
|
imports: [MikroOrmModule.forFeature([PaymentMethod, Payment, Shop]),
|
||||||
AuthModule, JwtModule, InventoryModule, AdminModule, NotificationsModule,
|
AuthModule, JwtModule, AdminModule, NotificationsModule,
|
||||||
forwardRef(() => OrdersModule)],
|
forwardRef(() => OrdersModule)],
|
||||||
controllers: [PaymentsController],
|
controllers: [PaymentsController],
|
||||||
providers: [PaymentsService, PaymentMethodService, PaymentMethodRepository,
|
providers: [PaymentsService, PaymentMethodService, PaymentMethodRepository,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { PaymentMethodRepository } from '../repositories/payment-method.reposito
|
|||||||
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
||||||
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { RestMessage, PaymentMessage } from 'src/common/enums/message.enum';
|
import { RestMessage, PaymentMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } f
|
|||||||
import { FoodService } from '../providers/product.service';
|
import { FoodService } from '../providers/product.service';
|
||||||
import { CreateFoodDto } from '../dto/create-product.dto';
|
import { CreateFoodDto } 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 { FindFoodsDto } from ../../../products.dto';
|
||||||
import {
|
import {
|
||||||
ApiTags,
|
ApiTags,
|
||||||
ApiOperation,
|
ApiOperation,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Cron } from '@nestjs/schedule';
|
import { Cron } from '@nestjs/schedule';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { Inventory } from '../../../inventory/entities/inventory.entity';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FoodStockCrone {
|
export class FoodStockCrone {
|
||||||
@@ -9,37 +8,37 @@ export class FoodStockCrone {
|
|||||||
|
|
||||||
constructor(private readonly em: EntityManager) {}
|
constructor(private readonly em: EntityManager) {}
|
||||||
|
|
||||||
// run every day at 00:03
|
// Inventory cron job disabled - inventory module removed
|
||||||
@Cron('3 0 * * *', {
|
// @Cron('3 0 * * *', {
|
||||||
name: 'resetAvailableStock',
|
// name: 'resetAvailableStock',
|
||||||
timeZone: 'UTC',
|
// timeZone: 'UTC',
|
||||||
})
|
// })
|
||||||
async handleCron() {
|
// async handleCron() {
|
||||||
try {
|
// try {
|
||||||
this.logger.debug('Starting daily inventory reset (availableStock = totalStock)');
|
// this.logger.debug('Starting daily inventory reset (availableStock = totalStock)');
|
||||||
|
|
||||||
const inventories = await this.em.find(Inventory, {});
|
// const inventories = await this.em.find(Inventory, {});
|
||||||
if (!inventories || inventories.length === 0) {
|
// if (!inventories || inventories.length === 0) {
|
||||||
this.logger.debug('No inventory records found to reset');
|
// this.logger.debug('No inventory records found to reset');
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.logger.log(`Resetting available stock for ${inventories.length} inventory records`);
|
// this.logger.log(`Resetting available stock for ${inventories.length} inventory records`);
|
||||||
|
|
||||||
await this.em.transactional(async em => {
|
// await this.em.transactional(async em => {
|
||||||
for (const inv of inventories) {
|
// for (const inv of inventories) {
|
||||||
// reload inside transaction to avoid concurrency issues
|
// // reload inside transaction to avoid concurrency issues
|
||||||
const record = await em.findOne(Inventory, { id: inv.id });
|
// const record = await em.findOne(Inventory, { id: inv.id });
|
||||||
if (!record) continue;
|
// if (!record) continue;
|
||||||
record.availableStock = record.totalStock;
|
// record.availableStock = record.totalStock;
|
||||||
em.persist(record);
|
// em.persist(record);
|
||||||
}
|
// }
|
||||||
await em.flush();
|
// await em.flush();
|
||||||
});
|
// });
|
||||||
|
|
||||||
this.logger.log('Daily inventory reset completed');
|
// this.logger.log('Daily inventory reset completed');
|
||||||
} catch (err) {
|
// } catch (err) {
|
||||||
this.logger.error(`FoodStockCrone failed: ${err?.message}`, err);
|
// this.logger.error(`FoodStockCrone failed: ${err?.message}`, err);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'] })
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ 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 { Inventory } from 'src/modules/inventory/entities/inventory.entity';
|
|
||||||
import { Favorite } from './favorite.entity';
|
import { Favorite } from './favorite.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'products' })
|
@Entity({ tableName: 'products' })
|
||||||
@@ -20,11 +19,6 @@ 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);
|
||||||
|
|
||||||
@OneToOne(() => Inventory, {
|
|
||||||
mappedBy: 'product',
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
inventory?: Inventory;
|
|
||||||
|
|
||||||
@OneToMany(() => Favorite, favorite => favorite.product)
|
@OneToMany(() => Favorite, favorite => favorite.product)
|
||||||
favorites = new Collection<Favorite>(this);
|
favorites = new Collection<Favorite>(this);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { EntityManager } from '@mikro-orm/postgresql';
|
|||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { Category } from '../entities/category.entity';
|
import { Category } from '../entities/category.entity';
|
||||||
import { CategoryMessage, RestMessage } from 'src/common/enums/message.enum';
|
import { CategoryMessage, RestMessage } from 'src/common/enums/message.enum';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CategoryService {
|
export class CategoryService {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { CategoryMessage, FoodMessage, RestMessage } from 'src/common/enums/mess
|
|||||||
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
||||||
import { CacheService } from '../../../utils/cache.service';
|
import { CacheService } from '../../../utils/cache.service';
|
||||||
import { Favorite } from '../entities/favorite.entity';
|
import { Favorite } from '../entities/favorite.entity';
|
||||||
import { Inventory } from 'src/modules/inventory/entities/inventory.entity';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FoodService {
|
export class FoodService {
|
||||||
@@ -22,7 +21,7 @@ export class FoodService {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
async create(restId: string, createFoodDto: CreateFoodDto) {
|
async create(restId: string, createFoodDto: CreateFoodDto) {
|
||||||
const { categoryId, dailyStock = 0, ...rest } = createFoodDto;
|
const { categoryId, ...rest } = createFoodDto;
|
||||||
const shop = await this.restRepository.findOne({ id: restId });
|
const shop = await this.restRepository.findOne({ id: restId });
|
||||||
if (!shop) {
|
if (!shop) {
|
||||||
throw new NotFoundException(RestMessage.NOT_FOUND);
|
throw new NotFoundException(RestMessage.NOT_FOUND);
|
||||||
@@ -32,7 +31,7 @@ export class FoodService {
|
|||||||
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
throw new NotFoundException(CategoryMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { product, inventory } = await this.em.transactional(async em => {
|
const product = await this.em.transactional(async em => {
|
||||||
// prepare data with defaults for required fields so repository.create typing is satisfied
|
// prepare data with defaults for required fields so repository.create typing is satisfied
|
||||||
const data: RequiredEntityData<Product> = {
|
const data: RequiredEntityData<Product> = {
|
||||||
desc: rest.desc,
|
desc: rest.desc,
|
||||||
@@ -51,23 +50,16 @@ export class FoodService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const product = em.create(Product, data);
|
const product = em.create(Product, data);
|
||||||
const newInventoryRecord = em.create(Inventory, {
|
|
||||||
product: product,
|
|
||||||
availableStock: dailyStock,
|
|
||||||
totalStock: dailyStock,
|
|
||||||
});
|
|
||||||
|
|
||||||
await em.flush();
|
await em.flush();
|
||||||
return { product, inventory: newInventoryRecord };
|
return product;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-load created entities with the primary EM to ensure they're attached
|
// Re-load created entities with the primary EM to ensure they're attached
|
||||||
const savedFood = product?.id
|
const savedFood = product?.id
|
||||||
? await this.foodRepository.findOne({ id: product.id }, { populate: ['category', 'shop'] })
|
? await this.foodRepository.findOne({ id: product.id }, { populate: ['category', 'shop'] })
|
||||||
: null;
|
: null;
|
||||||
const savedInventory = inventory?.id ? await this.em.findOne(Inventory, { id: inventory.id }) : inventory;
|
|
||||||
|
|
||||||
return { product: savedFood ?? product, inventory: savedInventory ?? inventory };
|
return savedFood ?? product;
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(restId: string, dto: FindFoodsDto) {
|
findAll(restId: string, dto: FindFoodsDto) {
|
||||||
@@ -78,7 +70,7 @@ export class FoodService {
|
|||||||
* Public product detail (only active products are visible).
|
* Public product detail (only active products are visible).
|
||||||
*/
|
*/
|
||||||
async findPublicById(foodId: string, userId?: string): Promise<any> {
|
async findPublicById(foodId: string, userId?: string): Promise<any> {
|
||||||
const product = await this.foodRepository.findOne({ id: foodId, isActive: true }, { populate: ['category', 'inventory'] });
|
const product = await this.foodRepository.findOne({ id: foodId, isActive: true }, { populate: ['category'] });
|
||||||
if (!product) throw new NotFoundException(FoodMessage.NOT_FOUND);
|
if (!product) throw new NotFoundException(FoodMessage.NOT_FOUND);
|
||||||
let isFavorite = false;
|
let isFavorite = false;
|
||||||
if (userId) {
|
if (userId) {
|
||||||
@@ -94,7 +86,7 @@ export class FoodService {
|
|||||||
* Admin product detail (scoped to the authenticated shop).
|
* Admin product detail (scoped to the authenticated shop).
|
||||||
*/
|
*/
|
||||||
async findAdminById(restId: string, id: string): Promise<Product> {
|
async findAdminById(restId: string, id: string): Promise<Product> {
|
||||||
const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['category', 'inventory'] });
|
const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['category'] });
|
||||||
|
|
||||||
if (!product) throw new NotFoundException(FoodMessage.NOT_FOUND);
|
if (!product) throw new NotFoundException(FoodMessage.NOT_FOUND);
|
||||||
return product;
|
return product;
|
||||||
@@ -123,7 +115,7 @@ export class FoodService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(restId: string, id: string, dto: Partial<CreateFoodDto>): Promise<Product> {
|
async update(restId: string, id: string, dto: Partial<CreateFoodDto>): Promise<Product> {
|
||||||
const { categoryId, dailyStock, ...rest } = dto;
|
const { categoryId, ...rest } = dto;
|
||||||
const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['shop'] });
|
const product = await this.foodRepository.findOne({ id, shop: { id: restId } }, { populate: ['shop'] });
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new NotFoundException(FoodMessage.NOT_FOUND);
|
throw new NotFoundException(FoodMessage.NOT_FOUND);
|
||||||
@@ -143,25 +135,7 @@ export class FoodService {
|
|||||||
// 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);
|
||||||
|
|
||||||
// Persist product and update/create related inventory atomically
|
await this.em.persistAndFlush(product);
|
||||||
await this.em.transactional(async em => {
|
|
||||||
await em.persistAndFlush(product);
|
|
||||||
|
|
||||||
if (typeof dailyStock !== 'undefined') {
|
|
||||||
const inventoryRecord = await em.findOne(Inventory, { product: product });
|
|
||||||
if (inventoryRecord) {
|
|
||||||
inventoryRecord.totalStock = dailyStock;
|
|
||||||
} else {
|
|
||||||
em.create(Inventory, {
|
|
||||||
product: product,
|
|
||||||
availableStock: dailyStock,
|
|
||||||
totalStock: dailyStock,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await em.flush();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Re-load the product to ensure populated relations and stable instance
|
// Re-load the product to ensure populated relations and stable instance
|
||||||
const savedFood = await this.foodRepository.findOne({ id: product.id }, { populate: ['category', 'shop'] });
|
const savedFood = await this.foodRepository.findOne({ id: product.id }, { populate: ['category', 'shop'] });
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export class FoodRepository extends EntityRepository<Product> {
|
|||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||||
populate: ['category','inventory'],
|
populate: ['category'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Cron } from '@nestjs/schedule';
|
import { Cron } from '@nestjs/schedule';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { FoodRepository } from ../../..products/repositories/product.repository';
|
import { FoodRepository } from ../../../products/repositories/product.repository';
|
||||||
import { ReviewRepository } from '../repositories/review.repository';
|
import { ReviewRepository } from '../repositories/review.repository';
|
||||||
import { ReviewStatus } from '../enums/review-status.enum';
|
import { ReviewStatus } from '../enums/review-status.enum';
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export class FoodRatingCronService {
|
|||||||
this.logger.log('Starting daily product rating calculation cron job...');
|
this.logger.log('Starting daily product rating calculation cron job...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get all products from ../../..shops
|
// Get all products from ../../../shops
|
||||||
const products = await this.foodRepository.find({});
|
const products = await this.foodRepository.find({});
|
||||||
|
|
||||||
this.logger.log(`Found ${products.length} products to process`);
|
this.logger.log(`Found ${products.length} products to process`);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { CreateReviewDto } from '../dto/create-review.dto';
|
|||||||
import { UpdateReviewDto } from '../dto/update-review.dto';
|
import { UpdateReviewDto } from '../dto/update-review.dto';
|
||||||
import { FindRestuarantReviewsDto, FindReviewsDto } from '../dto/find-reviews.dto';
|
import { FindRestuarantReviewsDto, FindReviewsDto } from '../dto/find-reviews.dto';
|
||||||
import { ReviewRepository } from '../repositories/review.repository';
|
import { ReviewRepository } from '../repositories/review.repository';
|
||||||
import { FoodRepository } from ../../..products/repositories/product.repository';
|
import { FoodRepository } from ../../../products/repositories/product.repository';
|
||||||
import { UserRepository } from '../../../users/repositories/user.repository';
|
import { UserRepository } from '../../../users/repositories/user.repository';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
@@ -11,7 +11,7 @@ import { Review } from '../entities/review.entity';
|
|||||||
import { FoodMessage, UserMessage, ReviewMessage } from 'src/common/enums/message.enum';
|
import { FoodMessage, UserMessage, ReviewMessage } from 'src/common/enums/message.enum';
|
||||||
import { Order } from '../../../orders/entities/order.entity';
|
import { Order } from '../../../orders/entities/order.entity';
|
||||||
import { OrderItem } from '../../../orders/entities/order-item.entity';
|
import { OrderItem } from '../../../orders/entities/order-item.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { ReviewStatus } from '../enums/review-status.enum';
|
import { ReviewStatus } from '../enums/review-status.enum';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { ReviewCreatedEvent } from '../events/review.events';
|
import { ReviewCreatedEvent } from '../events/review.events';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ReviewRepository } from './repositories/review.repository';
|
|||||||
import { FoodRatingCronService } from './providers/product-rating-cron.service';
|
import { FoodRatingCronService } from './providers/product-rating-cron.service';
|
||||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||||
import { Review } from './entities/review.entity';
|
import { Review } from './entities/review.entity';
|
||||||
import { FoodModule } from ../../..products/product.module';
|
import { FoodModule } from ../../../products/product.module';
|
||||||
import { UserModule } from '../users/user.module';
|
import { UserModule } from '../users/user.module';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Collection, Entity, Index, ManyToMany, OneToMany, ManyToOne, Property } from '@mikro-orm/core';
|
import { Collection, Entity, Index, ManyToMany, OneToMany, ManyToOne, Property } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { Permission } from './permission.entity';
|
import { Permission } from './permission.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { RolePermission } from './rolePermission.entity';
|
import { RolePermission } from './rolePermission.entity';
|
||||||
import { AdminRole } from 'src/modules/admin/entities/adminRole.entity';
|
import { AdminRole } from 'src/modules/admin/entities/adminRole.entity';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { InjectRepository } from '@mikro-orm/nestjs';
|
|||||||
import { EntityRepository, FilterQuery } from '@mikro-orm/core';
|
import { EntityRepository, FilterQuery } from '@mikro-orm/core';
|
||||||
import { Role } from '../entities/role.entity';
|
import { Role } from '../entities/role.entity';
|
||||||
import { Permission } from '../entities/permission.entity';
|
import { Permission } from '../entities/permission.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { CreateRoleDto } from '../dto/create-role.dto';
|
import { CreateRoleDto } from '../dto/create-role.dto';
|
||||||
import { UpdateRoleDto } from '../dto/update-role.dto';
|
import { UpdateRoleDto } from '../dto/update-role.dto';
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export class RestaurantsService {
|
|||||||
// Delete coupons
|
// Delete coupons
|
||||||
await em.nativeDelete(Coupon, { shop: id });
|
await em.nativeDelete(Coupon, { shop: id });
|
||||||
|
|
||||||
// Delete products (will cascade to reviews, favorites, and inventory due to cascade settings)
|
// Delete products (will cascade to reviews and favorites due to cascade settings)
|
||||||
await em.nativeDelete(Product, { shop: id });
|
await em.nativeDelete(Product, { shop: id });
|
||||||
|
|
||||||
// Delete categories (products should be deleted by cascade, but delete explicitly to be safe)
|
// Delete categories (products should be deleted by cascade, but delete explicitly to be safe)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Entity, Property, ManyToOne, Index, Enum } from '@mikro-orm/core';
|
import { Entity, Property, ManyToOne, Index, Enum } 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 { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
import { PointTransactionReason } from '../interface/point';
|
import { PointTransactionReason } from '../interface/point';
|
||||||
import { PointTransactionType } from '../interface/point';
|
import { PointTransactionType } from '../interface/point';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Entity, Index, Property, ManyToOne, Enum } from '@mikro-orm/core';
|
|||||||
import { BaseEntity } from '../../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../../common/entities/base.entity';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'wallet_transactions' })
|
@Entity({ tableName: 'wallet_transactions' })
|
||||||
@Index({ properties: ['user', 'shop'] })
|
@Index({ properties: ['user', 'shop'] })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Injectable, NotFoundException, BadRequestException } from '@nestjs/comm
|
|||||||
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { User } from '../entities/user.entity';
|
import { User } from '../entities/user.entity';
|
||||||
import { UserAddress } from '../entities/user-address.entity';
|
import { UserAddress } from '../entities/user-address.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { UpdateUserDto } from '../dto/update-user.dto';
|
import { UpdateUserDto } from '../dto/update-user.dto';
|
||||||
import { FindUsersDto } from '../dto/find-user.dto';
|
import { FindUsersDto } from '../dto/find-user.dto';
|
||||||
@@ -13,7 +13,7 @@ import { UpdateUserAddressDto } from '../dto/update-user-address.dto';
|
|||||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
import { UserRepository } from '../repositories/user.repository';
|
import { UserRepository } from '../repositories/user.repository';
|
||||||
import { normalizePhone } from '../../../utils/phone.util';
|
import { normalizePhone } from '../../../utils/phone.util';
|
||||||
import { RestRepository } from ../../..shops/repositories/rest.repository';
|
import { RestRepository } from ../../../shops/repositories/rest.repository';
|
||||||
import { WalletTransactionRepository } from '../repositories/wallet-transaction.repository';
|
import { WalletTransactionRepository } from '../repositories/wallet-transaction.repository';
|
||||||
import { WalletService } from './wallet.service';
|
import { WalletService } from './wallet.service';
|
||||||
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { User } from './entities/user.entity';
|
|||||||
import { UserAddress } from './entities/user-address.entity';
|
import { UserAddress } from './entities/user-address.entity';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { UserRepository } from './repositories/user.repository';
|
import { UserRepository } from './repositories/user.repository';
|
||||||
import { RestaurantsModule } from ../../..shops.module';
|
import { RestaurantsModule } from ../../../shops.module';
|
||||||
import { WalletTransactionRepository } from './repositories/wallet-transaction.repository';
|
import { WalletTransactionRepository } from './repositories/wallet-transaction.repository';
|
||||||
import { WalletService } from './providers/wallet.service';
|
import { WalletService } from './providers/wallet.service';
|
||||||
import { WalletTransaction } from './entities/wallet-transaction.entity';
|
import { WalletTransaction } from './entities/wallet-transaction.entity';
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ import { UsersSeeder } from './users.seeder';
|
|||||||
import { CouponsSeeder } from './coupons.seeder';
|
import { CouponsSeeder } from './coupons.seeder';
|
||||||
import { NotificationPreferencesSeeder } from './notification-preferences.seeder';
|
import { NotificationPreferencesSeeder } from './notification-preferences.seeder';
|
||||||
import { UserWalletsSeeder } from './user-wallets.seeder';
|
import { UserWalletsSeeder } from './user-wallets.seeder';
|
||||||
import { InventorySeeder } from './inventory.seeder';
|
|
||||||
// import { NotificationsSeeder } from './notifications.seeder';
|
// import { NotificationsSeeder } from './notifications.seeder';
|
||||||
|
|
||||||
export class DatabaseSeeder extends Seeder {
|
export class DatabaseSeeder extends Seeder {
|
||||||
async run(em: EntityManager) {
|
async run(em: EntityManager) {
|
||||||
const TOTAL_STEPS = 15;
|
const TOTAL_STEPS = 14;
|
||||||
console.info('[Seeder] Starting database seed');
|
console.info('[Seeder] Starting database seed');
|
||||||
// 1. Create Permissions
|
// 1. Create Permissions
|
||||||
console.info(`[Seeder] Step 1/${TOTAL_STEPS}: Creating permissions`);
|
console.info(`[Seeder] Step 1/${TOTAL_STEPS}: Creating permissions`);
|
||||||
@@ -76,38 +75,33 @@ export class DatabaseSeeder extends Seeder {
|
|||||||
await productsSeeder.run(em, shopsMap, categoriesMap);
|
await productsSeeder.run(em, shopsMap, categoriesMap);
|
||||||
console.info(`[Seeder] Step 9/${TOTAL_STEPS}: Done`);
|
console.info(`[Seeder] Step 9/${TOTAL_STEPS}: Done`);
|
||||||
|
|
||||||
// 10. Create Inventory for all Products
|
|
||||||
console.info(`[Seeder] Step 10/${TOTAL_STEPS}: Creating inventory`);
|
|
||||||
const inventorySeeder = new InventorySeeder();
|
|
||||||
await inventorySeeder.run(em);
|
|
||||||
console.info(`[Seeder] Step 10/${TOTAL_STEPS}: Done`);
|
|
||||||
|
|
||||||
// 11. Create Coupons
|
// 10. Create Coupons
|
||||||
console.info(`[Seeder] Step 11/${TOTAL_STEPS}: Creating coupons`);
|
console.info(`[Seeder] Step 10/${TOTAL_STEPS}: Creating coupons`);
|
||||||
const couponsSeeder = new CouponsSeeder();
|
const couponsSeeder = new CouponsSeeder();
|
||||||
await couponsSeeder.run(em, shopsMap);
|
await couponsSeeder.run(em, shopsMap);
|
||||||
console.info(`[Seeder] Step 11/${TOTAL_STEPS}: Done`);
|
console.info(`[Seeder] Step 10/${TOTAL_STEPS}: Done`);
|
||||||
|
|
||||||
// 12. Create Admins
|
// 11. Create Admins
|
||||||
console.info(`[Seeder] Step 12/${TOTAL_STEPS}: Creating admins`);
|
console.info(`[Seeder] Step 11/${TOTAL_STEPS}: Creating admins`);
|
||||||
const adminsSeeder = new AdminsSeeder();
|
const adminsSeeder = new AdminsSeeder();
|
||||||
await adminsSeeder.run(em, rolesMap, shopsMap);
|
await adminsSeeder.run(em, rolesMap, shopsMap);
|
||||||
console.info(`[Seeder] Step 12/${TOTAL_STEPS}: Done`);
|
console.info(`[Seeder] Step 11/${TOTAL_STEPS}: Done`);
|
||||||
|
|
||||||
// 13. Create Users
|
// 12. Create Users
|
||||||
console.info(`[Seeder] Step 13/${TOTAL_STEPS}: Creating users`);
|
console.info(`[Seeder] Step 12/${TOTAL_STEPS}: Creating users`);
|
||||||
const usersSeeder = new UsersSeeder();
|
const usersSeeder = new UsersSeeder();
|
||||||
await usersSeeder.run(em);
|
await usersSeeder.run(em);
|
||||||
console.info(`[Seeder] Step 13/${TOTAL_STEPS}: Done`);
|
console.info(`[Seeder] Step 12/${TOTAL_STEPS}: Done`);
|
||||||
|
|
||||||
// 14. Create User Wallets
|
// 13. Create User Wallets
|
||||||
console.info(`[Seeder] Step 14/${TOTAL_STEPS}: Creating user wallets`);
|
console.info(`[Seeder] Step 13/${TOTAL_STEPS}: Creating user wallets`);
|
||||||
const userWalletsSeeder = new UserWalletsSeeder();
|
const userWalletsSeeder = new UserWalletsSeeder();
|
||||||
await userWalletsSeeder.run(em);
|
await userWalletsSeeder.run(em);
|
||||||
console.info(`[Seeder] Step 14/${TOTAL_STEPS}: Done`);
|
console.info(`[Seeder] Step 13/${TOTAL_STEPS}: Done`);
|
||||||
|
|
||||||
// 15. Create Notifications for admins and users for all shops
|
// 14. Create Notifications for admins and users for all shops
|
||||||
console.info(`[Seeder] Step 15/${TOTAL_STEPS}: (optional) creating notifications`);
|
console.info(`[Seeder] Step 14/${TOTAL_STEPS}: (optional) creating notifications`);
|
||||||
// const notificationsSeeder = new NotificationsSeeder();
|
// const notificationsSeeder = new NotificationsSeeder();
|
||||||
// await notificationsSeeder.run(em);
|
// await notificationsSeeder.run(em);
|
||||||
console.info(`[Seeder] Step 15/${TOTAL_STEPS}: Done`);
|
console.info(`[Seeder] Step 15/${TOTAL_STEPS}: Done`);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { EntityManager } from '@mikro-orm/core';
|
|||||||
import { Admin } from '../modules/admin/entities/admin.entity';
|
import { Admin } from '../modules/admin/entities/admin.entity';
|
||||||
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
|
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
|
||||||
import type { Role } from '../modules/roles/entities/role.entity';
|
import type { Role } from '../modules/roles/entities/role.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { adminsData } from './data/admins.data';
|
import { adminsData } from './data/admins.data';
|
||||||
import { normalizePhone } from '../modules/utils/phone.util';
|
import { normalizePhone } from '../modules/utils/phone.util';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Category } from ../../..products/entities/category.entity';
|
import { Category } from ../../../products/entities/category.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { categoriesData } from './data/categories.data';
|
import { categoriesData } from './data/categories.data';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Coupon } from '../modules/coupons/entities/coupon.entity';
|
import { Coupon } from '../modules/coupons/entities/coupon.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { couponsData } from './data/coupons.data';
|
import { couponsData } from './data/coupons.data';
|
||||||
|
|
||||||
export class CouponsSeeder {
|
export class CouponsSeeder {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Delivery } from '../modules/delivery/entities/delivery.entity';
|
import { Delivery } from '../modules/delivery/entities/delivery.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { deliveryMethodsData } from './data/delivery-methods.data';
|
import { deliveryMethodsData } from './data/delivery-methods.data';
|
||||||
import { DeliveryFeeTypeEnum } from 'src/modules/delivery/interface/delivery';
|
import { DeliveryFeeTypeEnum } from 'src/modules/delivery/interface/delivery';
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
|
||||||
import { Product } from ../../..products/entities/product.entity';
|
|
||||||
import { Inventory } from '../modules/inventory/entities/inventory.entity';
|
|
||||||
|
|
||||||
export class InventorySeeder {
|
|
||||||
async run(em: EntityManager): Promise<void> {
|
|
||||||
const products = await em.find(Product, {});
|
|
||||||
|
|
||||||
for (const product of products) {
|
|
||||||
// Check if inventory already exists for this product
|
|
||||||
const existingInventory = await em.findOne(Inventory, {
|
|
||||||
product: { id: product.id },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingInventory) {
|
|
||||||
const inventory = em.create(Inventory, {
|
|
||||||
product,
|
|
||||||
totalStock: 50,
|
|
||||||
availableStock: 50,
|
|
||||||
});
|
|
||||||
|
|
||||||
em.persist(inventory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await em.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { NotificationPreference } from '../modules/notifications/entities/notification-preference.entity';
|
import { NotificationPreference } from '../modules/notifications/entities/notification-preference.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { notificationPreferencesData } from './data/notification-preferences.data';
|
import { notificationPreferencesData } from './data/notification-preferences.data';
|
||||||
|
|
||||||
export class NotificationPreferencesSeeder {
|
export class NotificationPreferencesSeeder {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { EntityManager } from '@mikro-orm/core';
|
|||||||
import { Notification } from '../modules/notifications/entities/notification.entity';
|
import { Notification } from '../modules/notifications/entities/notification.entity';
|
||||||
import { Admin } from '../modules/admin/entities/admin.entity';
|
import { Admin } from '../modules/admin/entities/admin.entity';
|
||||||
import { User } from '../modules/users/entities/user.entity';
|
import { User } from '../modules/users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { NotifTitleEnum } from '../modules/notifications/interfaces/notification.interface';
|
import { NotifTitleEnum } from '../modules/notifications/interfaces/notification.interface';
|
||||||
|
|
||||||
export class NotificationsSeeder {
|
export class NotificationsSeeder {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { PaymentMethod } from '../modules/payments/entities/payment-method.entity';
|
import { PaymentMethod } from '../modules/payments/entities/payment-method.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { paymentMethodsData } from './data/payment-methods.data';
|
import { paymentMethodsData } from './data/payment-methods.data';
|
||||||
|
|
||||||
export class PaymentMethodsSeeder {
|
export class PaymentMethodsSeeder {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Schedule } from ../../..shops/entities/schedule.entity';
|
import { Schedule } from ../../../shops/entities/schedule.entity';
|
||||||
import type { Shop } from ../../..shops/entities/shop.entity';
|
import type { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { timeSlots } from './data/schedules.data';
|
import { timeSlots } from './data/schedules.data';
|
||||||
|
|
||||||
export class SchedulesSeeder {
|
export class SchedulesSeeder {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { User } from '../modules/users/entities/user.entity';
|
import { User } from '../modules/users/entities/user.entity';
|
||||||
import { Shop } from ../../..shops/entities/shop.entity';
|
import { Shop } from ../../../shops/entities/shop.entity';
|
||||||
import { WalletTransaction } from '../modules/users/entities/wallet-transaction.entity';
|
import { WalletTransaction } from '../modules/users/entities/wallet-transaction.entity';
|
||||||
import { WalletTransactionReason, WalletTransactionType } from 'src/modules/users/interface/wallet';
|
import { WalletTransactionReason, WalletTransactionType } from 'src/modules/users/interface/wallet';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user