clean order services

This commit is contained in:
2026-01-27 14:52:55 +03:30
parent 935f905e29
commit e2b065aa2b
9 changed files with 319 additions and 277 deletions
+246 -260
View File
@@ -1,15 +1,18 @@
import { Injectable, BadRequestException, Logger } from '@nestjs/common';
import { EntityManager } from '@mikro-orm/postgresql';
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, CreateOrderItemAsUserDto,
CreateOrderItemDtoAsAdmin, CreateOrderAsAdminDto
CreateOrderItemDtoAsAdmin
} from '../dto/create-order.dto';
import { UserService } from 'src/modules/user/providers/user.service';
import { IAddOrderItem, ICreateOrder, OrderStatusEnum, IUpdateOrderItem, IUpdateOrder } from '../interface/order.interface';
import {
CreateOrderItemParam,
CreateOrderParam, OrderStatusEnum,
UpdateOrderItem, UpdateOrderParam
} 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';
@@ -20,6 +23,9 @@ 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 { Product } from 'src/modules/product/entities/product.entity';
import { AdminService } from 'src/modules/admin/providers/admin.service';
import { OrderItem } from '../entities/order-item.entity';
@Injectable()
@@ -31,6 +37,7 @@ export class OrderService {
private readonly orderRepository: OrderRepository,
private readonly orderItemRepository: OrderItemRepository,
private readonly userService: UserService,
private readonly adminService: AdminService,
private readonly productService: ProductService,
private readonly productRepository: ProductRepository,
private readonly ticketRepository: TicketRepository,
@@ -40,7 +47,7 @@ export class OrderService {
private readonly paymentRepository: PaymentRepository,
) { }
async createOrderAsAdmin(adminId: string, dto: CreateOrderAsAdminDto) {
async createOrderAsAdmin(adminId: string, dto: CreateOrderParam) {
const order = await this.createOrder({ ...dto, adminId })
// await this.calculateOrder(order)
// this.em.flush()
@@ -56,129 +63,11 @@ export class OrderService {
}
async createOrder(dto: ICreateOrder) {
const { items, userId, attachments, designerId, enableTax, estimatedDays, paymentMethod, status, adminId } = dto
const user = await this.userService.findById(userId)
if (!user) {
throw new BadRequestException("User not found!")
}
async updateOrderAsAdmin(orderId: string, param: UpdateOrderParam) {
const order = await this.findOrderOrFail(orderId)
let admin: null | Admin = null
if (adminId) {
admin = await this.adminRepository.findOne({ id: adminId })
if (!admin) {
throw new BadRequestException("Admin not found")
}
}
let designer: null | Admin = null
if (designerId) {
designer = await this.adminRepository.findOne({ id: designerId })
if (!designer) {
throw new BadRequestException("designer not found")
}
}
return this.em.transactional(async (em) => {
const order = this.orderRepository.create({
creator: admin,
user,
attachments,
designer,
enableTax,
estimatedDays,
paymentMethod,
status,
})
em.persist(order)
const productIds = items.map(item => item.productId)
const products = await this.productRepository.find({
id: { $in: productIds }
})
if (productIds.length !== products.length) {
throw new BadRequestException("some products not found")
}
for (const item of items) {
await this.persistOrderItem(order, item)
}
// TODO : calculation must be done after create order
// await this.calculateOrder(order)
await em.flush()
return order
})
}
async updateOrder(order: Order, dto: IUpdateOrder) {
const { attachments, adminId, designerId, enableTax, estimatedDays, paymentMethod, status, userId } = dto
if (userId) {
const user = await this.userService.findById(userId)
if (!user) {
throw new BadRequestException("User not found!")
}
order.user = user
}
if (adminId != undefined) {
const admin = await this.adminRepository.findOne({ id: adminId })
if (!admin) {
throw new BadRequestException("Admin not found")
}
order.creator = admin
}
if (designerId != undefined) {
const designer = await this.adminRepository.findOne({ id: designerId })
if (!designer) {
throw new BadRequestException("designer not found")
}
order.designer = designer
}
if (attachments != undefined) {
order.attachments = attachments
}
if (enableTax != undefined) {
order.enableTax = enableTax
}
if (estimatedDays != undefined) {
order.estimatedDays = estimatedDays
}
if (paymentMethod != undefined) {
order.paymentMethod = paymentMethod
}
if (status != undefined) {
order.status = status
}
await this.em.persistAndFlush(order)
return order
}
async updateOrderAsAdmin(orderId: string, dto: IUpdateOrder) {
const order = await this.findOneOrFail(orderId)
const updateOrder = await this.updateOrder(order, dto)
const updateOrder = await this.updateOrder(order, param)
// const updateOrder = await this.calculateOrder(order)
@@ -191,7 +80,7 @@ export class OrderService {
async addOrderItemAsUser(userId: string, orderId: string, dto: CreateOrderItemAsUserDto) {
const order = await this.findOneOrFail(orderId)
const order = await this.findOrderOrFail(orderId)
if (order.status !== OrderStatusEnum.CREATED) {
throw new BadRequestException(`You can not update when status is ${order.status}`)
@@ -201,7 +90,9 @@ export class OrderService {
throw new BadRequestException(`This order doest belongs to you!`)
}
const orderItem = this.persistOrderItem(order, dto)
const product = await this.productService.findOneOrFail(dto.productId)
const orderItem = this.createOrderItem(order, product, dto)
await this.em.flush()
@@ -210,9 +101,11 @@ export class OrderService {
async addOrderItemAsAdmin(orderId: string, dto: CreateOrderItemDtoAsAdmin) {
const order = await this.findOneOrFail(orderId)
const order = await this.findOrderOrFail(orderId)
const orderItem = this.persistOrderItem(order, dto)
const product = await this.productService.findOneOrFail(dto.productId)
const orderItem = this.createOrderItem(order, product, dto)
await this.em.flush()
@@ -223,39 +116,10 @@ export class OrderService {
return orderItem
}
private async persistOrderItem(order: Order, dto: IAddOrderItem) {
const { attributes, 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`)
}
console.log(productId)
const product = await this.productRepository.findOne({ id: productId })
if (!product) {
throw new BadRequestException(`Product not found`)
}
const orderItem = this.orderItemRepository.create({
order,
attributes,
description,
quantity,
attachments,
subTotal: 0,
discount: discount ?? 0,
total: 0,
unitPrice: unitPrice ?? 0,
product
})
this.em.persist(orderItem)
return orderItem
}
async updateOrderItemAsUser(userId: string, orderId: string, itemId: string, dto: UpdateOrderItemDtoAsUser) {
const order = await this.findOneOrFail(orderId)
const order = await this.findOrderOrFail(orderId)
if (order.user.id !== userId) {
throw new BadRequestException(`This order doesnt belongs to you`)
@@ -272,7 +136,7 @@ export class OrderService {
async updateOrderItemAsAdmin(orderId: string, itemId: string, dto: UpdateOrderItemDtoAsAdmin) {
const order = await this.findOneOrFail(orderId)
await this.findOrderOrFail(orderId)
const orderItem = await this.updateOrderItem(itemId, dto)
@@ -283,57 +147,10 @@ export class OrderService {
return orderItem
}
async updateOrderItem(itemId: string, dto: IUpdateOrderItem) {
const { attributes, description, productId, quantity, attachments, discount, unitPrice } = dto
const orderItem = await this.orderItemRepository.findOne({
id: itemId
})
if (!orderItem) {
throw new BadRequestException(`orderItem not found`)
}
// product is changed
if (productId && orderItem.product.id !== productId) {
const product = await this.productRepository.findOne({ id: productId })
if (!product) {
throw new BadRequestException(`product not found`)
}
orderItem.product = product
}
if (attributes) {
orderItem.attributes = attributes
}
if (description) {
orderItem.description = description
}
if (quantity) {
orderItem.quantity = quantity
}
if (attachments) {
orderItem.attachments = attachments
}
if (discount) {
orderItem.discount = discount
}
if (unitPrice) {
orderItem.unitPrice = unitPrice
}
await this.em.flush()
return orderItem
}
async removeOrderItemAsAdmin(orderId: string, itemId: string) {
const order = await this.findOneOrFail(orderId)
const order = await this.findOrderOrFail(orderId)
await this.removeOrderItem(itemId)
@@ -346,7 +163,7 @@ export class OrderService {
async removeOrderItemAsUser(userId: string, orderId: string, itemId: string) {
const order = await this.findOneOrFail(orderId)
const order = await this.findOrderOrFail(orderId)
if (order.user.id !== userId) {
throw new BadRequestException(`this order is not belongs to you`)
@@ -361,36 +178,12 @@ export class OrderService {
return true
}
async removeOrderItem(itemId: string) {
const orderItem = await this.orderItemRepository.findOne({
id: itemId
})
if (!orderItem) {
throw new BadRequestException(`orderItem not found`)
}
await this.em.removeAndFlush(orderItem)
return true
}
async findUserOrders(userId: string, dto: FindOrdersDto) {
const orders = await this.orderRepository.findAllPaginated({ userId, ...dto })
return orders
}
async findOneOrFail(orderId: string) {
const order = await this.orderRepository.findOne({ id: orderId },
{ populate: ['items', 'items.product', 'payments'] })
if (!order) {
throw new BadRequestException('Order not found')
}
return order
}
// async calculateOrder(inputOrder?: Order, orderId?: string) {
// let order: undefined | Order = inputOrder
@@ -471,6 +264,219 @@ export class OrderService {
}
async removeOrderAsUser(orderId: string) {
const order = await this.orderRepository.findOne({ id: orderId })
if (!order) {
throw new BadRequestException("Order not found")
}
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.findOrderOrFail(orderId)
await this.updateStatus(orderId, OrderStatusEnum.INVOICED)
// await this.calculateOrder(order)
await this.em.flush()
}
// ==================== Entity Methods ====================
private async createOrderItem(order: Order, product: Product, dto: CreateOrderItemParam, em?: EntityManager) {
const { attributes, description, quantity, attachments, discount, unitPrice } = dto
const eM = em ?? this.em
return eM.create(
OrderItem,
{
order,
attributes,
description,
quantity,
attachments,
discount: discount ?? undefined,
unitPrice: unitPrice ?? undefined,
product
})
}
// ==================== Core Logic ====================
async createOrder(dto: CreateOrderParam) {
const { items, userId, attachments, designerId, enableTax,
estimatedDays, paymentMethod, status, 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 designer = designerId ? await this.adminService.findOrFail(designerId) : undefined
const products = await this.productService.findProductsByIds(items.map(item => item.productId))
return this.em.transactional(async (em) => {
const order = this.em.create(Order, {
creator: admin,
user,
attachments,
designer,
enableTax,
estimatedDays,
paymentMethod,
status,
})
em.persist(order)
for (const item of items) {
const product = products.find(p => Number(p.id) === item.productId)
if (!product) {
throw new BadRequestException("Product not found!")
}
const orderItem = await this.createOrderItem(order, product, item, 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: UpdateOrderParam) {
const { attachments, adminId, designerId, enableTax,
estimatedDays, paymentMethod, status, userId } = param
if (userId) {
const user = await this.userService.findOrFail(userId)
order.user = user
}
if (adminId != undefined) {
const admin = await this.adminService.findOrFail(adminId)
order.creator = admin
}
if (designerId != undefined) {
const designer = await this.adminService.findOrFail(designerId)
order.designer = designer
}
if (attachments != undefined) {
order.attachments = attachments
}
if (enableTax != undefined) {
order.enableTax = enableTax
}
if (estimatedDays != undefined) {
order.estimatedDays = estimatedDays
}
if (paymentMethod != undefined) {
order.paymentMethod = paymentMethod
}
if (status != undefined) {
order.status = status
}
await this.em.persistAndFlush(order)
return order
}
async updateOrderItem(itemId: string, dto: UpdateOrderItem) {
const { attributes, description, productId, quantity, attachments, discount, unitPrice } = dto
const orderItem = await this.orderItemRepository.findOne({
id: itemId
})
if (!orderItem) {
throw new BadRequestException(`orderItem not found`)
}
// product is changed
if (productId && Number(orderItem.product.id) !== productId) {
const product = await this.productRepository.findOne({ id: productId })
if (!product) {
throw new BadRequestException(`product not found`)
}
orderItem.product = product
}
if (attributes) {
orderItem.attributes = attributes
}
if (description) {
orderItem.description = description
}
if (quantity) {
orderItem.quantity = quantity
}
if (attachments) {
orderItem.attachments = attachments
}
if (discount) {
orderItem.discount = discount
}
if (unitPrice) {
orderItem.unitPrice = unitPrice
}
await this.em.flush()
return orderItem
}
async removeOrderItem(itemId: string) {
const orderItem = await this.orderItemRepository.findOne({
id: itemId
})
if (!orderItem) {
throw new BadRequestException(`orderItem not found`)
}
await this.em.removeAndFlush(orderItem)
return true
}
async hardDeleteOrder(orderId: string) {
const order = await this.orderRepository.findOne({ id: orderId })
if (!order) {
@@ -493,36 +499,16 @@ export class OrderService {
}
async removeOrderAsUser(orderId: string) {
const order = await this.orderRepository.findOne({ id: orderId })
// ==================== Helper Methods ====================
async findOrderOrFail(orderId: string) {
const order = await this.orderRepository.findOne({ id: orderId },
{ populate: ['items', 'items.product', 'payments'] })
if (!order) {
throw new BadRequestException("Order not found")
throw new BadRequestException('Order not found')
}
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.orderRepository.findOne({ id: orderId })
if (!order) {
throw new BadRequestException("Order not found")
}
order.status = newStatus
await this.em.flush()
return order
}
async createInvoice(orderId: string) {
const order = await this.findOneOrFail(orderId)
await this.updateStatus(orderId, OrderStatusEnum.INVOICED)
// await this.calculateOrder(order)
await this.em.flush()
}
}