feat(expenses): add monthly totals and pagination support for expenses
This commit is contained in:
@@ -7,6 +7,8 @@ import { generateMonthlyExpenseHandler } from "./handlers/generateMonthlyExpense
|
||||
import { listExpensesHandler } from "./handlers/listExpenses";
|
||||
import { createNonPeriodicExpenseHandler } from "./handlers/createNonPeriodicExpense";
|
||||
import { payExpenseHandler } from "./handlers/payExpense";
|
||||
import { getMonthlyPayedTotalHandler } from "./handlers/getMonthlyPayedTotal";
|
||||
import { getMonthlyTotalsHandler } from "./handlers/getMonthlyTotals";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
@@ -16,6 +18,8 @@ app.put("/periodic-expenses/:id", updatePeriodicExpenseHandler);
|
||||
app.delete("/periodic-expenses/:id", softDeletePeriodicExpenseHandler);
|
||||
app.post("/periodic-expenses/:id/generate-month", generateMonthlyExpenseHandler);
|
||||
|
||||
app.get("/expenses/monthly-total", getMonthlyPayedTotalHandler);
|
||||
app.get("/expenses/totals", getMonthlyTotalsHandler);
|
||||
app.get("/expenses", listExpensesHandler);
|
||||
app.post("/expenses", createNonPeriodicExpenseHandler);
|
||||
app.put("/expenses/:id/pay", payExpenseHandler);
|
||||
|
||||
@@ -84,16 +84,27 @@ export async function generateMonthlyExpense(id: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function listExpenses(status?: string) {
|
||||
export async function listExpenses(params: {
|
||||
status?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}) {
|
||||
const { status, page = 1, pageSize = 10 } = params;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (status === "PENDING" || status === "PAYED") {
|
||||
where.status = status;
|
||||
}
|
||||
return prisma.expense.findMany({
|
||||
where,
|
||||
include: { periodicExpense: true },
|
||||
orderBy: { dueDate: "asc" },
|
||||
});
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.expense.findMany({
|
||||
where,
|
||||
include: { periodicExpense: true },
|
||||
orderBy: { dueDate: "desc" },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
prisma.expense.count({ where }),
|
||||
]);
|
||||
return { data, total, page, pageSize };
|
||||
}
|
||||
|
||||
export async function createNonPeriodicExpense(data: z.infer<typeof createNonPeriodicExpenseSchema>) {
|
||||
@@ -126,6 +137,32 @@ export async function payExpense(id: number, data: z.infer<typeof payExpenseSche
|
||||
});
|
||||
}
|
||||
|
||||
export async function getMonthlyPayedTotal(year: number, month: number) {
|
||||
const expenses = await prisma.expense.findMany({
|
||||
where: { year, month, status: PaymentStatus.PAYED },
|
||||
select: { amountPayed: true },
|
||||
});
|
||||
const total = expenses.reduce((sum, e) => sum + Number(e.amountPayed ?? 0), 0);
|
||||
return { total };
|
||||
}
|
||||
|
||||
export async function getMonthlyTotals(year: number, month: number) {
|
||||
const [payed, pending] = await Promise.all([
|
||||
prisma.expense.findMany({
|
||||
where: { year, month, status: PaymentStatus.PAYED },
|
||||
select: { amountPayed: true },
|
||||
}),
|
||||
prisma.expense.findMany({
|
||||
where: { year, month, status: PaymentStatus.PENDING },
|
||||
select: { amount: true },
|
||||
}),
|
||||
]);
|
||||
return {
|
||||
payed: payed.reduce((sum, e) => sum + Number(e.amountPayed ?? 0), 0),
|
||||
pending: pending.reduce((sum, e) => sum + Number(e.amount), 0),
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateExpensesForCurrentMonth() {
|
||||
const { year, month } = getCurrentYearMonthUTC();
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Context } from "hono";
|
||||
import { getMonthlyPayedTotal } from "../expenses.service";
|
||||
|
||||
export async function getMonthlyPayedTotalHandler(c: Context) {
|
||||
const year = parseInt(c.req.query("year") ?? String(new Date().getUTCFullYear()), 10);
|
||||
const month = parseInt(c.req.query("month") ?? String(new Date().getUTCMonth() + 1), 10);
|
||||
const result = await getMonthlyPayedTotal(year, month);
|
||||
return c.json(result);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Context } from "hono";
|
||||
import { getMonthlyTotals } from "../expenses.service";
|
||||
|
||||
export async function getMonthlyTotalsHandler(c: Context) {
|
||||
const year = parseInt(c.req.query("year") ?? String(new Date().getUTCFullYear()), 10);
|
||||
const month = parseInt(c.req.query("month") ?? String(new Date().getUTCMonth() + 1), 10);
|
||||
const result = await getMonthlyTotals(year, month);
|
||||
return c.json(result);
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import { listExpenses } from "../expenses.service";
|
||||
|
||||
export async function listExpensesHandler(c: Context) {
|
||||
const status = c.req.query("status");
|
||||
const expenses = await listExpenses(status);
|
||||
return c.json(expenses);
|
||||
const page = parseInt(c.req.query("page") ?? "1", 10);
|
||||
const pageSize = parseInt(c.req.query("pageSize") ?? "10", 10);
|
||||
const result = await listExpenses({ status, page, pageSize });
|
||||
return c.json(result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user