rez_core 1.0.86 → 1.0.89
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/constant/global.constant.d.ts +2 -0
- package/dist/constant/global.constant.js +3 -1
- package/dist/constant/global.constant.js.map +1 -1
- package/dist/core.module.js +3 -0
- package/dist/core.module.js.map +1 -1
- package/dist/module/filter/controller/filter.controller.d.ts +26 -0
- package/dist/module/filter/controller/filter.controller.js +50 -0
- package/dist/module/filter/controller/filter.controller.js.map +1 -0
- package/dist/module/filter/dto/filter-request.dto.d.ts +24 -0
- package/dist/module/filter/dto/filter-request.dto.js +3 -0
- package/dist/module/filter/dto/filter-request.dto.js.map +1 -0
- package/dist/module/filter/entity/saved-filter-detail.entity.d.ts +8 -0
- package/dist/module/filter/entity/saved-filter-detail.entity.js +43 -0
- package/dist/module/filter/entity/saved-filter-detail.entity.js.map +1 -0
- package/dist/module/filter/entity/saved-filter-master.entity.d.ts +8 -0
- package/dist/module/filter/entity/saved-filter-master.entity.js +43 -0
- package/dist/module/filter/entity/saved-filter-master.entity.js.map +1 -0
- package/dist/module/filter/filter.module.d.ts +2 -0
- package/dist/module/filter/filter.module.js +34 -0
- package/dist/module/filter/filter.module.js.map +1 -0
- package/dist/module/filter/repository/saved-filter.repository.d.ts +21 -0
- package/dist/module/filter/repository/saved-filter.repository.js +86 -0
- package/dist/module/filter/repository/saved-filter.repository.js.map +1 -0
- package/dist/module/filter/service/filter.service.d.ts +47 -0
- package/dist/module/filter/service/filter.service.js +251 -0
- package/dist/module/filter/service/filter.service.js.map +1 -0
- package/dist/module/filter/service/saved-filter.service.d.ts +12 -0
- package/dist/module/filter/service/saved-filter.service.js +73 -0
- package/dist/module/filter/service/saved-filter.service.js.map +1 -0
- package/dist/module/listmaster/listmaster.module.js +3 -2
- package/dist/module/listmaster/listmaster.module.js.map +1 -1
- package/dist/module/listmaster/service/list-master.service.d.ts +4 -3
- package/dist/module/listmaster/service/list-master.service.js +5 -4
- package/dist/module/listmaster/service/list-master.service.js.map +1 -1
- package/dist/module/meta/controller/meta.controller.d.ts +3 -1
- package/dist/module/meta/controller/meta.controller.js +6 -3
- package/dist/module/meta/controller/meta.controller.js.map +1 -1
- package/dist/module/meta/dto/entity-table.dto.d.ts +6 -0
- package/dist/module/meta/dto/entity-table.dto.js.map +1 -1
- package/dist/module/meta/entity.module.js +14 -3
- package/dist/module/meta/entity.module.js.map +1 -1
- package/dist/module/meta/repository/entity-table-column.repository.d.ts +1 -1
- package/dist/module/meta/repository/entity-table-column.repository.js +5 -5
- package/dist/module/meta/repository/entity-table-column.repository.js.map +1 -1
- package/dist/module/meta/service/attribute-master.service.d.ts +1 -1
- package/dist/module/meta/service/attribute-master.service.js.map +1 -1
- package/dist/module/meta/service/entity-table-column.service.d.ts +1 -1
- package/dist/module/meta/service/entity-table-column.service.js +2 -2
- package/dist/module/meta/service/entity-table-column.service.js.map +1 -1
- package/dist/module/meta/service/entity-table.service.d.ts +7 -3
- package/dist/module/meta/service/entity-table.service.js +22 -5
- package/dist/module/meta/service/entity-table.service.js.map +1 -1
- package/dist/module/meta/service/media-data.service.d.ts +1 -1
- package/dist/module/meta/service/media-data.service.js +1 -1
- package/dist/module/meta/service/media-data.service.js.map +1 -1
- package/dist/module/module/entity/module.entity.d.ts +1 -0
- package/dist/module/module/entity/module.entity.js +4 -0
- package/dist/module/module/entity/module.entity.js.map +1 -1
- package/dist/resources/properties.yaml.js +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/constant/global.constant.ts +2 -0
- package/src/core.module.ts +4 -2
- package/src/module/filter/controller/filter.controller.ts +34 -0
- package/src/module/filter/dto/filter-request.dto.ts +26 -0
- package/src/module/filter/entity/saved-filter-detail.entity.ts +23 -0
- package/src/module/filter/entity/saved-filter-master.entity.ts +23 -0
- package/src/module/filter/filter.module.ts +21 -0
- package/src/module/filter/repository/saved-filter.repository.ts +87 -0
- package/src/module/filter/service/filter.service.ts +326 -0
- package/src/module/filter/service/saved-filter.service.ts +103 -0
- package/src/module/listmaster/listmaster.module.ts +4 -3
- package/src/module/listmaster/service/list-master.service.ts +3 -2
- package/src/module/meta/controller/meta.controller.ts +6 -3
- package/src/module/meta/dto/entity-table.dto.ts +4 -0
- package/src/module/meta/entity.module.ts +15 -5
- package/src/module/meta/repository/entity-table-column.repository.ts +9 -5
- package/src/module/meta/service/attribute-master.service.ts +9 -9
- package/src/module/meta/service/entity-table-column.service.ts +6 -1
- package/src/module/meta/service/entity-table.service.ts +24 -4
- package/src/module/meta/service/media-data.service.ts +1 -1
- package/src/module/module/entity/module.entity.ts +3 -1
- package/src/resources/dev.properties.yaml +2 -2
- package/src/resources/properties.yaml.ts +1 -1
package/package.json
CHANGED
|
@@ -12,6 +12,8 @@ export const ENTITYTYPE_LISTMASTERITEMS = 'LMI';
|
|
|
12
12
|
export const ENTITYTYPE_SECTIONMASTER = 'SEC';
|
|
13
13
|
export const ENTITYTYPE_FIELDMASTER = 'FLD';
|
|
14
14
|
export const ENTITYTYPE_USRROLMAP = 'URM';
|
|
15
|
+
export const ENTITYTYPE_SAVEDFILTERMASTER = 'SFM';
|
|
16
|
+
export const ENTITYTYPE_SAVEDFILTERDETAIL = 'SFD';
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
export const STORAGETYPE_SINGLEJSON = 'singlejson';
|
package/src/core.module.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { ModuleModule } from './module/module/module.module';
|
|
|
8
8
|
import { NotificationModule } from './module/notification/notification.module';
|
|
9
9
|
import { LayoutModule } from './module/layout/layout.module';
|
|
10
10
|
import { ListMasterModule } from './module/listmaster/listmaster.module';
|
|
11
|
+
import { FilterModule } from './module/filter/filter.module';
|
|
11
12
|
|
|
12
13
|
@Global()
|
|
13
14
|
@Module({})
|
|
@@ -21,7 +22,7 @@ export class CoreModule {
|
|
|
21
22
|
module: CoreModule,
|
|
22
23
|
imports: [
|
|
23
24
|
ConfigModule.forRoot({ isGlobal: true }),
|
|
24
|
-
dbModule,
|
|
25
|
+
dbModule,
|
|
25
26
|
EntityModule,
|
|
26
27
|
EnterpriseModule,
|
|
27
28
|
UtilsModule,
|
|
@@ -30,6 +31,7 @@ export class CoreModule {
|
|
|
30
31
|
NotificationModule,
|
|
31
32
|
LayoutModule,
|
|
32
33
|
ListMasterModule,
|
|
34
|
+
FilterModule
|
|
33
35
|
],
|
|
34
36
|
exports: [
|
|
35
37
|
ConfigModule,
|
|
@@ -42,7 +44,7 @@ export class CoreModule {
|
|
|
42
44
|
NotificationModule,
|
|
43
45
|
LayoutModule,
|
|
44
46
|
ListMasterModule,
|
|
45
|
-
|
|
47
|
+
FilterModule
|
|
46
48
|
],
|
|
47
49
|
};
|
|
48
50
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Controller, Post, Body, Query, ParseIntPipe } from '@nestjs/common';
|
|
2
|
+
import { FilterService } from '../service/filter.service';
|
|
3
|
+
|
|
4
|
+
@Controller('filter')
|
|
5
|
+
export class FilterController {
|
|
6
|
+
constructor(private readonly filterService: FilterService) {}
|
|
7
|
+
|
|
8
|
+
@Post()
|
|
9
|
+
async applyFilter(
|
|
10
|
+
@Body() body: any,
|
|
11
|
+
@Query('page', ParseIntPipe) page: number = 1,
|
|
12
|
+
@Query('size', ParseIntPipe) size: number = 10,
|
|
13
|
+
) {
|
|
14
|
+
const {
|
|
15
|
+
entity_type,
|
|
16
|
+
quickFilter,
|
|
17
|
+
savedFilterCode,
|
|
18
|
+
attributeFilter,
|
|
19
|
+
tabs,
|
|
20
|
+
sortby,
|
|
21
|
+
} = body;
|
|
22
|
+
|
|
23
|
+
return this.filterService.applyFilter({
|
|
24
|
+
entity_type,
|
|
25
|
+
quickFilter,
|
|
26
|
+
savedFilterCode,
|
|
27
|
+
attributeFilter,
|
|
28
|
+
tabs,
|
|
29
|
+
sortby,
|
|
30
|
+
page,
|
|
31
|
+
size,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/module/filter/dto/filter-request.dto.ts
|
|
2
|
+
export interface FilterCondition {
|
|
3
|
+
filter_attribute: string;
|
|
4
|
+
filter_operator: string;
|
|
5
|
+
filter_value?: string | number | boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface TabsConfig {
|
|
9
|
+
columnName: string;
|
|
10
|
+
value: string;
|
|
11
|
+
sortBy?: 'ASC' | 'DSC';
|
|
12
|
+
}
|
|
13
|
+
export interface SortConfig {
|
|
14
|
+
sortColum: string;
|
|
15
|
+
sortType: 'ASC' | 'DSC';
|
|
16
|
+
}
|
|
17
|
+
export interface FilterRequestDto {
|
|
18
|
+
entity_type: string;
|
|
19
|
+
quickFilter?: FilterCondition[];
|
|
20
|
+
savedFilterCode?: string;
|
|
21
|
+
attributeFilter?: FilterCondition[];
|
|
22
|
+
sortby?: SortConfig[];
|
|
23
|
+
tabs?: TabsConfig;
|
|
24
|
+
page?: number;
|
|
25
|
+
size?: number;
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ENTITYTYPE_SAVEDFILTERDETAIL } from "src/constant/global.constant";
|
|
2
|
+
import { BaseEntity } from "src/module/meta/entity/base-entity.entity";
|
|
3
|
+
import { Column, Entity } from "typeorm";
|
|
4
|
+
|
|
5
|
+
@Entity({ name: 'cr_saved_filter_detail' })
|
|
6
|
+
export class SavedFilterDetail extends BaseEntity {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.entity_type = ENTITYTYPE_SAVEDFILTERDETAIL;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Column()
|
|
13
|
+
mapped_filter_code: string; // This links to SavedFilterMaster.code (no relation, manual)
|
|
14
|
+
|
|
15
|
+
@Column()
|
|
16
|
+
filter_attribute: string;
|
|
17
|
+
|
|
18
|
+
@Column()
|
|
19
|
+
filter_operator: string;
|
|
20
|
+
|
|
21
|
+
@Column()
|
|
22
|
+
filter_value: string;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ENTITYTYPE_SAVEDFILTERMASTER } from "src/constant/global.constant";
|
|
2
|
+
import { BaseEntity } from "src/module/meta/entity/base-entity.entity";
|
|
3
|
+
import { Column, Entity } from "typeorm";
|
|
4
|
+
|
|
5
|
+
@Entity({ name: 'cr_saved_filter_master' })
|
|
6
|
+
export class SavedFilterMaster extends BaseEntity {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.entity_type = ENTITYTYPE_SAVEDFILTERMASTER;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Column({ length: 50, type: 'varchar', nullable: true })
|
|
13
|
+
app_code: string;
|
|
14
|
+
|
|
15
|
+
@Column({ length: 50, type: 'varchar', nullable: true })
|
|
16
|
+
mapped_entity_type: string;
|
|
17
|
+
|
|
18
|
+
@Column({ type: 'int', nullable: true })
|
|
19
|
+
user_id: number;
|
|
20
|
+
|
|
21
|
+
@Column({ type: 'boolean', default: false })
|
|
22
|
+
is_default: boolean;
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { forwardRef, Module } from '@nestjs/common';
|
|
2
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
3
|
+
import { SavedFilterService } from './service/saved-filter.service';
|
|
4
|
+
import { SavedFilterDetail } from './entity/saved-filter-detail.entity';
|
|
5
|
+
import { SavedFilterMaster } from './entity/saved-filter-master.entity';
|
|
6
|
+
import { SavedFilterRepositoryService } from './repository/saved-filter.repository';
|
|
7
|
+
import { EntityModule } from '../meta/entity.module';
|
|
8
|
+
import { FilterService } from './service/filter.service';
|
|
9
|
+
import { FilterController } from './controller/filter.controller';
|
|
10
|
+
|
|
11
|
+
@Module({
|
|
12
|
+
imports: [TypeOrmModule.forFeature([SavedFilterDetail,SavedFilterMaster]),forwardRef(() => EntityModule)],
|
|
13
|
+
controllers: [FilterController],
|
|
14
|
+
providers: [
|
|
15
|
+
{ provide: 'SavedFilterService', useClass: SavedFilterService },
|
|
16
|
+
SavedFilterRepositoryService,
|
|
17
|
+
FilterService
|
|
18
|
+
],
|
|
19
|
+
exports: ['SavedFilterService', SavedFilterRepositoryService],
|
|
20
|
+
})
|
|
21
|
+
export class FilterModule {}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { Repository, Not } from 'typeorm';
|
|
4
|
+
import { SavedFilterMaster } from '../entity/saved-filter-master.entity';
|
|
5
|
+
import { SavedFilterDetail } from '../entity/saved-filter-detail.entity';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class SavedFilterRepositoryService {
|
|
9
|
+
constructor(
|
|
10
|
+
@InjectRepository(SavedFilterMaster)
|
|
11
|
+
private readonly savedFilterMasterRepo: Repository<SavedFilterMaster>,
|
|
12
|
+
@InjectRepository(SavedFilterDetail)
|
|
13
|
+
private readonly savedFilterDetailRepo: Repository<SavedFilterDetail>,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
async isFilterNameExists(
|
|
17
|
+
name: string,
|
|
18
|
+
orgId: number,
|
|
19
|
+
entId: number,
|
|
20
|
+
excludeId?: number,
|
|
21
|
+
): Promise<boolean> {
|
|
22
|
+
const where: any = {
|
|
23
|
+
name,
|
|
24
|
+
organization_id: orgId,
|
|
25
|
+
enterprise_id: entId,
|
|
26
|
+
};
|
|
27
|
+
if (excludeId) {
|
|
28
|
+
where.id = Not(excludeId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const existing = await this.savedFilterMasterRepo.findOne({ where });
|
|
32
|
+
return !!existing;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getDetailsByCode(code: string): Promise<SavedFilterDetail[]> {
|
|
36
|
+
return this.savedFilterDetailRepo.find({
|
|
37
|
+
where: { mapped_filter_code: code },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async deleteDetailsByCode(code: string): Promise<void> {
|
|
42
|
+
await this.savedFilterDetailRepo.delete({ mapped_filter_code: code });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async saveDetails(details: Partial<SavedFilterDetail>[]): Promise<void> {
|
|
46
|
+
const entities = this.savedFilterDetailRepo.create(details);
|
|
47
|
+
await this.savedFilterDetailRepo.save(entities);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async updateCode(id: number, code: string): Promise<void> {
|
|
51
|
+
await this.savedFilterMasterRepo.update(id, { code });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getDefaultFilterByEntityType(entityType: string): Promise<{ label: string; value: number } | null> {
|
|
55
|
+
const filter = await this.savedFilterMasterRepo.findOne({
|
|
56
|
+
where: {
|
|
57
|
+
mapped_entity_type: entityType,
|
|
58
|
+
is_default: true,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return filter ? { label: filter.name, value: filter.id } : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getSavedFiltersByUserIdAndEntityType(
|
|
66
|
+
userId: number,
|
|
67
|
+
entityType: string
|
|
68
|
+
){
|
|
69
|
+
const filters = await this.savedFilterMasterRepo.find({
|
|
70
|
+
where: {
|
|
71
|
+
user_id: userId,
|
|
72
|
+
mapped_entity_type: entityType,
|
|
73
|
+
},
|
|
74
|
+
order: {
|
|
75
|
+
modified_date: 'DESC',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return filters.map(filter => ({
|
|
80
|
+
label: filter.name,
|
|
81
|
+
value: filter.id,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { Injectable, BadRequestException, Inject } from '@nestjs/common';
|
|
2
|
+
import { AttributeMasterService } from 'src/module/meta/service/attribute-master.service';
|
|
3
|
+
import { EntityMasterService } from 'src/module/meta/service/entity-master.service';
|
|
4
|
+
import { DataSource } from 'typeorm';
|
|
5
|
+
import { SavedFilterService } from './saved-filter.service';
|
|
6
|
+
import { FilterRequestDto } from '../dto/filter-request.dto';
|
|
7
|
+
import { EntityTableService } from 'src/module/meta/service/entity-table.service';
|
|
8
|
+
import { EntityTableColumnService } from 'src/module/meta/service/entity-table-column.service';
|
|
9
|
+
import { get } from 'http';
|
|
10
|
+
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class FilterService {
|
|
13
|
+
constructor(
|
|
14
|
+
private dataSource: DataSource,
|
|
15
|
+
private readonly entityMasterService: EntityMasterService,
|
|
16
|
+
private readonly attributeMasterService: AttributeMasterService,
|
|
17
|
+
private readonly entityTableService: EntityTableService,
|
|
18
|
+
private readonly entityTableColumnService: EntityTableColumnService,
|
|
19
|
+
@Inject('SavedFilterService')
|
|
20
|
+
private readonly savedFilterService: SavedFilterService,
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
private async gettab_value_counts(
|
|
24
|
+
tableName: string,
|
|
25
|
+
column: string | undefined,
|
|
26
|
+
whereClauses: any[],
|
|
27
|
+
) {
|
|
28
|
+
if (!column) return [];
|
|
29
|
+
|
|
30
|
+
const qb = this.dataSource
|
|
31
|
+
.getRepository(tableName)
|
|
32
|
+
.createQueryBuilder('e')
|
|
33
|
+
.select(`e.${column}`, 'tab_value')
|
|
34
|
+
.addSelect('COUNT(*)', 'tab_value_count')
|
|
35
|
+
.groupBy(`e.${column}`);
|
|
36
|
+
|
|
37
|
+
whereClauses.forEach((clause) => qb.andWhere(clause.query, clause.params));
|
|
38
|
+
|
|
39
|
+
const rows = await qb.getRawMany();
|
|
40
|
+
|
|
41
|
+
const total = rows.reduce((sum, r) => sum + parseInt(r.tab_value_count), 0);
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
{ tab_value: 'All', tab_value_count: total },
|
|
45
|
+
...rows.map((r) => ({
|
|
46
|
+
tab_value: r.tab_value || 'UNKNOWN',
|
|
47
|
+
tab_value_count: parseInt(r.tab_value_count),
|
|
48
|
+
})),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async applyFilter(dto: FilterRequestDto) {
|
|
53
|
+
const {
|
|
54
|
+
entity_type,
|
|
55
|
+
quickFilter,
|
|
56
|
+
savedFilterCode,
|
|
57
|
+
attributeFilter,
|
|
58
|
+
tabs,
|
|
59
|
+
sortby,
|
|
60
|
+
} = dto;
|
|
61
|
+
|
|
62
|
+
// Fetch meta
|
|
63
|
+
const entityMeta =
|
|
64
|
+
await this.entityMasterService.getEntityData(entity_type);
|
|
65
|
+
const tableName = entityMeta?.db_table_name;
|
|
66
|
+
if (!tableName)
|
|
67
|
+
throw new BadRequestException(`Invalid entity_type: ${entity_type}`);
|
|
68
|
+
|
|
69
|
+
const getTableMeta =
|
|
70
|
+
await this.entityTableService.findByEntityTypeAndListTypeAndDisplayType(
|
|
71
|
+
entity_type,
|
|
72
|
+
entity_type,
|
|
73
|
+
'LIST',
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (!getTableMeta) {
|
|
77
|
+
throw new BadRequestException(
|
|
78
|
+
`Table meta not found for entity_type: ${entity_type}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const getTableColumnMeta =
|
|
82
|
+
await this.entityTableColumnService.findByParentIdAndParentType(
|
|
83
|
+
getTableMeta.id,
|
|
84
|
+
getTableMeta.entity_type,
|
|
85
|
+
true,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// const attributeMeta =
|
|
89
|
+
// await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
90
|
+
// entity_type,
|
|
91
|
+
// );
|
|
92
|
+
const attributeMetaMap = getTableColumnMeta.reduce(
|
|
93
|
+
(acc, attr) => {
|
|
94
|
+
acc[attr.attribute_key] = attr;
|
|
95
|
+
return acc;
|
|
96
|
+
},
|
|
97
|
+
{} as Record<string, any>,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Get and parse filters
|
|
101
|
+
const savedFilters = await this.getSavedFilters(savedFilterCode);
|
|
102
|
+
const baseFilters = [
|
|
103
|
+
...(quickFilter || []),
|
|
104
|
+
...savedFilters,
|
|
105
|
+
...(attributeFilter || []),
|
|
106
|
+
];
|
|
107
|
+
const baseWhere = this.buildWhereClauses(baseFilters, attributeMetaMap);
|
|
108
|
+
|
|
109
|
+
// Build query for tab counts (no tab.value filter here)
|
|
110
|
+
const allTabs = await this.gettab_value_counts(
|
|
111
|
+
tableName,
|
|
112
|
+
tabs?.columnName,
|
|
113
|
+
baseWhere,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Add tab.value filter only if present
|
|
117
|
+
const dataWhere = [...baseWhere];
|
|
118
|
+
if (tabs?.columnName && tabs?.value) {
|
|
119
|
+
const tabAttrMeta = attributeMetaMap[tabs.columnName];
|
|
120
|
+
const tabCondition = this.buildCondition(
|
|
121
|
+
{
|
|
122
|
+
filter_attribute: tabs.columnName,
|
|
123
|
+
filter_operator: 'equal',
|
|
124
|
+
filter_value: tabs.value,
|
|
125
|
+
},
|
|
126
|
+
tabAttrMeta,
|
|
127
|
+
);
|
|
128
|
+
if (tabCondition) {
|
|
129
|
+
dataWhere.push(tabCondition);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Final query for filtered data
|
|
134
|
+
// Final query for filtered data
|
|
135
|
+
const qb = this.dataSource.getRepository(tableName).createQueryBuilder('e');
|
|
136
|
+
dataWhere.forEach((clause) => qb.andWhere(clause.query, clause.params));
|
|
137
|
+
|
|
138
|
+
// Optional sorting
|
|
139
|
+
if (Array.isArray(sortby)) {
|
|
140
|
+
sortby.forEach(({ sortColum, sortType }) => {
|
|
141
|
+
qb.addOrderBy(
|
|
142
|
+
`e.${sortColum}`,
|
|
143
|
+
sortType.toUpperCase() === 'DSC' ? 'DESC' : 'ASC',
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Pagination
|
|
149
|
+
const page = dto.page && dto.page > 0 ? dto.page : 1;
|
|
150
|
+
const size = dto.size && dto.size > 0 ? dto.size : 10;
|
|
151
|
+
qb.skip((page - 1) * size).take(size);
|
|
152
|
+
|
|
153
|
+
const [entity_list, total] = await qb.getManyAndCount();
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
data: {
|
|
158
|
+
entity_tabs: allTabs,
|
|
159
|
+
entity_list,
|
|
160
|
+
pagination: {
|
|
161
|
+
total,
|
|
162
|
+
page,
|
|
163
|
+
size,
|
|
164
|
+
totalPages: Math.ceil(total / size),
|
|
165
|
+
hasNextPage: page * size < total,
|
|
166
|
+
hasPreviousPage: page > 1,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private parseFilters(raw: any, isSingle = false): any[] {
|
|
173
|
+
if (!raw) return [];
|
|
174
|
+
|
|
175
|
+
if (typeof raw === 'string') {
|
|
176
|
+
try {
|
|
177
|
+
const parsed = JSON.parse(raw);
|
|
178
|
+
return isSingle ? [parsed] : parsed;
|
|
179
|
+
} catch {
|
|
180
|
+
throw new BadRequestException('Invalid JSON in filter input');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return isSingle ? [raw] : Array.isArray(raw) ? raw : [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async getSavedFilters(code?: string): Promise<any[]> {
|
|
188
|
+
if (!code) return [];
|
|
189
|
+
|
|
190
|
+
const savedFilter = await this.savedFilterService.getDetailsByCode(code);
|
|
191
|
+
if (!savedFilter) {
|
|
192
|
+
throw new BadRequestException(`Saved filter not found for code: ${code}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log(savedFilter);
|
|
196
|
+
return savedFilter;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private buildWhereClauses(
|
|
200
|
+
filters: any[],
|
|
201
|
+
attributeMeta: Record<string, any>,
|
|
202
|
+
) {
|
|
203
|
+
return filters
|
|
204
|
+
.map((f) => this.buildCondition(f, attributeMeta[f.filter_attribute]))
|
|
205
|
+
.filter(
|
|
206
|
+
(clause): clause is { query: string; params: any } => clause !== null,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private buildCondition(
|
|
211
|
+
filter: any,
|
|
212
|
+
meta: any,
|
|
213
|
+
): { query: string; params: any } | null {
|
|
214
|
+
if (!meta) return null;
|
|
215
|
+
|
|
216
|
+
const attr = filter.filter_attribute;
|
|
217
|
+
const val = filter.filter_value;
|
|
218
|
+
const op = filter.filter_operator;
|
|
219
|
+
const key = `param_${attr}_${Math.random().toString(36).substring(2, 8)}`;
|
|
220
|
+
|
|
221
|
+
switch (meta.data_type) {
|
|
222
|
+
case 'text':
|
|
223
|
+
return this.buildTextCondition(attr, op, val, key);
|
|
224
|
+
case 'number':
|
|
225
|
+
return this.buildNumberCondition(attr, op, val, key);
|
|
226
|
+
case 'date':
|
|
227
|
+
return this.buildDateCondition(attr, op, val, key);
|
|
228
|
+
case 'select':
|
|
229
|
+
return this.buildSelectCondition(attr, op, val, key);
|
|
230
|
+
case 'multiselect':
|
|
231
|
+
return this.buildMultiSelectCondition(attr, val, key);
|
|
232
|
+
default:
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private buildTextCondition(attr: string, op: string, val: any, key: string) {
|
|
238
|
+
switch (op) {
|
|
239
|
+
case 'contains':
|
|
240
|
+
return {
|
|
241
|
+
query: `LOWER(e.${attr}) LIKE :${key}`,
|
|
242
|
+
params: { [key]: `%${val.toLowerCase()}%` },
|
|
243
|
+
};
|
|
244
|
+
case 'equal':
|
|
245
|
+
return {
|
|
246
|
+
query: `e.${attr} = :${key}`,
|
|
247
|
+
params: { [key]: val },
|
|
248
|
+
};
|
|
249
|
+
case 'not_equal':
|
|
250
|
+
return {
|
|
251
|
+
query: `e.${attr} != :${key}`,
|
|
252
|
+
params: { [key]: val },
|
|
253
|
+
};
|
|
254
|
+
case 'empty':
|
|
255
|
+
return {
|
|
256
|
+
query: `e.${attr} IS NULL`,
|
|
257
|
+
params: {},
|
|
258
|
+
};
|
|
259
|
+
case 'not_empty':
|
|
260
|
+
return {
|
|
261
|
+
query: `e.${attr} IS NOT NULL`,
|
|
262
|
+
params: {},
|
|
263
|
+
};
|
|
264
|
+
default:
|
|
265
|
+
throw new BadRequestException(`Unsupported operator for text: ${op}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private buildNumberCondition(
|
|
270
|
+
attr: string,
|
|
271
|
+
op: string,
|
|
272
|
+
val: any,
|
|
273
|
+
key: string,
|
|
274
|
+
) {
|
|
275
|
+
switch (op) {
|
|
276
|
+
case 'equal':
|
|
277
|
+
return { query: `e.${attr} = :${key}`, params: { [key]: val } };
|
|
278
|
+
case 'not_equal':
|
|
279
|
+
return { query: `e.${attr} != :${key}`, params: { [key]: val } };
|
|
280
|
+
case 'greater_than':
|
|
281
|
+
return { query: `e.${attr} > :${key}`, params: { [key]: val } };
|
|
282
|
+
case 'less_than':
|
|
283
|
+
return { query: `e.${attr} < :${key}`, params: { [key]: val } };
|
|
284
|
+
default:
|
|
285
|
+
throw new BadRequestException(`Unsupported operator for number: ${op}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private buildDateCondition(attr: string, op: string, val: any, key: string) {
|
|
290
|
+
switch (op) {
|
|
291
|
+
case 'equal':
|
|
292
|
+
return { query: `e.${attr} = :${key}`, params: { [key]: val } };
|
|
293
|
+
case 'before':
|
|
294
|
+
return { query: `e.${attr} < :${key}`, params: { [key]: val } };
|
|
295
|
+
case 'after':
|
|
296
|
+
return { query: `e.${attr} > :${key}`, params: { [key]: val } };
|
|
297
|
+
default:
|
|
298
|
+
throw new BadRequestException(`Unsupported operator for date: ${op}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private buildSelectCondition(
|
|
303
|
+
attr: string,
|
|
304
|
+
op: string,
|
|
305
|
+
val: any,
|
|
306
|
+
key: string,
|
|
307
|
+
) {
|
|
308
|
+
switch (op) {
|
|
309
|
+
case 'equal':
|
|
310
|
+
return { query: `e.${attr} = :${key}`, params: { [key]: val } };
|
|
311
|
+
|
|
312
|
+
default:
|
|
313
|
+
throw new BadRequestException(`Unsupported operator for select: ${op}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private buildMultiSelectCondition(attr: string, val: any[], key: string) {
|
|
318
|
+
if (!Array.isArray(val)) {
|
|
319
|
+
throw new BadRequestException(`Expected array for multiselect: ${attr}`);
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
query: `e.${attr} IN (:...${key})`,
|
|
323
|
+
params: { [key]: val },
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
2
|
+
import { EntityServiceImpl } from '../../meta/service/entity-service-impl.service';
|
|
3
|
+
import { BaseEntity } from '../../meta/entity/base-entity.entity';
|
|
4
|
+
import { UserData } from '../../user/entity/user.entity';
|
|
5
|
+
import { SavedFilterMaster } from '../entity/saved-filter-master.entity';
|
|
6
|
+
import { SavedFilterDetail } from '../entity/saved-filter-detail.entity';
|
|
7
|
+
import { ENTITYTYPE_SAVEDFILTERMASTER } from 'src/constant/global.constant';
|
|
8
|
+
import { SavedFilterRepositoryService } from '../repository/saved-filter.repository';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class SavedFilterService extends EntityServiceImpl {
|
|
12
|
+
constructor(private readonly savedFilterRepo: SavedFilterRepositoryService) {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async createEntity(
|
|
17
|
+
entityData: BaseEntity,
|
|
18
|
+
loggedInUser: UserData | null,
|
|
19
|
+
): Promise<BaseEntity> {
|
|
20
|
+
const master = entityData as SavedFilterMaster;
|
|
21
|
+
|
|
22
|
+
const exists = await this.savedFilterRepo.isFilterNameExists(
|
|
23
|
+
master.name,
|
|
24
|
+
master.organization_id,
|
|
25
|
+
master.enterprise_id,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (exists) {
|
|
29
|
+
throw new BadRequestException('Saved filter name already exists.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Save the master record without code
|
|
33
|
+
const savedMaster = await super.createEntity(master, loggedInUser);
|
|
34
|
+
|
|
35
|
+
// Generate the code like SFM00001 using the saved ID
|
|
36
|
+
const code = `SFM${savedMaster.id.toString().padStart(5, '0')}`;
|
|
37
|
+
|
|
38
|
+
// Update the code
|
|
39
|
+
savedMaster.code = code;
|
|
40
|
+
await this.savedFilterRepo.updateCode(savedMaster.id, code);
|
|
41
|
+
|
|
42
|
+
// Save filter details using generated code
|
|
43
|
+
const details = (entityData as any).filterDetails ?? [];
|
|
44
|
+
if (details.length) {
|
|
45
|
+
const prepared = details.map((detail: any) => ({
|
|
46
|
+
...detail,
|
|
47
|
+
mapped_filter_code: savedMaster.code,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
await this.savedFilterRepo.saveDetails(prepared);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return savedMaster;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async updateEntity(
|
|
57
|
+
entityData: BaseEntity,
|
|
58
|
+
loggedInUser: UserData | null,
|
|
59
|
+
): Promise<BaseEntity> {
|
|
60
|
+
const master = entityData as SavedFilterMaster;
|
|
61
|
+
|
|
62
|
+
const existing = await this.getEntityData(
|
|
63
|
+
ENTITYTYPE_SAVEDFILTERMASTER,
|
|
64
|
+
master.id,
|
|
65
|
+
);
|
|
66
|
+
if (!existing) {
|
|
67
|
+
throw new BadRequestException('Saved filter not found.');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const nameExists = await this.savedFilterRepo.isFilterNameExists(
|
|
71
|
+
master.name,
|
|
72
|
+
master.organization_id,
|
|
73
|
+
master.enterprise_id,
|
|
74
|
+
master.id,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (nameExists) {
|
|
78
|
+
throw new BadRequestException('Saved filter name already exists.');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const { filterDetails, ...persistableData } = master as any;
|
|
82
|
+
const updated = await super.updateEntity(persistableData, loggedInUser);
|
|
83
|
+
|
|
84
|
+
if (filterDetails?.length) {
|
|
85
|
+
await this.savedFilterRepo.deleteDetailsByCode(updated.code);
|
|
86
|
+
|
|
87
|
+
const prepared = filterDetails.map((detail: any) => ({
|
|
88
|
+
...detail,
|
|
89
|
+
mapped_filter_code: updated.code,
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
await this.savedFilterRepo.saveDetails(prepared);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return updated;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getDetailsByCode(code: string): Promise<SavedFilterDetail[]> {
|
|
99
|
+
return this.savedFilterRepo.getDetailsByCode(code);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
}
|