Add Didar gap features: lost-reason reports, audit logs, tags, card assignees, and saved-views API.
Ignore Graphify local artifacts and expose new modules in superadmin.
This commit is contained in:
@@ -76,6 +76,7 @@ export class ReportsService {
|
||||
stageId: true,
|
||||
ownerId: true,
|
||||
source: true,
|
||||
lostReason: true,
|
||||
updatedAt: true,
|
||||
createdAt: true,
|
||||
lineItems: {
|
||||
@@ -402,6 +403,27 @@ export class ReportsService {
|
||||
}))
|
||||
.sort((a, b) => b.count - a.count);
|
||||
|
||||
const lostReasonMap = new Map<string, { count: number; amount: number }>();
|
||||
for (const d of lostInPeriod) {
|
||||
const label = normalizeLostReason(d.lostReason);
|
||||
const prev = lostReasonMap.get(label) ?? { count: 0, amount: 0 };
|
||||
prev.count += 1;
|
||||
prev.amount += decimalToBase(d.amount, d.currency, rates);
|
||||
lostReasonMap.set(label, prev);
|
||||
}
|
||||
const lostReasonTotal =
|
||||
[...lostReasonMap.values()].reduce((a, b) => a + b.count, 0) || 1;
|
||||
const lostReasons: ReportsDashboardDto['lostReasons'] = [
|
||||
...lostReasonMap.entries(),
|
||||
]
|
||||
.map(([label, row]) => ({
|
||||
label,
|
||||
count: row.count,
|
||||
amount: Math.round(row.amount),
|
||||
percent: Math.round((row.count / lostReasonTotal) * 1000) / 10,
|
||||
}))
|
||||
.sort((a, b) => b.count - a.count);
|
||||
|
||||
type LineAgg = {
|
||||
name: string;
|
||||
category: string;
|
||||
@@ -636,6 +658,11 @@ export class ReportsService {
|
||||
`بیشترین منبع معاملات برنده «${sources[0].label}» بوده است.`,
|
||||
);
|
||||
}
|
||||
if (lostReasons[0] && lostReasons[0].count > 0) {
|
||||
insights.push(
|
||||
`بیشترین دلیل شکست معاملات «${lostReasons[0].label}» بوده است (${lostReasons[0].count} مورد).`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -647,6 +674,7 @@ export class ReportsService {
|
||||
monthlyComparison,
|
||||
customerGrowth,
|
||||
sources,
|
||||
lostReasons,
|
||||
salesByCategory,
|
||||
topProducts,
|
||||
customerStatus,
|
||||
@@ -1052,6 +1080,12 @@ function normalizeSource(raw: string | null | undefined): string {
|
||||
return map[value.toLowerCase()] ?? value;
|
||||
}
|
||||
|
||||
function normalizeLostReason(raw: string | null | undefined): string {
|
||||
const value = raw?.trim();
|
||||
if (!value) return 'بدون دلیل ثبتشده';
|
||||
return value;
|
||||
}
|
||||
|
||||
function activityTypeLabel(type: string): string {
|
||||
const map: Record<string, string> = {
|
||||
call: 'تماس',
|
||||
|
||||
Reference in New Issue
Block a user