Add reporting features with new report engine and bookmark management
- Introduced a comprehensive report engine for generating and managing reports, including sales and logistics reports. - Added new API endpoints for retrieving report configurations, data, and metadata, ensuring secure access with privilege checks. - Implemented a report bookmarks system to allow users to save and manage report filters and configurations. - Created database migrations for the `report_bookmarks` table and updated the schema to support new report functionalities. - Developed services and controllers for handling report queries and bookmarks, including CRUD operations for bookmarks. - Enhanced API documentation to reflect the new reporting features and endpoints. - Added unit and integration tests to validate the new functionalities and ensure data integrity across report operations.
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { toListPage } from '../../../common/http/response';
|
||||
import type { PaginationResponse } from '../../../common/http/response';
|
||||
import {
|
||||
REPORT_BOOKMARK_TYPE,
|
||||
type ReportBookmarkType,
|
||||
} from '../shared/constants/bookmark-type';
|
||||
import { findReportConfig } from '../shared/configs';
|
||||
import {
|
||||
ReportBookmarksRepository,
|
||||
type ReportBookmark,
|
||||
} from './report-bookmarks.repository';
|
||||
|
||||
export type ListReportBookmarksQuery = {
|
||||
groupName?: string;
|
||||
uniqueName?: string;
|
||||
type?: ReportBookmarkType;
|
||||
label?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
};
|
||||
|
||||
export type CreateReportBookmarkInput = {
|
||||
groupName: string;
|
||||
uniqueName: string;
|
||||
label: string;
|
||||
type: ReportBookmarkType;
|
||||
applied?: boolean;
|
||||
configuration: unknown;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ReportBookmarksService {
|
||||
constructor(private readonly repository: ReportBookmarksRepository) {}
|
||||
|
||||
async list(
|
||||
ownerId: string,
|
||||
query: ListReportBookmarksQuery,
|
||||
): Promise<PaginationResponse<ReturnType<ReportBookmarksService['toDto']>>> {
|
||||
const page = toListPage(query);
|
||||
const { data, total } = await this.repository.list(ownerId, {
|
||||
groupName: query.groupName,
|
||||
uniqueName: query.uniqueName,
|
||||
type: query.type,
|
||||
label: query.label,
|
||||
limit: page.limit,
|
||||
offset: page.offset,
|
||||
});
|
||||
return { data: data.map((item) => this.toDto(item)), total };
|
||||
}
|
||||
|
||||
async labelHistory(ownerId: string, label?: string): Promise<string[]> {
|
||||
return this.repository.labelHistory(ownerId, label);
|
||||
}
|
||||
|
||||
async findApplied(
|
||||
ownerId: string,
|
||||
groupName?: string,
|
||||
uniqueName?: string,
|
||||
type?: ReportBookmarkType,
|
||||
) {
|
||||
if (!groupName || !uniqueName || !type) {
|
||||
throw new BadRequestException(
|
||||
'groupName, uniqueName, and type are required',
|
||||
);
|
||||
}
|
||||
const bookmark = await this.repository.findApplied(
|
||||
ownerId,
|
||||
groupName,
|
||||
uniqueName,
|
||||
type,
|
||||
);
|
||||
return bookmark ? this.toDto(bookmark) : null;
|
||||
}
|
||||
|
||||
async create(ownerId: string, input: CreateReportBookmarkInput) {
|
||||
this.assertValidBookmarkInput(input);
|
||||
const bookmark = await this.repository.create(ownerId, {
|
||||
groupName: input.groupName,
|
||||
uniqueName: input.uniqueName,
|
||||
label: input.label,
|
||||
type: input.type,
|
||||
applied: input.applied ?? false,
|
||||
configuration: input.configuration,
|
||||
});
|
||||
return this.toDto(bookmark);
|
||||
}
|
||||
|
||||
async apply(ownerId: string, id: string) {
|
||||
const bookmark = await this.repository.apply(ownerId, id);
|
||||
return this.toDto(bookmark);
|
||||
}
|
||||
|
||||
async unapply(ownerId: string, id: string) {
|
||||
const bookmark = await this.repository.unapply(ownerId, id);
|
||||
return this.toDto(bookmark);
|
||||
}
|
||||
|
||||
async delete(ownerId: string, id: string): Promise<void> {
|
||||
await this.repository.delete(ownerId, id);
|
||||
}
|
||||
|
||||
private assertValidBookmarkInput(input: CreateReportBookmarkInput) {
|
||||
if (!findReportConfig(input.groupName, input.uniqueName)) {
|
||||
throw new NotFoundException('Report not found');
|
||||
}
|
||||
if (
|
||||
input.type !== REPORT_BOOKMARK_TYPE.FILTER_TABLE &&
|
||||
input.type !== REPORT_BOOKMARK_TYPE.TABLE_CONFIG
|
||||
) {
|
||||
throw new BadRequestException('Invalid bookmark type');
|
||||
}
|
||||
}
|
||||
|
||||
private toDto(bookmark: ReportBookmark) {
|
||||
return {
|
||||
id: bookmark.id,
|
||||
groupName: bookmark.groupName,
|
||||
uniqueName: bookmark.uniqueName,
|
||||
label: bookmark.label,
|
||||
type: bookmark.type,
|
||||
applied: bookmark.applied,
|
||||
configuration: bookmark.configuration,
|
||||
status: bookmark.status,
|
||||
createdAt: bookmark.createdAt,
|
||||
updatedAt: bookmark.updatedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user