diff --git a/src/modules/order/dto/create-order-as-admin.dto.ts b/src/modules/order/dto/create-order-as-admin.dto.ts index 7def32d..6e1bcf2 100644 --- a/src/modules/order/dto/create-order-as-admin.dto.ts +++ b/src/modules/order/dto/create-order-as-admin.dto.ts @@ -3,10 +3,14 @@ import { IsInt, IsArray, IsNumber, IsNotEmpty, ArrayMinSize, - IsMobilePhone + IsMobilePhone, + IsOptional, + IsBoolean, + IsEnum } from 'class-validator'; import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; +import { OrderStatusEnum } from '../interface/order.interface'; export class CreateOrderItemDto { @@ -38,14 +42,13 @@ export class CreateOrderItemDto { @IsArray() @IsString({ each: true }) attachments: { url: string, type: string }[] -} +} export class CreateOrderAsAdminDto { - @ApiPropertyOptional({ example: 'توضیحات' }) + @ApiProperty({ example: '' }) @IsString() @IsNotEmpty() - @IsMobilePhone('fa-IR') - userPhone: string + userId: string @IsArray() @ApiProperty({ @@ -62,5 +65,33 @@ export class CreateOrderAsAdminDto { @Type(() => CreateOrderItemDto) items: CreateOrderItemDto[]; + @ApiProperty({ example: '' }) + @IsString() + @IsNotEmpty() + designerId: string + + @ApiProperty({ example: '' }) + @IsArray() + @IsString({ each: true }) + @IsNotEmpty() + attachments: string[] + + @ApiProperty({}) + @IsString() + @IsOptional() + paymentMethod: string + + @ApiProperty({}) + @IsBoolean() + enableTax: Boolean + + @ApiProperty({}) + @IsInt() + estimatedDays: number + + @ApiProperty({ enum: OrderStatusEnum }) + @IsEnum(OrderStatusEnum) + status: OrderStatusEnum + } diff --git a/src/modules/order/entities/order-item.entity.ts b/src/modules/order/entities/order-item.entity.ts index 5d45779..0e8efd7 100644 --- a/src/modules/order/entities/order-item.entity.ts +++ b/src/modules/order/entities/order-item.entity.ts @@ -22,24 +22,24 @@ export class OrderItem extends BaseEntity { @Property({ type: 'int' }) quantity!: number; - @Property({ type: 'decimal', precision: 10, scale: 0 }) - unitPrice!: number; + @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true, }) + unitPrice?: number; get subTotal(): number { return Number(this.unitPrice) * (this.quantity) } - @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) - discount: number = 0; + @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true }) + discount?: number; get total(): number { return Number(this.subTotal) - Number(this.discount || 0) } - @Property({type:'text', nullable: true }) + @Property({ type: 'text', nullable: true }) description?: string; - @Property({type:'text', nullable: true }) + @Property({ type: 'text', nullable: true }) adminDescription?: string; @Property({ type: 'json', nullable: true }) diff --git a/src/modules/order/entities/order.entity.ts b/src/modules/order/entities/order.entity.ts index b991375..3270561 100644 --- a/src/modules/order/entities/order.entity.ts +++ b/src/modules/order/entities/order.entity.ts @@ -55,44 +55,41 @@ export class Order extends BaseEntity { orderNumber?: number; - @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) - discount: number = 0; + @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true }) + discount?: number; - @Property({ type: 'decimal', precision: 10, scale: 0 }) - subTotal!: number; + @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true }) + subTotal?: number; - @Property({ type: 'decimal', precision: 10, scale: 0 }) - taxAmount: number; + @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true }) + taxAmount?: number; - @Property({ type: 'decimal', precision: 10, scale: 0 }) - total!: number; + @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true }) + total?: number; - @Property({ type: 'decimal', precision: 10, scale: 0 }) - paidAmount!: number; + @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true }) + paidAmount?: number; // private _balance!: number; - - @Property({ type: 'decimal', precision: 10, scale: 0 }) - balance!: number; - - @Property({ type: 'decimal', precision: 10, scale: 0, nullable: true }) - dueDate?: number; // number of days to be done // get balance(): number { // return this._balance // } // set balance(value) { // this._balance = this.total - this.paidAmount // } - // @Property({ type: 'text', nullable: true }) - // description?: string; + + @Property({ type: 'decimal', precision: 10, scale: 0, default: null, nullable: true }) + balance?: number; + + @Property({ type: 'int', nullable: true }) + estimatedDays?: number; // number of days to be done @Enum(() => OrderStatusEnum) status!: OrderStatusEnum; - @Property({ type: 'json', nullable: true }) attachments?: string[] diff --git a/src/modules/order/interface/order.interface.ts b/src/modules/order/interface/order.interface.ts index 1ba2f5d..b7f54de 100644 --- a/src/modules/order/interface/order.interface.ts +++ b/src/modules/order/interface/order.interface.ts @@ -26,7 +26,7 @@ export enum OrderStatusEnum { } -export interface AddOrderItem { +export interface IAddOrderItem { productId: bigint; quantity: number attributesValues: string[] @@ -36,5 +36,17 @@ export interface AddOrderItem { discount?: number } -export type UpdateOrderItem = Partial +export type IUpdateOrderItem = Partial +export interface ICreateOrder { + userId: string + items: IAddOrderItem[]; + status: OrderStatusEnum + //optional + enableTax?: Boolean + attachments?: string[] + paymentMethod?: string + adminId?: string + estimatedDays?: number + designerId?: string +} \ No newline at end of file diff --git a/src/modules/order/providers/order.service.ts b/src/modules/order/providers/order.service.ts index 48eff23..e9ff592 100644 --- a/src/modules/order/providers/order.service.ts +++ b/src/modules/order/providers/order.service.ts @@ -6,7 +6,7 @@ import { FindOrdersDto } from '../dto/find-orders.dto'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { CreateOrderDto, CreateOrderItemAsUserDto, CreateOrderItemDtoAsAdmin } from '../dto/create-order.dto'; import { UserService } from 'src/modules/user/providers/user.service'; -import { AddOrderItem, OrderStatusEnum, UpdateOrderItem } from '../interface/order.interface'; +import { IAddOrderItem, ICreateOrder, OrderStatusEnum, IUpdateOrderItem } 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'; @@ -18,6 +18,7 @@ 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 { UpdateOrderDto } from '../dto/update-order.dto'; +import { Admin } from 'src/modules/admin/entities/admin.entity'; @Injectable() @@ -131,7 +132,7 @@ export class OrderService { return orderItem } - private async persistOrderItem(order: Order, dto: AddOrderItem) { + private async persistOrderItem(order: Order, dto: IAddOrderItem) { const { attributesValues, description, productId, quantity, attachments, discount, unitPrice } = dto const found = order.items.find(it => it.product.id == productId) @@ -187,7 +188,7 @@ export class OrderService { return orderItem } - async updateOrderItem(itemId: string, dto: UpdateOrderItem) { + async updateOrderItem(itemId: string, dto: IUpdateOrderItem) { const { attributesValues, description, productId, quantity, attachments, discount, unitPrice } = dto const orderItem = await this.orderItemRepository.findOne({ @@ -277,16 +278,39 @@ export class OrderService { } async createOrderAsAdmin(adminId: string, dto: CreateOrderAsAdminDto) { - const { items, userPhone } = dto + const order = await this.createOrder({ ...dto, adminId }) + return order - const user = await this.userService.findByPhone(userPhone) + } + + async createOrderAsUser(userId: string, dto: CreateOrderDto) { + const order = await this.createOrder({ ...dto, userId, status: OrderStatusEnum.CREATED }) + return order + + } + + 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 with this phone not found!") + throw new BadRequestException("User not found!") } - const admin = await this.adminRepository.findOne({ id: adminId }) - if (!admin) { - throw new BadRequestException("Admin not found") + 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") + } } const order = await this.em.transactional(async (em) => { @@ -294,13 +318,12 @@ export class OrderService { const order = this.orderRepository.create({ creator: admin, user, - discount: 0, - subTotal: 0, - total: 0, - paidAmount: 0, - balance: 0, - status: OrderStatusEnum.INVOICED, - taxAmount: 0 + attachments, + designer, + enableTax, + estimatedDays, + paymentMethod, + status, }) em.persist(order) @@ -314,33 +337,14 @@ export class OrderService { } items.forEach(item => { - const product = products.find(p => p.id == item.productId) - - if (!product) { - throw new BadRequestException(`product ${product} not found`) - } - - const orderItem = this.orderItemRepository.create({ - order, - attributesValues: item.attributesValues, - product, - quantity: item.quantity, - unitPrice: item.unitPrice, - total: item.unitPrice * item.quantity, - discount: item.discount, - subTotal: item.unitPrice * item.quantity - item.discount, - attachments: item.attachments, - description: item.description - }) - - em.persist(orderItem) + this.persistOrderItem(order, item) }); // TODO : calculation must be done after create order await this.calculateOrder(order) - await em.flush() + // await em.flush() return order }) @@ -381,7 +385,7 @@ export class OrderService { // TODO : use reduce for (let orderItem of order.items) { subTotal += orderItem.total - totalDiscount += orderItem.discount + totalDiscount += Number(orderItem.discount) } const totalBeforeTax = subTotal - totalDiscount diff --git a/src/modules/payment/services/payments.service.ts b/src/modules/payment/services/payments.service.ts index 060dc6a..536ea91 100644 --- a/src/modules/payment/services/payments.service.ts +++ b/src/modules/payment/services/payments.service.ts @@ -67,6 +67,10 @@ export class PaymentService { throw new NotFoundException(OrderMessage.NOT_FOUND); } + if (!order.balance) { + throw new NotFoundException('Balance is not set'); + } + let admin: null | Admin = null if (adminId) { admin = await this.adminRepository.findOne({ id: adminId }) @@ -218,8 +222,8 @@ export class PaymentService { payment.transactionId = referenceId; // TODO : use decimal js // does need persist or not - payment.order.paidAmount += payment.amount - payment.order.balance -= payment.amount + payment.order.paidAmount! += payment.amount + payment.order.balance! -= payment.amount await em.flush(); return payment; @@ -257,8 +261,8 @@ export class PaymentService { payment.failedAt = null; // TODO : use decimal js - payment.order.paidAmount += payment.amount - payment.order.balance -= payment.amount + payment.order.paidAmount! += payment.amount + payment.order.balance! -= payment.amount em.persist(payment); em.persist(payment.order); @@ -282,8 +286,8 @@ export class PaymentService { } if (payment.status == PaymentStatusEnum.Paid) { - payment.order.paidAmount -= payment.amount - payment.order.balance += payment.amount + payment.order.paidAmount! -= payment.amount + payment.order.balance! += payment.amount em.persistAndFlush(payment.order) }