forked from nathydre21/nepa
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnalyticsService.ts
More file actions
110 lines (91 loc) · 3.03 KB
/
Copy pathAnalyticsService.ts
File metadata and controls
110 lines (91 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { PrismaClient, BillStatus } from '@prisma/client';
import { subDays, format } from 'date-fns';
const prisma = new PrismaClient();
export class AnalyticsService {
async getBillingStats() {
const totalRevenue = await prisma.payment.aggregate({
where: { status: 'SUCCESS' },
_sum: { amount: true }
});
const overdueCount = await prisma.bill.count({
where: { status: BillStatus.OVERDUE }
});
const pendingCount = await prisma.bill.count({
where: { status: BillStatus.PENDING }
});
return {
totalRevenue: totalRevenue._sum.amount || 0,
overdueBills: overdueCount,
pendingBills: pendingCount
};
}
async getLateFeeRevenue() {
return prisma.bill.aggregate({
where: { lateFee: { gt: 0 } },
_sum: { lateFee: true }
});
}
async getDailyRevenue(days: number = 30) {
const startDate = subDays(new Date(), days);
const payments = await prisma.payment.findMany({
where: {
status: 'SUCCESS',
createdAt: { gte: startDate }
},
select: { createdAt: true, amount: true }
});
const revenueMap = new Map<string, number>();
payments.forEach(p => {
const date = format(p.createdAt, 'yyyy-MM-dd');
const amount = Number(p.amount);
revenueMap.set(date, (revenueMap.get(date) || 0) + amount);
});
return Array.from(revenueMap.entries())
.map(([date, value]) => ({ date, value }))
.sort((a, b) => a.date.localeCompare(b.date));
}
async getUserGrowth(days: number = 30) {
const startDate = subDays(new Date(), days);
const users = await prisma.user.findMany({
where: { createdAt: { gte: startDate } },
select: { createdAt: true }
});
const growthMap = new Map<string, number>();
users.forEach(u => {
const date = format(u.createdAt, 'yyyy-MM-dd');
growthMap.set(date, (growthMap.get(date) || 0) + 1);
});
return Array.from(growthMap.entries())
.map(([date, count]) => ({ date, count }))
.sort((a, b) => a.date.localeCompare(b.date));
}
async predictRevenue() {
// Simple predictive model: Moving Average of last 30 days
const dailyRevenue = await this.getDailyRevenue(30);
if (dailyRevenue.length === 0) return { prediction: 0, confidence: 'LOW' };
const total = dailyRevenue.reduce((sum, day) => sum + day.value, 0);
const average = total / 30;
return {
predictedDailyRevenue: average,
predictedMonthlyRevenue: average * 30,
trend: dailyRevenue[dailyRevenue.length - 1].value > average ? 'UP' : 'DOWN',
confidence: 'MEDIUM'
};
}
async saveReport(userId: string, title: string, type: string, data: any) {
return prisma.report.create({
data: {
title,
type,
data,
createdBy: userId
}
});
}
async exportRevenueData() {
const data = await this.getDailyRevenue(90);
const header = 'Date,Revenue\n';
const rows = data.map(row => `${row.date},${row.value.toFixed(2)}`).join('\n');
return header + rows;
}
}