From 61f473ea347c05bc33a504d06d0aa0b77cc4e4be Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 19 Feb 2026 09:21:53 +0330 Subject: [PATCH] order --- src/app.module.ts | 5 +- .../invoice/entities/invoice.entity.ts | 2 +- .../order/controllers/order.controller.ts | 216 ------ src/modules/order/crone/order.crone.ts | 17 - src/modules/order/dto/assign-designer.dto.ts | 11 - src/modules/order/dto/create-order.dto.ts | 146 ---- .../order/dto/create-print-form.dto.ts | 16 - src/modules/order/dto/find-order-items.dto.ts | 53 -- src/modules/order/dto/find-orders.dto.ts | 80 --- .../order/dto/update-order-item.dto.ts | 7 - src/modules/order/dto/update-order.dto.ts | 14 - .../order/entities/order-item.entity.ts | 82 --- src/modules/order/entities/order.entity.ts | 98 --- src/modules/order/events/order.events.ts | 21 - .../order/interface/order.interface.ts | 81 --- .../order/listeners/order.listeners.ts | 212 ------ src/modules/order/order.module.ts | 39 -- src/modules/order/providers/order.service.ts | 636 ------------------ .../repositories/order-item.repository.ts | 88 --- .../order/repositories/order.repository.ts | 147 ---- .../payment/controllers/payment.controller.ts | 110 +-- src/modules/payment/dto/find-payments.dto.ts | 2 +- .../payment/entities/payment.entity.ts | 6 +- src/modules/payment/interface/payment.ts | 4 +- .../payment/listeners/payment.listeners.ts | 4 +- src/modules/payment/payments.module.ts | 10 +- .../repositories/payment.repository.ts | 8 +- .../payment/services/payments.service.ts | 487 +++++++------- .../ticket/controller/ticket.controller.ts | 8 +- src/modules/ticket/entities/ticket.entity.ts | 7 +- .../ticket/providers/tickets.service.ts | 58 +- .../ticket/repositories/tickets.repository.ts | 6 +- src/modules/ticket/ticket.module.ts | 3 +- src/modules/user/entities/user.entity.ts | 6 +- 34 files changed, 357 insertions(+), 2333 deletions(-) delete mode 100644 src/modules/order/controllers/order.controller.ts delete mode 100644 src/modules/order/crone/order.crone.ts delete mode 100644 src/modules/order/dto/assign-designer.dto.ts delete mode 100644 src/modules/order/dto/create-order.dto.ts delete mode 100644 src/modules/order/dto/create-print-form.dto.ts delete mode 100644 src/modules/order/dto/find-order-items.dto.ts delete mode 100644 src/modules/order/dto/find-orders.dto.ts delete mode 100644 src/modules/order/dto/update-order-item.dto.ts delete mode 100644 src/modules/order/dto/update-order.dto.ts delete mode 100644 src/modules/order/entities/order-item.entity.ts delete mode 100644 src/modules/order/entities/order.entity.ts delete mode 100644 src/modules/order/events/order.events.ts delete mode 100644 src/modules/order/interface/order.interface.ts delete mode 100644 src/modules/order/listeners/order.listeners.ts delete mode 100644 src/modules/order/order.module.ts delete mode 100644 src/modules/order/providers/order.service.ts delete mode 100644 src/modules/order/repositories/order-item.repository.ts delete mode 100644 src/modules/order/repositories/order.repository.ts diff --git a/src/app.module.ts b/src/app.module.ts index 5f5162a..c61767d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,8 +12,7 @@ import { ScheduleModule } from '@nestjs/schedule'; import { ProductModule } from './modules/product/product.module'; import { RolesModule } from './modules/roles/roles.module'; import { PaymentModule } from './modules/payment/payments.module'; -import { OrderModule } from './modules/order/order.module'; -import { NotificationsModule } from './modules/notification/notifications.module'; + import { NotificationsModule } from './modules/notification/notifications.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { CacheModule } from '@nestjs/cache-manager'; import { PrintModule } from './modules/print/print.module'; @@ -40,7 +39,7 @@ import { InvoiceModule } from './modules/invoice/invoice.module'; ProductModule, RolesModule, PaymentModule, - OrderModule, + // OrderModule, NotificationsModule, EventEmitterModule.forRoot(), PrintModule, diff --git a/src/modules/invoice/entities/invoice.entity.ts b/src/modules/invoice/entities/invoice.entity.ts index 7595bdd..ea0ace0 100644 --- a/src/modules/invoice/entities/invoice.entity.ts +++ b/src/modules/invoice/entities/invoice.entity.ts @@ -33,7 +33,7 @@ export class Invoice extends BaseEntity { @ManyToOne(() => Request) request!: Request; - @OneToMany(() => Payment, payment => payment.order, { + @OneToMany(() => Payment, payment => payment.invoice, { cascade: [Cascade.ALL], orphanRemoval: true, }) diff --git a/src/modules/order/controllers/order.controller.ts b/src/modules/order/controllers/order.controller.ts deleted file mode 100644 index 450c660..0000000 --- a/src/modules/order/controllers/order.controller.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { Controller, Get, Post, Param, UseGuards, Patch, Query, Body, Delete } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; -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 { - CreateOrderAsUSerDto, CreateOrderItemAsUserDto, - CreateOrderItemDtoAsAdmin, CreateOrderAsAdminDto -} from '../dto/create-order.dto'; -import { AdminId } from 'src/common/decorators/admin-id.decorator'; -import { AssignDesignerDto } from '../dto/assign-designer.dto'; -import { UpdateOrderItemDtoAsAdmin, UpdateOrderItemDtoAsUser } from '../dto/update-order-item.dto'; -import { UpdateOrderAsAdminDto } from '../dto/update-order.dto'; -import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { CreatePrintFormDto } from '../dto/create-print-form.dto'; -import { FindOrderItemsDto } from '../dto/find-order-items.dto'; -import { Permissions } from 'src/common/decorators/permissions.decorator'; -import { PermissionEnum } from 'src/common/enums/permission.enum'; - -@ApiTags('orders') -@ApiBearerAuth() -@Controller() -export class OrderController { - constructor(private readonly orderService: OrderService) { } - - // ================== Public =================== - - @Post('public/orders') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'create order ' }) - createOrder(@UserId() userId: string, @Body() body: CreateOrderAsUSerDto) { - return this.orderService.createOrderAsUser(userId, body); - } - - @Delete('public/orders/:id') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Delete order ' }) - deleteOrder(@Param('id') orderId: string, @UserId() userId: string) { - return this.orderService.removeOrderAsUser(userId, orderId); - } - - @Get('public/orders') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) { - return this.orderService.findOrdersAsUser(userId, dto); - } - - @Get('public/orders/:id') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'order detail' }) - orderDetail(@Param('id') orderId: string, @UserId() userId: string) { - return this.orderService.getOrderAsUser(userId, orderId); - } - - @Post('public/orders/:id/item') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'add item to order ' }) - addOrderItem(@Param('id') orderId: string, @UserId() userId: string, @Body() body: CreateOrderItemAsUserDto) { - return this.orderService.addOrderItemAsUser(userId, orderId, body); - } - - - @Patch('public/orders/:id/item/:itemId') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Update order item ' }) - updateOrderItem(@Param('id') orderId: string, @UserId() userId: string, @Param('itemId') itemId: string, @Body() body: UpdateOrderItemDtoAsUser) { - return this.orderService.updateOrderItemAsUser(userId, orderId, itemId, body); - } - - - @Patch('public/orders/:orderId/items/:orderItemId/confirm') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Confirm Invoice Item ' }) - confirmOrderItem( - @Param('orderId') orderId: string, - @Param('orderItemId') orderItemId: string, - @UserId() userId: string - ) { - return this.orderService.confirmOrderItem(userId, orderId, orderItemId); - } - - @Delete('public/orders/:orderId/items/:orderItemId') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Remove order Item ' }) - removeOrderItem( - @Param('orderId') orderId: string, - @Param('orderItemId') orderItemId: string, - @UserId() userId: string - ) { - return this.orderService.removeOrderItemAsUser(userId, orderId, orderItemId); - } - - /*========================== Admin Routes =====================*/ - - @Post('admin/orders') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.CREATE_ORDER) - @ApiOperation({ summary: 'create order ' }) - createOrderAsAdmin(@AdminId() adminId: string, @Body() body: CreateOrderAsAdminDto) { - return this.orderService.createOrderAsAdmin(adminId, body); - } - - @Get('admin/orders/request') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.VIEW_REQUESTS) - @ApiOperation({ summary: 'Get all order requests with pagination and filters' }) - findOrderRequestAsAdmin(@Query() dto: FindOrdersDto) { - return this.orderService.findOrderRequestsAsAdmin(dto); - } - - // this route permission is handled inside service - @Get('admin/orders/invoiced') - @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'Get all invoiced orders with pagination and filters' }) - findInvoicedOrdersAsAdmin(@Query() dto: FindOrdersDto, @AdminId() adminId: string) { - return this.orderService.findInvoicedOrdersAsAdmin(adminId, dto); - } - - @Get('admin/orders/:id') - @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'order detail' }) - orderDetailAsAdmin(@Param('id') orderId: string) { - return this.orderService.findOrderOrFail(orderId); - } - - @Patch('admin/orders/:id') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'Update Order ' }) - updateOrder(@Param('id') id: string, @Body() dto: UpdateOrderAsAdminDto) { - return this.orderService.updateOrderAsAdmin2(id, dto); - } - - @Delete('admin/orders/:orderId') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.REMOVE_ORDER) - @ApiOperation({ summary: 'Hard Delete Order ' }) - hardDelete(@Param('orderId') orderId: string) { - return this.orderService.hardDeleteOrder(orderId); - } - - // =============== Order Item ================ - - @Get('admin/orders/item/print') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.VIEW_All_ORDERS) - @ApiOperation({ summary: 'get print orders' }) - getPrintOrders(@Body() dto: FindOrderItemsDto) { - return this.orderService.findPrintOrderItemsAsAdmin(dto); - } - - @Get('admin/orders/item/:id') - @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'order item detail' }) - getOrderItem(@Param('id') itemId: string) { - return this.orderService.findOrderItemOrFailByItemId(itemId); - } - - - @Post('admin/orders/:orderId/item/:itemId/designer') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.ASSIGN_DESIGNER) - @ApiOperation({ summary: 'Assign Order Designer ' }) - assignDesigner(@Param('orderId') orderId: string, @Param('itemId') itemId: string, - @Body() body: AssignDesignerDto) { - return this.orderService.assignDesigner(orderId, itemId, body.designerId); - } - - @Patch('admin/orders/:id/item/:itemId/print') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.CREATE_ORDER_PRINT) - @ApiOperation({ summary: 'Create Print form for order item' }) - createPrintForm(@Param('id') id: string, @Param('itemId') itemId: string, @Body() dto: CreatePrintFormDto) { - return this.orderService.createPrintForm(id, itemId, dto); - } - - // @Post('admin/orders/:orderId/invoice') - // @UseGuards(AdminAuthGuard) - // @ApiOperation({ summary: 'Create Order invoice ' }) - // createInvoice(@Param('orderId') orderId: string) { - // return this.orderService.createInvoice(orderId); - // } - - - @Delete('admin/orders/:id/items/:itemId') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'Remove order Item ' }) - removeOrderItemAdAdmin( - @Param('id') orderId: string, - @Param('itemId') itemId: string, - ) { - return this.orderService.removeOrderItemAsAdmin(orderId, itemId); - } - - @Post('admin/orders/:id/item') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'add item to order ' }) - addOrderItemAsAdmin(@Param('id') orderId: string, @Body() body: CreateOrderItemDtoAsAdmin) { - return this.orderService.addOrderItemAsAdmin(orderId, body); - } - - - @Patch('admin/orders/:id/item/:itemId') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.UPDATE_ORDER) - @ApiOperation({ summary: 'Update order item' }) - updateOrderItemAsAdmin(@Param('id') orderId: string, @Param('itemId') itemId: string, - @Body() body: UpdateOrderItemDtoAsAdmin) { - return this.orderService.updateOrderItemAsAdmin(orderId, itemId, body); - } - -} diff --git a/src/modules/order/crone/order.crone.ts b/src/modules/order/crone/order.crone.ts deleted file mode 100644 index 0013f67..0000000 --- a/src/modules/order/crone/order.crone.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { EntityManager } from '@mikro-orm/postgresql'; -import { EventEmitter2 } from '@nestjs/event-emitter'; - - -@Injectable() -export class OrdersCrone { - private readonly logger = new Logger(OrdersCrone.name); - - constructor( - private readonly em: EntityManager, - private readonly eventEmitter: EventEmitter2, - ) { } - - -} diff --git a/src/modules/order/dto/assign-designer.dto.ts b/src/modules/order/dto/assign-designer.dto.ts deleted file mode 100644 index 9894459..0000000 --- a/src/modules/order/dto/assign-designer.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { - IsString -} from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; - -export class AssignDesignerDto { - @IsString() - @ApiProperty() - designerId: string; -} - diff --git a/src/modules/order/dto/create-order.dto.ts b/src/modules/order/dto/create-order.dto.ts deleted file mode 100644 index 850e077..0000000 --- a/src/modules/order/dto/create-order.dto.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { - IsString, - IsInt, IsArray, IsNumber, - IsNotEmpty, - ArrayMinSize, - IsOptional, - IsBoolean, - IsEnum, - IsDateString -} from 'class-validator'; -import { ApiPropertyOptional, ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IAttachment, IField, OrderItemStatusEnum } from '../interface/order.interface'; - - -export class CreateOrderItemAsUserDto { - @IsString() - @ApiProperty() - productId: string; - - @ApiProperty() - @IsNumber() - quantity: number - - @ApiProperty({ enum: OrderItemStatusEnum }) - @IsEnum(() => OrderItemStatusEnum) - status: OrderItemStatusEnum - - @ApiProperty() - @IsArray() - attributes: IField[] - - @ApiPropertyOptional({ example: 'توضیحات' }) - @IsString() - description: string - - @ApiPropertyOptional({ example: [] }) - @IsArray() - attachments: { url: string, type: string }[] - - -} - -export class CreateOrderItemDtoAsAdmin extends CreateOrderItemAsUserDto { - // @ApiPropertyOptional({ example: [] }) - // @IsArray() - // printAttributes?: IField[] - - // @ApiPropertyOptional({ example: [] }) - // @IsArray() - // printAttachments?: IAttachment[] - - @IsInt() - @ApiProperty() - designerId: string; - - @ApiProperty() - @IsNumber() - unitPrice: number - - @ApiProperty() - @IsNumber() - discount: number - - @ApiPropertyOptional() - @IsString() - adminDescription: string - - @ApiPropertyOptional() - @IsDateString() - confirmedAt: string - -} - -export class CreateOrderAsUSerDto { - @IsArray() - @ApiProperty({ - isArray: true, type: [CreateOrderItemAsUserDto], - example: [ - { - productId: 'sd4', - quantity: 100, - attributes: [ - { - attributeId: 1, - value: 'string | boolean| number' - } - ], - attachments: [ - { url: '', type: '' } - ], - description: '' - } - ] - }) - @IsNotEmpty() - @ArrayMinSize(1, { message: 'At least one item is required' }) - @Type(() => CreateOrderItemAsUserDto) - items: CreateOrderItemAsUserDto[]; -} - - -export class CreateOrderAsAdminDto { - @ApiProperty({ example: '' }) - @IsString() - @IsNotEmpty() - userId: string - - @IsArray() - @ApiProperty({ - isArray: true, type: [CreateOrderItemDtoAsAdmin], example: [ - { - productId: 1, - designerId: '', - attachments: [{ url: '', type: '' }], - quantity: 100, - attributes: [{ fieldId: 10, value: 'blue' }], - unitPrice: 150000, - discount: 20000, - adminDescription: '', - description: 'توضیحات کاربر', - - } - ] - }) - @IsNotEmpty() - @ArrayMinSize(1, { message: 'At least one product is required' }) - @Type(() => CreateOrderItemDtoAsAdmin) - items: CreateOrderItemDtoAsAdmin[]; - - - @ApiProperty({}) - @IsString() - @IsOptional() - paymentMethod: string - - @ApiProperty({}) - @IsBoolean() - enableTax: boolean - - @ApiProperty({}) - @IsInt() - estimatedDays: number - - -} diff --git a/src/modules/order/dto/create-print-form.dto.ts b/src/modules/order/dto/create-print-form.dto.ts deleted file mode 100644 index 532664b..0000000 --- a/src/modules/order/dto/create-print-form.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IsArray, } from 'class-validator'; -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IField } from '../interface/order.interface'; - -export class CreatePrintFormDto { - @ApiPropertyOptional({ - example: [{ - fieldId: 10, value: '' - }] - }) - @IsArray() - printAttributes?: IField[] - -} - - diff --git a/src/modules/order/dto/find-order-items.dto.ts b/src/modules/order/dto/find-order-items.dto.ts deleted file mode 100644 index 4a6bfa7..0000000 --- a/src/modules/order/dto/find-order-items.dto.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - IsOptional, - IsString, - IsNumber, - Min, - IsIn, - IsEnum, - IsDateString, - IsArray, -} from 'class-validator'; -import { Type, Transform } from 'class-transformer'; -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { OrderStatusEnum } from '../interface/order.interface'; -import { PaymentStatusEnum } from '../../payment/interface/payment'; - -const sortOrderOptions = ['asc', 'desc'] as const; -type SortOrder = (typeof sortOrderOptions)[number]; - -export class FindOrderItemsDto { - @ApiPropertyOptional({ default: 1, minimum: 1 }) - @IsOptional() - @Type(() => Number) - @IsNumber() - @Min(1) - page: number = 1; - - @ApiPropertyOptional({ default: 10, minimum: 1 }) - @IsOptional() - @Type(() => Number) - @IsNumber() - @Min(1) - limit: number = 10; - - - @ApiPropertyOptional() - @IsOptional() - @IsString() - search?: string; - - - @ApiPropertyOptional({ default: 'createdAt' }) - @IsOptional() - @IsString() - orderBy: string = 'createdAt'; - - @ApiPropertyOptional({ - enum: sortOrderOptions, - default: 'desc', - }) - @IsOptional() - @IsIn(sortOrderOptions) - order: SortOrder = 'desc'; -} diff --git a/src/modules/order/dto/find-orders.dto.ts b/src/modules/order/dto/find-orders.dto.ts deleted file mode 100644 index 941a134..0000000 --- a/src/modules/order/dto/find-orders.dto.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - IsOptional, - IsString, - IsNumber, - Min, - IsIn, - IsEnum, - IsDateString, - IsArray, -} from 'class-validator'; -import { Type, Transform } from 'class-transformer'; -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { OrderStatusEnum } from '../interface/order.interface'; -import { PaymentStatusEnum } from '../../payment/interface/payment'; - -const sortOrderOptions = ['asc', 'desc'] as const; -type SortOrder = (typeof sortOrderOptions)[number]; - -export class FindOrdersDto { - @ApiPropertyOptional({ default: 1, minimum: 1 }) - @IsOptional() - @Type(() => Number) - @IsNumber() - @Min(1) - page: number = 1; - - @ApiPropertyOptional({ default: 10, minimum: 1 }) - @IsOptional() - @Type(() => Number) - @IsNumber() - @Min(1) - limit: number = 10; - - - @ApiPropertyOptional({ - description: 'Filter by order statuses', - enum: OrderStatusEnum, - isArray: true, - }) - @IsOptional() - @Transform(({ value }) => - Array.isArray(value) ? value : value?.split(',') - ) - @IsArray() - @IsEnum(OrderStatusEnum, { each: true }) - statuses?: OrderStatusEnum[]; - - // @ApiPropertyOptional({ enum: PaymentStatusEnum }) - // @IsOptional() - // @IsEnum(PaymentStatusEnum) - // paymentStatus?: PaymentStatusEnum; - - @ApiPropertyOptional() - @IsOptional() - @IsString() - search?: string; - - @ApiPropertyOptional({ format: 'date-time' }) - @IsOptional() - @IsDateString() - from?: string; - - @ApiPropertyOptional({ format: 'date-time' }) - @IsOptional() - @IsDateString() - to?: string; - - @ApiPropertyOptional({ default: 'createdAt' }) - @IsOptional() - @IsString() - orderBy: string = 'createdAt'; - - @ApiPropertyOptional({ - enum: sortOrderOptions, - default: 'desc', - }) - @IsOptional() - @IsIn(sortOrderOptions) - order: SortOrder = 'desc'; -} diff --git a/src/modules/order/dto/update-order-item.dto.ts b/src/modules/order/dto/update-order-item.dto.ts deleted file mode 100644 index 41de503..0000000 --- a/src/modules/order/dto/update-order-item.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateOrderItemAsUserDto, CreateOrderItemDtoAsAdmin } from './create-order.dto'; - -export class UpdateOrderItemDtoAsUser extends PartialType(CreateOrderItemAsUserDto) { } -export class UpdateOrderItemDtoAsAdmin extends PartialType(CreateOrderItemDtoAsAdmin) { } - - diff --git a/src/modules/order/dto/update-order.dto.ts b/src/modules/order/dto/update-order.dto.ts deleted file mode 100644 index 879235d..0000000 --- a/src/modules/order/dto/update-order.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger'; -import { CreateOrderAsAdminDto, CreateOrderItemDtoAsAdmin } from './create-order.dto'; -import { IsNumber } from 'class-validator'; - -export class UpdateOrderAsAdminDto extends PartialType(OmitType(CreateOrderAsAdminDto, 'items' as any)) { - @ApiProperty() - items: UpdateOrCreateOrderItem[] -} - -export class UpdateOrCreateOrderItem extends CreateOrderItemDtoAsAdmin { - @ApiProperty() - @IsNumber() - id: string -} \ No newline at end of file diff --git a/src/modules/order/entities/order-item.entity.ts b/src/modules/order/entities/order-item.entity.ts deleted file mode 100644 index fb11edb..0000000 --- a/src/modules/order/entities/order-item.entity.ts +++ /dev/null @@ -1,82 +0,0 @@ -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, OrderItemStatusEnum } from '../interface/order.interface'; -import { Admin } from 'src/modules/admin/entities/admin.entity'; -import { BaseEntity } from 'src/common/entities/base.entity'; - - -@Entity({ tableName: 'order_items' }) -@Index({ properties: ['order'] }) -export class OrderItem extends BaseEntity { - [OptionalProps]?: 'createdAt' | 'deletedAt' | 'subTotal' | 'total'; - - @ManyToOne(() => Order) - order!: Order; - - @ManyToOne(() => Product) - product!: Product; - - @Enum(() => OrderItemStatusEnum) - status!: OrderItemStatusEnum; - - @ManyToOne(() => Admin, { nullable: true }) - designer?: Admin - - @Property({ type: 'json', nullable: true }) - attributes?: IField[] - - @Property({ type: 'int' }) - quantity!: number; - - @Property({ type: 'int', nullable: true, }) - unitPrice?: number; - - // @Formula(alias => ` - // COALESCE(${alias}.unit_price, 0) * ${alias}.quantity - // `) - @Property({ - type: 'int', - columnType: 'int GENERATED ALWAYS AS (COALESCE(unit_price, 0) * quantity) STORED', - persist: false, - nullable: true - }) - subTotal!: number; - - @Property({ - type: 'int', - nullable: true - }) - discount?: number; - - // @Formula(alias => ` - // (COALESCE(${alias}.unit_price, 0) * ${alias}.quantity) - // - COALESCE(${alias}.discount, 0) - // `) - @Property({ - type: 'int', - nullable: true, - columnType: 'int GENERATED ALWAYS AS ((COALESCE(unit_price, 0) * quantity) - COALESCE(discount, 0)) STORED', - persist: false - }) - total!: number; - - @Property({ type: 'text', nullable: true }) - description?: string; - - @Property({ type: 'text', nullable: true }) - adminDescription?: string; - - @Property({ type: 'json', nullable: true }) - attachments?: IAttachment[] // user attachments like voice and photos - - @Property({ type: 'json', nullable: true }) - printAttributes?: IField[] // print form selected attributes - - @Property({ nullable: true, columnType: 'timestamptz' }) - confirmedAt?: Date; - - @Property({ nullable: true, columnType: 'timestamptz' }) - printFormCreatedAt?: Date; - -} diff --git a/src/modules/order/entities/order.entity.ts b/src/modules/order/entities/order.entity.ts deleted file mode 100644 index c3b5b0c..0000000 --- a/src/modules/order/entities/order.entity.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - Entity, - Index, - ManyToOne, - OneToMany, - Property, - Collection, - Cascade, - Enum, - PrimaryKey, - OptionalProps, -} from '@mikro-orm/core'; -import { User } from '../../user/entities/user.entity'; -import { OrderItem } from './order-item.entity'; -import { Payment } from 'src/modules/payment/entities/payment.entity'; -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'] }) -export class Order extends BaseEntity { - [OptionalProps]?: 'createdAt' | 'deletedAt' | 'orderNumber' - - @PrimaryKey({ type: 'string', columnType: 'char(26)' }) - id: string = ulid() - - - @ManyToOne(() => User) - user!: User; - - @OneToMany(() => Payment, payment => payment.order, { - cascade: [Cascade.ALL], - orphanRemoval: true, - }) - payments = new Collection(this); - - @OneToMany(() => OrderItem, item => item.order, { - cascade: [Cascade.ALL], - orphanRemoval: true, - }) - items = new Collection(this); - - @ManyToOne(() => Admin, { nullable: true }) - creator?: Admin - - @Property({ - type: 'int', - autoincrement: true - }) - orderNumber: number; - - - @Property({ type: 'int', nullable: true }) - discount?: number; - - - @Property({ type: 'int', nullable: true }) - subTotal?: number; - - - @Property({ type: 'int', nullable: true }) - taxAmount?: number; - - - @Property({ type: 'int', nullable: true }) - total?: number; - - - @Property({ type: 'int', default: 0 }) - paidAmount: number = 0; - - - @Property({ type: 'int', default: 0 }) - balance: number = 0; - - - @Property({ type: 'int', nullable: true }) - estimatedDays?: number; // number of days to be done - - - @Enum(() => OrderStatusEnum) - status: OrderStatusEnum = OrderStatusEnum.REQUEST; - - - @Property({ type: 'string', nullable: true }) - paymentMethod?: string; - - @Property({ nullable: true, columnType: 'timestamptz' }) - invoicedAt?: Date; - - @Property({ type: 'boolean', default: false }) - enableTax?: boolean = false - - -} diff --git a/src/modules/order/events/order.events.ts b/src/modules/order/events/order.events.ts deleted file mode 100644 index f937188..0000000 --- a/src/modules/order/events/order.events.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { OrderStatusEnum } from '../interface/order.interface'; - -export class OrderCreatedEvent { - constructor( - public readonly orderId: string, - public readonly: string, - public readonly orderNumber: string, - public readonly total: number, - ) { } -} - -export class OrderStatusChangedEvent { - constructor( - public readonly orderId: string, - public readonly userId: string, - public readonly orderNumber: string, - public readonly: string, - public readonly previousStatus: OrderStatusEnum, - public readonly newStatus: OrderStatusEnum, - ) { } -} diff --git a/src/modules/order/interface/order.interface.ts b/src/modules/order/interface/order.interface.ts deleted file mode 100644 index 3a51499..0000000 --- a/src/modules/order/interface/order.interface.ts +++ /dev/null @@ -1,81 +0,0 @@ -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', - WAITING_to_CONFIRM_INVOICE = 'waiting_to_confirm_invoice', - - - DESIGN_INITIATED = 'design_initiated', - DESIGN_FINISHED = 'design_finished', - DESIGN_REVISION = 'design_revision', - - READY_TO_PRINTING = 'ready_to_printing', - IN_PRINTING = 'in_printing', - - READY_FOR_DELIVERY = 'ready_for_delivery', - DELIVERED = 'delivered', - - AFTER_PRINT_COVER = 'after_print_cover', - AFTER_PRINT_BOXING = 'after_print_boxing', - AFTER_PRINT_CARTONING = 'after_print_cartoning', - - - FINISHED = 'finished', - - CANCELED = 'canceled', -} - - - -export interface IAttachment { type: string, url: string } - -export interface CreateOrderItem { - productId: string, - quantity: number - status: OrderItemStatusEnum, - attributes?: IField[] - description?: string - attachments?: IAttachment[] - unitPrice?: number - discount?: number - printFormCreatedAt?: Date - - printAttributes?: IField[] - designerId?: string -} - -export type CreateOrderItemPopulated = Omit & { - product: Product, - designer?: Admin -} - -export interface CreateOrder { - userId: string - // status: OrderStatusEnum - enableTax?: boolean - paymentMethod?: string - adminId?: string - estimatedDays?: number -} - -export interface CreateOrderWithItems extends CreateOrder { - items: CreateOrderItem[] -} - -export type UpdateOrder = Partial - - -export type UpdateOrderItem = Partial - diff --git a/src/modules/order/listeners/order.listeners.ts b/src/modules/order/listeners/order.listeners.ts deleted file mode 100644 index d15550a..0000000 --- a/src/modules/order/listeners/order.listeners.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { OnEvent } from '@nestjs/event-emitter'; -import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; -import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events'; -import { PermissionEnum } from 'src/common/enums/permission.enum'; -import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface'; -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 { PaymentMethodEnum } from 'src/modules/payment/interface/payment'; -import { UserService } from 'src/modules/user/providers/user.service'; - -@Injectable() -export class OrderListeners { - private readonly logger = new Logger(OrderListeners.name); - private orderCreatedSmsTemplateId: string; - private orderStatusChangedSmsTemplateId: string; - // private orderCompletedSmsTemplateId: string; - constructor( - private readonly adminService: AdminRepository, - private readonly OrderRepository: OrderRepository, - private readonly notificationService: NotificationService, - private readonly configService: ConfigService, - private readonly userService: UserService, - ) { - this.orderCreatedSmsTemplateId = this.configService.get('SMS_PATTERN_ORDER_CREATED') ?? '123'; - this.orderStatusChangedSmsTemplateId = this.configService.get('SMS_PATTERN_ORDER_STATUS_CHANGE') ?? '123'; - // this.orderCompletedSmsTemplateId = this.configService.get('SMS_PATTERN_ORDER_STATUS_COMPLETED') ?? '123'; - } - - // private getStatusFarsi(status: OrderStatus): string { - // const statusMap: Record = { - // [OrderStatus.PENDING_PAYMENT]: 'در انتظار پرداخت', - // [OrderStatus.PAID]: 'پرداخت شده', - // [OrderStatus.PREPARING]: 'در حال آماده‌سازی', - // [OrderStatus.DELIVERED_TO_RECEPTIONIST]: 'تحویل به پذیرش', - // [OrderStatus.DELIVERED_TO_WAITER]: 'تحویل به گارسون', - // [OrderStatus.SHIPPED]: 'ارسال شده', - // [OrderStatus.COMPLETED]: 'تکمیل شده', - // [OrderStatus.CANCELED]: 'لغو شده', - // }; - // return statusMap[status] || status; - // } - - // @OnEvent(OrderCreatedEvent.name) - // async handleOrderCreated(event: OrderCreatedEvent) { - // try { - // this.logger.log( - // `Order created event received: ${event.orderId} for restaurant: ${event.} and order number: ${event.orderNumber}`, - // ); - - // const order = await this.OrderRepository.findOne(event.orderId); - // if (order?.paymentMethod.method === PaymentMethodEnum.Online) { - // return; - // } - - - // // get admnin os restuaraant that have order permissuins - // const admins = await this.adminService.findAdminsWithPermission(event., Permission.MANAGE_ORDERS); - // const recipients = admins.map(admin => ({ - // adminId: admin.id, - // })); - - // await this.notificationService.sendNotification({ - - // message: { - // title: NotifTitleEnum.ORDER_CREATED, - // content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`, - // sms: { - // templateId: this.orderCreatedSmsTemplateId, - // parameters: { - // orderNumber: event.orderNumber, - // total: event.total.toString(), - // }, - // }, - // pushNotif: { - // title: `سفارش جدید`, - // content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`, - // icon: `/`, - // action: { - // type: NotifTitleEnum.ORDER_CREATED, - // url: `/`, - // }, - // }, - // }, - // recipients, - // metadata: { - // priority: 1, - // }, - // }); - // } catch (error) { - // this.logger.error( - // `Failed to send notification for order created event: ${event.}`, - // error instanceof Error ? error.stack : String(error), - // ); - // } - // } - - // @OnEvent(OrderStatusChangedEvent.name) - // async handleOrderStatusChanged(event: OrderStatusChangedEvent) { - // try { - // this.logger.log( - // `Order status changed event received: ${event.orderId} for restaurant: ${event.} and order number: ${event.orderNumber}`, - // ); - // //TODO : REFACTOR to use queue or other way to handle this - // const recipients = [ - // { - // userId: event.userId, - // }, - // ]; - // if (event.newStatus === OrderStatus.COMPLETED) { - - // if (!event?.userId) { - // this.logger.log( - // `User not found for order: ${event.orderId} for restaurant: ${event.} and order number: ${event.orderNumber}`, - // ); - // } - - // // const restaurant = await this.RestaurantRepository.findOne(event.); - // // if (!restaurant) { - // // this.logger.log( - // // `Restaurant not found for order: ${event.orderId} for restaurant: ${event.} and order number: ${event.orderNumber}`, - // // ); - // // return; - // // } - // // const score = restaurant.score; - // // if (!score) { - // // this.logger.log( - // // `Score not found for restaurant: ${event.} and order number: ${event.orderNumber}`, - // // ); - // // return; - // // } - - // // increase score for user - // // const order = await this.OrderRepository.findOne(event.orderId); - // // if (!order) { - // // this.logger.log( - // // `Order not found for order: ${event.orderId} for restaurant: ${event.} and order number: ${event.orderNumber}`, - // // ); - // // return; - // // } - // // this.userService.createWalletTransaction(event.userId, event., { - // // amount: order.subTotal, - // // type: WalletTransactionType.CREDIT, - // // reason: WalletTransactionReason.ORDER_COMPLETED_DEPOSIT, - // // }); - - // await this.notificationService.sendNotification({ - // : event., - // message: { - // title: NotifTitleEnum.ORDER_STATUS_CHANGED, - // content: `لطفابرای ثبت نظر سفارش ${event.orderNumber} به اپ مراجعه کنید`, - // sms: { - // templateId: this.orderCreatedSmsTemplateId, - // parameters: { - // orderNumber: event.orderNumber, - // }, - // }, - // pushNotif: { - // title: `تغییر وضعیت سفارش`, - // content: `لطفا برای ثبت نظر سفارش ${event.orderNumber} به اپ مراجعه کنید`, - // icon: `/`, - // action: { - // type: NotifTitleEnum.ORDER_STATUS_CHANGED, - // url: ``, - // }, - // }, - // }, - // recipients, - // metadata: { - // priority: 1, - // }, - // }); - // } else { - // await this.notificationService.sendNotification({ - // : event., - // message: { - // title: NotifTitleEnum.ORDER_STATUS_CHANGED, - // content: `وضعیت سفارش شماره ${event.orderNumber} به ${this.getStatusFarsi(event.newStatus)} تغییر کرد`, - // sms: { - // templateId: this.orderStatusChangedSmsTemplateId, - // parameters: { - // orderNumber: event.orderNumber, - // status: this.getStatusFarsi(event.newStatus), - // }, - // }, - // pushNotif: { - // title: `تغییر وضعیت سفارش`, - // content: `وضعیت سفارش شماره ${event.orderNumber} به ${this.getStatusFarsi(event.newStatus)} تغییر کرد`, - // icon: `/`, - // action: { - // type: NotifTitleEnum.ORDER_STATUS_CHANGED, - // url: ``, - // }, - // }, - // }, - // recipients, - // metadata: { - // priority: 1, - // }, - // }); - // } - // } catch (error) { - // this.logger.error( - // `Failed to send notification for order status changed event: ${event.}`, - // error instanceof Error ? error.stack : String(error), - // ); - // } - // } - -} diff --git a/src/modules/order/order.module.ts b/src/modules/order/order.module.ts deleted file mode 100644 index 53f9a3b..0000000 --- a/src/modules/order/order.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Module, forwardRef } from '@nestjs/common'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { OrderService } from './providers/order.service'; -import { OrderController } from './controllers/order.controller'; -import { Order } from './entities/order.entity'; -import { OrderItem } from './entities/order-item.entity'; -import { AuthModule } from '../auth/auth.module'; -import { PaymentModule } from '../payment/payments.module'; -import { JwtModule } from '@nestjs/jwt'; -import { OrderRepository } from './repositories/order.repository'; -import { OrderListeners } from './listeners/order.listeners'; -import { OrdersCrone } from './crone/order.crone'; -import { AdminModule } from '../admin/admin.module'; -import { NotificationsModule } from '../notification/notifications.module'; -import { UserModule } from '../user/user.module'; -import { UtilsModule } from '../util/utils.module'; -import { ProductModule } from '../product/product.module'; -import { TicketModule } from '../ticket/ticket.module'; -import { OrderItemRepository } from './repositories/order-item.repository'; - -@Module({ - imports: [ - MikroOrmModule.forFeature([Order, OrderItem]), - AuthModule, - forwardRef(() => PaymentModule), - JwtModule, - AdminModule, - NotificationsModule, - UtilsModule, - forwardRef(() => UserModule), - ProductModule, - forwardRef(() => TicketModule) - ], - controllers: [OrderController], - providers: [OrderService, OrderRepository, OrderListeners, - OrdersCrone, OrderItemRepository], - exports: [OrderRepository, OrderService, OrderItemRepository], -}) -export class OrderModule { } diff --git a/src/modules/order/providers/order.service.ts b/src/modules/order/providers/order.service.ts deleted file mode 100644 index 1674b7d..0000000 --- a/src/modules/order/providers/order.service.ts +++ /dev/null @@ -1,636 +0,0 @@ -import { Injectable, BadRequestException, Logger, BadGatewayException } from '@nestjs/common'; -import { EntityManager } from '@mikro-orm/postgresql'; -import { OrderRepository } from '../repositories/order.repository'; -import { FindOrdersDto } from '../dto/find-orders.dto'; -import { EventEmitter2 } from '@nestjs/event-emitter'; -import { - CreateOrderAsAdminDto, - CreateOrderAsUSerDto, CreateOrderItemAsUserDto, - CreateOrderItemDtoAsAdmin -} from '../dto/create-order.dto'; -import { UserService } from 'src/modules/user/providers/user.service'; -import { - OrderItemStatusEnum, - CreateOrderWithItems, - CreateOrderItemPopulated, - UpdateOrder, - UpdateOrderItem, - 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'; -import { TicketService } from 'src/modules/ticket/providers/tickets.service'; -import { TicketRepository } from 'src/modules/ticket/repositories/tickets.repository'; -import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; -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 { AdminService } from 'src/modules/admin/providers/admin.service'; -import { OrderItem } from '../entities/order-item.entity'; -import { UpdateOrderAsAdminDto } from '../dto/update-order.dto'; -import { CreatePrintFormDto } from '../dto/create-print-form.dto'; -import { FindOrderItemsDto } from '../dto/find-order-items.dto'; -import { PermissionService } from 'src/modules/roles/providers/permissions.service'; -import { PermissionEnum } from 'src/common/enums/permission.enum'; - - -@Injectable() -export class OrderService { - private readonly logger = new Logger(OrderService.name); - - constructor( - private readonly em: EntityManager, - 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, - private readonly ticketService: TicketService, - private readonly eventEmitter: EventEmitter2, - private readonly adminRepository: AdminRepository, - private readonly paymentRepository: PaymentRepository, - private readonly permissionService: PermissionService, - ) { } - - async createOrderAsAdmin(adminId: string, dto: CreateOrderAsAdminDto) { - const order = await this.createOrder({ ...dto, adminId }) - // await this.calculateOrder(order) - // this.em.flush() - this.logger.log("Order created as admin") - return order - - } - - async createOrderAsUser(userId: string, dto: CreateOrderAsUSerDto) { - const order = await this.createOrder({ ...dto, userId }) - this.logger.log("Order created as user") - return order - - } - - async updateOrderAsAdmin(orderId: string, dto: UpdateOrderAsAdminDto) { - const order = await this.findOrderOrFail(orderId) - - // const updateOrder = await this.updateOrder(order, dto) - - // const updateOrder = await this.calculateOrder(order) - - // await this.em.flush() - - this.logger.log("Order updated as admin") - - // return updateOrder - } - - - async updateOrderAsAdmin2(orderId: string, dto: UpdateOrderAsAdminDto) { - const order = await this.findOrderOrFail(orderId) - - const { items, ...rest } = dto - - return this.em.transactional(async (em) => { - - em.assign(order, rest) - - for (const item of items) { - - if (item.id) { // update - const currentItem = order.items.find(i => i.id == item.id) - - if (!currentItem) { - throw new BadGatewayException("Order Item not found") - } - - const { designerId, productId, ...itemRest } = item - - let newProduct = currentItem.product - - if (productId && productId !== currentItem.product.id) { - - newProduct = await this.productService.findOneOrFail(productId) - - } - - let newDesigner: Admin | undefined - - if (designerId && designerId !== currentItem.designer?.id) { - - newDesigner = await this.adminService.findOrFail(designerId) - - } - - em.assign(currentItem, { ...itemRest, designer: newDesigner, product: newProduct }) - - - } else { - - if (!item.productId) { - throw new BadRequestException(`Product id is required for item ${item.id}`) - } - - const product = await this.productService.findOneOrFail(item.productId) - - const newOrderItem = em.create(OrderItem, { - order, - product, - quantity: item.quantity, - unitPrice: item.unitPrice, - - adminDescription: item.adminDescription, - attachments: item.attachments, - attributes: item.attributes, - description: item.description, - discount: item.discount, - confirmedAt: item.confirmedAt, - status: OrderItemStatusEnum.CREATED - }) - - em.persist(newOrderItem) - } - - } - - await em.flush() - - this.logger.log(`order #${order.orderNumber} was updated as Admin`) - - return order - }) - - - } - - async addOrderItemAsUser(userId: string, orderId: string, dto: CreateOrderItemAsUserDto) { - - 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.user.id !== userId) { - throw new BadRequestException(`This order doest belongs to you!`) - } - - const product = await this.productService.findOneOrFail(dto.productId) - - const orderItem = this.createOrderItemEntity(order, { ...dto, product }) - - await this.em.flush() - - return orderItem - } - - async addOrderItemAsAdmin(orderId: string, dto: CreateOrderItemDtoAsAdmin) { - - const order = await this.findOrderOrFail(orderId) - - const product = await this.productService.findOneOrFail(dto.productId) - - let designer: Admin | undefined - if (dto.designerId) { - designer = await this.adminService.findOrFail(dto.designerId) - } - - const orderItem = this.createOrderItemEntity(order, { ...dto, designer, product }) - - await this.em.flush() - - // await this.calculateOrder(undefined, orderId) - - // await this.em.flush() - - return orderItem - } - - async updateOrderItemAsUser(userId: string, orderId: string, itemId: string, dto: UpdateOrderItemDtoAsUser) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - if (orderItem.order.user.id !== userId) { - 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.confirmedAt) { - throw new BadRequestException(`You can not update when item is confirmed`) - } - - const updated = await this.updateOrderItem(orderItem, dto) - - return updated - } - - async updateOrderItemAsAdmin(orderId: string, itemId: string, dto: UpdateOrderItemDtoAsAdmin) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - const updated = await this.updateOrderItem(orderItem, dto) - - // await this.calculateOrder(undefined, orderId) - - // await this.em.flush() - - return updated - } - - async createPrintForm(orderId: string, itemId: string, dto: CreatePrintFormDto) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - const updated = await this.updateOrderItem(orderItem, { ...dto, printFormCreatedAt: new Date() }) - - return updated - } - - async removeOrderItemAsAdmin(orderId: string, itemId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - await this.removeOrderItem(orderItem) - - // await this.calculateOrder(undefined, orderId) - - // await this.em.flush() - - return { message: 'success' } - } - - async removeOrderItemAsUser(userId: string, orderId: string, itemId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, itemId) - - if (orderItem.order.user.id !== userId) { - 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.confirmedAt) { - throw new BadRequestException(`You can not delete when item is confirmed`) - } - - await this.removeOrderItem(orderItem) - - return { message: 'success' } - } - - - async findOrdersAsUser(userId: string, dto: FindOrdersDto) { - const orders = await this.orderRepository.findAllPaginated({ userId, ...dto }) - return orders - } - - async findOrderRequestsAsAdmin(dto: FindOrdersDto) { - const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: false }) - return orders - } - - async findInvoicedOrdersAsAdmin(adminId: string, dto: FindOrdersDto) { - const canSeeAll = await this.adminService.hasPermission(adminId, PermissionEnum.VIEW_All_INVOICED_ORDERS) - - let canSeeAsigned = false - - if (!canSeeAll) { - canSeeAsigned = await this.adminService.hasPermission(adminId, PermissionEnum.VIEW_MY_ASSIGNED_INVOICED_REQUESTS) - } - - if (!canSeeAsigned && !canSeeAll) { - throw new BadRequestException("You dont have Permission") - } - - let designerId = canSeeAll ? undefined : adminId - - const orders = await this.orderRepository.findAllPaginated({ ...dto, isInvoiced: true, designerId }) - - return orders - } - - async findPrintOrderItemsAsAdmin(dto: FindOrderItemsDto) { - const orderItems = await this.orderItemRepository.findAllPaginated({ ...dto, isPrintFormAdded: true, }) - return orderItems - } - - async findPrints(dto: FindOrdersDto) { - const orders = await this.orderRepository.findAllPaginated({ ...dto, statuses: [] }) - return orders - } - - - async confirmOrderItem(userId: string, orderId: string, orderItemId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, orderItemId) - - - if (orderItem.order.user.id !== userId) { - throw new BadRequestException("Order Item does not belong to you") - } - - orderItem.confirmedAt = new Date() - - await this.em.persistAndFlush(orderItem) - - return orderItem - } - - async assignDesigner(orderId: string, orderItemId: string, designerId: string) { - - const orderItem = await this.findOrderItemOrFail(orderId, orderItemId) - - const designer = await this.adminRepository.findOne({ id: designerId }) - - if (!designer) { - throw new BadRequestException("designer not found") - } - - orderItem.designer = designer - - await this.em.persistAndFlush(orderItem) - - return orderItem - - } - - async removeOrderAsUser(userId: string, orderId: string) { - const order = await this.findOrderOrFail(orderId) - - if (order.user.id !== userId) { - throw new BadRequestException("this order doesnt belongs to you") - } - - // 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.updateStatus(orderId, OrderStatusEnum.INVOICED) - // return order - // } - - async getOrderAsUser(userId: string, orderId: string) { - const order = await this.findOrderOrFail(orderId) - if (order.user.id !== userId) { - throw new BadRequestException("This order is not belongs to you") - } - return order - } - - // ==================== Entity Methods ==================== - - private async createOrderItemEntity(order: Order, dto: CreateOrderItemPopulated, em?: EntityManager) { - const { attributes, description, quantity, attachments, discount, - unitPrice, product, designer, printAttributes, status } = dto - - const eM = em ?? this.em - return eM.create( - OrderItem, - { - designer, - order, - attributes, - description, - quantity, - attachments, - discount, - unitPrice, - product, - printAttributes: printAttributes, - status - }) - } - - // ==================== Core Logic ==================== - - async createOrder(dto: CreateOrderWithItems) { - const { items, userId, enableTax, - estimatedDays, paymentMethod, 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 products = await this.productService.findProductsByIds(items.map(item => item.productId)) - const desiners = await this.adminService.findByIds(items.map(item => item.designerId).filter(id => id != null)) - - const productMap = new Map( - products.map(p => [Number(p.id), p]) - ); - const designerMap = new Map( - desiners.map(d => [d.id, d]) - ) - - return this.em.transactional(async (em) => { - - const order = em.create(Order, { - creator: admin, - user, - enableTax, - estimatedDays, - paymentMethod, - status: OrderStatusEnum.REQUEST, - }) - - em.persist(order) - - for (const item of items) { - const product = productMap.get(Number(item.productId)) - - if (!product) { - throw new BadRequestException("Product not found!") - } - let designer: undefined | Admin = undefined - - if (item.designerId) { - designer = designerMap.get(item.designerId) - if (!designer) { - throw new BadRequestException("designer not found!") - } - } - - const orderItem = await this.createOrderItemEntity(order, { ...item, designer, product }, 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: UpdateOrder) { - const { adminId, enableTax, - estimatedDays, paymentMethod, userId } = param - - - if (userId) { - const user = await this.userService.findOrFail(userId) - order.user = user - } - - - if (adminId != undefined && adminId !== null) { - const admin = await this.adminService.findOrFail(adminId) - order.creator = admin - } - - - if (enableTax != undefined) { - order.enableTax = enableTax - } - - if (estimatedDays != undefined) { - order.estimatedDays = estimatedDays - } - - if (paymentMethod != undefined) { - order.paymentMethod = paymentMethod - } - - // if (status != undefined) { - // order.status = status - - // if (status === OrderStatusEnum.INVOICED && !order.invoicedAt) { - // order.invoicedAt = new Date() - // } - // } - - - - await this.em.persistAndFlush(order) - - return order - } - - async updateOrderItem(orderItem: OrderItem, dto: UpdateOrderItem) { - const { attributes, description, product, quantity, printFormCreatedAt, - attachments, discount, unitPrice, designer, printAttributes, } = dto - - if (product && orderItem.product.id !== product.id) { - orderItem.product = product - } - - if (designer && orderItem.designer?.id !== designer.id) { - orderItem.designer = designer - } - - if (attributes != undefined) { - orderItem.attributes = attributes - } - - if (description != undefined) { - orderItem.description = description - } - if (quantity != undefined) { - orderItem.quantity = quantity - } - - if (attachments != undefined) { - orderItem.attachments = attachments - } - if (discount != undefined) { - orderItem.discount = discount - } - - if (unitPrice != undefined) { - orderItem.unitPrice = unitPrice - } - - if (printFormCreatedAt != undefined) { - orderItem.printFormCreatedAt = printFormCreatedAt - } - - if (printAttributes != undefined) { - orderItem.printAttributes = printAttributes - } - - await this.em.flush() - - return orderItem - } - - async removeOrderItem(orderItem: OrderItem) { - - await this.em.removeAndFlush(orderItem) - - return true - } - - async hardDeleteOrder(orderId: string) { - const order = await this.findOrderOrFail(orderId) - - if (order.payments.length > 0) { - throw new BadRequestException("order with payments can not be deleted!") - } - - await this.em.transactional(async (em) => { - await this.ticketRepository.nativeDelete({ orderItem: { order: { id: orderId } } }) - await this.orderRepository.nativeDelete(order) - // order item will be deleted because cascade is true - - // TODO : images also must be deleted - await em.flush() - }) - - return { message: "Order deleted successfully" } - - } - - // ==================== Helper Methods ==================== - - async findOrderOrFail(orderId: string) { - const order = await this.orderRepository.findOne({ id: orderId }, - { populate: ['items', 'items.product', 'payments', 'user', 'items.designer'] }) - if (!order) { - throw new BadRequestException('Order not found') - } - return order - } - - async findOrderItemOrFail(orderId: string, itemId: string) { - const orderItem = await this.orderItemRepository.findOne({ id: itemId, order: { id: orderId } }, - { populate: ['order', 'order.user'] }) - if (!orderItem) { - throw new BadRequestException('Order item not found') - } - return orderItem - } - - async findOrderItemOrFailByItemId(itemId: string) { - const orderItem = await this.orderItemRepository.findOne({ id: itemId }, - { populate: ['order', 'order.user'] }) - if (!orderItem) { - throw new BadRequestException('Order item not found') - } - return orderItem - } - -} diff --git a/src/modules/order/repositories/order-item.repository.ts b/src/modules/order/repositories/order-item.repository.ts deleted file mode 100644 index bd2b820..0000000 --- a/src/modules/order/repositories/order-item.repository.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { EntityManager, EntityRepository, FilterQuery } from '@mikro-orm/postgresql'; -import { OrderItem } from '../entities/order-item.entity'; -import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; -import { FindOrderItemsDto } from '../dto/find-order-items.dto'; - -class FindOrderItemsOpts extends FindOrderItemsDto { - userId?: string; - isPrintFormAdded?: boolean -}; - - -@Injectable() -export class OrderItemRepository extends EntityRepository { - constructor(readonly em: EntityManager) { - super(em, OrderItem); - } - - async findAllPaginated(opts: FindOrderItemsOpts): Promise> { - const { - page = 1, - limit = 10, - search, - orderBy = 'createdAt', - order = 'desc', - userId, - isPrintFormAdded - } = opts; - - const offset = (page - 1) * limit; - - const where: FilterQuery = {}; - - - - if (userId) { - where.order = { user: { id: userId } }; - } - - if (isPrintFormAdded != undefined) { - if (isPrintFormAdded == true) { - where.printFormCreatedAt = { $not: null } - } else { - where.printFormCreatedAt = null - } - } - - - // Search by order number or user information - if (search) { - const searchPattern = `%${search}%`; - const searchConditions: any[] = [ - { user: { firstName: { $ilike: searchPattern } } }, - { user: { lastName: { $ilike: searchPattern } } }, - { user: { phone: { $ilike: searchPattern } } }, - ]; - - // If search is numeric, also search by orderNumber - const numericSearch = Number(search); - if (!isNaN(numericSearch) && isFinite(numericSearch)) { - searchConditions.push({ orderNumber: numericSearch }); - } - - where.$or = searchConditions; - } - - // First, fetch orders without reviews - const [data, total] = await this.findAndCount(where, { - limit, - offset, - orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, - populate: ['designer', 'product'], - }); - - - const totalPages = Math.ceil(total / limit); - - return { - data, - meta: { - total, - page, - limit, - totalPages, - }, - }; - } -} diff --git a/src/modules/order/repositories/order.repository.ts b/src/modules/order/repositories/order.repository.ts deleted file mode 100644 index c72fef3..0000000 --- a/src/modules/order/repositories/order.repository.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; -import { FilterQuery } from '@mikro-orm/core'; -import { Order } from '../entities/order.entity'; -import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; -import { OrderStatusEnum } from '../interface/order.interface'; -import { FindOrdersDto } from '../dto/find-orders.dto'; - - -class FindOrdersOpts extends FindOrdersDto { - userId?: string; - isInvoiced?: boolean; - designerId?: string -}; - -@Injectable() -export class OrderRepository extends EntityRepository { - constructor(readonly em: EntityManager) { - super(em, Order); - } - - async findAllPaginated(opts: FindOrdersOpts): Promise> { - const { - page = 1, - limit = 10, - statuses, - search, - orderBy = 'createdAt', - order = 'desc', - userId, - from, - to, - isInvoiced, - designerId - } = opts; - - const offset = (page - 1) * limit; - - const where: FilterQuery = {}; - - // Filter by statuses - if (statuses) { - // Ensure statuses is always an array and normalize it - const normalizedStatuses = Array.isArray(statuses) ? statuses : [statuses]; - // Filter out any empty values - const validStatuses = normalizedStatuses.filter((s): s is OrderStatusEnum => !!s); - - if (validStatuses.length > 0) { - // Use direct equality for single value, $in for multiple values to avoid SQL generation issues - if (validStatuses.length === 1) { - where.status = validStatuses[0]; - } else { - where.status = { $in: validStatuses }; - } - } - } - - if (userId) { - where.user = { id: userId }; - } - - if (isInvoiced != undefined) { - if (isInvoiced == true) { - where.invoicedAt = { $not: null } - } else { - where.invoicedAt = null - } - } - - if (designerId) { - where.items = { $some: { designer: { id: designerId } } } - } - // Filter by date range - if (from || to) { - where.createdAt = {}; - if (from) { - where.createdAt.$gte = new Date(from); - } - if (to) { - where.createdAt.$lte = new Date(to); - } - } - - // Search by order number or user information - if (search) { - const searchPattern = `%${search}%`; - const searchConditions: any[] = [ - { user: { firstName: { $ilike: searchPattern } } }, - { user: { lastName: { $ilike: searchPattern } } }, - { user: { phone: { $ilike: searchPattern } } }, - ]; - - // If search is numeric, also search by orderNumber - const numericSearch = Number(search); - if (!isNaN(numericSearch) && isFinite(numericSearch)) { - searchConditions.push({ orderNumber: numericSearch }); - } - - where.$or = searchConditions; - } - - // First, fetch orders without reviews - const [data, total] = await this.findAndCount(where, { - limit, - offset, - orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, - populate: ['items', 'items.product', 'user'], - }); - - // Collect all (orderId, productId) pairs for efficient review lookup - // const orderproductPairs: Array<{ orderId: string; productId: bigint }> = []; - // for (const order of data) { - // for (const item of order.items.getItems()) { - // if (item.product?.id) { - // orderproductPairs.push({ orderId: order.id, productId: item.product.id }); - // } - // } - // } - - - - // Map reviewIds to products - // for (const order of data) { - // for (const item of order.items.getItems()) { - // if (item.product) { - // const key = `${order.id}-${item.product.id}`; - // // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - // const product = item.product as any; - // // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - - // } - // } - // } - - const totalPages = Math.ceil(total / limit); - - return { - data, - meta: { - total, - page, - limit, - totalPages, - }, - }; - } -} diff --git a/src/modules/payment/controllers/payment.controller.ts b/src/modules/payment/controllers/payment.controller.ts index ec68c85..da7e79d 100644 --- a/src/modules/payment/controllers/payment.controller.ts +++ b/src/modules/payment/controllers/payment.controller.ts @@ -5,7 +5,7 @@ import { Query, Delete, } from '@nestjs/common'; -import { PaymentService } from '../services/payments.service'; +// import { PaymentService } from '../services/payments.service'; import { ApiTags, ApiOperation, @@ -25,72 +25,72 @@ import { PermissionEnum } from 'src/common/enums/permission.enum'; @Controller() export class PaymentController { constructor( - private readonly paymentService: PaymentService, + // private readonly paymentService: PaymentService, ) { } - @Post('public/payments/order/:id/pay') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Pay for an order' }) - payAnOrder(@Param('id') orderId: string, @Body() dto: PayOrderDto) { - return this.paymentService.payOrder(orderId, dto); - } + // @Post('public/payments/order/:id/pay') + // @UseGuards(AuthGuard) + // @ApiOperation({ summary: 'Pay for an order' }) + // payAnOrder(@Param('id') orderId: string, @Body() dto: PayOrderDto) { + // return this.paymentService.payOrder(orderId, dto); + // } - @Post('public/payments/online/:token/verify') - @UseGuards(AuthGuard) - @ApiOperation({ summary: 'Verify online payment' }) - verifyOnlinePayment(@Param('paymentId') token: string) { - return this.paymentService.verifyOnlinePayment(token); - } + // @Post('public/payments/online/:token/verify') + // @UseGuards(AuthGuard) + // @ApiOperation({ summary: 'Verify online payment' }) + // verifyOnlinePayment(@Param('paymentId') token: string) { + // return this.paymentService.verifyOnlinePayment(token); + // } - @Get('public/payments') - @ApiOperation({ summary: 'get all payments as user' }) - findAllByUser(@UserId() userId: string, @Query() dto: FindPaymentsDto) { - return this.paymentService.findAllByUser(dto, userId); - } + // @Get('public/payments') + // @ApiOperation({ summary: 'get all payments as user' }) + // findAllByUser(@UserId() userId: string, @Query() dto: FindPaymentsDto) { + // return this.paymentService.findAllByUser(dto, userId); + // } - /*=============== Admin =============== */ + // /*=============== Admin =============== */ - @Post('admin/payments/order/:id/pay') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.MANAGE_PAYMENTS) - @ApiOperation({ summary: 'Pay for an order as Admin' }) - payOrderAsAdmin(@Param('orderId') orderId: string, @AdminId() adminId: string, @Body() dto: PayOrderDto) { - return this.paymentService.payOrder(orderId, dto, adminId); - } + // @Post('admin/payments/order/:id/pay') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.MANAGE_PAYMENTS) + // @ApiOperation({ summary: 'Pay for an order as Admin' }) + // payOrderAsAdmin(@Param('orderId') orderId: string, @AdminId() adminId: string, @Body() dto: PayOrderDto) { + // return this.paymentService.payOrder(orderId, dto, adminId); + // } - @Post('admin/payments/online/:token/verify') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.MANAGE_PAYMENTS) - @ApiOperation({ summary: 'Verify online payment as admin' }) - verifyOnlinePaymentAsAdmin(@Param('paymentId') token: string) { - return this.paymentService.verifyOnlinePayment(token); - } + // @Post('admin/payments/online/:token/verify') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.MANAGE_PAYMENTS) + // @ApiOperation({ summary: 'Verify online payment as admin' }) + // verifyOnlinePaymentAsAdmin(@Param('paymentId') token: string) { + // return this.paymentService.verifyOnlinePayment(token); + // } - @Post('admin/payments/:paymentId/cash/verify') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.MANAGE_PAYMENTS) - @ApiOperation({ summary: 'Verify cash payment' }) - confirmPayment(@Param('paymentId') paymentId: string) { - return this.paymentService.confirmCashPayment(paymentId); - } + // @Post('admin/payments/:paymentId/cash/verify') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.MANAGE_PAYMENTS) + // @ApiOperation({ summary: 'Verify cash payment' }) + // confirmPayment(@Param('paymentId') paymentId: string) { + // return this.paymentService.confirmCashPayment(paymentId); + // } - @Delete('admin/payments/:paymentId/cash/deny') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.MANAGE_PAYMENTS) - @ApiOperation({ summary: 'Deny cash payment' }) - denyPayment(@Param('paymentId') paymentId: string) { - return this.paymentService.denyCashPayment(paymentId); - } + // @Delete('admin/payments/:paymentId/cash/deny') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.MANAGE_PAYMENTS) + // @ApiOperation({ summary: 'Deny cash payment' }) + // denyPayment(@Param('paymentId') paymentId: string) { + // return this.paymentService.denyCashPayment(paymentId); + // } - @Get('admin/payments') - @UseGuards(AdminAuthGuard) - @Permissions(PermissionEnum.MANAGE_PAYMENTS) - @ApiOperation({ summary: 'get all payments as admin' }) - findAllPayments(@Body() dto: FindPaymentsDto) { - return this.paymentService.findAll(dto); - } + // @Get('admin/payments') + // @UseGuards(AdminAuthGuard) + // @Permissions(PermissionEnum.MANAGE_PAYMENTS) + // @ApiOperation({ summary: 'get all payments as admin' }) + // findAllPayments(@Body() dto: FindPaymentsDto) { + // return this.paymentService.findAll(dto); + // } } diff --git a/src/modules/payment/dto/find-payments.dto.ts b/src/modules/payment/dto/find-payments.dto.ts index 57f3866..735259e 100644 --- a/src/modules/payment/dto/find-payments.dto.ts +++ b/src/modules/payment/dto/find-payments.dto.ts @@ -58,7 +58,7 @@ export class FindPaymentsDto { @ApiPropertyOptional() @IsOptional() @IsString() - orderId?: string; + invoiceId?: string; @ApiPropertyOptional({ format: 'date-time' }) @IsOptional() diff --git a/src/modules/payment/entities/payment.entity.ts b/src/modules/payment/entities/payment.entity.ts index 872e76e..ba200af 100644 --- a/src/modules/payment/entities/payment.entity.ts +++ b/src/modules/payment/entities/payment.entity.ts @@ -1,9 +1,9 @@ import { Entity, ManyToOne, Property, Enum, Index, PrimaryKey, OptionalProps } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; -import { Order } from '../../order/entities/order.entity'; import { PaymentGatewayEnum, PaymentMethodEnum, PaymentStatusEnum } from '../interface/payment'; import { ulid } from 'ulid'; import { Admin } from 'src/modules/admin/entities/admin.entity'; +import { Invoice } from 'src/modules/invoice/entities/invoice.entity'; @Entity({ tableName: 'payments' }) export class Payment extends BaseEntity { @@ -12,9 +12,9 @@ export class Payment extends BaseEntity { @PrimaryKey({ type: 'string', columnType: 'char(26)' }) id: string = ulid() - @ManyToOne(() => Order) + @ManyToOne(() => Invoice) @Index() - order!: Order; + invoice!: Invoice; @ManyToOne(() => Admin, { nullable: true }) creator?: Admin diff --git a/src/modules/payment/interface/payment.ts b/src/modules/payment/interface/payment.ts index d9b59ea..79d6616 100644 --- a/src/modules/payment/interface/payment.ts +++ b/src/modules/payment/interface/payment.ts @@ -1,5 +1,5 @@ import { Admin } from 'src/modules/admin/entities/admin.entity'; -import type { Order } from 'src/modules/order/entities/order.entity'; +import { Invoice } from 'src/modules/invoice/entities/invoice.entity'; import { User } from 'src/modules/user/entities/user.entity'; export enum PaymentMethodEnum { @@ -28,7 +28,7 @@ export interface ICreatePayment { export interface OrderPaymentContext { admin: Admin|null user: User; - order: Order; + invoice: Invoice; amount: number; method: PaymentMethodEnum attachments?: string[] diff --git a/src/modules/payment/listeners/payment.listeners.ts b/src/modules/payment/listeners/payment.listeners.ts index 39f628f..2fe9fea 100644 --- a/src/modules/payment/listeners/payment.listeners.ts +++ b/src/modules/payment/listeners/payment.listeners.ts @@ -6,7 +6,7 @@ import { PermissionEnum } from 'src/common/enums/permission.enum'; import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface'; import { NotificationService } from 'src/modules/notification/services/notification.service'; import { ConfigService } from '@nestjs/config'; -import { OrderService } from 'src/modules/order/providers/order.service'; +// import { OrderService } from 'src/modules/order/providers/order.service'; @Injectable() export class PaymentListeners { @@ -17,7 +17,7 @@ export class PaymentListeners { private readonly adminService: AdminRepository, private readonly notificationService: NotificationService, private readonly configService: ConfigService, - private readonly orderService: OrderService, + // private readonly orderService: OrderService, ) { this.orderCreatedSmsTemplateId = this.configService.get('SMS_PATTERN_ORDER_CREATED') ?? '123'; this.paymentSucceedSmsTemplateId = this.configService.get('SMS_PATTERN_ONLINE_PAYMENT_SUCCEED') ?? '123'; diff --git a/src/modules/payment/payments.module.ts b/src/modules/payment/payments.module.ts index 07edb95..dc13fb1 100644 --- a/src/modules/payment/payments.module.ts +++ b/src/modules/payment/payments.module.ts @@ -1,5 +1,5 @@ import { Module, forwardRef } from '@nestjs/common'; -import { PaymentService } from './services/payments.service'; +// import { PaymentsService } from './services/payments.service'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { PaymentController } from './controllers/payment.controller'; import { AuthModule } from '../auth/auth.module'; @@ -11,7 +11,7 @@ import { PaymentRepository } from './repositories/payment.repository'; import { PaymentListeners } from './listeners/payment.listeners'; import { AdminModule } from '../admin/admin.module'; import { NotificationsModule } from '../notification/notifications.module'; -import { OrderModule } from '../order/order.module'; +// import { OrderModule } from '../order/order.module'; import { UserModule } from '../user/user.module'; @Module({ @@ -21,12 +21,12 @@ import { UserModule } from '../user/user.module'; JwtModule, AdminModule, NotificationsModule, - forwardRef(() => OrderModule), + // forwardRef(() => OrderModule), UserModule ], controllers: [PaymentController], providers: [ - PaymentService, + // PaymentService, PaymentRepository, ZarinpalGateway, GatewayManager, @@ -34,7 +34,7 @@ import { UserModule } from '../user/user.module'; ], exports: [ PaymentRepository, - PaymentService, + // PaymentService, // PaymentGatewayService, ], }) diff --git a/src/modules/payment/repositories/payment.repository.ts b/src/modules/payment/repositories/payment.repository.ts index 06165b2..e978a57 100644 --- a/src/modules/payment/repositories/payment.repository.ts +++ b/src/modules/payment/repositories/payment.repository.ts @@ -26,7 +26,7 @@ export class PaymentRepository extends EntityRepository { userId, from, to, - orderId + invoiceId } = opts; const offset = (page - 1) * limit; @@ -51,11 +51,11 @@ export class PaymentRepository extends EntityRepository { } if (userId) { - where.order = { user: { id: userId } }; + where.invoice = { user: { id: userId } }; } - if (orderId) { - where.order = { id: orderId } + if (invoiceId) { + where.invoice = { id: invoiceId } } // Filter by date range if (from || to) { diff --git a/src/modules/payment/services/payments.service.ts b/src/modules/payment/services/payments.service.ts index d59c951..c81384c 100644 --- a/src/modules/payment/services/payments.service.ts +++ b/src/modules/payment/services/payments.service.ts @@ -1,302 +1,301 @@ -import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; -import { PaymentGatewayEnum, PaymentMethodEnum, PaymentStatusEnum } from '../interface/payment'; -import { EntityManager } from '@mikro-orm/postgresql'; -import { Order } from '../../order/entities/order.entity'; -import { Logger } from '@nestjs/common'; -import { GatewayManager } from '../gateways/gateway.manager'; -import { OrderPaymentContext } from '../interface/payment'; -import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum'; -import { EventEmitter2 } from '@nestjs/event-emitter'; -import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events'; -import { OrderService } from 'src/modules/order/providers/order.service'; -import { PayOrderDto } from '../dto/pay-order.dto'; -import { PaymentRepository } from '../repositories/payment.repository'; -import { CreditRepository } from 'src/modules/user/repositories/credit.repository'; -import { CreditService } from 'src/modules/user/providers/credit.service'; -import { CreditTransactionType } from 'src/modules/user/interface/user'; -import { Payment } from '../entities/payment.entity'; -import { FindPaymentsDto } from '../dto/find-payments.dto'; -import { Admin } from 'src/modules/admin/entities/admin.entity'; -import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; -import { CreditTransaction } from 'src/modules/user/entities/credit-transaction.entity'; +// import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +// import { PaymentGatewayEnum, PaymentMethodEnum, PaymentStatusEnum } from '../interface/payment'; +// import { EntityManager } from '@mikro-orm/postgresql'; +// import { Logger } from '@nestjs/common'; +// import { GatewayManager } from '../gateways/gateway.manager'; +// import { OrderPaymentContext } from '../interface/payment'; +// import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum'; +// import { EventEmitter2 } from '@nestjs/event-emitter'; +// import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events'; +// import { OrderService } from 'src/modules/order/providers/order.service'; +// import { PayOrderDto } from '../dto/pay-order.dto'; +// import { PaymentRepository } from '../repositories/payment.repository'; +// import { CreditRepository } from 'src/modules/user/repositories/credit.repository'; +// import { CreditService } from 'src/modules/user/providers/credit.service'; +// import { CreditTransactionType } from 'src/modules/user/interface/user'; +// import { Payment } from '../entities/payment.entity'; +// import { FindPaymentsDto } from '../dto/find-payments.dto'; +// import { Admin } from 'src/modules/admin/entities/admin.entity'; +// import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; +// import { CreditTransaction } from 'src/modules/user/entities/credit-transaction.entity'; -@Injectable() -export class PaymentService { - private readonly logger = new Logger(PaymentService.name); +// @Injectable() +// export class PaymentService { +// private readonly logger = new Logger(PaymentService.name); - constructor( - private readonly em: EntityManager, - private readonly orderService: OrderService, - private readonly paymentRepository: PaymentRepository, - private readonly gatewayManager: GatewayManager, - private readonly eventEmitter: EventEmitter2, - private readonly creditService: CreditService, - private readonly creditRepository: CreditRepository, - private readonly adminRepository: AdminRepository, - ) { } +// constructor( +// private readonly em: EntityManager, +// private readonly orderService: OrderService, +// private readonly paymentRepository: PaymentRepository, +// private readonly gatewayManager: GatewayManager, +// private readonly eventEmitter: EventEmitter2, +// private readonly creditService: CreditService, +// private readonly creditRepository: CreditRepository, +// private readonly adminRepository: AdminRepository, +// ) { } - async payOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise<{ paymentUrl: string | null }> { +// async payOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise<{ paymentUrl: string | null }> { - const ctx = await this.loadAndValidateOrder(orderId, dto, adminId) +// const ctx = await this.loadAndValidateOrder(orderId, dto, adminId) - switch (dto.method) { - case PaymentMethodEnum.Cash: - await this.handleCashPayment(ctx); - return { paymentUrl: null }; +// switch (dto.method) { +// case PaymentMethodEnum.Cash: +// await this.handleCashPayment(ctx); +// return { paymentUrl: null }; - case PaymentMethodEnum.Credit: - await this.handleCreditPayment(ctx); - return { paymentUrl: null }; +// case PaymentMethodEnum.Credit: +// await this.handleCreditPayment(ctx); +// return { paymentUrl: null }; - case PaymentMethodEnum.Online: - return this.handleOnlinePayment(ctx); +// case PaymentMethodEnum.Online: +// return this.handleOnlinePayment(ctx); - default: - throw new BadRequestException(PaymentMessage.UNSUPPORTED_PAYMENT_METHOD); - } - } +// default: +// throw new BadRequestException(PaymentMessage.UNSUPPORTED_PAYMENT_METHOD); +// } +// } - private async loadAndValidateOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise { - const { amount, method, attachments, description, gateway } = dto +// private async loadAndValidateOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise { +// const { amount, method, attachments, description, gateway } = dto - const order = await this.orderService.findOrderOrFail(orderId) +// const order = await this.orderService.findOrderOrFail(orderId) - if (!order.balance) { - throw new NotFoundException('Balance is not set'); - } +// if (!order.balance) { +// throw new NotFoundException('Balance is not set'); +// } - let admin: null | Admin = null - if (adminId) { - 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") +// } +// } - if (amount <= 0) { - throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO); - } +// if (amount <= 0) { +// throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO); +// } - if (amount > order.balance) { - throw new BadRequestException(`You cant pay more than ${order.balance}`); - } +// if (amount > order.balance) { +// throw new BadRequestException(`You cant pay more than ${order.balance}`); +// } - return { - admin, - user: order.user, - order, - amount, - attachments, - description, - gateway, - method - }; - } +// return { +// admin, +// user: order.user, +// order, +// amount, +// attachments, +// description, +// gateway, +// method +// }; +// } - private async handleCashPayment(ctx: OrderPaymentContext): Promise { - const { order, amount, method, attachments, description, admin } = ctx - const payment = this.paymentRepository.create({ - creator: admin, - order, - amount, - method, - status: PaymentStatusEnum.Pending, - attachments, - description - }) - await this.em.persistAndFlush(payment) - return - } +// private async handleCashPayment(ctx: OrderPaymentContext): Promise { +// const { order, amount, method, attachments, description, admin } = ctx +// const payment = this.paymentRepository.create({ +// creator: admin, +// order, +// amount, +// method, +// status: PaymentStatusEnum.Pending, +// attachments, +// description +// }) +// await this.em.persistAndFlush(payment) +// return +// } - private async handleCreditPayment(ctx: OrderPaymentContext): Promise { - const { order, amount, user, admin } = ctx +// private async handleCreditPayment(ctx: OrderPaymentContext): Promise { +// const { order, amount, user, admin } = ctx - await this.em.transactional(async em => { - // 1. get User remained Credit - const remainedCredit = await this.creditService.getCurrentCredit(user.id) +// await this.em.transactional(async em => { +// // 1. get User remained Credit +// const remainedCredit = await this.creditService.getCurrentCredit(user.id) - if (remainedCredit < amount) { - throw new BadRequestException("You Dont have enough Credit") - } +// if (remainedCredit < amount) { +// throw new BadRequestException("You Dont have enough Credit") +// } - const newBalance = remainedCredit - amount; - // 2. Create payment record - const payment = em.create(Payment, { - creator: admin, - amount: ctx.amount, - method: PaymentMethodEnum.Credit, - gateway: null, - order, - status: PaymentStatusEnum.Paid, - paidAt: new Date(), - }); - // 3. Create Credit transaction record - const newCreditTransaction = em.create(CreditTransaction, { - user: order.user, - amount, - type: CreditTransactionType.WITHDRAW, - balance: newBalance, - orderId: order.id, - }); +// const newBalance = remainedCredit - amount; +// // 2. Create payment record +// const payment = em.create(Payment, { +// creator: admin, +// amount: ctx.amount, +// method: PaymentMethodEnum.Credit, +// gateway: null, +// order, +// status: PaymentStatusEnum.Paid, +// paidAt: new Date(), +// }); +// // 3. Create Credit transaction record +// const newCreditTransaction = em.create(CreditTransaction, { +// user: order.user, +// amount, +// type: CreditTransactionType.WITHDRAW, +// balance: newBalance, +// orderId: order.id, +// }); - await em.persistAndFlush([payment, newCreditTransaction]); - }); +// await em.persistAndFlush([payment, newCreditTransaction]); +// }); - this.eventEmitter.emit( - paymentSucceedEvent.name, - new paymentSucceedEvent(ctx.order.id, String(ctx.order?.orderNumber) || '', ctx.method, ctx.amount), - ); - } +// this.eventEmitter.emit( +// paymentSucceedEvent.name, +// new paymentSucceedEvent(ctx.order.id, String(ctx.order?.orderNumber) || '', ctx.method, ctx.amount), +// ); +// } - private async handleOnlinePayment(ctx: OrderPaymentContext): Promise<{ paymentUrl: string }> { - const { amount, order, gateway: requestedGateway, admin } = ctx +// private async handleOnlinePayment(ctx: OrderPaymentContext): Promise<{ paymentUrl: string }> { +// const { amount, order, gateway: requestedGateway, admin } = ctx - const gateway = this.gatewayManager.get(requestedGateway!); +// const gateway = this.gatewayManager.get(requestedGateway!); - const { token, paymentUrl } = await gateway.requestPayment({ - amount: ctx.amount, - orderId: ctx.order.id, - }); +// const { token, paymentUrl } = await gateway.requestPayment({ +// amount: ctx.amount, +// orderId: ctx.order.id, +// }); - const payment = this.paymentRepository.create({ - creator: admin, - order, - amount, - method: PaymentMethodEnum.Online, - status: PaymentStatusEnum.Pending, - gateway: requestedGateway, - token, - }) +// const payment = this.paymentRepository.create({ +// creator: admin, +// order, +// amount, +// method: PaymentMethodEnum.Online, +// status: PaymentStatusEnum.Pending, +// gateway: requestedGateway, +// token, +// }) - await this.em.persistAndFlush(payment); +// await this.em.persistAndFlush(payment); - return { - paymentUrl, - }; - } +// return { +// paymentUrl, +// }; +// } - async verifyOnlinePayment(token: string): Promise { - const payment = await this.em.transactional(async em => { - const payment = await this.paymentRepository.findOne( - { token }, - { populate: ['order'] }, - ); +// async verifyOnlinePayment(token: string): Promise { +// const payment = await this.em.transactional(async em => { +// const payment = await this.paymentRepository.findOne( +// { token }, +// { populate: ['order'] }, +// ); - if (!payment) { - throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND); - } +// if (!payment) { +// throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND); +// } - if (payment.status === PaymentStatusEnum.Paid) { - return payment; - } +// if (payment.status === PaymentStatusEnum.Paid) { +// return payment; +// } - if (!payment.gateway) { - throw new NotFoundException("Payment gateway not found"); - } +// if (!payment.gateway) { +// throw new NotFoundException("Payment gateway not found"); +// } - const gateway = this.gatewayManager.get(payment.gateway); +// const gateway = this.gatewayManager.get(payment.gateway); - const { raw, referenceId, success } = await gateway.verifyPayment({ - amount: payment.amount, - token: payment.token!, - }); +// const { raw, referenceId, success } = await gateway.verifyPayment({ +// amount: payment.amount, +// token: payment.token!, +// }); - payment.verifyResponse = raw; +// payment.verifyResponse = raw; - if (!success) { - payment.status = PaymentStatusEnum.Failed; - payment.failedAt = new Date(); - return payment; - } +// if (!success) { +// payment.status = PaymentStatusEnum.Failed; +// payment.failedAt = new Date(); +// return payment; +// } - payment.status = PaymentStatusEnum.Paid; - payment.paidAt = new Date(); - payment.transactionId = referenceId; - // TODO : use decimal js - // does need persist or not - // payment.order.paidAmount! += payment.amount - // payment.order.balance! -= payment.amount +// payment.status = PaymentStatusEnum.Paid; +// payment.paidAt = new Date(); +// payment.transactionId = referenceId; +// // TODO : use decimal js +// // does need persist or not +// // payment.order.paidAmount! += payment.amount +// // payment.order.balance! -= payment.amount - await em.flush(); - return payment; - }); - this.eventEmitter.emit( - onlinePaymentSucceedEvent.name, - new onlinePaymentSucceedEvent(payment.id, payment.order.id, String(payment.order?.orderNumber) || '', payment.amount), - ); - return payment; - } +// await em.flush(); +// return payment; +// }); +// this.eventEmitter.emit( +// onlinePaymentSucceedEvent.name, +// new onlinePaymentSucceedEvent(payment.id, payment.order.id, String(payment.order?.orderNumber) || '', payment.amount), +// ); +// return payment; +// } - findAll(dto: FindPaymentsDto) { - return this.paymentRepository.findAllPaginated(dto) - } +// findAll(dto: FindPaymentsDto) { +// return this.paymentRepository.findAllPaginated(dto) +// } - findAllByUser(dto: FindPaymentsDto, userId: string) { - return this.paymentRepository.findAllPaginated({ ...dto, userId }) - } +// findAllByUser(dto: FindPaymentsDto, userId: string) { +// return this.paymentRepository.findAllPaginated({ ...dto, userId }) +// } - async confirmCashPayment(paymentId: string): Promise { - const payment = await this.em.transactional(async em => { - const payment = await em.findOne(Payment, paymentId, { populate: ['order'] }); - if (!payment) { - throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND); - } - if (payment.method !== PaymentMethodEnum.Cash) { - throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH); - } - if (payment.status === PaymentStatusEnum.Paid) { - throw new BadRequestException(PaymentMessage.PAYMENT_ALREADY_PAID); - } +// async confirmCashPayment(paymentId: string): Promise { +// const payment = await this.em.transactional(async em => { +// const payment = await em.findOne(Payment, paymentId, { populate: ['order'] }); +// if (!payment) { +// throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND); +// } +// if (payment.method !== PaymentMethodEnum.Cash) { +// throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH); +// } +// if (payment.status === PaymentStatusEnum.Paid) { +// throw new BadRequestException(PaymentMessage.PAYMENT_ALREADY_PAID); +// } - payment.status = PaymentStatusEnum.Paid; - payment.paidAt = new Date(); - payment.failedAt = null; +// payment.status = PaymentStatusEnum.Paid; +// payment.paidAt = new Date(); +// payment.failedAt = null; - // TODO : use decimal js - // payment.order.paidAmount! += payment.amount - // payment.order.balance! -= payment.amount +// // TODO : use decimal js +// // payment.order.paidAmount! += payment.amount +// // payment.order.balance! -= payment.amount - em.persist(payment); - em.persist(payment.order); - await em.flush(); - return payment; - }); - return payment; - } +// em.persist(payment); +// em.persist(payment.order); +// await em.flush(); +// return payment; +// }); +// return payment; +// } - async denyCashPayment(paymentId: string): Promise { - const payment = await this.em.transactional(async em => { +// async denyCashPayment(paymentId: string): Promise { +// const payment = await this.em.transactional(async em => { - const payment = await em.findOne(Payment, paymentId, { populate: ['order'] }); +// const payment = await em.findOne(Payment, paymentId, { populate: ['order'] }); - if (!payment) { - throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND); - } +// if (!payment) { +// throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND); +// } - if (payment.method !== PaymentMethodEnum.Cash) { - throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH); - } +// if (payment.method !== PaymentMethodEnum.Cash) { +// throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH); +// } - if (payment.status == PaymentStatusEnum.Paid) { - // payment.order.paidAmount! -= payment.amount - // payment.order.balance! += payment.amount +// if (payment.status == PaymentStatusEnum.Paid) { +// // payment.order.paidAmount! -= payment.amount +// // payment.order.balance! += payment.amount - em.persistAndFlush(payment.order) - } +// em.persistAndFlush(payment.order) +// } - payment.status = PaymentStatusEnum.Failed; - payment.failedAt = new Date(); - payment.paidAt = null +// payment.status = PaymentStatusEnum.Failed; +// payment.failedAt = new Date(); +// payment.paidAt = null - em.persistAndFlush(payment); +// em.persistAndFlush(payment); - return payment; - }); - return payment; - } +// return payment; +// }); +// return payment; +// } -} +// } diff --git a/src/modules/ticket/controller/ticket.controller.ts b/src/modules/ticket/controller/ticket.controller.ts index cf55440..6ad5011 100755 --- a/src/modules/ticket/controller/ticket.controller.ts +++ b/src/modules/ticket/controller/ticket.controller.ts @@ -16,7 +16,7 @@ import { AuthGuard } from "src/modules/auth/guards/auth.guard"; export class TicketController { constructor(private readonly ticketsService: TicketService) { } - @Post('public/ticket/order/item/:id') + @Post('public/ticket/ref/:id') @UseGuards(AuthGuard) @ApiOperation({ summary: "create ticket ==> user route" }) createTicket(@Param('id') id: string, @Body() createDto: CreateTicketDto, @UserId() userId: string) { @@ -24,7 +24,7 @@ export class TicketController { } - @Get('public/ticket/order/item/:id') + @Get('public/ticket/ref/:id') @UseGuards(AuthGuard) @ApiOperation({ summary: "Get all order item tickets ==> user route" }) getTickets(@Query() queryDto: FindTicketQueryDto, @Param('id') id: string, @UserId() userId: string) { @@ -47,7 +47,7 @@ export class TicketController { } /*=========================== Admin ============================== */ - @Post('admin/ticket/order/item/:id') + @Post('admin/ticket/ref/:id') @UseGuards(AdminAuthGuard) @ApiOperation({ summary: "create ticket ==> admin route" }) createTicketAsAdmin(@Param('id') itemId: string, @Body() createDto: CreateTicketDto, @AdminId() adminId: string) { @@ -55,7 +55,7 @@ export class TicketController { } - @Get('admin/ticket/order/item/:id') + @Get('admin/ticket/ref/:id') @UseGuards(AdminAuthGuard) @ApiOperation({ summary: "Get all order item tickets ==> admin route" }) getTicketsAsAdmin(@Query() queryDto: FindTicketQueryDto, @Param('id') itemId: string) { diff --git a/src/modules/ticket/entities/ticket.entity.ts b/src/modules/ticket/entities/ticket.entity.ts index 0ba1d0c..3efc644 100755 --- a/src/modules/ticket/entities/ticket.entity.ts +++ b/src/modules/ticket/entities/ticket.entity.ts @@ -8,11 +8,10 @@ import { import { User } from "../../user/entities/user.entity"; import { Admin } from 'src/modules/admin/entities/admin.entity'; import { AttachmentType } from '../interfaces/ticket'; -import { OrderItem } from 'src/modules/order/entities/order-item.entity'; import { BaseEntity } from 'src/common/entities/base.entity'; @Entity() -export class Ticket extends BaseEntity{ +export class Ticket extends BaseEntity { [OptionalProps]?: 'createdAt' | 'deletedAt' @@ -28,8 +27,8 @@ export class Ticket extends BaseEntity{ @Property({ type: 'json', nullable: true }) attachments?: AttachmentType[] - @ManyToOne(() => OrderItem) - orderItem!: OrderItem + @Property({ type: 'string' }) + refId!: string @ManyToOne(() => Ticket, { nullable: true }) parent?: Ticket diff --git a/src/modules/ticket/providers/tickets.service.ts b/src/modules/ticket/providers/tickets.service.ts index a3a9118..2dd0f5e 100755 --- a/src/modules/ticket/providers/tickets.service.ts +++ b/src/modules/ticket/providers/tickets.service.ts @@ -8,10 +8,8 @@ import { Admin } from "src/modules/admin/entities/admin.entity"; import { FindTicketQueryDto } from "../DTO/search-ticket-query.dto"; import { EntityManager } from "@mikro-orm/postgresql"; import { UpdateTicketDto } from "../DTO/update-ticket.dto"; -import { OrderItemRepository } from "src/modules/order/repositories/order-item.repository"; -import { Ticket } from "../entities/ticket.entity"; -import { OrderItem } from "src/modules/order/entities/order-item.entity"; -import { AdminService } from "src/modules/admin/providers/admin.service"; + import { Ticket } from "../entities/ticket.entity"; + import { AdminService } from "src/modules/admin/providers/admin.service"; import { UserService } from "src/modules/user/providers/user.service"; @Injectable() @@ -19,33 +17,33 @@ export class TicketService { constructor( private readonly ticketsRepository: TicketRepository, - private readonly orderItemRepository: OrderItemRepository, + // private readonly orderItemRepository: OrderItemRepository, private readonly adminService: AdminService, private readonly userService: UserService, private readonly em: EntityManager, ) { } - async createTicketAsAdmin(orderItemId: string, dto: CreateTicketDto, adminId: string) { + async createTicketAsAdmin(refId: string, dto: CreateTicketDto, adminId: string) { - const orderItem = await this.findOrderItemOrFail(orderItemId) + // const orderItem = await this.findOrderItemOrFail(refId) let admin: null | Admin = null admin = await this.adminService.findOrFail(adminId) - const ticket = await this.createTicket(orderItem, dto, undefined, admin) + const ticket = await this.createTicket(refId, dto, undefined, admin) return ticket } - async createTicketAsUser(orderItemId: string, dto: CreateTicketDto, userId: string) { + async createTicketAsUser(refId: string, dto: CreateTicketDto, userId: string) { - const orderItem = await this.findOrderItemOrFail(orderItemId) + // const orderItem = await this.findOrderItemOrFail(refId) const user = await this.userService.findOrFail(userId) - const ticket = await this.createTicket(orderItem, dto, user, undefined) + const ticket = await this.createTicket(refId, dto, user, undefined) return ticket @@ -97,9 +95,9 @@ export class TicketService { async deleteTicketAsUSer(ticketId: string, userId: string) { const ticket = await this.findOneOrFail(ticketId) - if (ticket.orderItem.order.user.id !== userId) { - throw new BadRequestException("You can not Delete this Ticket") - } + // if (ticket.orderItem.order.user.id !== userId) { + // throw new BadRequestException("You can not Delete this Ticket") + // } await this.em.removeAndFlush(ticket) @@ -108,21 +106,15 @@ export class TicketService { // ================ Core Logic ============ - async getTickets(orderItemId: string, queryDto: FindTicketQueryDto, userId?: string) { - const orderItem = await this.findOrderItemOrFail(orderItemId) + async getTickets(refId: string, queryDto: FindTicketQueryDto, userId?: string) { + // const orderItem = await this.findOrderItemOrFail(refId) - if (userId) { - if (orderItem.order.user.id !== userId) { - throw new BadRequestException("This ticket doenst belongs to You !") - } - } - - const data = await this.ticketsRepository.findAllPaginated({ ...queryDto, orderItemId }); + const data = await this.ticketsRepository.findAllPaginated({ ...queryDto, refId }); return data } - async createTicket(orderItem: OrderItem, dto: CreateTicketDto, user?: User, admin?: Admin) { + async createTicket(refId:string,dto: CreateTicketDto, user?: User, admin?: Admin) { const { content, attachments } = dto if (!user && !admin) { @@ -134,7 +126,7 @@ export class TicketService { attachments, admin, user, - orderItem, + refId }) await this.ticketsRepository.em.persistAndFlush(ticket) @@ -163,7 +155,7 @@ export class TicketService { async findOneOrFail(ticketId: string) { const ticket = await this.ticketsRepository.findOne({ id: ticketId }, - { populate: ['orderItem', 'orderItem.order', 'user'] }) + { populate: [ 'user'] }) if (!ticket) { throw new BadRequestException('ticket not found!') } @@ -190,14 +182,14 @@ export class TicketService { } } - async findOrderItemOrFail(orderItemId: string) { - const orderItem = await this.orderItemRepository.findOne({ id: orderItemId }, { populate: ['order', 'order.user'] }) + // async findOrderItemOrFail(refId: string) { + // const orderItem = await this.orderItemRepository.findOne({ id: refId }, { populate: ['order', 'order.user'] }) - if (!orderItem) { - throw new BadRequestException("orderItem not found!") - } + // if (!orderItem) { + // throw new BadRequestException("orderItem not found!") + // } - return orderItem - } + // return orderItem + // } } diff --git a/src/modules/ticket/repositories/tickets.repository.ts b/src/modules/ticket/repositories/tickets.repository.ts index 1df5002..d79a204 100755 --- a/src/modules/ticket/repositories/tickets.repository.ts +++ b/src/modules/ticket/repositories/tickets.repository.ts @@ -5,7 +5,7 @@ import { FindTicketQueryDto } from '../DTO/search-ticket-query.dto'; import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; class FindOrdersOpts extends FindTicketQueryDto { - orderItemId: string; + refId: string; }; @Injectable() @@ -20,13 +20,13 @@ export class TicketRepository extends EntityRepository { const { page = 1, limit = 10, - orderItemId: orderId + refId } = opts; const offset = (page - 1) * limit; const where: FilterQuery = { - orderItem: { id: orderId } + refId }; diff --git a/src/modules/ticket/ticket.module.ts b/src/modules/ticket/ticket.module.ts index 8850f7b..f052186 100755 --- a/src/modules/ticket/ticket.module.ts +++ b/src/modules/ticket/ticket.module.ts @@ -4,7 +4,6 @@ import { TicketService } from "./providers/tickets.service"; import { TicketRepository } from "./repositories/tickets.repository"; import { TicketController } from "./controller/ticket.controller"; import { MikroOrmModule } from "@mikro-orm/nestjs"; -import { OrderModule } from "../order/order.module"; import { AdminModule } from "../admin/admin.module"; import { UserModule } from "../user/user.module"; import { JwtModule } from "@nestjs/jwt"; @@ -13,7 +12,7 @@ import { JwtModule } from "@nestjs/jwt"; @Module({ imports: [ - forwardRef(() => OrderModule), + // forwardRef(() => OrderModule), MikroOrmModule.forFeature([Ticket]), AdminModule, UserModule, diff --git a/src/modules/user/entities/user.entity.ts b/src/modules/user/entities/user.entity.ts index 2d0040a..48661aa 100644 --- a/src/modules/user/entities/user.entity.ts +++ b/src/modules/user/entities/user.entity.ts @@ -1,6 +1,6 @@ import { Entity, Index, Property, OneToMany, Collection, Cascade, PrimaryKey, OptionalProps } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; -import { Order } from 'src/modules/order/entities/order.entity'; +// import { Order } from 'src/modules/order/entities/order.entity'; import { normalizePhone } from '../../util/phone.util'; import { ulid } from 'ulid'; @@ -8,8 +8,8 @@ import { ulid } from 'ulid'; export class User extends BaseEntity { [OptionalProps]?: 'createdAt' | 'deletedAt' - @OneToMany(() => Order, order => order.user) - orders = new Collection(this); + // @OneToMany(() => Order, order => order.user) + // orders = new Collection(this); @Property({ nullable: true })