diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 3f8c80b..157965f 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -107,6 +107,7 @@ export const enum UserMessage { ADMIN_ID_SHOULD_BE_UUID = "شناسه ادمین باید یک UUID باشد", ADMIN_NOT_FOUND = "ادمین یافت نشد", PHONE_MUST_BE_A_STRING = "شماره تلفن باید یک رشته باشد", + USER_ID_REQUIRED = "شناسه کاربر مورد نیاز است", } export const enum CommonMessage { diff --git a/src/modules/invoices/providers/invoices.service.ts b/src/modules/invoices/providers/invoices.service.ts index e5d1ea0..faaa500 100755 --- a/src/modules/invoices/providers/invoices.service.ts +++ b/src/modules/invoices/providers/invoices.service.ts @@ -3,7 +3,7 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common"; import { Queue } from "bullmq"; import dayjs from "dayjs"; import Decimal from "decimal.js"; -import { Between, DataSource, QueryRunner } from "typeorm"; +import { Between, DataSource, In, QueryRunner } from "typeorm"; import { AuthMessage, InvoiceMessage, WalletMessage } from "../../../common/enums/message.enum"; import { VerifyOtpWithUserId } from "../../auth/DTO/verify-otp.dto"; @@ -557,9 +557,10 @@ export class InvoicesService { const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner); - const invoice = await this.getPendingInvoiceByIdWithQueryRunner(invoiceId, user.id, queryRunner); + const invoice = await this.getPendingOrOverdueInvoiceByIdWithQueryRunner(invoiceId, user.id, queryRunner); - if (invoice.status !== InvoiceStatus.WAIT_PAYMENT) throw new BadRequestException(InvoiceMessage.INVOICE_CAN_NOT_PAID); + if (invoice.status !== InvoiceStatus.WAIT_PAYMENT && invoice.status !== InvoiceStatus.OVERDUE) + throw new BadRequestException(InvoiceMessage.INVOICE_CAN_NOT_PAID); const userWallet = await this.walletsService.getWalletByUserId(user.id, queryRunner); @@ -632,11 +633,11 @@ export class InvoicesService { //*********************************** */ - async getPendingInvoiceByIdWithQueryRunner(invoiceId: string, userId: string, queryRunner: QueryRunner) { + async getPendingOrOverdueInvoiceByIdWithQueryRunner(invoiceId: string, userId: string, queryRunner: QueryRunner) { const invoice = await queryRunner.manager.findOne(Invoice, { where: { user: { id: userId }, - status: InvoiceStatus.WAIT_PAYMENT, + status: In([InvoiceStatus.WAIT_PAYMENT, InvoiceStatus.OVERDUE]), id: invoiceId, }, relations: { diff --git a/src/modules/tickets/DTO/create-ticket.dto.ts b/src/modules/tickets/DTO/create-ticket.dto.ts index a80d454..859bd72 100755 --- a/src/modules/tickets/DTO/create-ticket.dto.ts +++ b/src/modules/tickets/DTO/create-ticket.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from "@nestjs/swagger"; import { ArrayMinSize, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator"; -import { TicketMessageEnum } from "../../../common/enums/message.enum"; +import { TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum"; import { TicketPriority } from "../enums/ticket-priority.enum"; export class CreateTicketDto { @@ -46,3 +46,10 @@ export class CreateTicketDto { @ApiProperty({ description: "attachment url", example: ["https://example.com/test.png"] }) attachmentUrls?: string[]; } + +export class CreateTicketByAdminDto extends CreateTicketDto { + @IsNotEmpty({ message: UserMessage.USER_ID_REQUIRED }) + @IsUUID("4", { message: UserMessage.USER_ID_SHOULD_BE_A_UUID }) + @ApiProperty({ description: "User ID of the ticket", example: "123e4567-e89b-12d3-a456-426614174000" }) + userId: string; +} diff --git a/src/modules/tickets/providers/tickets.service.ts b/src/modules/tickets/providers/tickets.service.ts index f7d8c9b..786e1c6 100755 --- a/src/modules/tickets/providers/tickets.service.ts +++ b/src/modules/tickets/providers/tickets.service.ts @@ -17,7 +17,7 @@ import { AdminsService } from "../../users/providers/admins.service"; import { UsersService } from "../../users/providers/users.service"; import { CreateTicketCategoryDto } from "../DTO/create-ticket-category.dto"; import { CreateTicketMessageDto } from "../DTO/create-ticket-message.dto"; -import { CreateTicketDto } from "../DTO/create-ticket.dto"; +import { CreateTicketByAdminDto, CreateTicketDto } from "../DTO/create-ticket.dto"; import { ReferTicketDto } from "../DTO/refer-ticket.dto"; import { SearchTicketCategoryDto } from "../DTO/search-ticket-category.dto"; import { SearchTicketQueryDto } from "../DTO/search-ticket-query.dto"; @@ -207,6 +207,56 @@ export class TicketsService implements OnModuleInit { await queryRunner.release(); } } + //******************************** */ + + async createTicketByAdmin(createDto: CreateTicketByAdminDto, adminId: string) { + const queryRunner = this.dataSource.createQueryRunner(); + + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); + + const [user, admin, ticketCategory] = await Promise.all([ + this.usersService.findOneByIdWithQueryRunner(createDto.userId, queryRunner), + this.usersService.findOneByIdWithQueryRunner(adminId, queryRunner), + queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }), + ]); + + if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); + + const ticketMessage: Partial = { content: createDto.message, author: admin }; + + const ticket = queryRunner.manager.create(Ticket, { + ...createDto, + user, + category: ticketCategory, + messages: [ticketMessage], + assignedTo: admin, + }); + + await queryRunner.manager.save(Ticket, ticket); + + await this.notificationQueue.addTicketNotification(user.id, { + ticketId: ticket.numericId.toString(), + subject: ticket.subject, + date: ticket.createdAt, + userPhone: user.phone, + userEmail: user.email, + }); + + await queryRunner.commitTransaction(); + + return { + message: TicketMessageEnum.CREATED, + ticketId: ticket.id, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + } //******************************** */ diff --git a/src/modules/tickets/tickets.controller.ts b/src/modules/tickets/tickets.controller.ts index 100cf64..586e760 100755 --- a/src/modules/tickets/tickets.controller.ts +++ b/src/modules/tickets/tickets.controller.ts @@ -3,7 +3,7 @@ import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto"; import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto"; -import { CreateTicketDto } from "./DTO/create-ticket.dto"; +import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto"; import { ReferTicketDto } from "./DTO/refer-ticket.dto"; import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto"; import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto"; @@ -69,13 +69,13 @@ export class TicketsController { return this.ticketsService.createTicket(createDto, userId); } - // @ApiOperation({ summary: "create ticket by admin" }) - // @AdminRoute() - // @PermissionsDec(PermissionEnum.TICKETS) - // @Post("admin") - // createTicketByAdmin(@Body() createDto: CreateTicketDto, @UserDec("id") adminId: string) { - // return this.ticketsService.createTicketByAdmin(createDto, adminId); - // } + @ApiOperation({ summary: "create ticket by admin" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.TICKETS) + @Post("admin") + createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserDec("id") adminId: string) { + return this.ticketsService.createTicketByAdmin(createDto, adminId); + } @ApiOperation({ summary: "Get all tickets of user ==> user route" }) @Get()