rez_core 6.5.72 → 6.5.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/module/app_master/service/app-master.service.d.ts +3 -3
  2. package/dist/module/app_master/service/app-master.service.js +5 -5
  3. package/dist/module/app_master/service/app-master.service.js.map +1 -1
  4. package/dist/module/enterprise/controller/organization.controller.d.ts +1 -1
  5. package/dist/module/enterprise/controller/organization.controller.js +5 -1
  6. package/dist/module/enterprise/controller/organization.controller.js.map +1 -1
  7. package/dist/module/enterprise/service/organization.service.d.ts +9 -1
  8. package/dist/module/enterprise/service/organization.service.js +72 -7
  9. package/dist/module/enterprise/service/organization.service.js.map +1 -1
  10. package/dist/module/listmaster/entity/list-master.entity.d.ts +1 -0
  11. package/dist/module/listmaster/entity/list-master.entity.js +4 -0
  12. package/dist/module/listmaster/entity/list-master.entity.js.map +1 -1
  13. package/dist/module/listmaster/service/list-master.service.js.map +1 -1
  14. package/dist/module/module/controller/menu.controller.d.ts +6 -1
  15. package/dist/module/module/controller/menu.controller.js +20 -2
  16. package/dist/module/module/controller/menu.controller.js.map +1 -1
  17. package/dist/module/module/module.module.js +2 -0
  18. package/dist/module/module/module.module.js.map +1 -1
  19. package/dist/module/module/service/module-import.service.d.ts +18 -0
  20. package/dist/module/module/service/module-import.service.js +203 -0
  21. package/dist/module/module/service/module-import.service.js.map +1 -0
  22. package/dist/module/user/service/role.service.js +2 -3
  23. package/dist/module/user/service/role.service.js.map +1 -1
  24. package/dist/module/user/service/user-role-mapping.service.d.ts +3 -0
  25. package/dist/module/user/service/user-role-mapping.service.js +3 -0
  26. package/dist/module/user/service/user-role-mapping.service.js.map +1 -1
  27. package/dist/tsconfig.build.tsbuildinfo +1 -1
  28. package/package.json +3 -2
  29. package/src/module/app_master/service/app-master.service.ts +3 -2
  30. package/src/module/enterprise/controller/organization.controller.ts +9 -2
  31. package/src/module/enterprise/service/organization.service.ts +117 -11
  32. package/src/module/listmaster/entity/list-master.entity.ts +3 -0
  33. package/src/module/listmaster/service/list-master.service.ts +0 -4
  34. package/src/module/module/controller/menu.controller.ts +28 -2
  35. package/src/module/module/module.module.ts +2 -0
  36. package/src/module/module/service/module-import.service.ts +294 -0
  37. package/src/module/user/service/role.service.ts +4 -5
  38. package/src/module/user/service/user-role-mapping.service.ts +8 -0
  39. package/.claude/settings.local.json +0 -26
  40. package/.idea/250218_nodejs_core.iml +0 -9
  41. package/.idea/codeStyles/Project.xml +0 -59
  42. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  43. package/.idea/copilot.data.migration.agent.xml +0 -6
  44. package/.idea/copilot.data.migration.ask.xml +0 -6
  45. package/.idea/copilot.data.migration.ask2agent.xml +0 -6
  46. package/.idea/copilot.data.migration.edit.xml +0 -6
  47. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  48. package/.idea/misc.xml +0 -6
  49. package/.idea/modules.xml +0 -8
  50. package/.idea/prettier.xml +0 -6
  51. package/.idea/vcs.xml +0 -6
  52. package/server.log +0 -850
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "6.5.72",
3
+ "version": "6.5.74",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -63,6 +63,7 @@
63
63
  "passport-google-oauth20": "^2.0.0",
64
64
  "passport-jwt": "^4.0.1",
65
65
  "passport-local": "^1.0.0",
66
+ "pg": "^8.16.3",
66
67
  "redis": "^4.7.1",
67
68
  "reflect-metadata": "^0.2.2",
68
69
  "rxjs": "^7.8.1",
@@ -122,4 +123,4 @@
122
123
  "coverageDirectory": "../coverage",
123
124
  "testEnvironment": "node"
124
125
  }
125
- }
126
+ }
@@ -1,17 +1,18 @@
1
1
  import { Injectable } from '@nestjs/common';
2
2
  import { UserRoleMappingRepository } from 'src/module/user/repository/user-role-mapping.repository';
3
3
  import { AppMasterRespository } from '../repository/app-master.repository';
4
+ import { UserRoleMappingService } from 'src/module/user/service/user-role-mapping.service';
4
5
 
5
6
  @Injectable()
6
7
  export class AppMasterService {
7
8
  constructor(
8
9
  private readonly appMasterRepository: AppMasterRespository,
9
- private readonly userRoleMappingRepository: UserRoleMappingRepository,
10
+ private readonly userRoleMappingService:UserRoleMappingService
10
11
  ) {}
11
12
 
12
13
  async getAppMasterDataByAppCode(userId: number) {
13
14
  const app_code =
14
- await this.userRoleMappingRepository.findDistinctAppcodeByUserId(userId);
15
+ await this.userRoleMappingService.findDistinctAppcodeByUserId(userId);
15
16
 
16
17
  const finalResult: any[] = [];
17
18
 
@@ -12,6 +12,7 @@ import {
12
12
  HttpCode,
13
13
  HttpStatus,
14
14
  ParseIntPipe,
15
+ BadRequestException,
15
16
  } from '@nestjs/common';
16
17
  import { Request } from 'express';
17
18
  import { JwtAuthGuard } from 'src/module/auth/guards/jwt.guard';
@@ -79,9 +80,15 @@ export class OrganizationController {
79
80
  @Param('id', ParseIntPipe) id: number,
80
81
  @Body() organizationDto: Partial<OrganizationData>,
81
82
  @Req() req: Request & { user: any },
82
- ): Promise<OrganizationData | null> {
83
+ ): Promise<OrganizationData> {
83
84
  const loggedInUser = req.user.userData;
84
- return await this.orgService.update(id, organizationDto, loggedInUser);
85
+ const result = await this.orgService.update(id, organizationDto, loggedInUser);
86
+
87
+ if (!result.success) {
88
+ throw new BadRequestException(result.error);
89
+ }
90
+
91
+ return result.data;
85
92
  }
86
93
 
87
94
  @Delete(':id')
@@ -10,6 +10,11 @@ import { SchoolRepository } from '../repository/school.repository';
10
10
  import { WBSCodeGenService } from 'src/utils/service/wbsCodeGen.service';
11
11
  import { StatusConstant } from '../../../constant/status.constant';
12
12
 
13
+ // Type for update operation result
14
+ export type UpdateResult<T> =
15
+ | { success: true; data: T }
16
+ | { success: false; error: string };
17
+
13
18
  @Injectable()
14
19
  export class OrganizationService {
15
20
  constructor(
@@ -138,7 +143,7 @@ export class OrganizationService {
138
143
  organizationDto: Partial<OrganizationData>,
139
144
  loggedInUser?: any,
140
145
  manager?: EntityManager,
141
- ): Promise<OrganizationData | null> {
146
+ ): Promise<UpdateResult<OrganizationData>> {
142
147
  const repo = manager
143
148
  ? manager.getRepository(OrganizationData)
144
149
  : this.organizationRepository;
@@ -146,12 +151,18 @@ export class OrganizationService {
146
151
  // Get existing organization
147
152
  const existingOrg = await repo.findOne({ where: { id } });
148
153
  if (!existingOrg) {
149
- throw new Error(`Organization with id ${id} not found.`);
154
+ return {
155
+ success: false,
156
+ error: `Organization with id ${id} not found.`,
157
+ };
150
158
  }
151
159
 
152
160
  // Prevent circular reference - check if trying to set itself as parent
153
161
  if (organizationDto.parent_id === id) {
154
- throw new Error('Organization cannot be its own parent.');
162
+ return {
163
+ success: false,
164
+ error: 'Organization cannot be its own parent.',
165
+ };
155
166
  }
156
167
 
157
168
  // Check if parent_id is being changed
@@ -161,7 +172,19 @@ export class OrganizationService {
161
172
 
162
173
  // If parent_id is being set or changed, recalculate WBS code
163
174
  if (parentIdChanged) {
175
+ // VALIDATION: Cannot change parent of organizations with WBS code length 5 or 11
176
+ const wbsCodeLength = existingOrg.wbs_code?.length || 0;
177
+ if (wbsCodeLength === 5 || wbsCodeLength === 11) {
178
+ return {
179
+ success: false,
180
+ error:
181
+ `Cannot change parent of organization with WBS code length ${wbsCodeLength}. ` +
182
+ `Organizations at this level (${existingOrg.wbs_code}) are locked and cannot be moved.`,
183
+ };
184
+ }
185
+
164
186
  const newParentId = organizationDto.parent_id;
187
+ const oldWbsCode = existingOrg.wbs_code;
165
188
 
166
189
  if (newParentId) {
167
190
  // Sub-org case: parent_id is being set
@@ -170,18 +193,39 @@ export class OrganizationService {
170
193
  });
171
194
 
172
195
  if (!parentOrg) {
173
- throw new Error('Parent organization not found.');
196
+ return {
197
+ success: false,
198
+ error: 'Parent organization not found.',
199
+ };
174
200
  }
175
201
 
176
202
  if (!parentOrg.wbs_code) {
177
- throw new Error('Parent organization must have a WBS code.');
203
+ return {
204
+ success: false,
205
+ error: 'Parent organization must have a WBS code.',
206
+ };
178
207
  }
179
208
 
180
- // Prevent setting a descendant as parent (circular reference check)
181
- if (parentOrg.wbs_code?.startsWith(existingOrg.wbs_code || '')) {
182
- throw new Error(
183
- 'Cannot set a descendant organization as parent (circular reference).',
184
- );
209
+ // CIRCULAR DEPENDENCY CHECK: Prevent setting a descendant as parent
210
+ // This checks if the proposed parent is actually a child/descendant of the current org
211
+ if (existingOrg.wbs_code && parentOrg.wbs_code) {
212
+ // Check if parent's WBS starts with current org's WBS (parent is a descendant)
213
+ if (parentOrg.wbs_code.startsWith(existingOrg.wbs_code + '.')) {
214
+ return {
215
+ success: false,
216
+ error:
217
+ `Cannot set organization ${parentOrg.wbs_code} as parent. ` +
218
+ `It is a descendant of ${existingOrg.wbs_code} (circular reference).`,
219
+ };
220
+ }
221
+
222
+ // Also check if they're the same (redundant with earlier check, but explicit)
223
+ if (parentOrg.wbs_code === existingOrg.wbs_code) {
224
+ return {
225
+ success: false,
226
+ error: 'Organization cannot be its own parent (circular reference).',
227
+ };
228
+ }
185
229
  }
186
230
 
187
231
  // Find existing sub-orgs with the same parent_id (excluding current org)
@@ -235,6 +279,17 @@ export class OrganizationService {
235
279
 
236
280
  organizationDto.wbs_code = this.wbcCodeGenService.padCode(nextOrgCode);
237
281
  }
282
+
283
+ // CASCADE UPDATE: Update all descendant organizations' WBS codes
284
+ const newWbsCode = organizationDto.wbs_code;
285
+ if (oldWbsCode && newWbsCode && oldWbsCode !== newWbsCode) {
286
+ await this.updateDescendantWbsCodes(
287
+ repo,
288
+ oldWbsCode,
289
+ newWbsCode,
290
+ loggedInUser,
291
+ );
292
+ }
238
293
  }
239
294
 
240
295
  // Set modified_by
@@ -243,7 +298,19 @@ export class OrganizationService {
243
298
  }
244
299
 
245
300
  await repo.update(id, organizationDto);
246
- return await repo.findOne({ where: { id } });
301
+ const updatedOrg = await repo.findOne({ where: { id } });
302
+
303
+ if (!updatedOrg) {
304
+ return {
305
+ success: false,
306
+ error: 'Failed to retrieve updated organization.',
307
+ };
308
+ }
309
+
310
+ return {
311
+ success: true,
312
+ data: updatedOrg,
313
+ };
247
314
  }
248
315
 
249
316
  async remove(id: number, manager?: EntityManager): Promise<void> {
@@ -254,6 +321,45 @@ export class OrganizationService {
254
321
  await repo.delete(id);
255
322
  }
256
323
 
324
+ /**
325
+ * Recursively updates WBS codes for all descendant organizations
326
+ * when a parent organization's WBS code changes
327
+ */
328
+ private async updateDescendantWbsCodes(
329
+ repo: Repository<OrganizationData>,
330
+ oldWbsCode: string,
331
+ newWbsCode: string,
332
+ loggedInUser?: any,
333
+ ): Promise<void> {
334
+ // Find all organizations whose WBS code starts with the old WBS code
335
+ // This will get all direct and indirect descendants
336
+ const descendants = await repo
337
+ .createQueryBuilder('org')
338
+ .where('org.wbs_code LIKE :pattern', { pattern: `${oldWbsCode}.%` })
339
+ .getMany();
340
+
341
+ // Update each descendant's WBS code
342
+ for (const descendant of descendants) {
343
+ if (descendant.wbs_code) {
344
+ // Replace the old WBS prefix with the new one
345
+ const updatedWbsCode = descendant.wbs_code.replace(
346
+ oldWbsCode,
347
+ newWbsCode,
348
+ );
349
+
350
+ const updateData: Partial<OrganizationData> = {
351
+ wbs_code: updatedWbsCode,
352
+ };
353
+
354
+ if (loggedInUser) {
355
+ updateData.modified_by = loggedInUser.id;
356
+ }
357
+
358
+ await repo.update(descendant.id, updateData);
359
+ }
360
+ }
361
+ }
362
+
257
363
  async getOrganizationHierarchy(
258
364
  userId: number,
259
365
  appCode: string,
@@ -9,6 +9,9 @@ export class ListMasterData extends BaseEntity {
9
9
  @Column({nullable: true})
10
10
  type: string;
11
11
 
12
+ @Column({nullable: true})
13
+ storage_type: string;
14
+
12
15
  @Column({nullable: true})
13
16
  sort_by: string;
14
17
 
@@ -358,12 +358,9 @@ export class ListMasterService {
358
358
  async createEntity(entityData: any, loggedInUser: UserData): Promise<any> {
359
359
  // Trim and validate name and code
360
360
  const name = entityData.name?.trim();
361
- // const code = entityData.code?.trim();
362
-
363
361
  if (!name) {
364
362
  throw new BadRequestException('Name is required and cannot be empty');
365
363
  }
366
-
367
364
  entityData.name = name;
368
365
  entityData.is_factory = entityData.is_factory || 0;
369
366
  entityData.source = entityData.source || 'master';
@@ -391,7 +388,6 @@ export class ListMasterService {
391
388
  if (!createdListMaster) {
392
389
  throw new BadRequestException('Failed to create entity');
393
390
  }
394
-
395
391
  return createdListMaster;
396
392
  }
397
393
 
@@ -1,10 +1,23 @@
1
- import { Controller, Get, Request, UseGuards } from '@nestjs/common';
1
+ import {
2
+ BadRequestException,
3
+ Controller,
4
+ Get,
5
+ Post,
6
+ Request,
7
+ UploadedFile,
8
+ UseGuards,
9
+ UseInterceptors,
10
+ } from '@nestjs/common';
2
11
  import { MenuService } from '../service/menu.service';
3
12
  import { JwtAuthGuard } from 'src/module/auth/guards/jwt.guard';
13
+ import { FileInterceptor } from '@nestjs/platform-express';
14
+ import { Express } from 'express';
15
+ import { ModuleImportService } from '../service/module-import.service';
4
16
 
5
17
  @Controller('menu')
6
18
  export class MenuController {
7
- constructor(private readonly menuService: MenuService) {}
19
+ constructor(private readonly menuService: MenuService,
20
+ private readonly moduleImportService: ModuleImportService,) {}
8
21
 
9
22
  @UseGuards(JwtAuthGuard)
10
23
  @Get()
@@ -12,4 +25,17 @@ export class MenuController {
12
25
  const { id: userId, appcode, level_type, level_id } = req.user.userData;
13
26
  return this.menuService.getUserMenu(userId, appcode, level_type, level_id);
14
27
  }
28
+
29
+ @Post('upload-module-data')
30
+ @UseInterceptors(FileInterceptor('file'))
31
+ async uploadModuleDataXls(
32
+ @UploadedFile() file: Express.Multer.File,
33
+ ) {
34
+ if (!file) {
35
+ throw new BadRequestException('File is required');
36
+ }
37
+ return await this.moduleImportService.uploadModuleData(
38
+ file,
39
+ );
40
+ }
15
41
  }
@@ -14,6 +14,7 @@ import { UserRoleMapping } from '../user/entity/user-role-mapping.entity';
14
14
  import { UserModule } from '../user/user.module';
15
15
  import { Role } from '../user/entity/role.entity';
16
16
  import { ModuleAccess } from './entity/module-access.entity';
17
+ import { ModuleImportService } from './service/module-import.service';
17
18
 
18
19
  @Module({
19
20
  imports: [
@@ -34,6 +35,7 @@ import { ModuleAccess } from './entity/module-access.entity';
34
35
  MenuService,
35
36
  MenuRepository,
36
37
  ModuleAccessRepository,
38
+ ModuleImportService
37
39
  ],
38
40
  exports: [ModuleAccessService,MenuService],
39
41
  })
@@ -0,0 +1,294 @@
1
+ import { BadRequestException, Injectable } from '@nestjs/common';
2
+ import { EntityManager } from 'typeorm';
3
+ import { ExcelUtil } from 'src/utils/service/excelUtil.service';
4
+ import { ConfigService } from '@nestjs/config';
5
+
6
+ @Injectable()
7
+ export class ModuleImportService {
8
+ constructor(
9
+ private readonly entityManager: EntityManager,
10
+ private readonly configService: ConfigService,
11
+ ) { }
12
+
13
+ schema = this.configService.get('DB_SCHEMA');
14
+
15
+ private readonly moduleSequence = [
16
+ {
17
+ table: 'sso_module',
18
+ unique_fields: ['module_code'],
19
+ },
20
+ {
21
+ table: 'sso_menu',
22
+ unique_fields: ['module_id'],
23
+ },
24
+ {
25
+ table: 'sso_module_ucp',
26
+ unique_fields: ['module_id', 'action_code'],
27
+ },
28
+ {
29
+ table: 'sso_module_access',
30
+ unique_fields: ['module_id', 'action_type', 'role_id', 'level_type'],
31
+ },
32
+ ];
33
+
34
+ /**
35
+ * Get columns to exclude from INSERT/UPDATE operations based on table
36
+ * module_code is only needed in sso_module table, excluded from others
37
+ * action_type is only needed in sso_module_access table, excluded from others
38
+ */
39
+ private getExcludedColumns(tableName: string): string[] {
40
+ const baseExcluded = ['role_code', 'appcode'];
41
+
42
+ // module_code should only be included in sso_module table
43
+ if (tableName !== 'sso_module') {
44
+ baseExcluded.push('module_code');
45
+ }
46
+
47
+ // action_type should only be included in sso_module_access table
48
+ if (tableName !== 'sso_module_access') {
49
+ baseExcluded.push('action_type');
50
+ }
51
+
52
+ return baseExcluded;
53
+ }
54
+
55
+ async uploadModuleData(file) {
56
+ const data = ExcelUtil.readExcel(file.buffer);
57
+ const errors: any[] = [];
58
+
59
+ for (let i = 0; i < this.moduleSequence.length; i++) {
60
+ const { table, unique_fields } = this.moduleSequence[i];
61
+ const sheetData = data[table];
62
+
63
+ if (!sheetData) {
64
+ console.log(`Sheet ${table} not found in the Excel file.`);
65
+ continue;
66
+ }
67
+
68
+ console.log(`Processing sheet: ${table}`);
69
+
70
+ // Process each row and resolve codes to IDs
71
+ for (let rowIndex = 0; rowIndex < sheetData.length; rowIndex++) {
72
+ const row = sheetData[rowIndex];
73
+
74
+ try {
75
+ // Resolve codes to IDs based on table
76
+ if (table === 'sso_menu') {
77
+ await this.resolveModuleCode(row);
78
+ await this.resolveAppCode(row);
79
+ } else if (table === 'sso_module_ucp') {
80
+ await this.resolveModuleCode(row);
81
+ } else if (table === 'sso_module_access') {
82
+ await this.resolveModuleCode(row);
83
+ await this.resolveRoleCode(row);
84
+ await this.resolveAppCode(row);
85
+ await this.resolveActionType(row);
86
+ } else if (table === 'sso_module') {
87
+ await this.resolveAppCode(row);
88
+ }
89
+ } catch (error) {
90
+ errors.push({
91
+ sheet: table,
92
+ row: rowIndex + 1,
93
+ error: error.message,
94
+ });
95
+ }
96
+ }
97
+
98
+ // If there are errors, throw exception before upserting
99
+ if (errors.length > 0) {
100
+ throw new BadRequestException({
101
+ message: 'Validation errors found during code resolution',
102
+ errors,
103
+ });
104
+ }
105
+
106
+ // Upsert data for this table
107
+ await this.upsertModuleData(table, unique_fields, sheetData);
108
+ }
109
+
110
+ return {
111
+ message: 'Module data uploaded successfully',
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Resolve module_code to module_id
117
+ */
118
+ private async resolveModuleCode(row: any): Promise<void> {
119
+ if (!row.module_code) {
120
+ throw new Error('module_code is required but not provided');
121
+ }
122
+
123
+ const moduleData = await this.entityManager.query(
124
+ `SELECT id FROM ${this.schema}.sso_module WHERE module_code = $1 LIMIT 1`,
125
+ [row.module_code],
126
+ );
127
+
128
+ if (!moduleData || moduleData.length === 0) {
129
+ throw new Error(
130
+ `Module not found for module_code: ${row.module_code}`,
131
+ );
132
+ }
133
+
134
+ row.module_id = moduleData[0].id;
135
+ }
136
+
137
+ /**
138
+ * Resolve role_code to role_id
139
+ */
140
+ private async resolveRoleCode(row: any): Promise<void> {
141
+ if (!row.role_code) {
142
+ throw new Error('role_code is required but not provided');
143
+ }
144
+
145
+ const roleData = await this.entityManager.query(
146
+ `SELECT id FROM ${this.schema}.sso_role WHERE code = $1 LIMIT 1`,
147
+ [row.role_code],
148
+ );
149
+
150
+ if (!roleData || roleData.length === 0) {
151
+ throw new Error(`Role not found for role_code: ${row.role_code}`);
152
+ }
153
+
154
+ row.role_id = roleData[0].id;
155
+ }
156
+
157
+ /**
158
+ * Resolve appcode to app_id
159
+ */
160
+ private async resolveAppCode(row: any): Promise<void> {
161
+ if (!row.appcode) {
162
+ // appcode might be optional in some cases
163
+ return;
164
+ }
165
+
166
+ const appData = await this.entityManager.query(
167
+ `SELECT id FROM ${this.schema}.sso_app_master WHERE code = $1 LIMIT 1`,
168
+ [row.appcode],
169
+ );
170
+
171
+ if (!appData || appData.length === 0) {
172
+ throw new Error(`App not found for appcode: ${row.appcode}`);
173
+ }
174
+
175
+ row.app_id = appData[0].id;
176
+ }
177
+
178
+ /**
179
+ * Resolve action_type (action_code) to action_id
180
+ * This requires both module_code and action_type to find the correct action
181
+ */
182
+ private async resolveActionType(row: any): Promise<void> {
183
+ if (!row.action_type) {
184
+ throw new Error('action_type is required but not provided');
185
+ }
186
+
187
+ if (!row.module_code) {
188
+ throw new Error('module_code is required to resolve action_type');
189
+ }
190
+
191
+ // First get the module_id
192
+ const moduleData = await this.entityManager.query(
193
+ `SELECT id FROM ${this.schema}.sso_module WHERE module_code = $1 LIMIT 1`,
194
+ [row.module_code],
195
+ );
196
+
197
+ if (!moduleData || moduleData.length === 0) {
198
+ throw new Error(
199
+ `Module not found for module_code: ${row.module_code}`,
200
+ );
201
+ }
202
+
203
+ // Then find the action using module_id and action_code
204
+ const actionData = await this.entityManager.query(
205
+ `SELECT id FROM ${this.schema}.sso_module_ucp WHERE module_id = $1 AND action_code = $2 LIMIT 1`,
206
+ [moduleData[0].id, row.action_type],
207
+ );
208
+
209
+ if (!actionData || actionData.length === 0) {
210
+ throw new Error(
211
+ `Action not found for module_code: ${row.module_code} and action_type: ${row.action_type}`,
212
+ );
213
+ }
214
+
215
+ row.action_id = actionData[0].id;
216
+ }
217
+
218
+ /**
219
+ * Upsert data into the specified table
220
+ */
221
+ private async upsertModuleData(
222
+ tableName: string,
223
+ uniqueFields: string[],
224
+ data: any[],
225
+ ): Promise<void> {
226
+ const excludedColumns = this.getExcludedColumns(tableName);
227
+
228
+ for (const row of data) {
229
+ const whereClause = uniqueFields
230
+ .map((field, index) => `${field} = $${index + 1}`)
231
+ .join(' AND ');
232
+
233
+ const whereParams = uniqueFields.map((field) => row[field]);
234
+
235
+ const existing = await this.entityManager.query(
236
+ `SELECT * FROM ${this.schema}.${tableName} WHERE ${whereClause} LIMIT 1`,
237
+ whereParams,
238
+ );
239
+
240
+ if (existing.length > 0) {
241
+ // Update existing record
242
+ const setClauses: string[] = [];
243
+ const setParams: any[] = [];
244
+ let paramIndex = 1;
245
+
246
+ for (const key in row) {
247
+ // Exclude code columns and unique fields from update
248
+ if (row.hasOwnProperty(key) &&
249
+ !uniqueFields.includes(key) &&
250
+ !excludedColumns.includes(key)) {
251
+ setClauses.push(`${key} = $${paramIndex}`);
252
+ setParams.push(row[key]);
253
+ paramIndex++;
254
+ }
255
+ }
256
+
257
+ // Add where parameters
258
+ whereParams.forEach((param) => {
259
+ setParams.push(param);
260
+ });
261
+
262
+ const updateWhereClause = uniqueFields
263
+ .map((field, index) => `${field} = $${paramIndex + index}`)
264
+ .join(' AND ');
265
+
266
+ if (setClauses.length > 0) {
267
+ await this.entityManager.query(
268
+ `UPDATE ${this.schema}.${tableName} SET ${setClauses.join(', ')} WHERE ${updateWhereClause}`,
269
+ setParams,
270
+ );
271
+ }
272
+ } else {
273
+ // Insert new record - exclude code columns
274
+ const filteredRow: any = {};
275
+ for (const key in row) {
276
+ if (row.hasOwnProperty(key) && !excludedColumns.includes(key)) {
277
+ filteredRow[key] = row[key];
278
+ }
279
+ }
280
+
281
+ const columns = Object.keys(filteredRow).join(', ');
282
+ const placeholders = Object.keys(filteredRow)
283
+ .map((_, index) => `$${index + 1}`)
284
+ .join(', ');
285
+ const values = Object.values(filteredRow);
286
+
287
+ await this.entityManager.query(
288
+ `INSERT INTO ${this.schema}.${tableName} (${columns}) VALUES (${placeholders})`,
289
+ values,
290
+ );
291
+ }
292
+ }
293
+ }
294
+ }
@@ -1,7 +1,6 @@
1
1
  import { Injectable } from '@nestjs/common';
2
2
  import { UserData } from '../entity/user.entity';
3
3
  import { Role } from '../entity/role.entity';
4
- import { STATUS_INACTIVE } from 'src/constant/global.constant';
5
4
  import { RoleRepository } from '../repository/role.repository';
6
5
  import { Repository } from 'typeorm';
7
6
  import { InjectRepository } from '@nestjs/typeorm';
@@ -45,9 +44,9 @@ export class RoleService {
45
44
  return { success: false, error: 'Role name already exists.' };
46
45
  }
47
46
 
48
- if(!role.code) {
47
+ if (!role.code) {
49
48
  const maxId = await this.roleRepository.findMaxIdRecord();
50
- if(maxId && maxId.id) {
49
+ if (maxId && maxId.id) {
51
50
  role.code = `ROL${maxId.id + 1}`;
52
51
  } else {
53
52
  role.code = `ROL1`;
@@ -75,7 +74,7 @@ export class RoleService {
75
74
  access_flag: perm.access_flag,
76
75
  level_type: perm.level_type,
77
76
  appcode: perm.appcode,
78
- module_id: perm.module_id
77
+ module_id: perm.module_id,
79
78
  }),
80
79
  );
81
80
 
@@ -93,7 +92,7 @@ export class RoleService {
93
92
  return { success: false, error: 'Role name is required' };
94
93
  }
95
94
 
96
- if (role.status == STATUS_INACTIVE) {
95
+ if (role.status == StatusConstant.INACTIVE) {
97
96
  //get role by id
98
97
  const existingRole = await this.roleRepository.findById(id);
99
98