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/.idea/250218_nodejs_core.iml +12 -0
- package/.idea/codeStyles/Project.xml +59 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/prettier.xml +6 -0
- package/.idea/vcs.xml +6 -0
- package/dist/module/entity_json/entity_json.module.js +2 -1
- package/dist/module/entity_json/entity_json.module.js.map +1 -1
- package/dist/module/entity_json/service/entityJson.repository.d.ts +7 -0
- package/dist/module/entity_json/service/entityJson.repository.js +45 -0
- package/dist/module/entity_json/service/entityJson.repository.js.map +1 -0
- package/dist/module/entity_json/service/entity_json.service.d.ts +3 -1
- package/dist/module/entity_json/service/entity_json.service.js +48 -73
- package/dist/module/entity_json/service/entity_json.service.js.map +1 -1
- package/dist/module/filter/service/filter.service.js +44 -4
- package/dist/module/filter/service/filter.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/module/entity_json/entity_json.module.ts +2 -1
- package/src/module/entity_json/service/entityJson.repository.ts +37 -0
- package/src/module/entity_json/service/entity_json.service.ts +81 -165
- package/src/module/filter/service/filter.service.ts +56 -7
package/package.json
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
'
|
|
104
|
-
'
|
|
105
|
-
'
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
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
|
-
|
|
160
|
+
childEntityType,
|
|
220
161
|
linkAttr.saved_filter_code,
|
|
221
|
-
|
|
162
|
+
sourceKey,
|
|
222
163
|
mappingValue,
|
|
223
|
-
|
|
164
|
+
targetKey,
|
|
224
165
|
loggedInUser,
|
|
225
166
|
entityId,
|
|
226
167
|
);
|
|
227
|
-
|
|
168
|
+
|
|
228
169
|
if (value !== null && value !== undefined) {
|
|
229
|
-
|
|
230
|
-
|| (linkAttr.target_attribute_key || linkAttr.applicable_attribute_key);
|
|
231
|
-
flatJson[flatKey] = value;
|
|
170
|
+
flatJson[targetKey] = value;
|
|
232
171
|
}
|
|
233
172
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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;
|
|
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][
|
|
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 = `
|
|
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} <
|
|
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} >
|
|
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
|
|
976
|
+
// ===== TODAY =====
|
|
928
977
|
case 'today': {
|
|
929
|
-
const today =
|
|
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
|
|
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
|
}
|