Merge pull request 'feat/report' (#11) from feat/report into development
continuous-integration/drone/tag Build is passing
Details
continuous-integration/drone/tag Build is passing
Details
Reviewed-on: #11pull/13/head devel_10.5.4
commit
0410b481e1
|
@ -1,9 +1,15 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ReportBookmarkController } from './report-bookmark.controller';
|
import { ReportBookmarkController } from './report-bookmark.controller';
|
||||||
import { ReportBookmarkService } from './report-bookmark.service';
|
import { ReportBookmarkService } from './report-bookmark.service';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
|
import { ReportBookmarkModel } from '../shared/models/report-bookmark.model';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([ReportBookmarkModel], CONNECTION_NAME.DEFAULT),
|
||||||
|
],
|
||||||
controllers: [ReportBookmarkController],
|
controllers: [ReportBookmarkController],
|
||||||
providers: [ReportBookmarkService],
|
providers: [ReportBookmarkService],
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,15 +5,45 @@ import {
|
||||||
GetLabelReportBookmarkDto,
|
GetLabelReportBookmarkDto,
|
||||||
GetReportBookmarkDto,
|
GetReportBookmarkDto,
|
||||||
} from '../shared/dto/report-bookmark.get.dto';
|
} from '../shared/dto/report-bookmark.get.dto';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { ReportBookmarkModel } from '../shared/models/report-bookmark.model';
|
||||||
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReportBookmarkService extends BaseReportService {
|
export class ReportBookmarkService extends BaseReportService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ReportBookmarkModel, CONNECTION_NAME.DEFAULT)
|
||||||
|
private repo: Repository<ReportBookmarkModel>,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
async create(body: CreateReportBookmarkDto) {
|
async create(body: CreateReportBookmarkDto) {
|
||||||
return 'you hit API for create report bookmark';
|
return 'you hit API for create report bookmark';
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(query: GetReportBookmarkDto) {
|
async getAll(query: GetReportBookmarkDto) {
|
||||||
return 'you hit API for get all report bookmark';
|
const modelName = ReportBookmarkModel.name;
|
||||||
|
|
||||||
|
const requestor_id = this.userProvider.user.id;
|
||||||
|
const unique_names = query.unique_names;
|
||||||
|
const group_names = query.group_names;
|
||||||
|
|
||||||
|
const qb = this.repo
|
||||||
|
.createQueryBuilder(modelName)
|
||||||
|
.where((query) => {
|
||||||
|
if (unique_names) {
|
||||||
|
query.andWhere(`unique_name IN (:...unique_names)`, { unique_names });
|
||||||
|
}
|
||||||
|
if (group_names) {
|
||||||
|
query.andWhere(`group_name =IN (:...group_names)`, { group_names });
|
||||||
|
}
|
||||||
|
query.andWhere(`requestor_id = :requestor_id`, { requestor_id });
|
||||||
|
})
|
||||||
|
.orderBy(`${modelName}.created_at`, 'DESC');
|
||||||
|
|
||||||
|
return await qb.getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllLabelHistory(query: GetLabelReportBookmarkDto) {
|
async getAllLabelHistory(query: GetLabelReportBookmarkDto) {
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ReportExportController } from './report-export.controller';
|
import { ReportExportController } from './report-export.controller';
|
||||||
import { ReportExportService } from './report-export.service';
|
import { ReportExportService } from './report-export.service';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { ExportReportHistoryModel } from '../shared/models/export-report-history.model';
|
||||||
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature(
|
||||||
|
[ExportReportHistoryModel],
|
||||||
|
CONNECTION_NAME.DEFAULT,
|
||||||
|
),
|
||||||
|
],
|
||||||
controllers: [ReportExportController],
|
controllers: [ReportExportController],
|
||||||
providers: [ReportExportService],
|
providers: [ReportExportService],
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,9 +6,23 @@ import {
|
||||||
GetReportExportFileNameDto,
|
GetReportExportFileNameDto,
|
||||||
GetReportExportProcessingDto,
|
GetReportExportProcessingDto,
|
||||||
} from '../shared/dto/report-export.get.dto';
|
} from '../shared/dto/report-export.get.dto';
|
||||||
|
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { ExportReportHistoryModel } from '../shared/models/export-report-history.model';
|
||||||
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReportExportService extends BaseReportService {
|
export class ReportExportService extends BaseReportService {
|
||||||
|
constructor(
|
||||||
|
@InjectDataSource(CONNECTION_NAME.DEFAULT)
|
||||||
|
private dataSource: DataSource,
|
||||||
|
|
||||||
|
@InjectRepository(ExportReportHistoryModel, CONNECTION_NAME.DEFAULT)
|
||||||
|
private exportHistoryRepo: Repository<ExportReportHistoryModel>,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
async create(body: CreateReportExportDto) {
|
async create(body: CreateReportExportDto) {
|
||||||
return 'you hit API for create report export';
|
return 'you hit API for create report export';
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,9 @@ export class ReportController {
|
||||||
async getReportData(@Body() body: GetReportDataDto) {
|
async getReportData(@Body() body: GetReportDataDto) {
|
||||||
return await this.service.getReportData(body);
|
return await this.service.getReportData(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('meta')
|
||||||
|
async getReportMeta(@Body() body: GetReportDataDto) {
|
||||||
|
return await this.service.getReportMeta(body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ReportController } from './report.controller';
|
import { ReportController } from './report.controller';
|
||||||
import { ReportService } from './report.service';
|
import { ReportService } from './report.service';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { ExportReportHistoryModel } from '../shared/models/export-report-history.model';
|
||||||
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature(
|
||||||
|
[ExportReportHistoryModel],
|
||||||
|
CONNECTION_NAME.DEFAULT,
|
||||||
|
),
|
||||||
|
],
|
||||||
controllers: [ReportController],
|
controllers: [ReportController],
|
||||||
providers: [ReportService],
|
providers: [ReportService],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,150 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { BaseReportService } from '../shared/services/base-report.service';
|
import { BaseReportService } from '../shared/services/base-report.service';
|
||||||
import { GetReportConfigDto } from '../shared/dto/report-config.get.dto';
|
import { GetReportConfigDto } from '../shared/dto/report-config.get.dto';
|
||||||
import { GetReportDataDto } from '../shared/dto/report-data.get.dto';
|
import { GetReportDataDto } from '../shared/dto/report-data.get.dto';
|
||||||
|
import { ReportConfigs } from '../shared/configs';
|
||||||
|
import { InjectDataSource } from '@nestjs/typeorm';
|
||||||
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { ReportConfigEntity } from '../shared/entities/report-config.entity';
|
||||||
|
import { ReportQueryBuilder } from '../shared/helpers';
|
||||||
|
import { DATA_FORMAT } from '../shared/constant';
|
||||||
|
import { roundingCurrency } from '../shared/helpers/rounding-currency';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReportService extends BaseReportService {
|
export class ReportService extends BaseReportService {
|
||||||
|
private readonly logger = new Logger(ReportService.name);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectDataSource(CONNECTION_NAME.DEFAULT)
|
||||||
|
private dataSource: DataSource,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
async getReportConfig(query: GetReportConfigDto) {
|
async getReportConfig(query: GetReportConfigDto) {
|
||||||
return 'you hit API for get report config';
|
const { unique_names = [], group_names = [] } = query;
|
||||||
|
|
||||||
|
let configs = ReportConfigs;
|
||||||
|
if (group_names.length > 0) {
|
||||||
|
configs = configs.filter((item) => group_names.includes(item.group_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unique_names.length > 0) {
|
||||||
|
configs = configs.filter((item) =>
|
||||||
|
unique_names.includes(item.unique_name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
getReportConfigByUniqueName(unique_name): ReportConfigEntity {
|
||||||
|
return ReportConfigs.find((item) => item.unique_name === unique_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getReportData(body: GetReportDataDto) {
|
async getReportData(body: GetReportDataDto) {
|
||||||
return 'you hit API for get report data';
|
try {
|
||||||
|
const queryModel = body.query_model;
|
||||||
|
const reportConfig = this.getReportConfigByUniqueName(body.unique_name);
|
||||||
|
const builder = new ReportQueryBuilder(reportConfig, queryModel);
|
||||||
|
const SQL = builder.getSql();
|
||||||
|
const queryResult = await this.dataSource.query(SQL);
|
||||||
|
|
||||||
|
const realData = [];
|
||||||
|
const configColumns = reportConfig.column_configs;
|
||||||
|
|
||||||
|
for (const item of queryResult) {
|
||||||
|
const realItem = {};
|
||||||
|
for (const itemKey of Object.keys(item)) {
|
||||||
|
if (itemKey === 'count_child_group') {
|
||||||
|
const realValue = item[itemKey] ?? 0;
|
||||||
|
Object.assign(realItem, { [`${itemKey}`]: Number(realValue) });
|
||||||
|
} else {
|
||||||
|
const confCol = configColumns.find((c) => c.column === itemKey);
|
||||||
|
const isNumber = confCol.format === DATA_FORMAT.NUMBER;
|
||||||
|
const isCurrency = confCol.format === DATA_FORMAT.CURRENCY;
|
||||||
|
const isMinusCurrency =
|
||||||
|
confCol.format === DATA_FORMAT.MINUS_CURRENCY;
|
||||||
|
const isBoolean = confCol.format === DATA_FORMAT.BOOLEAN;
|
||||||
|
const isPercentage = confCol.format === DATA_FORMAT.PERCENTAGE;
|
||||||
|
const isSetInitNull = isNumber || isCurrency || isMinusCurrency;
|
||||||
|
const isTextUpperCase =
|
||||||
|
confCol.format === DATA_FORMAT.TEXT_UPPERCASE;
|
||||||
|
const isTextLowerCase =
|
||||||
|
confCol.format === DATA_FORMAT.TEXT_LOWERCASE;
|
||||||
|
|
||||||
|
if (isSetInitNull) {
|
||||||
|
const realValue = item[itemKey] ?? 0;
|
||||||
|
if (isCurrency) {
|
||||||
|
Object.assign(realItem, {
|
||||||
|
[`${itemKey}`]: roundingCurrency(realValue),
|
||||||
|
});
|
||||||
|
} else if (isMinusCurrency) {
|
||||||
|
Object.assign(realItem, {
|
||||||
|
[`${itemKey}`]: roundingCurrency(realValue) * -1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Object.assign(realItem, { [`${itemKey}`]: realValue });
|
||||||
|
}
|
||||||
|
} else if (isPercentage) {
|
||||||
|
const realValue = item[itemKey]
|
||||||
|
? `${item[itemKey]}%`
|
||||||
|
: item[itemKey];
|
||||||
|
Object.assign(realItem, { [`${itemKey}`]: realValue });
|
||||||
|
} else if (isBoolean) {
|
||||||
|
let realValue = '';
|
||||||
|
if (item[itemKey] === true || item[itemKey] === 1)
|
||||||
|
realValue = 'Yes';
|
||||||
|
else if (item[itemKey] === false || item[itemKey] === 0)
|
||||||
|
realValue = 'No';
|
||||||
|
Object.assign(realItem, { [`${itemKey}`]: realValue });
|
||||||
|
} else if (isTextUpperCase) {
|
||||||
|
Object.assign(realItem, {
|
||||||
|
[`${itemKey}`]: item[itemKey]?.toUpperCase(),
|
||||||
|
});
|
||||||
|
} else if (isTextLowerCase) {
|
||||||
|
Object.assign(realItem, {
|
||||||
|
[`${itemKey}`]: item[itemKey]?.toLowerCase(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Object.assign(realItem, { [`${itemKey}`]: item[itemKey] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
realData.push(realItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return realData;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getReportMeta(body: GetReportDataDto) {
|
||||||
|
try {
|
||||||
|
const queryModel = body.query_model;
|
||||||
|
const reportConfig = this.getReportConfigByUniqueName(body.unique_name);
|
||||||
|
const builder = new ReportQueryBuilder(reportConfig, queryModel);
|
||||||
|
const SQL_COUNT = builder.getSqlCount();
|
||||||
|
const queryResult = await this.dataSource.query(SQL_COUNT);
|
||||||
|
|
||||||
|
const totalRow = parseInt(queryResult[0].count);
|
||||||
|
const startRow = queryModel.startRow;
|
||||||
|
const endRow = queryModel.endRow;
|
||||||
|
const pageSize = endRow - startRow;
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
total_row: totalRow,
|
||||||
|
limit: pageSize,
|
||||||
|
offset: startRow,
|
||||||
|
};
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { DATA_FORMAT, DATA_TYPE, REPORT_GROUP } from '../../../constant';
|
||||||
|
import { ReportConfigEntity } from '../../../entities/report-config.entity';
|
||||||
|
|
||||||
|
export default <ReportConfigEntity>{
|
||||||
|
group_name: REPORT_GROUP.general_report,
|
||||||
|
unique_name: `${REPORT_GROUP.general_report}__sample`,
|
||||||
|
label: 'Sample General Report ',
|
||||||
|
table_schema: 'season_types main',
|
||||||
|
main_table_alias: 'main',
|
||||||
|
defaultOrderBy: [],
|
||||||
|
lowLevelOrderBy: [],
|
||||||
|
filter_period_config: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
column_configs: [
|
||||||
|
{
|
||||||
|
column: 'main__created_at',
|
||||||
|
query: 'main.created_at',
|
||||||
|
label: 'Created Date',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.DATE_EPOCH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__updated_at',
|
||||||
|
query: 'main.updated_at',
|
||||||
|
label: 'Updated Date',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.DATE_EPOCH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__name',
|
||||||
|
query: 'main.name',
|
||||||
|
label: 'Name',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filter_configs: [],
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { ReportConfigEntity } from '../../entities/report-config.entity';
|
||||||
|
|
||||||
|
import SampleReport from './configs/sample.report';
|
||||||
|
|
||||||
|
export const GeneralReportConfig: ReportConfigEntity[] = [SampleReport];
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { ReportConfigEntity } from '../entities/report-config.entity';
|
||||||
|
import { GeneralReportConfig } from './general-report';
|
||||||
|
import { TenantReportConfig } from './tenant-report';
|
||||||
|
|
||||||
|
export const ReportConfigs: ReportConfigEntity[] = [
|
||||||
|
...GeneralReportConfig,
|
||||||
|
...TenantReportConfig,
|
||||||
|
];
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { DATA_FORMAT, DATA_TYPE, REPORT_GROUP } from '../../../constant';
|
||||||
|
import { ReportConfigEntity } from '../../../entities/report-config.entity';
|
||||||
|
|
||||||
|
export default <ReportConfigEntity>{
|
||||||
|
group_name: REPORT_GROUP.tenant_report,
|
||||||
|
unique_name: `${REPORT_GROUP.tenant_report}__sample`,
|
||||||
|
label: 'Sample Tenant Report ',
|
||||||
|
table_schema: 'season_types main',
|
||||||
|
main_table_alias: 'main',
|
||||||
|
defaultOrderBy: [],
|
||||||
|
lowLevelOrderBy: [],
|
||||||
|
filter_period_config: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
column_configs: [
|
||||||
|
{
|
||||||
|
column: 'main__created_at',
|
||||||
|
query: 'main.created_at',
|
||||||
|
label: 'Created Date',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.DATE_EPOCH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__updated_at',
|
||||||
|
query: 'main.updated_at',
|
||||||
|
label: 'Updated Date',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.DATE_EPOCH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__name',
|
||||||
|
query: 'main.name',
|
||||||
|
label: 'Name',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filter_configs: [],
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { ReportConfigEntity } from '../../entities/report-config.entity';
|
||||||
|
import SampleReport from './configs/sample.report';
|
||||||
|
|
||||||
|
export const TenantReportConfig: ReportConfigEntity[] = [SampleReport];
|
|
@ -1,5 +1,6 @@
|
||||||
export enum REPORT_GROUP {
|
export enum REPORT_GROUP {
|
||||||
// PATTERN => MODULE__MENU__SUB_MENU
|
// PATTERN => MODULE__MENU__SUB_MENU
|
||||||
// EXAMPLE:
|
// EXAMPLE:
|
||||||
contact__reports = 'contact__reports',
|
general_report = 'general_report',
|
||||||
|
tenant_report = 'tenant_report',
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsObject, IsString, ValidateIf } from 'class-validator';
|
import { IsObject, IsString, ValidateIf } from 'class-validator';
|
||||||
import { QueryModelEntity } from '../entities/query-model.entity';
|
import { QueryModelEntity } from '../entities/query-model.entity';
|
||||||
|
import { REPORT_GROUP } from '../constant';
|
||||||
|
|
||||||
export class GetReportDataDto {
|
export class GetReportDataDto {
|
||||||
@ApiProperty({ name: 'group_name', required: true })
|
@ApiProperty({
|
||||||
|
name: 'group_name',
|
||||||
|
required: true,
|
||||||
|
default: REPORT_GROUP.general_report,
|
||||||
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
group_name: string;
|
group_name: string;
|
||||||
|
|
||||||
@ApiProperty({ name: 'unique_name', required: true })
|
@ApiProperty({
|
||||||
|
name: 'unique_name',
|
||||||
|
required: true,
|
||||||
|
default: `${REPORT_GROUP.general_report}__sample`,
|
||||||
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
unique_name: string;
|
unique_name: string;
|
||||||
|
|
||||||
|
@ -15,6 +24,17 @@ export class GetReportDataDto {
|
||||||
name: 'query_model',
|
name: 'query_model',
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
|
default: {
|
||||||
|
startRow: 0,
|
||||||
|
endRow: 100,
|
||||||
|
rowGroupCols: [],
|
||||||
|
valueCols: [],
|
||||||
|
pivotCols: [],
|
||||||
|
pivotMode: true,
|
||||||
|
groupKeys: [],
|
||||||
|
filterModel: {},
|
||||||
|
sortModel: [],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
@IsObject()
|
@IsObject()
|
||||||
@ValidateIf((body) => body.query_model)
|
@ValidateIf((body) => body.query_model)
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { DATA_FORMAT, DATA_TYPE, FILTER_TYPE } from '../constant';
|
||||||
|
|
||||||
|
export interface ReportColumnConfigEntity {
|
||||||
|
column: string;
|
||||||
|
query: string;
|
||||||
|
label: string;
|
||||||
|
type: DATA_TYPE;
|
||||||
|
format: DATA_FORMAT;
|
||||||
|
date_format?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterConfigEntity {
|
||||||
|
filter_column: string;
|
||||||
|
filter_type: FILTER_TYPE;
|
||||||
|
|
||||||
|
filed_label: string;
|
||||||
|
field_type: string;
|
||||||
|
hide_field?: boolean;
|
||||||
|
|
||||||
|
select_data_source_url?: string;
|
||||||
|
select_custom_options?: string[];
|
||||||
|
select_value_key?: string;
|
||||||
|
select_label_key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterPeriodConfigEntity {
|
||||||
|
key: string;
|
||||||
|
type: FILTER_TYPE;
|
||||||
|
note?: string;
|
||||||
|
hidden?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReportConfigEntity {
|
||||||
|
group_name: string;
|
||||||
|
unique_name: string;
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
table_schema: string;
|
||||||
|
main_table_alias?: string;
|
||||||
|
customVirtualTableSchema?(
|
||||||
|
filterModel: any,
|
||||||
|
findQueryConfig: (column: string) => string,
|
||||||
|
createFilterSql: (key: string, item: any) => string,
|
||||||
|
): string;
|
||||||
|
whereCondition?(filterModel: any): string[];
|
||||||
|
whereDefaultConditions?: {
|
||||||
|
column: string;
|
||||||
|
filter_type: FILTER_TYPE;
|
||||||
|
values: string[];
|
||||||
|
}[];
|
||||||
|
defaultOrderBy?: string[];
|
||||||
|
lowLevelOrderBy?: string[];
|
||||||
|
|
||||||
|
column_configs: ReportColumnConfigEntity[];
|
||||||
|
filter_configs?: FilterConfigEntity[];
|
||||||
|
filter_period_config?: FilterPeriodConfigEntity;
|
||||||
|
ignore_filter_keys?: string[];
|
||||||
|
customQueryColumn?(column: string): string;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './query-builder';
|
|
@ -0,0 +1,520 @@
|
||||||
|
import { FILTER_TYPE } from '../constant';
|
||||||
|
import { QueryModelEntity } from '../entities/query-model.entity';
|
||||||
|
import {
|
||||||
|
ReportColumnConfigEntity,
|
||||||
|
ReportConfigEntity,
|
||||||
|
} from '../entities/report-config.entity';
|
||||||
|
|
||||||
|
export class ReportQueryBuilder {
|
||||||
|
public reportConfig: ReportConfigEntity;
|
||||||
|
public queryModel: QueryModelEntity;
|
||||||
|
|
||||||
|
constructor(reportConfig: ReportConfigEntity, queryModel: QueryModelEntity) {
|
||||||
|
this.reportConfig = reportConfig;
|
||||||
|
this.queryModel = queryModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseConfig() {
|
||||||
|
const tableSchema = this.reportConfig.table_schema;
|
||||||
|
const mainTableAlias = this.reportConfig.main_table_alias ?? 'main';
|
||||||
|
|
||||||
|
const selectSql = this.createSelectSql();
|
||||||
|
const selectSqlExport = this.createSelectSqlExport();
|
||||||
|
|
||||||
|
const whereSql = this.createWhereSql();
|
||||||
|
const limitSql = this.createLimitSql();
|
||||||
|
|
||||||
|
const orderBySql = this.createOrderBySql();
|
||||||
|
const orderBySqlExport = this.createOrderBySqlExport();
|
||||||
|
|
||||||
|
const groupBy = this.createGroupBySql();
|
||||||
|
const groupByExport = this.createGroupBySqlExport();
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableSchema,
|
||||||
|
mainTableAlias,
|
||||||
|
selectSql,
|
||||||
|
selectSqlExport,
|
||||||
|
whereSql,
|
||||||
|
limitSql,
|
||||||
|
orderBySql,
|
||||||
|
orderBySqlExport,
|
||||||
|
...groupBy,
|
||||||
|
...groupByExport,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getSql(): string {
|
||||||
|
const {
|
||||||
|
selectSql,
|
||||||
|
tableSchema,
|
||||||
|
whereSql,
|
||||||
|
groupByQuery,
|
||||||
|
orderBySql,
|
||||||
|
limitSql,
|
||||||
|
} = this.getBaseConfig();
|
||||||
|
return `SELECT ${selectSql} FROM ${tableSchema} ${whereSql} ${groupByQuery} ${orderBySql} ${limitSql}` as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSqlCount(): string {
|
||||||
|
const { groupByColumn, mainTableAlias, tableSchema, whereSql } =
|
||||||
|
this.getBaseConfig();
|
||||||
|
|
||||||
|
return `SELECT COUNT(${
|
||||||
|
groupByColumn ? `DISTINCT ${groupByColumn}` : `${mainTableAlias}.id`
|
||||||
|
}) ${
|
||||||
|
groupByColumn
|
||||||
|
? `+ COUNT(DISTINCT CASE WHEN ${groupByColumn} IS NULL THEN 1 END)`
|
||||||
|
: ''
|
||||||
|
} AS count FROM ${tableSchema} ${whereSql}` as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSqlExport(): string {
|
||||||
|
const {
|
||||||
|
selectSqlExport,
|
||||||
|
tableSchema,
|
||||||
|
whereSql,
|
||||||
|
groupByQueryExport,
|
||||||
|
orderBySqlExport,
|
||||||
|
} = this.getBaseConfig();
|
||||||
|
return `SELECT ${selectSqlExport} FROM ${tableSchema} ${whereSql} ${groupByQueryExport} ${orderBySqlExport}` as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSqlCountExport(): string {
|
||||||
|
const { groupByColumnExport, mainTableAlias, tableSchema, whereSql } =
|
||||||
|
this.getBaseConfig();
|
||||||
|
return `SELECT COUNT(${
|
||||||
|
groupByColumnExport
|
||||||
|
? `DISTINCT ${groupByColumnExport}`
|
||||||
|
: `${mainTableAlias}.id`
|
||||||
|
}) ${
|
||||||
|
groupByColumnExport
|
||||||
|
? `+ COUNT(DISTINCT CASE WHEN ${groupByColumnExport} IS NULL THEN 1 END)`
|
||||||
|
: ''
|
||||||
|
} AS count FROM ${tableSchema} ${whereSql}` as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDoingGrouping(rowGroupCols, groupKeys) {
|
||||||
|
// we are not doing grouping if at the lowest level. we are at the lowest level
|
||||||
|
// if we are grouping by more columns than we have keys for (that means the user
|
||||||
|
// has not expanded a lowest level group, OR we are not grouping at all).
|
||||||
|
return rowGroupCols.length > groupKeys.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
interpolate(str, o) {
|
||||||
|
return str.replace(/{([^{}]*)}/g, function (a, b) {
|
||||||
|
const r = o[b];
|
||||||
|
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createLimitSql() {
|
||||||
|
const startRow = this.queryModel.startRow;
|
||||||
|
const endRow = this.queryModel.endRow;
|
||||||
|
const pageSize = endRow - startRow;
|
||||||
|
return ' LIMIT ' + (pageSize + 1) + ' OFFSET ' + startRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowCount(results) {
|
||||||
|
if (results === null || results === undefined || results.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const currentLastRow = this.queryModel.startRow + results.length;
|
||||||
|
return currentLastRow <= this.queryModel.endRow ? currentLastRow : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cutResultsToPageSize(results) {
|
||||||
|
const pageSize = this.queryModel.endRow - this.queryModel.startRow;
|
||||||
|
if (results && results.length > pageSize) {
|
||||||
|
return results.splice(0, pageSize);
|
||||||
|
} else {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findQueryConfig(column: string) {
|
||||||
|
const configColumns = this.reportConfig.column_configs ?? [];
|
||||||
|
|
||||||
|
const queryColumn: ReportColumnConfigEntity = configColumns.find(
|
||||||
|
(el) => el.column === column,
|
||||||
|
);
|
||||||
|
const customQueryColumn = this.reportConfig?.customQueryColumn
|
||||||
|
? this.reportConfig.customQueryColumn(column)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (customQueryColumn) return customQueryColumn;
|
||||||
|
else if (queryColumn) return queryColumn.query;
|
||||||
|
|
||||||
|
return column.replace('__', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// GENERATE SELECT QUERY ============================================
|
||||||
|
createSelectSql(): string {
|
||||||
|
const configColumns = this.reportConfig.column_configs;
|
||||||
|
const mainTableAlias = this.reportConfig.main_table_alias ?? 'main';
|
||||||
|
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
const valueCols = this.queryModel.valueCols;
|
||||||
|
const groupKeys = this.queryModel.groupKeys;
|
||||||
|
|
||||||
|
if (this.isDoingGrouping(rowGroupCols, groupKeys)) {
|
||||||
|
const colsToSelect = [];
|
||||||
|
const rowGroupCol = rowGroupCols[groupKeys.length];
|
||||||
|
const rowGroupColChildCount = rowGroupCols[groupKeys.length + 1];
|
||||||
|
|
||||||
|
colsToSelect.push(
|
||||||
|
this.findQueryConfig(rowGroupCol.field) + ` AS ${rowGroupCol.field}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rowGroupColChildCount) {
|
||||||
|
colsToSelect.push(
|
||||||
|
`COUNT(DISTINCT ${this.findQueryConfig(
|
||||||
|
rowGroupColChildCount.field,
|
||||||
|
)}) AS count_child_group`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
colsToSelect.push(`COUNT(${mainTableAlias}.id) AS count_child_group`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const thisSelf = this;
|
||||||
|
valueCols.forEach(function (valueCol) {
|
||||||
|
colsToSelect.push(
|
||||||
|
`${valueCol.aggFunc} (${thisSelf.findQueryConfig(
|
||||||
|
valueCol.field,
|
||||||
|
)}) AS ${valueCol.field}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return colsToSelect.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = configColumns.map(
|
||||||
|
(item) => `${item.query} AS ${item.column}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return columns.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
createSelectSqlExport(): string {
|
||||||
|
const configColumns = this.reportConfig.column_configs;
|
||||||
|
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
const valueCols = this.queryModel.valueCols;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const thisSelf = this;
|
||||||
|
|
||||||
|
if (rowGroupCols.length > 0) {
|
||||||
|
const colsToSelect = [];
|
||||||
|
rowGroupCols.forEach(function (rowGroupCol) {
|
||||||
|
colsToSelect.push(
|
||||||
|
thisSelf.findQueryConfig(rowGroupCol.field) +
|
||||||
|
` AS ${rowGroupCol.field}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
valueCols.forEach(function (valueCol) {
|
||||||
|
colsToSelect.push(
|
||||||
|
`${valueCol.aggFunc} (${thisSelf.findQueryConfig(
|
||||||
|
valueCol.field,
|
||||||
|
)}) AS ${valueCol.field}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return colsToSelect.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = configColumns.map((item) => `${item.query} ${item.column}`);
|
||||||
|
|
||||||
|
return columns.join(', ');
|
||||||
|
}
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
// GENERATE WHERE QUERY =============================================
|
||||||
|
createFilterSql(column: string, item: { type: FILTER_TYPE; filter: any }) {
|
||||||
|
switch (item.type) {
|
||||||
|
// TEXT
|
||||||
|
case FILTER_TYPE.TEXT_EQUAL:
|
||||||
|
return `${column} = '${item.filter}'`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_NOT_EQUAL:
|
||||||
|
return `${column} != '${item.filter}'`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_CONTAINS:
|
||||||
|
return `${column} ILIKE '%${item.filter}%'`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_NOT_CONTAINS:
|
||||||
|
return `${column} NOT ILIKE '%${item.filter}%'`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_START_WITH:
|
||||||
|
return `${column} ILIKE '${item.filter}%'`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_END_WITH:
|
||||||
|
return `${column} ILIKE '%${item.filter}'`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_IN_MEMBER:
|
||||||
|
return item.filter?.length > 0
|
||||||
|
? `${column} IN(${item.filter.map((i) => `'${i}'`).join(', ')})`
|
||||||
|
: null;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_MULTIPLE_CONTAINS:
|
||||||
|
return item.filter?.length > 0
|
||||||
|
? `${column} ILIKE ANY(ARRAY[${item.filter
|
||||||
|
.map((i) => `'%${i}%'`)
|
||||||
|
.join(', ')}])`
|
||||||
|
: null;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_MULTIPLE_REGEXP_CONTAINS:
|
||||||
|
return item.filter?.length > 0
|
||||||
|
? `${column} REGEXP '${item.filter.join('|')}'`
|
||||||
|
: null;
|
||||||
|
|
||||||
|
case FILTER_TYPE.DATE_IN_RANGE_EPOCH:
|
||||||
|
return `(${column} >= ${item.filter[0]} AND ${column} <= ${item.filter[1]})`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.DATE_IN_RANGE_TIMESTAMP:
|
||||||
|
return `(${column} BETWEEN '${item.filter[0]}' AND '${item.filter[1]}')`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.TEXT_IN_RANGE:
|
||||||
|
return `(${column} >= '${item.filter[0]}' AND ${column} <= '${item.filter[1]}')`;
|
||||||
|
|
||||||
|
// NUMBER
|
||||||
|
case FILTER_TYPE.NUMBER_EQUAL:
|
||||||
|
return `${column} = ${item.filter}`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.NUMBER_NOT_EQUAL:
|
||||||
|
return `${column} != ${item.filter}`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.NUMBER_GREATER_THAN:
|
||||||
|
return `${column} > ${item.filter}`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.NUMBER_GREATER_THAN_OR_EQUAL:
|
||||||
|
return `${column} >= ${item.filter}`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.NUMBER_LESS_THAN:
|
||||||
|
return `${column} < ${item.filter}`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.NUMBER_LESS_THAN_OR_EQUAL:
|
||||||
|
return `${column} <= ${item.filter}`;
|
||||||
|
|
||||||
|
case FILTER_TYPE.NUMBER_IN_RANGE:
|
||||||
|
return `(${column} >= ${item.filter[0]} AND ${column} <= ${item.filter[1]})`;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log('UNKNOWN FILTER TYPE:', item.type);
|
||||||
|
return 'true';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createWhereSql() {
|
||||||
|
const configFilters = this.reportConfig.filter_configs ?? [];
|
||||||
|
const ignoreFilterKeys = this.reportConfig.ignore_filter_keys ?? [];
|
||||||
|
const ignoreFilter = configFilters
|
||||||
|
.filter((el) => el.hide_field)
|
||||||
|
.map((el) => el.filter_column);
|
||||||
|
|
||||||
|
const ignoreFilterKey = [...ignoreFilter, ...ignoreFilterKeys];
|
||||||
|
|
||||||
|
const whereCondition = this.reportConfig?.whereCondition
|
||||||
|
? this.reportConfig.whereCondition(this.queryModel.filterModel)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
const groupKeys = this.queryModel.groupKeys;
|
||||||
|
const filterModel = this.queryModel.filterModel;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const thisSelf = this;
|
||||||
|
|
||||||
|
const whereParts = [];
|
||||||
|
if (groupKeys.length > 0) {
|
||||||
|
groupKeys.forEach(function (key, index) {
|
||||||
|
const colName = rowGroupCols[index].field;
|
||||||
|
// whereParts.push(colName + ' = "' + key + '"');
|
||||||
|
whereParts.push(`${thisSelf.findQueryConfig(colName)} = '${key}'`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterModel) {
|
||||||
|
const keySet = Object.keys(filterModel);
|
||||||
|
keySet.forEach(function (key) {
|
||||||
|
if (!ignoreFilterKey.includes(key)) {
|
||||||
|
const item = filterModel[key];
|
||||||
|
const newKey = thisSelf.findQueryConfig(key);
|
||||||
|
whereParts.push(thisSelf.createFilterSql(newKey, item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default where conditions
|
||||||
|
const defaultConditions = this.reportConfig.whereDefaultConditions;
|
||||||
|
const defaultWhereOptions = [];
|
||||||
|
if (defaultConditions) {
|
||||||
|
defaultConditions.forEach((condition) => {
|
||||||
|
defaultWhereOptions.push(
|
||||||
|
this.createFilterSql(condition.column, {
|
||||||
|
type: condition.filter_type,
|
||||||
|
filter: condition.values,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableWhereConditions = [...whereCondition, ...whereParts];
|
||||||
|
const defaultWhereConditions = defaultWhereOptions;
|
||||||
|
|
||||||
|
if (tableWhereConditions.length > 0) {
|
||||||
|
return `WHERE (${
|
||||||
|
defaultWhereConditions.length
|
||||||
|
? defaultWhereConditions?.filter(Boolean).join(' AND ') + ' AND '
|
||||||
|
: ' '
|
||||||
|
} ${tableWhereConditions.filter(Boolean).join(' AND ')})`;
|
||||||
|
} else if (defaultWhereConditions.length) {
|
||||||
|
return `WHERE (${defaultWhereConditions.filter(Boolean).join(' AND ')})`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
// GENERATE ORDER QUERY =============================================
|
||||||
|
createOrderBySql() {
|
||||||
|
const mainTableAlias = this.reportConfig.main_table_alias ?? 'main';
|
||||||
|
const defaultOrderBy = this.reportConfig.defaultOrderBy ?? [];
|
||||||
|
const lowLevelOrderBy = this.reportConfig.lowLevelOrderBy ?? [];
|
||||||
|
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
const groupKeys = this.queryModel.groupKeys;
|
||||||
|
const sortModel = this.queryModel.sortModel;
|
||||||
|
|
||||||
|
const grouping = this.isDoingGrouping(rowGroupCols, groupKeys);
|
||||||
|
|
||||||
|
const sortParts = [];
|
||||||
|
if (sortModel) {
|
||||||
|
const groupColIds = rowGroupCols
|
||||||
|
.map((groupCol) => groupCol.id)
|
||||||
|
.slice(0, groupKeys.length + 1);
|
||||||
|
|
||||||
|
sortModel.forEach(function (item) {
|
||||||
|
if (grouping && groupColIds.indexOf(item.colId) < 0) {
|
||||||
|
// ignore
|
||||||
|
} else {
|
||||||
|
sortParts.push(item.colId + ' ' + item.sort);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOrder = defaultOrderBy[0]
|
||||||
|
? defaultOrderBy
|
||||||
|
: [`${mainTableAlias}.created_at DESC`];
|
||||||
|
|
||||||
|
const lowLevelOrder = lowLevelOrderBy[0]
|
||||||
|
? lowLevelOrderBy
|
||||||
|
: [`${mainTableAlias}.id DESC`];
|
||||||
|
|
||||||
|
if (sortParts.length > 0) {
|
||||||
|
if (rowGroupCols?.length > 0) {
|
||||||
|
if (groupKeys.length > 0) {
|
||||||
|
const sortBy = sortParts[groupKeys.length];
|
||||||
|
if (sortBy) return ' ORDER BY ' + sortBy;
|
||||||
|
// return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ' ORDER BY ' + sortParts.join(', ');
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
' ORDER BY ' + sortParts.join(', ') + `, ${lowLevelOrder.join(', ')}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (rowGroupCols?.length > 0) {
|
||||||
|
const sortGroupData = rowGroupCols[groupKeys.length];
|
||||||
|
if (sortGroupData) return ' ORDER BY ' + `${sortGroupData['id']} DESC`;
|
||||||
|
// return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
` ORDER BY ` + defaultOrder.join(', ') + `, ${lowLevelOrder.join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createOrderBySqlExport() {
|
||||||
|
const mainTableAlias = this.reportConfig.main_table_alias ?? 'main';
|
||||||
|
const defaultOrderBy = this.reportConfig.defaultOrderBy ?? [];
|
||||||
|
const lowLevelOrderBy = this.reportConfig.lowLevelOrderBy ?? [];
|
||||||
|
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
const sortModel = this.queryModel.sortModel;
|
||||||
|
|
||||||
|
const defaultOrder = defaultOrderBy[0]
|
||||||
|
? defaultOrderBy
|
||||||
|
: [`${mainTableAlias}.created_at DESC`];
|
||||||
|
|
||||||
|
const lowLevelOrder = lowLevelOrderBy[0]
|
||||||
|
? lowLevelOrderBy
|
||||||
|
: [`${mainTableAlias}.id DESC`];
|
||||||
|
|
||||||
|
if (sortModel.length > 0) {
|
||||||
|
const sortParts = sortModel.map((i) => `${i.colId} ${i.sort}`);
|
||||||
|
return ' ORDER BY ' + `${sortParts.join(', ')}`;
|
||||||
|
} else {
|
||||||
|
if (rowGroupCols?.length > 0) {
|
||||||
|
const sortParts = rowGroupCols.map((i) => `${i.id} DESC`);
|
||||||
|
return ' ORDER BY ' + `${sortParts.join(', ')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
` ORDER BY ` + defaultOrder.join(', ') + `, ${lowLevelOrder.join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ==================================================================
|
||||||
|
|
||||||
|
// GENERATE GROUP BY QUERY ==========================================
|
||||||
|
createGroupBySql() {
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
const groupKeys = this.queryModel.groupKeys;
|
||||||
|
|
||||||
|
if (this.isDoingGrouping(rowGroupCols, groupKeys)) {
|
||||||
|
const colsToGroupBy = [];
|
||||||
|
|
||||||
|
const rowGroupCol = rowGroupCols[groupKeys.length];
|
||||||
|
colsToGroupBy.push(this.findQueryConfig(rowGroupCol.field));
|
||||||
|
|
||||||
|
return {
|
||||||
|
groupByQuery: ' GROUP BY ' + colsToGroupBy.join(', '),
|
||||||
|
groupByColumn: colsToGroupBy.join(', '),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
groupByQuery: '',
|
||||||
|
groupByColumn: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createGroupBySqlExport() {
|
||||||
|
const rowGroupCols = this.queryModel.rowGroupCols;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const thisSelf = this;
|
||||||
|
|
||||||
|
if (rowGroupCols.length > 0) {
|
||||||
|
const colsToGroupBy = [];
|
||||||
|
rowGroupCols.forEach(function (rowGroupCol) {
|
||||||
|
colsToGroupBy.push(thisSelf.findQueryConfig(rowGroupCol.field));
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
groupByQueryExport: ' GROUP BY ' + colsToGroupBy.join(', '),
|
||||||
|
groupByColumnExport: colsToGroupBy.join(', '),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
groupByQueryExport: '',
|
||||||
|
groupByColumnExport: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ==================================================================
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export function roundingCurrency(value) {
|
||||||
|
if (!value) return value;
|
||||||
|
return Number(value).toFixed(2);
|
||||||
|
}
|
Loading…
Reference in New Issue