orders pagination

This commit is contained in:
2025-12-06 10:36:51 +03:30
parent 63ab227290
commit 8336e7f81a
5 changed files with 176 additions and 26 deletions
+127
View File
@@ -0,0 +1,127 @@
import { IsOptional, IsString, IsNumber, Min, IsIn, IsEnum, IsDateString } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { OrderStatus } from '../interface/order-status';
import { PaymentStatusEnum } from '../../payments/interface/payment';
// Define the valid sort directions
const sortOrderOptions = ['asc', 'desc'] as const;
type SortOrder = (typeof sortOrderOptions)[number];
export class FindOrdersDto {
/**
* The page number to retrieve.
* @default 1
*/
@ApiPropertyOptional({
description: 'Page number',
type: Number,
default: 1,
minimum: 1,
})
@IsOptional()
@IsNumber()
@Min(1)
@Type(() => Number)
page?: number = 1;
/**
* The number of items per page.
* @default 10
*/
@ApiPropertyOptional({
description: 'Number of items per page',
type: Number,
default: 10,
minimum: 1,
})
@IsOptional()
@IsNumber()
@Min(1)
@Type(() => Number)
limit?: number = 10;
/**
* Filter by order status
*/
@ApiPropertyOptional({
description: 'Filter by order status',
enum: OrderStatus,
})
@IsOptional()
@IsEnum(OrderStatus)
status?: OrderStatus;
/**
* Filter by payment status
*/
@ApiPropertyOptional({
description: 'Filter by payment status',
enum: PaymentStatusEnum,
})
@IsOptional()
@IsEnum(PaymentStatusEnum)
paymentStatus?: PaymentStatusEnum;
/**
* Search by order number or user information
*/
@ApiPropertyOptional({
description: 'Search by order number or user information',
type: String,
})
@IsOptional()
@IsString()
search?: string;
/**
* Filter orders from this date (ISO date string)
*/
@ApiPropertyOptional({
description: 'Filter orders from this date (ISO date string)',
type: String,
format: 'date-time',
})
@IsOptional()
@IsDateString()
startDate?: string;
/**
* Filter orders until this date (ISO date string)
*/
@ApiPropertyOptional({
description: 'Filter orders until this date (ISO date string)',
type: String,
format: 'date-time',
})
@IsOptional()
@IsDateString()
endDate?: string;
/**
* The field to sort the results by.
* @default "createdAt"
*/
@ApiPropertyOptional({
description: 'Field to sort by (createdAt, total, orderNumber)',
type: String,
default: 'createdAt',
})
@IsOptional()
@IsString()
orderBy?: string = 'createdAt';
/**
* The direction to sort the results.
* @default "desc"
*/
@ApiPropertyOptional({
description: 'Sort direction (asc or desc)',
enum: sortOrderOptions,
default: 'desc',
})
@IsOptional()
@IsIn(sortOrderOptions)
order?: SortOrder = 'desc';
}
@@ -1,5 +1,8 @@
export enum OrderStatus {
Pending = 'pending',
// PendingConfirmation = 'pendingConfirmation',
Confirmed = 'confirmed',
RejectedByRestaurant = 'rejectedByRestaurant',
CancelledByUser = 'cancelledByUser',
+16 -13
View File
@@ -1,11 +1,12 @@
import { Controller, Get, Post, Param, UseGuards, Patch } from '@nestjs/common';
import { Controller, Get, Post, Param, UseGuards, Patch, Query } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam } from '@nestjs/swagger';
import { OrdersService } from './orders.service';
import { AuthGuard } from '../auth/guards/auth.guard';
import { UserId } from '../../common/decorators/user-id.decorator';
import { RestId } from 'src/common/decorators/rest-id.decorator';
import { AdminAuthGuard } from '../auth/guards/adminAuth.guard';
import { FindOrdersDto } from './dto/find-orders.dto';
@ApiTags('orders')
@Controller()
export class OrdersController {
@@ -22,9 +23,9 @@ export class OrdersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@Get('public/orders')
@ApiOperation({ summary: 'Get all orders' })
findAll(@RestId() restId: string) {
return this.ordersService.findAll(restId);
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
findAll(@RestId() restId: string, @Query() dto: FindOrdersDto) {
return this.ordersService.findAll(restId, dto);
}
@UseGuards(AuthGuard)
@@ -34,12 +35,19 @@ export class OrdersController {
return this.ordersService.findOne(+id);
}
@UseGuards(AuthGuard)
@ApiBearerAuth()
@Patch('public/orders/:id/cancel')
cancelOrder(@Param('id') id: string) {
return this.ordersService.findOne(+id);
}
@UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@Get('admin/orders')
@ApiOperation({ summary: 'Get all orders' })
findAllAdmin(@RestId() restId: string) {
return this.ordersService.findAll(restId);
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
findAllAdmin(@RestId() restId: string, @Query() dto: FindOrdersDto) {
return this.ordersService.findAll(restId, dto);
}
@UseGuards(AdminAuthGuard)
@@ -68,9 +76,4 @@ export class OrdersController {
readyForPickup(@Param('orderId') orderId: string) {
return this.ordersService.readyForDelivery(orderId);
}
// @Delete(':id')
// remove(@Param('id') id: string) {
// return this.ordersService.remove(+id);
// }
}
+3 -1
View File
@@ -14,6 +14,7 @@ import { UtilsModule } from '../utils/utils.module';
import { AuthModule } from '../auth/auth.module';
import { PaymentsModule } from '../payments/payments.module';
import { JwtModule } from '@nestjs/jwt';
import { OrderRepository } from './repositories/order.repository';
@Module({
imports: [
@@ -25,6 +26,7 @@ import { JwtModule } from '@nestjs/jwt';
JwtModule,
],
controllers: [OrdersController],
providers: [OrdersService],
providers: [OrdersService, OrderRepository],
exports: [OrderRepository],
})
export class OrdersModule {}
+27 -12
View File
@@ -16,6 +16,9 @@ import { PaymentsService } from '../payments/services/payments.service';
import { DeliveryMethodEnum } from '../delivery/interface/delivery';
import { Delivery } from '../delivery/entities/delivery.entity';
import { Cron, CronExpression } from '@nestjs/schedule';
import { OrderRepository } from './repositories/order.repository';
import { FindOrdersDto } from './dto/find-orders.dto';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
@Injectable()
export class OrdersService {
@@ -25,6 +28,7 @@ export class OrdersService {
private readonly em: EntityManager,
private readonly cartService: CartService,
private readonly paymentsService: PaymentsService,
private readonly orderRepository: OrderRepository,
// private readonly paymentGatewayService: PaymentGatewayService,
) {}
@@ -234,16 +238,18 @@ export class OrdersService {
};
}
async findAll(restId: string) {
const orders = await this.em.find(
Order,
{ restaurant: { id: restId } },
{ populate: ['user', 'restaurant', 'deliveryMethod', 'address', 'paymentMethod'] },
);
if (!orders) {
throw new NotFoundException('No orders found');
}
return orders;
async findAll(restId: string, dto: FindOrdersDto): Promise<PaginatedResult<Order>> {
return this.orderRepository.findAllPaginated(restId, {
page: dto.page,
limit: dto.limit,
status: dto.status,
paymentStatus: dto.paymentStatus,
search: dto.search,
startDate: dto.startDate,
endDate: dto.endDate,
orderBy: dto.orderBy,
order: dto.order,
});
}
findOne(id: number) {
@@ -299,8 +305,17 @@ export class OrdersService {
return order;
}
remove(id: number) {
return `This action removes a #${id} order`;
async cancelOrder(orderId: string) {
const order = await this.em.findOne(Order, { id: orderId });
if (!order) {
throw new NotFoundException('Order not found');
}
if (order.status !== OrderStatus.Pending) {
throw new BadRequestException('Order is not pending');
}
order.status = OrderStatus.CancelledByUser;
await this.em.persistAndFlush(order);
return order;
}
/**