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()
|
@IsNotEmpty()
|
||||||
taskPhaseId: string;
|
taskPhaseId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "ID of the related ticket",
|
||||||
|
required: false,
|
||||||
|
example: "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
ticketId?: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: "IDs of users to assign to the task",
|
description: "IDs of users to assign to the task",
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { TMAttachment } from "./attachment.entity";
|
|||||||
import { TMCheckListItem } from "./check-list-item.entity";
|
import { TMCheckListItem } from "./check-list-item.entity";
|
||||||
import { TMTime } from "./time.entity";
|
import { TMTime } from "./time.entity";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||||
|
|
||||||
@Entity("tm_tasks")
|
@Entity("tm_tasks")
|
||||||
export class TMTask extends BaseEntity {
|
export class TMTask extends BaseEntity {
|
||||||
@@ -27,6 +28,13 @@ export class TMTask extends BaseEntity {
|
|||||||
@Column({type: 'int', default: 1})
|
@Column({type: 'int', default: 1})
|
||||||
priority: number;
|
priority: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => Ticket, { nullable: true, onDelete: "SET NULL" })
|
||||||
|
@JoinColumn({ name: "ticketId" })
|
||||||
|
ticket: Ticket | null;
|
||||||
|
|
||||||
|
@Column({ type: "uuid", nullable: true })
|
||||||
|
ticketId: string | null;
|
||||||
|
|
||||||
// --- Relations ---
|
// --- Relations ---
|
||||||
|
|
||||||
@ManyToOne(() => TMTaskPhase, (taskPhase) => taskPhase.tasks, {
|
@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 { UpdateTaskDto } from "../dto/task/update-task.dto";
|
||||||
import { ChangeTaskPhaseDto } from "../dto/task/change-task-phase.dto";
|
import { ChangeTaskPhaseDto } from "../dto/task/change-task-phase.dto";
|
||||||
import { User } from "../../users/entities/user.entity";
|
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 { TaskPhaseRepository } from "../repositories/task-phase.repository";
|
||||||
import { TMTaskPhase } from "../entities/task-phase.entity";
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
import { PermissionEnum } from "../../users/enums/permission.enum";
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
@@ -21,6 +22,8 @@ export class TaskService {
|
|||||||
private readonly taskPhaseRepository: TaskPhaseRepository,
|
private readonly taskPhaseRepository: TaskPhaseRepository,
|
||||||
@InjectRepository(User)
|
@InjectRepository(User)
|
||||||
private readonly userRepository: Repository<User>,
|
private readonly userRepository: Repository<User>,
|
||||||
|
@InjectRepository(Ticket)
|
||||||
|
private readonly ticketRepository: Repository<Ticket>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async findOneOrFail(id: string): Promise<TMTask> {
|
async findOneOrFail(id: string): Promise<TMTask> {
|
||||||
@@ -29,12 +32,19 @@ export class TaskService {
|
|||||||
relations: {
|
relations: {
|
||||||
taskPhase: true,
|
taskPhase: true,
|
||||||
users: true,
|
users: true,
|
||||||
|
ticket: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!task) throw new NotFoundException(TaskMessage.TASK_NOT_FOUND);
|
if (!task) throw new NotFoundException(TaskMessage.TASK_NOT_FOUND);
|
||||||
return task;
|
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> {
|
async findTaskPhaseOrFail(taskPhaseId: string): Promise<TMTaskPhase> {
|
||||||
const taskPhase = await this.taskPhaseRepository.findOne({
|
const taskPhase = await this.taskPhaseRepository.findOne({
|
||||||
where: { id: taskPhaseId },
|
where: { id: taskPhaseId },
|
||||||
@@ -50,10 +60,14 @@ export class TaskService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async create(dto: CreateTaskDto): Promise<TMTask> {
|
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);
|
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||||
|
|
||||||
|
if (ticketId) {
|
||||||
|
await this.findTicketOrFail(ticketId);
|
||||||
|
}
|
||||||
|
|
||||||
if (userIds?.length) {
|
if (userIds?.length) {
|
||||||
const isUsersValid = userIds.every((userId) => taskPhase.project.users.some((user) => user.id === userId));
|
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);
|
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({
|
const task = this.taskRepository.create({
|
||||||
...rest,
|
...rest,
|
||||||
|
ticketId,
|
||||||
startDate: startDate ? new Date(startDate) : undefined,
|
startDate: startDate ? new Date(startDate) : undefined,
|
||||||
endDate: endDate ? new Date(endDate) : undefined,
|
endDate: endDate ? new Date(endDate) : undefined,
|
||||||
});
|
});
|
||||||
@@ -74,7 +89,11 @@ export class TaskService {
|
|||||||
|
|
||||||
async update(id: string, dto: UpdateTaskDto): Promise<TMTask> {
|
async update(id: string, dto: UpdateTaskDto): Promise<TMTask> {
|
||||||
const task = await this.findOneOrFail(id);
|
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) {
|
if (dto.taskPhaseId) {
|
||||||
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||||
@@ -87,6 +106,7 @@ export class TaskService {
|
|||||||
|
|
||||||
Object.assign(task, {
|
Object.assign(task, {
|
||||||
...rest,
|
...rest,
|
||||||
|
...(ticketId !== undefined && { ticketId }),
|
||||||
startDate: startDate ? new Date(startDate) : task.startDate,
|
startDate: startDate ? new Date(startDate) : task.startDate,
|
||||||
endDate: endDate ? new Date(endDate) : task.endDate,
|
endDate: endDate ? new Date(endDate) : task.endDate,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class TaskRepository extends Repository<TMTask> {
|
|||||||
.leftJoinAndSelect("task.checkListItems", "checkListItem")
|
.leftJoinAndSelect("task.checkListItems", "checkListItem")
|
||||||
.leftJoinAndSelect("task.users", "user")
|
.leftJoinAndSelect("task.users", "user")
|
||||||
.leftJoinAndSelect("task.times", "time")
|
.leftJoinAndSelect("task.times", "time")
|
||||||
|
.leftJoinAndSelect("task.ticket", "ticket")
|
||||||
.leftJoinAndSelect("time.admin", "timeAdmin")
|
.leftJoinAndSelect("time.admin", "timeAdmin")
|
||||||
|
|
||||||
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
|
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
|
||||||
@@ -45,6 +46,11 @@ export class TaskRepository extends Repository<TMTask> {
|
|||||||
"user.firstName",
|
"user.firstName",
|
||||||
"user.lastName",
|
"user.lastName",
|
||||||
|
|
||||||
|
"ticket.id",
|
||||||
|
"ticket.subject",
|
||||||
|
"ticket.numericId",
|
||||||
|
"ticket.status",
|
||||||
|
|
||||||
"time.id",
|
"time.id",
|
||||||
"time.startedAt",
|
"time.startedAt",
|
||||||
"time.endedAt",
|
"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.
|
// response needs. IN (:...ids) doesn't preserve order, so re-order to match seekQuery.
|
||||||
const detailRows = await this.createQueryBuilder("task")
|
const detailRows = await this.createQueryBuilder("task")
|
||||||
.leftJoinAndSelect("task.remarks", "remark")
|
.leftJoinAndSelect("task.remarks", "remark")
|
||||||
|
.leftJoinAndSelect("task.ticket", "ticket")
|
||||||
.loadRelationCountAndMap("task.attachmentCount", "task.attachments")
|
.loadRelationCountAndMap("task.attachmentCount", "task.attachments")
|
||||||
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
|
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
|
||||||
.loadRelationCountAndMap("task.completedCheckListItemCount", "task.checkListItems", "completedCheckListItems", (qb) =>
|
.loadRelationCountAndMap("task.completedCheckListItemCount", "task.checkListItems", "completedCheckListItems", (qb) =>
|
||||||
qb.where("completedCheckListItems.isDone = :isDone", { isDone: true }),
|
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 })
|
.where("task.id IN (:...ids)", { ids })
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { WorkspaceRepository } from "./repositories/workspace.repository";
|
|||||||
import { WorkspaceController } from "./controllers/workspace.controller";
|
import { WorkspaceController } from "./controllers/workspace.controller";
|
||||||
import { WorkspaceService } from "./providers/workspace.service";
|
import { WorkspaceService } from "./providers/workspace.service";
|
||||||
import { User } from "../users/entities/user.entity";
|
import { User } from "../users/entities/user.entity";
|
||||||
|
import { Ticket } from "../tickets/entities/ticket.entity";
|
||||||
import { ProjectController } from "./controllers/project.controller";
|
import { ProjectController } from "./controllers/project.controller";
|
||||||
import { ProjectRepository } from "./repositories/project.repository";
|
import { ProjectRepository } from "./repositories/project.repository";
|
||||||
import { ProjectService } from "./providers/project.service";
|
import { ProjectService } from "./providers/project.service";
|
||||||
@@ -37,7 +38,7 @@ import { TimeService } from "./providers/time.service";
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
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: [
|
controllers: [
|
||||||
WorkspaceController,
|
WorkspaceController,
|
||||||
|
|||||||
Reference in New Issue
Block a user