order item

This commit is contained in:
2026-01-26 09:41:07 +03:30
parent 5dcf5c3c34
commit b45bc93799
6 changed files with 79 additions and 117 deletions
@@ -4,7 +4,7 @@ import { OrderService } from '../providers/order.service';
import { AuthGuard } from '../../auth/guards/auth.guard';
import { UserId } from '../../../common/decorators/user-id.decorator';
import { FindOrdersDto } from '../dto/find-orders.dto';
import { CreateOrderDto, CreateOrderItemDto } from '../dto/create-order.dto';
import { CreateOrderDto, CreateOrderItemAsUserDto } from '../dto/create-order.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';
@@ -40,7 +40,7 @@ export class OrderController {
@Patch('public/order/:id/item')
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'add item to order as user' })
addOrderItem(@Param('id') orderId: string, @Body() body: CreateOrderItemDto) {
addOrderItem(@Param('id') orderId: string, @Body() body: CreateOrderItemAsUserDto) {
return this.orderService.addOrderItemAsUser(orderId, body);
}
+19 -7
View File
@@ -1,13 +1,13 @@
import {
IsString, IsOptional, IsBoolean,
IsInt, Min, IsArray, IsNumber,
IsString,
IsInt, IsArray, IsNumber,
IsNotEmpty,
ArrayMinSize
} from 'class-validator';
import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger';
import { ApiPropertyOptional, ApiProperty, OmitType } from '@nestjs/swagger';
import { Type } from 'class-transformer';
export class CreateOrderItemDto {
export class CreateOrderItemDtoAsAdmin {
@IsInt()
@ApiProperty()
@@ -28,12 +28,24 @@ export class CreateOrderItemDto {
@ApiPropertyOptional({ example: [] })
@IsArray()
attachments: { url: string, type: string }[]
@ApiProperty()
@IsNumber()
unitPrice: number
@ApiProperty()
@IsNumber()
discount: number
}
export class CreateOrderItemAsUserDto extends OmitType(CreateOrderItemDtoAsAdmin, ['unitPrice', 'discount']) { }
export class CreateOrderDto {
@IsArray()
@ApiProperty({
isArray: true, type: [CreateOrderItemDto], example: [
isArray: true, type: [CreateOrderItemAsUserDto], example: [
{
productId: 1,
quantity: 100,
@@ -47,7 +59,7 @@ export class CreateOrderDto {
})
@IsNotEmpty()
@ArrayMinSize(1, { message: 'At least one item is required' })
@Type(() => CreateOrderItemDto)
items: CreateOrderItemDto[];
@Type(() => CreateOrderItemAsUserDto)
items: CreateOrderItemAsUserDto[];
}
@@ -1,6 +1,6 @@
import { PartialType } from '@nestjs/swagger';
import { CreateOrderItemDto } from './create-order.dto';
import { CreateOrderItemAsUserDto } from './create-order.dto';
export class UpdateOrderItemDto extends PartialType(CreateOrderItemDto) { }
export class UpdateOrderItemDto extends PartialType(CreateOrderItemAsUserDto) { }
+1 -1
View File
@@ -50,7 +50,7 @@ export class NewItemDto {
}
export class UpdateNewOrderDto {
export class UpdateOrderDto {
@IsArray()
@ApiProperty({
isArray: true, type: [ItemDto], example: [
@@ -26,7 +26,12 @@ export enum OrderStatusEnum {
}
// export enum OrderItemStatus {
// PENDING = 'pending',
// CONFIRMED = 'confirmed',
// }
export interface AddOrderItem {
productId: bigint;
quantity: number
attributesValues: string[]
description: string
attachments: { url: string, type: string }[]
unitPrice?: number
discount?: number
}
+46 -101
View File
@@ -4,9 +4,9 @@ import { OrderItem } from '../entities/order-item.entity';
import { OrderRepository } from '../repositories/order.repository';
import { FindOrdersDto } from '../dto/find-orders.dto';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { CreateOrderDto, CreateOrderItemDto } from '../dto/create-order.dto';
import { CreateOrderDto, CreateOrderItemAsUserDto, CreateOrderItemDtoAsAdmin } from '../dto/create-order.dto';
import { UserService } from 'src/modules/user/providers/user.service';
import { OrderStatusEnum } from '../interface/order.interface';
import { AddOrderItem, OrderStatusEnum } from '../interface/order.interface';
import { OrderItemRepository } from '../repositories/order-item.repository';
import { ProductService } from 'src/modules/product/providers/product.service';
import { ProductRepository } from 'src/modules/product/repositories/product.repository';
@@ -17,6 +17,7 @@ import { CreateOrderAsAdminDto } from '../dto/create-order-as-admin.dto';
import { Order } from '../entities/order.entity';
import { PaymentRepository } from 'src/modules/payment/repositories/payment.repository';
import { UpdateOrderItemDto } from '../dto/update-order-item.dto';
import { UpdateOrderDto } from '../dto/update-order.dto';
@Injectable()
@@ -96,19 +97,39 @@ export class OrderService {
return order
}
async addOrderItemAsUser(orderId: string, dto: CreateOrderItemDto) {
const { attributesValues, description, productId, quantity, attachments } = dto
async addOrderItemAsUser(orderId: string, dto: CreateOrderItemAsUserDto) {
const order = await this.findOneOrFail(orderId)
if (!order) {
throw new BadRequestException(`Order not found`)
}
if (order.status !== OrderStatusEnum.CREATED) {
throw new BadRequestException(`You can not update when status is ${order.status}`)
}
const orderItem = this.persistOrderItem(order, dto)
await this.em.flush()
return orderItem
}
async addOrderItemAsAdmin(orderId: string, dto: CreateOrderItemDtoAsAdmin) {
const order = await this.findOneOrFail(orderId)
const orderItem = this.persistOrderItem(order, dto)
await this.em.flush()
await this.calculateOrder(undefined, orderId)
await this.em.flush()
return orderItem
}
private async persistOrderItem(order: Order, dto: AddOrderItem) {
const { attributesValues, description, productId, quantity, attachments, discount, unitPrice } = dto
const found = order.items.find(it => it.product.id == productId)
if (found) {
throw new BadRequestException(`Product already exists`)
@@ -125,13 +146,13 @@ export class OrderService {
quantity,
attachments,
subTotal: 0,
discount: 0,
discount: discount ?? 0,
total: 0,
unitPrice: 0,
unitPrice: unitPrice ?? 0,
product
})
this.em.persistAndFlush(orderItem)
this.em.persist(orderItem)
return orderItem
}
@@ -274,7 +295,7 @@ export class OrderService {
// TODO : calculation must be done after create order
await this.calculateOrder(order, em)
await this.calculateOrder(order)
await em.flush()
@@ -299,8 +320,17 @@ export class OrderService {
return order
}
async calculateOrder(order: Order, em: EntityManager) {
async calculateOrder(inputOrder?: Order, orderId?: string) {
let order: undefined | Order = undefined
if (orderId) {
order = await this.findOneOrFail(orderId)
}
if (order) {
order = inputOrder
}
if (!order) {
throw new BadRequestException("Order not found")
}
// calculate order financials
let subTotal = 0
let totalDiscount = 0
@@ -330,91 +360,6 @@ export class OrderService {
return order
}
// async updateOrderAsAdmin(orderId: string, dto: UpdateNewOrderDto) {
// const { items, enableTax, attachments, newItems } = dto
// const order = await this.findOneOrFail(orderId)
// if (order.status !== OrderStatusEnum.CREATED) {
// throw new BadRequestException(`You can not update when status is ${order.status}`)
// }
// const updatedOrder = await this.em.transactional(async (em) => {
// for (let orderItem of order.items) {
// const found = items.find(it => it.orderItemId == Number(orderItem.id))
// if (!found) {
// throw new BadRequestException(`order item ${orderItem} not found`)
// }
// // TODO use Deciaml js to calculation
// orderItem.unitPrice = found.unitPrice
// subTotal += orderItem.subTotal
// em.persist(orderItem)
// }
// newItems.forEach(async item => {
// const found = updatedOrder.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,
// confirmedAt: new Date(),
// attributesValues: item.attributesValues,
// order: updatedOrder
// })
// subTotal += item.unitPrice
// this.em.persist(newOrderItem)
// })
// // calculate order financials
// order.subTotal = subTotal
// order.discount = totalDiscount
// const total = subTotal - totalDiscount
// let tax = 0
// if (enableTax) {
// tax = 0.1 * total
// }
// order.taxAmount = tax
// order.total = total + tax
// order.balance = total + tax
// if (attachments.length) {
// updatedOrder.attachments = attachments
// }
// // change status
// order.status = OrderStatusEnum.INVOICED
// order.invoicedAt = new Date()
// em.persist(order)
// return order
// })
// return updatedOrder
// }
async confirmOrderItem(userId: string, orderId: string, orderItemId: number) {
const orderItem = await this.orderItemRepository.findOne({ id: orderItemId, order: { id: orderId } },
@@ -478,7 +423,7 @@ export class OrderService {
}
async removeOrderAsUser(orderId:string){
async removeOrderAsUser(orderId: string) {
const order = await this.orderRepository.findOne({ id: orderId })
if (!order) {
throw new BadRequestException("Order not found")
@@ -488,7 +433,7 @@ export class OrderService {
throw new BadRequestException("Order can not be deleted")
}
return this.hardDeleteOrder(orderId)
return this.hardDeleteOrder(orderId)
}
async updateStatus(orderId: string, newStatus: OrderStatusEnum) {