import type express from 'express'; import { asString, asyncErr, KError } from '../helpers'; import { Budget, Category } from '../models'; import { checkBudget } from '../shared/validators'; import type { IdentifiedRequest, PreloadedRequest } from './routes'; async function createBudget( userId: number, budget: { viewId: number; year: number; month: number; categoryId: number; threshold: number | null; } ) { // Missing parameters if (typeof budget.categoryId !== 'undefined') { const categoryExists = await Category.exists(userId, budget.categoryId); if (!categoryExists) { throw new KError(`Category ${budget.categoryId} not found`, 404); } } const error = checkBudget(budget); if (error) { throw new KError(error, 400); } return await Budget.create(userId, budget); } export async function getByYearAndMonth(req: IdentifiedRequest, res: express.Response) { try { const { id: userId } = req.user; const { viewId: viewIdStr, year: yearStr, month: monthStr } = req.params; const viewId = Number.parseInt(asString(viewIdStr), 10); if (Number.isNaN(viewId)) { throw new KError('Invalid viewId parameter', 400); } const year = Number.parseInt(asString(yearStr), 10); if (Number.isNaN(year)) { throw new KError('Invalid year parameter', 400); } const month = Number.parseInt(asString(monthStr), 10); if (Number.isNaN(month) || month < 0 || month > 11) { throw new KError('Invalid month parameter', 400); } const budgets = await Budget.byYearAndMonth(userId, viewId, year, month); // Ensure there is a budget for each category. const categories = await Category.all(userId); for (const cat of categories) { if (!budgets.find(b => b.categoryId === cat.id)) { // Retrieve the last threshold used for this category instead of defaulting to 0. // "last" here means "last in time" not last entered (TODO: fix it when we'll be // able to sort by creation/update order). const sameCategoryBudgets = await Budget.byCategory(userId, viewId, cat.id); let currentYear = 0; let currentMonth = 0; let threshold: number | null = null; for (const b of sameCategoryBudgets) { if ( b.year > currentYear || (b.year === currentYear && b.month > currentMonth) ) { currentYear = b.year; currentMonth = b.month; threshold = b.threshold; } } const budget = await createBudget(userId, { viewId, year, month, categoryId: cat.id, threshold, }); budgets.push(budget); } } res.status(200).json({ viewId, year, month, budgets, }); } catch (err) { asyncErr(res, err, 'when loading budgets by year/month'); } } export async function update(req: PreloadedRequest, res: express.Response) { try { const { id: userId } = req.user; const params = req.body; const { viewId: viewIdStr, year: yearStr, month: monthStr, budgetCatId } = req.params; const viewId = Number.parseInt(asString(viewIdStr), 10); const year = Number.parseInt(asString(yearStr), 10); const month = Number.parseInt(asString(monthStr), 10); const categoryId = Number.parseInt(asString(budgetCatId), 10); const error = checkBudget({ viewId, year, month, threshold: params.threshold, }); if (error) { throw new KError(error, 400); } const newBudget = await Budget.findAndUpdate( userId, viewId, categoryId, year, month, params.threshold ); res.status(200).json(newBudget); } catch (err) { asyncErr(res, err, 'when updating a budget'); } }