diff --git a/src/modules/order/controllers/order.controller.ts b/src/modules/order/controllers/order.controller.ts index 1917b21..aaaacdb 100644 --- a/src/modules/order/controllers/order.controller.ts +++ b/src/modules/order/controllers/order.controller.ts @@ -13,6 +13,7 @@ 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'; @ApiTags('orders') @ApiBearerAuth() @@ -96,7 +97,7 @@ export class OrderController { } @Get('admin/orders') - @UseGuards(AuthGuard) + @UseGuards(AdminAuthGuard) @ApiOperation({ summary: 'Get all orders with pagination and filters' }) findAllAsAdmin(@Query() dto: FindOrdersDto) { return this.orderService.findOrdersAsAdmin(dto); @@ -117,11 +118,11 @@ export class OrderController { return this.orderService.updateOrderAsAdmin(orderId, dto); } - @Patch('admin/orders/:orderId/print-form') + @Patch('admin/orders/:id/item/:itemId/print') @UseGuards(AdminAuthGuard) - @ApiOperation({ summary: 'Update Order ' }) - updateOrderForPrint(@Param('orderId') orderId: string, @Body() dto: UpdateOrderAsAdminDto) { - return this.orderService.updateOrderAsAdmin(orderId, dto); + @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); } @Delete('admin/orders/:orderId') @@ -172,4 +173,11 @@ export class OrderController { return this.orderService.updateOrderItemAsAdmin(orderId, +itemId, body); } + @Get('admin/prints') + @UseGuards(AdminAuthGuard) + @ApiOperation({ summary: 'Get all orders with pagination and filters' }) + findAllPrints(@Query() dto: FindOrdersDto) { + return this.orderService.findOrdersAsAdmin(dto); + } + } diff --git a/src/modules/order/dto/create-order.dto.ts b/src/modules/order/dto/create-order.dto.ts index ce86aee..d0e77e5 100644 --- a/src/modules/order/dto/create-order.dto.ts +++ b/src/modules/order/dto/create-order.dto.ts @@ -37,13 +37,13 @@ export class CreateOrderItemAsUserDto { } export class CreateOrderItemDtoAsAdmin extends CreateOrderItemAsUserDto { - @ApiPropertyOptional({ example: [] }) - @IsArray() - printAttributes?: IField[] + // @ApiPropertyOptional({ example: [] }) + // @IsArray() + // printAttributes?: IField[] - @ApiPropertyOptional({ example: [] }) - @IsArray() - printAttachments?: IAttachment[] + // @ApiPropertyOptional({ example: [] }) + // @IsArray() + // printAttachments?: IAttachment[] @IsInt() @ApiProperty() diff --git a/src/modules/order/dto/create-print-form.dto.ts b/src/modules/order/dto/create-print-form.dto.ts new file mode 100644 index 0000000..59e366e --- /dev/null +++ b/src/modules/order/dto/create-print-form.dto.ts @@ -0,0 +1,15 @@ +import { IsArray, } from 'class-validator'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IAttachment, IField } from '../interface/order.interface'; + +export class CreatePrintFormDto { + @ApiPropertyOptional({ example: [] }) + @IsArray() + printAttributes?: IField[] + + @ApiPropertyOptional({ example: [] }) + @IsArray() + printAttachments?: IAttachment[] +} + + diff --git a/src/modules/order/entities/order.entity.ts b/src/modules/order/entities/order.entity.ts index 67d4918..55b0e50 100644 --- a/src/modules/order/entities/order.entity.ts +++ b/src/modules/order/entities/order.entity.ts @@ -13,7 +13,7 @@ import { OptionalProps, Formula } from '@mikro-orm/core'; -import { IField, OrderStatusEnum } from '../interface/order.interface'; +import { OrderStatusEnum } from '../interface/order.interface'; import { User } from '../../user/entities/user.entity'; import { OrderItem } from './order-item.entity'; import { Payment } from 'src/modules/payment/entities/payment.entity'; diff --git a/src/modules/order/order.module.ts b/src/modules/order/order.module.ts index f0fc6bb..53f9a3b 100644 --- a/src/modules/order/order.module.ts +++ b/src/modules/order/order.module.ts @@ -34,6 +34,6 @@ import { OrderItemRepository } from './repositories/order-item.repository'; controllers: [OrderController], providers: [OrderService, OrderRepository, OrderListeners, OrdersCrone, OrderItemRepository], - exports: [OrderRepository, OrderService], + 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 index e42da9e..a92c3b4 100644 --- a/src/modules/order/providers/order.service.ts +++ b/src/modules/order/providers/order.service.ts @@ -32,6 +32,7 @@ import { Product } from 'src/modules/product/entities/product.entity'; import { AdminService } from 'src/modules/admin/providers/admin.service'; import { OrderItem } from '../entities/order-item.entity'; import { UpdateOrderAsAdminDto } from '../dto/update-order.dto'; +import { CreatePrintFormDto } from '../dto/create-print-form.dto'; @Injectable() @@ -161,6 +162,19 @@ export class OrderService { return updated } + async createPrintForm(orderId: string, itemId: number, dto: CreatePrintFormDto) { + + 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 removeOrderItemAsAdmin(orderId: string, itemId: number) { const orderItem = await this.findOrderItemOrFail(orderId, itemId) @@ -206,6 +220,11 @@ export class OrderService { return orders } + async findPrints(dto: FindOrdersDto) { + const orders = await this.orderRepository.findAllPaginated({...dto,statuses:[]}) + return orders + } + // async calculateOrder(inputOrder?: Order, orderId?: string) { // let order: undefined | Order = inputOrder @@ -509,7 +528,7 @@ export class OrderService { await this.em.transactional(async (em) => { await this.orderRepository.nativeDelete(order) await this.orderItemRepository.nativeDelete({ order: { id: orderId } }) - await this.ticketRepository.nativeDelete({ order: { id: orderId } }) + // await this.ticketRepository.nativeDelete({ orderItemorder: { id: orderId } }) await em.flush() }) diff --git a/src/modules/order/repositories/order.repository.ts b/src/modules/order/repositories/order.repository.ts index 3c05e95..195cdc1 100644 --- a/src/modules/order/repositories/order.repository.ts +++ b/src/modules/order/repositories/order.repository.ts @@ -4,7 +4,6 @@ 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 { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment'; import { FindOrdersDto } from '../dto/find-orders.dto'; @@ -94,7 +93,7 @@ export class OrderRepository extends EntityRepository { limit, offset, orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, - populate: ['items','items.product'], + populate: ['items', 'items.product', 'user'], }); // Collect all (orderId, productId) pairs for efficient review lookup diff --git a/src/modules/ticket/controller/ticket.controller.ts b/src/modules/ticket/controller/ticket.controller.ts index 40fba6a..152b0f0 100755 --- a/src/modules/ticket/controller/ticket.controller.ts +++ b/src/modules/ticket/controller/ticket.controller.ts @@ -16,11 +16,11 @@ import { AuthGuard } from "src/modules/auth/guards/auth.guard"; export class TicketController { constructor(private readonly ticketsService: TicketService) { } - @Post('public/ticket/order/:orderId') + @Post('public/ticket/order/item/:id') @UseGuards(AuthGuard) @ApiOperation({ summary: "create ticket ==> user route" }) - createTicket(@Param('orderId') orderId: string, @Body() createDto: CreateTicketDto, @UserId() userId: string) { - return this.ticketsService.createTicket(orderId, createDto, userId); + createTicket(@Param('id') itemId: string, @Body() createDto: CreateTicketDto, @UserId() userId: string) { + return this.ticketsService.createTicketAsUser(itemId, createDto, userId); } @@ -35,14 +35,14 @@ export class TicketController { @ApiOperation({ summary: "Delete Ticket ==> admin route" }) @UseGuards(AuthGuard) deleteTicket(@Param('ticketId') ticketId: string, @UserId() userId: string) { - return this.ticketsService.deleteTicket(ticketId, userId); + return this.ticketsService.deleteTicketAsUSer(+ticketId, userId); } @Patch('public/ticket/:ticketId') @UseGuards(AuthGuard) @ApiOperation({ summary: "Delete Ticket ==> admin route" }) updateTicket(@Param('ticketId') ticketId: string, @UserId() userId: string, @Body() body: UpdateTicketDto) { - return this.ticketsService.updateTicket(ticketId, body, userId,); + return this.ticketsService.updateTicketAsUser(+ticketId, userId, body); } /*=========================== Admin ============================== */ @@ -50,15 +50,15 @@ export class TicketController { @Delete('admin/ticket/:ticketId') @UseGuards(AdminAuthGuard) @ApiOperation({ summary: "Delete Ticket ==> admin route" }) - deleteTicketAsAdmin(@Param('ticketId') ticketId: string) { - return this.ticketsService.deleteTicket(ticketId); + deleteTicketAsAdmin(@Param('ticketId') ticketId: string, @AdminId() adminId: string) { + return this.ticketsService.deleteTicketAsAdmin(+ticketId, adminId); } - @Post('admin/ticket/order/:orderId') + @Post('admin/ticket/order/item/:id') @UseGuards(AdminAuthGuard) @ApiOperation({ summary: "create ticket ==> admin route" }) - createTicketAsAdmin(@Param('orderId') orderId: string, @Body() createDto: CreateTicketDto, @AdminId() adminId: string) { - return this.ticketsService.createTicket(orderId, createDto, undefined, adminId); + createTicketAsAdmin(@Param('id') orderItemId: string, @Body() createDto: CreateTicketDto, @AdminId() adminId: string) { + return this.ticketsService.createTicketAsAdmin(orderItemId, createDto, adminId); } @@ -72,7 +72,7 @@ export class TicketController { @Patch('admin/ticket/:ticketId') @UseGuards(AdminAuthGuard) @ApiOperation({ summary: "Delete Ticket ==> admin route" }) - updateTicketAsAdmin(@Param('ticketId') ticketId: string, @Body() body: UpdateTicketDto) { - return this.ticketsService.updateTicket(ticketId, body); + updateTicketAsAdmin(@Param('ticketId') ticketId: string, @AdminId() adminId: string, @Body() body: UpdateTicketDto) { + return this.ticketsService.updateTicketAsAdmin(+ticketId, adminId, body); } } diff --git a/src/modules/ticket/entities/ticket.entity.ts b/src/modules/ticket/entities/ticket.entity.ts index 39d7ccb..5fe0274 100755 --- a/src/modules/ticket/entities/ticket.entity.ts +++ b/src/modules/ticket/entities/ticket.entity.ts @@ -7,8 +7,8 @@ import { } from '@mikro-orm/core'; import { User } from "../../user/entities/user.entity"; import { Admin } from 'src/modules/admin/entities/admin.entity'; -import { Order } from 'src/modules/order/entities/order.entity'; import { AttachmentType } from '../interfaces/ticket'; +import { OrderItem } from 'src/modules/order/entities/order-item.entity'; @Entity() export class Ticket { @@ -29,8 +29,8 @@ export class Ticket { @Property({ type: 'json', nullable: true }) attachments?: AttachmentType[] - @ManyToOne(() => Order) - order!: Order + @ManyToOne(() => OrderItem) + orderItem!: OrderItem @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 a44a4f7..62447be 100755 --- a/src/modules/ticket/providers/tickets.service.ts +++ b/src/modules/ticket/providers/tickets.service.ts @@ -1,7 +1,6 @@ import { TicketRepository } from "../repositories/tickets.repository"; import { BadRequestException, Injectable } from "@nestjs/common"; import { CreateTicketDto } from "../DTO/create-ticket.dto"; -import { OrderRepository } from "src/modules/order/repositories/order.repository"; import { User } from "src/modules/user/entities/user.entity"; import { UserRepository } from "src/modules/user/repositories/user.repository"; import { AdminRepository } from "src/modules/admin/repositories/admin.repository"; @@ -9,6 +8,11 @@ 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 { UserService } from "src/modules/user/providers/user.service"; @Injectable() @@ -16,37 +20,127 @@ export class TicketService { constructor( private readonly ticketsRepository: TicketRepository, - private readonly orderRepository: OrderRepository, + private readonly orderItemRepository: OrderItemRepository, private readonly userRepository: UserRepository, private readonly adminRepository: AdminRepository, + private readonly adminService: AdminService, + private readonly userService: UserService, private readonly em: EntityManager, ) { } - async createTicket(orderId: string, dto: CreateTicketDto, userId?: string, adminId?: string) { - const { content, attachments } = dto - if (!userId && !adminId) { - throw new BadRequestException("One of userId ord adminId is required") - } - const order = await this.orderRepository.findOne({ id: orderId }) - if (!order) { - throw new BadRequestException("Order not found") + async createTicketAsAdmin(itemId: string, dto: CreateTicketDto, adminId: string) { + + const orderItem = await this.orderItemRepository.findOne({ id: itemId }) + if (!orderItem) { + throw new BadRequestException("orderItem not found") } - let user: null | User = null let admin: null | Admin = null + admin = await this.adminService.findOrFail(adminId) + + const ticket = await this.createTicket(orderItem, dto, undefined, admin) + + return ticket + + } + + async createTicketAsUser(itemId: string, dto: CreateTicketDto, userId: string) { + + const orderItem = await this.orderItemRepository.findOne({ id: itemId }) + + if (!orderItem) { + throw new BadRequestException("orderItem not found") + } + + const user = await this.userService.findOrFail(userId) + + const ticket = await this.createTicket(orderItem, dto, user, undefined) + + return ticket + + } + + async updateTicketAsUser(ticketId: number, userId: string, dto: UpdateTicketDto) { + + const ticket = await this.findOneOrFail(ticketId) + + this.assertUserOwner(ticket, userId) + + await this.updateTicket(ticket, dto) + + return { message: 'success' } + } + + async updateTicketAsAdmin(ticketId: number, adminId: string, dto: UpdateTicketDto) { + + const ticket = await this.findOneOrFail(ticketId) + + this.assertAdminOwner(ticket, adminId) + + await this.updateTicket(ticket, dto) + + return { message: 'success' } + } + + async deleteTicketAsUser(ticketId: number, userId: string) { + + const ticket = await this.findOneOrFail(ticketId) + + this.assertUserOwner(ticket, userId) + + await this.deleteTicket(ticket) + + return { message: 'success' } + } + + async deleteTicketAsAdmin(ticketId: number, adminId: string) { + + const ticket = await this.findOneOrFail(ticketId) + + this.assertAdminOwner(ticket, adminId) + + await this.deleteTicket(ticket) + + return { message: 'success' } + } + // //******************************** */ + + + + //******************************** */ + + async deleteTicketAsUSer(ticketId: number, userId: string) { + const ticket = await this.findOneOrFail(ticketId) + if (ticket.orderItem.order.user.id !== userId) { + throw new BadRequestException("You can not Delete this Ticket") + } + + await this.em.removeAndFlush(ticket) + + return { message: 'success' } + } + + // ================ Core Logic ============ + + async getTickets(orderItemId: string, queryDto: FindTicketQueryDto, userId?: string) { + const orderItem = await this.orderItemRepository.findOne({ id: orderItemId }) + + if (!orderItem) { + throw new BadRequestException("orderItem not found!") + } if (userId) { - user = await this.userRepository.findOne({ id: userId }) - if (!user) { - throw new BadRequestException("User not found") - } - } - if (adminId) { - admin = await this.adminRepository.findOne({ id: adminId }) - if (!admin) { - throw new BadRequestException("Admin not found") + if (orderItem.order.user.id !== userId) { + throw new BadRequestException("This ticket doenst belongs to You !") } } + const data = await this.ticketsRepository.findAllPaginated({ ...queryDto, orderItemId }); + + return data + } + + async createTicket(orderItem: OrderItem, dto: CreateTicketDto, user?: User, admin?: Admin) { + const { content, attachments } = dto if (!user && !admin) { throw new BadRequestException("One of user or admin is required") @@ -57,7 +151,7 @@ export class TicketService { attachments, admin, user, - order, + orderItem, }) await this.ticketsRepository.em.persistAndFlush(ticket) @@ -66,50 +160,13 @@ export class TicketService { } - // //******************************** */ - - async getTickets(orderId: string, queryDto: FindTicketQueryDto, userId?: string) { - - const order = await this.orderRepository.findOne({ id: orderId }) - if (!order) { - throw new BadRequestException("order not found!") - } - if (userId) { - if (order.user.id !== userId) { - throw new BadRequestException("This ticket doenst belongs to You !") - } - } - const data = await this.ticketsRepository.findAllPaginated({ ...queryDto, orderId }); - - return data - } - - //******************************** */ - - async deleteTicket(ticketId: string, userId?: string) { - const ticket = await this.ticketsRepository.findOne({ id: ticketId }, { populate: ['order', 'order.user'] }) - if (!ticket) { - throw new BadRequestException('ticket not found!') - } - if (userId && ticket.order.user.id !== userId) { - throw new BadRequestException("You can not Delete this Ticket") - } - + async deleteTicket(ticket: Ticket) { await this.em.removeAndFlush(ticket) - return true } - // //******************************** */ + async updateTicket(ticket: Ticket, dto: UpdateTicketDto) { - async updateTicket(ticketId: string, dto: UpdateTicketDto, userId?: string) { - const ticket = await this.ticketsRepository.findOne({ id: ticketId }, { populate: ['order', 'order.user'] }) - if (!ticket) { - throw new BadRequestException('ticket not found!') - } - if (userId && ticket.order.user.id !== userId) { - throw new BadRequestException("You can not Update this Ticket") - } this.ticketsRepository.assign(ticket, dto, { onlyProperties: true, }) @@ -119,5 +176,35 @@ export class TicketService { return true } + // ================ Helpers ================= + + async findOneOrFail(ticketId: number) { + const ticket = await this.ticketsRepository.findOne({ id: ticketId }, + { populate: ['orderItem', 'orderItem.order', 'user'] }) + if (!ticket) { + throw new BadRequestException('ticket not found!') + } + return ticket + } + + assertUserOwner(ticket: Ticket, userId: string) { + if (!ticket.user) { + throw new BadRequestException("User not found!") + } + + if (ticket.user.id !== userId) { + throw new BadRequestException("You are not owner of this ticket!") + } + } + + assertAdminOwner(ticket: Ticket, adminId: string) { + if (!ticket.admin) { + throw new BadRequestException("Admin not found!") + } + + if (ticket.admin.id !== adminId) { + throw new BadRequestException("You are not owner of this ticket!") + } + } } diff --git a/src/modules/ticket/repositories/tickets.repository.ts b/src/modules/ticket/repositories/tickets.repository.ts index 09b040a..1df5002 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 { - orderId: string; + orderItemId: string; }; @Injectable() @@ -20,13 +20,13 @@ export class TicketRepository extends EntityRepository { const { page = 1, limit = 10, - orderId + orderItemId: orderId } = opts; const offset = (page - 1) * limit; const where: FilterQuery = { - order: { id: orderId } + orderItem: { id: orderId } };