using Budget.Api.Data; using Budget.Api.DTOs; using Budget.Api.Models; using Budget.Api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace Budget.Api.Controllers; [ApiController] [Route("api/budgets/{budgetId:guid}/incomes")] [Authorize] public class IncomesController(AppDbContext db, BudgetAuthorizationService authz) : ControllerBase { private string UserId => User.FindFirst("sub")!.Value; private static IncomeDto ToDto(Income i) => new( i.Id, i.BudgetId, i.Name, i.Frequency, i.Amount, FrequencyCalculator.ToMonthly(i.Amount, i.Frequency), FrequencyCalculator.ToAnnually(i.Amount, i.Frequency), i.SortOrder); [HttpGet] public async Task List(Guid budgetId) { if (!await authz.CanReadAsync(budgetId, UserId)) return Forbid(); var incomes = await db.Incomes .Where(i => i.BudgetId == budgetId) .OrderBy(i => i.SortOrder) .ToListAsync(); return Ok(incomes.Select(ToDto)); } [HttpPost] public async Task Create(Guid budgetId, [FromBody] CreateIncomeRequest req) { if (!await authz.CanWriteAsync(budgetId, UserId)) return Forbid(); var maxOrder = await db.Incomes.Where(i => i.BudgetId == budgetId).MaxAsync(i => (int?)i.SortOrder) ?? -1; var income = new Income { Id = Guid.NewGuid(), BudgetId = budgetId, Name = req.Name, Frequency = req.Frequency, Amount = req.Amount, SortOrder = maxOrder + 1, }; db.Incomes.Add(income); await db.SaveChangesAsync(); return Ok(ToDto(income)); } [HttpPut("{incomeId:guid}")] public async Task Update(Guid budgetId, Guid incomeId, [FromBody] UpdateIncomeRequest req) { if (!await authz.CanWriteAsync(budgetId, UserId)) return Forbid(); var income = await db.Incomes.FirstOrDefaultAsync(i => i.Id == incomeId && i.BudgetId == budgetId); if (income is null) return NotFound(); income.Name = req.Name; income.Frequency = req.Frequency; income.Amount = req.Amount; await db.SaveChangesAsync(); return Ok(ToDto(income)); } [HttpDelete("{incomeId:guid}")] public async Task Delete(Guid budgetId, Guid incomeId) { if (!await authz.CanWriteAsync(budgetId, UserId)) return Forbid(); var income = await db.Incomes.FirstOrDefaultAsync(i => i.Id == incomeId && i.BudgetId == budgetId); if (income is null) return NotFound(); db.Incomes.Remove(income); await db.SaveChangesAsync(); return NoContent(); } [HttpPut("order")] public async Task Reorder(Guid budgetId, [FromBody] ReorderIncomesRequest req) { if (!await authz.CanWriteAsync(budgetId, UserId)) return Forbid(); var incomes = await db.Incomes.Where(i => i.BudgetId == budgetId).ToListAsync(); var lookup = incomes.ToDictionary(i => i.Id); for (int idx = 0; idx < req.OrderedIds.Count; idx++) { if (lookup.TryGetValue(req.OrderedIds[idx], out var income)) income.SortOrder = idx; } await db.SaveChangesAsync(); return NoContent(); } }