diff --git a/src/modules/orders/interface/order-status.ts b/src/modules/orders/interface/order-status.ts index 8c4079e..77caad1 100644 --- a/src/modules/orders/interface/order-status.ts +++ b/src/modules/orders/interface/order-status.ts @@ -4,8 +4,11 @@ export enum OrderStatus { // PendingConfirmation = 'pendingConfirmation', Confirmed = 'confirmed', + Preparing = 'preparing', + RejectedByRestaurant = 'rejectedByRestaurant', CancelledByUser = 'cancelledByUser', + CancelledBySystem = 'cancelledBySystem', ReadyForCustomerPickup = 'readyForCustomerPickup', ReadyForDineIn = 'readyForDineIn', @@ -13,5 +16,4 @@ export enum OrderStatus { ReadyForDeliveryCourier = 'readyForDeliveryCourier', Delivered = 'delivered', - CancelledBySystem = 'cancelledBySystem', } diff --git a/src/modules/orders/orders.service.ts b/src/modules/orders/orders.service.ts index 55a3807..8b56aa3 100644 --- a/src/modules/orders/orders.service.ts +++ b/src/modules/orders/orders.service.ts @@ -252,8 +252,15 @@ export class OrdersService { }); } - findOne(id: number) { - return `This action returns a #${id} order`; + async findOne(id: string): Promise { + const order = await this.orderRepository.findOne( + { id }, + { populate: ['user', 'restaurant', 'deliveryMethod', 'address', 'paymentMethod', 'items', 'items.food'] }, + ); + if (!order) { + throw new NotFoundException('Order not found'); + } + return order; } async acceptOrder(orderId: string) { diff --git a/src/modules/orders/repositories/order.repository.ts b/src/modules/orders/repositories/order.repository.ts new file mode 100644 index 0000000..a7938c9 --- /dev/null +++ b/src/modules/orders/repositories/order.repository.ts @@ -0,0 +1,106 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { FilterQuery } from '@mikro-orm/core'; +import { Order } from '../entities/order.entity'; +import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; +import { OrderStatus } from '../interface/order-status'; +import { PaymentStatusEnum } from '../../payments/interface/payment'; + +type FindOrdersOpts = { + page?: number; + limit?: number; + status?: OrderStatus; + paymentStatus?: PaymentStatusEnum; + search?: string; + startDate?: string; + endDate?: string; + orderBy?: string; + order?: 'asc' | 'desc'; +}; + +@Injectable() +export class OrderRepository extends EntityRepository { + constructor(readonly em: EntityManager) { + super(em, Order); + } + + /** + * Find orders with pagination and optional filters. + * Supports: status, paymentStatus, search (orderNumber), date range, ordering. + */ + async findAllPaginated(restId: string, opts: FindOrdersOpts = {}): Promise> { + const { + page = 1, + limit = 10, + status, + paymentStatus, + search, + startDate, + endDate, + orderBy = 'createdAt', + order = 'desc', + } = opts; + + const offset = (page - 1) * limit; + + const where: FilterQuery = { restaurant: { id: restId } }; + + // Filter by status + if (status) { + where.status = status; + } + + // Filter by payment status + if (paymentStatus) { + where.paymentStatus = paymentStatus; + } + + // Filter by date range + if (startDate || endDate) { + where.createdAt = {}; + if (startDate) { + where.createdAt.$gte = new Date(startDate); + } + if (endDate) { + where.createdAt.$lte = new Date(endDate); + } + } + + // 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; + } + + const [data, total] = await this.findAndCount(where, { + limit, + offset, + orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, + populate: ['user', 'restaurant', 'deliveryMethod', 'address', 'paymentMethod', 'items', 'items.food'], + }); + + const totalPages = Math.ceil(total / limit); + + return { + data, + meta: { + total, + page, + limit, + totalPages, + }, + }; + } +}