From 75a4154f9070f4927d52b961264c0e3c2b1de987 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 19 Feb 2026 12:36:26 +0330 Subject: [PATCH] order --- .../order/controllers/order.controller.ts | 243 ++----- src/modules/order/dto/create-order.dto.ts | 156 +---- .../order/dto/create-print-form.dto.ts | 16 - .../order/dto/update-order-item.dto.ts | 7 - src/modules/order/entities/order.entity.ts | 10 +- src/modules/order/order.module.ts | 9 +- src/modules/order/providers/order.service.ts | 621 +----------------- .../repositories/order-item.repository.ts | 88 --- .../order/repositories/order.repository.ts | 13 +- 9 files changed, 143 insertions(+), 1020 deletions(-) delete mode 100644 src/modules/order/dto/create-print-form.dto.ts delete mode 100644 src/modules/order/dto/update-order-item.dto.ts delete mode 100644 src/modules/order/repositories/order-item.repository.ts diff --git a/src/modules/order/controllers/order.controller.ts b/src/modules/order/controllers/order.controller.ts index 450c660..797f316 100644 --- a/src/modules/order/controllers/order.controller.ts +++ b/src/modules/order/controllers/order.controller.ts @@ -5,16 +5,10 @@ import { AuthGuard } from '../../auth/guards/auth.guard'; import { UserId } from '../../../common/decorators/user-id.decorator'; import { FindOrdersDto } from '../dto/find-orders.dto'; import { - CreateOrderAsUSerDto, CreateOrderItemAsUserDto, - CreateOrderItemDtoAsAdmin, CreateOrderAsAdminDto + CreateOrderAsAdminDto, } from '../dto/create-order.dto'; import { AdminId } from 'src/common/decorators/admin-id.decorator'; -import { AssignDesignerDto } from '../dto/assign-designer.dto'; -import { UpdateOrderItemDtoAsAdmin, UpdateOrderItemDtoAsUser } from '../dto/update-order-item.dto'; -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'; import { Permissions } from 'src/common/decorators/permissions.decorator'; import { PermissionEnum } from 'src/common/enums/permission.enum'; @@ -26,71 +20,20 @@ export class OrderController { // ================== Public =================== - @Post('public/orders') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'create order ' }) - createOrder(@UserId() userId: string, @Body() body: CreateOrderAsUSerDto) { - return this.orderService.createOrderAsUser(userId, body); - } + // @Get('public/orders') + // @UseGuards(AuthGuard) + // @ApiOperation({ summary: 'Get all orders with pagination and filters' }) + // findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) { + // return this.orderService.findOrdersAsUser(userId, dto); + // } - @Delete('public/orders/:id') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Delete order ' }) - deleteOrder(@Param('id') orderId: string, @UserId() userId: string) { - return this.orderService.removeOrderAsUser(userId, orderId); - } + // @Get('public/orders/:id') + // @UseGuards(AuthGuard) + // @ApiOperation({ summary: 'order detail' }) + // orderDetail(@Param('id') orderId: string, @UserId() userId: string) { + // return this.orderService.getOrderAsUser(userId, orderId); + // } - @Get('public/orders') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) { - return this.orderService.findOrdersAsUser(userId, dto); - } - - @Get('public/orders/:id') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'order detail' }) - orderDetail(@Param('id') orderId: string, @UserId() userId: string) { - return this.orderService.getOrderAsUser(userId, orderId); - } - - @Post('public/orders/:id/item') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'add item to order ' }) - addOrderItem(@Param('id') orderId: string, @UserId() userId: string, @Body() body: CreateOrderItemAsUserDto) { - return this.orderService.addOrderItemAsUser(userId, orderId, body); - } - - - @Patch('public/orders/:id/item/:itemId') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Update order item ' }) - updateOrderItem(@Param('id') orderId: string, @UserId() userId: string, @Param('itemId') itemId: string, @Body() body: UpdateOrderItemDtoAsUser) { - return this.orderService.updateOrderItemAsUser(userId, orderId, itemId, body); - } - - - @Patch('public/orders/:orderId/items/:orderItemId/confirm') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Confirm Invoice Item ' }) - confirmOrderItem( - @Param('orderId') orderId: string, - @Param('orderItemId') orderItemId: string, - @UserId() userId: string - ) { - return this.orderService.confirmOrderItem(userId, orderId, orderItemId); - } - - @Delete('public/orders/:orderId/items/:orderItemId') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Remove order Item ' }) - removeOrderItem( - @Param('orderId') orderId: string, - @Param('orderItemId') orderItemId: string, - @UserId() userId: string - ) { - return this.orderService.removeOrderItemAsUser(userId, orderId, orderItemId); - } /*========================== Admin Routes =====================*/ @@ -102,115 +45,71 @@ export class OrderController { return this.orderService.createOrderAsAdmin(adminId, body); } - @Get('admin/orders/request') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.VIEW_REQUESTS) - @ApiOperation({ summary: 'Get all order requests with pagination and filters' }) - findOrderRequestAsAdmin(@Query() dto: FindOrdersDto) { - return this.orderService.findOrderRequestsAsAdmin(dto); - } - // this route permission is handled inside service - @Get('admin/orders/invoiced') - @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'Get all invoiced orders with pagination and filters' }) - findInvoicedOrdersAsAdmin(@Query() dto: FindOrdersDto, @AdminId() adminId: string) { - return this.orderService.findInvoicedOrdersAsAdmin(adminId, dto); - } - - @Get('admin/orders/:id') - @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'order detail' }) - orderDetailAsAdmin(@Param('id') orderId: string) { - return this.orderService.findOrderOrFail(orderId); - } - - @Patch('admin/orders/:id') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'Update Order ' }) - updateOrder(@Param('id') id: string, @Body() dto: UpdateOrderAsAdminDto) { - return this.orderService.updateOrderAsAdmin2(id, dto); - } - - @Delete('admin/orders/:orderId') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.REMOVE_ORDER) - @ApiOperation({ summary: 'Hard Delete Order ' }) - hardDelete(@Param('orderId') orderId: string) { - return this.orderService.hardDeleteOrder(orderId); - } - - // =============== Order Item ================ - - @Get('admin/orders/item/print') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.VIEW_All_ORDERS) - @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' }) - getOrderItem(@Param('id') itemId: string) { - return this.orderService.findOrderItemOrFailByItemId(itemId); - } - - - @Post('admin/orders/:orderId/item/:itemId/designer') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.ASSIGN_DESIGNER) - @ApiOperation({ summary: 'Assign Order Designer ' }) - assignDesigner(@Param('orderId') orderId: string, @Param('itemId') itemId: string, - @Body() body: AssignDesignerDto) { - return this.orderService.assignDesigner(orderId, itemId, body.designerId); - } - - @Patch('admin/orders/:id/item/:itemId/print') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.CREATE_ORDER_PRINT) - @ApiOperation({ summary: 'Create Print form for order item' }) - createPrintForm(@Param('id') id: string, @Param('itemId') itemId: string, @Body() dto: CreatePrintFormDto) { - return this.orderService.createPrintForm(id, itemId, dto); - } - - // @Post('admin/orders/:orderId/invoice') + // @Get('admin/orders/:id') // @UseGuards(AdminAuthGuard) - // @ApiOperation({ summary: 'Create Order invoice ' }) - // createInvoice(@Param('orderId') orderId: string) { - // return this.orderService.createInvoice(orderId); + // @ApiOperation({ summary: 'order detail' }) + // orderDetailAsAdmin(@Param('id') orderId: string) { + // return this.orderService.findOrderOrFail(orderId); + // } + + // @Patch('admin/orders/:id') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.UPDATE_ORDER) + // @ApiOperation({ summary: 'Update Order ' }) + // updateOrder(@Param('id') id: string, @Body() dto: UpdateOrderAsAdminDto) { + // return this.orderService.updateOrderAsAdmin(id, dto); + // } + + // @Delete('admin/orders/:orderId') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.REMOVE_ORDER) + // @ApiOperation({ summary: 'Hard Delete Order ' }) + // hardDelete(@Param('orderId') orderId: string) { + // return this.orderService.hardDeleteOrder(orderId); + // } + + // // =============== Order Item ================ + + + // @Post('admin/orders/:orderId/item/:itemId/designer') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.ASSIGN_DESIGNER) + // @ApiOperation({ summary: 'Assign Order Designer ' }) + // assignDesigner(@Param('orderId') orderId: string, @Param('itemId') itemId: string, + // @Body() body: AssignDesignerDto) { + // return this.orderService.assignDesigner(orderId, itemId, body.designerId); // } - @Delete('admin/orders/:id/items/:itemId') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'Remove order Item ' }) - removeOrderItemAdAdmin( - @Param('id') orderId: string, - @Param('itemId') itemId: string, - ) { - return this.orderService.removeOrderItemAsAdmin(orderId, itemId); - } - @Post('admin/orders/:id/item') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'add item to order ' }) - addOrderItemAsAdmin(@Param('id') orderId: string, @Body() body: CreateOrderItemDtoAsAdmin) { - return this.orderService.addOrderItemAsAdmin(orderId, body); - } + // @Delete('admin/orders/:id/items/:itemId') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.UPDATE_ORDER) + // @ApiOperation({ summary: 'Remove order Item ' }) + // removeOrderItemAdAdmin( + // @Param('id') orderId: string, + // @Param('itemId') itemId: string, + // ) { + // return this.orderService.removeOrderItemAsAdmin(orderId, itemId); + // } + + // @Post('admin/orders/:id/item') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.UPDATE_ORDER) + // @ApiOperation({ summary: 'add item to order ' }) + // addOrderItemAsAdmin(@Param('id') orderId: string, @Body() body: CreateOrderItemDtoAsAdmin) { + // return this.orderService.addOrderItemAsAdmin(orderId, body); + // } - @Patch('admin/orders/:id/item/:itemId') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'Update order item' }) - updateOrderItemAsAdmin(@Param('id') orderId: string, @Param('itemId') itemId: string, - @Body() body: UpdateOrderItemDtoAsAdmin) { - return this.orderService.updateOrderItemAsAdmin(orderId, itemId, body); - } + // @Patch('admin/orders/:id/item/:itemId') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.UPDATE_ORDER) + // @ApiOperation({ summary: 'Update order item' }) + // updateOrderItemAsAdmin(@Param('id') orderId: string, @Param('itemId') itemId: string, + // @Body() body: UpdateOrderItemDtoAsAdmin) { + // return this.orderService.updateOrderItemAsAdmin(orderId, itemId, body); + // } } diff --git a/src/modules/order/dto/create-order.dto.ts b/src/modules/order/dto/create-order.dto.ts index 850e077..3165edf 100644 --- a/src/modules/order/dto/create-order.dto.ts +++ b/src/modules/order/dto/create-order.dto.ts @@ -1,146 +1,46 @@ import { IsString, - IsInt, IsArray, IsNumber, + IsInt, IsNotEmpty, - ArrayMinSize, IsOptional, - IsBoolean, - IsEnum, - IsDateString } from 'class-validator'; -import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IAttachment, IField, OrderItemStatusEnum } from '../interface/order.interface'; - - -export class CreateOrderItemAsUserDto { - @IsString() - @ApiProperty() - productId: string; - - @ApiProperty() - @IsNumber() - quantity: number - - @ApiProperty({ enum: OrderItemStatusEnum }) - @IsEnum(() => OrderItemStatusEnum) - status: OrderItemStatusEnum - - @ApiProperty() - @IsArray() - attributes: IField[] - - @ApiPropertyOptional({ example: 'توضیحات' }) - @IsString() - description: string - - @ApiPropertyOptional({ example: [] }) - @IsArray() - attachments: { url: string, type: string }[] - - -} - -export class CreateOrderItemDtoAsAdmin extends CreateOrderItemAsUserDto { - // @ApiPropertyOptional({ example: [] }) - // @IsArray() - // printAttributes?: IField[] - - // @ApiPropertyOptional({ example: [] }) - // @IsArray() - // printAttachments?: IAttachment[] - - @IsInt() - @ApiProperty() - designerId: string; - - @ApiProperty() - @IsNumber() - unitPrice: number - - @ApiProperty() - @IsNumber() - discount: number - - @ApiPropertyOptional() - @IsString() - adminDescription: string - - @ApiPropertyOptional() - @IsDateString() - confirmedAt: string - -} - -export class CreateOrderAsUSerDto { - @IsArray() - @ApiProperty({ - isArray: true, type: [CreateOrderItemAsUserDto], - example: [ - { - productId: 'sd4', - quantity: 100, - attributes: [ - { - attributeId: 1, - value: 'string | boolean| number' - } - ], - attachments: [ - { url: '', type: '' } - ], - description: '' - } - ] - }) - @IsNotEmpty() - @ArrayMinSize(1, { message: 'At least one item is required' }) - @Type(() => CreateOrderItemAsUserDto) - items: CreateOrderItemAsUserDto[]; -} +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class CreateOrderAsAdminDto { @ApiProperty({ example: '' }) @IsString() @IsNotEmpty() - userId: string + userId: string; - @IsArray() - @ApiProperty({ - isArray: true, type: [CreateOrderItemDtoAsAdmin], example: [ - { - productId: 1, - designerId: '', - attachments: [{ url: '', type: '' }], - quantity: 100, - attributes: [{ fieldId: 10, value: 'blue' }], - unitPrice: 150000, - discount: 20000, - adminDescription: '', - description: 'توضیحات کاربر', - - } - ] - }) - @IsNotEmpty() - @ArrayMinSize(1, { message: 'At least one product is required' }) - @Type(() => CreateOrderItemDtoAsAdmin) - items: CreateOrderItemDtoAsAdmin[]; - - - @ApiProperty({}) + @ApiProperty() @IsString() - @IsOptional() - paymentMethod: string + @IsNotEmpty() + typeId: string; - @ApiProperty({}) - @IsBoolean() - enableTax: boolean - - @ApiProperty({}) + @ApiProperty({ description: 'Estimated days to complete' }) @IsInt() - estimatedDays: number + estimatedDays: number; + @ApiPropertyOptional() + @IsOptional() + @IsString() + productId?: string; + @ApiPropertyOptional() + @IsOptional() + @IsString() + title?: string; +} + +export class CreateOrderItemDtoAsAdmin { + @ApiProperty() + @IsString() + @IsNotEmpty() + productId: string; + + @ApiPropertyOptional() + @IsOptional() + @IsInt() + quantity?: number; } diff --git a/src/modules/order/dto/create-print-form.dto.ts b/src/modules/order/dto/create-print-form.dto.ts deleted file mode 100644 index 532664b..0000000 --- a/src/modules/order/dto/create-print-form.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IsArray, } from 'class-validator'; -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IField } from '../interface/order.interface'; - -export class CreatePrintFormDto { - @ApiPropertyOptional({ - example: [{ - fieldId: 10, value: '' - }] - }) - @IsArray() - printAttributes?: IField[] - -} - - diff --git a/src/modules/order/dto/update-order-item.dto.ts b/src/modules/order/dto/update-order-item.dto.ts deleted file mode 100644 index 41de503..0000000 --- a/src/modules/order/dto/update-order-item.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateOrderItemAsUserDto, CreateOrderItemDtoAsAdmin } from './create-order.dto'; - -export class UpdateOrderItemDtoAsUser extends PartialType(CreateOrderItemAsUserDto) { } -export class UpdateOrderItemDtoAsAdmin extends PartialType(CreateOrderItemDtoAsAdmin) { } - - diff --git a/src/modules/order/entities/order.entity.ts b/src/modules/order/entities/order.entity.ts index 3920a9c..814a607 100644 --- a/src/modules/order/entities/order.entity.ts +++ b/src/modules/order/entities/order.entity.ts @@ -22,11 +22,11 @@ import { OrderType } from './order-type.entity'; export class Order extends BaseEntity { [OptionalProps]?: 'createdAt' | 'deletedAt' | 'orderNumber' - @ManyToOne(() => InvoiceItem) - invoiceItem: InvoiceItem + @ManyToOne(() => InvoiceItem,{nullable:true}) + invoiceItem?: InvoiceItem - @ManyToOne(() => Product) - product: Product; + @ManyToOne(() => Product, { nullable: true }) + product?: Product | null; @PrimaryKey({ type: 'string', columnType: 'char(26)' }) id: string = ulid() @@ -58,4 +58,6 @@ export class Order extends BaseEntity { @Enum(() => OrderStatusEnum) status: OrderStatusEnum = OrderStatusEnum.CREATED; + @Property({ type: 'int', nullable: true }) + estimatedDays?: number; } diff --git a/src/modules/order/order.module.ts b/src/modules/order/order.module.ts index 53f9a3b..c35b94f 100644 --- a/src/modules/order/order.module.ts +++ b/src/modules/order/order.module.ts @@ -3,7 +3,6 @@ import { MikroOrmModule } from '@mikro-orm/nestjs'; import { OrderService } from './providers/order.service'; import { OrderController } from './controllers/order.controller'; import { Order } from './entities/order.entity'; -import { OrderItem } from './entities/order-item.entity'; import { AuthModule } from '../auth/auth.module'; import { PaymentModule } from '../payment/payments.module'; import { JwtModule } from '@nestjs/jwt'; @@ -16,11 +15,11 @@ import { UserModule } from '../user/user.module'; import { UtilsModule } from '../util/utils.module'; import { ProductModule } from '../product/product.module'; import { TicketModule } from '../ticket/ticket.module'; -import { OrderItemRepository } from './repositories/order-item.repository'; +import { OrderType } from './entities/order-type.entity'; @Module({ imports: [ - MikroOrmModule.forFeature([Order, OrderItem]), + MikroOrmModule.forFeature([Order, OrderType]), AuthModule, forwardRef(() => PaymentModule), JwtModule, @@ -33,7 +32,7 @@ import { OrderItemRepository } from './repositories/order-item.repository'; ], controllers: [OrderController], providers: [OrderService, OrderRepository, OrderListeners, - OrdersCrone, OrderItemRepository], - exports: [OrderRepository, OrderService, OrderItemRepository], + OrdersCrone,], + exports: [OrderRepository, OrderService], }) export class OrderModule { } diff --git a/src/modules/order/providers/order.service.ts b/src/modules/order/providers/order.service.ts index 1674b7d..55d4ee6 100644 --- a/src/modules/order/providers/order.service.ts +++ b/src/modules/order/providers/order.service.ts @@ -1,23 +1,14 @@ -import { Injectable, BadRequestException, Logger, BadGatewayException } from '@nestjs/common'; +import { Injectable, BadRequestException, Logger, NotFoundException } from '@nestjs/common'; import { EntityManager } from '@mikro-orm/postgresql'; import { OrderRepository } from '../repositories/order.repository'; import { FindOrdersDto } from '../dto/find-orders.dto'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { CreateOrderAsAdminDto, - CreateOrderAsUSerDto, CreateOrderItemAsUserDto, - CreateOrderItemDtoAsAdmin + CreateOrderItemDtoAsAdmin, } from '../dto/create-order.dto'; import { UserService } from 'src/modules/user/providers/user.service'; -import { - OrderItemStatusEnum, - CreateOrderWithItems, - CreateOrderItemPopulated, - UpdateOrder, - UpdateOrderItem, - OrderStatusEnum -} from '../interface/order.interface'; -import { OrderItemRepository } from '../repositories/order-item.repository'; +import { OrderStatusEnum } from '../interface/order.interface'; 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'; @@ -25,16 +16,14 @@ import { TicketRepository } from 'src/modules/ticket/repositories/tickets.reposi import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; import { Order } from '../entities/order.entity'; import { PaymentRepository } from 'src/modules/payment/repositories/payment.repository'; -import { UpdateOrderItemDtoAsAdmin, UpdateOrderItemDtoAsUser } from '../dto/update-order-item.dto'; -import { Admin } from 'src/modules/admin/entities/admin.entity'; import { AdminService } from 'src/modules/admin/providers/admin.service'; -import { OrderItem } from '../entities/order-item.entity'; import { UpdateOrderAsAdminDto } from '../dto/update-order.dto'; -import { CreatePrintFormDto } from '../dto/create-print-form.dto'; import { FindOrderItemsDto } from '../dto/find-order-items.dto'; import { PermissionService } from 'src/modules/roles/providers/permissions.service'; import { PermissionEnum } from 'src/common/enums/permission.enum'; - +import { User } from 'src/modules/user/entities/user.entity'; +import { OrderType } from '../entities/order-type.entity'; +import { Product } from 'src/modules/product/entities/product.entity'; @Injectable() export class OrderService { @@ -43,7 +32,6 @@ export class OrderService { constructor( private readonly em: EntityManager, private readonly orderRepository: OrderRepository, - private readonly orderItemRepository: OrderItemRepository, private readonly userService: UserService, private readonly adminService: AdminService, private readonly productService: ProductService, @@ -56,581 +44,38 @@ export class OrderService { private readonly permissionService: PermissionService, ) { } - async createOrderAsAdmin(adminId: string, dto: CreateOrderAsAdminDto) { - const order = await this.createOrder({ ...dto, adminId }) - // await this.calculateOrder(order) - // this.em.flush() - this.logger.log("Order created as admin") - return order + async createOrderAsAdmin(adminId: string, dto: CreateOrderAsAdminDto): Promise { + const user = await this.userService.findById(dto.userId); + if (!user) { + throw new NotFoundException(`User with ID ${dto.userId} not found.`); + } - } + const creator = await this.adminService.findOrFail(adminId); - async createOrderAsUser(userId: string, dto: CreateOrderAsUSerDto) { - const order = await this.createOrder({ ...dto, userId }) - this.logger.log("Order created as user") - return order - - } - - async updateOrderAsAdmin(orderId: string, dto: UpdateOrderAsAdminDto) { - const order = await this.findOrderOrFail(orderId) - - // const updateOrder = await this.updateOrder(order, dto) - - // const updateOrder = await this.calculateOrder(order) - - // await this.em.flush() - - this.logger.log("Order updated as admin") - - // return updateOrder - } - - - async updateOrderAsAdmin2(orderId: string, dto: UpdateOrderAsAdminDto) { - const order = await this.findOrderOrFail(orderId) - - const { items, ...rest } = dto - - return this.em.transactional(async (em) => { - - em.assign(order, rest) - - for (const item of items) { - - if (item.id) { // update - const currentItem = order.items.find(i => i.id == item.id) - - if (!currentItem) { - throw new BadGatewayException("Order Item not found") - } - - const { designerId, productId, ...itemRest } = item - - let newProduct = currentItem.product - - if (productId && productId !== currentItem.product.id) { - - newProduct = await this.productService.findOneOrFail(productId) - - } - - let newDesigner: Admin | undefined - - if (designerId && designerId !== currentItem.designer?.id) { - - newDesigner = await this.adminService.findOrFail(designerId) - - } - - em.assign(currentItem, { ...itemRest, designer: newDesigner, product: newProduct }) - - - } else { - - if (!item.productId) { - throw new BadRequestException(`Product id is required for item ${item.id}`) - } - - const product = await this.productService.findOneOrFail(item.productId) - - const newOrderItem = em.create(OrderItem, { - order, - product, - quantity: item.quantity, - unitPrice: item.unitPrice, - - adminDescription: item.adminDescription, - attachments: item.attachments, - attributes: item.attributes, - description: item.description, - discount: item.discount, - confirmedAt: item.confirmedAt, - status: OrderItemStatusEnum.CREATED - }) - - em.persist(newOrderItem) - } + const type = await this.em.findOne(OrderType, { id: dto.typeId }); + if (!type) { + throw new NotFoundException(`Order type with ID ${dto.typeId} not found.`); + } + let product: Product | null = null; + if (dto.productId) { + product = await this.em.findOne(Product, { id: dto.productId }); + if (!product) { + throw new NotFoundException(`Product with ID ${dto.productId} not found.`); } + } - await em.flush() - - this.logger.log(`order #${order.orderNumber} was updated as Admin`) - - return order - }) - + const order = this.em.create(Order, { + user: user as User, + creator, + type, + product: product ?? null, + title: dto.title, + status: OrderStatusEnum.CREATED, + estimatedDays: dto.estimatedDays, + }); + await this.em.persistAndFlush(order); + return order; } - - async addOrderItemAsUser(userId: string, orderId: string, dto: CreateOrderItemAsUserDto) { - - const order = await this.findOrderOrFail(orderId) - - // if (order.status !== OrderStatusEnum.CREATED) { - // throw new BadRequestException(`You can not update when status is ${order.status}`) - // } - - if (order.user.id !== userId) { - throw new BadRequestException(`This order doest belongs to you!`) - } - - const product = await this.productService.findOneOrFail(dto.productId) - - const orderItem = this.createOrderItemEntity(order, { ...dto, product }) - - await this.em.flush() - - return orderItem - } - - async addOrderItemAsAdmin(orderId: string, dto: CreateOrderItemDtoAsAdmin) { - - const order = await this.findOrderOrFail(orderId) - - const product = await this.productService.findOneOrFail(dto.productId) - - let designer: Admin | undefined - if (dto.designerId) { - designer = await this.adminService.findOrFail(dto.designerId) - } - - const orderItem = this.createOrderItemEntity(order, { ...dto, designer, product }) - - await this.em.flush() - - // await this.calculateOrder(undefined, orderId) - - // await this.em.flush() - - return orderItem - } - - async updateOrderItemAsUser(userId: string, orderId: string, itemId: string, dto: UpdateOrderItemDtoAsUser) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - if (orderItem.order.user.id !== userId) { - throw new BadRequestException(`This order doesnt belongs to you`) - } - - // if (orderItem.order.status !== OrderStatusEnum.CREATED) { - // throw new BadRequestException(`You can not update when status is ${orderItem.order.status}`) - // } - - if (orderItem.confirmedAt) { - throw new BadRequestException(`You can not update when item is confirmed`) - } - - const updated = await this.updateOrderItem(orderItem, dto) - - return updated - } - - async updateOrderItemAsAdmin(orderId: string, itemId: string, dto: UpdateOrderItemDtoAsAdmin) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - const updated = await this.updateOrderItem(orderItem, dto) - - // await this.calculateOrder(undefined, orderId) - - // await this.em.flush() - - return updated - } - - async createPrintForm(orderId: string, itemId: string, dto: CreatePrintFormDto) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - const updated = await this.updateOrderItem(orderItem, { ...dto, printFormCreatedAt: new Date() }) - - return updated - } - - async removeOrderItemAsAdmin(orderId: string, itemId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - await this.removeOrderItem(orderItem) - - // await this.calculateOrder(undefined, orderId) - - // await this.em.flush() - - return { message: 'success' } - } - - async removeOrderItemAsUser(userId: string, orderId: string, itemId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - if (orderItem.order.user.id !== userId) { - throw new BadRequestException(`this order is not belongs to you`) - } - - // if (orderItem.order.status !== OrderStatusEnum.CREATED) { - // throw new BadRequestException(`You can not delete when order status is ${orderItem.order.status}`) - // } - - if (orderItem.confirmedAt) { - throw new BadRequestException(`You can not delete when item is confirmed`) - } - - await this.removeOrderItem(orderItem) - - return { message: 'success' } - } - - - async findOrdersAsUser(userId: string, dto: FindOrdersDto) { - const orders = await this.orderRepository.findAllPaginated({ userId, ...dto }) - return orders - } - - async findOrderRequestsAsAdmin(dto: FindOrdersDto) { - const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: false }) - return orders - } - - async findInvoicedOrdersAsAdmin(adminId: string, dto: FindOrdersDto) { - const canSeeAll = await this.adminService.hasPermission(adminId, PermissionEnum.VIEW_All_INVOICED_ORDERS) - - let canSeeAsigned = false - - if (!canSeeAll) { - canSeeAsigned = await this.adminService.hasPermission(adminId, PermissionEnum.VIEW_MY_ASSIGNED_INVOICED_REQUESTS) - } - - if (!canSeeAsigned && !canSeeAll) { - throw new BadRequestException("You dont have Permission") - } - - let designerId = canSeeAll ? undefined : adminId - - const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: true, designerId }) - - 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 - } - - - async confirmOrderItem(userId: string, orderId: string, orderItemId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, orderItemId) - - - if (orderItem.order.user.id !== userId) { - throw new BadRequestException("Order Item does not belong to you") - } - - orderItem.confirmedAt = new Date() - - await this.em.persistAndFlush(orderItem) - - return orderItem - } - - async assignDesigner(orderId: string, orderItemId: string, designerId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, orderItemId) - - const designer = await this.adminRepository.findOne({ id: designerId }) - - if (!designer) { - throw new BadRequestException("designer not found") - } - - orderItem.designer = designer - - await this.em.persistAndFlush(orderItem) - - return orderItem - - } - - async removeOrderAsUser(userId: string, orderId: string) { - const order = await this.findOrderOrFail(orderId) - - if (order.user.id !== userId) { - throw new BadRequestException("this order doesnt belongs to you") - } - - // if (![OrderStatusEnum.CREATED].includes(order.status)) { - // throw new BadRequestException("Order can not be deleted") - // } - - return this.hardDeleteOrder(orderId) - } - - // async updateStatus(orderId: string, newStatus: OrderStatusEnum) { - - // const order = await this.findOrderOrFail(orderId) - - // // order.status = newStatus - - // await this.em.flush() - - // return order - // } - - // async createInvoice(orderId: string) { - // const order = await this.updateStatus(orderId, OrderStatusEnum.INVOICED) - // return order - // } - - async getOrderAsUser(userId: string, orderId: string) { - const order = await this.findOrderOrFail(orderId) - if (order.user.id !== userId) { - throw new BadRequestException("This order is not belongs to you") - } - return order - } - - // ==================== Entity Methods ==================== - - private async createOrderItemEntity(order: Order, dto: CreateOrderItemPopulated, em?: EntityManager) { - const { attributes, description, quantity, attachments, discount, - unitPrice, product, designer, printAttributes, status } = dto - - const eM = em ?? this.em - return eM.create( - OrderItem, - { - designer, - order, - attributes, - description, - quantity, - attachments, - discount, - unitPrice, - product, - printAttributes: printAttributes, - status - }) - } - - // ==================== Core Logic ==================== - - async createOrder(dto: CreateOrderWithItems) { - const { items, userId, enableTax, - estimatedDays, paymentMethod, adminId } = dto - - // Validate and fetch all required entities - const user = await this.userService.findOrFail(userId) - const admin = adminId ? await this.adminService.findOrFail(adminId) : undefined - const products = await this.productService.findProductsByIds(items.map(item => item.productId)) - const desiners = await this.adminService.findByIds(items.map(item => item.designerId).filter(id => id != null)) - - const productMap = new Map( - products.map(p => [Number(p.id), p]) - ); - const designerMap = new Map( - desiners.map(d => [d.id, d]) - ) - - return this.em.transactional(async (em) => { - - const order = em.create(Order, { - creator: admin, - user, - enableTax, - estimatedDays, - paymentMethod, - status: OrderStatusEnum.REQUEST, - }) - - em.persist(order) - - for (const item of items) { - const product = productMap.get(Number(item.productId)) - - if (!product) { - throw new BadRequestException("Product not found!") - } - let designer: undefined | Admin = undefined - - if (item.designerId) { - designer = designerMap.get(item.designerId) - if (!designer) { - throw new BadRequestException("designer not found!") - } - } - - const orderItem = await this.createOrderItemEntity(order, { ...item, designer, product }, em) - - order.items.add(orderItem) - } - - // TODO : calculation must be done after create order - - // await this.calculateOrder(order) - - await em.flush() - - return order - }) - - - } - - async updateOrder(order: Order, param: UpdateOrder) { - const { adminId, enableTax, - estimatedDays, paymentMethod, userId } = param - - - if (userId) { - const user = await this.userService.findOrFail(userId) - order.user = user - } - - - if (adminId != undefined && adminId !== null) { - const admin = await this.adminService.findOrFail(adminId) - order.creator = admin - } - - - if (enableTax != undefined) { - order.enableTax = enableTax - } - - if (estimatedDays != undefined) { - order.estimatedDays = estimatedDays - } - - if (paymentMethod != undefined) { - order.paymentMethod = paymentMethod - } - - // if (status != undefined) { - // order.status = status - - // if (status === OrderStatusEnum.INVOICED && !order.invoicedAt) { - // order.invoicedAt = new Date() - // } - // } - - - - await this.em.persistAndFlush(order) - - return order - } - - async updateOrderItem(orderItem: OrderItem, dto: UpdateOrderItem) { - const { attributes, description, product, quantity, printFormCreatedAt, - attachments, discount, unitPrice, designer, printAttributes, } = dto - - if (product && orderItem.product.id !== product.id) { - orderItem.product = product - } - - if (designer && orderItem.designer?.id !== designer.id) { - orderItem.designer = designer - } - - if (attributes != undefined) { - orderItem.attributes = attributes - } - - if (description != undefined) { - orderItem.description = description - } - if (quantity != undefined) { - orderItem.quantity = quantity - } - - if (attachments != undefined) { - orderItem.attachments = attachments - } - if (discount != undefined) { - orderItem.discount = discount - } - - if (unitPrice != undefined) { - orderItem.unitPrice = unitPrice - } - - if (printFormCreatedAt != undefined) { - orderItem.printFormCreatedAt = printFormCreatedAt - } - - if (printAttributes != undefined) { - orderItem.printAttributes = printAttributes - } - - await this.em.flush() - - return orderItem - } - - async removeOrderItem(orderItem: OrderItem) { - - await this.em.removeAndFlush(orderItem) - - return true - } - - async hardDeleteOrder(orderId: string) { - const order = await this.findOrderOrFail(orderId) - - if (order.payments.length > 0) { - throw new BadRequestException("order with payments can not be deleted!") - } - - await this.em.transactional(async (em) => { - await this.ticketRepository.nativeDelete({ orderItem: { order: { id: orderId } } }) - await this.orderRepository.nativeDelete(order) - // order item will be deleted because cascade is true - - // TODO : images also must be deleted - await em.flush() - }) - - return { message: "Order deleted successfully" } - - } - - // ==================== Helper Methods ==================== - - async findOrderOrFail(orderId: string) { - const order = await this.orderRepository.findOne({ id: orderId }, - { populate: ['items', 'items.product', 'payments', 'user', 'items.designer'] }) - if (!order) { - throw new BadRequestException('Order not found') - } - return order - } - - async findOrderItemOrFail(orderId: string, itemId: string) { - const orderItem = await this.orderItemRepository.findOne({ id: itemId, order: { id: orderId } }, - { populate: ['order', 'order.user'] }) - if (!orderItem) { - throw new BadRequestException('Order item not found') - } - return orderItem - } - - async findOrderItemOrFailByItemId(itemId: string) { - const orderItem = await this.orderItemRepository.findOne({ id: itemId }, - { populate: ['order', 'order.user'] }) - if (!orderItem) { - throw new BadRequestException('Order item not found') - } - return orderItem - } - } diff --git a/src/modules/order/repositories/order-item.repository.ts b/src/modules/order/repositories/order-item.repository.ts deleted file mode 100644 index bd2b820..0000000 --- a/src/modules/order/repositories/order-item.repository.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Injectable } from '@nestjs/common'; -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 c72fef3..657f08c 100644 --- a/src/modules/order/repositories/order.repository.ts +++ b/src/modules/order/repositories/order.repository.ts @@ -59,17 +59,6 @@ export class OrderRepository extends EntityRepository { where.user = { id: userId }; } - if (isInvoiced != undefined) { - if (isInvoiced == true) { - where.invoicedAt = { $not: null } - } else { - where.invoicedAt = null - } - } - - if (designerId) { - where.items = { $some: { designer: { id: designerId } } } - } // Filter by date range if (from || to) { where.createdAt = {}; @@ -104,7 +93,7 @@ export class OrderRepository extends EntityRepository { limit, offset, orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, - populate: ['items', 'items.product', 'user'], + populate: ['user'], }); // Collect all (orderId, productId) pairs for efficient review lookup