From 5b45c66ee994b8a33669d0ccb5765950529ef46d Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 15 Aug 2024 17:46:45 +0200 Subject: [PATCH 1/8] feat: get daily plans by team --- .../tasks/daily-plan/daily-plan.controller.ts | 25 +++++++++ .../tasks/daily-plan/daily-plan.service.ts | 51 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tasks/daily-plan/daily-plan.controller.ts b/packages/core/src/tasks/daily-plan/daily-plan.controller.ts index 0e66ee23d44..6ed1677cb62 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.controller.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.controller.ts @@ -63,6 +63,31 @@ export class DailyPlanController extends CrudController { return await this.dailyPlanService.getMyPlans(params); } + /** + * GET daily plans for a given team + * + * @param options + * @param employeeId + * @returns + */ + @ApiOperation({ + summary: 'Find team daily plans.' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found plans', + type: DailyPlan + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'No Record found' + }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.DAILY_PLAN_READ) + @Get('team') + async getTeamDailyPlans(@Query() params: PaginationParams): Promise> { + return await this.dailyPlanService.getTeamDailyPlans(params); + } + /** * GET daily plans for a given employee * diff --git a/packages/core/src/tasks/daily-plan/daily-plan.service.ts b/packages/core/src/tasks/daily-plan/daily-plan.service.ts index 6626953d31d..10bb6dc3d2b 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.service.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { BadRequestException, HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { SelectQueryBuilder, UpdateResult } from 'typeorm'; import { @@ -158,6 +158,55 @@ export class DailyPlanService extends TenantAwareCrudService { } } + /** + * Retrieves daily plans for all employees of a specific team with pagination and additional query options. + * + * @param teamId - The ID of the team for whom to retrieve daily plans. + * @param options - Pagination and additional query options for filtering and retrieving daily plans. + * @returns A promise that resolves to an object containing the list of daily plans and the total count. + * @throws BadRequestException - If there's an error during the query. + */ + async getTeamDailyPlans(options: PaginationParams): Promise> { + try { + const { where } = options; + const { organizationId, teamIds } = where; + const tenantId = RequestContext.currentTenantId(); + + // Create the initial query + const query = this.typeOrmRepository.createQueryBuilder(this.tableName); + + // Join related entities + query.leftJoinAndSelect(`${query.alias}.employee`, 'employee'); + query.leftJoinAndSelect(`${query.alias}.tasks`, 'tasks'); + query.leftJoinAndSelect('employee.teams', 'teams'); + + // Apply optional find options if provided + query.setFindOptions({ + ...(isNotEmpty(options) && + isNotEmpty(options.where) && { + where: options.where + }), + ...(isNotEmpty(options) && + isNotEmpty(options.relations) && { + relations: options.relations + }) + }); + + // Filter conditions + query.andWhere(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }); + query.andWhere(p(`"${query.alias}"."organizationId" = :organizationId`), { organizationId }); + query.andWhere('teams."organizationTeamId" IN (:...teamIds)', { teamIds }); + + // Retrieve results and total count + const [items, total] = await query.getManyAndCount(); + // Return the pagination result + return { items, total }; + } catch (error) { + console.log('Error while fetching daily plans for team'); + throw new HttpException(`Failed to fetch daily plans for team : ${error.message}`, HttpStatus.BAD_REQUEST); + } + } + /** * Retrieves daily plans for the current employee based on given pagination options. * From b62d12c02ca73660c99a16d162da202b3a82a3e9 Mon Sep 17 00:00:00 2001 From: "Rahul R." Date: Fri, 16 Aug 2024 12:14:05 +0530 Subject: [PATCH 2/8] refactor: updated daily plan controller --- .../tasks/daily-plan/daily-plan.controller.ts | 32 +++++++------------ .../tasks/daily-plan/daily-plan.service.ts | 30 ++++------------- 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/packages/core/src/tasks/daily-plan/daily-plan.controller.ts b/packages/core/src/tasks/daily-plan/daily-plan.controller.ts index 6ed1677cb62..4a0b22e27df 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.controller.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.controller.ts @@ -13,21 +13,14 @@ import { } from '@nestjs/common'; import { UpdateResult } from 'typeorm'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { - IDailyPlan, - IDailyPlanTasksUpdateInput, - IEmployee, - IPagination, - ITask, - PermissionsEnum -} from '@gauzy/contracts'; +import { ID, IDailyPlan, IDailyPlanTasksUpdateInput, IPagination, PermissionsEnum } from '@gauzy/contracts'; import { CrudController, PaginationParams } from '../../core/crud'; import { UseValidationPipe } from '../../shared/pipes'; -import { DailyPlan } from './daily-plan.entity'; -import { DailyPlanService } from './daily-plan.service'; import { CreateDailyPlanDTO, RemoveTaskFromManyPlansDTO, UpdateDailyPlanDTO } from './dto'; import { PermissionGuard, TenantPermissionGuard } from '../../shared/guards'; import { Permissions } from '../../shared/decorators'; +import { DailyPlan } from './daily-plan.entity'; +import { DailyPlanService } from './daily-plan.service'; @ApiTags('Daily Plan') @UseGuards(TenantPermissionGuard, PermissionGuard) @@ -110,7 +103,7 @@ export class DailyPlanController extends CrudController { @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.DAILY_PLAN_READ) @Get('employee/:id') async getEmployeeDailyPlans( - @Param('id') employeeId: IEmployee['id'], + @Param('id') employeeId: ID, @Query() params: PaginationParams ): Promise> { return await this.dailyPlanService.getDailyPlansByEmployee(params, employeeId); @@ -138,7 +131,7 @@ export class DailyPlanController extends CrudController { @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.DAILY_PLAN_READ) @Get('task/:id') async getDailyPlansForTaskId( - @Param('id') taskId: ITask['id'], + @Param('id') taskId: ID, @Query() params: PaginationParams ): Promise> { return await this.dailyPlanService.getDailyPlansByTask(params, taskId); @@ -166,7 +159,7 @@ export class DailyPlanController extends CrudController { @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.DAILY_PLAN_CREATE, PermissionsEnum.DAILY_PLAN_UPDATE) @Post(':id/task') // Route for adding a task to a daily plan async addTaskToDailyPlan( - @Param('id') planId: IDailyPlan['id'], // Extract the plan ID from the URL parameter + @Param('id') planId: ID, // Extract the plan ID from the URL parameter @Body() input: IDailyPlanTasksUpdateInput // Data for updating the daily plan ): Promise { // Call the service method to add a task to the daily plan @@ -196,7 +189,7 @@ export class DailyPlanController extends CrudController { @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.DAILY_PLAN_CREATE, PermissionsEnum.DAILY_PLAN_UPDATE) @Put(':id/task') // Endpoint for removing a task from a daily plan async removeTaskFromDailyPlan( - @Param('id') planId: IDailyPlan['id'], // Extract the daily plan ID from the URL parameter + @Param('id') planId: ID, // Extract the daily plan ID from the URL parameter @Body() input: IDailyPlanTasksUpdateInput // Data for updating the daily plan ): Promise { // Call the service to remove the task from the daily plan @@ -225,7 +218,7 @@ export class DailyPlanController extends CrudController { }) @Put(':taskId/remove') // Endpoint for removing a task from many daily plans async removeTaskFromManyPlans( - @Param('taskId') taskId: ITask['id'], + @Param('taskId') taskId: ID, @Body() input: RemoveTaskFromManyPlansDTO ): Promise { return this.dailyPlanService.removeTaskFromManyPlans(taskId, input); @@ -300,10 +293,7 @@ export class DailyPlanController extends CrudController { @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.DAILY_PLAN_UPDATE) @Put(':id') @UseValidationPipe({ transform: true, whitelist: true }) - async update( - @Query('id') id: IDailyPlan['id'], - @Body() entity: UpdateDailyPlanDTO - ): Promise { + async update(@Query('id') id: ID, @Body() entity: UpdateDailyPlanDTO): Promise { return await this.dailyPlanService.updateDailyPlan(id, entity); } @@ -328,7 +318,7 @@ export class DailyPlanController extends CrudController { }) @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.DAILY_PLAN_DELETE) @Delete(':id') - async delete(@Param('id') planId: IDailyPlan['id']) { - return await this.dailyPlanService.deletePlan(planId); + async delete(@Param('id') planId: ID) { + return await this.dailyPlanService.delete(planId); } } diff --git a/packages/core/src/tasks/daily-plan/daily-plan.service.ts b/packages/core/src/tasks/daily-plan/daily-plan.service.ts index 10bb6dc3d2b..83a6702aca9 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.service.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.service.ts @@ -2,6 +2,7 @@ import { BadRequestException, HttpException, HttpStatus, Injectable, NotFoundExc import { InjectRepository } from '@nestjs/typeorm'; import { SelectQueryBuilder, UpdateResult } from 'typeorm'; import { + ID, IDailyPlan, IDailyPlanCreateInput, IDailyPlansTasksUpdateInput, @@ -146,10 +147,7 @@ export class DailyPlanService extends TenantAwareCrudService { * @returns A promise that resolves to an object containing the list of daily plans and the total count. * @throws BadRequestException - If there's an error during the query. */ - async getDailyPlansByEmployee( - options: PaginationParams, - employeeId?: IEmployee['id'] - ): Promise> { + async getDailyPlansByEmployee(options: PaginationParams, employeeId?: ID): Promise> { try { // Fetch all daily plans for specific employee return await this.getAllPlans(options, employeeId); @@ -232,7 +230,7 @@ export class DailyPlanService extends TenantAwareCrudService { * @param input - An object containing details about the task to add, including task ID, employee ID, and organization ID. * @returns The updated daily plan with the newly added task. */ - async addTaskToPlan(planId: IDailyPlan['id'], input: IDailyPlanTasksUpdateInput): Promise { + async addTaskToPlan(planId: ID, input: IDailyPlanTasksUpdateInput): Promise { try { const tenantId = RequestContext.currentTenantId(); const { employeeId, taskId, organizationId } = input; @@ -273,7 +271,7 @@ export class DailyPlanService extends TenantAwareCrudService { * @param input - An object containing details about the task to remove, including task ID, employee ID, and organization ID. * @returns The updated daily plan without the deleted task. */ - async removeTaskFromPlan(planId: IDailyPlan['id'], input: IDailyPlanTasksUpdateInput): Promise { + async removeTaskFromPlan(planId: ID, input: IDailyPlanTasksUpdateInput): Promise { try { const tenantId = RequestContext.currentTenantId(); const { employeeId, taskId, organizationId } = input; @@ -318,7 +316,7 @@ export class DailyPlanService extends TenantAwareCrudService { * @returns The updated daily plans without the deleted task. */ - async removeTaskFromManyPlans(taskId: ITask['id'], input: IDailyPlansTasksUpdateInput): Promise { + async removeTaskFromManyPlans(taskId: ID, input: IDailyPlansTasksUpdateInput): Promise { try { const tenantId = RequestContext.currentTenantId(); const { employeeId, plansIds, organizationId } = input; @@ -392,10 +390,7 @@ export class DailyPlanService extends TenantAwareCrudService { * @returns The updated daily plan including related tasks. * @memberof DailyPlanService */ - async updateDailyPlan( - id: IDailyPlan['id'], - partialEntity: IDailyPlanUpdateInput - ): Promise { + async updateDailyPlan(id: ID, partialEntity: IDailyPlanUpdateInput): Promise { try { const { employeeId, organizationId } = partialEntity; @@ -427,24 +422,13 @@ export class DailyPlanService extends TenantAwareCrudService { } } - /** - * DELETE daily plan - * - * @param {IDailyPlan['id']} planId - * @returns - * @memberof DailyPlanService - */ - async deletePlan(planId: IDailyPlan['id']) { - return await super.delete(planId); - } - /** * Retrieves daily plans for a specific task including employee * @param options pagination and additional query options * @param taskId - The ID of the task for whom to retrieve daily plans. * @returns A promise that resolves to an object containing the list of plans and total count */ - async getDailyPlansByTask(options: PaginationParams, taskId: ITask['id']): Promise> { + async getDailyPlansByTask(options: PaginationParams, taskId: ID): Promise> { try { const { where } = options; const { organizationId } = where; From 6dbe101757d0ed7acd778e8a0c64a271a1dd0bed Mon Sep 17 00:00:00 2001 From: "Rahul R." Date: Fri, 16 Aug 2024 12:21:04 +0530 Subject: [PATCH 3/8] fix(deepscan): removed unused import --- packages/core/src/tasks/daily-plan/daily-plan.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/tasks/daily-plan/daily-plan.service.ts b/packages/core/src/tasks/daily-plan/daily-plan.service.ts index 83a6702aca9..04d86596622 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.service.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.service.ts @@ -9,8 +9,7 @@ import { IDailyPlanTasksUpdateInput, IDailyPlanUpdateInput, IEmployee, - IPagination, - ITask + IPagination } from '@gauzy/contracts'; import { isNotEmpty } from '@gauzy/common'; import { prepareSQLQuery as p } from '../../database/database.helper'; From 01798c47e57c7ac915d6dbc3bc9d4dd396e37f69 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 19 Aug 2024 15:53:09 +0200 Subject: [PATCH 4/8] refact: daily plan adding team relation --- packages/contracts/src/daily-plan.model.ts | 20 +- ...062299873-AlterDailyPlanAddTeamRelation.ts | 205 ++++++++++++++++++ .../core/src/organization-team/dto/index.ts | 8 +- .../dto/organization-team-feature.dto.ts | 16 ++ .../organization-team.entity.ts | 16 +- .../tasks/daily-plan/daily-plan.controller.ts | 3 +- .../src/tasks/daily-plan/daily-plan.entity.ts | 27 ++- .../tasks/daily-plan/daily-plan.service.ts | 9 +- .../daily-plan/dto/create-daily-plan.dto.ts | 13 +- .../daily-plan/dto/update-daily-plan.dto.ts | 4 +- 10 files changed, 294 insertions(+), 27 deletions(-) create mode 100644 packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts create mode 100644 packages/core/src/organization-team/dto/organization-team-feature.dto.ts diff --git a/packages/contracts/src/daily-plan.model.ts b/packages/contracts/src/daily-plan.model.ts index 1eba808d810..33ec48665e1 100644 --- a/packages/contracts/src/daily-plan.model.ts +++ b/packages/contracts/src/daily-plan.model.ts @@ -1,4 +1,5 @@ -import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model'; +import { IRelationalOrganizationTeam } from 'organization-team.model'; +import { IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel, ID } from './base-entity.model'; import { IRelationalEmployee } from './employee.model'; import { ITask } from './task.model'; @@ -14,15 +15,22 @@ export interface IDailyPlanBase extends IBasePerTenantAndOrganizationEntityModel status: DailyPlanStatusEnum; } -export interface IDailyPlan extends IDailyPlanBase, IRelationalEmployee { +export interface IDailyPlan extends IDailyPlanBase, IRelationalEmployee, IRelationalOrganizationTeam { tasks?: ITask[]; } -export interface IDailyPlanCreateInput extends IDailyPlanBase, IRelationalEmployee { - taskId?: ITask['id']; +export interface IDailyPlanCreateInput extends IDailyPlanBase, IRelationalEmployee, IRelationalOrganizationTeam { + taskId?: ID; } -export interface IDailyPlanUpdateInput extends Partial, Pick {} +export interface IDailyPlanUpdateInput + extends Partial, + Pick, + Partial> {} + +export interface IGetDailyPlansByTeamInput extends IBaseRelationsEntityModel, IBasePerTenantAndOrganizationEntityModel { + teamIds?: ID[]; +} export interface IDailyPlanTasksUpdateInput extends Pick, @@ -32,5 +40,5 @@ export interface IDailyPlanTasksUpdateInput export interface IDailyPlansTasksUpdateInput extends Pick, IBasePerTenantAndOrganizationEntityModel { - plansIds: IDailyPlan['id'][]; + plansIds: ID[]; } diff --git a/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts b/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts new file mode 100644 index 00000000000..060ed6ea5a7 --- /dev/null +++ b/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts @@ -0,0 +1,205 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { yellow } from 'chalk'; +import { DatabaseTypeEnum } from '@gauzy/config'; + +export class AlterDailyPlanAddTeamRelation1724062299873 implements MigrationInterface { + name = 'AlterDailyPlanAddTeamRelation1724062299873'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + console.log(yellow(this.name + ' start running!')); + + switch (queryRunner.connection.options.type) { + case DatabaseTypeEnum.sqlite: + case DatabaseTypeEnum.betterSqlite3: + await this.sqliteUpQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.postgres: + await this.postgresUpQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.mysql: + await this.mysqlUpQueryRunner(queryRunner); + break; + default: + throw Error(`Unsupported database: ${queryRunner.connection.options.type}`); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + switch (queryRunner.connection.options.type) { + case DatabaseTypeEnum.sqlite: + case DatabaseTypeEnum.betterSqlite3: + await this.sqliteDownQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.postgres: + await this.postgresDownQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.mysql: + await this.mysqlDownQueryRunner(queryRunner); + break; + default: + throw Error(`Unsupported database: ${queryRunner.connection.options.type}`); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "daily_plan" ADD "organizationTeamId" uuid`); + await queryRunner.query( + `CREATE INDEX "IDX_b022c2b684c35dcc63c22850f1" ON "daily_plan" ("organizationTeamId") ` + ); + await queryRunner.query( + `ALTER TABLE "daily_plan" ADD CONSTRAINT "FK_b022c2b684c35dcc63c22850f13" FOREIGN KEY ("organizationTeamId") REFERENCES "organization_team"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "daily_plan" DROP CONSTRAINT "FK_b022c2b684c35dcc63c22850f13"`); + await queryRunner.query(`DROP INDEX "public"."IDX_b022c2b684c35dcc63c22850f1"`); + await queryRunner.query(`ALTER TABLE "daily_plan" DROP COLUMN "organizationTeamId"`); + } + + /** + * SqliteDB and BetterSQlite3DB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_f2cf366f3f08e31784b056df88"`); + await queryRunner.query(`DROP INDEX "IDX_9779a35ef1338bafb7b90714f1"`); + await queryRunner.query(`DROP INDEX "IDX_ecb357a3764a7344c633a257d7"`); + await queryRunner.query(`DROP INDEX "IDX_ce5e588780497b05cd6267e20e"`); + await queryRunner.query(`DROP INDEX "IDX_903b08cd4c8025e73316342452"`); + await queryRunner.query( + `CREATE TABLE "temporary_daily_plan" ("deletedAt" datetime, "id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "tenantId" varchar, "organizationId" varchar, "date" datetime NOT NULL, "workTimePlanned" decimal NOT NULL, "status" varchar NOT NULL, "employeeId" varchar, "organizationTeamId" varchar, CONSTRAINT "FK_f2cf366f3f08e31784b056df880" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_9779a35ef1338bafb7b90714f16" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_ecb357a3764a7344c633a257d76" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_daily_plan"("deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId") SELECT "deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId" FROM "daily_plan"` + ); + await queryRunner.query(`DROP TABLE "daily_plan"`); + await queryRunner.query(`ALTER TABLE "temporary_daily_plan" RENAME TO "daily_plan"`); + await queryRunner.query(`CREATE INDEX "IDX_f2cf366f3f08e31784b056df88" ON "daily_plan" ("employeeId") `); + await queryRunner.query(`CREATE INDEX "IDX_9779a35ef1338bafb7b90714f1" ON "daily_plan" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_ecb357a3764a7344c633a257d7" ON "daily_plan" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_ce5e588780497b05cd6267e20e" ON "daily_plan" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_903b08cd4c8025e73316342452" ON "daily_plan" ("isActive") `); + await queryRunner.query( + `CREATE INDEX "IDX_b022c2b684c35dcc63c22850f1" ON "daily_plan" ("organizationTeamId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_f2cf366f3f08e31784b056df88"`); + await queryRunner.query(`DROP INDEX "IDX_9779a35ef1338bafb7b90714f1"`); + await queryRunner.query(`DROP INDEX "IDX_ecb357a3764a7344c633a257d7"`); + await queryRunner.query(`DROP INDEX "IDX_ce5e588780497b05cd6267e20e"`); + await queryRunner.query(`DROP INDEX "IDX_903b08cd4c8025e73316342452"`); + await queryRunner.query(`DROP INDEX "IDX_b022c2b684c35dcc63c22850f1"`); + await queryRunner.query( + `CREATE TABLE "temporary_daily_plan" ("deletedAt" datetime, "id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "tenantId" varchar, "organizationId" varchar, "date" datetime NOT NULL, "workTimePlanned" decimal NOT NULL, "status" varchar NOT NULL, "employeeId" varchar, "organizationTeamId" varchar, CONSTRAINT "FK_f2cf366f3f08e31784b056df880" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_9779a35ef1338bafb7b90714f16" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_ecb357a3764a7344c633a257d76" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b022c2b684c35dcc63c22850f13" FOREIGN KEY ("organizationTeamId") REFERENCES "organization_team" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_daily_plan"("deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId", "organizationTeamId") SELECT "deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId", "organizationTeamId" FROM "daily_plan"` + ); + await queryRunner.query(`DROP TABLE "daily_plan"`); + await queryRunner.query(`ALTER TABLE "temporary_daily_plan" RENAME TO "daily_plan"`); + await queryRunner.query(`CREATE INDEX "IDX_f2cf366f3f08e31784b056df88" ON "daily_plan" ("employeeId") `); + await queryRunner.query(`CREATE INDEX "IDX_9779a35ef1338bafb7b90714f1" ON "daily_plan" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_ecb357a3764a7344c633a257d7" ON "daily_plan" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_ce5e588780497b05cd6267e20e" ON "daily_plan" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_903b08cd4c8025e73316342452" ON "daily_plan" ("isActive") `); + await queryRunner.query( + `CREATE INDEX "IDX_b022c2b684c35dcc63c22850f1" ON "daily_plan" ("organizationTeamId") ` + ); + } + + /** + * SqliteDB and BetterSQlite3DB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_b022c2b684c35dcc63c22850f1"`); + await queryRunner.query(`DROP INDEX "IDX_903b08cd4c8025e73316342452"`); + await queryRunner.query(`DROP INDEX "IDX_ce5e588780497b05cd6267e20e"`); + await queryRunner.query(`DROP INDEX "IDX_ecb357a3764a7344c633a257d7"`); + await queryRunner.query(`DROP INDEX "IDX_9779a35ef1338bafb7b90714f1"`); + await queryRunner.query(`DROP INDEX "IDX_f2cf366f3f08e31784b056df88"`); + await queryRunner.query(`ALTER TABLE "daily_plan" RENAME TO "temporary_daily_plan"`); + await queryRunner.query( + `CREATE TABLE "daily_plan" ("deletedAt" datetime, "id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "tenantId" varchar, "organizationId" varchar, "date" datetime NOT NULL, "workTimePlanned" decimal NOT NULL, "status" varchar NOT NULL, "employeeId" varchar, "organizationTeamId" varchar, CONSTRAINT "FK_f2cf366f3f08e31784b056df880" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_9779a35ef1338bafb7b90714f16" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_ecb357a3764a7344c633a257d76" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "daily_plan"("deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId", "organizationTeamId") SELECT "deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId", "organizationTeamId" FROM "temporary_daily_plan"` + ); + await queryRunner.query(`DROP TABLE "temporary_daily_plan"`); + await queryRunner.query( + `CREATE INDEX "IDX_b022c2b684c35dcc63c22850f1" ON "daily_plan" ("organizationTeamId") ` + ); + await queryRunner.query(`CREATE INDEX "IDX_903b08cd4c8025e73316342452" ON "daily_plan" ("isActive") `); + await queryRunner.query(`CREATE INDEX "IDX_ce5e588780497b05cd6267e20e" ON "daily_plan" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_ecb357a3764a7344c633a257d7" ON "daily_plan" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_9779a35ef1338bafb7b90714f1" ON "daily_plan" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_f2cf366f3f08e31784b056df88" ON "daily_plan" ("employeeId") `); + await queryRunner.query(`DROP INDEX "IDX_b022c2b684c35dcc63c22850f1"`); + await queryRunner.query(`DROP INDEX "IDX_903b08cd4c8025e73316342452"`); + await queryRunner.query(`DROP INDEX "IDX_ce5e588780497b05cd6267e20e"`); + await queryRunner.query(`DROP INDEX "IDX_ecb357a3764a7344c633a257d7"`); + await queryRunner.query(`DROP INDEX "IDX_9779a35ef1338bafb7b90714f1"`); + await queryRunner.query(`DROP INDEX "IDX_f2cf366f3f08e31784b056df88"`); + await queryRunner.query(`ALTER TABLE "daily_plan" RENAME TO "temporary_daily_plan"`); + await queryRunner.query( + `CREATE TABLE "daily_plan" ("deletedAt" datetime, "id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "tenantId" varchar, "organizationId" varchar, "date" datetime NOT NULL, "workTimePlanned" decimal NOT NULL, "status" varchar NOT NULL, "employeeId" varchar, CONSTRAINT "FK_f2cf366f3f08e31784b056df880" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_9779a35ef1338bafb7b90714f16" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_ecb357a3764a7344c633a257d76" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "daily_plan"("deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId") SELECT "deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "date", "workTimePlanned", "status", "employeeId" FROM "temporary_daily_plan"` + ); + await queryRunner.query(`DROP TABLE "temporary_daily_plan"`); + await queryRunner.query(`CREATE INDEX "IDX_903b08cd4c8025e73316342452" ON "daily_plan" ("isActive") `); + await queryRunner.query(`CREATE INDEX "IDX_ce5e588780497b05cd6267e20e" ON "daily_plan" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_ecb357a3764a7344c633a257d7" ON "daily_plan" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_9779a35ef1338bafb7b90714f1" ON "daily_plan" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_f2cf366f3f08e31784b056df88" ON "daily_plan" ("employeeId") `); + } + + /** + * MySQL Up Migration + * + * @param queryRunner + */ + public async mysqlUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`daily_plan\` ADD \`organizationTeamId\` CHAR(36)`); + await queryRunner.query( + `CREATE INDEX \`IDX_b022c2b684c35dcc63c22850f1\` ON \`daily_plan\` (\`organizationTeamId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`daily_plan\` ADD CONSTRAINT \`FK_b022c2b684c35dcc63c22850f13\` FOREIGN KEY (\`organizationTeamId\`) REFERENCES \`organization_team\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + } + + /** + * MySQL Down Migration + * + * @param queryRunner + */ + public async mysqlDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`daily_plan\` DROP FOREIGN KEY \`FK_b022c2b684c35dcc63c22850f13\``); + await queryRunner.query(`DROP INDEX \`IDX_b022c2b684c35dcc63c22850f1\` ON \`daily_plan\``); + await queryRunner.query(`ALTER TABLE \`daily_plan\` DROP COLUMN \`organizationTeamId\``); + } +} diff --git a/packages/core/src/organization-team/dto/index.ts b/packages/core/src/organization-team/dto/index.ts index 48ffe5cfc63..487bbbdf58b 100644 --- a/packages/core/src/organization-team/dto/index.ts +++ b/packages/core/src/organization-team/dto/index.ts @@ -1,4 +1,4 @@ - -export * from "./create-organization-team.dto"; -export * from "./update-organization-team.dto"; -export * from "./organization-team-statistic.dto"; +export * from './create-organization-team.dto'; +export * from './update-organization-team.dto'; +export * from './organization-team-statistic.dto'; +export * from './organization-team-feature.dto'; diff --git a/packages/core/src/organization-team/dto/organization-team-feature.dto.ts b/packages/core/src/organization-team/dto/organization-team-feature.dto.ts new file mode 100644 index 00000000000..e48b7a7676f --- /dev/null +++ b/packages/core/src/organization-team/dto/organization-team-feature.dto.ts @@ -0,0 +1,16 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsObject, IsUUID, ValidateIf } from 'class-validator'; +import { ID, IOrganizationTeam, IRelationalOrganizationTeam } from '@gauzy/contracts'; +import { OrganizationTeam } from '../organization-team.entity'; + +export class OrganizationTeamFeatureDTO implements IRelationalOrganizationTeam { + @ApiPropertyOptional({ type: () => OrganizationTeam }) + @ValidateIf((it) => !it.organizationTeamId || it.organizationTeam) + @IsObject() + readonly organizationTeam: IOrganizationTeam; + + @ApiPropertyOptional({ type: () => String }) + @ValidateIf((it) => !it.employee || it.employeeId) + @IsUUID() + readonly organizationTeamId: ID; +} diff --git a/packages/core/src/organization-team/organization-team.entity.ts b/packages/core/src/organization-team/organization-team.entity.ts index 35ad489ba20..98cc9347aa1 100644 --- a/packages/core/src/organization-team/organization-team.entity.ts +++ b/packages/core/src/organization-team/organization-team.entity.ts @@ -1,5 +1,7 @@ import { JoinTable, JoinColumn, RelationId } from 'typeorm'; import { + ID, + IDailyPlan, IEquipmentSharing, IGoal, IImageAsset, @@ -20,6 +22,7 @@ import { import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator'; import { + DailyPlan, EquipmentSharing, Goal, ImageAsset, @@ -166,7 +169,7 @@ export class OrganizationTeam extends TenantOrganizationBaseEntity implements IO @RelationId((it: OrganizationTeam) => it.createdBy) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - createdById?: IUser['id']; + createdById?: ID; /** * ImageAsset @@ -190,7 +193,7 @@ export class OrganizationTeam extends TenantOrganizationBaseEntity implements IO @RelationId((it: OrganizationTeam) => it.image) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - imageId?: IImageAsset['id']; + imageId?: ID; /* |-------------------------------------------------------------------------- @@ -261,6 +264,15 @@ export class OrganizationTeam extends TenantOrganizationBaseEntity implements IO @MultiORMOneToMany(() => IssueType, (it) => it.organizationTeam) issueTypes?: IIssueType[]; + /** + * Team daily plans + */ + @ApiPropertyOptional({ type: () => DailyPlan, isArray: true }) + @MultiORMOneToMany(() => DailyPlan, (dailyPlan) => dailyPlan.organizationTeam, { + cascade: true + }) + dailyPlans?: IDailyPlan[]; + /* |-------------------------------------------------------------------------- | @ManyToMany diff --git a/packages/core/src/tasks/daily-plan/daily-plan.controller.ts b/packages/core/src/tasks/daily-plan/daily-plan.controller.ts index 4a0b22e27df..86abd69bd4f 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.controller.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.controller.ts @@ -75,8 +75,9 @@ export class DailyPlanController extends CrudController { status: HttpStatus.NOT_FOUND, description: 'No Record found' }) - @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.DAILY_PLAN_READ) @Get('team') + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.DAILY_PLAN_READ) + @UseValidationPipe() async getTeamDailyPlans(@Query() params: PaginationParams): Promise> { return await this.dailyPlanService.getTeamDailyPlans(params); } diff --git a/packages/core/src/tasks/daily-plan/daily-plan.entity.ts b/packages/core/src/tasks/daily-plan/daily-plan.entity.ts index 8c65e8729fa..d14d575c6b1 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.entity.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.entity.ts @@ -3,7 +3,7 @@ import { JoinColumn, JoinTable, RelationId } from 'typeorm'; import { EntityRepositoryType } from '@mikro-orm/knex'; import { IsDate, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID } from 'class-validator'; import { Type } from 'class-transformer'; -import { DailyPlanStatusEnum, IDailyPlan, IEmployee, ITask } from '@gauzy/contracts'; +import { DailyPlanStatusEnum, ID, IDailyPlan, IEmployee, IOrganizationTeam, ITask } from '@gauzy/contracts'; import { ColumnIndex, MultiORMColumn, @@ -11,7 +11,7 @@ import { MultiORMManyToMany, MultiORMManyToOne } from '../../core/decorators/entity'; -import { Employee, Task, TenantOrganizationBaseEntity } from '../../core/entities/internal'; +import { Employee, OrganizationTeam, Task, TenantOrganizationBaseEntity } from '../../core/entities/internal'; import { MikroOrmDailyPlanRepository } from './repository'; @MultiORMEntity('daily_plan', { mikroOrmRepository: () => MikroOrmDailyPlanRepository }) @@ -61,7 +61,28 @@ export class DailyPlan extends TenantOrganizationBaseEntity implements IDailyPla @RelationId((it: DailyPlan) => it.employee) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - employeeId?: IEmployee['id']; + employeeId?: ID; + + /** + * OrganizationTeam + */ + @MultiORMManyToOne(() => OrganizationTeam, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + + /** Database cascade action on delete. */ + onDelete: 'CASCADE' + }) + @JoinColumn() + organizationTeam?: IOrganizationTeam; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @RelationId((it: DailyPlan) => it.organizationTeam) + @ColumnIndex() + @MultiORMColumn({ nullable: true, relationId: true }) + organizationTeamId?: ID; /* |-------------------------------------------------------------------------- diff --git a/packages/core/src/tasks/daily-plan/daily-plan.service.ts b/packages/core/src/tasks/daily-plan/daily-plan.service.ts index 04d86596622..58200794bfe 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.service.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.service.ts @@ -163,10 +163,10 @@ export class DailyPlanService extends TenantAwareCrudService { * @returns A promise that resolves to an object containing the list of daily plans and the total count. * @throws BadRequestException - If there's an error during the query. */ - async getTeamDailyPlans(options: PaginationParams): Promise> { + async getTeamDailyPlans(options: PaginationParams): Promise> { try { const { where } = options; - const { organizationId, teamIds } = where; + const { organizationId, organizationTeamId } = where; const tenantId = RequestContext.currentTenantId(); // Create the initial query @@ -175,7 +175,6 @@ export class DailyPlanService extends TenantAwareCrudService { // Join related entities query.leftJoinAndSelect(`${query.alias}.employee`, 'employee'); query.leftJoinAndSelect(`${query.alias}.tasks`, 'tasks'); - query.leftJoinAndSelect('employee.teams', 'teams'); // Apply optional find options if provided query.setFindOptions({ @@ -190,9 +189,9 @@ export class DailyPlanService extends TenantAwareCrudService { }); // Filter conditions - query.andWhere(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }); + query.where(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }); query.andWhere(p(`"${query.alias}"."organizationId" = :organizationId`), { organizationId }); - query.andWhere('teams."organizationTeamId" IN (:...teamIds)', { teamIds }); + query.andWhere(p(`"${query.alias}"."organizationTeamId" = :organizationTeamId`), { organizationTeamId }); // Retrieve results and total count const [items, total] = await query.getManyAndCount(); diff --git a/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts b/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts index 4ea9fb927fa..5bd5758b0ee 100644 --- a/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts +++ b/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts @@ -4,15 +4,18 @@ import { IsDate, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsUUID } from 'class- import { DailyPlanStatusEnum, IDailyPlanCreateInput, ITask } from '@gauzy/contracts'; import { TenantOrganizationBaseDTO } from '../../../core/dto'; import { EmployeeFeatureDTO } from '../../../employee/dto'; +import { OrganizationTeamFeatureDTO } from '../../../organization-team/dto'; /** * Create Daily Plan DTO validation */ -export class CreateDailyPlanDTO extends IntersectionType( - TenantOrganizationBaseDTO, - EmployeeFeatureDTO -) implements IDailyPlanCreateInput { - +export class CreateDailyPlanDTO + extends IntersectionType( + TenantOrganizationBaseDTO, + IntersectionType(EmployeeFeatureDTO, OrganizationTeamFeatureDTO) + ) + implements IDailyPlanCreateInput +{ @ApiProperty({ type: () => Date }) @Type(() => Date) @IsNotEmpty() diff --git a/packages/core/src/tasks/daily-plan/dto/update-daily-plan.dto.ts b/packages/core/src/tasks/daily-plan/dto/update-daily-plan.dto.ts index c8460b014b4..1a04eac7e9f 100644 --- a/packages/core/src/tasks/daily-plan/dto/update-daily-plan.dto.ts +++ b/packages/core/src/tasks/daily-plan/dto/update-daily-plan.dto.ts @@ -10,6 +10,8 @@ import { CreateDailyPlanDTO } from './create-daily-plan.dto'; export class UpdateDailyPlanDTO extends IntersectionType( TenantOrganizationBaseDTO, - PartialType(PickType(CreateDailyPlanDTO, ['date', 'workTimePlanned', 'status', 'employeeId'])) + PartialType( + PickType(CreateDailyPlanDTO, ['date', 'workTimePlanned', 'status', 'employeeId', 'organizationTeamId']) + ) ) implements IDailyPlanUpdateInput {} From 1b3f2c44f8eb9f320da1a47fdf5d0d30e2db970b Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 19 Aug 2024 17:40:59 +0200 Subject: [PATCH 5/8] fix: import build issue --- packages/contracts/src/daily-plan.model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/daily-plan.model.ts b/packages/contracts/src/daily-plan.model.ts index 33ec48665e1..64b1275583e 100644 --- a/packages/contracts/src/daily-plan.model.ts +++ b/packages/contracts/src/daily-plan.model.ts @@ -1,4 +1,4 @@ -import { IRelationalOrganizationTeam } from 'organization-team.model'; +import { IRelationalOrganizationTeam } from './organization-team.model'; import { IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel, ID } from './base-entity.model'; import { IRelationalEmployee } from './employee.model'; import { ITask } from './task.model'; From 8ef9cc2a808e7da0c043e2414336cd6321e4359d Mon Sep 17 00:00:00 2001 From: "Rahul R." Date: Tue, 20 Aug 2024 14:32:36 +0530 Subject: [PATCH 6/8] Update 1724062299873-AlterDailyPlanAddTeamRelation.ts --- .../migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts b/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts index 060ed6ea5a7..5dd891c5fc4 100644 --- a/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts +++ b/packages/core/src/database/migrations/1724062299873-AlterDailyPlanAddTeamRelation.ts @@ -183,7 +183,7 @@ export class AlterDailyPlanAddTeamRelation1724062299873 implements MigrationInte * @param queryRunner */ public async mysqlUpQueryRunner(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`daily_plan\` ADD \`organizationTeamId\` CHAR(36)`); + await queryRunner.query(`ALTER TABLE \`daily_plan\` ADD \`organizationTeamId\` varchar(255) NULL`); await queryRunner.query( `CREATE INDEX \`IDX_b022c2b684c35dcc63c22850f1\` ON \`daily_plan\` (\`organizationTeamId\`)` ); From c627a22743f54f231fd2230c03b4284ebe49c89d Mon Sep 17 00:00:00 2001 From: "Rahul R." Date: Tue, 20 Aug 2024 14:42:30 +0530 Subject: [PATCH 7/8] fix: organization team feature DTO --- .../src/employee/dto/employee-feature.dto.ts | 31 +++++++++---------- .../dto/organization-team-feature.dto.ts | 2 +- .../organization-team.entity.ts | 1 - .../tasks/daily-plan/daily-plan.service.ts | 24 +++++++------- .../daily-plan/dto/create-daily-plan.dto.ts | 9 ++---- 5 files changed, 31 insertions(+), 36 deletions(-) diff --git a/packages/core/src/employee/dto/employee-feature.dto.ts b/packages/core/src/employee/dto/employee-feature.dto.ts index da940ba67d6..25849161f14 100644 --- a/packages/core/src/employee/dto/employee-feature.dto.ts +++ b/packages/core/src/employee/dto/employee-feature.dto.ts @@ -1,20 +1,19 @@ -import { ApiPropertyOptional } from "@nestjs/swagger"; -import { IsObject, IsString, ValidateIf } from "class-validator"; -import { IEmployee, IRelationalEmployee } from "@gauzy/contracts"; -import { Employee } from "./../employee.entity"; -import { IsEmployeeBelongsToOrganization } from "./../../shared/validators"; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsObject, IsString, ValidateIf } from 'class-validator'; +import { ID, IEmployee, IRelationalEmployee } from '@gauzy/contracts'; +import { Employee } from './../employee.entity'; +import { IsEmployeeBelongsToOrganization } from './../../shared/validators'; export class EmployeeFeatureDTO implements IRelationalEmployee { + @ApiPropertyOptional({ type: () => Employee }) + @ValidateIf((it) => !it.employeeId || it.employee) + @IsObject() + @IsEmployeeBelongsToOrganization() + readonly employee: IEmployee; - @ApiPropertyOptional({ type: () => Employee }) - @ValidateIf((it) => !it.employeeId || it.employee) - @IsObject() - @IsEmployeeBelongsToOrganization() - readonly employee: IEmployee; - - @ApiPropertyOptional({ type: () => String }) - @ValidateIf((it) => !it.employee || it.employeeId) - @IsString() - @IsEmployeeBelongsToOrganization() - readonly employeeId: IEmployee['id']; + @ApiPropertyOptional({ type: () => String }) + @ValidateIf((it) => !it.employee || it.employeeId) + @IsString() + @IsEmployeeBelongsToOrganization() + readonly employeeId: ID; } diff --git a/packages/core/src/organization-team/dto/organization-team-feature.dto.ts b/packages/core/src/organization-team/dto/organization-team-feature.dto.ts index e48b7a7676f..cf543072c8f 100644 --- a/packages/core/src/organization-team/dto/organization-team-feature.dto.ts +++ b/packages/core/src/organization-team/dto/organization-team-feature.dto.ts @@ -10,7 +10,7 @@ export class OrganizationTeamFeatureDTO implements IRelationalOrganizationTeam { readonly organizationTeam: IOrganizationTeam; @ApiPropertyOptional({ type: () => String }) - @ValidateIf((it) => !it.employee || it.employeeId) + @ValidateIf((it) => !it.organizationTeam || it.organizationTeamId) @IsUUID() readonly organizationTeamId: ID; } diff --git a/packages/core/src/organization-team/organization-team.entity.ts b/packages/core/src/organization-team/organization-team.entity.ts index 98cc9347aa1..689fab8134e 100644 --- a/packages/core/src/organization-team/organization-team.entity.ts +++ b/packages/core/src/organization-team/organization-team.entity.ts @@ -267,7 +267,6 @@ export class OrganizationTeam extends TenantOrganizationBaseEntity implements IO /** * Team daily plans */ - @ApiPropertyOptional({ type: () => DailyPlan, isArray: true }) @MultiORMOneToMany(() => DailyPlan, (dailyPlan) => dailyPlan.organizationTeam, { cascade: true }) diff --git a/packages/core/src/tasks/daily-plan/daily-plan.service.ts b/packages/core/src/tasks/daily-plan/daily-plan.service.ts index 58200794bfe..5fabc553623 100644 --- a/packages/core/src/tasks/daily-plan/daily-plan.service.ts +++ b/packages/core/src/tasks/daily-plan/daily-plan.service.ts @@ -165,7 +165,8 @@ export class DailyPlanService extends TenantAwareCrudService { */ async getTeamDailyPlans(options: PaginationParams): Promise> { try { - const { where } = options; + // Apply optional find options if provided + const { where, relations = [] } = options || {}; const { organizationId, organizationTeamId } = where; const tenantId = RequestContext.currentTenantId(); @@ -176,30 +177,29 @@ export class DailyPlanService extends TenantAwareCrudService { query.leftJoinAndSelect(`${query.alias}.employee`, 'employee'); query.leftJoinAndSelect(`${query.alias}.tasks`, 'tasks'); - // Apply optional find options if provided query.setFindOptions({ - ...(isNotEmpty(options) && - isNotEmpty(options.where) && { - where: options.where - }), - ...(isNotEmpty(options) && - isNotEmpty(options.relations) && { - relations: options.relations - }) + where: isNotEmpty(where) && where, + relations: isNotEmpty(relations) && relations }); // Filter conditions query.where(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }); query.andWhere(p(`"${query.alias}"."organizationId" = :organizationId`), { organizationId }); - query.andWhere(p(`"${query.alias}"."organizationTeamId" = :organizationTeamId`), { organizationTeamId }); + + if (organizationTeamId) { + query.andWhere(p(`"${query.alias}"."organizationTeamId" = :organizationTeamId`), { + organizationTeamId + }); + } // Retrieve results and total count const [items, total] = await query.getManyAndCount(); + // Return the pagination result return { items, total }; } catch (error) { console.log('Error while fetching daily plans for team'); - throw new HttpException(`Failed to fetch daily plans for team : ${error.message}`, HttpStatus.BAD_REQUEST); + throw new HttpException(`Failed to fetch daily plans for team: ${error.message}`, HttpStatus.BAD_REQUEST); } } diff --git a/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts b/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts index 5bd5758b0ee..716908576a7 100644 --- a/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts +++ b/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty, ApiPropertyOptional, IntersectionType } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsDate, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsUUID } from 'class-validator'; -import { DailyPlanStatusEnum, IDailyPlanCreateInput, ITask } from '@gauzy/contracts'; +import { DailyPlanStatusEnum, ID, IDailyPlanCreateInput, ITask } from '@gauzy/contracts'; import { TenantOrganizationBaseDTO } from '../../../core/dto'; import { EmployeeFeatureDTO } from '../../../employee/dto'; import { OrganizationTeamFeatureDTO } from '../../../organization-team/dto'; @@ -10,10 +10,7 @@ import { OrganizationTeamFeatureDTO } from '../../../organization-team/dto'; * Create Daily Plan DTO validation */ export class CreateDailyPlanDTO - extends IntersectionType( - TenantOrganizationBaseDTO, - IntersectionType(EmployeeFeatureDTO, OrganizationTeamFeatureDTO) - ) + extends IntersectionType(TenantOrganizationBaseDTO, EmployeeFeatureDTO, OrganizationTeamFeatureDTO) implements IDailyPlanCreateInput { @ApiProperty({ type: () => Date }) @@ -35,5 +32,5 @@ export class CreateDailyPlanDTO @ApiPropertyOptional({ type: () => String }) @IsOptional() @IsUUID() - readonly taskId?: ITask['id']; + readonly taskId?: ID; } From 1b3f72ae9bdc184dcc0992c6a3547293484bcf39 Mon Sep 17 00:00:00 2001 From: "Rahul R." Date: Tue, 20 Aug 2024 14:45:04 +0530 Subject: [PATCH 8/8] fix(deepscan): removed unused import --- packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts b/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts index 716908576a7..55104387f12 100644 --- a/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts +++ b/packages/core/src/tasks/daily-plan/dto/create-daily-plan.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty, ApiPropertyOptional, IntersectionType } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsDate, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsUUID } from 'class-validator'; -import { DailyPlanStatusEnum, ID, IDailyPlanCreateInput, ITask } from '@gauzy/contracts'; +import { DailyPlanStatusEnum, ID, IDailyPlanCreateInput } from '@gauzy/contracts'; import { TenantOrganizationBaseDTO } from '../../../core/dto'; import { EmployeeFeatureDTO } from '../../../employee/dto'; import { OrganizationTeamFeatureDTO } from '../../../organization-team/dto';