176 lines
4.4 KiB
TypeScript
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;
|
|
} |