order
This commit is contained in:
@@ -9,6 +9,7 @@ import { OrderStatus } from '../interface/order.interface';
|
|||||||
import { UpdateOrderStatusDto } from '../dto/update-order-status.dto';
|
import { UpdateOrderStatusDto } from '../dto/update-order-status.dto';
|
||||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||||
import { Permission } from 'src/common/enums/permission.enum';
|
import { Permission } from 'src/common/enums/permission.enum';
|
||||||
|
import { CreateOrderDto } from '../dto/create-order.dto';
|
||||||
|
|
||||||
@ApiTags('orders')
|
@ApiTags('orders')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -16,13 +17,13 @@ import { Permission } from 'src/common/enums/permission.enum';
|
|||||||
export class OrdersController {
|
export class OrdersController {
|
||||||
constructor(private readonly ordersService: OrdersService) { }
|
constructor(private readonly ordersService: OrdersService) { }
|
||||||
|
|
||||||
|
@Post('public/checkout')
|
||||||
// @UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
// @Post('public/checkout')
|
@ApiOperation({ summary: 'Checkout : create order ' })
|
||||||
|
@ApiBody({ type: CreateOrderDto })
|
||||||
// @ApiOperation({ summary: 'Checkout : create order and payment record' })
|
checkout(@UserId() userId: string, @Body() body: CreateOrderDto) {
|
||||||
// checkout(@UserId() userId: string,) {
|
return this.ordersService.createOrder(userId, body);
|
||||||
// return this.ordersService.checkout(userId);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// @UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
// @Get('public/orders')
|
// @Get('public/orders')
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { IsString, IsOptional, IsBoolean, IsInt, Min, IsArray, IsNumber } from 'class-validator';
|
||||||
|
import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
|
export class CreateOrderDto {
|
||||||
|
@IsArray()
|
||||||
|
@ApiProperty({ isArray: true })
|
||||||
|
items: CreateOrderItemDto[];
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
content: string
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
@IsString({ each: true })
|
||||||
|
attachments: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateOrderItemDto {
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@ApiProperty()
|
||||||
|
productId: bigint;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
quantity: number
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
attributesValues: string[]
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Entity, Enum, Index, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core';
|
import { Entity, Enum, Index, ManyToOne, OneToMany, PrimaryKey, 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 'src/modules/product/entities/product.entity';
|
import { Product } from 'src/modules/product/entities/product.entity';
|
||||||
@@ -10,13 +10,16 @@ import { OrderItemStatus } from '../interface/order.interface';
|
|||||||
export class OrderItem extends BaseEntity {
|
export class OrderItem extends BaseEntity {
|
||||||
@PrimaryKey({ type: 'bigint', autoincrement: true })
|
@PrimaryKey({ type: 'bigint', autoincrement: true })
|
||||||
id: bigint
|
id: bigint
|
||||||
|
|
||||||
@ManyToOne(() => Order)
|
@ManyToOne(() => Order)
|
||||||
order!: Order;
|
order!: Order;
|
||||||
|
|
||||||
@ManyToOne(() => Product)
|
@ManyToOne(() => Product)
|
||||||
product!: Product;
|
product!: Product;
|
||||||
|
|
||||||
|
@Property({ type: 'json' })
|
||||||
|
attributesValues: string[]
|
||||||
|
|
||||||
@Property({ type: 'int' })
|
@Property({ type: 'int' })
|
||||||
quantity!: number;
|
quantity!: number;
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ import { ulid } from 'ulid';
|
|||||||
@Index({ properties: ['user', 'status'] })
|
@Index({ properties: ['user', 'status'] })
|
||||||
@Index({ properties: ['status'] })
|
@Index({ properties: ['status'] })
|
||||||
export class Order extends BaseEntity {
|
export class Order extends BaseEntity {
|
||||||
@PrimaryKey({type:'string',columnType:'char(26)'})
|
@PrimaryKey({ type: 'string', columnType: 'char(26)' })
|
||||||
id:string=ulid()
|
id: string = ulid()
|
||||||
|
|
||||||
@ManyToOne(() => User)
|
@ManyToOne(() => User)
|
||||||
user!: User;
|
user!: User;
|
||||||
|
|
||||||
@@ -38,8 +38,8 @@ export class Order extends BaseEntity {
|
|||||||
})
|
})
|
||||||
items = new Collection<OrderItem>(this);
|
items = new Collection<OrderItem>(this);
|
||||||
|
|
||||||
@Property({ nullable: true })
|
// @Property({ nullable: true })
|
||||||
userAddress?: string | null;
|
// userAddress?: string | null;
|
||||||
|
|
||||||
@Property({ type: 'bigint', autoincrement: true })
|
@Property({ type: 'bigint', autoincrement: true })
|
||||||
orderNumber: number;
|
orderNumber: number;
|
||||||
@@ -52,8 +52,8 @@ export class Order extends BaseEntity {
|
|||||||
subTotal!: number;
|
subTotal!: number;
|
||||||
|
|
||||||
|
|
||||||
@Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
|
// @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
|
||||||
deliveryFee: number = 0;
|
// deliveryFee: number = 0;
|
||||||
|
|
||||||
@Property({ type: 'decimal', precision: 10, scale: 0 })
|
@Property({ type: 'decimal', precision: 10, scale: 0 })
|
||||||
total!: number;
|
total!: number;
|
||||||
@@ -64,12 +64,11 @@ export class Order extends BaseEntity {
|
|||||||
@Property({ type: 'decimal', precision: 10, scale: 0 })
|
@Property({ type: 'decimal', precision: 10, scale: 0 })
|
||||||
balance!: number;
|
balance!: number;
|
||||||
|
|
||||||
@Property({ type: 'text', nullable: true })
|
// @Property({ type: 'text', nullable: true })
|
||||||
description?: string;
|
// description?: string;
|
||||||
|
|
||||||
|
|
||||||
@Enum(() => OrderStatus)
|
@Enum(() => OrderStatus)
|
||||||
status!: OrderStatus;
|
status!: OrderStatus;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ export enum OrderStatus {
|
|||||||
|
|
||||||
export enum OrderItemStatus {
|
export enum OrderItemStatus {
|
||||||
PENDING = 'pending',
|
PENDING = 'pending',
|
||||||
VERIFIED = 'verified',
|
CONFIRMED = 'confirmed',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,35 +3,106 @@ 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 '../../user/entities/user.entity';
|
import { User } from '../../user/entities/user.entity';
|
||||||
|
|
||||||
import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment';
|
import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment';
|
||||||
|
|
||||||
import { PaymentsService } from '../../payment/services/payments.service';
|
import { PaymentsService } from '../../payment/services/payments.service';
|
||||||
|
|
||||||
import { OrderRepository } from '../repositories/order.repository';
|
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/payment/entities/payment.entity';
|
import { Payment } from 'src/modules/payment/entities/payment.entity';
|
||||||
|
|
||||||
|
|
||||||
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';
|
||||||
import { OrderMessage } from 'src/common/enums/message.enum';
|
import { OrderMessage } from 'src/common/enums/message.enum';
|
||||||
|
import { CreateOrderDto } from '../dto/create-order.dto';
|
||||||
|
import { UserService } from 'src/modules/user/providers/user.service';
|
||||||
|
import { OrderItemStatus, OrderStatus } from '../interface/order.interface';
|
||||||
|
import { OrderItemRepository } from '../repositories/order-item.repository';
|
||||||
|
import { ProductService } from 'src/modules/product/providers/product.service';
|
||||||
|
import { ProductRepository } from 'src/modules/product/repositories/product.repository';
|
||||||
|
import { TicketService } from 'src/modules/ticket/providers/tickets.service';
|
||||||
|
import { TicketRepository } from 'src/modules/ticket/repositories/tickets.repository';
|
||||||
|
import { TicketStatus } from 'src/modules/ticket/enums/ticket-status.enum';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrdersService {
|
export class OrdersService {
|
||||||
private readonly logger = new Logger(OrdersService.name);
|
private readonly logger = new Logger(OrdersService.name);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
|
|
||||||
private readonly orderRepository: OrderRepository,
|
private readonly orderRepository: OrderRepository,
|
||||||
|
private readonly orderItemRepository: OrderItemRepository,
|
||||||
private readonly paymentsService: PaymentsService,
|
private readonly paymentsService: PaymentsService,
|
||||||
|
private readonly userService: UserService,
|
||||||
|
private readonly productService: ProductService,
|
||||||
|
private readonly productRepository: ProductRepository,
|
||||||
|
private readonly ticketRepository: TicketRepository,
|
||||||
|
private readonly ticketService: TicketService,
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
async createOrder(userId: string, dto: CreateOrderDto) {
|
||||||
|
const { attachments, content, items } = dto
|
||||||
|
this.em.transactional(async (em) => {
|
||||||
|
const user = await this.userService.findById(userId)
|
||||||
|
if (!user) {
|
||||||
|
throw new BadRequestException("user not found")
|
||||||
|
}
|
||||||
|
const order = this.orderRepository.create({
|
||||||
|
user,
|
||||||
|
discount: 0,
|
||||||
|
subTotal: 0,
|
||||||
|
orderNumber: 1,
|
||||||
|
total: 0,
|
||||||
|
paidAmount: 0,
|
||||||
|
balance: 0,
|
||||||
|
status: OrderStatus.NEW
|
||||||
|
})
|
||||||
|
|
||||||
|
em.persist(order)
|
||||||
|
|
||||||
|
const productIds = items.map(item => item.productId)
|
||||||
|
const products = await this.productRepository.find({
|
||||||
|
id: { $in: productIds }
|
||||||
|
})
|
||||||
|
if (productIds.length !== products.length) {
|
||||||
|
throw new BadRequestException("some products not found")
|
||||||
|
}
|
||||||
|
items.forEach(item => {
|
||||||
|
const product = products.find(p => p.id == item.productId)
|
||||||
|
if (!product) {
|
||||||
|
throw new BadRequestException(`product ${product} not found`)
|
||||||
|
}
|
||||||
|
const orderItem = this.orderItemRepository.create({
|
||||||
|
order,
|
||||||
|
attributesValues: item.attributesValues,
|
||||||
|
discount: 0,
|
||||||
|
product,
|
||||||
|
quantity: item.quantity,
|
||||||
|
status: OrderItemStatus.PENDING,
|
||||||
|
unitPrice: 0,
|
||||||
|
totalPrice: 0,
|
||||||
|
})
|
||||||
|
em.persist(orderItem)
|
||||||
|
});
|
||||||
|
|
||||||
|
const ticket = this.ticketRepository.create({
|
||||||
|
content,
|
||||||
|
user,
|
||||||
|
attachments,
|
||||||
|
status: TicketStatus.PENDING
|
||||||
|
})
|
||||||
|
em.persist(ticket)
|
||||||
|
|
||||||
|
em.flush()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
|
import { OrderItem } from '../entities/order-item.entity';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OrderItemRepository extends EntityRepository<OrderItem> {
|
||||||
|
constructor(readonly em: EntityManager) {
|
||||||
|
super(em, OrderItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,24 +7,25 @@ import {
|
|||||||
Collection,
|
Collection,
|
||||||
Cascade,
|
Cascade,
|
||||||
Enum,
|
Enum,
|
||||||
|
PrimaryKey,
|
||||||
} from '@mikro-orm/core';
|
} from '@mikro-orm/core';
|
||||||
|
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
import { User } from "../../user/entities/user.entity";
|
import { User } from "../../user/entities/user.entity";
|
||||||
import { TicketStatus } from "../enums/ticket-status.enum";
|
import { TicketStatus } from "../enums/ticket-status.enum";
|
||||||
import { Product } from 'src/modules/product/entities/product.entity';
|
// import { Product } from 'src/modules/product/entities/product.entity';
|
||||||
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Ticket extends BaseEntity {
|
export class Ticket extends BaseEntity {
|
||||||
@Property({ type: "bigint", primary: true })
|
@PrimaryKey({ type: "bigint", autoincrement: true })
|
||||||
id: number;
|
id: bigint;
|
||||||
|
|
||||||
@Enum(() => TicketStatus)
|
@Enum(() => TicketStatus)
|
||||||
status: TicketStatus;
|
status: TicketStatus;
|
||||||
// TODO : create relation
|
|
||||||
@Property({ type: 'json' })
|
@Property({ type: 'text' })
|
||||||
products: Product | null;
|
content: string;
|
||||||
|
|
||||||
@ManyToOne(() => Admin)
|
@ManyToOne(() => Admin)
|
||||||
admin: Admin | null;
|
admin: Admin | null;
|
||||||
@@ -35,6 +36,9 @@ export class Ticket extends BaseEntity {
|
|||||||
@ManyToOne(() => Ticket)
|
@ManyToOne(() => Ticket)
|
||||||
parent: Ticket | null;
|
parent: Ticket | null;
|
||||||
|
|
||||||
|
@OneToMany(()=>Ticket,(ticket)=>ticket.id)
|
||||||
|
children=new Collection<Ticket>(this)
|
||||||
|
|
||||||
@Property({ type: 'json' })
|
@Property({ type: 'json' })
|
||||||
attachments: string[]
|
attachments: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import dayjs from "dayjs";
|
|||||||
// import { TicketsRepository } from "../repositories/tickets.repository";
|
// import { TicketsRepository } from "../repositories/tickets.repository";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TicketsService {
|
export class TicketService {
|
||||||
private readonly logger = new Logger(TicketsService.name);
|
private readonly logger = new Logger(TicketService.name);
|
||||||
|
|
||||||
// constructor(
|
// constructor(
|
||||||
// @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue,
|
// @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue,
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
|||||||
import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto";
|
import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto";
|
||||||
import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto";
|
import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto";
|
||||||
import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto";
|
import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto";
|
||||||
import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto";
|
import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto";
|
||||||
import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto";
|
import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto";
|
||||||
import { TicketsService } from "./providers/tickets.service";
|
import { TicketService } from "./providers/tickets.service";
|
||||||
|
|
||||||
|
|
||||||
import { ParamDto } from "../../common/DTO/param.dto";
|
import { ParamDto } from "../../common/DTO/param.dto";
|
||||||
@@ -15,7 +15,7 @@ import { ParamDto } from "../../common/DTO/param.dto";
|
|||||||
@ApiTags("Tickets")
|
@ApiTags("Tickets")
|
||||||
|
|
||||||
export class TicketsController {
|
export class TicketsController {
|
||||||
constructor(private readonly ticketsService: TicketsService) {}
|
constructor(private readonly ticketsService: TicketService) { }
|
||||||
|
|
||||||
//----------------- ticket categories -------------------
|
//----------------- ticket categories -------------------
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { Module } from "@nestjs/common";
|
|||||||
import { TICKET } from "./constant";
|
import { TICKET } from "./constant";
|
||||||
import { Ticket } from "./entities/ticket.entity";
|
import { Ticket } from "./entities/ticket.entity";
|
||||||
|
|
||||||
import { TicketsService } from "./providers/tickets.service";
|
import { TicketService } from "./providers/tickets.service";
|
||||||
|
|
||||||
import { TicketRepository } from "./repositories/tickets.repository";
|
import { TicketRepository } from "./repositories/tickets.repository";
|
||||||
import { TicketsController } from "./tickets.controller";
|
import { TicketsController } from "./tickets.controller";
|
||||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||||
|
|
||||||
@@ -17,8 +17,8 @@ import { MikroOrmModule } from "@mikro-orm/nestjs";
|
|||||||
MikroOrmModule.forFeature([Ticket]),
|
MikroOrmModule.forFeature([Ticket]),
|
||||||
BullModule.registerQueue({ name: TICKET.QUEUE_NAME }),
|
BullModule.registerQueue({ name: TICKET.QUEUE_NAME }),
|
||||||
],
|
],
|
||||||
providers: [TicketsService, TicketRepository],
|
providers: [TicketService, TicketRepository],
|
||||||
controllers: [TicketsController],
|
controllers: [TicketsController],
|
||||||
exports: [TicketsService],
|
exports: [TicketService],
|
||||||
})
|
})
|
||||||
export class TicketsModule { }
|
export class TicketsModule { }
|
||||||
|
|||||||
Reference in New Issue
Block a user