rez_core 4.0.290 → 4.0.292

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": "4.0.290",
3
+ "version": "4.0.292",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -6,11 +6,12 @@ import { EntityJSONController } from './controller/entity_json.controller';
6
6
  import { FilterModule } from '../filter/filter.module';
7
7
  import { UtilsModule } from 'src/utils/utils.module';
8
8
  import { EntityJson } from './entity/entityJson.entity';
9
+ import { EntityJSONRepository } from './service/entityJson.repository';
9
10
 
10
11
  @Module({
11
12
  imports: [EntityModule, TypeOrmModule.forFeature([EntityJson]),FilterModule,UtilsModule],
12
13
  controllers: [EntityJSONController],
13
- providers: [EntityJSONService],
14
+ providers: [EntityJSONService,EntityJSONRepository],
14
15
  exports: [],
15
16
  })
16
17
  export class EntityJSONModule {}
@@ -0,0 +1,37 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { InjectRepository } from "@nestjs/typeorm";
3
+ import { Repository } from "typeorm";
4
+ import { EntityJson } from "../entity/entityJson.entity";
5
+ import { create } from "domain";
6
+
7
+
8
+ @Injectable()
9
+ export class EntityJSONRepository {
10
+ constructor(
11
+ @InjectRepository(EntityJson)
12
+ private readonly entityJSONRepository: Repository<EntityJson>,
13
+ ) {}
14
+
15
+ async create(flatJson: Partial<EntityJson>) {
16
+ const { entity_type, entity_id } = flatJson;
17
+
18
+ // Step 1 — check if exists by unique keys
19
+ const existing = await this.entityJSONRepository.findOne({
20
+ where: { entity_type, entity_id },
21
+ });
22
+
23
+ if (existing) {
24
+ // Step 2 — normal update (no merge)
25
+ await this.entityJSONRepository.update(existing.id, flatJson);
26
+
27
+ // Optionally return updated row (common practice)
28
+ return this.entityJSONRepository.findOne({
29
+ where: { id: existing.id },
30
+ });
31
+ }
32
+
33
+ // Step 3 — insert new
34
+ const created = this.entityJSONRepository.create(flatJson);
35
+ return this.entityJSONRepository.save(created);
36
+ }
37
+ }
@@ -6,13 +6,15 @@ import { EntityRelation } from 'src/module/meta/entity/entity-relation.entity';
6
6
  import { EntityServiceImpl } from 'src/module/meta/service/entity-service-impl.service';
7
7
  import { LoggingService } from 'src/utils/service/loggingUtil.service';
8
8
  import { DataSource } from 'typeorm';
9
+ import { EntityJSONRepository } from './entityJson.repository';
9
10
 
10
11
  @Injectable()
11
12
  export class EntityJSONService extends EntityServiceImpl {
12
13
  constructor(
13
14
  private readonly dataSource: DataSource,
14
15
  private readonly filterService: FilterService,
15
- private readonly loggerService: LoggingService, // <-- inject logging service
16
+ private readonly loggerService: LoggingService,
17
+ private readonly EntityJSONRepository:EntityJSONRepository
16
18
  ) {
17
19
  super();
18
20
  }
@@ -23,27 +25,18 @@ export class EntityJSONService extends EntityServiceImpl {
23
25
  flag?: 'flat_json' | 'dropdown' | 'all',
24
26
  ) {
25
27
  const orgId = loggedInUser.organization_id;
26
- await this.loggerService.log(
27
- 'info',
28
- 'EntityJSONService',
29
- 'getAttributeForFlatJSON',
30
- `Loading attributes for entity: ${entityType}, org: ${orgId}`,
31
- );
28
+
29
+ await this.loggerService.log('info', 'EntityJSONService', 'getAttributeForFlatJSON', `Loading attributes for entity: ${entityType}, org: ${orgId}`);
32
30
 
33
31
  const mainAttributes = await this.dataSource
34
32
  .getRepository(AttributeMaster)
35
33
  .createQueryBuilder('attr')
36
- .select(['attr.id', 'attr.name', 'attr.flat_json_key','attr.attribute_key'])
34
+ .select(['attr.id', 'attr.name', 'attr.flat_json_key', 'attr.attribute_key'])
37
35
  .where('attr.mapped_entity_type = :entityType', { entityType })
38
36
  .andWhere('attr.organization_id = :orgId', { orgId })
39
37
  .getMany();
40
38
 
41
- await this.loggerService.log(
42
- 'debug',
43
- 'EntityJSONService',
44
- 'getAttributeForFlatJSON',
45
- `Loaded ${mainAttributes.length} main attributes`,
46
- );
39
+ await this.loggerService.log('debug', 'EntityJSONService', 'getAttributeForFlatJSON', `Loaded ${mainAttributes.length} main attributes`);
47
40
 
48
41
  const relatedEntityTypes = await this.dataSource
49
42
  .getRepository(EntityRelation)
@@ -51,44 +44,24 @@ export class EntityJSONService extends EntityServiceImpl {
51
44
  .select(['rel.target_entity_type'])
52
45
  .where('rel.source_entity_type = :entityType', { entityType })
53
46
  .andWhere('rel.organization_id = :orgId', { orgId })
54
- .andWhere('rel.relation_type = :relationType', {
55
- relationType: 'ONE_TO_ONE',
56
- })
47
+ .andWhere('rel.relation_type = :relationType', { relationType: 'ONE_TO_ONE' })
57
48
  .getRawMany();
58
49
 
59
- const relatedTypesList = relatedEntityTypes.map(
60
- (x) => x.rel_target_entity_type,
61
- );
62
- await this.loggerService.log(
63
- 'debug',
64
- 'EntityJSONService',
65
- 'getAttributeForFlatJSON',
66
- `Found ${relatedTypesList.length} ONE-TO-ONE related entity types`,
67
- );
50
+ const relatedTypesList = relatedEntityTypes.map(x => x.rel_target_entity_type);
51
+
52
+ await this.loggerService.log('debug', 'EntityJSONService', 'getAttributeForFlatJSON', `Found ${relatedTypesList.length} ONE-TO-ONE related entity types`);
68
53
 
69
54
  const relatedAttributes = relatedTypesList.length
70
55
  ? await this.dataSource
71
56
  .getRepository(AttributeMaster)
72
57
  .createQueryBuilder('attr')
73
- .select([
74
- 'attr.id',
75
- 'attr.name',
76
- 'attr.flat_json_key',
77
- 'attr.mapped_entity_type',
78
- 'attr.attribute_key',
79
- ])
80
- .where('attr.mapped_entity_type IN (:...types)', {
81
- types: relatedTypesList,
82
- })
58
+ .select(['attr.id', 'attr.name', 'attr.flat_json_key', 'attr.mapped_entity_type', 'attr.attribute_key'])
59
+ .where('attr.mapped_entity_type IN (:...types)', { types: relatedTypesList })
83
60
  .andWhere('attr.organization_id = :orgId', { orgId })
84
61
  .getMany()
85
62
  : [];
86
- await this.loggerService.log(
87
- 'debug',
88
- 'EntityJSONService',
89
- 'getAttributeForFlatJSON',
90
- `Loaded ${relatedAttributes.length} related attributes`,
91
- );
63
+
64
+ await this.loggerService.log('debug', 'EntityJSONService', 'getAttributeForFlatJSON', `Loaded ${relatedAttributes.length} related attributes`);
92
65
 
93
66
  const linkedAttributes = await this.dataSource
94
67
  .getRepository(LinkedAttributes)
@@ -99,103 +72,67 @@ export class EntityJSONService extends EntityServiceImpl {
99
72
  'fla.applicable_entity_type = attr.mapped_entity_type AND fla.applicable_attribute_key = attr.attribute_key',
100
73
  )
101
74
  .select([
102
- 'fla.applicable_entity_type',
103
- 'attr.id',
104
- 'attr.name',
105
- 'attr.attribute_key',
75
+ 'fla.applicable_entity_type AS applicable_entity_type',
76
+ 'fla.applicable_attribute_key AS applicable_attribute_key',
77
+ 'fla.attribute_key AS target_attribute_key',
78
+ 'fla.saved_filter_code AS saved_filter_code',
79
+ 'attr.name AS name',
80
+ 'attr.id AS id',
106
81
  ])
107
82
  .where('attr.organization_id = :orgId', { orgId })
108
83
  .getRawMany();
109
- await this.loggerService.log(
110
- 'debug',
111
- 'EntityJSONService',
112
- 'getAttributeForFlatJSON',
113
- `Loaded ${linkedAttributes.length} linked attributes`,
114
- );
84
+
85
+ await this.loggerService.log('debug', 'EntityJSONService', 'getAttributeForFlatJSON', `Loaded ${linkedAttributes.length} linked attributes`);
115
86
 
116
87
  if (flag === 'flat_json' || flag === 'all') {
117
88
  const result: Record<string, null> = {};
118
- mainAttributes.forEach((attr) => {
119
- if (attr.flat_json_key) result[attr.flat_json_key] = null;
120
- });
121
- relatedAttributes.forEach((attr) => {
122
- if (attr.flat_json_key) result[attr.flat_json_key] = null;
123
- });
124
- linkedAttributes.forEach((link) => {
125
- if (link.applicable_attribute_key)
126
- result[link.applicable_attribute_key] = null;
127
- });
128
- if(flag === 'all')
129
- return {flat_json: result,attributes: {mainAttributes,relatedAttributes,linkedAttributes}};
130
-
131
- return result;
89
+ mainAttributes.forEach(attr => { if (attr.flat_json_key) result[attr.flat_json_key] = null; });
90
+ relatedAttributes.forEach(attr => { if (attr.flat_json_key) result[attr.flat_json_key] = null; });
91
+ linkedAttributes.forEach(link => { if (link.target_attribute_key) result[link.target_attribute_key] = null; });
92
+
93
+ if (flag === 'all') {
94
+ return { flat_json: result, attributes: { mainAttributes, relatedAttributes, linkedAttributes } };
95
+ }
96
+
97
+ return result;
132
98
  }
133
99
 
134
100
  const dropdown: any[] = [];
135
- dropdown.push(
136
- ...mainAttributes.map((a) => ({ label: a.name, value: a.flat_json_key })),
137
- );
138
- dropdown.push(
139
- ...relatedAttributes.map((a) => ({
140
- label: a.name,
141
- value: a.flat_json_key,
142
- })),
143
- );
101
+ dropdown.push(...mainAttributes.map(a => ({ label: a.name, value: a.flat_json_key })));
102
+ dropdown.push(...relatedAttributes.map(a => ({ label: a.name, value: a.flat_json_key })));
144
103
  if (linkedAttributes.length > 0) {
145
- dropdown.push({
146
- ...linkedAttributes.map((a) => ({
147
- label: a.name,
148
- value: a.attribute_key,
149
- })),
150
- });
104
+ dropdown.push(...linkedAttributes.map(a => ({ label: a.name, value: a.attribute_key })));
151
105
  }
106
+
152
107
  return dropdown;
153
108
  }
154
109
 
155
110
  async updateEntityJSON(entityType: string, entityId: number, loggedInUser) {
156
- await this.loggerService.log(
157
- 'info',
158
- 'EntityJSONService',
159
- 'updateEntityJSON',
160
- `Building flat JSON for entity: ${entityType}#${entityId}`,
161
- );
162
-
163
- // 1. Load flat JSON template + attributes
111
+ await this.loggerService.log('info', 'EntityJSONService', 'updateEntityJSON', `Building flat JSON for entity: ${entityType}#${entityId}`);
112
+
164
113
  const response = await this.getAttributeForFlatJSON(entityType, loggedInUser, 'all');
165
-
166
- // ---- Structural validation ----
114
+
167
115
  if (!response || !('flat_json' in response) || !('attributes' in response) || !response.attributes) {
168
- await this.loggerService.log(
169
- 'error',
170
- 'EntityJSONService',
171
- 'updateEntityJSON',
172
- `getAttributeForFlatJSON() did not return expected structure`,
173
- );
116
+ await this.loggerService.log('error', 'EntityJSONService', 'updateEntityJSON', `getAttributeForFlatJSON() did not return expected structure`);
174
117
  return null;
175
118
  }
176
-
119
+
177
120
  const { flat_json: flatJson, attributes } = response;
178
121
  if (!flatJson) return null;
179
-
180
- // ---- Strong safety fix ----
122
+
181
123
  const safeAttributes = {
182
124
  mainAttributes: attributes.mainAttributes || [],
183
125
  relatedAttributes: attributes.relatedAttributes || [],
184
126
  linkedAttributes: attributes.linkedAttributes || [],
185
127
  };
186
-
187
- // 2. Build attribute_key → flat_json_key map
128
+
188
129
  const attrMap: Record<string, string> = {};
189
130
  const allAttrs = [...safeAttributes.mainAttributes, ...safeAttributes.relatedAttributes];
190
- allAttrs.forEach(attr => {
191
- if (attr.attribute_key) attrMap[attr.attribute_key] = attr.flat_json_key || attr.attribute_key;
192
- });
193
-
194
- // 3. Merge main entity data
131
+ allAttrs.forEach(attr => { if (attr.attribute_key) attrMap[attr.attribute_key] = attr.flat_json_key || attr.attribute_key; });
132
+
195
133
  const mainData = await this.getResolvedEntityData(entityType, entityId, loggedInUser);
196
134
  this.mergeEntityDataIntoFlatJson(flatJson, mainData, attrMap);
197
-
198
- // 4. Merge ONE-TO-ONE related entities
135
+
199
136
  const relations = await this.dataSource
200
137
  .getRepository('frm_entity_relation_data')
201
138
  .createQueryBuilder('erd')
@@ -204,67 +141,62 @@ export class EntityJSONService extends EntityServiceImpl {
204
141
  .andWhere('erd.source_entity_id = :entityId', { entityId })
205
142
  .andWhere('erd.relation_type = :rt', { rt: 'ONE_TO_ONE' })
206
143
  .getRawMany();
207
-
144
+
208
145
  for (const rel of relations) {
209
146
  const relatedData = await this.getResolvedEntityData(rel.target_entity_type, rel.target_entity_id, loggedInUser);
210
147
  this.mergeEntityDataIntoFlatJson(flatJson, relatedData, attrMap);
211
148
  }
212
-
213
- // 5. Merge linked attributes using saved filters
149
+
214
150
  for (const linkAttr of safeAttributes.linkedAttributes) {
215
- if (!linkAttr.applicable_entity_type || !linkAttr.applicable_attribute_key) continue;
216
-
217
- const mappingValue = mainData?.[linkAttr.parent_attribute_key || ''] ?? null;
151
+ const childEntityType = linkAttr.applicable_entity_type;
152
+ const sourceKey = linkAttr.applicable_attribute_key;
153
+ const targetKey = linkAttr.target_attribute_key;
154
+
155
+ if (!childEntityType || !sourceKey || !targetKey) continue;
156
+
157
+ const mappingValue = mainData?.[sourceKey] ?? null;
158
+
218
159
  const value = await this.applyLinkedFilterUsingSavedFilter(
219
- linkAttr.applicable_entity_type,
160
+ childEntityType,
220
161
  linkAttr.saved_filter_code,
221
- linkAttr.applicable_attribute_key,
162
+ sourceKey,
222
163
  mappingValue,
223
- linkAttr.target_attribute_key || linkAttr.applicable_attribute_key,
164
+ targetKey,
224
165
  loggedInUser,
225
166
  entityId,
226
167
  );
227
-
168
+
228
169
  if (value !== null && value !== undefined) {
229
- const flatKey = attrMap[linkAttr.target_attribute_key || linkAttr.applicable_attribute_key]
230
- || (linkAttr.target_attribute_key || linkAttr.applicable_attribute_key);
231
- flatJson[flatKey] = value;
170
+ flatJson[targetKey] = value;
232
171
  }
233
172
  }
234
-
235
- // 6. Save JSON
236
- await this.dataSource.query(
237
- `INSERT INTO frm_entity_json (entity_type, entity_id, json_data, created_by)
238
- VALUES (?, ?, ?, ?)
239
- ON DUPLICATE KEY UPDATE json_data = VALUES(json_data), updated_by = VALUES(created_by)`,
240
- [entityType, entityId, JSON.stringify(flatJson), loggedInUser.id],
241
- );
242
-
173
+
174
+ await this.loggerService.log('info', 'EntityJSONService', 'updateEntityJSON', `Saving flat JSON for entity: ${entityType}#${entityId}`);
175
+ let JsonData = {
176
+ entity_type: entityType,
177
+ entity_id: entityId,
178
+ json_data: flatJson,
179
+ created_by: loggedInUser.id,
180
+ }
181
+
182
+ await this.EntityJSONRepository.create(JsonData);
183
+
243
184
  return flatJson;
244
185
  }
245
-
246
-
247
- // Helper: Merge entity data using attribute_key → flat_json_key mapping
248
- private mergeEntityDataIntoFlatJson(
249
- flatJson: Record<string, any>,
250
- entityData: any | any[], // accept both object or array
251
- attrMap: Record<string, string>,
252
- ) {
186
+
187
+ private mergeEntityDataIntoFlatJson(flatJson: Record<string, any>, entityData: any | any[], attrMap: Record<string, string>) {
253
188
  const records = Array.isArray(entityData) ? entityData : [entityData];
254
-
189
+
255
190
  for (const record of records) {
256
191
  if (!record || typeof record !== 'object') continue;
257
-
258
192
  for (const key of Object.keys(record)) {
259
- const flatKey = attrMap[key] || key; // map to flat_json_key if exists
193
+ const flatKey = attrMap[key] || key;
260
194
  if (flatJson.hasOwnProperty(flatKey)) {
261
195
  flatJson[flatKey] = record[key];
262
196
  }
263
197
  }
264
198
  }
265
199
  }
266
-
267
-
268
200
 
269
201
  private async applyLinkedFilterUsingSavedFilter(
270
202
  childEntityType: string,
@@ -275,11 +207,7 @@ export class EntityJSONService extends EntityServiceImpl {
275
207
  loggedInUser,
276
208
  entity_id
277
209
  ) {
278
- if (
279
- !savedFilterCode &&
280
- (mappingValue === null || mappingValue === undefined)
281
- )
282
- return null;
210
+ if (!savedFilterCode && (mappingValue === null || mappingValue === undefined)) return null;
283
211
 
284
212
  const dto: any = {
285
213
  entity_type: childEntityType,
@@ -289,30 +217,18 @@ export class EntityJSONService extends EntityServiceImpl {
289
217
  size: 1,
290
218
  };
291
219
 
292
- if (
293
- mappingValue !== null &&
294
- mappingValue !== undefined &&
295
- mappingValue !== ''
296
- ) {
220
+ if (mappingValue !== null && mappingValue !== undefined && mappingValue !== '') {
297
221
  dto.quickFilter = [
298
- {
299
- filter_attribute: childFilterAttribute,
300
- filter_operator: 'equal',
301
- filter_value: mappingValue,
302
- },
222
+ { filter_attribute: childFilterAttribute, filter_operator: 'equal', filter_value: mappingValue },
303
223
  ];
304
224
  }
305
225
 
306
226
  dto.quickFilter = [
307
- {
308
- filter_attribute: 'parent_id',
309
- filter_operator: 'equal',
310
- filter_value: entity_id,
311
- },
227
+ { filter_attribute: 'parent_id', filter_operator: 'equal', filter_value: [entity_id] },
312
228
  ];
313
229
 
314
230
  const result = await this.filterService.applyFilter(dto);
315
231
  const rows = result?.data?.entity_list || [];
316
- return rows.length ? (rows[0][targetAttribute] ?? null) : null;
232
+ return rows.length ? (rows[0][childFilterAttribute] ?? null) : null;
317
233
  }
318
234
  }
@@ -849,7 +849,9 @@ export class FilterService {
849
849
 
850
850
  private buildDateCondition(attr: string, op: string, val: any, key: string) {
851
851
  const dateColumn = `DATE(e.${attr})`;
852
- const monthColumn = `DATE_TRUNC('month', e.${attr})`;
852
+ const monthColumn = `DATE_FORMAT(e.${attr}, '%Y-%m-01')`; // MySQL equivalent
853
+
854
+ const numVal = Number(val);
853
855
 
854
856
  switch (op) {
855
857
  // ===== BASIC COMPARISONS =====
@@ -888,17 +890,64 @@ export class FilterService {
888
890
 
889
891
  // ===== MONTH COMPARISONS =====
890
892
  case 'is_month_before':
893
+ if (!isNaN(numVal)) {
894
+ const target = moment()
895
+ .subtract(numVal, 'months')
896
+ .startOf('month')
897
+ .format('YYYY-MM-DD');
898
+ return {
899
+ query: `${monthColumn} < DATE_FORMAT(:${key}, '%Y-%m-01')`,
900
+ params: { [key]: target },
901
+ };
902
+ }
891
903
  return {
892
- query: `${monthColumn} < DATE_TRUNC('month', :${key})`,
904
+ query: `${monthColumn} < DATE_FORMAT(:${key}, '%Y-%m-01')`,
893
905
  params: { [key]: val },
894
906
  };
895
907
 
896
908
  case 'is_month_after':
909
+ if (!isNaN(numVal)) {
910
+ const target = moment()
911
+ .add(numVal, 'months')
912
+ .startOf('month')
913
+ .format('YYYY-MM-DD');
914
+ return {
915
+ query: `${monthColumn} > DATE_FORMAT(:${key}, '%Y-%m-01')`,
916
+ params: { [key]: target },
917
+ };
918
+ }
897
919
  return {
898
- query: `${monthColumn} > DATE_TRUNC('month', :${key})`,
920
+ query: `${monthColumn} > DATE_FORMAT(:${key}, '%Y-%m-01')`,
899
921
  params: { [key]: val },
900
922
  };
901
923
 
924
+ // ===== DAY BEFORE / AFTER =====
925
+ case 'is_day_before':
926
+ if (isNaN(numVal))
927
+ throw new BadRequestException(
928
+ 'Value must be a number for is_day_before',
929
+ );
930
+
931
+ const beforeDate = moment()
932
+ .subtract(numVal, 'days')
933
+ .format('YYYY-MM-DD');
934
+ return {
935
+ query: `${dateColumn} < :${key}`,
936
+ params: { [key]: beforeDate },
937
+ };
938
+
939
+ case 'is_day_after':
940
+ if (isNaN(numVal))
941
+ throw new BadRequestException(
942
+ 'Value must be a number for is_day_after',
943
+ );
944
+
945
+ const afterDate = moment().add(numVal, 'days').format('YYYY-MM-DD');
946
+ return {
947
+ query: `${dateColumn} > :${key}`,
948
+ params: { [key]: afterDate },
949
+ };
950
+
902
951
  // ===== BETWEEN =====
903
952
  case 'between': {
904
953
  if (typeof val === 'string') {
@@ -924,17 +973,18 @@ export class FilterService {
924
973
  };
925
974
  }
926
975
 
927
- // ===== TODAY KEYWORD =====
976
+ // ===== TODAY =====
928
977
  case 'today': {
929
- const today = new Date().toISOString().split('T')[0];
978
+ const today = moment().format('YYYY-MM-DD');
930
979
  return {
931
980
  query: `${dateColumn} = :today`,
932
981
  params: { today },
933
982
  };
934
983
  }
935
984
 
936
- // ===== EMPTY / NOT EMPTY =====
985
+ // ===== EMPTY =====
937
986
  case 'empty':
987
+ case 'is_empty':
938
988
  return {
939
989
  query: `e.${attr} IS NULL`,
940
990
  params: {},
@@ -946,7 +996,6 @@ export class FilterService {
946
996
  params: {},
947
997
  };
948
998
 
949
- // ===== UNSUPPORTED =====
950
999
  default:
951
1000
  throw new BadRequestException(`Unsupported operator for date: ${op}`);
952
1001
  }