car_mms/app/lib/expense-management.server.ts
2025-09-11 14:22:27 +03:00

176 lines
4.4 KiB
TypeScript

import { prisma } from "./db.server";
import type { Expense } from "@prisma/client";
import type { CreateExpenseData, UpdateExpenseData, FinancialSearchParams } from "~/types/database";
// Get all expenses with search and pagination
export async function getExpenses(
searchQuery?: string,
page: number = 1,
limit: number = 10,
category?: string,
dateFrom?: Date,
dateTo?: Date
): Promise<{
expenses: Expense[];
total: number;
totalPages: number;
}> {
const offset = (page - 1) * limit;
// Build where clause for search and filters
const whereClause: any = {};
if (category) {
whereClause.category = category;
}
if (dateFrom || dateTo) {
whereClause.expenseDate = {};
if (dateFrom) {
whereClause.expenseDate.gte = dateFrom;
}
if (dateTo) {
whereClause.expenseDate.lte = dateTo;
}
}
if (searchQuery) {
const searchLower = searchQuery.toLowerCase();
whereClause.OR = [
{ description: { contains: searchLower } },
{ category: { contains: searchLower } },
];
}
const [expenses, total] = await Promise.all([
prisma.expense.findMany({
where: whereClause,
orderBy: { expenseDate: 'desc' },
skip: offset,
take: limit,
}),
prisma.expense.count({ where: whereClause }),
]);
const totalPages = Math.ceil(total / limit);
return {
expenses,
total,
totalPages,
};
}
// Get a single expense by ID
export async function getExpenseById(id: number): Promise<Expense | null> {
return prisma.expense.findUnique({
where: { id },
});
}
// Create a new expense
export async function createExpense(data: CreateExpenseData): Promise<Expense> {
return prisma.expense.create({
data: {
description: data.description,
category: data.category,
amount: data.amount,
expenseDate: data.expenseDate || new Date(),
},
});
}
// Update an existing expense
export async function updateExpense(id: number, data: UpdateExpenseData): Promise<Expense> {
return prisma.expense.update({
where: { id },
data: {
description: data.description,
category: data.category,
amount: data.amount,
expenseDate: data.expenseDate,
},
});
}
// Delete an expense
export async function deleteExpense(id: number): Promise<void> {
await prisma.expense.delete({
where: { id },
});
}
// Get expense categories for dropdown
export async function getExpenseCategories(): Promise<string[]> {
const categories = await prisma.expense.findMany({
select: { category: true },
distinct: ['category'],
orderBy: { category: 'asc' },
});
return categories.map(c => c.category);
}
// Get expenses by category for reporting
export async function getExpensesByCategory(
dateFrom?: Date,
dateTo?: Date
): Promise<{ category: string; total: number; count: number }[]> {
const whereClause: any = {};
if (dateFrom || dateTo) {
whereClause.expenseDate = {};
if (dateFrom) {
whereClause.expenseDate.gte = dateFrom;
}
if (dateTo) {
whereClause.expenseDate.lte = dateTo;
}
}
const result = await prisma.expense.groupBy({
by: ['category'],
where: whereClause,
_sum: {
amount: true,
},
_count: {
id: true,
},
orderBy: {
_sum: {
amount: 'desc',
},
},
});
return result.map(item => ({
category: item.category,
total: item._sum.amount || 0,
count: item._count.id,
}));
}
// Get total expenses for a date range
export async function getTotalExpenses(dateFrom?: Date, dateTo?: Date): Promise<number> {
const whereClause: any = {};
if (dateFrom || dateTo) {
whereClause.expenseDate = {};
if (dateFrom) {
whereClause.expenseDate.gte = dateFrom;
}
if (dateTo) {
whereClause.expenseDate.lte = dateTo;
}
}
const result = await prisma.expense.aggregate({
where: whereClause,
_sum: {
amount: true,
},
});
return result._sum.amount || 0;
}