add : ticket to task
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AddTicketToTaskEntity1784600000000 implements MigrationInterface {
|
||||
name = "AddTicketToTaskEntity1784600000000";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "tm_tasks" ADD "ticketId" uuid`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "tm_tasks" ADD CONSTRAINT "FK_tm_tasks_ticket" FOREIGN KEY ("ticketId") REFERENCES "ticket"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "tm_tasks" DROP CONSTRAINT "FK_tm_tasks_ticket"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_tasks" DROP COLUMN "ticketId"`);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,15 @@ export class CreateTaskDto {
|
||||
@IsNotEmpty()
|
||||
taskPhaseId: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "ID of the related ticket",
|
||||
required: false,
|
||||
example: "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||||
})
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
ticketId?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "IDs of users to assign to the task",
|
||||
required: false,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { TMAttachment } from "./attachment.entity";
|
||||
import { TMCheckListItem } from "./check-list-item.entity";
|
||||
import { TMTime } from "./time.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||
|
||||
@Entity("tm_tasks")
|
||||
export class TMTask extends BaseEntity {
|
||||
@@ -27,6 +28,13 @@ export class TMTask extends BaseEntity {
|
||||
@Column({type: 'int', default: 1})
|
||||
priority: number;
|
||||
|
||||
@ManyToOne(() => Ticket, { nullable: true, onDelete: "SET NULL" })
|
||||
@JoinColumn({ name: "ticketId" })
|
||||
ticket: Ticket | null;
|
||||
|
||||
@Column({ type: "uuid", nullable: true })
|
||||
ticketId: string | null;
|
||||
|
||||
// --- Relations ---
|
||||
|
||||
@ManyToOne(() => TMTaskPhase, (taskPhase) => taskPhase.tasks, {
|
||||
|
||||
@@ -7,7 +7,8 @@ import { CreateTaskDto } from "../dto/task/create-task.dto";
|
||||
import { UpdateTaskDto } from "../dto/task/update-task.dto";
|
||||
import { ChangeTaskPhaseDto } from "../dto/task/change-task-phase.dto";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { TaskMessage, TaskPhaseMessage } from "../../../common/enums/message.enum";
|
||||
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||
import { TaskMessage, TaskPhaseMessage, TicketMessageEnum } from "../../../common/enums/message.enum";
|
||||
import { TaskPhaseRepository } from "../repositories/task-phase.repository";
|
||||
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||
@@ -21,6 +22,8 @@ export class TaskService {
|
||||
private readonly taskPhaseRepository: TaskPhaseRepository,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
@InjectRepository(Ticket)
|
||||
private readonly ticketRepository: Repository<Ticket>,
|
||||
) {}
|
||||
|
||||
async findOneOrFail(id: string): Promise<TMTask> {
|
||||
@@ -29,12 +32,19 @@ export class TaskService {
|
||||
relations: {
|
||||
taskPhase: true,
|
||||
users: true,
|
||||
ticket: true,
|
||||
},
|
||||
});
|
||||
if (!task) throw new NotFoundException(TaskMessage.TASK_NOT_FOUND);
|
||||
return task;
|
||||
}
|
||||
|
||||
private async findTicketOrFail(ticketId: string): Promise<Ticket> {
|
||||
const ticket = await this.ticketRepository.findOne({ where: { id: ticketId } });
|
||||
if (!ticket) throw new NotFoundException(TicketMessageEnum.TICKET_NOT_FOUND);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
async findTaskPhaseOrFail(taskPhaseId: string): Promise<TMTaskPhase> {
|
||||
const taskPhase = await this.taskPhaseRepository.findOne({
|
||||
where: { id: taskPhaseId },
|
||||
@@ -50,10 +60,14 @@ export class TaskService {
|
||||
}
|
||||
|
||||
async create(dto: CreateTaskDto): Promise<TMTask> {
|
||||
const { userIds, startDate, endDate, ...rest } = dto;
|
||||
const { userIds, startDate, endDate, ticketId, ...rest } = dto;
|
||||
|
||||
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||
|
||||
if (ticketId) {
|
||||
await this.findTicketOrFail(ticketId);
|
||||
}
|
||||
|
||||
if (userIds?.length) {
|
||||
const isUsersValid = userIds.every((userId) => taskPhase.project.users.some((user) => user.id === userId));
|
||||
if (!isUsersValid) throw new BadRequestException(TaskMessage.USERS_ASSIGNED_TO_TASK_NOT_PROJECT_MEMBER);
|
||||
@@ -61,6 +75,7 @@ export class TaskService {
|
||||
|
||||
const task = this.taskRepository.create({
|
||||
...rest,
|
||||
ticketId,
|
||||
startDate: startDate ? new Date(startDate) : undefined,
|
||||
endDate: endDate ? new Date(endDate) : undefined,
|
||||
});
|
||||
@@ -74,7 +89,11 @@ export class TaskService {
|
||||
|
||||
async update(id: string, dto: UpdateTaskDto): Promise<TMTask> {
|
||||
const task = await this.findOneOrFail(id);
|
||||
const { userIds, startDate, endDate, ...rest } = dto;
|
||||
const { userIds, startDate, endDate, ticketId, ...rest } = dto;
|
||||
|
||||
if (ticketId) {
|
||||
await this.findTicketOrFail(ticketId);
|
||||
}
|
||||
|
||||
if (dto.taskPhaseId) {
|
||||
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||
@@ -87,6 +106,7 @@ export class TaskService {
|
||||
|
||||
Object.assign(task, {
|
||||
...rest,
|
||||
...(ticketId !== undefined && { ticketId }),
|
||||
startDate: startDate ? new Date(startDate) : task.startDate,
|
||||
endDate: endDate ? new Date(endDate) : task.endDate,
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ export class TaskRepository extends Repository<TMTask> {
|
||||
.leftJoinAndSelect("task.checkListItems", "checkListItem")
|
||||
.leftJoinAndSelect("task.users", "user")
|
||||
.leftJoinAndSelect("task.times", "time")
|
||||
.leftJoinAndSelect("task.ticket", "ticket")
|
||||
.leftJoinAndSelect("time.admin", "timeAdmin")
|
||||
|
||||
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
|
||||
@@ -45,6 +46,11 @@ export class TaskRepository extends Repository<TMTask> {
|
||||
"user.firstName",
|
||||
"user.lastName",
|
||||
|
||||
"ticket.id",
|
||||
"ticket.subject",
|
||||
"ticket.numericId",
|
||||
"ticket.status",
|
||||
|
||||
"time.id",
|
||||
"time.startedAt",
|
||||
"time.endedAt",
|
||||
@@ -113,12 +119,13 @@ export class TaskRepository extends Repository<TMTask> {
|
||||
// response needs. IN (:...ids) doesn't preserve order, so re-order to match seekQuery.
|
||||
const detailRows = await this.createQueryBuilder("task")
|
||||
.leftJoinAndSelect("task.remarks", "remark")
|
||||
.leftJoinAndSelect("task.ticket", "ticket")
|
||||
.loadRelationCountAndMap("task.attachmentCount", "task.attachments")
|
||||
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
|
||||
.loadRelationCountAndMap("task.completedCheckListItemCount", "task.checkListItems", "completedCheckListItems", (qb) =>
|
||||
qb.where("completedCheckListItems.isDone = :isDone", { isDone: true }),
|
||||
)
|
||||
.select(["task", "remark.id", "remark.title", "remark.color"])
|
||||
.select(["task", "remark.id", "remark.title", "remark.color", "ticket.id", "ticket.subject", "ticket.numericId", "ticket.status"])
|
||||
.where("task.id IN (:...ids)", { ids })
|
||||
.getMany();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { WorkspaceRepository } from "./repositories/workspace.repository";
|
||||
import { WorkspaceController } from "./controllers/workspace.controller";
|
||||
import { WorkspaceService } from "./providers/workspace.service";
|
||||
import { User } from "../users/entities/user.entity";
|
||||
import { Ticket } from "../tickets/entities/ticket.entity";
|
||||
import { ProjectController } from "./controllers/project.controller";
|
||||
import { ProjectRepository } from "./repositories/project.repository";
|
||||
import { ProjectService } from "./providers/project.service";
|
||||
@@ -37,7 +38,7 @@ import { TimeService } from "./providers/time.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([TMWorkspace, TMProject, TMTaskPhase, TMTask, TMRemark, TMAttachment, TMCheckListItem, TMTime, User]),
|
||||
TypeOrmModule.forFeature([TMWorkspace, TMProject, TMTaskPhase, TMTask, TMRemark, TMAttachment, TMCheckListItem, TMTime, User, Ticket]),
|
||||
],
|
||||
controllers: [
|
||||
WorkspaceController,
|
||||
|
||||
Reference in New Issue
Block a user