feat(api): add BudgetsController with status, create, update endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user