rez_core 6.5.73 → 6.5.75

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 (30) hide show
  1. package/dist/module/listmaster/entity/list-master.entity.d.ts +1 -0
  2. package/dist/module/listmaster/entity/list-master.entity.js +4 -0
  3. package/dist/module/listmaster/entity/list-master.entity.js.map +1 -1
  4. package/dist/module/listmaster/repository/list-master-items.repository.d.ts +2 -2
  5. package/dist/module/listmaster/repository/list-master-items.repository.js +2 -2
  6. package/dist/module/listmaster/repository/list-master-items.repository.js.map +1 -1
  7. package/dist/module/listmaster/service/list-master-item.service.js +10 -0
  8. package/dist/module/listmaster/service/list-master-item.service.js.map +1 -1
  9. package/dist/module/listmaster/service/list-master.service.js +2 -2
  10. package/dist/module/listmaster/service/list-master.service.js.map +1 -1
  11. package/dist/module/module/controller/menu.controller.d.ts +6 -1
  12. package/dist/module/module/controller/menu.controller.js +20 -2
  13. package/dist/module/module/controller/menu.controller.js.map +1 -1
  14. package/dist/module/module/module.module.js +2 -0
  15. package/dist/module/module/module.module.js.map +1 -1
  16. package/dist/module/module/service/module-import.service.d.ts +18 -0
  17. package/dist/module/module/service/module-import.service.js +203 -0
  18. package/dist/module/module/service/module-import.service.js.map +1 -0
  19. package/dist/module/user/service/role.service.js +2 -3
  20. package/dist/module/user/service/role.service.js.map +1 -1
  21. package/dist/tsconfig.build.tsbuildinfo +1 -1
  22. package/package.json +3 -2
  23. package/src/module/listmaster/entity/list-master.entity.ts +3 -0
  24. package/src/module/listmaster/repository/list-master-items.repository.ts +3 -1
  25. package/src/module/listmaster/service/list-master-item.service.ts +15 -0
  26. package/src/module/listmaster/service/list-master.service.ts +2 -4
  27. package/src/module/module/controller/menu.controller.ts +28 -2
  28. package/src/module/module/module.module.ts +2 -0
  29. package/src/module/module/service/module-import.service.ts +294 -0
  30. package/src/module/user/service/role.service.ts +4 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "6.5.73",
3
+ "version": "6.5.75",
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
+ }
@@ -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
 
@@ -23,6 +23,7 @@ export class ListMasterItemsRepository {
23
23
  async findItemsByType(
24
24
  type: string,
25
25
  sort_by?,
26
+ storage_type?,
26
27
  inactiveIdsArray?: number[],
27
28
  enterprise_id?: number,
28
29
  params?: Record<string, string>,
@@ -79,9 +80,10 @@ export class ListMasterItemsRepository {
79
80
  order: orderBy,
80
81
  });
81
82
 
83
+
82
84
  let result = items.map((i) => ({
83
85
  label: i.name,
84
- value: i.id,
86
+ value: storage_type === 'code' ? i.code : parseInt(String(i.id), 10),
85
87
  }));
86
88
 
87
89
  // Handle inactive items if specified
@@ -127,6 +127,21 @@ export class ListMasterItemService extends EntityServiceImpl {
127
127
 
128
128
  const finalCode = code || ''; // Could generate code here if needed
129
129
 
130
+ // Check if code already exists (if code is provided)
131
+ if (finalCode) {
132
+ const codeExists = await this.listItemsRepo.findOneByCondition({
133
+ code: finalCode,
134
+ listtype: listType,
135
+ enterprise_id: entId,
136
+ });
137
+
138
+ if (codeExists) {
139
+ throw new BadRequestException(
140
+ `list item with code ${finalCode} already exists`,
141
+ );
142
+ }
143
+ }
144
+
130
145
  await this.createEntity(
131
146
  {
132
147
  ...item,
@@ -124,6 +124,7 @@ export class ListMasterService {
124
124
  return this.listItemsRepo.findItemsByType(
125
125
  type,
126
126
  config.sort_by,
127
+ config.storage_type,
127
128
  inactiveIdsArray,
128
129
  loggedInUser?.enterprise_id,
129
130
  params,
@@ -358,12 +359,9 @@ export class ListMasterService {
358
359
  async createEntity(entityData: any, loggedInUser: UserData): Promise<any> {
359
360
  // Trim and validate name and code
360
361
  const name = entityData.name?.trim();
361
- // const code = entityData.code?.trim();
362
-
363
362
  if (!name) {
364
363
  throw new BadRequestException('Name is required and cannot be empty');
365
364
  }
366
-
367
365
  entityData.name = name;
368
366
  entityData.is_factory = entityData.is_factory || 0;
369
367
  entityData.source = entityData.source || 'master';
@@ -391,7 +389,6 @@ export class ListMasterService {
391
389
  if (!createdListMaster) {
392
390
  throw new BadRequestException('Failed to create entity');
393
391
  }
394
-
395
392
  return createdListMaster;
396
393
  }
397
394
 
@@ -590,6 +587,7 @@ export class ListMasterService {
590
587
  return this.listItemsRepo.findItemsByType(
591
588
  lmCode,
592
589
  listMaster.sort_by,
590
+ listMaster.storage_type,
593
591
  inactiveIdsArray,
594
592
  loggedInUser.enterprise_id,
595
593
  params,
@@ -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