From e53c574931efe4643a0e92958ddad5d984c162f2 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 22 Jan 2026 11:24:11 +0330 Subject: [PATCH] add order item --- .../order/controllers/order.controller.ts | 10 ++- src/modules/order/dto/add-order-item.dto.ts | 47 ++++++++++++++ src/modules/order/dto/create-invoice.dto.ts | 8 ++- .../order/entities/order-item.entity.ts | 7 +- src/modules/order/providers/order.service.ts | 65 +++++++++++++++---- 5 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 src/modules/order/dto/add-order-item.dto.ts diff --git a/src/modules/order/controllers/order.controller.ts b/src/modules/order/controllers/order.controller.ts index e655d0e..f8911d5 100644 --- a/src/modules/order/controllers/order.controller.ts +++ b/src/modules/order/controllers/order.controller.ts @@ -11,6 +11,7 @@ import { CreateInvoiceDto } from '../dto/create-invoice.dto'; import { AdminId } from 'src/common/decorators/admin-id.decorator'; import { CreateOrderAsAdminDto } from '../dto/create-order-as-admin.dto'; import { AssignDesignerDto } from '../dto/assign-designer.dto'; +import { AddOrderItemDto } from '../dto/add-order-item.dto'; @ApiTags('orders') @ApiBearerAuth() @@ -61,7 +62,14 @@ export class OrderController { hardDelete(@Param('orderId') orderId: string) { return this.orderService.hardDeleteOrder(orderId); } - + + @Post('admin/orders/:orderId/add-item') + @UseGuards(AuthGuard) + @ApiOperation({ summary: 'add item to Order ' }) + addItem(@Param('orderId') orderId: string, @Body() body: AddOrderItemDto) { + return this.orderService.addOrderItem(orderId, body); + } + @Post('admin/orders/:orderId/create-invoice') @UseGuards(AuthGuard) @ApiOperation({ summary: 'Create invoice for new order' }) diff --git a/src/modules/order/dto/add-order-item.dto.ts b/src/modules/order/dto/add-order-item.dto.ts new file mode 100644 index 0000000..62ebc8d --- /dev/null +++ b/src/modules/order/dto/add-order-item.dto.ts @@ -0,0 +1,47 @@ +import { + IsArray, IsNumber, + IsNotEmpty, + ArrayMinSize +} from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; + +export class InvoiceItemDto { + @IsNumber() + @ApiProperty() + productId: number; + + @ApiProperty() + @IsArray() + attributesValues: string[] + + @ApiProperty() + @IsNumber() + quantity: number + + @ApiProperty() + @IsNumber() + unitPrice: number + + @ApiProperty() + @IsNumber() + discount: number + +} + +export class AddOrderItemDto { + @IsArray() + @ApiProperty({ + isArray: true, type: [InvoiceItemDto], example: [ + { + orderItemId: 1, + unitPrice: 100, + } + ] + }) + @IsNotEmpty() + @ArrayMinSize(1, { message: 'At least one product is required' }) + @Type(() => InvoiceItemDto) + items: InvoiceItemDto[]; +} + diff --git a/src/modules/order/dto/create-invoice.dto.ts b/src/modules/order/dto/create-invoice.dto.ts index 58388ca..00279ff 100644 --- a/src/modules/order/dto/create-invoice.dto.ts +++ b/src/modules/order/dto/create-invoice.dto.ts @@ -15,6 +15,10 @@ export class InvoiceItemDto { @IsNumber() unitPrice: number + @ApiProperty() + @IsNumber() + discount: number + } export class CreateInvoiceDto { @@ -32,8 +36,6 @@ export class CreateInvoiceDto { @Type(() => InvoiceItemDto) items: InvoiceItemDto[]; - @ApiProperty() - @IsNumber() - discount: number + } diff --git a/src/modules/order/entities/order-item.entity.ts b/src/modules/order/entities/order-item.entity.ts index caca531..e0f00a9 100644 --- a/src/modules/order/entities/order-item.entity.ts +++ b/src/modules/order/entities/order-item.entity.ts @@ -26,9 +26,14 @@ export class OrderItem extends BaseEntity { @Property({ type: 'decimal', precision: 10, scale: 0 }) unitPrice!: number; + @Property({ type: 'decimal', precision: 10, scale: 0 }) + subTotal!: number; + + @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) + discount: number = 0; @Property({ type: 'decimal', precision: 10, scale: 0 }) - totalPrice!: number; + total!: number; @Property({ nullable: true }) description?: string; diff --git a/src/modules/order/providers/order.service.ts b/src/modules/order/providers/order.service.ts index 240f295..bef5424 100644 --- a/src/modules/order/providers/order.service.ts +++ b/src/modules/order/providers/order.service.ts @@ -1,14 +1,9 @@ import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common'; import { EntityManager } from '@mikro-orm/postgresql'; import { OrderItem } from '../entities/order-item.entity'; -import { PaymentService } from '../../payment/services/payments.service'; import { OrderRepository } from '../repositories/order.repository'; import { FindOrdersDto } from '../dto/find-orders.dto'; -import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; -import { Payment } from 'src/modules/payment/entities/payment.entity'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events'; -import { OrderMessage } from 'src/common/enums/message.enum'; import { CreateOrderDto } from '../dto/create-order.dto'; import { UserService } from 'src/modules/user/providers/user.service'; import { OrderItemStatus, OrderStatusEnum } from '../interface/order.interface'; @@ -20,6 +15,7 @@ import { TicketRepository } from 'src/modules/ticket/repositories/tickets.reposi import { CreateInvoiceDto } from '../dto/create-invoice.dto'; import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; import { CreateOrderAsAdminDto } from '../dto/create-order-as-admin.dto'; +import { AddOrderItemDto } from '../dto/add-order-item.dto'; @Injectable() @@ -81,7 +77,9 @@ export class OrderService { quantity: item.quantity, status: OrderItemStatus.PENDING, unitPrice: 0, - totalPrice: 0, + total: 0, + subTotal: 0, + discount: 0 }) em.persist(orderItem) }); @@ -159,7 +157,9 @@ export class OrderService { quantity: item.quantity, status: OrderItemStatus.CONFIRMED, unitPrice: item.unitPrice, - totalPrice: item.unitPrice * item.quantity, + total: item.unitPrice * item.quantity, + discount: 0, + subTotal: 0 }) em.persist(orderItem) @@ -200,7 +200,7 @@ export class OrderService { } async createInvoice(orderId: string, dto: CreateInvoiceDto) { - const { items, discount } = dto + const { items } = dto const targetOrder = await this.findOneOrFail(orderId) @@ -212,6 +212,7 @@ export class OrderService { const order = await this.em.transactional(async (em) => { let subTotal = 0 + let totalDiscount = 0 // Calculate order item finnicials for (let orderItem of targetOrder.items) { @@ -222,9 +223,10 @@ export class OrderService { } // TODO use Deciaml js to calculation orderItem.unitPrice = found.unitPrice - orderItem.totalPrice = (found.unitPrice) * orderItem.quantity + orderItem.subTotal = (found.unitPrice) * orderItem.quantity - subTotal += orderItem.totalPrice + + subTotal += orderItem.subTotal em.persist(orderItem) @@ -232,10 +234,10 @@ export class OrderService { // calculate order financials targetOrder.subTotal = subTotal - targetOrder.discount = discount + targetOrder.discount = totalDiscount - targetOrder.total = subTotal - discount - targetOrder.balance = subTotal - discount + targetOrder.total = subTotal - totalDiscount + targetOrder.balance = subTotal - totalDiscount // change status targetOrder.status = OrderStatusEnum.INVOICED @@ -311,4 +313,41 @@ export class OrderService { return { message: "Order deleted successfully" } } + + async addOrderItem(orderId: string, dto: AddOrderItemDto) { + const order = await this.orderRepository.findOne({ id: orderId }) + if (!order) { + throw new BadRequestException("Order not Found!") + } + const { items } = dto + + items.forEach(async item => { + const found = order.items.find((it => Number(it.product.id) === item.productId)) + + if (found) { + throw new BadRequestException("Product already exist!") + } + const product = await this.productRepository.findOne({ id: item.productId }) + if (!product) { + throw new BadRequestException("Product not found!") + } + const newOrderItem = this.orderItemRepository.create({ + discount: item.discount, + product, + unitPrice: item.unitPrice, + total: (item.unitPrice - item.discount) * item.quantity, + subTotal: item.unitPrice * item.quantity, + quantity: item.quantity, + status: OrderItemStatus.CONFIRMED, + attributesValues: item.attributesValues, + order + }) + + this.em.persist(newOrderItem) + }) + // need calculate order finacial + await this.em.flush() + + return order + } }