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.
- package/dist/module/app_master/service/app-master.service.d.ts +3 -3
- package/dist/module/app_master/service/app-master.service.js +5 -5
- package/dist/module/app_master/service/app-master.service.js.map +1 -1
- package/dist/module/enterprise/controller/organization.controller.d.ts +1 -1
- package/dist/module/enterprise/controller/organization.controller.js +5 -1
- package/dist/module/enterprise/controller/organization.controller.js.map +1 -1
- package/dist/module/enterprise/service/organization.service.d.ts +9 -1
- package/dist/module/enterprise/service/organization.service.js +72 -7
- package/dist/module/enterprise/service/organization.service.js.map +1 -1
- package/dist/module/listmaster/entity/list-master.entity.d.ts +1 -0
- package/dist/module/listmaster/entity/list-master.entity.js +4 -0
- package/dist/module/listmaster/entity/list-master.entity.js.map +1 -1
- package/dist/module/listmaster/service/list-master.service.js.map +1 -1
- package/dist/module/module/controller/menu.controller.d.ts +6 -1
- package/dist/module/module/controller/menu.controller.js +20 -2
- package/dist/module/module/controller/menu.controller.js.map +1 -1
- package/dist/module/module/module.module.js +2 -0
- package/dist/module/module/module.module.js.map +1 -1
- package/dist/module/module/service/module-import.service.d.ts +18 -0
- package/dist/module/module/service/module-import.service.js +203 -0
- package/dist/module/module/service/module-import.service.js.map +1 -0
- package/dist/module/user/service/role.service.js +2 -3
- package/dist/module/user/service/role.service.js.map +1 -1
- package/dist/module/user/service/user-role-mapping.service.d.ts +3 -0
- package/dist/module/user/service/user-role-mapping.service.js +3 -0
- package/dist/module/user/service/user-role-mapping.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/module/app_master/service/app-master.service.ts +3 -2
- package/src/module/enterprise/controller/organization.controller.ts +9 -2
- package/src/module/enterprise/service/organization.service.ts +117 -11
- package/src/module/listmaster/entity/list-master.entity.ts +3 -0
- package/src/module/listmaster/service/list-master.service.ts +0 -4
- package/src/module/module/controller/menu.controller.ts +28 -2
- package/src/module/module/module.module.ts +2 -0
- package/src/module/module/service/module-import.service.ts +294 -0
- package/src/module/user/service/role.service.ts +4 -5
- package/src/module/user/service/user-role-mapping.service.ts +8 -0
- package/.claude/settings.local.json +0 -26
- package/.idea/250218_nodejs_core.iml +0 -9
- package/.idea/codeStyles/Project.xml +0 -59
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/copilot.data.migration.agent.xml +0 -6
- package/.idea/copilot.data.migration.ask.xml +0 -6
- package/.idea/copilot.data.migration.ask2agent.xml +0 -6
- package/.idea/copilot.data.migration.edit.xml +0 -6
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -6
- package/.idea/vcs.xml +0 -6
- package/server.log +0 -850
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rez_core",
|
|
3
|
-
"version": "6.5.
|
|
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
|
|
10
|
+
private readonly userRoleMappingService:UserRoleMappingService
|
|
10
11
|
) {}
|
|
11
12
|
|
|
12
13
|
async getAppMasterDataByAppCode(userId: number) {
|
|
13
14
|
const app_code =
|
|
14
|
-
await this.
|
|
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
|
|
83
|
+
): Promise<OrganizationData> {
|
|
83
84
|
const loggedInUser = req.user.userData;
|
|
84
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
error: 'Parent organization not found.',
|
|
199
|
+
};
|
|
174
200
|
}
|
|
175
201
|
|
|
176
202
|
if (!parentOrg.wbs_code) {
|
|
177
|
-
|
|
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
|
|
181
|
-
if
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -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 {
|
|
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 ==
|
|
95
|
+
if (role.status == StatusConstant.INACTIVE) {
|
|
97
96
|
//get role by id
|
|
98
97
|
const existingRole = await this.roleRepository.findById(id);
|
|
99
98
|
|