Files
trackgo-be/src/modules/reports/shared/configs/sales-reports.ts
T
shancheas 51db4f4a4d Refactor privilege management to support hierarchical privilege keys
- Updated privilege key structure to use a 3- or 4-part dotted hierarchy (e.g., `GROUP.PARENT.MODULE`).
- Modified the `RequirePrivilege` decorator to accept multiple keys, allowing for OR logic in privilege checks.
- Enhanced `PrivilegesGuard` to validate against multiple privilege keys, improving access control logic.
- Created migration scripts to update existing privilege keys in the database to the new format.
- Updated related services, controllers, and tests to accommodate the new privilege key structure and validation logic.
2026-09-01 13:14:58 +07:00

571 lines
15 KiB
TypeScript

import {
DATA_FORMAT,
DATA_TYPE,
FILTER_FIELD_TYPE,
FILTER_TYPE,
REPORT_GROUP,
} from '../constants';
import type { ReportConfigEntity } from '../entities';
const SALES_PREFIX = `${REPORT_GROUP.SALES_REPORT}__`;
const LOGISTICS_PREFIX = `${REPORT_GROUP.LOGISTICS_REPORT}__`;
export const salesOrderReport: ReportConfigEntity = {
groupName: REPORT_GROUP.SALES_REPORT,
uniqueName: `${SALES_PREFIX}sales_order`,
privilegeKey: 'ADMIN.SALES.REPORT',
label: 'Report Sales Order',
tableSchema: `sales_orders main
JOIN customers cust ON cust.id = main.customer_id
JOIN branches br ON br.id = main.branch_id
JOIN divisions dv ON dv.id = main.division_id
JOIN employees emp ON emp.id = main.sales_person_id
LEFT JOIN (
SELECT sales_order_id, SUM(quantity * price) AS amount
FROM sales_order_products
GROUP BY sales_order_id
) sop ON sop.sales_order_id = main.id`,
mainTableAlias: 'main',
whereDefaultConditions: [],
defaultOrderBy: ['main.created_at DESC'],
lowLevelOrderBy: ['main.id DESC'],
filterPeriodConfig: { hidden: true },
columnConfigs: [
{
column: 'main__date',
query: 'main.date',
label: 'Date',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.DATE_EPOCH,
},
{
column: 'br__name',
query: 'br.name',
label: 'Branch',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'dv__name',
query: 'dv.name',
label: 'Division',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__code',
query: 'main.code',
label: 'No. Sales Order',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'cust__name',
query: 'cust.name',
label: 'Customer',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__amount',
query: 'COALESCE(sop.amount, 0)',
label: 'Invoice Amount',
type: DATA_TYPE.MEASURE,
format: DATA_FORMAT.CURRENCY,
},
{
column: 'emp__name',
query: 'emp.name',
label: 'Sales Rep.',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__status',
query: 'main.status',
label: 'Last Status Order',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.STATUS,
},
],
filterConfigs: [
{
fieldLabel: 'Date',
filterColumn: 'main__date',
fieldType: FILTER_FIELD_TYPE.DATE_RANGE_PICKER,
filterType: FILTER_TYPE.TEXT_IN_DATE_RANGE_EPOCH,
},
{
fieldLabel: 'Branch',
filterColumn: 'br__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Division',
filterColumn: 'dv__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Customer',
filterColumn: 'cust__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Sales Rep.',
filterColumn: 'emp__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Status',
filterColumn: 'main__status',
fieldType: FILTER_FIELD_TYPE.SELECT,
filterType: FILTER_TYPE.TEXT_IN_MEMBER_TEXT,
selectCustomOptions: ['draft', 'active', 'archived'],
},
],
customQueryColumn: (column) => {
if (column === 'main__date') {
return 'main.date';
}
return undefined;
},
};
export const salesRequestReport: ReportConfigEntity = {
groupName: REPORT_GROUP.SALES_REPORT,
uniqueName: `${SALES_PREFIX}sales_request`,
privilegeKey: 'ADMIN.SALES.REPORT',
label: 'Report Request Order',
tableSchema: `sales_requests main
JOIN customers cust ON cust.id = main.customer_id
JOIN branches br ON br.id = main.branch_id
JOIN divisions dv ON dv.id = main.division_id
JOIN employees emp ON emp.id = main.sales_person_id`,
mainTableAlias: 'main',
defaultOrderBy: ['main.created_at DESC'],
lowLevelOrderBy: ['main.id DESC'],
filterPeriodConfig: { hidden: true },
columnConfigs: [
{
column: 'main__date',
query: 'main.date',
label: 'Date',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.DATE_EPOCH,
},
{
column: 'br__name',
query: 'br.name',
label: 'Branch',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'dv__name',
query: 'dv.name',
label: 'Division',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__code',
query: 'main.code',
label: 'No. Request Order',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'cust__name',
query: 'cust.name',
label: 'Customer',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'emp__name',
query: 'emp.name',
label: 'Sales Rep.',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__status',
query: 'main.status',
label: 'Status',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.STATUS,
},
],
filterConfigs: [
{
fieldLabel: 'Date',
filterColumn: 'main__date',
fieldType: FILTER_FIELD_TYPE.DATE_RANGE_PICKER,
filterType: FILTER_TYPE.TEXT_IN_DATE_RANGE_EPOCH,
},
{
fieldLabel: 'Branch',
filterColumn: 'br__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Division',
filterColumn: 'dv__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Customer',
filterColumn: 'cust__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Sales Rep.',
filterColumn: 'emp__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Status',
filterColumn: 'main__status',
fieldType: FILTER_FIELD_TYPE.SELECT,
filterType: FILTER_TYPE.TEXT_IN_MEMBER_TEXT,
selectCustomOptions: ['draft', 'active', 'archived'],
},
],
customQueryColumn: (column) => {
if (column === 'main__date') {
return 'main.date';
}
return undefined;
},
};
export const salesInvoiceReport: ReportConfigEntity = {
groupName: REPORT_GROUP.SALES_REPORT,
uniqueName: `${SALES_PREFIX}sales_invoice`,
privilegeKey: 'ADMIN.SALES.REPORT',
label: 'Report Invoice',
tableSchema: `sales_invoices main
JOIN customers cust ON cust.id = main.customer_id
JOIN branches br ON br.id = main.branch_id
JOIN divisions dv ON dv.id = main.division_id
JOIN employees emp ON emp.id = main.sales_person_id`,
mainTableAlias: 'main',
defaultOrderBy: ['main.created_at DESC'],
lowLevelOrderBy: ['main.id DESC'],
filterPeriodConfig: { hidden: true },
columnConfigs: [
{
column: 'main__date',
query: 'main.date',
label: 'Date',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.DATE_EPOCH,
},
{
column: 'br__name',
query: 'br.name',
label: 'Branch',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'dv__name',
query: 'dv.name',
label: 'Division',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'cust__name',
query: 'cust.name',
label: 'Customer',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'cust__code',
query: 'cust.code',
label: 'Customer code',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__sales_order_code',
query: 'main.sales_order_code',
label: 'Sales Order No.',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__code',
query: 'main.code',
label: 'Invoice No.',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__status',
query: 'main.status',
label: 'Status',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.STATUS,
},
{
column: 'emp__name',
query: 'emp.name',
label: 'Sales Rep.',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__balance',
query: 'main.balance',
label: 'Balance',
type: DATA_TYPE.MEASURE,
format: DATA_FORMAT.CURRENCY,
},
],
filterConfigs: [
{
fieldLabel: 'Date',
filterColumn: 'main__date',
fieldType: FILTER_FIELD_TYPE.DATE_RANGE_PICKER,
filterType: FILTER_TYPE.TEXT_IN_DATE_RANGE_EPOCH,
},
{
fieldLabel: 'Branch',
filterColumn: 'br__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Status',
filterColumn: 'main__status',
fieldType: FILTER_FIELD_TYPE.SELECT,
filterType: FILTER_TYPE.TEXT_IN_MEMBER_TEXT,
selectCustomOptions: ['draft', 'active', 'archived'],
},
],
customQueryColumn: (column) => {
if (column === 'main__date') {
return 'main.date';
}
return undefined;
},
};
export const salesPaymentReport: ReportConfigEntity = {
groupName: REPORT_GROUP.SALES_REPORT,
uniqueName: `${SALES_PREFIX}sales_payment`,
privilegeKey: 'ADMIN.SALES.REPORT',
label: 'Report Payment',
tableSchema: `sales_payments main
JOIN sales_payment_invoices spi ON spi.sales_payment_id = main.id
JOIN sales_invoices inv ON inv.id = spi.sales_invoice_id
JOIN customers cust ON cust.id = inv.customer_id
JOIN branches br ON br.id = inv.branch_id
JOIN divisions dv ON dv.id = inv.division_id
JOIN employees emp ON emp.id = inv.sales_person_id`,
mainTableAlias: 'main',
defaultOrderBy: ['main.created_at DESC'],
lowLevelOrderBy: ['main.id DESC'],
filterPeriodConfig: { hidden: true },
columnConfigs: [
{
column: 'main__date',
query: 'main.date',
label: 'Date',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.DATE_EPOCH,
},
{
column: 'main__code',
query: 'main.code',
label: 'Payment No.',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'cust__code',
query: 'cust.code',
label: 'Customer code',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'br__name',
query: 'br.name',
label: 'Branch',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'dv__name',
query: 'dv.name',
label: 'Division',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'emp__name',
query: 'emp.name',
label: 'Sales Rep',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'inv__code',
query: 'inv.code',
label: 'Invoice ID',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'inv__balance',
query: 'inv.balance',
label: 'Invoice Amount',
type: DATA_TYPE.MEASURE,
format: DATA_FORMAT.CURRENCY,
},
{
column: 'spi__amount',
query: 'spi.amount',
label: 'Payment Amount',
type: DATA_TYPE.MEASURE,
format: DATA_FORMAT.CURRENCY,
},
{
column: 'main__status',
query: 'main.status',
label: 'Status',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.STATUS,
},
],
filterConfigs: [
{
fieldLabel: 'Date',
filterColumn: 'main__date',
fieldType: FILTER_FIELD_TYPE.DATE_RANGE_PICKER,
filterType: FILTER_TYPE.TEXT_IN_DATE_RANGE_EPOCH,
},
{
fieldLabel: 'Status',
filterColumn: 'main__status',
fieldType: FILTER_FIELD_TYPE.SELECT,
filterType: FILTER_TYPE.TEXT_IN_MEMBER_TEXT,
selectCustomOptions: ['draft', 'active', 'archived'],
},
],
customQueryColumn: (column) => {
if (column === 'main__date') {
return 'main.date';
}
return undefined;
},
};
export const visitPlanReport: ReportConfigEntity = {
groupName: REPORT_GROUP.SALES_REPORT,
uniqueName: `${SALES_PREFIX}visit_plan`,
privilegeKey: 'ADMIN.SALES.REPORT',
label: 'Report Visit Plan',
tableSchema: `plans main
JOIN employees emp ON emp.id = main.employee_id
JOIN branches start_br ON start_br.id = main.start_branch_id
LEFT JOIN (
SELECT plan_id, COUNT(*) AS destination_count
FROM plan_destinations
GROUP BY plan_id
) pd ON pd.plan_id = main.id
LEFT JOIN (
SELECT plan_id, COUNT(*) AS invoice_count
FROM plan_invoices
GROUP BY plan_id
) pi ON pi.plan_id = main.id`,
mainTableAlias: 'main',
whereDefaultConditions: ["main.purpose = 'sales'"],
defaultOrderBy: ['main.date DESC'],
lowLevelOrderBy: ['main.id DESC'],
filterPeriodConfig: { hidden: true },
columnConfigs: [
{
column: 'main__date',
query: 'main.date',
label: 'Date',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.DATE_EPOCH,
},
{
column: 'emp__name',
query: 'emp.name',
label: 'Sales Rep',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'start_br__name',
query: 'start_br.name',
label: 'Branch',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'pd__destination_count',
query: 'COALESCE(pd.destination_count, 0)',
label: 'Plan',
type: DATA_TYPE.MEASURE,
format: DATA_FORMAT.NUMBER,
},
{
column: 'pi__invoice_count',
query: 'COALESCE(pi.invoice_count, 0)',
label: 'Invoice',
type: DATA_TYPE.MEASURE,
format: DATA_FORMAT.NUMBER,
},
{
column: 'main__status',
query: 'main.status',
label: 'Status',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.STATUS,
},
],
filterConfigs: [
{
fieldLabel: 'Date',
filterColumn: 'main__date',
fieldType: FILTER_FIELD_TYPE.DATE_RANGE_PICKER,
filterType: FILTER_TYPE.TEXT_IN_DATE_RANGE_EPOCH,
},
{
fieldLabel: 'Sales Rep',
filterColumn: 'emp__name',
fieldType: FILTER_FIELD_TYPE.INPUT_TAG,
filterType: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
},
{
fieldLabel: 'Status',
filterColumn: 'main__status',
fieldType: FILTER_FIELD_TYPE.SELECT,
filterType: FILTER_TYPE.TEXT_IN_MEMBER_TEXT,
selectCustomOptions: ['draft', 'active', 'archived'],
},
],
customQueryColumn: (column) => {
if (column === 'main__date') {
return 'main.date';
}
return undefined;
},
};