inventory
This commit is contained in:
@@ -22,6 +22,8 @@ import { NotificationsModule } from './modules/notifications/notifications.modul
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { PagerModule } from './modules/pager/pager.module';
|
||||
import { ContactModule } from './modules/contact/contact.module';
|
||||
import { ReservationsModule } from './modules/reservations/reservations.module';
|
||||
import { InventoryModule } from './modules/inventory/inventory.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -52,6 +54,8 @@ import { ContactModule } from './modules/contact/contact.module';
|
||||
EventEmitterModule.forRoot(),
|
||||
PagerModule,
|
||||
ContactModule,
|
||||
ReservationsModule,
|
||||
InventoryModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Category } from './category.entity';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Restaurant } from '../../../modules/restaurants/entities/restaurant.entity';
|
||||
import { Review } from 'src/modules/review/entities/review.entity';
|
||||
import { Inventory } from 'src/modules/inventory/entities/inventory.entity';
|
||||
|
||||
@Entity({ tableName: 'foods' })
|
||||
@Index({ properties: ['restaurant', 'isActive'] })
|
||||
@@ -18,6 +19,9 @@ export class Food extends BaseEntity {
|
||||
@OneToMany(() => Review, review => review.food, { cascade: [Cascade.ALL], orphanRemoval: true })
|
||||
reviews = new Collection<Review>(this);
|
||||
|
||||
@OneToMany(() => Inventory, inventory => inventory.food, { cascade: [Cascade.ALL], orphanRemoval: true })
|
||||
inventory = new Collection<Inventory>(this);
|
||||
|
||||
@Property({ nullable: true })
|
||||
title?: string;
|
||||
|
||||
@@ -54,11 +58,11 @@ export class Food extends BaseEntity {
|
||||
@Property({ type: 'boolean', default: false })
|
||||
dinner: boolean = false;
|
||||
|
||||
@Property({ type: 'int', default: 0 })
|
||||
stock: number = 0;
|
||||
// @Property({ type: 'int', default: 0 })
|
||||
// stock: number = 0;
|
||||
|
||||
@Property({ type: 'int', default: 0 })
|
||||
stockDefault: number = 0;
|
||||
// @Property({ type: 'int', default: 0 })
|
||||
// stockDefault: number = 0;
|
||||
|
||||
@Property({ type: 'boolean', default: true })
|
||||
isActive: boolean = true;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsArray, ValidateNested, ArrayMinSize, IsString, IsNumber, Min, IsDate } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class BulkReserveFoodItemDto {
|
||||
@ApiProperty({ example: 'food-123', description: 'Food ID' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
foodId!: string;
|
||||
|
||||
@ApiProperty({ example: 5, description: 'Quantity to reserve' })
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
quantity!: number;
|
||||
|
||||
@ApiProperty({ example: '2024-12-31T23:59:59Z', description: 'Reservation expiration date' })
|
||||
@IsNotEmpty()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
expiresAt!: Date;
|
||||
}
|
||||
|
||||
export class BulkReserveFoodDto {
|
||||
@ApiProperty({
|
||||
description: 'Array of food reservations to create',
|
||||
type: [BulkReserveFoodItemDto],
|
||||
example: [
|
||||
{ foodId: 'food-123', quantity: 5, expiresAt: '2024-12-31T23:59:59Z' },
|
||||
{ foodId: 'food-789', quantity: 3, expiresAt: '2024-12-31T23:59:59Z' },
|
||||
],
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsArray()
|
||||
@ArrayMinSize(1, { message: 'At least one item is required' })
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => BulkReserveFoodItemDto)
|
||||
items!: BulkReserveFoodItemDto[];
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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: 'food-123', description: 'Food ID' })
|
||||
@IsNotEmpty()
|
||||
foodId!: string;
|
||||
}
|
||||
|
||||
export class BulkSetStockDto {
|
||||
@ApiProperty({
|
||||
description: 'Array of stock items to set',
|
||||
type: [BulkSetStockItemDto],
|
||||
example: [
|
||||
{ foodId: 'food-123', totalStock: 100, availableStock: 80 },
|
||||
{ foodId: 'food-456', totalStock: 50, availableStock: 45 },
|
||||
],
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsArray()
|
||||
@ArrayMinSize(1, { message: 'At least one item is required' })
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => BulkSetStockItemDto)
|
||||
items!: BulkSetStockItemDto[];
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export class CreateInventoryDto {}
|
||||
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateInventoryDto } from './create-inventory.dto';
|
||||
|
||||
export class UpdateInventoryDto extends PartialType(CreateInventoryDto) {}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Entity, ManyToOne, Property, Unique } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
import { Food } from '../../foods/entities/food.entity';
|
||||
|
||||
@Entity({ tableName: 'inventory' })
|
||||
@Unique({ properties: ['food', 'restaurant'] })
|
||||
export class Inventory extends BaseEntity {
|
||||
@ManyToOne(() => Food, { unique: true })
|
||||
food!: Food;
|
||||
|
||||
@ManyToOne(() => Restaurant)
|
||||
restaurant!: Restaurant;
|
||||
|
||||
@Property({ type: 'int' })
|
||||
totalStock!: number;
|
||||
|
||||
@Property({ type: 'int' })
|
||||
availableStock!: number;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Entity, Enum, ManyToOne, Property } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Food } from '../../foods/entities/food.entity';
|
||||
import { Order } from '../../orders/entities/order.entity';
|
||||
import { ReservationStatus } from '../inteface/reservation';
|
||||
|
||||
@Entity({ tableName: 'reservations' })
|
||||
export class Reservation extends BaseEntity {
|
||||
@ManyToOne(() => Food)
|
||||
food!: Food;
|
||||
|
||||
@ManyToOne(() => Order)
|
||||
order!: Order;
|
||||
|
||||
@Property({ type: 'int' })
|
||||
quantity!: number;
|
||||
|
||||
@Property({ type: 'date' })
|
||||
expiresAt!: Date;
|
||||
|
||||
@Enum(() => ReservationStatus)
|
||||
status: ReservationStatus = ReservationStatus.ACTIVE;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum ReservationStatus {
|
||||
ACTIVE = 'active',
|
||||
CONFIRMED = 'confirmed',
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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';
|
||||
|
||||
@ApiTags('inventory')
|
||||
@Controller()
|
||||
export class InventoryController {
|
||||
constructor(private readonly inventoryService: InventoryService) {}
|
||||
|
||||
@Patch('admin/inventory/food/:foodId/stock')
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Set available and total stock for a food item' })
|
||||
@ApiParam({ name: 'foodId', description: 'Food 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/foods/stock/bulk')
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Bulk set available and total stock for multiple food items' })
|
||||
@ApiBody({ type: BulkSetStockDto })
|
||||
bulkSetStockForFoods(@RestId() restaurantId: string, @Body() bulkSetStockDto: BulkSetStockDto): Promise<Inventory[]> {
|
||||
return this.inventoryService.bulkSetStockForFoods(restaurantId, bulkSetStockDto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
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';
|
||||
|
||||
@Module({
|
||||
imports: [MikroOrmModule.forFeature([Inventory]), AuthModule],
|
||||
controllers: [InventoryController],
|
||||
providers: [InventoryService],
|
||||
})
|
||||
export class InventoryModule {}
|
||||
@@ -0,0 +1,221 @@
|
||||
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-food.dto';
|
||||
import { Inventory } from './entities/inventory.entity';
|
||||
import { Reservation } from './entities/reservation.entity';
|
||||
import { Food } from '../foods/entities/food.entity';
|
||||
import { Restaurant } from '../restaurants/entities/restaurant.entity';
|
||||
import { Order } from '../orders/entities/order.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 food and verify it belongs to the restaurant
|
||||
const food = await this.em.findOne(Food, { id: foodId }, { populate: ['restaurant'] });
|
||||
if (!food) {
|
||||
throw new NotFoundException(`Food with ID ${foodId} not found`);
|
||||
}
|
||||
|
||||
if (food.restaurant.id !== restaurantId) {
|
||||
throw new BadRequestException(`Food does not belong to restaurant ${restaurantId}`);
|
||||
}
|
||||
|
||||
// Find or create inventory record
|
||||
let inventory = await this.em.findOne(Inventory, {
|
||||
food: { id: foodId },
|
||||
restaurant: { id: restaurantId },
|
||||
});
|
||||
|
||||
if (!inventory) {
|
||||
// Create new inventory record
|
||||
const restaurant = await this.em.findOne(Restaurant, { id: restaurantId });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException(`Restaurant with ID ${restaurantId} not found`);
|
||||
}
|
||||
|
||||
inventory = this.em.create(Inventory, {
|
||||
food,
|
||||
restaurant,
|
||||
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 food ${item.foodId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all food IDs
|
||||
const foodIds = items.map(item => item.foodId);
|
||||
|
||||
// Load all foods in one query
|
||||
const foods = await this.em.find(Food, { id: { $in: foodIds } }, { populate: ['restaurant'] });
|
||||
|
||||
// Verify all foods exist and belong to the restaurant
|
||||
const foodMap = new Map<string, Food>();
|
||||
for (const food of foods) {
|
||||
if (food.restaurant.id !== restaurantId) {
|
||||
throw new BadRequestException(`Food ${food.id} does not belong to restaurant ${restaurantId}`);
|
||||
}
|
||||
foodMap.set(food.id, food);
|
||||
}
|
||||
|
||||
// Check for missing foods
|
||||
const missingFoodIds = foodIds.filter(id => !foodMap.has(id));
|
||||
if (missingFoodIds.length > 0) {
|
||||
throw new NotFoundException(`Foods not found: ${missingFoodIds.join(', ')}`);
|
||||
}
|
||||
|
||||
// Load all existing inventories in one query
|
||||
const existingInventories = await this.em.find(Inventory, {
|
||||
food: { id: { $in: foodIds } },
|
||||
restaurant: { id: restaurantId },
|
||||
});
|
||||
|
||||
const inventoryMap = new Map<string, Inventory>();
|
||||
for (const inventory of existingInventories) {
|
||||
inventoryMap.set(inventory.food.id, inventory);
|
||||
}
|
||||
|
||||
// Get restaurant once
|
||||
const restaurant = await this.em.findOne(Restaurant, { id: restaurantId });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException(`Restaurant with ID ${restaurantId} not found`);
|
||||
}
|
||||
|
||||
// Process all items
|
||||
const results: Inventory[] = [];
|
||||
for (const item of items) {
|
||||
const food = foodMap.get(item.foodId)!;
|
||||
let inventory = inventoryMap.get(item.foodId);
|
||||
|
||||
if (!inventory) {
|
||||
// Create new inventory record
|
||||
inventory = this.em.create(Inventory, {
|
||||
food,
|
||||
restaurant,
|
||||
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 bulkReserveFood(
|
||||
restaurantId: string,
|
||||
orderId: string,
|
||||
bulkReserveFoodDto: BulkReserveFoodDto,
|
||||
): Promise<Reservation[]> {
|
||||
const { items } = bulkReserveFoodDto;
|
||||
|
||||
// Get all unique food IDs
|
||||
const foodIds = [...new Set(items.map(item => item.foodId))];
|
||||
|
||||
// Load all foods in one query
|
||||
const foods = await this.em.find(Food, { id: { $in: foodIds } }, { populate: ['restaurant'] });
|
||||
|
||||
// Verify all foods exist and belong to the restaurant
|
||||
const foodMap = new Map<string, Food>();
|
||||
for (const food of foods) {
|
||||
if (food.restaurant.id !== restaurantId) {
|
||||
throw new BadRequestException(`Food ${food.id} does not belong to restaurant ${restaurantId}`);
|
||||
}
|
||||
foodMap.set(food.id, food);
|
||||
}
|
||||
|
||||
// Check for missing foods
|
||||
const missingFoodIds = foodIds.filter(id => !foodMap.has(id));
|
||||
if (missingFoodIds.length > 0) {
|
||||
throw new NotFoundException(`Foods not found: ${missingFoodIds.join(', ')}`);
|
||||
}
|
||||
|
||||
// Load order
|
||||
const order = await this.em.findOne(Order, { id: orderId });
|
||||
if (!order) {
|
||||
throw new NotFoundException(`Order with ID ${orderId} not found`);
|
||||
}
|
||||
|
||||
// Load all existing inventories in one query
|
||||
const existingInventories = await this.em.find(Inventory, {
|
||||
food: { id: { $in: foodIds } },
|
||||
restaurant: { id: restaurantId },
|
||||
});
|
||||
|
||||
const inventoryMap = new Map<string, Inventory>();
|
||||
for (const inventory of existingInventories) {
|
||||
inventoryMap.set(inventory.food.id, inventory);
|
||||
}
|
||||
|
||||
// Validate stock availability and create reservations
|
||||
const reservations: Reservation[] = [];
|
||||
for (const item of items) {
|
||||
const food = foodMap.get(item.foodId)!;
|
||||
const inventory = inventoryMap.get(item.foodId);
|
||||
|
||||
// Check if inventory exists
|
||||
if (!inventory) {
|
||||
throw new NotFoundException(`Inventory not found for food ${item.foodId}`);
|
||||
}
|
||||
|
||||
// Check if available stock is sufficient
|
||||
if (inventory.availableStock < item.quantity) {
|
||||
throw new BadRequestException(
|
||||
`Insufficient stock for food ${item.foodId}. Available: ${inventory.availableStock}, Requested: ${item.quantity}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Create reservation record
|
||||
const reservation = this.em.create(Reservation, {
|
||||
food,
|
||||
order,
|
||||
quantity: item.quantity,
|
||||
expiresAt: item.expiresAt,
|
||||
});
|
||||
|
||||
reservations.push(reservation);
|
||||
|
||||
// Update available stock (decrease by quantity)
|
||||
inventory.availableStock -= item.quantity;
|
||||
}
|
||||
|
||||
// Flush all changes at once
|
||||
await this.em.flush();
|
||||
|
||||
return reservations;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,40 @@
|
||||
import type { CouponType } from 'src/modules/coupons/interface/coupon';
|
||||
|
||||
// export enum OrderStatus {
|
||||
// // Initial status
|
||||
// Pending = 'pending',
|
||||
|
||||
// // Cancellation statuses
|
||||
// CancelledBySystem = 'cancelledBySystem',
|
||||
// CancelledByUser = 'cancelledByUser',
|
||||
// RejectedByRestaurant = 'rejectedByRestaurant',
|
||||
|
||||
// // Active processing statuses
|
||||
// Confirmed = 'confirmed',
|
||||
// Preparing = 'preparing',
|
||||
|
||||
// // Ready for pickup/delivery statuses
|
||||
// ReadyForCustomerPickup = 'readyForCustomerPickup',
|
||||
// ReadyForDineIn = 'readyForDineIn',
|
||||
// ReadyForDeliveryCar = 'readyForDeliveryCar',
|
||||
// ReadyForDeliveryCourier = 'readyForDeliveryCourier',
|
||||
|
||||
// Shipping = 'shipping',
|
||||
// // Final status
|
||||
// Delivered = 'delivered',
|
||||
// }
|
||||
export enum OrderStatus {
|
||||
// Initial status
|
||||
Pending = 'pending',
|
||||
|
||||
// Cancellation statuses
|
||||
CancelledBySystem = 'cancelledBySystem',
|
||||
CancelledByUser = 'cancelledByUser',
|
||||
RejectedByRestaurant = 'rejectedByRestaurant',
|
||||
|
||||
// Active processing statuses
|
||||
Confirmed = 'confirmed',
|
||||
Preparing = 'preparing',
|
||||
|
||||
// Ready for pickup/delivery statuses
|
||||
ReadyForCustomerPickup = 'readyForCustomerPickup',
|
||||
ReadyForDineIn = 'readyForDineIn',
|
||||
ReadyForDeliveryCar = 'readyForDeliveryCar',
|
||||
ReadyForDeliveryCourier = 'readyForDeliveryCourier',
|
||||
|
||||
Shipping = 'shipping',
|
||||
// Final status
|
||||
Delivered = 'delivered',
|
||||
CREATED = 'created',
|
||||
PENDING_PAYMENT = 'pendingPayment',
|
||||
PAID = 'paid',
|
||||
CONFIRMED = 'confirmed',
|
||||
PREPARING = 'preparing',
|
||||
READY = 'ready',
|
||||
SHIPPED = 'shipped',
|
||||
COMPLETED = 'completed',
|
||||
CANCELED = 'canceled',
|
||||
FAILED = 'failed',
|
||||
REFUNDED = 'refunded',
|
||||
}
|
||||
|
||||
export interface OrderCouponDetail {
|
||||
|
||||
@@ -3,9 +3,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaymentMethodEnum, PaymentGatewayEnum } from '../interface/payment';
|
||||
|
||||
export class CreatePaymentMethodDto {
|
||||
@ApiProperty({ description: 'Payment method title' })
|
||||
@IsString()
|
||||
title!: string;
|
||||
|
||||
@ApiProperty({ description: 'Payment method', enum: PaymentMethodEnum })
|
||||
@IsEnum(PaymentMethodEnum)
|
||||
|
||||
@@ -8,8 +8,6 @@ export class PaymentMethod extends BaseEntity {
|
||||
@ManyToOne(() => Restaurant)
|
||||
restaurant!: Restaurant;
|
||||
|
||||
@Property()
|
||||
title!: string;
|
||||
|
||||
@Enum(() => PaymentMethodEnum)
|
||||
method!: PaymentMethodEnum;
|
||||
|
||||
Reference in New Issue
Block a user