uup
This commit is contained in:
176
app/lib/expense-management.server.ts
Normal file
176
app/lib/expense-management.server.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user