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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "1.0.97",
3
+ "version": "1.0.99",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -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
- const refEntity = await this.entityMasterService.getEntityData(
233
- attr.datasource_list,
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
- row[attr.attribute_key] = refData[0].id;
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
- continue; // Skip this row
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' ,nullable: true })
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
  }
@@ -49,4 +49,7 @@ export class BaseEntity {
49
49
  @Column({ name: 'organization_id', type: 'int', nullable: true })
50
50
  @Expose()
51
51
  organization_id: number;
52
+ @Column({ name: 'appcode',type: 'varchar', length: 100, nullable: true })
53
+ @Expose()
54
+ appcode: string;
52
55
  }
@@ -141,7 +141,7 @@ protected readonly entityValidationService: EntityValidationService;
141
141
 
142
142
  return result;
143
143
  }
144
-
144
+
145
145
  async updateEntity(
146
146
  entityData: BaseEntity,
147
147
  loggedInUser: UserData | null,
@@ -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(`${db_table_name}.organization_id = :organization_id`, {
70
- organization_id: orgId,
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 = await this.attributeMasterService.findAttributesByMappedEntityType(entityData.entity_type);
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(entityData, attributes, entityData.entity_type,entityMaster.db_table_name);
101
-
102
- return [...requiredErrors, ...uniqueErrors];
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;