diff --git a/.github/workflows/desktop-timer-app-stage.yml b/.github/workflows/desktop-timer-app-stage.yml index 1f9c5218d28..2ce59c1c8a7 100644 --- a/.github/workflows/desktop-timer-app-stage.yml +++ b/.github/workflows/desktop-timer-app-stage.yml @@ -216,6 +216,10 @@ jobs: ChocolateyInstall: '' ChromeWebDriver: '' COBERTURA_HOME: '' + COMPUTERNAME: '' + COMSPEC: '' + CONDA: '' + DEPLOYMENT_BASEPATH: '' SBT_HOME: '' SELENIUM_JAR_PATH: '' STATS_BLT: '' @@ -236,10 +240,49 @@ jobs: ANDROID_NDK_LATEST_HOME: '' ANDROID_NDK_ROOT: '' ANDROID_SDK_ROOT: '' + GITHUB_ACTION: '' + GITHUB_ACTIONS: '' + GITHUB_ACTION_REF: '' + GITHUB_ACTION_REPOSITORY: '' + GITHUB_ACTOR: '' + GITHUB_ACTOR_ID: '' + GITHUB_API_URL: '' + GITHUB_BASE_REF: '' + GITHUB_ENV: '' + GITHUB_EVENT_NAME: '' + GITHUB_EVENT_PATH: '' + GITHUB_GRAPHQL_URL: '' + GITHUB_HEAD_REF: '' + GITHUB_JOB: '' + GITHUB_OUTPUT: '' + GITHUB_PATH: '' + GITHUB_REF: '' + GITHUB_REF_NAME: '' + GITHUB_REF_PROTECTED: '' + GITHUB_REF_TYPE: '' + GITHUB_REPOSITORY: '' + GITHUB_REPOSITORY_ID: '' + GITHUB_REPOSITORY_OWNER: '' + GITHUB_REPOSITORY_OWNER_ID: '' + GITHUB_RETENTION_DAYS: '' + GITHUB_RUN_ATTEMPT: '' + GITHUB_RUN_ID: '' + GITHUB_RUN_NUMBER: '' + GITHUB_SERVER_URL: '' + GITHUB_SHA: '' + GITHUB_STATE: '' + GITHUB_STEP_SUMMARY: '' + GITHUB_TRIGGERING_ACTOR: '' + GITHUB_WORKFLOW: '' + GITHUB_WORKFLOW_REF: '' + GITHUB_WORKFLOW_SHA: '' + GITHUB_WORKSPACE: '' GOROOT_1_20_X64: '' GOROOT_1_21_X64: '' GOROOT_1_22_X64: '' GRADLE_HOME: '' + HOMEDRIVE: '' + HOMEPATH: '' IEWebDriver: '' ImageOS: '' ImageVersion: '' @@ -248,10 +291,17 @@ jobs: JAVA_HOME_17_X64: '' JAVA_HOME_21_X64: '' JAVA_HOME_8_X64: '' + LOCALAPPDATA: '' + LOGONSERVER: '' M2: '' M2_REPO: '' MAVEN_OPTS: '' MonAgentClientLocation: '' + npm_config_prefix: '' + NUMBER_OF_PROCESSORS: '' + OS: '' + PATHEXT: '' + PERFLOG_LOCATION_SETTING: '' PGBIN: '' PGDATA: '' PGPASSWORD: '' @@ -277,6 +327,7 @@ jobs: GeckoWebDriver: '' GHCUP_INSTALL_BASE_PREFIX: '' GHCUP_MSYS2: '' + RTOOLS44_HOME: '' RUNNER_ARCH: '' RUNNER_ENVIRONMENT: '' RUNNER_NAME: '' @@ -286,8 +337,20 @@ jobs: RUNNER_TOOL_CACHE: '' RUNNER_TRACKING_ID: '' RUNNER_WORKSPACE: '' + USERDOMAIN: '' + USERDOMAIN_ROAMINGPROFILE: '' + USERNAME: '' + USERPROFILE: '' + VCPKG_INSTALLATION_ROOT: '' + WIX: '' + TERM: '' + # HOME: '' + # WINDIR: '' + # ProgramData: '' # PROGRAMFILES: '' # ProgramW6432: '' # ALLUSERSPROFILE: '' # APPDATA: '' # COMMONPROGRAMFILES: '' + # CommonProgramFiles(x86) + # CommonProgramW6432 diff --git a/packages/contracts/src/employee.model.ts b/packages/contracts/src/employee.model.ts index 671bf33ebe0..b7760cca8b3 100644 --- a/packages/contracts/src/employee.model.ts +++ b/packages/contracts/src/employee.model.ts @@ -22,6 +22,7 @@ import { ITimesheet, ITimeSlot } from './timesheet.model'; import { ITask } from './task.model'; import { ICandidate } from './candidate.model'; import { IEmployeeAward } from './employee-award.model'; +import { IOrganizationProjectModule } from './organization-project-module.model'; export interface IFindMembersInput extends IBasePerTenantAndOrganizationEntityModel { organizationTeamId: ID; @@ -71,6 +72,7 @@ export interface IEmployee extends IBasePerTenantAndOrganizationEntityModel { expenses?: IExpense[]; timesheets?: ITimesheet[]; tasks?: ITask[]; + modules?: IOrganizationProjectModule[]; timeSlots?: ITimeSlot[]; contact?: IContact; candidate?: ICandidate; diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 2195a86393b..ccd5273a99d 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -79,6 +79,7 @@ export * from './organization-expense-category.model'; export * from './organization-language.model'; export * from './organization-positions.model'; export * from './organization-projects.model'; +export * from './organization-project-module.model'; export * from './organization-recurring-expense.model'; export * from './organization-sprint.model'; export * from './organization-task-setting.model'; diff --git a/packages/contracts/src/organization-project-module.model.ts b/packages/contracts/src/organization-project-module.model.ts new file mode 100644 index 00000000000..9a4fe67be0f --- /dev/null +++ b/packages/contracts/src/organization-project-module.model.ts @@ -0,0 +1,47 @@ +import { IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; +import { IEmployee } from './employee.model'; +import { IRelationalOrganizationProject } from './organization-projects.model'; +import { IOrganizationSprint } from './organization-sprint.model'; +import { IOrganizationTeam } from './organization-team.model'; +import { TaskStatusEnum } from './task-status.model'; +import { ITask } from './task.model'; +import { IUser } from './user.model'; + +export interface IRelationalOrganizationProjectModule { + projectModule?: IOrganizationProjectModule; + projectModuleId?: ID; +} + +export interface IOrganizationProjectModule + extends IBasePerTenantAndOrganizationEntityModel, + IRelationalOrganizationProject { + name: string; + description?: string; + status?: TaskStatusEnum; + startDate?: Date; + endDate?: Date; + isFavorite?: boolean; + public?: boolean; + parent?: IOrganizationProjectModule; + parentId?: ID; // Optional field for specifying the parent module ID + children?: IOrganizationProjectModule[]; // Modules related as children + manager?: IUser; + managerId?: ID; + creator?: IUser; + creatorId?: ID; + members?: IEmployee[]; + organizationSprints?: IOrganizationSprint[]; + teams?: IOrganizationTeam[]; + tasks?: ITask[]; +} + +export interface IOrganizationProjectModuleFindInput + extends IBasePerTenantAndOrganizationEntityModel, + Partial> { + organizationTeamId?: ID; + organizationSprintId?: ID; +} + +export interface IOrganizationProjectModuleCreateInput extends Omit {} + +export interface IOrganizationProjectModuleUpdateInput extends Partial {} diff --git a/packages/contracts/src/organization-projects.model.ts b/packages/contracts/src/organization-projects.model.ts index d1bf27454fb..131f01639d3 100644 --- a/packages/contracts/src/organization-projects.model.ts +++ b/packages/contracts/src/organization-projects.model.ts @@ -11,6 +11,7 @@ import { ITimeLog } from './timesheet.model'; import { IRelationalImageAsset } from './image-asset.model'; import { IOrganizationTeam } from './organization-team.model'; import { CustomFieldsObject } from './shared-types'; +import { IOrganizationProjectModule } from './organization-project-module.model'; export interface IRelationalOrganizationProject { project?: IOrganizationProject; @@ -41,6 +42,7 @@ export interface IOrganizationProject teams?: IOrganizationTeam[]; timeLogs?: ITimeLog[]; organizationSprints?: IOrganizationSprint[]; + modules?: IOrganizationProjectModule[]; taskListType: TaskListTypeEnum; payments?: IPayment[]; // prefix to project tasks / issues, e.g. GA-XXXX (GA is prefix) @@ -73,8 +75,8 @@ export enum OrganizationProjectBudgetTypeEnum { export interface IOrganizationProjectsFindInput extends IBasePerTenantAndOrganizationEntityModel { name?: string; - organizationTeamId?: IOrganizationTeam['id']; - organizationContactId?: IOrganizationContact['id']; + organizationTeamId?: ID; + organizationContactId?: ID; organizationContact?: IOrganizationContact; public?: boolean; billable?: boolean; @@ -86,7 +88,7 @@ export interface IOrganizationProjectCreateInput IRelationalImageAsset { name?: string; organizationContact?: IOrganizationContact; - organizationContactId?: IOrganizationContact['id']; + organizationContactId?: ID; startDate?: Date; endDate?: Date; billing?: ProjectBillingEnum; @@ -108,7 +110,7 @@ export interface IOrganizationProjectCreateInput } export interface IOrganizationProjectUpdateInput extends IOrganizationProjectCreateInput, IOrganizationProjectSetting { - id?: IOrganizationContact['id']; + id?: ID; } export interface IOrganizationProjectStoreState { diff --git a/packages/contracts/src/organization-sprint.model.ts b/packages/contracts/src/organization-sprint.model.ts index 329340f38be..083147fbfcc 100644 --- a/packages/contracts/src/organization-sprint.model.ts +++ b/packages/contracts/src/organization-sprint.model.ts @@ -1,9 +1,9 @@ +import { IOrganizationProjectModule } from './organization-project-module.model'; import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model'; import { IOrganizationProject } from './organization-projects.model'; import { ITask } from './task.model'; -export interface IOrganizationSprint - extends IBasePerTenantAndOrganizationEntityModel { +export interface IOrganizationSprint extends IBasePerTenantAndOrganizationEntityModel { name: string; projectId: string; goal?: string; @@ -13,6 +13,7 @@ export interface IOrganizationSprint dayStart?: number; // Enum ((Sunday-Saturday) => (0-7)) project?: IOrganizationProject; tasks?: ITask[]; + modules?: IOrganizationProjectModule[]; } export enum SprintStartDayEnum { diff --git a/packages/contracts/src/organization-team.model.ts b/packages/contracts/src/organization-team.model.ts index d4659236e71..bd636c9d7b1 100644 --- a/packages/contracts/src/organization-team.model.ts +++ b/packages/contracts/src/organization-team.model.ts @@ -1,5 +1,5 @@ import { IRelationalEmployee } from './employee.model'; -import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model'; +import { IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; import { IOrganizationTeamEmployee } from './organization-team-employee-model'; import { ITag } from './tag.model'; import { ITask } from './task.model'; @@ -7,6 +7,7 @@ import { ITimerStatusInput } from './timesheet.model'; import { IRelationalImageAsset } from './image-asset.model'; import { CrudActionEnum } from './organization.model'; import { IOrganizationProject } from './organization-projects.model'; +import { IOrganizationProjectModule } from './organization-project-module.model'; export interface IOrganizationTeam extends IBasePerTenantAndOrganizationEntityModel, IRelationalImageAsset { name: string; @@ -16,12 +17,13 @@ export interface IOrganizationTeam extends IBasePerTenantAndOrganizationEntityMo logo?: string; prefix?: string; shareProfileView?: boolean; // If true, all members can view "Worked" tasks and "Daily Plan" tabs of all other employees, By default, it's true - requirePlanToTrack?: boolean; // If true, members can't be able to track time without have a "Daily Plan". By defaut, it's false + requirePlanToTrack?: boolean; // If true, members can't be able to track time without have a "Daily Plan". By default, it's false public?: boolean; profile_link?: string; members?: IOrganizationTeamEmployee[]; managers?: IOrganizationTeamEmployee[]; projects?: IOrganizationProject[]; + modules?: IOrganizationProjectModule[]; tags?: ITag[]; tasks?: ITask[]; } @@ -64,7 +66,7 @@ export interface IOrganizationTeamStatisticInput extends ITimerStatusInput { export interface IRelationalOrganizationTeam { organizationTeam?: IOrganizationTeam; - organizationTeamId?: IOrganizationTeam['id']; + organizationTeamId?: ID; } export interface IOrganizationTeamStoreState { diff --git a/packages/contracts/src/role-permission.model.ts b/packages/contracts/src/role-permission.model.ts index 601d7747a2c..864bb9ac852 100644 --- a/packages/contracts/src/role-permission.model.ts +++ b/packages/contracts/src/role-permission.model.ts @@ -182,7 +182,13 @@ export enum PermissionsEnum { DAILY_PLAN_CREATE = 'DAILY_PLAN_CREATE', DAILY_PLAN_READ = 'DAILY_PLAN_READ', DAILY_PLAN_UPDATE = 'DAILY_PLAN_UPDATE', - DAILY_PLAN_DELETE = 'DAILY_PLAN_DELETE' + DAILY_PLAN_DELETE = 'DAILY_PLAN_DELETE', + + /** Project Module */ + PROJECT_MODULE_CREATE = 'PROJECT_MODULE_CREATE', + PROJECT_MODULE_READ = 'PROJECT_MODULE_READ', + PROJECT_MODULE_UPDATE = 'PROJECT_MODULE_UPDATE', + PROJECT_MODULE_DELETE = 'PROJECT_MODULE_DELETE' } export const PermissionGroups = { @@ -288,6 +294,13 @@ export const PermissionGroups = { PermissionsEnum.DAILY_PLAN_DELETE, /** Daily Plan Permissions End */ + /** Project Module Permissions start */ + PermissionsEnum.PROJECT_MODULE_CREATE, + PermissionsEnum.PROJECT_MODULE_READ, + PermissionsEnum.PROJECT_MODULE_UPDATE, + PermissionsEnum.PROJECT_MODULE_DELETE, + /** Project Module Permissions start */ + /** Organization Team Permissions Start */ PermissionsEnum.ORG_TEAM_ADD, PermissionsEnum.ORG_TEAM_VIEW, diff --git a/packages/contracts/src/task.model.ts b/packages/contracts/src/task.model.ts index 33e0235f02c..99a7e63edcd 100644 --- a/packages/contracts/src/task.model.ts +++ b/packages/contracts/src/task.model.ts @@ -1,10 +1,7 @@ -import { - IBasePerTenantAndOrganizationEntityModel, - IBaseRelationsEntityModel, -} from './base-entity.model'; +import { IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel, ID } from './base-entity.model'; import { IEmployee } from './employee.model'; import { IInvoiceItem } from './invoice-item.model'; -import { IOrganizationProject } from './organization-projects.model'; +import { IRelationalOrganizationProject } from './organization-projects.model'; import { IOrganizationSprint } from './organization-sprint.model'; import { IOrganizationTeam } from './organization-team.model'; import { ITag } from './tag.model'; @@ -12,8 +9,12 @@ import { IUser } from './user.model'; import { ITaskStatus, TaskStatusEnum } from './task-status.model'; import { ITaskPriority, TaskPriorityEnum } from './task-priority.model'; import { ITaskSize, TaskSizeEnum } from './task-size.model'; +import { IRelationalOrganizationProjectModule } from './organization-project-module.model'; -export interface ITask extends IBasePerTenantAndOrganizationEntityModel { +export interface ITask + extends IBasePerTenantAndOrganizationEntityModel, + IRelationalOrganizationProject, + IRelationalOrganizationProjectModule { title: string; number?: number; public?: boolean; @@ -24,34 +25,31 @@ export interface ITask extends IBasePerTenantAndOrganizationEntityModel { size?: TaskSizeEnum; dueDate?: Date; estimate?: number; - project?: IOrganizationProject; - projectId?: IOrganizationProject['id']; tags?: ITag[]; members?: IEmployee[]; invoiceItems?: IInvoiceItem[]; teams?: IOrganizationTeam[]; organizationSprint?: IOrganizationSprint; - organizationSprintId?: IOrganizationSprint['id']; + organizationSprintId?: ID; creator?: IUser; - creatorId?: IUser['id']; + creatorId?: ID; parent?: ITask; - parentId?: ITask['id']; // Optional field for specifying the parent task ID + parentId?: ID; // Optional field for specifying the parent task ID children?: ITask[]; taskStatus?: ITaskStatus; taskSize?: ITaskSize; taskPriority?: ITaskPriority; - taskStatusId?: ITaskStatus['id']; - taskSizeId?: ITaskSize['id']; - taskPriorityId?: ITaskPriority['id']; + taskStatusId?: ID; + taskSizeId?: ID; + taskPriorityId?: ID; rootEpic?: ITask; } -export interface IGetTaskOptions - extends IBasePerTenantAndOrganizationEntityModel { - projectId?: IOrganizationProject['id']; +export interface IGetTaskOptions extends IBasePerTenantAndOrganizationEntityModel { + projectId?: ID; } export interface IGetTaskByEmployeeOptions extends IBaseRelationsEntityModel { @@ -62,7 +60,7 @@ export type IGetSprintsOptions = IGetTaskOptions; export enum TaskParticipantEnum { EMPLOYEES = 'employees', - TEAMS = 'teams', + TEAMS = 'teams' } export type ITaskCreateInput = ITask; diff --git a/packages/contracts/src/timesheet.model.ts b/packages/contracts/src/timesheet.model.ts index dec76948219..5fda6298fda 100644 --- a/packages/contracts/src/timesheet.model.ts +++ b/packages/contracts/src/timesheet.model.ts @@ -1,4 +1,4 @@ -import { IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel } from './base-entity.model'; +import { IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel, ID } from './base-entity.model'; import { IOrganizationContact, OrganizationContactBudgetTypeEnum } from './organization-contact.model'; import { IOrganizationProject, @@ -17,9 +17,9 @@ import { TimeFormatEnum } from './organization.model'; export interface ITimesheet extends IBasePerTenantAndOrganizationEntityModel { employee: IEmployee; - employeeId?: IEmployee['id']; + employeeId?: ID; approvedBy?: IUser; - approvedById?: IUser['id']; + approvedById?: ID; timeLogs?: ITimeLog[]; duration?: number; keyboard?: number; @@ -100,16 +100,16 @@ export interface ITimeLog IRelationalOrganizationProject, IRelationalOrganizationTeam { employee: IEmployee; - employeeId: IEmployee['id']; + employeeId: ID; timesheet?: ITimesheet; - timesheetId?: ITimesheet['id']; + timesheetId?: ID; task?: ITask; - taskId?: ITask['id']; + taskId?: ID; timeSlots?: ITimeSlot[]; project?: IOrganizationProject; - projectId?: IOrganizationProject['id']; + projectId?: ID; organizationContact?: IOrganizationContact; - organizationContactId?: IOrganizationContact['id']; + organizationContactId?: ID; source?: TimeLogSourceEnum; startedAt?: Date; stoppedAt?: Date; diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 211f0cf3b24..5ac5c890157 100644 --- a/packages/core/src/app.module.ts +++ b/packages/core/src/app.module.ts @@ -141,6 +141,7 @@ import { OrganizationTaskSettingModule } from './organization-task-setting/organ import { TaskEstimationModule } from './tasks/estimation/task-estimation.module'; import { DailyPlanModule } from './tasks/daily-plan/daily-plan.module'; import { SocialAccountModule } from './auth/social-account/social-account.module'; +import { OrganizationProjectModuleModule } from './organization-project-module/organization-project-module.module'; const { unleashConfig } = environment; @@ -347,6 +348,7 @@ if (environment.THROTTLE_ENABLED) { OrganizationContactModule, OrganizationPositionModule, OrganizationProjectModule, + OrganizationProjectModuleModule, OrganizationVendorModule, OrganizationAwardModule, OrganizationLanguageModule, diff --git a/packages/core/src/core/crud/tenant-aware-crud.service.ts b/packages/core/src/core/crud/tenant-aware-crud.service.ts index 63776ee727b..1a368de460f 100644 --- a/packages/core/src/core/crud/tenant-aware-crud.service.ts +++ b/packages/core/src/core/crud/tenant-aware-crud.service.ts @@ -14,8 +14,10 @@ import { ITryRequest } from './try-request'; * This abstract class adds tenantId to all query filters if a user is available in the current RequestContext * If a user is not available in RequestContext, then it behaves exactly the same as CrudService */ -export abstract class TenantAwareCrudService extends CrudService implements ICrudService { - +export abstract class TenantAwareCrudService + extends CrudService + implements ICrudService +{ constructor(typeOrmRepository: Repository, mikroOrmRepository: MikroOrmBaseEntityRepository) { super(typeOrmRepository, mikroOrmRepository); } @@ -35,13 +37,13 @@ export abstract class TenantAwareCrudService extends ( isNotEmpty(employeeId) ? !RequestContext.hasPermission(PermissionsEnum.CHANGE_SELECTED_EMPLOYEE) && - this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('employeeId') + this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('employeeId') ? { - employee: { - id: employeeId - }, - employeeId: employeeId - } + employee: { + id: employeeId + }, + employeeId: employeeId + } : {} : {} ) as FindOptionsWhere @@ -58,11 +60,11 @@ export abstract class TenantAwareCrudService extends return { ...(this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('tenantId') ? { - tenant: { - id: user.tenantId - }, - tenantId: user.tenantId - } + tenant: { + id: user.tenantId + }, + tenantId: user.tenantId + } : {}), ...this.findConditionsWithEmployeeByUser() } as FindOptionsWhere; @@ -92,12 +94,12 @@ export abstract class TenantAwareCrudService extends return ( where ? { - ...where, - ...this.findConditionsWithTenantByUser(user) - } + ...where, + ...this.findConditionsWithTenantByUser(user) + } : { - ...this.findConditionsWithTenantByUser(user) - } + ...this.findConditionsWithTenantByUser(user) + } ) as FindOptionsWhere; } @@ -329,24 +331,24 @@ export abstract class TenantAwareCrudService extends ...entity, ...(this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('tenantId') ? { - tenant: { - id: tenantId - }, - tenantId - } + tenant: { + id: tenantId + }, + tenantId + } : {}), /** * If employee has login & create data for self */ ...(isNotEmpty(employeeId) ? !RequestContext.hasPermission(PermissionsEnum.CHANGE_SELECTED_EMPLOYEE) && - this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('employeeId') + this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('employeeId') ? { - employee: { - id: employeeId - }, - employeeId: employeeId - } + employee: { + id: employeeId + }, + employeeId: employeeId + } : {} : {}) }); @@ -365,11 +367,11 @@ export abstract class TenantAwareCrudService extends ...entity, ...(this.typeOrmRepository.metadata?.hasColumnWithPropertyPath('tenantId') ? { - tenant: { - id: tenantId - }, - tenantId - } + tenant: { + id: tenantId + }, + tenantId + } : {}) }); } @@ -400,13 +402,11 @@ export abstract class TenantAwareCrudService extends * @param options - Additional options for querying, such as extra conditions or query parameters. * @returns {Promise} - The result of the delete operation. */ - public async delete( - criteria: string | FindOptionsWhere, - options?: FindOneOptions - ): Promise { + public async delete(criteria: string | FindOptionsWhere, options?: FindOneOptions): Promise { try { // Merge additional where conditions from options into criteria if needed - let where: FindOptionsWhere = typeof criteria === 'string' ? { id: criteria } as FindOptionsWhere : { ...criteria }; + let where: FindOptionsWhere = + typeof criteria === 'string' ? ({ id: criteria } as FindOptionsWhere) : { ...criteria }; if (options?.where) { where = { ...where, ...options.where }; diff --git a/packages/core/src/core/entities/index.ts b/packages/core/src/core/entities/index.ts index dfa37158aaa..dc83a1d0b40 100644 --- a/packages/core/src/core/entities/index.ts +++ b/packages/core/src/core/entities/index.ts @@ -77,6 +77,7 @@ import { OrganizationLanguage, OrganizationPosition, OrganizationProject, + OrganizationProjectModule, OrganizationRecurringExpense, OrganizationSprint, OrganizationTaskSetting, @@ -215,6 +216,7 @@ export const coreEntities = [ OrganizationLanguage, OrganizationPosition, OrganizationProject, + OrganizationProjectModule, OrganizationRecurringExpense, OrganizationSprint, OrganizationTaskSetting, diff --git a/packages/core/src/core/entities/internal.ts b/packages/core/src/core/entities/internal.ts index 17ac73e1fe4..467077d8fde 100644 --- a/packages/core/src/core/entities/internal.ts +++ b/packages/core/src/core/entities/internal.ts @@ -80,6 +80,7 @@ export * from '../../organization-employment-type/organization-employment-type.e export * from '../../organization-language/organization-language.entity'; export * from '../../organization-position/organization-position.entity'; export * from '../../organization-project/organization-project.entity'; +export * from '../../organization-project-module/organization-project-module.entity'; export * from '../../organization-recurring-expense/organization-recurring-expense.entity'; export * from '../../organization-sprint/organization-sprint.entity'; export * from '../../organization-task-setting/organization-task-setting.entity'; diff --git a/packages/core/src/database/migrations/1724929664505-CreateProjectModuleTable.ts b/packages/core/src/database/migrations/1724929664505-CreateProjectModuleTable.ts new file mode 100644 index 00000000000..d09ab31335f --- /dev/null +++ b/packages/core/src/database/migrations/1724929664505-CreateProjectModuleTable.ts @@ -0,0 +1,767 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { yellow } from 'chalk'; +import { DatabaseTypeEnum } from '@gauzy/config'; + +export class CreateProjectModuleTable1724929664505 implements MigrationInterface { + name = 'CreateProjectModuleTable1724929664505'; + + /** + * 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( + `CREATE TABLE "organization_project_module" ("deletedAt" TIMESTAMP, "id" uuid NOT NULL DEFAULT gen_random_uuid(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "isActive" boolean DEFAULT true, "isArchived" boolean DEFAULT false, "tenantId" uuid, "organizationId" uuid, "name" character varying NOT NULL, "description" text, "status" character varying, "startDate" TIMESTAMP, "endDate" TIMESTAMP, "public" boolean DEFAULT false, "isFavorite" boolean DEFAULT false, "parentId" uuid, "projectId" uuid, "creatorId" uuid, "managerId" uuid, CONSTRAINT "PK_61c6dccd818b5e91c438dcd9901" PRIMARY KEY ("id"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f33638d289aff2306328c32a8c" ON "organization_project_module" ("isActive") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_a56086e95fb2627ba2a3dd2eaa" ON "organization_project_module" ("isArchived") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8a7a4d4206c003c3827c5afe5d" ON "organization_project_module" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_cd928adcb5ebb00c9f2c57e390" ON "organization_project_module" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_86438fbaa1d857f32f66b24885" ON "organization_project_module" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7fd3c8f54c01943b283080aefa" ON "organization_project_module" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0" ON "organization_project_module" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e6b6555e5fc6c5120110a0195c" ON "organization_project_module" ("managerId") ` + ); + await queryRunner.query( + `CREATE TABLE "project_module_sprint" ("organizationProjectModuleId" uuid NOT NULL, "organizationSprintId" uuid NOT NULL, CONSTRAINT "PK_7e6929079783cb90588e0f93762" PRIMARY KEY ("organizationProjectModuleId", "organizationSprintId"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_c91ef400079e93fec908cf9384" ON "project_module_sprint" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7af935d75a7e21fd76f072fbc0" ON "project_module_sprint" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE TABLE "project_module_team" ("organizationProjectModuleId" uuid NOT NULL, "organizationTeamId" uuid NOT NULL, CONSTRAINT "PK_64723331f528c88f4037f0bf437" PRIMARY KEY ("organizationProjectModuleId", "organizationTeamId"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_42c46289259b3fcdf2dc61744a" ON "project_module_team" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_d14aeb1b3e08d80eb32dd05934" ON "project_module_team" ("organizationTeamId") ` + ); + await queryRunner.query( + `CREATE TABLE "project_module_employee" ("organizationProjectModuleId" uuid NOT NULL, "employeeId" uuid NOT NULL, CONSTRAINT "PK_809c3beb646a1666d4d8161b637" PRIMARY KEY ("organizationProjectModuleId", "employeeId"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e9fd7310fc93849b1d55e64d28" ON "project_module_employee" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_18e428e909e48a4b7df43d7e01" ON "project_module_employee" ("employeeId") ` + ); + await queryRunner.query(`ALTER TABLE "task" ADD "projectModuleId" uuid`); + await queryRunner.query(`CREATE INDEX "IDX_579534d8e12f22d308d6bd5f42" ON "task" ("projectModuleId") `); + await queryRunner.query( + `ALTER TABLE "organization_project_module" ADD CONSTRAINT "FK_8a7a4d4206c003c3827c5afe5dc" FOREIGN KEY ("tenantId") REFERENCES "tenant"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" ADD CONSTRAINT "FK_cd928adcb5ebb00c9f2c57e3908" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" ADD CONSTRAINT "FK_4bb6fbfa64cf5d5977c2e5346a9" FOREIGN KEY ("parentId") REFERENCES "organization_project_module"("id") ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" ADD CONSTRAINT "FK_7fd3c8f54c01943b283080aefa3" FOREIGN KEY ("projectId") REFERENCES "organization_project"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" ADD CONSTRAINT "FK_8f2054a6a2d4b9c17624b9c8a01" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" ADD CONSTRAINT "FK_e6b6555e5fc6c5120110a0195cd" FOREIGN KEY ("managerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "task" ADD CONSTRAINT "FK_579534d8e12f22d308d6bd5f428" FOREIGN KEY ("projectModuleId") REFERENCES "organization_project_module"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE "project_module_sprint" ADD CONSTRAINT "FK_c91ef400079e93fec908cf93845" FOREIGN KEY ("organizationProjectModuleId") REFERENCES "organization_project_module"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE "project_module_sprint" ADD CONSTRAINT "FK_7af935d75a7e21fd76f072fbc03" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE "project_module_team" ADD CONSTRAINT "FK_42c46289259b3fcdf2dc61744a7" FOREIGN KEY ("organizationProjectModuleId") REFERENCES "organization_project_module"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE "project_module_team" ADD CONSTRAINT "FK_d14aeb1b3e08d80eb32dd05934b" FOREIGN KEY ("organizationTeamId") REFERENCES "organization_team"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE "project_module_employee" ADD CONSTRAINT "FK_e9fd7310fc93849b1d55e64d280" FOREIGN KEY ("organizationProjectModuleId") REFERENCES "organization_project_module"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE "project_module_employee" ADD CONSTRAINT "FK_18e428e909e48a4b7df43d7e01e" FOREIGN KEY ("employeeId") REFERENCES "employee"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "project_module_employee" DROP CONSTRAINT "FK_18e428e909e48a4b7df43d7e01e"` + ); + await queryRunner.query( + `ALTER TABLE "project_module_employee" DROP CONSTRAINT "FK_e9fd7310fc93849b1d55e64d280"` + ); + await queryRunner.query(`ALTER TABLE "project_module_team" DROP CONSTRAINT "FK_d14aeb1b3e08d80eb32dd05934b"`); + await queryRunner.query(`ALTER TABLE "project_module_team" DROP CONSTRAINT "FK_42c46289259b3fcdf2dc61744a7"`); + await queryRunner.query(`ALTER TABLE "project_module_sprint" DROP CONSTRAINT "FK_7af935d75a7e21fd76f072fbc03"`); + await queryRunner.query(`ALTER TABLE "project_module_sprint" DROP CONSTRAINT "FK_c91ef400079e93fec908cf93845"`); + await queryRunner.query(`ALTER TABLE "task" DROP CONSTRAINT "FK_579534d8e12f22d308d6bd5f428"`); + await queryRunner.query( + `ALTER TABLE "organization_project_module" DROP CONSTRAINT "FK_e6b6555e5fc6c5120110a0195cd"` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" DROP CONSTRAINT "FK_8f2054a6a2d4b9c17624b9c8a01"` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" DROP CONSTRAINT "FK_7fd3c8f54c01943b283080aefa3"` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" DROP CONSTRAINT "FK_4bb6fbfa64cf5d5977c2e5346a9"` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" DROP CONSTRAINT "FK_cd928adcb5ebb00c9f2c57e3908"` + ); + await queryRunner.query( + `ALTER TABLE "organization_project_module" DROP CONSTRAINT "FK_8a7a4d4206c003c3827c5afe5dc"` + ); + await queryRunner.query(`DROP INDEX "public"."IDX_579534d8e12f22d308d6bd5f42"`); + await queryRunner.query(`ALTER TABLE "task" DROP COLUMN "projectModuleId"`); + await queryRunner.query(`DROP INDEX "public"."IDX_18e428e909e48a4b7df43d7e01"`); + await queryRunner.query(`DROP INDEX "public"."IDX_e9fd7310fc93849b1d55e64d28"`); + await queryRunner.query(`DROP TABLE "project_module_employee"`); + await queryRunner.query(`DROP INDEX "public"."IDX_d14aeb1b3e08d80eb32dd05934"`); + await queryRunner.query(`DROP INDEX "public"."IDX_42c46289259b3fcdf2dc61744a"`); + await queryRunner.query(`DROP TABLE "project_module_team"`); + await queryRunner.query(`DROP INDEX "public"."IDX_7af935d75a7e21fd76f072fbc0"`); + await queryRunner.query(`DROP INDEX "public"."IDX_c91ef400079e93fec908cf9384"`); + await queryRunner.query(`DROP TABLE "project_module_sprint"`); + await queryRunner.query(`DROP INDEX "public"."IDX_e6b6555e5fc6c5120110a0195c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_8f2054a6a2d4b9c17624b9c8a0"`); + await queryRunner.query(`DROP INDEX "public"."IDX_7fd3c8f54c01943b283080aefa"`); + await queryRunner.query(`DROP INDEX "public"."IDX_86438fbaa1d857f32f66b24885"`); + await queryRunner.query(`DROP INDEX "public"."IDX_cd928adcb5ebb00c9f2c57e390"`); + await queryRunner.query(`DROP INDEX "public"."IDX_8a7a4d4206c003c3827c5afe5d"`); + await queryRunner.query(`DROP INDEX "public"."IDX_a56086e95fb2627ba2a3dd2eaa"`); + await queryRunner.query(`DROP INDEX "public"."IDX_f33638d289aff2306328c32a8c"`); + await queryRunner.query(`DROP TABLE "organization_project_module"`); + } + + /** + * SqliteDB and BetterSQlite3DB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "organization_project_module" ("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, "name" varchar NOT NULL, "description" text, "status" varchar, "startDate" datetime, "endDate" datetime, "public" boolean DEFAULT (0), "isFavorite" boolean DEFAULT (0), "parentId" varchar, "projectId" varchar, "creatorId" varchar, "managerId" varchar)` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f33638d289aff2306328c32a8c" ON "organization_project_module" ("isActive") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_a56086e95fb2627ba2a3dd2eaa" ON "organization_project_module" ("isArchived") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8a7a4d4206c003c3827c5afe5d" ON "organization_project_module" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_cd928adcb5ebb00c9f2c57e390" ON "organization_project_module" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_86438fbaa1d857f32f66b24885" ON "organization_project_module" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7fd3c8f54c01943b283080aefa" ON "organization_project_module" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0" ON "organization_project_module" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e6b6555e5fc6c5120110a0195c" ON "organization_project_module" ("managerId") ` + ); + await queryRunner.query( + `CREATE TABLE "project_module_sprint" ("organizationProjectModuleId" varchar NOT NULL, "organizationSprintId" varchar NOT NULL, PRIMARY KEY ("organizationProjectModuleId", "organizationSprintId"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_c91ef400079e93fec908cf9384" ON "project_module_sprint" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7af935d75a7e21fd76f072fbc0" ON "project_module_sprint" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE TABLE "project_module_team" ("organizationProjectModuleId" varchar NOT NULL, "organizationTeamId" varchar NOT NULL, PRIMARY KEY ("organizationProjectModuleId", "organizationTeamId"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_42c46289259b3fcdf2dc61744a" ON "project_module_team" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_d14aeb1b3e08d80eb32dd05934" ON "project_module_team" ("organizationTeamId") ` + ); + await queryRunner.query( + `CREATE TABLE "project_module_employee" ("organizationProjectModuleId" varchar NOT NULL, "employeeId" varchar NOT NULL, PRIMARY KEY ("organizationProjectModuleId", "employeeId"))` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e9fd7310fc93849b1d55e64d28" ON "project_module_employee" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_18e428e909e48a4b7df43d7e01" ON "project_module_employee" ("employeeId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query(`DROP INDEX "IDX_3e16c81005c389a4db83c0e5e3"`); + await queryRunner.query(`DROP INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54"`); + await queryRunner.query( + `CREATE TABLE "temporary_task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "projectModuleId" varchar, CONSTRAINT "FK_b8616deefe44d0622233e73fbf9" FOREIGN KEY ("taskPriorityId") REFERENCES "task_priority" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076" FOREIGN KEY ("taskSizeId") REFERENCES "task_size" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b" FOREIGN KEY ("taskStatusId") REFERENCES "task_status" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt" FROM "task"` + ); + await queryRunner.query(`DROP TABLE "task"`); + await queryRunner.query(`ALTER TABLE "temporary_task" RENAME TO "task"`); + await queryRunner.query(`CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") `); + await queryRunner.query(`CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") `); + await queryRunner.query(`CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") `); + await queryRunner.query(`CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") `); + await queryRunner.query(`CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") `); + await queryRunner.query(`CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") `); + await queryRunner.query(`CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") `); + await queryRunner.query(`CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") `); + await queryRunner.query(`CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") `); + await queryRunner.query(`CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") `); + await queryRunner.query(`CREATE INDEX "IDX_3e16c81005c389a4db83c0e5e3" ON "task" ("isActive") `); + await queryRunner.query(`CREATE INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54" ON "task" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_579534d8e12f22d308d6bd5f42" ON "task" ("projectModuleId") `); + await queryRunner.query(`DROP INDEX "IDX_f33638d289aff2306328c32a8c"`); + await queryRunner.query(`DROP INDEX "IDX_a56086e95fb2627ba2a3dd2eaa"`); + await queryRunner.query(`DROP INDEX "IDX_8a7a4d4206c003c3827c5afe5d"`); + await queryRunner.query(`DROP INDEX "IDX_cd928adcb5ebb00c9f2c57e390"`); + await queryRunner.query(`DROP INDEX "IDX_86438fbaa1d857f32f66b24885"`); + await queryRunner.query(`DROP INDEX "IDX_7fd3c8f54c01943b283080aefa"`); + await queryRunner.query(`DROP INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0"`); + await queryRunner.query(`DROP INDEX "IDX_e6b6555e5fc6c5120110a0195c"`); + await queryRunner.query( + `CREATE TABLE "temporary_organization_project_module" ("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, "name" varchar NOT NULL, "description" text, "status" varchar, "startDate" datetime, "endDate" datetime, "public" boolean DEFAULT (0), "isFavorite" boolean DEFAULT (0), "parentId" varchar, "projectId" varchar, "creatorId" varchar, "managerId" varchar, CONSTRAINT "FK_8a7a4d4206c003c3827c5afe5dc" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_cd928adcb5ebb00c9f2c57e3908" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_4bb6fbfa64cf5d5977c2e5346a9" FOREIGN KEY ("parentId") REFERENCES "organization_project_module" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_7fd3c8f54c01943b283080aefa3" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_8f2054a6a2d4b9c17624b9c8a01" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_e6b6555e5fc6c5120110a0195cd" FOREIGN KEY ("managerId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_organization_project_module"("deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "name", "description", "status", "startDate", "endDate", "public", "isFavorite", "parentId", "projectId", "creatorId", "managerId") SELECT "deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "name", "description", "status", "startDate", "endDate", "public", "isFavorite", "parentId", "projectId", "creatorId", "managerId" FROM "organization_project_module"` + ); + await queryRunner.query(`DROP TABLE "organization_project_module"`); + await queryRunner.query( + `ALTER TABLE "temporary_organization_project_module" RENAME TO "organization_project_module"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f33638d289aff2306328c32a8c" ON "organization_project_module" ("isActive") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_a56086e95fb2627ba2a3dd2eaa" ON "organization_project_module" ("isArchived") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8a7a4d4206c003c3827c5afe5d" ON "organization_project_module" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_cd928adcb5ebb00c9f2c57e390" ON "organization_project_module" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_86438fbaa1d857f32f66b24885" ON "organization_project_module" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7fd3c8f54c01943b283080aefa" ON "organization_project_module" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0" ON "organization_project_module" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e6b6555e5fc6c5120110a0195c" ON "organization_project_module" ("managerId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query(`DROP INDEX "IDX_3e16c81005c389a4db83c0e5e3"`); + await queryRunner.query(`DROP INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54"`); + await queryRunner.query(`DROP INDEX "IDX_579534d8e12f22d308d6bd5f42"`); + await queryRunner.query( + `CREATE TABLE "temporary_task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "projectModuleId" varchar, CONSTRAINT "FK_b8616deefe44d0622233e73fbf9" FOREIGN KEY ("taskPriorityId") REFERENCES "task_priority" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076" FOREIGN KEY ("taskSizeId") REFERENCES "task_size" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b" FOREIGN KEY ("taskStatusId") REFERENCES "task_status" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_579534d8e12f22d308d6bd5f428" FOREIGN KEY ("projectModuleId") REFERENCES "organization_project_module" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt", "projectModuleId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt", "projectModuleId" FROM "task"` + ); + await queryRunner.query(`DROP TABLE "task"`); + await queryRunner.query(`ALTER TABLE "temporary_task" RENAME TO "task"`); + await queryRunner.query(`CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") `); + await queryRunner.query(`CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") `); + await queryRunner.query(`CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") `); + await queryRunner.query(`CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") `); + await queryRunner.query(`CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") `); + await queryRunner.query(`CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") `); + await queryRunner.query(`CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") `); + await queryRunner.query(`CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") `); + await queryRunner.query(`CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") `); + await queryRunner.query(`CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") `); + await queryRunner.query(`CREATE INDEX "IDX_3e16c81005c389a4db83c0e5e3" ON "task" ("isActive") `); + await queryRunner.query(`CREATE INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54" ON "task" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_579534d8e12f22d308d6bd5f42" ON "task" ("projectModuleId") `); + await queryRunner.query(`DROP INDEX "IDX_c91ef400079e93fec908cf9384"`); + await queryRunner.query(`DROP INDEX "IDX_7af935d75a7e21fd76f072fbc0"`); + await queryRunner.query( + `CREATE TABLE "temporary_project_module_sprint" ("organizationProjectModuleId" varchar NOT NULL, "organizationSprintId" varchar NOT NULL, CONSTRAINT "FK_c91ef400079e93fec908cf93845" FOREIGN KEY ("organizationProjectModuleId") REFERENCES "organization_project_module" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_7af935d75a7e21fd76f072fbc03" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("organizationProjectModuleId", "organizationSprintId"))` + ); + await queryRunner.query( + `INSERT INTO "temporary_project_module_sprint"("organizationProjectModuleId", "organizationSprintId") SELECT "organizationProjectModuleId", "organizationSprintId" FROM "project_module_sprint"` + ); + await queryRunner.query(`DROP TABLE "project_module_sprint"`); + await queryRunner.query(`ALTER TABLE "temporary_project_module_sprint" RENAME TO "project_module_sprint"`); + await queryRunner.query( + `CREATE INDEX "IDX_c91ef400079e93fec908cf9384" ON "project_module_sprint" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7af935d75a7e21fd76f072fbc0" ON "project_module_sprint" ("organizationSprintId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_42c46289259b3fcdf2dc61744a"`); + await queryRunner.query(`DROP INDEX "IDX_d14aeb1b3e08d80eb32dd05934"`); + await queryRunner.query( + `CREATE TABLE "temporary_project_module_team" ("organizationProjectModuleId" varchar NOT NULL, "organizationTeamId" varchar NOT NULL, CONSTRAINT "FK_42c46289259b3fcdf2dc61744a7" FOREIGN KEY ("organizationProjectModuleId") REFERENCES "organization_project_module" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_d14aeb1b3e08d80eb32dd05934b" FOREIGN KEY ("organizationTeamId") REFERENCES "organization_team" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("organizationProjectModuleId", "organizationTeamId"))` + ); + await queryRunner.query( + `INSERT INTO "temporary_project_module_team"("organizationProjectModuleId", "organizationTeamId") SELECT "organizationProjectModuleId", "organizationTeamId" FROM "project_module_team"` + ); + await queryRunner.query(`DROP TABLE "project_module_team"`); + await queryRunner.query(`ALTER TABLE "temporary_project_module_team" RENAME TO "project_module_team"`); + await queryRunner.query( + `CREATE INDEX "IDX_42c46289259b3fcdf2dc61744a" ON "project_module_team" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_d14aeb1b3e08d80eb32dd05934" ON "project_module_team" ("organizationTeamId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_e9fd7310fc93849b1d55e64d28"`); + await queryRunner.query(`DROP INDEX "IDX_18e428e909e48a4b7df43d7e01"`); + await queryRunner.query( + `CREATE TABLE "temporary_project_module_employee" ("organizationProjectModuleId" varchar NOT NULL, "employeeId" varchar NOT NULL, CONSTRAINT "FK_e9fd7310fc93849b1d55e64d280" FOREIGN KEY ("organizationProjectModuleId") REFERENCES "organization_project_module" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_18e428e909e48a4b7df43d7e01e" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("organizationProjectModuleId", "employeeId"))` + ); + await queryRunner.query( + `INSERT INTO "temporary_project_module_employee"("organizationProjectModuleId", "employeeId") SELECT "organizationProjectModuleId", "employeeId" FROM "project_module_employee"` + ); + await queryRunner.query(`DROP TABLE "project_module_employee"`); + await queryRunner.query(`ALTER TABLE "temporary_project_module_employee" RENAME TO "project_module_employee"`); + await queryRunner.query( + `CREATE INDEX "IDX_e9fd7310fc93849b1d55e64d28" ON "project_module_employee" ("organizationProjectModuleId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_18e428e909e48a4b7df43d7e01" ON "project_module_employee" ("employeeId") ` + ); + } + + /** + * SqliteDB and BetterSQlite3DB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_18e428e909e48a4b7df43d7e01"`); + await queryRunner.query(`DROP INDEX "IDX_e9fd7310fc93849b1d55e64d28"`); + await queryRunner.query(`ALTER TABLE "project_module_employee" RENAME TO "temporary_project_module_employee"`); + await queryRunner.query( + `CREATE TABLE "project_module_employee" ("organizationProjectModuleId" varchar NOT NULL, "employeeId" varchar NOT NULL, PRIMARY KEY ("organizationProjectModuleId", "employeeId"))` + ); + await queryRunner.query( + `INSERT INTO "project_module_employee"("organizationProjectModuleId", "employeeId") SELECT "organizationProjectModuleId", "employeeId" FROM "temporary_project_module_employee"` + ); + await queryRunner.query(`DROP TABLE "temporary_project_module_employee"`); + await queryRunner.query( + `CREATE INDEX "IDX_18e428e909e48a4b7df43d7e01" ON "project_module_employee" ("employeeId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_e9fd7310fc93849b1d55e64d28" ON "project_module_employee" ("organizationProjectModuleId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_d14aeb1b3e08d80eb32dd05934"`); + await queryRunner.query(`DROP INDEX "IDX_42c46289259b3fcdf2dc61744a"`); + await queryRunner.query(`ALTER TABLE "project_module_team" RENAME TO "temporary_project_module_team"`); + await queryRunner.query( + `CREATE TABLE "project_module_team" ("organizationProjectModuleId" varchar NOT NULL, "organizationTeamId" varchar NOT NULL, PRIMARY KEY ("organizationProjectModuleId", "organizationTeamId"))` + ); + await queryRunner.query( + `INSERT INTO "project_module_team"("organizationProjectModuleId", "organizationTeamId") SELECT "organizationProjectModuleId", "organizationTeamId" FROM "temporary_project_module_team"` + ); + await queryRunner.query(`DROP TABLE "temporary_project_module_team"`); + await queryRunner.query( + `CREATE INDEX "IDX_d14aeb1b3e08d80eb32dd05934" ON "project_module_team" ("organizationTeamId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_42c46289259b3fcdf2dc61744a" ON "project_module_team" ("organizationProjectModuleId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_7af935d75a7e21fd76f072fbc0"`); + await queryRunner.query(`DROP INDEX "IDX_c91ef400079e93fec908cf9384"`); + await queryRunner.query(`ALTER TABLE "project_module_sprint" RENAME TO "temporary_project_module_sprint"`); + await queryRunner.query( + `CREATE TABLE "project_module_sprint" ("organizationProjectModuleId" varchar NOT NULL, "organizationSprintId" varchar NOT NULL, PRIMARY KEY ("organizationProjectModuleId", "organizationSprintId"))` + ); + await queryRunner.query( + `INSERT INTO "project_module_sprint"("organizationProjectModuleId", "organizationSprintId") SELECT "organizationProjectModuleId", "organizationSprintId" FROM "temporary_project_module_sprint"` + ); + await queryRunner.query(`DROP TABLE "temporary_project_module_sprint"`); + await queryRunner.query( + `CREATE INDEX "IDX_7af935d75a7e21fd76f072fbc0" ON "project_module_sprint" ("organizationSprintId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_c91ef400079e93fec908cf9384" ON "project_module_sprint" ("organizationProjectModuleId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_579534d8e12f22d308d6bd5f42"`); + await queryRunner.query(`DROP INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54"`); + await queryRunner.query(`DROP INDEX "IDX_3e16c81005c389a4db83c0e5e3"`); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query(`ALTER TABLE "task" RENAME TO "temporary_task"`); + await queryRunner.query( + `CREATE TABLE "task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "projectModuleId" varchar, CONSTRAINT "FK_b8616deefe44d0622233e73fbf9" FOREIGN KEY ("taskPriorityId") REFERENCES "task_priority" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076" FOREIGN KEY ("taskSizeId") REFERENCES "task_size" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b" FOREIGN KEY ("taskStatusId") REFERENCES "task_status" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt", "projectModuleId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt", "projectModuleId" FROM "temporary_task"` + ); + await queryRunner.query(`DROP TABLE "temporary_task"`); + await queryRunner.query(`CREATE INDEX "IDX_579534d8e12f22d308d6bd5f42" ON "task" ("projectModuleId") `); + await queryRunner.query(`CREATE INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54" ON "task" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_3e16c81005c389a4db83c0e5e3" ON "task" ("isActive") `); + await queryRunner.query(`CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") `); + await queryRunner.query(`CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") `); + await queryRunner.query(`CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") `); + await queryRunner.query(`CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") `); + await queryRunner.query(`CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") `); + await queryRunner.query(`CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") `); + await queryRunner.query(`CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") `); + await queryRunner.query(`CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") `); + await queryRunner.query(`CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") `); + await queryRunner.query(`CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") `); + await queryRunner.query(`CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") `); + await queryRunner.query(`DROP INDEX "IDX_e6b6555e5fc6c5120110a0195c"`); + await queryRunner.query(`DROP INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0"`); + await queryRunner.query(`DROP INDEX "IDX_7fd3c8f54c01943b283080aefa"`); + await queryRunner.query(`DROP INDEX "IDX_86438fbaa1d857f32f66b24885"`); + await queryRunner.query(`DROP INDEX "IDX_cd928adcb5ebb00c9f2c57e390"`); + await queryRunner.query(`DROP INDEX "IDX_8a7a4d4206c003c3827c5afe5d"`); + await queryRunner.query(`DROP INDEX "IDX_a56086e95fb2627ba2a3dd2eaa"`); + await queryRunner.query(`DROP INDEX "IDX_f33638d289aff2306328c32a8c"`); + await queryRunner.query( + `ALTER TABLE "organization_project_module" RENAME TO "temporary_organization_project_module"` + ); + await queryRunner.query( + `CREATE TABLE "organization_project_module" ("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, "name" varchar NOT NULL, "description" text, "status" varchar, "startDate" datetime, "endDate" datetime, "public" boolean DEFAULT (0), "isFavorite" boolean DEFAULT (0), "parentId" varchar, "projectId" varchar, "creatorId" varchar, "managerId" varchar)` + ); + await queryRunner.query( + `INSERT INTO "organization_project_module"("deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "name", "description", "status", "startDate", "endDate", "public", "isFavorite", "parentId", "projectId", "creatorId", "managerId") SELECT "deletedAt", "id", "createdAt", "updatedAt", "isActive", "isArchived", "tenantId", "organizationId", "name", "description", "status", "startDate", "endDate", "public", "isFavorite", "parentId", "projectId", "creatorId", "managerId" FROM "temporary_organization_project_module"` + ); + await queryRunner.query(`DROP TABLE "temporary_organization_project_module"`); + await queryRunner.query( + `CREATE INDEX "IDX_e6b6555e5fc6c5120110a0195c" ON "organization_project_module" ("managerId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0" ON "organization_project_module" ("creatorId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_7fd3c8f54c01943b283080aefa" ON "organization_project_module" ("projectId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_86438fbaa1d857f32f66b24885" ON "organization_project_module" ("status") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_cd928adcb5ebb00c9f2c57e390" ON "organization_project_module" ("organizationId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_8a7a4d4206c003c3827c5afe5d" ON "organization_project_module" ("tenantId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_a56086e95fb2627ba2a3dd2eaa" ON "organization_project_module" ("isArchived") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_f33638d289aff2306328c32a8c" ON "organization_project_module" ("isActive") ` + ); + await queryRunner.query(`DROP INDEX "IDX_579534d8e12f22d308d6bd5f42"`); + await queryRunner.query(`DROP INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54"`); + await queryRunner.query(`DROP INDEX "IDX_3e16c81005c389a4db83c0e5e3"`); + await queryRunner.query(`DROP INDEX "taskNumber"`); + await queryRunner.query(`DROP INDEX "IDX_1e1f64696aa3a26d3e12c840e5"`); + await queryRunner.query(`DROP INDEX "IDX_94fe6b3a5aec5f85427df4f8cd"`); + await queryRunner.query(`DROP INDEX "IDX_3797a20ef5553ae87af126bc2f"`); + await queryRunner.query(`DROP INDEX "IDX_5b0272d923a31c972bed1a1ac4"`); + await queryRunner.query(`DROP INDEX "IDX_e91cbff3d206f150ccc14d0c3a"`); + await queryRunner.query(`DROP INDEX "IDX_2fe7a278e6f08d2be55740a939"`); + await queryRunner.query(`DROP INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1"`); + await queryRunner.query(`DROP INDEX "IDX_7127880d6fae956ecc1c84ac31"`); + await queryRunner.query(`DROP INDEX "IDX_ed5441fb13e82854a994da5a78"`); + await queryRunner.query(`DROP INDEX "IDX_0cbe714983eb0aae5feeee8212"`); + await queryRunner.query(`DROP INDEX "IDX_2f4bdd2593fd6038aaa91fd107"`); + await queryRunner.query(`DROP INDEX "IDX_b8616deefe44d0622233e73fbf"`); + await queryRunner.query(`ALTER TABLE "task" RENAME TO "temporary_task"`); + await queryRunner.query( + `CREATE TABLE "task" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "title" varchar NOT NULL, "description" varchar, "status" varchar, "estimate" integer, "dueDate" datetime, "projectId" varchar, "creatorId" varchar, "organizationSprintId" varchar, "number" integer, "prefix" varchar, "priority" varchar, "size" varchar, "public" boolean DEFAULT (1), "startDate" datetime, "resolvedAt" datetime, "version" varchar, "issueType" varchar, "parentId" varchar, "taskStatusId" varchar, "taskSizeId" varchar, "taskPriorityId" varchar, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, CONSTRAINT "FK_b8616deefe44d0622233e73fbf9" FOREIGN KEY ("taskPriorityId") REFERENCES "task_priority" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_2f4bdd2593fd6038aaa91fd1076" FOREIGN KEY ("taskSizeId") REFERENCES "task_size" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_0cbe714983eb0aae5feeee8212b" FOREIGN KEY ("taskStatusId") REFERENCES "task_status" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_e91cbff3d206f150ccc14d0c3a1" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_5b0272d923a31c972bed1a1ac4d" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_3797a20ef5553ae87af126bc2fe" FOREIGN KEY ("projectId") REFERENCES "organization_project" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_94fe6b3a5aec5f85427df4f8cd7" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_1e1f64696aa3a26d3e12c840e55" FOREIGN KEY ("organizationSprintId") REFERENCES "organization_sprint" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_8c9920b5fb32c3d8453f64b705c" FOREIGN KEY ("parentId") REFERENCES "task" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "task"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "title", "description", "status", "estimate", "dueDate", "projectId", "creatorId", "organizationSprintId", "number", "prefix", "priority", "size", "public", "startDate", "resolvedAt", "version", "issueType", "parentId", "taskStatusId", "taskSizeId", "taskPriorityId", "isActive", "isArchived", "deletedAt" FROM "temporary_task"` + ); + await queryRunner.query(`DROP TABLE "temporary_task"`); + await queryRunner.query(`CREATE INDEX "IDX_ca2f7edd5a5ce8f14b257c9d54" ON "task" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_3e16c81005c389a4db83c0e5e3" ON "task" ("isActive") `); + await queryRunner.query(`CREATE UNIQUE INDEX "taskNumber" ON "task" ("projectId", "number") `); + await queryRunner.query(`CREATE INDEX "IDX_1e1f64696aa3a26d3e12c840e5" ON "task" ("organizationSprintId") `); + await queryRunner.query(`CREATE INDEX "IDX_94fe6b3a5aec5f85427df4f8cd" ON "task" ("creatorId") `); + await queryRunner.query(`CREATE INDEX "IDX_3797a20ef5553ae87af126bc2f" ON "task" ("projectId") `); + await queryRunner.query(`CREATE INDEX "IDX_5b0272d923a31c972bed1a1ac4" ON "task" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_e91cbff3d206f150ccc14d0c3a" ON "task" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_2fe7a278e6f08d2be55740a939" ON "task" ("status") `); + await queryRunner.query(`CREATE INDEX "IDX_f092f3386f10f2e2ef5b0b6ad1" ON "task" ("priority") `); + await queryRunner.query(`CREATE INDEX "IDX_7127880d6fae956ecc1c84ac31" ON "task" ("size") `); + await queryRunner.query(`CREATE INDEX "IDX_ed5441fb13e82854a994da5a78" ON "task" ("issueType") `); + await queryRunner.query(`CREATE INDEX "IDX_0cbe714983eb0aae5feeee8212" ON "task" ("taskStatusId") `); + await queryRunner.query(`CREATE INDEX "IDX_2f4bdd2593fd6038aaa91fd107" ON "task" ("taskSizeId") `); + await queryRunner.query(`CREATE INDEX "IDX_b8616deefe44d0622233e73fbf" ON "task" ("taskPriorityId") `); + await queryRunner.query(`DROP INDEX "IDX_18e428e909e48a4b7df43d7e01"`); + await queryRunner.query(`DROP INDEX "IDX_e9fd7310fc93849b1d55e64d28"`); + await queryRunner.query(`DROP TABLE "project_module_employee"`); + await queryRunner.query(`DROP INDEX "IDX_d14aeb1b3e08d80eb32dd05934"`); + await queryRunner.query(`DROP INDEX "IDX_42c46289259b3fcdf2dc61744a"`); + await queryRunner.query(`DROP TABLE "project_module_team"`); + await queryRunner.query(`DROP INDEX "IDX_7af935d75a7e21fd76f072fbc0"`); + await queryRunner.query(`DROP INDEX "IDX_c91ef400079e93fec908cf9384"`); + await queryRunner.query(`DROP TABLE "project_module_sprint"`); + await queryRunner.query(`DROP INDEX "IDX_e6b6555e5fc6c5120110a0195c"`); + await queryRunner.query(`DROP INDEX "IDX_8f2054a6a2d4b9c17624b9c8a0"`); + await queryRunner.query(`DROP INDEX "IDX_7fd3c8f54c01943b283080aefa"`); + await queryRunner.query(`DROP INDEX "IDX_86438fbaa1d857f32f66b24885"`); + await queryRunner.query(`DROP INDEX "IDX_cd928adcb5ebb00c9f2c57e390"`); + await queryRunner.query(`DROP INDEX "IDX_8a7a4d4206c003c3827c5afe5d"`); + await queryRunner.query(`DROP INDEX "IDX_a56086e95fb2627ba2a3dd2eaa"`); + await queryRunner.query(`DROP INDEX "IDX_f33638d289aff2306328c32a8c"`); + await queryRunner.query(`DROP TABLE "organization_project_module"`); + } + + /** + * MySQL Up Migration + * + * @param queryRunner + */ + public async mysqlUpQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE \`organization_project_module\` (\`deletedAt\` datetime(6) NULL, \`id\` varchar(36) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updatedAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`isActive\` tinyint NULL DEFAULT 1, \`isArchived\` tinyint NULL DEFAULT 0, \`tenantId\` varchar(255) NULL, \`organizationId\` varchar(255) NULL, \`name\` varchar(255) NOT NULL, \`description\` text NULL, \`status\` varchar(255) NULL, \`startDate\` datetime NULL, \`endDate\` datetime NULL, \`public\` tinyint NULL DEFAULT 0, \`isFavorite\` tinyint NULL DEFAULT 0, \`parentId\` varchar(255) NULL, \`projectId\` varchar(255) NULL, \`creatorId\` varchar(255) NULL, \`managerId\` varchar(255) NULL, INDEX \`IDX_f33638d289aff2306328c32a8c\` (\`isActive\`), INDEX \`IDX_a56086e95fb2627ba2a3dd2eaa\` (\`isArchived\`), INDEX \`IDX_8a7a4d4206c003c3827c5afe5d\` (\`tenantId\`), INDEX \`IDX_cd928adcb5ebb00c9f2c57e390\` (\`organizationId\`), INDEX \`IDX_86438fbaa1d857f32f66b24885\` (\`status\`), INDEX \`IDX_7fd3c8f54c01943b283080aefa\` (\`projectId\`), INDEX \`IDX_8f2054a6a2d4b9c17624b9c8a0\` (\`creatorId\`), INDEX \`IDX_e6b6555e5fc6c5120110a0195c\` (\`managerId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB` + ); + await queryRunner.query( + `CREATE TABLE \`project_module_sprint\` (\`organizationProjectModuleId\` varchar(36) NOT NULL, \`organizationSprintId\` varchar(36) NOT NULL, INDEX \`IDX_c91ef400079e93fec908cf9384\` (\`organizationProjectModuleId\`), INDEX \`IDX_7af935d75a7e21fd76f072fbc0\` (\`organizationSprintId\`), PRIMARY KEY (\`organizationProjectModuleId\`, \`organizationSprintId\`)) ENGINE=InnoDB` + ); + await queryRunner.query( + `CREATE TABLE \`project_module_team\` (\`organizationProjectModuleId\` varchar(36) NOT NULL, \`organizationTeamId\` varchar(36) NOT NULL, INDEX \`IDX_42c46289259b3fcdf2dc61744a\` (\`organizationProjectModuleId\`), INDEX \`IDX_d14aeb1b3e08d80eb32dd05934\` (\`organizationTeamId\`), PRIMARY KEY (\`organizationProjectModuleId\`, \`organizationTeamId\`)) ENGINE=InnoDB` + ); + await queryRunner.query( + `CREATE TABLE \`project_module_employee\` (\`organizationProjectModuleId\` varchar(36) NOT NULL, \`employeeId\` varchar(36) NOT NULL, INDEX \`IDX_e9fd7310fc93849b1d55e64d28\` (\`organizationProjectModuleId\`), INDEX \`IDX_18e428e909e48a4b7df43d7e01\` (\`employeeId\`), PRIMARY KEY (\`organizationProjectModuleId\`, \`employeeId\`)) ENGINE=InnoDB` + ); + await queryRunner.query(`ALTER TABLE \`issue_type\` ADD \`isDefault\` tinyint NOT NULL DEFAULT 0`); + await queryRunner.query(`ALTER TABLE \`task\` ADD \`projectModuleId\` varchar(255) NULL`); + await queryRunner.query(`CREATE INDEX \`IDX_579534d8e12f22d308d6bd5f42\` ON \`task\` (\`projectModuleId\`)`); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` ADD CONSTRAINT \`FK_8a7a4d4206c003c3827c5afe5dc\` FOREIGN KEY (\`tenantId\`) REFERENCES \`tenant\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` ADD CONSTRAINT \`FK_cd928adcb5ebb00c9f2c57e3908\` FOREIGN KEY (\`organizationId\`) REFERENCES \`organization\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` ADD CONSTRAINT \`FK_4bb6fbfa64cf5d5977c2e5346a9\` FOREIGN KEY (\`parentId\`) REFERENCES \`organization_project_module\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` ADD CONSTRAINT \`FK_7fd3c8f54c01943b283080aefa3\` FOREIGN KEY (\`projectId\`) REFERENCES \`organization_project\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` ADD CONSTRAINT \`FK_8f2054a6a2d4b9c17624b9c8a01\` FOREIGN KEY (\`creatorId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` ADD CONSTRAINT \`FK_e6b6555e5fc6c5120110a0195cd\` FOREIGN KEY (\`managerId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`task\` ADD CONSTRAINT \`FK_579534d8e12f22d308d6bd5f428\` FOREIGN KEY (\`projectModuleId\`) REFERENCES \`organization_project_module\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_sprint\` ADD CONSTRAINT \`FK_c91ef400079e93fec908cf93845\` FOREIGN KEY (\`organizationProjectModuleId\`) REFERENCES \`organization_project_module\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_sprint\` ADD CONSTRAINT \`FK_7af935d75a7e21fd76f072fbc03\` FOREIGN KEY (\`organizationSprintId\`) REFERENCES \`organization_sprint\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_team\` ADD CONSTRAINT \`FK_42c46289259b3fcdf2dc61744a7\` FOREIGN KEY (\`organizationProjectModuleId\`) REFERENCES \`organization_project_module\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_team\` ADD CONSTRAINT \`FK_d14aeb1b3e08d80eb32dd05934b\` FOREIGN KEY (\`organizationTeamId\`) REFERENCES \`organization_team\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_employee\` ADD CONSTRAINT \`FK_e9fd7310fc93849b1d55e64d280\` FOREIGN KEY (\`organizationProjectModuleId\`) REFERENCES \`organization_project_module\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_employee\` ADD CONSTRAINT \`FK_18e428e909e48a4b7df43d7e01e\` FOREIGN KEY (\`employeeId\`) REFERENCES \`employee\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + } + + /** + * MySQL Down Migration + * + * @param queryRunner + */ + public async mysqlDownQueryRunner(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`project_module_employee\` DROP FOREIGN KEY \`FK_18e428e909e48a4b7df43d7e01e\`` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_employee\` DROP FOREIGN KEY \`FK_e9fd7310fc93849b1d55e64d280\`` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_team\` DROP FOREIGN KEY \`FK_d14aeb1b3e08d80eb32dd05934b\`` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_team\` DROP FOREIGN KEY \`FK_42c46289259b3fcdf2dc61744a7\`` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_sprint\` DROP FOREIGN KEY \`FK_7af935d75a7e21fd76f072fbc03\`` + ); + await queryRunner.query( + `ALTER TABLE \`project_module_sprint\` DROP FOREIGN KEY \`FK_c91ef400079e93fec908cf93845\`` + ); + await queryRunner.query(`ALTER TABLE \`task\` DROP FOREIGN KEY \`FK_579534d8e12f22d308d6bd5f428\``); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` DROP FOREIGN KEY \`FK_e6b6555e5fc6c5120110a0195cd\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` DROP FOREIGN KEY \`FK_8f2054a6a2d4b9c17624b9c8a01\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` DROP FOREIGN KEY \`FK_7fd3c8f54c01943b283080aefa3\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` DROP FOREIGN KEY \`FK_4bb6fbfa64cf5d5977c2e5346a9\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` DROP FOREIGN KEY \`FK_cd928adcb5ebb00c9f2c57e3908\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_project_module\` DROP FOREIGN KEY \`FK_8a7a4d4206c003c3827c5afe5dc\`` + ); + await queryRunner.query(`DROP INDEX \`IDX_579534d8e12f22d308d6bd5f42\` ON \`task\``); + await queryRunner.query(`ALTER TABLE \`task\` DROP COLUMN \`projectModuleId\``); + await queryRunner.query(`ALTER TABLE \`issue_type\` DROP COLUMN \`isDefault\``); + await queryRunner.query(`DROP INDEX \`IDX_18e428e909e48a4b7df43d7e01\` ON \`project_module_employee\``); + await queryRunner.query(`DROP INDEX \`IDX_e9fd7310fc93849b1d55e64d28\` ON \`project_module_employee\``); + await queryRunner.query(`DROP TABLE \`project_module_employee\``); + await queryRunner.query(`DROP INDEX \`IDX_d14aeb1b3e08d80eb32dd05934\` ON \`project_module_team\``); + await queryRunner.query(`DROP INDEX \`IDX_42c46289259b3fcdf2dc61744a\` ON \`project_module_team\``); + await queryRunner.query(`DROP TABLE \`project_module_team\``); + await queryRunner.query(`DROP INDEX \`IDX_7af935d75a7e21fd76f072fbc0\` ON \`project_module_sprint\``); + await queryRunner.query(`DROP INDEX \`IDX_c91ef400079e93fec908cf9384\` ON \`project_module_sprint\``); + await queryRunner.query(`DROP TABLE \`project_module_sprint\``); + await queryRunner.query(`DROP INDEX \`IDX_e6b6555e5fc6c5120110a0195c\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_8f2054a6a2d4b9c17624b9c8a0\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_7fd3c8f54c01943b283080aefa\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_86438fbaa1d857f32f66b24885\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_cd928adcb5ebb00c9f2c57e390\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_8a7a4d4206c003c3827c5afe5d\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_a56086e95fb2627ba2a3dd2eaa\` ON \`organization_project_module\``); + await queryRunner.query(`DROP INDEX \`IDX_f33638d289aff2306328c32a8c\` ON \`organization_project_module\``); + await queryRunner.query(`DROP TABLE \`organization_project_module\``); + } +} diff --git a/packages/core/src/employee/employee.entity.ts b/packages/core/src/employee/employee.entity.ts index 61e6156a40b..fdd4e064315 100644 --- a/packages/core/src/employee/employee.entity.ts +++ b/packages/core/src/employee/employee.entity.ts @@ -30,7 +30,8 @@ import { IEmployeeAward, IEquipmentSharing, IEmployeePhone, - IDailyPlan + IDailyPlan, + IOrganizationProjectModule } from '@gauzy/contracts'; import { ColumnIndex, @@ -59,6 +60,7 @@ import { OrganizationEmploymentType, OrganizationPosition, OrganizationProject, + OrganizationProjectModule, OrganizationTeamEmployee, RequestApprovalEmployee, Skill, @@ -640,6 +642,15 @@ export class Employee extends TenantOrganizationBaseEntity implements IEmployee, @JoinTable() tasks?: ITask[]; + /** + * Project Module + */ + @MultiORMManyToMany(() => OrganizationProjectModule, (module) => module.members, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE' + }) + modules?: IOrganizationProjectModule[]; + /** * Equipment Sharing */ diff --git a/packages/core/src/organization-project-module/dto/create-organization-project-module.dto.ts b/packages/core/src/organization-project-module/dto/create-organization-project-module.dto.ts new file mode 100644 index 00000000000..e080d6b5d60 --- /dev/null +++ b/packages/core/src/organization-project-module/dto/create-organization-project-module.dto.ts @@ -0,0 +1,14 @@ +import { IntersectionType, OmitType } from '@nestjs/swagger'; +import { IOrganizationProjectModuleCreateInput } from '@gauzy/contracts'; +import { TenantOrganizationBaseDTO } from './../../core/dto'; +import { OrganizationProjectModule } from './../organization-project-module.entity'; + +/** + * Create Project Module validation request DTO + */ +export class CreateOrganizationProjectModuleDTO + extends IntersectionType( + TenantOrganizationBaseDTO, + OmitType(OrganizationProjectModule, ['organizationId', 'organization']) + ) + implements IOrganizationProjectModuleCreateInput {} diff --git a/packages/core/src/organization-project-module/dto/index.ts b/packages/core/src/organization-project-module/dto/index.ts new file mode 100644 index 00000000000..344fea2e281 --- /dev/null +++ b/packages/core/src/organization-project-module/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-organization-project-module.dto'; +export * from './update-organization-project-module.dto'; +export * from './organization-project-module-find-input.dto'; diff --git a/packages/core/src/organization-project-module/dto/organization-project-module-find-input.dto.ts b/packages/core/src/organization-project-module/dto/organization-project-module-find-input.dto.ts new file mode 100644 index 00000000000..e004df56eb7 --- /dev/null +++ b/packages/core/src/organization-project-module/dto/organization-project-module-find-input.dto.ts @@ -0,0 +1,24 @@ +import { ID, IOrganizationProjectModuleFindInput } from '@gauzy/contracts'; +import { ApiPropertyOptional, IntersectionType, PartialType, PickType } from '@nestjs/swagger'; +import { IsOptional, IsUUID } from 'class-validator'; +import { TenantOrganizationBaseDTO } from './../../core/dto'; +import { CreateOrganizationProjectModuleDTO } from './create-organization-project-module.dto'; + +/** Organization Project Module query validation DTO */ +export class OrganizationProjectModuleFindInputDTO + extends IntersectionType( + TenantOrganizationBaseDTO, + PartialType(PickType(CreateOrganizationProjectModuleDTO, ['name', 'projectId'] as const)) + ) + implements IOrganizationProjectModuleFindInput +{ + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + organizationTeamId?: ID; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + organizationSprintId?: ID; +} diff --git a/packages/core/src/organization-project-module/dto/update-organization-project-module.dto.ts b/packages/core/src/organization-project-module/dto/update-organization-project-module.dto.ts new file mode 100644 index 00000000000..bc779cfc1f7 --- /dev/null +++ b/packages/core/src/organization-project-module/dto/update-organization-project-module.dto.ts @@ -0,0 +1,8 @@ +import { PartialType } from '@nestjs/swagger'; +import { IOrganizationProjectModuleUpdateInput } from '@gauzy/contracts'; +import { CreateOrganizationProjectModuleDTO } from './create-organization-project-module.dto'; + +/** Update Organization Project Module validation request DTO */ +export class UpdateOrganizationProjectModuleDTO + extends PartialType(CreateOrganizationProjectModuleDTO) + implements IOrganizationProjectModuleUpdateInput {} diff --git a/packages/core/src/organization-project-module/index.ts b/packages/core/src/organization-project-module/index.ts new file mode 100644 index 00000000000..f3d0d93d92a --- /dev/null +++ b/packages/core/src/organization-project-module/index.ts @@ -0,0 +1,2 @@ +export * from './organization-project-module.service'; +export * from './organization-project-module.module'; diff --git a/packages/core/src/organization-project-module/organization-project-module.controller.ts b/packages/core/src/organization-project-module/organization-project-module.controller.ts new file mode 100644 index 00000000000..570cb8ef904 --- /dev/null +++ b/packages/core/src/organization-project-module/organization-project-module.controller.ts @@ -0,0 +1,200 @@ +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Param, + Post, + Put, + Query, + UseGuards +} from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { DeleteResult, UpdateResult } from 'typeorm'; +import { ID, IOrganizationProjectModule, IPagination, PermissionsEnum } from '@gauzy/contracts'; +import { UseValidationPipe, UUIDValidationPipe } from './../shared/pipes'; +import { PermissionGuard, TenantPermissionGuard } from '../shared/guards'; +import { Permissions } from '../shared/decorators'; +import { CrudController, PaginationParams } from '../core/crud'; +import { OrganizationProjectModule } from './organization-project-module.entity'; +import { OrganizationProjectModuleService } from './organization-project-module.service'; +import { + CreateOrganizationProjectModuleDTO, + OrganizationProjectModuleFindInputDTO, + UpdateOrganizationProjectModuleDTO +} from './dto'; + +@ApiTags('Project Modules') +@UseGuards(TenantPermissionGuard, PermissionGuard) +@Permissions(PermissionsEnum.ALL_ORG_EDIT) +@Controller() +export class OrganizationProjectModuleController extends CrudController { + constructor(private readonly projectModuleService: OrganizationProjectModuleService) { + super(projectModuleService); + } + + /** + * @description Find employee project modules + * @param options - Options finders and relations + * @returns - A promise that resolves after found project modules + * @memberof OrganizationProjectModuleController + */ + @ApiOperation({ summary: 'Find employee project modules.' }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found employee project modules', + type: OrganizationProjectModule + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Records not found' + }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.PROJECT_MODULE_READ) + @Get('employee') + @UseValidationPipe({ transform: true }) + async getEmployeeProjectModules( + @Query() params: PaginationParams + ): Promise> { + return await this.projectModuleService.getEmployeeProjectModules(params); + } + + /** + * @description Find Team's project modules + * @param options - Options finders and relations + * @returns - A promise that resolves after found project modules + * @memberof OrganizationProjectModuleController + */ + @ApiOperation({ summary: 'Find my team project modules.' }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found team project modules', + type: OrganizationProjectModule + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Records not found' + }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.PROJECT_MODULE_READ) + @Get('team') + @UseValidationPipe({ transform: true }) + async findTeamProjectModules( + @Query() params: PaginationParams + ): Promise> { + return await this.projectModuleService.findTeamProjectModules(params); + } + + /** + * @description Find project modules by employee + * @param employeeId - The employee ID for whom to search project modules + * @param options - Finders options + * @returns A promise that resolves after found project modules + * @memberof OrganizationProjectModuleController + */ + @ApiOperation({ + summary: 'Find Employee project modules.' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found Employee project modules', + type: OrganizationProjectModule + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.PROJECT_MODULE_READ) + @Get('employee/:id') + @UseValidationPipe() + async findByEmployee( + @Param('id') employeeId: ID, + @Query() params: OrganizationProjectModuleFindInputDTO + ): Promise> { + return await this.projectModuleService.findByEmployee(employeeId, params); + } + + @ApiOperation({ summary: 'Find all project modules.' }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found project modules', + type: OrganizationProjectModule + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.PROJECT_MODULE_READ) + @Get() + async findAll( + @Query() params: PaginationParams + ): Promise> { + return await this.projectModuleService.findAll(params); + } + + @UseValidationPipe() + @ApiOperation({ summary: 'Find by id' }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found one record' + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.PROJECT_MODULE_READ) + @Get(':id') + async findById( + @Param('id', UUIDValidationPipe) id: ID, + @Query() params: PaginationParams + ): Promise { + return this.projectModuleService.findOneByIdString(id, params); + } + + @ApiOperation({ summary: 'create a project module' }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'The record has been successfully created.' + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid input, The response body may contain clues as to what went wrong' + }) + @HttpCode(HttpStatus.ACCEPTED) + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.PROJECT_MODULE_CREATE) + @Post() + @UseValidationPipe({ whitelist: true }) + async create(@Body() entity: CreateOrganizationProjectModuleDTO): Promise { + return await this.projectModuleService.create(entity); + } + + @ApiOperation({ summary: 'Update an existing project module' }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'The record has been successfully edited.' + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid input, The response body may contain clues as to what went wrong' + }) + @HttpCode(HttpStatus.ACCEPTED) + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.PROJECT_MODULE_UPDATE) + @Put(':id') + @UseValidationPipe({ whitelist: true }) + async update( + @Param('id', UUIDValidationPipe) id: ID, + @Body() entity: UpdateOrganizationProjectModuleDTO + ): Promise { + return await this.projectModuleService.update(id, entity); + } + + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.PROJECT_MODULE_DELETE) + @Delete(':id') + async delete(@Param('id', UUIDValidationPipe) id: ID): Promise { + return await this.projectModuleService.delete(id); + } +} diff --git a/packages/core/src/organization-project-module/organization-project-module.entity.ts b/packages/core/src/organization-project-module/organization-project-module.entity.ts new file mode 100644 index 00000000000..ebfe8108ab3 --- /dev/null +++ b/packages/core/src/organization-project-module/organization-project-module.entity.ts @@ -0,0 +1,252 @@ +import { JoinColumn, JoinTable, RelationId } from 'typeorm'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { EntityRepositoryType } from '@mikro-orm/core'; +import { + IsArray, + IsBoolean, + IsDate, + IsEnum, + IsNotEmpty, + IsObject, + IsOptional, + IsString, + IsUUID +} from 'class-validator'; +import { Type } from 'class-transformer'; +import { + ID, + IEmployee, + IOrganizationProject, + IOrganizationProjectModule, + IOrganizationSprint, + IOrganizationTeam, + ITask, + IUser, + TaskStatusEnum +} from '@gauzy/contracts'; +import { + Employee, + OrganizationProject, + OrganizationSprint, + OrganizationTeam, + Task, + TenantOrganizationBaseEntity, + User +} from '../core/entities/internal'; +import { + ColumnIndex, + MultiORMColumn, + MultiORMEntity, + MultiORMManyToMany, + MultiORMManyToOne, + MultiORMOneToMany +} from '../core/decorators/entity'; +import { MikroOrmOrganizationProjectModuleRepository } from './repository/mikro-orm-organization-project-module.repository'; + +@MultiORMEntity('organization_project_module', { + mikroOrmRepository: () => MikroOrmOrganizationProjectModuleRepository +}) +export class OrganizationProjectModule extends TenantOrganizationBaseEntity implements IOrganizationProjectModule { + [EntityRepositoryType]?: MikroOrmOrganizationProjectModuleRepository; + + @ApiProperty({ type: () => String }) + @IsNotEmpty() + @IsString() + @MultiORMColumn() + name: string; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsString() + @MultiORMColumn({ nullable: true, type: 'text' }) + description?: string; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsEnum(TaskStatusEnum) + @ColumnIndex() + @MultiORMColumn({ nullable: true }) + status?: TaskStatusEnum; + + @ApiPropertyOptional({ type: () => Date }) + @Type(() => Date) + @IsOptional() + @IsDate() + @MultiORMColumn({ nullable: true }) + startDate?: Date; + + @ApiPropertyOptional({ type: () => Date }) + @Type(() => Date) + @IsOptional() + @IsDate() + @MultiORMColumn({ nullable: true }) + endDate?: Date; + + @ApiPropertyOptional({ type: () => Boolean }) + @IsOptional() + @IsBoolean() + @MultiORMColumn({ nullable: true, default: false }) + public?: boolean; + + @ApiPropertyOptional({ type: () => Boolean }) + @IsOptional() + @IsBoolean() + @MultiORMColumn({ nullable: true, default: false }) + isFavorite?: boolean; + + /* + |-------------------------------------------------------------------------- + | @ManyToOne + |-------------------------------------------------------------------------- + */ + + // Define the parent-child relationship + @ApiPropertyOptional({ type: () => OrganizationProjectModule }) + @IsOptional() + @IsObject() + @MultiORMManyToOne(() => OrganizationProjectModule, (module) => module.children, { + onDelete: 'SET NULL' + }) + parent?: OrganizationProjectModule; + + // Define the parent-child relationship + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @MultiORMColumn({ nullable: true, relationId: true }) + parentId?: ID; + + /** + * Organization Project + */ + @ApiPropertyOptional({ type: () => Object }) + @IsOptional() + @IsObject() + @MultiORMManyToOne(() => OrganizationProject, (it) => it.modules, { + /** Indicates if the relation column value can be nullable or not. */ + nullable: true, + + /** Defines the database cascade action on delete. */ + onDelete: 'CASCADE' + }) + project?: IOrganizationProject; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @RelationId((it: OrganizationProjectModule) => it.project) + @ColumnIndex() + @MultiORMColumn({ nullable: true, relationId: true }) + projectId?: ID; + + /** + * Creator + */ + @MultiORMManyToOne(() => User, { + nullable: true, + onDelete: 'CASCADE' + }) + @JoinColumn() + creator?: IUser; + + @RelationId((it: OrganizationProjectModule) => it.creator) + @ColumnIndex() + @MultiORMColumn({ nullable: true, relationId: true }) + creatorId?: ID; + + /** + * Module manager + */ + @MultiORMManyToOne(() => User, { + nullable: true, + onDelete: 'CASCADE' + }) + @JoinColumn() + manager?: IUser; + + @RelationId((it: OrganizationProjectModule) => it.manager) + @ColumnIndex() + @MultiORMColumn({ nullable: true, relationId: true }) + managerId?: ID; + + /* + |-------------------------------------------------------------------------- + | @OneToMany + |-------------------------------------------------------------------------- + */ + + /** + * Children modules + */ + @MultiORMOneToMany(() => OrganizationProjectModule, (module) => module.parent) + children?: OrganizationProjectModule[]; + + /** + * Organization Tasks Relationship + */ + @MultiORMOneToMany(() => Task, (it) => it.projectModule) + tasks?: ITask[]; + + /* + |-------------------------------------------------------------------------- + | @ManyToMany + |-------------------------------------------------------------------------- + */ + + /** + * Organization Sprint + */ + @ApiPropertyOptional({ type: () => Array, isArray: true }) + @IsOptional() + @IsArray() + @MultiORMManyToMany(() => OrganizationSprint, (it) => it.modules, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + owner: true, + pivotTable: 'project_module_sprint', + joinColumn: 'organizationProjectModuleId', + inverseJoinColumn: 'organizationSprintId' + }) + @JoinTable({ + name: 'project_module_sprint' + }) + organizationSprints?: IOrganizationSprint[]; + + /** + * OrganizationTeam + */ + @ApiPropertyOptional({ type: () => Array, isArray: true }) + @IsOptional() + @IsArray() + @MultiORMManyToMany(() => OrganizationTeam, (it) => it.modules, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + owner: true, + pivotTable: 'project_module_team', + joinColumn: 'organizationProjectModuleId', + inverseJoinColumn: 'organizationTeamId' + }) + @JoinTable({ + name: 'project_module_team' + }) + teams?: IOrganizationTeam[]; + + /** + * Members + */ + @ApiPropertyOptional({ type: () => Array, isArray: true }) + @IsOptional() + @IsArray() + @MultiORMManyToMany(() => Employee, (employee) => employee.modules, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + owner: true, + pivotTable: 'project_module_employee', + joinColumn: 'organizationProjectModuleId', + inverseJoinColumn: 'employeeId' + }) + @JoinTable({ + name: 'project_module_employee' + }) + members?: IEmployee[]; +} diff --git a/packages/core/src/organization-project-module/organization-project-module.module.ts b/packages/core/src/organization-project-module/organization-project-module.module.ts new file mode 100644 index 00000000000..a1d90e9df04 --- /dev/null +++ b/packages/core/src/organization-project-module/organization-project-module.module.ts @@ -0,0 +1,29 @@ +import { Module } from '@nestjs/common'; +import { RouterModule } from '@nestjs/core'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; +import { OrganizationProjectModuleService } from './organization-project-module.service'; +import { OrganizationProjectModuleController } from './organization-project-module.controller'; +import { OrganizationProjectModule } from './organization-project-module.entity'; +import { TypeOrmOrganizationProjectModuleRepository } from './repository/type-orm-organization-project-module.repository'; +import { RolePermissionModule } from '../role-permission/role-permission.module'; +import { RoleModule } from '../role/role.module'; + +@Module({ + imports: [ + RouterModule.register([ + { + path: '/organization-project-modules', + module: OrganizationProjectModuleModule + } + ]), + TypeOrmModule.forFeature([OrganizationProjectModule]), + MikroOrmModule.forFeature([OrganizationProjectModule]), + MikroOrmModule, + RolePermissionModule + ], + controllers: [OrganizationProjectModuleController], + providers: [OrganizationProjectModuleService, TypeOrmOrganizationProjectModuleRepository], + exports: [TypeOrmModule, OrganizationProjectModuleService, TypeOrmOrganizationProjectModuleRepository] +}) +export class OrganizationProjectModuleModule {} diff --git a/packages/core/src/organization-project-module/organization-project-module.service.ts b/packages/core/src/organization-project-module/organization-project-module.service.ts new file mode 100644 index 00000000000..edeb0064a83 --- /dev/null +++ b/packages/core/src/organization-project-module/organization-project-module.service.ts @@ -0,0 +1,323 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { Brackets, FindManyOptions, SelectQueryBuilder, WhereExpressionBuilder } from 'typeorm'; +import { + ID, + IOrganizationProjectModule, + IOrganizationProjectModuleFindInput, + IPagination, + PermissionsEnum, + TaskStatusEnum +} from '@gauzy/contracts'; +import { isEmpty, isNotEmpty } from '@gauzy/common'; +import { isPostgres } from '@gauzy/config'; +import { PaginationParams, TenantAwareCrudService } from './../core/crud'; +import { RequestContext } from '../core/context'; +import { OrganizationProjectModule } from './organization-project-module.entity'; +import { prepareSQLQuery as p } from './../database/database.helper'; +import { TypeOrmOrganizationProjectModuleRepository } from './repository/type-orm-organization-project-module.repository'; +import { MikroOrmOrganizationProjectModuleRepository } from './repository/mikro-orm-organization-project-module.repository'; + +@Injectable() +export class OrganizationProjectModuleService extends TenantAwareCrudService { + constructor( + readonly typeOrmProjectModuleRepository: TypeOrmOrganizationProjectModuleRepository, + readonly mikroOrmProjectModuleRepository: MikroOrmOrganizationProjectModuleRepository + ) { + super(typeOrmProjectModuleRepository, mikroOrmProjectModuleRepository); + } + + /** + * @description Find employee project modules + * @param options - Options finders and relations + * @returns - A promise that resolves after found project modules + * @memberof OrganizationProjectModuleService + */ + async getEmployeeProjectModules( + options: PaginationParams + ): Promise> { + try { + const { where } = options; + const { name, status, organizationId, projectId, members } = where; + const tenantId = RequestContext.currentTenantId() || options.where.tenantId; + + // Create query builder + const query = this.typeOrmRepository.createQueryBuilder(this.tableName); + // Join employees + query.innerJoin(`${query.alias}.members`, 'members'); + + // Apply pagination and query options + this.applyPaginationAndOptions(query, options); + + query.andWhere((qb: SelectQueryBuilder) => { + const subQuery = qb.subQuery(); + subQuery + .select(p('"project_module_employee"."organizationProjectModuleId"')) + .from(p('project_module_employee'), p('project_module_employee')); + + // If user have permission to change employee + if (RequestContext.hasPermission(PermissionsEnum.CHANGE_SELECTED_EMPLOYEE)) { + if (isNotEmpty(members) && isNotEmpty(members['id'])) { + const employeeId = members['id']; + subQuery.andWhere(p('"project_module_employee"."employeeId" = :employeeId'), { employeeId }); + } + } else { + // If employee has login and don't have permission to change employee + const employeeId = RequestContext.currentEmployeeId(); + if (isNotEmpty(employeeId)) { + subQuery.andWhere(p('"project_module_employee"."employeeId" = :employeeId'), { employeeId }); + } + } + return ( + p('"organization_project_module_members"."organizationProjectModuleId" IN ') + + subQuery.distinct(true).getQuery() + ); + }); + query.andWhere( + new Brackets((qb: WhereExpressionBuilder) => { + qb.andWhere(p(`"${query.alias}"."organizationId" = :organizationId`), { organizationId }); + qb.andWhere(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }); + }) + ); + query.andWhere( + new Brackets((qb: WhereExpressionBuilder) => { + // Apply optional filters + const filters: IOrganizationProjectModuleFindInput = { + status: status as TaskStatusEnum, + projectId: projectId as ID, + name: name as string + }; + + // Apply optional filters + this.applyOptionalFilters(query, qb, filters); + }) + ); + + console.log('Get Employees modules', query.getSql()); // Query logs for debugging + + // Execute the query with pagination + return await this.executePaginationQuery(query); + } catch (error) { + // Error logging for debugging + throw new HttpException( + `Error while retrieving employee project modules: ${error.message}`, + HttpStatus.BAD_REQUEST + ); + } + } + + /** + * @description Find Team's project modules + * @param options - Options finders and relations + * @returns - A promise that resolves after found project modules + * @memberof OrganizationProjectModuleService + */ + async findTeamProjectModules( + options: PaginationParams + ): Promise> { + try { + const { where } = options; + const { name, status, teams = [], organizationId, projectId, members } = where; + const tenantId = RequestContext.currentTenantId() || where.tenantId; + + // Create query builder + const query = this.typeOrmRepository.createQueryBuilder(this.tableName); + + // Join teams + query.leftJoin(`${query.alias}.teams`, 'teams'); + + // Apply pagination and query options + this.applyPaginationAndOptions(query, options); + + query.andWhere((qb: SelectQueryBuilder) => { + const subQuery = qb.subQuery(); + subQuery + .select(p('"project_module_team"."organizationProjectModuleId"')) + .from(p('project_module_team'), p('project_module_team')); + subQuery.leftJoin( + 'organization_team_employee', + 'organization_team_employee', + p('"organization_team_employee"."organizationTeamId" = "project_module_team"."organizationTeamId"') + ); + // If user have permission to change employee + if (RequestContext.hasPermission(PermissionsEnum.CHANGE_SELECTED_EMPLOYEE)) { + if (isNotEmpty(members) && isNotEmpty(members['id'])) { + const employeeId = members['id']; + subQuery.andWhere(p('"organization_team_employee"."employeeId" = :employeeId'), { employeeId }); + } + } else { + // If employee has login and don't have permission to change employee + const employeeId = RequestContext.currentEmployeeId(); + if (isNotEmpty(employeeId)) { + subQuery.andWhere(p('"organization_team_employee"."employeeId" = :employeeId'), { employeeId }); + } + } + if (isNotEmpty(teams)) { + subQuery.andWhere(p(`"${subQuery.alias}"."organizationTeamId" IN (:...teams)`), { teams }); + } + return ( + p(`"organization_project_module_members"."organizationProjectModuleId" IN `) + + subQuery.distinct(true).getQuery() + ); + }); + query.andWhere( + new Brackets((qb: WhereExpressionBuilder) => { + qb.andWhere(p(`"${query.alias}"."organizationId" = :organizationId`), { organizationId }); + qb.andWhere(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }); + }) + ); + if (isNotEmpty(projectId) && isNotEmpty(teams)) { + query.orWhere(p(`"${query.alias}"."projectId" = :projectId`), { projectId }); + } + query.andWhere( + new Brackets((qb: WhereExpressionBuilder) => { + if (isNotEmpty(projectId) && isEmpty(teams)) { + qb.andWhere(p(`"${query.alias}"."projectId" = :projectId`), { projectId }); + } + + // Apply optional filters + const filters: IOrganizationProjectModuleFindInput = { + status: status as TaskStatusEnum, + name: name as string + }; + + // Apply optional filters + this.applyOptionalFilters(query, qb, filters); + }) + ); + + console.log('Get Employees modules', query.getSql()); // Query logs for debugging + + // Execute the query with pagination + return await this.executePaginationQuery(query); + } catch (error) { + // Error logging for debugging + throw new HttpException( + `Error while retrieving organization team project modules: ${error.message}`, + HttpStatus.BAD_REQUEST + ); + } + } + + /** + * @description Find project modules by employee + * @param employeeId - The employee ID for whom to search project modules + * @param options - Finders options + * @returns A promise that resolves after found project modules + * @memberof OrganizationProjectModuleService + */ + async findByEmployee( + employeeId: ID, + options: IOrganizationProjectModuleFindInput + ): Promise> { + try { + const tenantId = RequestContext.currentTenantId() || options?.tenantId; + const organizationId = options?.organizationId; + + // Create query builder + const query = this.typeOrmRepository.createQueryBuilder(this.tableName); + + // Joins and where clauses + query.innerJoin(`${query.alias}.members`, 'member'); + query.leftJoin(`${query.alias}.teams`, 'project_team'); + query.leftJoin(`${query.alias}."organizationSprints"`, 'sprint'); + + query.andWhere( + new Brackets((qb: WhereExpressionBuilder) => { + qb.andWhere(p('member.id = :employeeId'), { employeeId }) + .andWhere(p(`"${query.alias}"."tenantId" = :tenantId`), { tenantId }) + .andWhere(p(`"${query.alias}"."organizationId" = :organizationId`), { organizationId }); + + // Apply optional filters + this.applyOptionalFilters(query, qb, options); + }) + ); + + // Execute the query with pagination + return await this.executePaginationQuery(query); + } catch (error) { + // Error logging for debugging + throw new HttpException( + `Error while retrieving organization project modules by employee: ${error.message}`, + HttpStatus.BAD_REQUEST + ); + } + } + + /** + * Apply pagination and query options + * + * @param query - The query builder to apply pagination and options + * @param options - Pagination and query options + */ + private applyPaginationAndOptions( + query: SelectQueryBuilder, + params: PaginationParams + ) { + if (isNotEmpty(params)) { + const options: FindManyOptions = {}; + + if ('skip' in params) { + options.skip = (params.take || 10) * (params.skip - 1); + options.take = params.take || 10; + } + + if (params.select) { + options.select = params.select; + } + + if (params.relations) { + options.relations = params.relations; + } + + if (params.order) { + options.order = params.order; + } + + // Apply pagination and query options + query.setFindOptions(options); + } + } + + /**' + * Apply optional filters to the query builder + */ + private applyOptionalFilters( + query: SelectQueryBuilder, + qb: WhereExpressionBuilder, + options: IOrganizationProjectModuleFindInput + ) { + const { projectId, status, name, organizationSprintId, organizationTeamId } = options; + + if (isNotEmpty(projectId)) { + qb.andWhere(p(`"${query.alias}"."projectId" = :projectId`), { projectId }); + } + + if (isNotEmpty(status)) { + qb.andWhere(p(`"${query.alias}"."status" = :status`), { status }); + } + + if (isNotEmpty(name)) { + const like = isPostgres() ? 'ILIKE' : 'LIKE'; + qb.andWhere(p(`"${query.alias}"."name" ${like} :name`), { name: `%${name}%` }); + } + + if (isNotEmpty(organizationSprintId)) { + qb.andWhere('sprint.id = :organizationSprintId', { organizationSprintId }); + } + + if (isNotEmpty(organizationTeamId)) { + qb.andWhere('project_team.id = :organizationTeamId', { organizationTeamId }); + } + } + + /** + * Executes the given query with pagination and returns the results. + * + * @param query The query builder instance to execute. + * @returns A promise that resolves to an object containing the paginated items and total count. + */ + async executePaginationQuery(query: SelectQueryBuilder): Promise> { + const [items, total] = await query.getManyAndCount(); + return { items, total }; + } +} diff --git a/packages/core/src/organization-project-module/repository/index.ts b/packages/core/src/organization-project-module/repository/index.ts new file mode 100644 index 00000000000..e67e2f28d48 --- /dev/null +++ b/packages/core/src/organization-project-module/repository/index.ts @@ -0,0 +1,2 @@ +export * from './mikro-orm-organization-project-module.repository'; +export * from './type-orm-organization-project-module.repository'; diff --git a/packages/core/src/organization-project-module/repository/mikro-orm-organization-project-module.repository.ts b/packages/core/src/organization-project-module/repository/mikro-orm-organization-project-module.repository.ts new file mode 100644 index 00000000000..08e1d2332f4 --- /dev/null +++ b/packages/core/src/organization-project-module/repository/mikro-orm-organization-project-module.repository.ts @@ -0,0 +1,4 @@ +import { MikroOrmBaseEntityRepository } from '../../core/repository/mikro-orm-base-entity.repository'; +import { OrganizationProjectModule } from '../organization-project-module.entity'; + +export class MikroOrmOrganizationProjectModuleRepository extends MikroOrmBaseEntityRepository {} diff --git a/packages/core/src/organization-project-module/repository/type-orm-organization-project-module.repository.ts b/packages/core/src/organization-project-module/repository/type-orm-organization-project-module.repository.ts new file mode 100644 index 00000000000..6dd0b708838 --- /dev/null +++ b/packages/core/src/organization-project-module/repository/type-orm-organization-project-module.repository.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { OrganizationProjectModule } from '../organization-project-module.entity'; + +@Injectable() +export class TypeOrmOrganizationProjectModuleRepository extends Repository { + constructor( + @InjectRepository(OrganizationProjectModule) readonly repository: Repository + ) { + super(repository.target, repository.manager, repository.queryRunner); + } +} diff --git a/packages/core/src/organization-project/organization-project.entity.ts b/packages/core/src/organization-project/organization-project.entity.ts index 5775113d7e0..7d1c959f999 100644 --- a/packages/core/src/organization-project/organization-project.entity.ts +++ b/packages/core/src/organization-project/organization-project.entity.ts @@ -11,6 +11,7 @@ import { IInvoiceItem, IOrganizationContact, IOrganizationProject, + IOrganizationProjectModule, IOrganizationSprint, IOrganizationTeam, IPayment, @@ -35,6 +36,7 @@ import { ImageAsset, InvoiceItem, OrganizationContact, + OrganizationProjectModule, OrganizationSprint, OrganizationTeam, Payment, @@ -296,6 +298,12 @@ export class OrganizationProject @MultiORMOneToMany(() => TaskVersion, (it) => it.project) versions?: ITaskVersion[]; + /** + * Organization modules Relationship + */ + @MultiORMOneToMany(() => OrganizationProjectModule, (it) => it.project) + modules?: IOrganizationProjectModule[]; + /* |-------------------------------------------------------------------------- | @ManyToMany diff --git a/packages/core/src/organization-sprint/organization-sprint.entity.ts b/packages/core/src/organization-sprint/organization-sprint.entity.ts index 4a756b99d33..cb8aacaa473 100644 --- a/packages/core/src/organization-sprint/organization-sprint.entity.ts +++ b/packages/core/src/organization-sprint/organization-sprint.entity.ts @@ -1,19 +1,20 @@ import { JoinColumn } from 'typeorm'; -import { IOrganizationSprint, SprintStartDayEnum } from '@gauzy/contracts'; +import { IOrganizationProjectModule, IOrganizationSprint, SprintStartDayEnum } from '@gauzy/contracts'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsDate, - IsNotEmpty, - IsNumber, - IsOptional, - IsString -} from 'class-validator'; +import { IsDate, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; import { OrganizationProject, + OrganizationProjectModule, Task, TenantOrganizationBaseEntity } from '../core/entities/internal'; -import { MultiORMColumn, MultiORMEntity, MultiORMManyToOne, MultiORMOneToMany } from './../core/decorators/entity'; +import { + MultiORMColumn, + MultiORMEntity, + MultiORMManyToMany, + MultiORMManyToOne, + MultiORMOneToMany +} from './../core/decorators/entity'; import { MikroOrmOrganizationSprintRepository } from './repository/mikro-orm-organization-sprint.repository'; @MultiORMEntity('organization_sprint', { mikroOrmRepository: () => MikroOrmOrganizationSprintRepository }) @@ -67,12 +68,11 @@ export class OrganizationSprint extends TenantOrganizationBaseEntity implements nullable: true, /** Defines the database cascade action on delete. */ - onDelete: 'CASCADE', + onDelete: 'CASCADE' }) @JoinColumn() project?: OrganizationProject; - @ApiProperty({ type: () => String }) @IsString() @IsNotEmpty() @@ -89,4 +89,21 @@ export class OrganizationSprint extends TenantOrganizationBaseEntity implements @MultiORMOneToMany(() => Task, (task) => task.organizationSprint) @JoinColumn() tasks?: Task[]; + + /* + |-------------------------------------------------------------------------- + | @ManyToMany + |-------------------------------------------------------------------------- + */ + + /** + * Organization Project Module + */ + @MultiORMManyToMany(() => OrganizationProjectModule, (it) => it.organizationSprints, { + /** Defines the database action to perform on update. */ + onUpdate: 'CASCADE', + /** Defines the database cascade action on delete. */ + onDelete: 'CASCADE' + }) + modules?: IOrganizationProjectModule[]; } diff --git a/packages/core/src/organization-team/organization-team.entity.ts b/packages/core/src/organization-team/organization-team.entity.ts index 689fab8134e..660e577ed48 100644 --- a/packages/core/src/organization-team/organization-team.entity.ts +++ b/packages/core/src/organization-team/organization-team.entity.ts @@ -7,6 +7,7 @@ import { IImageAsset, IIssueType, IOrganizationProject, + IOrganizationProjectModule, IOrganizationTeam, IOrganizationTeamEmployee, IRequestApprovalTeam, @@ -28,6 +29,7 @@ import { ImageAsset, IssueType, OrganizationProject, + OrganizationProjectModule, OrganizationTeamEmployee, RequestApprovalTeam, Tag, @@ -304,6 +306,17 @@ export class OrganizationTeam extends TenantOrganizationBaseEntity implements IO @JoinTable() tasks?: ITask[]; + /** + * Organization Project Module + */ + @MultiORMManyToMany(() => OrganizationProjectModule, (it) => it.teams, { + /** Defines the database action to perform on update. */ + onUpdate: 'CASCADE', + /** Defines the database cascade action on delete. */ + onDelete: 'CASCADE' + }) + modules?: IOrganizationProjectModule[]; + /** * Equipment Sharing */ diff --git a/packages/core/src/role-permission/default-role-permissions.ts b/packages/core/src/role-permission/default-role-permissions.ts index 9393ac17eed..13ff04aaecf 100644 --- a/packages/core/src/role-permission/default-role-permissions.ts +++ b/packages/core/src/role-permission/default-role-permissions.ts @@ -105,6 +105,13 @@ export const DEFAULT_ROLE_PERMISSIONS = [ PermissionsEnum.DAILY_PLAN_DELETE, /** Daily CRUD Permissions End */ + /** Project Module Permissions start */ + PermissionsEnum.PROJECT_MODULE_CREATE, + PermissionsEnum.PROJECT_MODULE_READ, + PermissionsEnum.PROJECT_MODULE_UPDATE, + PermissionsEnum.PROJECT_MODULE_DELETE, + /** Project Module Permissions start */ + /** Organization Team */ PermissionsEnum.ORG_TEAM_ADD, PermissionsEnum.ORG_TEAM_VIEW, @@ -269,6 +276,13 @@ export const DEFAULT_ROLE_PERMISSIONS = [ PermissionsEnum.DAILY_PLAN_DELETE, /** Daily CRUD Permissions End */ + /** Project Module Permissions start */ + PermissionsEnum.PROJECT_MODULE_CREATE, + PermissionsEnum.PROJECT_MODULE_READ, + PermissionsEnum.PROJECT_MODULE_UPDATE, + PermissionsEnum.PROJECT_MODULE_DELETE, + /** Project Module Permissions start */ + /** Organization Team */ PermissionsEnum.ORG_TEAM_ADD, PermissionsEnum.ORG_TEAM_VIEW, @@ -352,6 +366,10 @@ export const DEFAULT_ROLE_PERMISSIONS = [ PermissionsEnum.ORG_TASK_VIEW, PermissionsEnum.ORG_TASK_EDIT, PermissionsEnum.ORG_TASK_DELETE, + PermissionsEnum.PROJECT_MODULE_CREATE, + PermissionsEnum.PROJECT_MODULE_READ, + PermissionsEnum.PROJECT_MODULE_UPDATE, + PermissionsEnum.PROJECT_MODULE_DELETE, PermissionsEnum.ORG_CANDIDATES_TASK_EDIT, PermissionsEnum.ORG_CANDIDATES_INTERVIEW_EDIT, PermissionsEnum.ORG_CANDIDATES_INTERVIEW_VIEW, @@ -402,6 +420,11 @@ export const DEFAULT_ROLE_PERMISSIONS = [ PermissionsEnum.DAILY_PLAN_READ, PermissionsEnum.DAILY_PLAN_UPDATE, PermissionsEnum.DAILY_PLAN_DELETE, + /** Project Module */ + PermissionsEnum.PROJECT_MODULE_CREATE, + PermissionsEnum.PROJECT_MODULE_READ, + PermissionsEnum.PROJECT_MODULE_UPDATE, + PermissionsEnum.PROJECT_MODULE_DELETE, /** Organization Team */ PermissionsEnum.ORG_TEAM_ADD, PermissionsEnum.ORG_TEAM_VIEW, diff --git a/packages/core/src/tasks/task.entity.ts b/packages/core/src/tasks/task.entity.ts index a18e0b3558f..0ee1eefdf5b 100644 --- a/packages/core/src/tasks/task.entity.ts +++ b/packages/core/src/tasks/task.entity.ts @@ -4,10 +4,12 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsArray, IsBoolean, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; import { IActivity, + ID, IDailyPlan, IEmployee, IInvoiceItem, IOrganizationProject, + IOrganizationProjectModule, IOrganizationSprint, IOrganizationTeam, ITag, @@ -28,6 +30,7 @@ import { Employee, InvoiceItem, OrganizationProject, + OrganizationProjectModule, OrganizationSprint, OrganizationTeam, OrganizationTeamEmployee, @@ -174,7 +177,7 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @IsOptional() @IsUUID() @MultiORMColumn({ nullable: true, relationId: true }) - parentId?: Task['id']; + parentId?: ID; /** * Organization Project @@ -197,7 +200,30 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @RelationId((it: Task) => it.project) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - projectId?: IOrganizationProject['id']; + projectId?: ID; + + /** + * Organization Project Module + */ + @ApiPropertyOptional({ type: () => Object }) + @IsOptional() + @IsObject() + @MultiORMManyToOne(() => OrganizationProjectModule, (it) => it.tasks, { + /** Indicates if the relation column value can be nullable or not. */ + nullable: true, + + /** Defines the database cascade action on delete. */ + onDelete: 'CASCADE' + }) + projectModule?: IOrganizationProjectModule; + + @ApiPropertyOptional({ type: () => String }) + @IsOptional() + @IsUUID() + @RelationId((it: Task) => it.projectModule) + @ColumnIndex() + @MultiORMColumn({ nullable: true, relationId: true }) + projectModuleId?: ID; /** * Creator @@ -212,7 +238,7 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @RelationId((it: Task) => it.creator) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - creatorId?: IUser['id']; + creatorId?: ID; /** * Organization Sprint @@ -230,7 +256,7 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @RelationId((it: Task) => it.organizationSprint) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - organizationSprintId?: IOrganizationSprint['id']; + organizationSprintId?: ID; /** * Task Status @@ -250,7 +276,7 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @RelationId((it: Task) => it.taskStatus) @ColumnIndex() @MultiORMColumn({ nullable: true, type: 'varchar', relationId: true }) - taskStatusId?: ITaskStatus['id']; + taskStatusId?: ID; /** * Task Size @@ -270,7 +296,7 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @RelationId((it: Task) => it.taskSize) @ColumnIndex() @MultiORMColumn({ nullable: true, type: 'varchar', relationId: true }) - taskSizeId?: ITaskSize['id']; + taskSizeId?: ID; /** * Task Priority @@ -290,7 +316,7 @@ export class Task extends TenantOrganizationBaseEntity implements ITask { @RelationId((it: Task) => it.taskPriority) @ColumnIndex() @MultiORMColumn({ nullable: true, type: 'varchar', relationId: true }) - taskPriorityId?: ITaskPriority['id']; + taskPriorityId?: ID; /* |-------------------------------------------------------------------------- diff --git a/packages/core/src/time-tracking/time-log/time-log.entity.ts b/packages/core/src/time-tracking/time-log/time-log.entity.ts index 3b8a4cf4729..1f1ff927477 100644 --- a/packages/core/src/time-tracking/time-log/time-log.entity.ts +++ b/packages/core/src/time-tracking/time-log/time-log.entity.ts @@ -26,12 +26,18 @@ import { Timesheet, TimeSlot } from './../../core/entities/internal'; -import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToMany, MultiORMManyToOne, VirtualMultiOrmColumn } from '../../core/decorators/entity'; +import { + ColumnIndex, + MultiORMColumn, + MultiORMEntity, + MultiORMManyToMany, + MultiORMManyToOne, + VirtualMultiOrmColumn +} from '../../core/decorators/entity'; import { MikroOrmTimeLogRepository } from './repository/mikro-orm-time-log.repository'; @MultiORMEntity('time_log', { mikroOrmRepository: () => MikroOrmTimeLogRepository }) export class TimeLog extends TenantOrganizationBaseEntity implements ITimeLog { - @ApiProperty({ type: () => 'timestamptz' }) @IsDateString() @ColumnIndex() @@ -165,7 +171,7 @@ export class TimeLog extends TenantOrganizationBaseEntity implements ITimeLog { nullable: true, /** Defines the database cascade action on delete. */ - onDelete: 'SET NULL', + onDelete: 'SET NULL' }) @JoinColumn() project?: IOrganizationProject; @@ -189,7 +195,7 @@ export class TimeLog extends TenantOrganizationBaseEntity implements ITimeLog { nullable: true, /** Defines the database cascade action on delete. */ - onDelete: 'SET NULL', + onDelete: 'SET NULL' }) @JoinColumn() task?: ITask; @@ -210,7 +216,7 @@ export class TimeLog extends TenantOrganizationBaseEntity implements ITimeLog { nullable: true, /** Defines the database cascade action on delete. */ - onDelete: 'SET NULL', + onDelete: 'SET NULL' }) @JoinColumn() organizationContact?: IOrganizationContact; @@ -231,7 +237,7 @@ export class TimeLog extends TenantOrganizationBaseEntity implements ITimeLog { nullable: true, /** Defines the database cascade action on delete. */ - onDelete: 'SET NULL', + onDelete: 'SET NULL' }) @JoinColumn() organizationTeam?: IOrganizationTeam; diff --git a/packages/ui-core/i18n/assets/i18n/ar.json b/packages/ui-core/i18n/assets/i18n/ar.json index 790cc131ea4..16c542b0701 100644 --- a/packages/ui-core/i18n/assets/i18n/ar.json +++ b/packages/ui-core/i18n/assets/i18n/ar.json @@ -2079,6 +2079,10 @@ "ORG_PROJECT_DELETE": "حذف المشاريع", "ORG_CONTACT_EDIT": "إنشاء/تعديل جهات الاتصال", "ORG_CONTACT_VIEW": "عرض جهات الاتصال", + "PROJECT_MODULE_CREATE": "إنشاء وحدات المشروع", + "PROJECT_MODULE_READ": "عرض وحدات المشروع", + "PROJECT_MODULE_UPDATE": "تحديث وحدات المشروع", + "PROJECT_MODULE_DELETE": "حذف وحدات المشروع", "ORG_TEAM_ADD": "إضافة فِرَق", "ORG_TEAM_VIEW": "عرض الفرق", "ORG_TEAM_EDIT_ACTIVE_TASK": "تحرير المهام النشطة", diff --git a/packages/ui-core/i18n/assets/i18n/bg.json b/packages/ui-core/i18n/assets/i18n/bg.json index adecfe9a631..51b52f3cfb5 100644 --- a/packages/ui-core/i18n/assets/i18n/bg.json +++ b/packages/ui-core/i18n/assets/i18n/bg.json @@ -2110,6 +2110,10 @@ "ORG_PROJECT_EDIT": "Create/Edit Projects", "ORG_CONTACT_EDIT": "Create/Edit Contacts", "ORG_CONTACT_VIEW": "View Contacts", + "PROJECT_MODULE_CREATE": "Създаване на модули на проекта", + "PROJECT_MODULE_READ": "Преглед на модули на проекта", + "PROJECT_MODULE_UPDATE": "Актуализиране на модули на проекта", + "PROJECT_MODULE_DELETE": "Изтриване на модули на проекта", "ORG_TEAM_ADD": "Add Teams", "ORG_TEAM_VIEW": "View Teams", "ORG_TEAM_EDIT_ACTIVE_TASK": "Редактиране на активни задачи", diff --git a/packages/ui-core/i18n/assets/i18n/de.json b/packages/ui-core/i18n/assets/i18n/de.json index e675a392ac1..8a863321fac 100644 --- a/packages/ui-core/i18n/assets/i18n/de.json +++ b/packages/ui-core/i18n/assets/i18n/de.json @@ -2076,6 +2076,10 @@ "ORG_PROJECT_DELETE": "Projekte löschen", "ORG_CONTACT_EDIT": "Kontakte erstellen/bearbeiten", "ORG_CONTACT_VIEW": "Kontakte anzeigen", + "PROJECT_MODULE_CREATE": "Projekmodule erstellen", + "PROJECT_MODULE_READ": "Projekmodule anzeigen", + "PROJECT_MODULE_UPDATE": "Projekmodule aktualisieren", + "PROJECT_MODULE_DELETE": "Projekmodule löschen", "ORG_TEAM_ADD": "Teams hinzufügen", "ORG_TEAM_VIEW": "Teams anzeigen", "ORG_TEAM_EDIT_ACTIVE_TASK": "Aktive Aufgaben bearbeiten", diff --git a/packages/ui-core/i18n/assets/i18n/en.json b/packages/ui-core/i18n/assets/i18n/en.json index 81f11bda40c..29680bfe99a 100644 --- a/packages/ui-core/i18n/assets/i18n/en.json +++ b/packages/ui-core/i18n/assets/i18n/en.json @@ -2193,6 +2193,10 @@ "DAILY_PLAN_READ": "View Daily Plans", "DAILY_PLAN_UPDATE": "Update Daily Plans", "DAILY_PLAN_DELETE": "Delete Daily Plans", + "PROJECT_MODULE_CREATE": "Create Project Modules", + "PROJECT_MODULE_READ": "View Project Modules", + "PROJECT_MODULE_UPDATE": "Update Project Modules", + "PROJECT_MODULE_DELETE": "Delete Project Modules", "ORG_TEAM_ADD": "Add Teams", "ORG_TEAM_VIEW": "View Teams", "ORG_TEAM_EDIT_ACTIVE_TASK": "Edit Active Tasks", diff --git a/packages/ui-core/i18n/assets/i18n/es.json b/packages/ui-core/i18n/assets/i18n/es.json index ae8e34ed736..76388ce2d7d 100644 --- a/packages/ui-core/i18n/assets/i18n/es.json +++ b/packages/ui-core/i18n/assets/i18n/es.json @@ -2082,6 +2082,10 @@ "ORG_PROJECT_DELETE": "Eliminar Proyectos", "ORG_CONTACT_EDIT": "Crear/Editar Contactos", "ORG_CONTACT_VIEW": "Ver contactos", + "PROJECT_MODULE_CREATE": "Crear módulos de proyecto", + "PROJECT_MODULE_READ": "Ver módulos de proyecto", + "PROJECT_MODULE_UPDATE": "Actualizar módulos de proyecto", + "PROJECT_MODULE_DELETE": "Eliminar módulos de proyecto", "ORG_TEAM_ADD": "Agregar equipos", "ORG_TEAM_VIEW": "Ver Equipos", "ORG_TEAM_EDIT_ACTIVE_TASK": "Editar tareas activas", diff --git a/packages/ui-core/i18n/assets/i18n/fr.json b/packages/ui-core/i18n/assets/i18n/fr.json index 6c893dee232..8ca2dd632f4 100644 --- a/packages/ui-core/i18n/assets/i18n/fr.json +++ b/packages/ui-core/i18n/assets/i18n/fr.json @@ -2081,6 +2081,10 @@ "ORG_PROJECT_DELETE": "Supprimer les projets", "ORG_CONTACT_EDIT": "Créer/Modifier des contacts", "ORG_CONTACT_VIEW": "Voir les contacts", + "PROJECT_MODULE_CREATE": "Créer des modules de projet", + "PROJECT_MODULE_READ": "Voir les modules de projet", + "PROJECT_MODULE_UPDATE": "Mettre à jour les modules de projet", + "PROJECT_MODULE_DELETE": "Supprimer les modules de projet", "ORG_TEAM_ADD": "Ajouter des équipes", "ORG_TEAM_VIEW": "Voir les équipes", "ORG_TEAM_EDIT_ACTIVE_TASK": "Modifier les tâches actives", diff --git a/packages/ui-core/i18n/assets/i18n/he.json b/packages/ui-core/i18n/assets/i18n/he.json index 37314025b6e..fdc13b48f7f 100644 --- a/packages/ui-core/i18n/assets/i18n/he.json +++ b/packages/ui-core/i18n/assets/i18n/he.json @@ -2103,6 +2103,10 @@ "ORG_PROJECT_EDIT": "Create/Edit Projects", "ORG_CONTACT_EDIT": "Create/Edit Contacts", "ORG_CONTACT_VIEW": "View Contacts", + "PROJECT_MODULE_CREATE": "צור מודולי פרויקט", + "PROJECT_MODULE_READ": "צפה במודולי פרויקט", + "PROJECT_MODULE_UPDATE": "עדכן מודולי פרויקט", + "PROJECT_MODULE_DELETE": "מחק מודולי פרויקט", "ORG_TEAM_ADD": "Add Teams", "ORG_TEAM_VIEW": "View Teams", "ORG_TEAM_EDIT_ACTIVE_TASK": "ערוך משימות פעילות", diff --git a/packages/ui-core/i18n/assets/i18n/it.json b/packages/ui-core/i18n/assets/i18n/it.json index 0ac656c2738..99c45a5429a 100644 --- a/packages/ui-core/i18n/assets/i18n/it.json +++ b/packages/ui-core/i18n/assets/i18n/it.json @@ -2080,6 +2080,10 @@ "ORG_PROJECT_DELETE": "Elimina progetti", "ORG_CONTACT_EDIT": "Crea/Modifica Contatti", "ORG_CONTACT_VIEW": "Visualizza contatti", + "PROJECT_MODULE_CREATE": "Crea moduli di progetto", + "PROJECT_MODULE_READ": "Visualizza moduli di progetto", + "PROJECT_MODULE_UPDATE": "Aggiorna moduli di progetto", + "PROJECT_MODULE_DELETE": "Elimina moduli di progetto", "ORG_TEAM_ADD": "Aggiungi Teams", "ORG_TEAM_VIEW": "Visualizza squadre", "ORG_TEAM_EDIT_ACTIVE_TASK": "Modifica attività attive", diff --git a/packages/ui-core/i18n/assets/i18n/nl.json b/packages/ui-core/i18n/assets/i18n/nl.json index 7752f2d8476..b93aa5c4fbe 100644 --- a/packages/ui-core/i18n/assets/i18n/nl.json +++ b/packages/ui-core/i18n/assets/i18n/nl.json @@ -2080,6 +2080,10 @@ "ORG_PROJECT_DELETE": "Verwijder Projecten", "ORG_CONTACT_EDIT": "Maak/bewerk contacten", "ORG_CONTACT_VIEW": "Bekijk contacten", + "PROJECT_MODULE_CREATE": "Projectmodules aanmaken", + "PROJECT_MODULE_READ": "Projectmodules bekijken", + "PROJECT_MODULE_UPDATE": "Projectmodules bijwerken", + "PROJECT_MODULE_DELETE": "Projectmodules verwijderen", "ORG_TEAM_ADD": "Voeg Teams toe", "ORG_TEAM_VIEW": "Bekijk Teams", "ORG_TEAM_EDIT_ACTIVE_TASK": "Bewerk actieve taken", diff --git a/packages/ui-core/i18n/assets/i18n/pl.json b/packages/ui-core/i18n/assets/i18n/pl.json index 308bdbb0b01..6d47935b3f8 100644 --- a/packages/ui-core/i18n/assets/i18n/pl.json +++ b/packages/ui-core/i18n/assets/i18n/pl.json @@ -2080,6 +2080,10 @@ "ORG_PROJECT_DELETE": "Usuń Projekty", "ORG_CONTACT_EDIT": "Utwórz/edytuj kontakty", "ORG_CONTACT_VIEW": "Wyświetl kontakty", + "PROJECT_MODULE_CREATE": "Utwórz moduły projektu", + "PROJECT_MODULE_READ": "Zobacz moduły projektu", + "PROJECT_MODULE_UPDATE": "Aktualizuj moduły projektu", + "PROJECT_MODULE_DELETE": "Usuń moduły projektu", "ORG_TEAM_ADD": "Dodaj zespoły", "ORG_TEAM_VIEW": "Wyświetl zespoły", "ORG_TEAM_EDIT_ACTIVE_TASK": "Edytuj aktywne zadania", diff --git a/packages/ui-core/i18n/assets/i18n/pt.json b/packages/ui-core/i18n/assets/i18n/pt.json index 6eac14b6b29..74753029b23 100644 --- a/packages/ui-core/i18n/assets/i18n/pt.json +++ b/packages/ui-core/i18n/assets/i18n/pt.json @@ -2080,6 +2080,10 @@ "ORG_PROJECT_DELETE": "Excluir Projetos.", "ORG_CONTACT_EDIT": "Criar/Editar contatos", "ORG_CONTACT_VIEW": "Visualizar contatos", + "PROJECT_MODULE_CREATE": "Criar módulos de projeto", + "PROJECT_MODULE_READ": "Ver módulos de projeto", + "PROJECT_MODULE_UPDATE": "Atualizar módulos de projeto", + "PROJECT_MODULE_DELETE": "Excluir módulos de projeto", "ORG_TEAM_ADD": "Adicionar equipes", "ORG_TEAM_VIEW": "Ver Equipes", "ORG_TEAM_EDIT_ACTIVE_TASK": "Editar tarefas ativas", diff --git a/packages/ui-core/i18n/assets/i18n/ru.json b/packages/ui-core/i18n/assets/i18n/ru.json index 033b44d51cf..23e1e3ef740 100644 --- a/packages/ui-core/i18n/assets/i18n/ru.json +++ b/packages/ui-core/i18n/assets/i18n/ru.json @@ -2110,6 +2110,10 @@ "ORG_PROJECT_EDIT": "Создание/Редактирование проектов", "ORG_CONTACT_EDIT": "Создать/редактировать контакты", "ORG_CONTACT_VIEW": "Контакты", + "PROJECT_MODULE_CREATE": "Создать модули проекта", + "PROJECT_MODULE_READ": "Просмотреть модули проекта", + "PROJECT_MODULE_UPDATE": "Обновить модули проекта", + "PROJECT_MODULE_DELETE": "Удалить модули проекта", "ORG_TEAM_ADD": "Добавить команды", "ORG_TEAM_VIEW": "Посмотреть команды", "ORG_TEAM_EDIT_ACTIVE_TASK": "Редактировать активные задачи", diff --git a/packages/ui-core/i18n/assets/i18n/zh.json b/packages/ui-core/i18n/assets/i18n/zh.json index 8ad06762807..42a668fe972 100644 --- a/packages/ui-core/i18n/assets/i18n/zh.json +++ b/packages/ui-core/i18n/assets/i18n/zh.json @@ -2080,6 +2080,10 @@ "ORG_PROJECT_DELETE": "删除项目", "ORG_CONTACT_EDIT": "创建/编辑联系人", "ORG_CONTACT_VIEW": "查看联系人", + "PROJECT_MODULE_CREATE": "创建项目模块", + "PROJECT_MODULE_READ": "查看项目模块", + "PROJECT_MODULE_UPDATE": "更新项目模块", + "PROJECT_MODULE_DELETE": "删除项目模块", "ORG_TEAM_ADD": "添加团队", "ORG_TEAM_VIEW": "查看团队", "ORG_TEAM_EDIT_ACTIVE_TASK": "编辑活动任务",