feat(api): add BudgetsController with status, create, update endpoints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 20:34:07 -04:00
parent e773a0f218
commit 004f99c2b4
+103
View File
@@ -0,0 +1,103 @@
using Microsoft.AspNetCore.Mvc;
using MoneyMap.Models;
using MoneyMap.Services;
namespace MoneyMap.Controllers;
[ApiController]
[Route("api/[controller]")]
public class BudgetsController : ControllerBase
{
private readonly IBudgetService _budgetService;
public BudgetsController(IBudgetService budgetService) => _budgetService = budgetService;
[HttpGet("status")]
public async Task<IActionResult> GetStatus([FromQuery] string? asOfDate = null)
{
DateTime? date = null;
if (!string.IsNullOrWhiteSpace(asOfDate) && DateTime.TryParse(asOfDate, out var parsed))
date = parsed;
var statuses = await _budgetService.GetAllBudgetStatusesAsync(date);
var result = statuses.Select(s => new
{
s.Budget.Id,
Category = s.Budget.DisplayName,
s.Budget.Amount,
Period = s.Budget.Period.ToString(),
s.PeriodStart,
s.PeriodEnd,
s.Spent,
s.Remaining,
PercentUsed = Math.Round(s.PercentUsed, 1),
s.IsOverBudget
}).ToList();
return Ok(result);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateBudgetRequest request)
{
if (!Enum.TryParse<BudgetPeriod>(request.Period, true, out var budgetPeriod))
return BadRequest(new { message = $"Invalid period '{request.Period}'. Must be Weekly, Monthly, or Yearly." });
if (!DateTime.TryParse(request.StartDate, out var startDate))
return BadRequest(new { message = "Invalid start date format" });
var budget = new Budget
{
Category = request.Category,
Amount = request.Amount,
Period = budgetPeriod,
StartDate = startDate,
IsActive = true
};
var result = await _budgetService.CreateBudgetAsync(budget);
return Ok(new { result.Success, result.Message, BudgetId = budget.Id });
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateBudgetRequest request)
{
var budget = await _budgetService.GetBudgetByIdAsync(id);
if (budget == null)
return NotFound(new { message = "Budget not found" });
if (request.Amount.HasValue)
budget.Amount = request.Amount.Value;
if (!string.IsNullOrWhiteSpace(request.Period))
{
if (!Enum.TryParse<BudgetPeriod>(request.Period, true, out var budgetPeriod))
return BadRequest(new { message = $"Invalid period '{request.Period}'. Must be Weekly, Monthly, or Yearly." });
budget.Period = budgetPeriod;
}
if (request.IsActive.HasValue)
budget.IsActive = request.IsActive.Value;
var result = await _budgetService.UpdateBudgetAsync(budget);
return Ok(new { result.Success, result.Message });
}
}
public class CreateBudgetRequest
{
public string? Category { get; set; }
public decimal Amount { get; set; }
public string Period { get; set; } = "";
public string StartDate { get; set; } = "";
}
public class UpdateBudgetRequest
{
public decimal? Amount { get; set; }
public string? Period { get; set; }
public bool? IsActive { get; set; }
}