order
This commit is contained in:
@@ -14,6 +14,7 @@ import { UpdateOrderItemDtoAsAdmin, UpdateOrderItemDtoAsUser } from '../dto/upda
|
|||||||
import { UpdateOrderAsAdminDto } from '../dto/update-order.dto';
|
import { UpdateOrderAsAdminDto } from '../dto/update-order.dto';
|
||||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||||
import { CreatePrintFormDto } from '../dto/create-print-form.dto';
|
import { CreatePrintFormDto } from '../dto/create-print-form.dto';
|
||||||
|
import { FindOrderItemsDto } from '../dto/find-order-items.dto';
|
||||||
|
|
||||||
@ApiTags('orders')
|
@ApiTags('orders')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -22,7 +23,7 @@ export class OrderController {
|
|||||||
constructor(private readonly orderService: OrderService) { }
|
constructor(private readonly orderService: OrderService) { }
|
||||||
|
|
||||||
// ================== Public ===================
|
// ================== Public ===================
|
||||||
|
|
||||||
@Post('public/orders')
|
@Post('public/orders')
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiOperation({ summary: 'create order ' })
|
@ApiOperation({ summary: 'create order ' })
|
||||||
@@ -98,11 +99,19 @@ export class OrderController {
|
|||||||
return this.orderService.createOrderAsAdmin(adminId, body);
|
return this.orderService.createOrderAsAdmin(adminId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('admin/orders')
|
@Get('admin/orders/request')
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
@ApiOperation({ summary: 'Get all order requests with pagination and filters' })
|
||||||
findAllAsAdmin(@Query() dto: FindOrdersDto) {
|
findOrderRequestAsAdmin(@Query() dto: FindOrdersDto) {
|
||||||
return this.orderService.findOrdersAsAdmin(dto);
|
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')
|
@Get('admin/orders/:id')
|
||||||
@@ -112,6 +121,13 @@ export class OrderController {
|
|||||||
return this.orderService.findOrderOrFail(orderId);
|
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')
|
@Get('admin/orders/item/:id')
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@ApiOperation({ summary: 'order item detail' })
|
@ApiOperation({ summary: 'order item detail' })
|
||||||
@@ -119,7 +135,12 @@ export class OrderController {
|
|||||||
return this.orderService.findOrderItemOrFailByItemId(+itemId);
|
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')
|
// @Patch('admin/orders/:orderId')
|
||||||
// @UseGuards(AdminAuthGuard)
|
// @UseGuards(AdminAuthGuard)
|
||||||
// @ApiOperation({ summary: 'Update Order ' })
|
// @ApiOperation({ summary: 'Update Order ' })
|
||||||
|
|||||||
@@ -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';
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ import { OrderItem } from '../entities/order-item.entity';
|
|||||||
import { UpdateOrderAsAdminDto } from '../dto/update-order.dto';
|
import { UpdateOrderAsAdminDto } from '../dto/update-order.dto';
|
||||||
import { CreatePrintFormDto } from '../dto/create-print-form.dto';
|
import { CreatePrintFormDto } from '../dto/create-print-form.dto';
|
||||||
import { Product } from 'src/modules/product/entities/product.entity';
|
import { Product } from 'src/modules/product/entities/product.entity';
|
||||||
|
import { FindOrderItemsDto } from '../dto/find-order-items.dto';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -285,11 +286,21 @@ export class OrderService {
|
|||||||
return orders
|
return orders
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOrdersAsAdmin(dto: FindOrdersDto) {
|
async findOrderRequestsAsAdmin(dto: FindOrdersDto) {
|
||||||
const orders = await this.orderRepository.findAllPaginated(dto)
|
const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: false })
|
||||||
return orders
|
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) {
|
async findPrints(dto: FindOrdersDto) {
|
||||||
const orders = await this.orderRepository.findAllPaginated({ ...dto, statuses: [] })
|
const orders = await this.orderRepository.findAllPaginated({ ...dto, statuses: [] })
|
||||||
return orders
|
return orders
|
||||||
@@ -524,6 +535,10 @@ export class OrderService {
|
|||||||
|
|
||||||
if (status != undefined) {
|
if (status != undefined) {
|
||||||
order.status = status
|
order.status = status
|
||||||
|
|
||||||
|
if (status === OrderStatusEnum.INVOICED && !order.invoicedAt) {
|
||||||
|
order.invoicedAt = new Date()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,88 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
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 { 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()
|
@Injectable()
|
||||||
export class OrderItemRepository extends EntityRepository<OrderItem> {
|
export class OrderItemRepository extends EntityRepository<OrderItem> {
|
||||||
constructor(readonly em: EntityManager) {
|
constructor(readonly em: EntityManager) {
|
||||||
super(em, OrderItem);
|
super(em, OrderItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findAllPaginated(opts: FindOrderItemsOpts): Promise<PaginatedResult<OrderItem>> {
|
||||||
|
const {
|
||||||
|
page = 1,
|
||||||
|
limit = 10,
|
||||||
|
search,
|
||||||
|
orderBy = 'createdAt',
|
||||||
|
order = 'desc',
|
||||||
|
userId,
|
||||||
|
isPrintFormAdded
|
||||||
|
} = opts;
|
||||||
|
|
||||||
|
const offset = (page - 1) * limit;
|
||||||
|
|
||||||
|
const where: FilterQuery<OrderItem> = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { FindOrdersDto } from '../dto/find-orders.dto';
|
|||||||
|
|
||||||
class FindOrdersOpts extends FindOrdersDto {
|
class FindOrdersOpts extends FindOrdersDto {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
isInvoiced?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -17,10 +18,6 @@ export class OrderRepository extends EntityRepository<Order> {
|
|||||||
super(em, Order);
|
super(em, Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find orders with pagination and optional filters.
|
|
||||||
* Supports: statuses, paymentStatus, search (orderNumber), date range, ordering.
|
|
||||||
*/
|
|
||||||
async findAllPaginated(opts: FindOrdersOpts): Promise<PaginatedResult<Order>> {
|
async findAllPaginated(opts: FindOrdersOpts): Promise<PaginatedResult<Order>> {
|
||||||
const {
|
const {
|
||||||
page = 1,
|
page = 1,
|
||||||
@@ -31,7 +28,8 @@ export class OrderRepository extends EntityRepository<Order> {
|
|||||||
order = 'desc',
|
order = 'desc',
|
||||||
userId,
|
userId,
|
||||||
from,
|
from,
|
||||||
to
|
to,
|
||||||
|
isInvoiced
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
@@ -59,6 +57,14 @@ export class OrderRepository extends EntityRepository<Order> {
|
|||||||
where.user = { id: userId };
|
where.user = { id: userId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isInvoiced != undefined) {
|
||||||
|
if (isInvoiced == true) {
|
||||||
|
where.invoicedAt = { $not: null }
|
||||||
|
}else{
|
||||||
|
where.invoicedAt = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Filter by date range
|
// Filter by date range
|
||||||
if (from || to) {
|
if (from || to) {
|
||||||
where.createdAt = {};
|
where.createdAt = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user