schema created

This commit is contained in:
2026-02-18 11:40:59 +03:30
parent 6fda27491b
commit 9a8e00bf93
6 changed files with 91 additions and 208 deletions
+7 -6
View File
@@ -10,7 +10,7 @@ import {
} from 'class-validator';
import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IAttachment, IField, OrderStatusEnum } from '../interface/order.interface';
import { IAttachment, IField, OrderItemStatusEnum } from '../interface/order.interface';
export class CreateOrderItemAsUserDto {
@@ -22,6 +22,10 @@ export class CreateOrderItemAsUserDto {
@IsNumber()
quantity: number
@ApiProperty({ enum: OrderItemStatusEnum })
@IsEnum(() => OrderItemStatusEnum)
status: OrderItemStatusEnum
@ApiProperty()
@IsArray()
attributes: IField[]
@@ -113,8 +117,8 @@ export class CreateOrderAsAdminDto {
unitPrice: 150000,
discount: 20000,
adminDescription: '',
description:'توضیحات کاربر',
description: 'توضیحات کاربر',
}
]
})
@@ -137,8 +141,5 @@ export class CreateOrderAsAdminDto {
@IsInt()
estimatedDays: number
@ApiProperty({ enum: OrderStatusEnum })
@IsEnum(OrderStatusEnum)
status: OrderStatusEnum
}
+14 -12
View File
@@ -1,7 +1,7 @@
import { Entity, Formula, Index, ManyToOne, OptionalProps, Property } from '@mikro-orm/core';
import { Entity, Enum, Index, ManyToOne, OptionalProps, Property } from '@mikro-orm/core';
import { Order } from './order.entity';
import { Product } from 'src/modules/product/entities/product.entity';
import { IAttachment, IField } from '../interface/order.interface';
import { IAttachment, IField, OrderItemStatusEnum } from '../interface/order.interface';
import { Admin } from 'src/modules/admin/entities/admin.entity';
import { BaseEntity } from 'src/common/entities/base.entity';
@@ -17,6 +17,9 @@ export class OrderItem extends BaseEntity {
@ManyToOne(() => Product)
product!: Product;
@Enum(() => OrderItemStatusEnum)
status!: OrderItemStatusEnum;
@ManyToOne(() => Admin, { nullable: true })
designer?: Admin
@@ -26,24 +29,23 @@ export class OrderItem extends BaseEntity {
@Property({ type: 'int' })
quantity!: number;
@Property({ type: 'decimal', precision: 10, scale: 0, nullable: true, })
@Property({ type: 'int', nullable: true, })
unitPrice?: number;
// @Formula(alias => `
// COALESCE(${alias}.unit_price, 0) * ${alias}.quantity
// `)
@Property({
type: 'decimal', precision: 10,
columnType: 'numeric(10,0) GENERATED ALWAYS AS (COALESCE(unit_price, 0) * quantity) STORED',
type: 'int',
columnType: 'int GENERATED ALWAYS AS (COALESCE(unit_price, 0) * quantity) STORED',
persist: false,
scale: 0,
nullable: true
nullable: true
})
subTotal!: number;
@Property({
type: 'decimal', precision: 10,
scale: 0, nullable: true
type: 'int',
nullable: true
})
discount?: number;
@@ -52,9 +54,9 @@ export class OrderItem extends BaseEntity {
// - COALESCE(${alias}.discount, 0)
// `)
@Property({
type: 'decimal', precision: 10,
scale: 0, nullable: true,
columnType: 'numeric(10,0) GENERATED ALWAYS AS ((COALESCE(unit_price, 0) * quantity) - COALESCE(discount, 0)) STORED',
type: 'int',
nullable: true,
columnType: 'int GENERATED ALWAYS AS ((COALESCE(unit_price, 0) * quantity) - COALESCE(discount, 0)) STORED',
persist: false
})
total!: number;
+20 -152
View File
@@ -8,12 +8,8 @@ import {
Cascade,
Enum,
PrimaryKey,
BeforeCreate,
type EventArgs,
OptionalProps,
Formula
} from '@mikro-orm/core';
import { OrderStatusEnum } from '../interface/order.interface';
import { User } from '../../user/entities/user.entity';
import { OrderItem } from './order-item.entity';
import { Payment } from 'src/modules/payment/entities/payment.entity';
@@ -21,13 +17,12 @@ import { ulid } from 'ulid';
import { Admin } from 'src/modules/admin/entities/admin.entity';
import { PaymentStatusEnum } from 'src/modules/payment/interface/payment';
import { BaseEntity } from 'src/common/entities/base.entity';
import { OrderStatusEnum } from '../interface/order.interface';
@Entity({ tableName: 'orders' })
@Index({ properties: ['user', 'status'] })
@Index({ properties: ['status'] })
export class Order extends BaseEntity{
[OptionalProps]?: 'createdAt' | 'deletedAt' | 'balance' | 'paidAmount'
| 'discount' | 'subTotal' | 'total' | 'taxAmount';
@Index({ properties: ['user'] })
export class Order extends BaseEntity {
[OptionalProps]?: 'createdAt' | 'deletedAt' | 'orderNumber'|'paidAmount'|'balance';
@PrimaryKey({ type: 'string', columnType: 'char(26)' })
id: string = ulid()
@@ -51,140 +46,32 @@ export class Order extends BaseEntity{
@ManyToOne(() => Admin, { nullable: true })
creator?: Admin
@Property({ type: 'int', defaultRaw: `nextval('order_number_seq')` })
orderNumber!: number;
@Property({ type: 'int', nullable: true })
orderNumber?: number;
discount?: number;
// @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true })
// discount?: number;
@Formula(alias => `
(
SELECT COALESCE(SUM(oi.discount), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
)
`)
discount!: number;
@Property({ type: 'int', nullable: true })
subTotal?: number;
// @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true })
// subTotal?: number;
@Formula(alias => `
(
SELECT COALESCE(SUM(COALESCE(oi.unit_price, 0) * oi.quantity), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
)
`)
subTotal!: number;
@Property({ type: 'int', nullable: true })
taxAmount?: number;
// @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true })
// taxAmount?: number;
@Formula(alias => `
(
CASE
WHEN ${alias}.enable_tax = true THEN
(
SELECT COALESCE(SUM(COALESCE(oi.unit_price, 0) * oi.quantity), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
) * 0.1
ELSE 0
END
)
`)
taxAmount!: number;
@Property({ type: 'int', nullable: true })
total?: number;
// @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true })
// total?: number;
@Formula(alias => `
(
(
SELECT COALESCE(SUM(COALESCE(oi.unit_price, 0) * oi.quantity), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
)
-
(
SELECT COALESCE(SUM(oi.discount), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
)
+
CASE
WHEN ${alias}.enable_tax = true THEN
(
SELECT COALESCE(SUM(COALESCE(oi.unit_price, 0) * oi.quantity), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
) * 0.1
ELSE 0
END
)
`)
total!: number;
@Property({ type: 'int', default: 0 })
paidAmount: number = 0;
// @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true })
// paidAmount?: number;
// private _balance!: number;
// set balance(value) {
// this._balance = this.total - this.paidAmount
// }
@Formula(alias => `
(
SELECT COALESCE(SUM(p.amount), 0)
FROM payments p
WHERE p.order_id = ${alias}.id
AND p.status = '${PaymentStatusEnum.Paid}'
)
`)
paidAmount!: number;
@Formula(alias => `
(
(
(
SELECT COALESCE(SUM(COALESCE(oi.unit_price, 0) * oi.quantity), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
)
-
(
SELECT COALESCE(SUM(oi.discount), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
)
+
CASE
WHEN ${alias}.enable_tax = true THEN
(
SELECT COALESCE(SUM(COALESCE(oi.unit_price, 0) * oi.quantity), 0)
FROM order_items oi
WHERE oi.order_id = ${alias}.id
) * 0.1
ELSE 0
END
)
-
(
SELECT COALESCE(SUM(p.amount), 0)
FROM payments p
WHERE p.order_id = ${alias}.id
AND p.status = '${PaymentStatusEnum.Paid}'
)
)
`)
balance!: number;
@Property({ type: 'int', default: 0 })
balance: number = 0;
@Property({ type: 'int', nullable: true })
@@ -192,7 +79,7 @@ export class Order extends BaseEntity{
@Enum(() => OrderStatusEnum)
status!: OrderStatusEnum;
status: OrderStatusEnum = OrderStatusEnum.REQUEST;
@Property({ type: 'string', nullable: true })
@@ -202,26 +89,7 @@ export class Order extends BaseEntity{
invoicedAt?: Date;
@Property({ type: 'boolean', default: false })
enableTax?: Boolean = false
enableTax?: boolean = false
@BeforeCreate()
async generateOrderNumber(args: EventArgs<Order>) {
const em = args.em;
const order = args.entity;
const maxOrder = await em.findOne(
Order,
{ id: { $ne: null } },
{
orderBy: { orderNumber: 'DESC' },
fields: ['orderNumber'],
},
);
// Set the next order number (1 if no orders exist, otherwise max + 1)
order.orderNumber = maxOrder?.orderNumber ? maxOrder.orderNumber + 1 : 1;
}
}
+11 -1
View File
@@ -2,6 +2,15 @@ import { Admin } from "src/modules/admin/entities/admin.entity"
import { Product } from "src/modules/product/entities/product.entity"
export enum OrderStatusEnum {
REQUEST = 'request',
INVOICED = 'invoiced',
ORDER = 'order',
}
export enum OrderItemStatusEnum {
CREATED = 'created',
INVOICED = 'invoiced',
@@ -35,6 +44,7 @@ export interface IAttachment { type: string, url: string }
export interface CreateOrderItem {
productId: string,
quantity: number
status: OrderItemStatusEnum,
attributes?: IField[]
description?: string
attachments?: IAttachment[]
@@ -53,7 +63,7 @@ export type CreateOrderItemPopulated = Omit<CreateOrderItem, 'productId' | 'desi
export interface CreateOrder {
userId: string
status: OrderStatusEnum
// status: OrderStatusEnum
enableTax?: boolean
paymentMethod?: string
adminId?: string
@@ -7,7 +7,7 @@ import { NotifEvent } from 'src/modules/notification/interfaces/notification.int
import { NotificationService } from 'src/modules/notification/services/notification.service';
import { ConfigService } from '@nestjs/config';
import { OrderRepository } from '../repositories/order.repository';
import { OrderStatusEnum } from '../interface/order.interface';
// import { OrderStatusEnum } from '../interface/order.interface';
import { PaymentMethodEnum } from 'src/modules/payment/interface/payment';
import { UserService } from 'src/modules/user/providers/user.service';
+38 -36
View File
@@ -10,11 +10,12 @@ import {
} from '../dto/create-order.dto';
import { UserService } from 'src/modules/user/providers/user.service';
import {
OrderStatusEnum,
OrderItemStatusEnum,
CreateOrderWithItems,
CreateOrderItemPopulated,
UpdateOrder,
UpdateOrderItem
UpdateOrderItem,
OrderStatusEnum
} from '../interface/order.interface';
import { OrderItemRepository } from '../repositories/order-item.repository';
import { ProductService } from 'src/modules/product/providers/product.service';
@@ -65,7 +66,7 @@ export class OrderService {
}
async createOrderAsUser(userId: string, dto: CreateOrderAsUSerDto) {
const order = await this.createOrder({ ...dto, userId, status: OrderStatusEnum.CREATED })
const order = await this.createOrder({ ...dto, userId })
this.logger.log("Order created as user")
return order
@@ -145,7 +146,7 @@ export class OrderService {
description: item.description,
discount: item.discount,
confirmedAt: item.confirmedAt,
status: OrderItemStatusEnum.CREATED
})
em.persist(newOrderItem)
@@ -167,9 +168,9 @@ export class OrderService {
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.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!`)
@@ -214,9 +215,9 @@ export class OrderService {
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.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`)
@@ -270,9 +271,9 @@ export class OrderService {
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.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`)
@@ -366,28 +367,28 @@ export class OrderService {
throw new BadRequestException("this order doesnt belongs to you")
}
if (![OrderStatusEnum.CREATED].includes(order.status)) {
throw new BadRequestException("Order can not be deleted")
}
// if (![OrderStatusEnum.CREATED].includes(order.status)) {
// throw new BadRequestException("Order can not be deleted")
// }
return this.hardDeleteOrder(orderId)
}
async updateStatus(orderId: string, newStatus: OrderStatusEnum) {
// async updateStatus(orderId: string, newStatus: OrderStatusEnum) {
const order = await this.findOrderOrFail(orderId)
// const order = await this.findOrderOrFail(orderId)
order.status = newStatus
// // order.status = newStatus
await this.em.flush()
// await this.em.flush()
return order
}
// return order
// }
async createInvoice(orderId: string) {
const order = await this.updateStatus(orderId, OrderStatusEnum.INVOICED)
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)
@@ -401,7 +402,7 @@ export class OrderService {
private async createOrderItemEntity(order: Order, dto: CreateOrderItemPopulated, em?: EntityManager) {
const { attributes, description, quantity, attachments, discount,
unitPrice, product, designer, printAttributes } = dto
unitPrice, product, designer, printAttributes, status } = dto
const eM = em ?? this.em
return eM.create(
@@ -417,6 +418,7 @@ export class OrderService {
unitPrice,
product,
printAttributes: printAttributes,
status
})
}
@@ -424,7 +426,7 @@ export class OrderService {
async createOrder(dto: CreateOrderWithItems) {
const { items, userId, enableTax,
estimatedDays, paymentMethod, status, adminId } = dto
estimatedDays, paymentMethod, adminId } = dto
// Validate and fetch all required entities
const user = await this.userService.findOrFail(userId)
@@ -447,7 +449,7 @@ export class OrderService {
enableTax,
estimatedDays,
paymentMethod,
status,
status: OrderStatusEnum.REQUEST,
})
em.persist(order)
@@ -486,7 +488,7 @@ export class OrderService {
async updateOrder(order: Order, param: UpdateOrder) {
const { adminId, enableTax,
estimatedDays, paymentMethod, status, userId } = param
estimatedDays, paymentMethod, userId } = param
if (userId) {
@@ -513,13 +515,13 @@ export class OrderService {
order.paymentMethod = paymentMethod
}
if (status != undefined) {
order.status = status
// if (status != undefined) {
// order.status = status
if (status === OrderStatusEnum.INVOICED && !order.invoicedAt) {
order.invoicedAt = new Date()
}
}
// if (status === OrderStatusEnum.INVOICED && !order.invoicedAt) {
// order.invoicedAt = new Date()
// }
// }