rez_core 1.0.97 → 1.0.99
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/filter/service/filter.service.js +3 -3
- package/dist/module/filter/service/filter.service.js.map +1 -1
- package/dist/module/master/service/master.service.d.ts +7 -2
- package/dist/module/master/service/master.service.js +68 -15
- package/dist/module/master/service/master.service.js.map +1 -1
- package/dist/module/meta/entity/attribute-master.entity.d.ts +1 -0
- package/dist/module/meta/entity/attribute-master.entity.js +4 -0
- package/dist/module/meta/entity/attribute-master.entity.js.map +1 -1
- package/dist/module/meta/entity/base-entity.entity.d.ts +1 -0
- package/dist/module/meta/entity/base-entity.entity.js +5 -0
- package/dist/module/meta/entity/base-entity.entity.js.map +1 -1
- package/dist/module/meta/service/entity-validation.service.d.ts +2 -0
- package/dist/module/meta/service/entity-validation.service.js +27 -4
- package/dist/module/meta/service/entity-validation.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/module/filter/service/filter.service.ts +4 -3
- package/src/module/master/service/master.service.ts +83 -22
- package/src/module/meta/entity/attribute-master.entity.ts +5 -2
- package/src/module/meta/entity/base-entity.entity.ts +3 -0
- package/src/module/meta/service/entity-service-impl.service.ts +1 -1
- package/src/module/meta/service/entity-validation.service.ts +69 -23
package/package.json
CHANGED
|
@@ -100,10 +100,11 @@ export class FilterService {
|
|
|
100
100
|
// Get and parse filters
|
|
101
101
|
const savedFilters = await this.getSavedFilters(savedFilterCode);
|
|
102
102
|
const baseFilters = [
|
|
103
|
-
...(quickFilter || []),
|
|
104
|
-
...savedFilters,
|
|
105
|
-
...(attributeFilter || []),
|
|
103
|
+
...(quickFilter || []).filter(f => f.filter_value !== ""),
|
|
104
|
+
...savedFilters.filter(f => f.filter_value !== ""),
|
|
105
|
+
...(attributeFilter || []).filter(f => f.filter_value !== ""),
|
|
106
106
|
];
|
|
107
|
+
|
|
107
108
|
const baseWhere = this.buildWhereClauses(baseFilters, attributeMetaMap);
|
|
108
109
|
|
|
109
110
|
// Build query for tab counts (no tab.value filter here)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
1
|
+
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
2
2
|
import { Brackets, EntityManager } from 'typeorm';
|
|
3
3
|
import { ExcelUtil } from 'src/utils/service/excelUtil.service';
|
|
4
4
|
import { EntityMasterService } from '../../meta/service/entity-master.service';
|
|
@@ -6,6 +6,7 @@ import { AttributeMasterService } from 'src/module/meta/service/attribute-master
|
|
|
6
6
|
import { EntityMaster } from 'src/module/meta/entity/entity-master.entity';
|
|
7
7
|
import { EntityServiceImpl } from 'src/module/meta/service/entity-service-impl.service';
|
|
8
8
|
import { ReflectionHelper } from 'src/utils/service/reflection-helper.service';
|
|
9
|
+
import { EntityValidationService } from 'src/module/meta/service/entity-validation.service';
|
|
9
10
|
|
|
10
11
|
@Injectable()
|
|
11
12
|
export class MasterService {
|
|
@@ -17,6 +18,7 @@ export class MasterService {
|
|
|
17
18
|
private reflectionHelper: ReflectionHelper,
|
|
18
19
|
|
|
19
20
|
private readonly entityServiceImpl: EntityServiceImpl,
|
|
21
|
+
protected readonly entityValidationService: EntityValidationService,
|
|
20
22
|
) {}
|
|
21
23
|
|
|
22
24
|
private readonly metaSheets = [
|
|
@@ -197,6 +199,8 @@ export class MasterService {
|
|
|
197
199
|
loggedInUser,
|
|
198
200
|
duplicateHandling: 'skip_duplicates' | 'overwrite_items',
|
|
199
201
|
) {
|
|
202
|
+
const errors: { row: number; errors: any[] }[] = [];
|
|
203
|
+
|
|
200
204
|
const data = ExcelUtil.readExcel(file.buffer);
|
|
201
205
|
const entityMaster =
|
|
202
206
|
await this.entityMasterService.findByMappedEntityType(entityType);
|
|
@@ -214,6 +218,7 @@ export class MasterService {
|
|
|
214
218
|
await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
215
219
|
entityType,
|
|
216
220
|
);
|
|
221
|
+
|
|
217
222
|
const uniqueFields = attributes
|
|
218
223
|
.filter((attr) => attr.is_unique)
|
|
219
224
|
.map((attr) => attr.attribute_key);
|
|
@@ -222,28 +227,61 @@ export class MasterService {
|
|
|
222
227
|
throw new Error(`No unique fields found for entityType: ${entityType}`);
|
|
223
228
|
}
|
|
224
229
|
|
|
225
|
-
for (const row of sheetData) {
|
|
230
|
+
for (const [i, row] of sheetData.entries()) {
|
|
226
231
|
if (row.parent_type && row.parent_id) {
|
|
227
232
|
await this.resolveParent(row);
|
|
228
233
|
}
|
|
229
234
|
|
|
230
235
|
for (const attr of attributes) {
|
|
231
236
|
if (attr.data_source_type === 'entity' && row[attr.attribute_key]) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const refData = await this.entityManager.query(
|
|
236
|
-
`SELECT * FROM ${refEntity.db_table_name} WHERE code = ? LIMIT 1`,
|
|
237
|
-
[row[attr.attribute_key]],
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
if (!refData.length) {
|
|
241
|
-
throw new Error(
|
|
242
|
-
`Reference entity not found for code: ${row[attr.attribute_key]}`,
|
|
237
|
+
try {
|
|
238
|
+
const refEntity = await this.entityMasterService.getEntityData(
|
|
239
|
+
attr.datasource_list,
|
|
243
240
|
);
|
|
244
|
-
}
|
|
245
241
|
|
|
246
|
-
|
|
242
|
+
const refData = await this.entityManager.query(
|
|
243
|
+
`SELECT * FROM ${refEntity.db_table_name} WHERE code = ? LIMIT 1`,
|
|
244
|
+
[row[attr.attribute_key]],
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (!refData.length) {
|
|
248
|
+
errors.push({
|
|
249
|
+
row: i + 1,
|
|
250
|
+
errors: [
|
|
251
|
+
{
|
|
252
|
+
field: attr.name,
|
|
253
|
+
message: `Reference entity not found for code: ${row[attr.attribute_key]}`,
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (refData[0].organization_id !== loggedInUser.organization_id) {
|
|
261
|
+
errors.push({
|
|
262
|
+
row: i + 1,
|
|
263
|
+
errors: [
|
|
264
|
+
{
|
|
265
|
+
field: attr.name,
|
|
266
|
+
message: `Reference entity with code ${row[attr.attribute_key]} does not belong to the organization`,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
});
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
row[attr.attribute_key] = refData[0].id;
|
|
274
|
+
} catch (err) {
|
|
275
|
+
errors.push({
|
|
276
|
+
row: i + 1,
|
|
277
|
+
errors: [
|
|
278
|
+
{
|
|
279
|
+
field: attr.name,
|
|
280
|
+
message: `Error fetching reference entity for ${attr.attribute_key}`,
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
});
|
|
284
|
+
}
|
|
247
285
|
}
|
|
248
286
|
}
|
|
249
287
|
}
|
|
@@ -255,6 +293,7 @@ export class MasterService {
|
|
|
255
293
|
uniqueFields,
|
|
256
294
|
loggedInUser,
|
|
257
295
|
duplicateHandling,
|
|
296
|
+
errors,
|
|
258
297
|
);
|
|
259
298
|
|
|
260
299
|
return { message: 'Entity data uploaded successfully' };
|
|
@@ -345,6 +384,7 @@ export class MasterService {
|
|
|
345
384
|
uniqueFields: string[],
|
|
346
385
|
loggedInUser: any,
|
|
347
386
|
duplicateHandling: 'skip_duplicates' | 'overwrite_items',
|
|
387
|
+
errors: { row: number; errors: any[] }[],
|
|
348
388
|
): Promise<void> {
|
|
349
389
|
const entityMaster =
|
|
350
390
|
await this.entityMasterService.findByMappedEntityType(entityType);
|
|
@@ -357,6 +397,32 @@ export class MasterService {
|
|
|
357
397
|
if (!entityService)
|
|
358
398
|
throw new Error(`Entity service not found for ${entityType}`);
|
|
359
399
|
|
|
400
|
+
for (const [i, row] of data.entries()) {
|
|
401
|
+
row.entity_type = entityType;
|
|
402
|
+
row.organization_id = loggedInUser.organization_id;
|
|
403
|
+
|
|
404
|
+
const rowErrors =
|
|
405
|
+
await this.entityValidationService.validateExcelEntityData(
|
|
406
|
+
row,
|
|
407
|
+
entityMaster,
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
if (rowErrors?.length > 0) {
|
|
411
|
+
errors.push({
|
|
412
|
+
row: i + 1,
|
|
413
|
+
errors: rowErrors,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (errors.length > 0) {
|
|
419
|
+
throw new BadRequestException({
|
|
420
|
+
status: false,
|
|
421
|
+
message: `Validation errors found in the uploaded data.`,
|
|
422
|
+
details: errors,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
360
426
|
for (const row of data) {
|
|
361
427
|
const qb = this.entityManager
|
|
362
428
|
.createQueryBuilder()
|
|
@@ -381,15 +447,10 @@ export class MasterService {
|
|
|
381
447
|
|
|
382
448
|
const existing = await qb.limit(1).getRawOne();
|
|
383
449
|
|
|
384
|
-
row.entity_type = entityType;
|
|
385
|
-
row.organization_id = loggedInUser.organization_id;
|
|
386
|
-
|
|
387
450
|
if (existing) {
|
|
388
|
-
if (duplicateHandling === 'skip_duplicates')
|
|
389
|
-
|
|
390
|
-
} else if (duplicateHandling === 'overwrite_items') {
|
|
451
|
+
if (duplicateHandling === 'skip_duplicates') continue;
|
|
452
|
+
if (duplicateHandling === 'overwrite_items') {
|
|
391
453
|
row.id = existing.id;
|
|
392
|
-
|
|
393
454
|
await entityService.updateEntity(row, loggedInUser);
|
|
394
455
|
}
|
|
395
456
|
} else {
|
|
@@ -6,7 +6,7 @@ export class AttributeMaster extends BaseEntity {
|
|
|
6
6
|
@Column({ name: 'mapped_entity_type', type: 'varchar', nullable: true })
|
|
7
7
|
mapped_entity_type: string;
|
|
8
8
|
|
|
9
|
-
@Column({ name: 'mapped_entity_id'
|
|
9
|
+
@Column({ name: 'mapped_entity_id', nullable: true })
|
|
10
10
|
mapped_entity_id: number;
|
|
11
11
|
|
|
12
12
|
@Column({ name: 'element_type', type: 'varchar', nullable: true, length: 50 })
|
|
@@ -62,9 +62,12 @@ export class AttributeMaster extends BaseEntity {
|
|
|
62
62
|
@Column({ name: 'is_unique', nullable: true })
|
|
63
63
|
is_unique: number;
|
|
64
64
|
|
|
65
|
+
@Column({ name: 'regex', nullable: true })
|
|
66
|
+
regex: string;
|
|
67
|
+
|
|
65
68
|
@Column({ name: 'can_export', nullable: true })
|
|
66
69
|
can_export: number;
|
|
67
70
|
|
|
68
71
|
@Column({ name: 'appcode', nullable: true })
|
|
69
|
-
appcode:string;
|
|
72
|
+
appcode: string;
|
|
70
73
|
}
|
|
@@ -14,7 +14,7 @@ export class EntityValidationService {
|
|
|
14
14
|
constructor(
|
|
15
15
|
private readonly attributeMasterService: AttributeMasterService,
|
|
16
16
|
private readonly reflectionHelper: ReflectionHelper,
|
|
17
|
-
private dataSource: DataSource
|
|
17
|
+
private dataSource: DataSource,
|
|
18
18
|
) {}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -27,8 +27,8 @@ export class EntityValidationService {
|
|
|
27
27
|
const errors: ValidationError[] = [];
|
|
28
28
|
|
|
29
29
|
attributeData
|
|
30
|
-
.filter(attr => attr.required)
|
|
31
|
-
.forEach(attr => {
|
|
30
|
+
.filter((attr) => attr.required)
|
|
31
|
+
.forEach((attr) => {
|
|
32
32
|
const value = entityData[attr.attribute_key];
|
|
33
33
|
if (!this.hasValidValue(value)) {
|
|
34
34
|
errors.push({
|
|
@@ -48,32 +48,34 @@ export class EntityValidationService {
|
|
|
48
48
|
entityData: Record<string, any>,
|
|
49
49
|
attributeData: AttributeMaster[],
|
|
50
50
|
entityType: string,
|
|
51
|
-
db_table_name: string
|
|
51
|
+
db_table_name: string,
|
|
52
52
|
): Promise<ValidationError[]> {
|
|
53
53
|
const errors: ValidationError[] = [];
|
|
54
|
-
|
|
55
|
-
for (const attr of attributeData.filter(a => a.is_unique)) {
|
|
54
|
+
|
|
55
|
+
for (const attr of attributeData.filter((a) => a.is_unique)) {
|
|
56
56
|
const value = entityData[attr.attribute_key];
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
if (this.hasValidValue(value)) {
|
|
59
59
|
let qb = this.dataSource
|
|
60
60
|
.createQueryBuilder()
|
|
61
61
|
.select('*')
|
|
62
62
|
.from(db_table_name, db_table_name)
|
|
63
63
|
.where(`${db_table_name}.${attr.attribute_key} = :value`, { value });
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
// Add AND condition for organization_id if present
|
|
66
66
|
const orgId = entityData.organization_id;
|
|
67
67
|
|
|
68
68
|
if (orgId !== undefined && orgId !== null) {
|
|
69
|
-
qb = qb.andWhere(
|
|
70
|
-
organization_id:
|
|
71
|
-
|
|
69
|
+
qb = qb.andWhere(
|
|
70
|
+
`${db_table_name}.organization_id = :organization_id`,
|
|
71
|
+
{
|
|
72
|
+
organization_id: orgId,
|
|
73
|
+
},
|
|
74
|
+
);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
|
|
75
77
|
const existing = await qb.limit(1).getRawOne();
|
|
76
|
-
|
|
78
|
+
|
|
77
79
|
if (existing) {
|
|
78
80
|
errors.push({
|
|
79
81
|
field: attr.name,
|
|
@@ -82,27 +84,71 @@ export class EntityValidationService {
|
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
|
-
|
|
87
|
+
|
|
88
|
+
return errors;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validates regex patterns for fields based on attribute metadata.
|
|
93
|
+
*/
|
|
94
|
+
async validateRegexFields(
|
|
95
|
+
entityData: Record<string, any>,
|
|
96
|
+
attributeData: AttributeMaster[],
|
|
97
|
+
): Promise<ValidationError[]> {
|
|
98
|
+
const errors: ValidationError[] = [];
|
|
99
|
+
|
|
100
|
+
for (const attr of attributeData.filter((a) => a.regex)) {
|
|
101
|
+
const value = entityData[attr.attribute_key];
|
|
102
|
+
if (this.hasValidValue(value)) {
|
|
103
|
+
const regex = new RegExp(attr.regex);
|
|
104
|
+
if (!regex.test(value)) {
|
|
105
|
+
errors.push({
|
|
106
|
+
field: attr.name,
|
|
107
|
+
message: `Field ${attr.name} does not match the required pattern.`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
return errors;
|
|
87
114
|
}
|
|
88
|
-
|
|
89
|
-
|
|
115
|
+
|
|
90
116
|
/**
|
|
91
117
|
* Validates both required and unique fields for a given entity type.
|
|
92
118
|
*/
|
|
93
119
|
async validateEntityData(
|
|
94
120
|
entityData: Record<string, any>,
|
|
95
|
-
entityMaster
|
|
121
|
+
entityMaster,
|
|
96
122
|
): Promise<ValidationError[]> {
|
|
97
|
-
const attributes =
|
|
98
|
-
|
|
123
|
+
const attributes =
|
|
124
|
+
await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
125
|
+
entityData.entity_type,
|
|
126
|
+
);
|
|
127
|
+
|
|
99
128
|
const requiredErrors = this.validateRequiredFields(entityData, attributes);
|
|
100
|
-
const uniqueErrors = await this.validateUniqueFields(
|
|
101
|
-
|
|
102
|
-
|
|
129
|
+
const uniqueErrors = await this.validateUniqueFields(
|
|
130
|
+
entityData,
|
|
131
|
+
attributes,
|
|
132
|
+
entityData.entity_type,
|
|
133
|
+
entityMaster.db_table_name,
|
|
134
|
+
);
|
|
135
|
+
const regexErros = await this.validateRegexFields(entityData, attributes);
|
|
136
|
+
return [...requiredErrors, ...uniqueErrors, ...regexErros];
|
|
103
137
|
}
|
|
104
|
-
|
|
105
138
|
|
|
139
|
+
async validateExcelEntityData(
|
|
140
|
+
entityData: Record<string, any>,
|
|
141
|
+
entityMaster,
|
|
142
|
+
): Promise<ValidationError[]> {
|
|
143
|
+
const attributes =
|
|
144
|
+
await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
145
|
+
entityData.entity_type,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const requiredErrors = this.validateRequiredFields(entityData, attributes);
|
|
149
|
+
const regexErros = await this.validateRegexFields(entityData, attributes);
|
|
150
|
+
return [...requiredErrors, ...regexErros];
|
|
151
|
+
}
|
|
106
152
|
private hasValidValue(value: any): boolean {
|
|
107
153
|
if (value === null || value === undefined) return false;
|
|
108
154
|
if (typeof value === 'string' && value.trim() === '') return false;
|