From 14eeba4cfdabf9c55f4670d4b929d8c6d17541e8 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 3 Feb 2026 16:35:17 +0330 Subject: [PATCH] order --- .../order/controllers/order.controller.ts | 33 ++++++-- src/modules/order/dto/find-order-items.dto.ts | 53 ++++++++++++ src/modules/order/providers/order.service.ts | 19 ++++- .../repositories/order-item.repository.ts | 81 ++++++++++++++++++- .../order/repositories/order.repository.ts | 16 ++-- 5 files changed, 187 insertions(+), 15 deletions(-) create mode 100644 src/modules/order/dto/find-order-items.dto.ts diff --git a/src/modules/order/controllers/order.controller.ts b/src/modules/order/controllers/order.controller.ts index f286bc8..aa05775 100644 --- a/src/modules/order/controllers/order.controller.ts +++ b/src/modules/order/controllers/order.controller.ts @@ -14,6 +14,7 @@ import { UpdateOrderItemDtoAsAdmin, UpdateOrderItemDtoAsUser } from '../dto/upda import { UpdateOrderAsAdminDto } from '../dto/update-order.dto'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { CreatePrintFormDto } from '../dto/create-print-form.dto'; +import { FindOrderItemsDto } from '../dto/find-order-items.dto'; @ApiTags('orders') @ApiBearerAuth() @@ -22,7 +23,7 @@ export class OrderController { constructor(private readonly orderService: OrderService) { } // ================== Public =================== - + @Post('public/orders') @UseGuards(AuthGuard) @ApiOperation({ summary: 'create order ' }) @@ -98,11 +99,19 @@ export class OrderController { return this.orderService.createOrderAsAdmin(adminId, body); } - @Get('admin/orders') + @Get('admin/orders/request') @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAllAsAdmin(@Query() dto: FindOrdersDto) { - return this.orderService.findOrdersAsAdmin(dto); + @ApiOperation({ summary: 'Get all order requests with pagination and filters' }) + findOrderRequestAsAdmin(@Query() dto: FindOrdersDto) { + return this.orderService.findOrderRequestsAsAdmin(dto); + } + + + @Get('admin/orders/invoiced') + @UseGuards(AdminAuthGuard) + @ApiOperation({ summary: 'Get all invoiced orders with pagination and filters' }) + findInvoicedOrdersAsAdmin(@Query() dto: FindOrdersDto) { + return this.orderService.findOrderRequestsAsAdmin(dto); } @Get('admin/orders/:id') @@ -112,6 +121,13 @@ export class OrderController { return this.orderService.findOrderOrFail(orderId); } + @Get('admin/orders/item/print') + @UseGuards(AdminAuthGuard) + @ApiOperation({ summary: 'get print orders' }) + getPrintOrders(@Body() dto: FindOrderItemsDto) { + return this.orderService.findPrintOrderItemsAsAdmin(dto); + } + @Get('admin/orders/item/:id') @UseGuards(AdminAuthGuard) @ApiOperation({ summary: 'order item detail' }) @@ -119,7 +135,12 @@ export class OrderController { return this.orderService.findOrderItemOrFailByItemId(+itemId); } - + @Get('admin/orders/item/:id/print') + @UseGuards(AdminAuthGuard) + @ApiOperation({ summary: 'create order item print' }) + createOrderItemPrint(@Param('id') itemId: string) { + // return this.orderService.findOrderItemOrFailByItemId(+itemId); + } // @Patch('admin/orders/:orderId') // @UseGuards(AdminAuthGuard) // @ApiOperation({ summary: 'Update Order ' }) diff --git a/src/modules/order/dto/find-order-items.dto.ts b/src/modules/order/dto/find-order-items.dto.ts new file mode 100644 index 0000000..4a6bfa7 --- /dev/null +++ b/src/modules/order/dto/find-order-items.dto.ts @@ -0,0 +1,53 @@ +import { + IsOptional, + IsString, + IsNumber, + Min, + IsIn, + IsEnum, + IsDateString, + IsArray, +} from 'class-validator'; +import { Type, Transform } from 'class-transformer'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { OrderStatusEnum } from '../interface/order.interface'; +import { PaymentStatusEnum } from '../../payment/interface/payment'; + +const sortOrderOptions = ['asc', 'desc'] as const; +type SortOrder = (typeof sortOrderOptions)[number]; + +export class FindOrderItemsDto { + @ApiPropertyOptional({ default: 1, minimum: 1 }) + @IsOptional() + @Type(() => Number) + @IsNumber() + @Min(1) + page: number = 1; + + @ApiPropertyOptional({ default: 10, minimum: 1 }) + @IsOptional() + @Type(() => Number) + @IsNumber() + @Min(1) + limit: number = 10; + + + @ApiPropertyOptional() + @IsOptional() + @IsString() + search?: string; + + + @ApiPropertyOptional({ default: 'createdAt' }) + @IsOptional() + @IsString() + orderBy: string = 'createdAt'; + + @ApiPropertyOptional({ + enum: sortOrderOptions, + default: 'desc', + }) + @IsOptional() + @IsIn(sortOrderOptions) + order: SortOrder = 'desc'; +} diff --git a/src/modules/order/providers/order.service.ts b/src/modules/order/providers/order.service.ts index e29c5a4..257f90d 100644 --- a/src/modules/order/providers/order.service.ts +++ b/src/modules/order/providers/order.service.ts @@ -31,6 +31,7 @@ import { OrderItem } from '../entities/order-item.entity'; import { UpdateOrderAsAdminDto } from '../dto/update-order.dto'; import { CreatePrintFormDto } from '../dto/create-print-form.dto'; import { Product } from 'src/modules/product/entities/product.entity'; +import { FindOrderItemsDto } from '../dto/find-order-items.dto'; @Injectable() @@ -285,11 +286,21 @@ export class OrderService { return orders } - async findOrdersAsAdmin(dto: FindOrdersDto) { - const orders = await this.orderRepository.findAllPaginated(dto) + async findOrderRequestsAsAdmin(dto: FindOrdersDto) { + const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: false }) return orders } + async findInvoicedOrdersAsAdmin(dto: FindOrdersDto) { + const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: true, }) + return orders + } + + async findPrintOrderItemsAsAdmin(dto: FindOrderItemsDto) { + const orderItems = await this.orderItemRepository.findAllPaginated({ ...dto, isPrintFormAdded: true, }) + return orderItems + } + async findPrints(dto: FindOrdersDto) { const orders = await this.orderRepository.findAllPaginated({ ...dto, statuses: [] }) return orders @@ -524,6 +535,10 @@ export class OrderService { if (status != undefined) { order.status = status + + if (status === OrderStatusEnum.INVOICED && !order.invoicedAt) { + order.invoicedAt = new Date() + } } diff --git a/src/modules/order/repositories/order-item.repository.ts b/src/modules/order/repositories/order-item.repository.ts index f2b64e3..bd2b820 100644 --- a/src/modules/order/repositories/order-item.repository.ts +++ b/src/modules/order/repositories/order-item.repository.ts @@ -1,11 +1,88 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { EntityManager, EntityRepository, FilterQuery } from '@mikro-orm/postgresql'; import { OrderItem } from '../entities/order-item.entity'; - +import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; +import { FindOrderItemsDto } from '../dto/find-order-items.dto'; + +class FindOrderItemsOpts extends FindOrderItemsDto { + userId?: string; + isPrintFormAdded?: boolean +}; + @Injectable() export class OrderItemRepository extends EntityRepository { constructor(readonly em: EntityManager) { super(em, OrderItem); } + + async findAllPaginated(opts: FindOrderItemsOpts): Promise> { + const { + page = 1, + limit = 10, + search, + orderBy = 'createdAt', + order = 'desc', + userId, + isPrintFormAdded + } = opts; + + const offset = (page - 1) * limit; + + const where: FilterQuery = {}; + + + + if (userId) { + where.order = { user: { id: userId } }; + } + + if (isPrintFormAdded != undefined) { + if (isPrintFormAdded == true) { + where.printFormCreatedAt = { $not: null } + } else { + where.printFormCreatedAt = null + } + } + + + // Search by order number or user information + if (search) { + const searchPattern = `%${search}%`; + const searchConditions: any[] = [ + { user: { firstName: { $ilike: searchPattern } } }, + { user: { lastName: { $ilike: searchPattern } } }, + { user: { phone: { $ilike: searchPattern } } }, + ]; + + // If search is numeric, also search by orderNumber + const numericSearch = Number(search); + if (!isNaN(numericSearch) && isFinite(numericSearch)) { + searchConditions.push({ orderNumber: numericSearch }); + } + + where.$or = searchConditions; + } + + // First, fetch orders without reviews + const [data, total] = await this.findAndCount(where, { + limit, + offset, + orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, + populate: ['designer', 'product'], + }); + + + const totalPages = Math.ceil(total / limit); + + return { + data, + meta: { + total, + page, + limit, + totalPages, + }, + }; + } } diff --git a/src/modules/order/repositories/order.repository.ts b/src/modules/order/repositories/order.repository.ts index 195cdc1..93ec9bf 100644 --- a/src/modules/order/repositories/order.repository.ts +++ b/src/modules/order/repositories/order.repository.ts @@ -9,6 +9,7 @@ import { FindOrdersDto } from '../dto/find-orders.dto'; class FindOrdersOpts extends FindOrdersDto { userId?: string; + isInvoiced?: boolean; }; @Injectable() @@ -17,10 +18,6 @@ export class OrderRepository extends EntityRepository { super(em, Order); } - /** - * Find orders with pagination and optional filters. - * Supports: statuses, paymentStatus, search (orderNumber), date range, ordering. - */ async findAllPaginated(opts: FindOrdersOpts): Promise> { const { page = 1, @@ -31,7 +28,8 @@ export class OrderRepository extends EntityRepository { order = 'desc', userId, from, - to + to, + isInvoiced } = opts; const offset = (page - 1) * limit; @@ -59,6 +57,14 @@ export class OrderRepository extends EntityRepository { where.user = { id: userId }; } + if (isInvoiced != undefined) { + if (isInvoiced == true) { + where.invoicedAt = { $not: null } + }else{ + where.invoicedAt = null + } + } + // Filter by date range if (from || to) { where.createdAt = {};