- 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.
108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
import { DateTime } from '../../../../common/value-objects/date-time/date-time';
|
|
import { Decimal } from '../../../../common/value-objects/decimal/decimal';
|
|
import { DATA_FORMAT } from '../constants/data-format';
|
|
import type { DataFormat } from '../constants/data-format';
|
|
import type { ReportColumnConfigEntity } from '../entities/report-config.entity';
|
|
|
|
export function formatReportCell(raw: unknown, format: DataFormat): unknown {
|
|
if (raw === null || raw === undefined) {
|
|
switch (format) {
|
|
case DATA_FORMAT.NUMBER:
|
|
case DATA_FORMAT.CURRENCY:
|
|
case DATA_FORMAT.MINUS_CURRENCY:
|
|
return Decimal.zero().toString();
|
|
default:
|
|
return raw;
|
|
}
|
|
}
|
|
|
|
switch (format) {
|
|
case DATA_FORMAT.TEXT:
|
|
return String(raw);
|
|
case DATA_FORMAT.TEXT_UPPERCASE:
|
|
return String(raw).toUpperCase();
|
|
case DATA_FORMAT.TEXT_LOWERCASE:
|
|
return String(raw).toLowerCase();
|
|
case DATA_FORMAT.NUMBER:
|
|
return formatDecimalOrZero(raw);
|
|
case DATA_FORMAT.CURRENCY:
|
|
return formatDecimalOrZero(raw);
|
|
case DATA_FORMAT.MINUS_CURRENCY:
|
|
return negateDecimalString(formatDecimalOrZero(raw));
|
|
case DATA_FORMAT.PERCENTAGE:
|
|
return `${raw}%`;
|
|
case DATA_FORMAT.BOOLEAN:
|
|
return raw === true || raw === 1 || raw === '1' ? 'Yes' : 'No';
|
|
case DATA_FORMAT.STATUS:
|
|
return String(raw);
|
|
case DATA_FORMAT.DATE_EPOCH:
|
|
return formatEpoch(raw);
|
|
case DATA_FORMAT.DATE_TIMESTAMP:
|
|
return formatTimestamp(raw);
|
|
default:
|
|
return raw;
|
|
}
|
|
}
|
|
|
|
export function formatReportRow(
|
|
row: Record<string, unknown>,
|
|
columnConfigs: ReportColumnConfigEntity[],
|
|
): Record<string, unknown> {
|
|
const formatted: Record<string, unknown> = { ...row };
|
|
for (const col of columnConfigs) {
|
|
if (col.column in formatted) {
|
|
formatted[col.column] = formatReportCell(
|
|
formatted[col.column],
|
|
col.format,
|
|
);
|
|
}
|
|
}
|
|
return formatted;
|
|
}
|
|
|
|
function formatDecimalOrZero(raw: unknown): string {
|
|
try {
|
|
if (raw === null || raw === undefined || raw === '') {
|
|
return Decimal.zero().toString();
|
|
}
|
|
return Decimal.create(String(raw)).toString();
|
|
} catch {
|
|
return Decimal.zero().toString();
|
|
}
|
|
}
|
|
|
|
function negateDecimalString(value: string): string {
|
|
try {
|
|
const decimal = Decimal.create(value);
|
|
if (decimal.isZero()) {
|
|
return decimal.toString();
|
|
}
|
|
return Decimal.create(`-${value.replace(/^-/, '')}`).toString();
|
|
} catch {
|
|
return Decimal.zero().toString();
|
|
}
|
|
}
|
|
|
|
function formatEpoch(raw: unknown): string {
|
|
const ms = Number(raw);
|
|
if (!Number.isFinite(ms)) {
|
|
return String(raw);
|
|
}
|
|
return DateTime.fromUnixMs(ms).format();
|
|
}
|
|
|
|
function formatTimestamp(raw: unknown): string {
|
|
if (typeof raw === 'string') {
|
|
try {
|
|
return DateTime.create(raw).format();
|
|
} catch {
|
|
return raw;
|
|
}
|
|
}
|
|
const ms = Number(raw);
|
|
if (Number.isFinite(ms)) {
|
|
return DateTime.fromUnixMs(ms).format();
|
|
}
|
|
return String(raw);
|
|
}
|