Files
Aberwyn/Aberwyn/Controllers/BudgetApiController.cs
Elias Jansson eed1ce166f
All checks were successful
continuous-integration/drone/push Build is passing
Budget complete!
2025-05-28 10:29:56 +02:00

429 lines
15 KiB
C#

using Aberwyn.Data;
using Aberwyn.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Aberwyn.Controllers
{
[Authorize(Roles = "Budget")]
[ApiController]
[Route("api/budget")]
public class BudgetApiController : ControllerBase
{
private readonly ApplicationDbContext _context;
public BudgetApiController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet("{year:int}/{month:int}")]
public async Task<IActionResult> GetBudget(int year, int month)
{
try
{
var period = await _context.BudgetPeriods
.Include(p => p.Categories)
.ThenInclude(c => c.Items)
.FirstOrDefaultAsync(p => p.Year == year && p.Month == month);
if (period == null)
{
return Ok(new BudgetDto
{
Year = year,
Month = month,
Categories = new List<BudgetCategoryDto>()
});
}
var dto = new BudgetDto
{
Id = period.Id,
Year = period.Year,
Month = period.Month,
Categories = period.Categories
.OrderBy(cat => cat.Order)
.Select(cat => new BudgetCategoryDto
{
Id = cat.Id,
Name = cat.Name,
Color = cat.Color,
Items = cat.Items
.OrderBy(i => i.Order) // ← sortera innan mappning
.Select(i => new BudgetItemDto
{
Id = i.Id,
Name = i.Name,
Amount = i.Amount,
IsExpense = i.IsExpense,
IncludeInSummary = i.IncludeInSummary,
BudgetItemDefinitionId = i.BudgetItemDefinitionId
}).ToList()
}).ToList()
};
return Ok(dto);
}
catch (Exception ex)
{
return StatusCode(500, $"Fel: {ex.Message} \n{ex.StackTrace}");
}
}
[HttpPut("category/{id}")]
public async Task<IActionResult> UpdateCategory(int id, [FromBody] BudgetCategoryDto updatedCategory)
{
var category = await _context.BudgetCategories
.Include(c => c.Items)
.FirstOrDefaultAsync(c => c.Id == id);
if (category == null)
return NotFound();
// Hämta eller skapa kategori-definition
if (updatedCategory.BudgetCategoryDefinitionId.HasValue)
{
var def = await _context.BudgetCategoryDefinitions
.FirstOrDefaultAsync(d => d.Id == updatedCategory.BudgetCategoryDefinitionId.Value);
if (def != null)
{
category.BudgetCategoryDefinitionId = def.Id;
}
}
else
{
// Om ingen ID angavs, försök hitta via namn
var def = await _context.BudgetCategoryDefinitions
.FirstOrDefaultAsync(d => d.Name.ToLower() == updatedCategory.Name.ToLower());
if (def == null)
{
def = new BudgetCategoryDefinition
{
Name = updatedCategory.Name,
Color = updatedCategory.Color ?? "#666666"
};
_context.BudgetCategoryDefinitions.Add(def);
await _context.SaveChangesAsync();
}
category.BudgetCategoryDefinitionId = def.Id;
}
// ✅ Uppdatera namn och färg (kan avvika från definitionens namn)
category.Name = updatedCategory.Name;
category.Color = updatedCategory.Color;
// Spara undan inkommande IDs (ignorera nya med Id = 0)
var incomingItemIds = updatedCategory.Items
.Where(i => i.Id != 0)
.Select(i => i.Id)
.ToList();
// Ta bort poster som inte längre finns i inkommande data
var itemsToRemove = category.Items
.Where(i => !incomingItemIds.Contains(i.Id))
.ToList();
_context.BudgetItems.RemoveRange(itemsToRemove);
// Lägg till nya poster eller uppdatera befintliga
foreach (var incoming in updatedCategory.Items)
{
if (incoming.Id == 0)
{
category.Items.Add(new BudgetItem
{
Name = incoming.Name,
Amount = incoming.Amount,
IsExpense = incoming.IsExpense,
IncludeInSummary = incoming.IncludeInSummary,
Order = incoming.Order,
BudgetCategoryId = category.Id
});
}
else
{
var existing = category.Items.FirstOrDefault(i => i.Id == incoming.Id);
if (existing != null)
{
existing.Name = incoming.Name;
existing.Amount = incoming.Amount;
existing.IsExpense = incoming.IsExpense;
existing.IncludeInSummary = incoming.IncludeInSummary;
existing.Order = incoming.Order;
}
}
}
await _context.SaveChangesAsync();
return NoContent();
}
[HttpPut("category/order")]
public async Task<IActionResult> UpdateCategoryOrder([FromBody] List<BudgetCategoryDto> orderedCategories)
{
foreach (var dto in orderedCategories)
{
var cat = await _context.BudgetCategories.FindAsync(dto.Id);
if (cat != null)
cat.Order = dto.Order;
}
await _context.SaveChangesAsync();
return NoContent();
}
[HttpPost]
public async Task<IActionResult> CreatePeriod([FromBody] BudgetPeriod newPeriod)
{
_context.BudgetPeriods.Add(newPeriod);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetBudget), new { year = newPeriod.Year, month = newPeriod.Month }, newPeriod);
}
[HttpPut("item/{id}")]
public async Task<IActionResult> UpdateItem(int id, [FromBody] BudgetItem updatedItem)
{
var item = await _context.BudgetItems.FindAsync(id);
if (item == null) return NotFound();
item.Name = updatedItem.Name;
item.Amount = updatedItem.Amount;
item.IsExpense = updatedItem.IsExpense;
item.IncludeInSummary = updatedItem.IncludeInSummary;
item.Order = updatedItem.Order;
item.BudgetCategoryId = updatedItem.BudgetCategoryId;
await _context.SaveChangesAsync();
return Ok();
}
[HttpGet("definitions/items")]
public async Task<IActionResult> GetItemDefinitions()
{
var definitions = await _context.BudgetItemDefinitions
.OrderBy(d => d.Name)
.ToListAsync();
return Ok(definitions);
}
[HttpGet("definitions/categories")]
public async Task<IActionResult> GetCategoryDefinitions()
{
var definitions = await _context.BudgetCategoryDefinitions
.OrderBy(d => d.Name)
.ToListAsync();
return Ok(definitions);
}
[HttpPost("item")]
public async Task<IActionResult> CreateItem([FromBody] BudgetItem newItem)
{
if (newItem == null || newItem.BudgetCategoryId == 0 || string.IsNullOrWhiteSpace(newItem.Name))
return BadRequest("Ogiltig data.");
// ✅ Om BudgetItemDefinitionId är angiven, använd den
if (newItem.BudgetItemDefinitionId.HasValue && newItem.BudgetItemDefinitionId.Value > 0)
{
var existingDef = await _context.BudgetItemDefinitions
.FirstOrDefaultAsync(d => d.Id == newItem.BudgetItemDefinitionId);
if (existingDef == null)
return BadRequest("Ogiltigt definition-ID.");
// valfritt: du kan här också jämföra `newItem.Name != existingDef.Name`
// och t.ex. logga det som ett användarval av etikett.
}
else
{
// Om ID inte är angivet, sök på namn som fallback
var definition = await _context.BudgetItemDefinitions
.FirstOrDefaultAsync(d => d.Name.ToLower() == newItem.Name.ToLower());
if (definition == null)
{
definition = new BudgetItemDefinition
{
Name = newItem.Name,
IsExpense = newItem.IsExpense,
IncludeInSummary = newItem.IncludeInSummary
};
_context.BudgetItemDefinitions.Add(definition);
await _context.SaveChangesAsync();
}
newItem.BudgetItemDefinitionId = definition.Id;
}
_context.BudgetItems.Add(newItem);
await _context.SaveChangesAsync();
return Ok(new { id = newItem.Id });
}
[HttpDelete("item/{id}")]
public async Task<IActionResult> DeleteItem(int id)
{
var item = await _context.BudgetItems.FindAsync(id);
if (item == null) return NotFound();
_context.BudgetItems.Remove(item);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpDelete("{year:int}/{month:int}")]
public async Task<IActionResult> DeleteMonth(int year, int month)
{
var period = await _context.BudgetPeriods
.Include(p => p.Categories)
.ThenInclude(c => c.Items)
.FirstOrDefaultAsync(p => p.Year == year && p.Month == month);
if (period == null)
return NotFound();
// Ta bort alla items → kategorier → period
foreach (var category in period.Categories)
{
_context.BudgetItems.RemoveRange(category.Items);
}
_context.BudgetCategories.RemoveRange(period.Categories);
_context.BudgetPeriods.Remove(period);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpPost("category")]
public async Task<IActionResult> CreateCategory([FromBody] BudgetCategoryDto newCategoryDto)
{
if (newCategoryDto == null || string.IsNullOrWhiteSpace(newCategoryDto.Name))
return BadRequest("Ogiltig data.");
var period = await _context.BudgetPeriods
.FirstOrDefaultAsync(p => p.Year == newCategoryDto.Year && p.Month == newCategoryDto.Month);
if (period == null)
{
period = new BudgetPeriod
{
Year = newCategoryDto.Year,
Month = newCategoryDto.Month
};
_context.BudgetPeriods.Add(period);
await _context.SaveChangesAsync();
}
var definition = await _context.BudgetCategoryDefinitions
.FirstOrDefaultAsync(d => d.Name.ToLower() == newCategoryDto.Name.ToLower());
if (definition == null)
{
definition = new BudgetCategoryDefinition
{
Name = newCategoryDto.Name,
Color = newCategoryDto.Color ?? "#666666"
};
_context.BudgetCategoryDefinitions.Add(definition);
await _context.SaveChangesAsync();
}
var category = new BudgetCategory
{
Name = newCategoryDto.Name,
Color = newCategoryDto.Color ?? definition.Color,
BudgetPeriodId = period.Id,
Order = newCategoryDto.Order,
BudgetCategoryDefinitionId = definition.Id
};
_context.BudgetCategories.Add(category);
await _context.SaveChangesAsync();
return Ok(new { id = category.Id });
}
[HttpDelete("category/{id}")]
public async Task<IActionResult> DeleteCategory(int id)
{
var category = await _context.BudgetCategories
.Include(c => c.Items)
.FirstOrDefaultAsync(c => c.Id == id);
if (category == null)
return NotFound();
_context.BudgetItems.RemoveRange(category.Items);
_context.BudgetCategories.Remove(category);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpPost("copy/{year:int}/{month:int}")]
public async Task<IActionResult> CopyFromPreviousMonth(int year, int month)
{
var targetPeriod = await _context.BudgetPeriods
.Include(p => p.Categories)
.ThenInclude(c => c.Items)
.FirstOrDefaultAsync(p => p.Year == year && p.Month == month);
if (targetPeriod != null && targetPeriod.Categories.Any())
return BadRequest("Det finns redan data för denna månad.");
var previous = new DateTime(year, month, 1).AddMonths(-1);
var previousPeriod = await _context.BudgetPeriods
.Include(p => p.Categories)
.ThenInclude(c => c.Items)
.FirstOrDefaultAsync(p => p.Year == previous.Year && p.Month == previous.Month);
if (previousPeriod == null)
return NotFound("Ingen data att kopiera från.");
var newPeriod = new BudgetPeriod
{
Year = year,
Month = month,
Categories = previousPeriod.Categories.Select(cat => new BudgetCategory
{
Name = cat.Name,
Color = cat.Color,
Order = cat.Order,
BudgetCategoryDefinitionId = cat.BudgetCategoryDefinitionId, // 🟢 Lägg till denna rad
Items = cat.Items.Select(item => new BudgetItem
{
Name = item.Name,
Amount = item.Amount,
IsExpense = item.IsExpense,
IncludeInSummary = item.IncludeInSummary,
Order = item.Order,
BudgetItemDefinitionId = item.BudgetItemDefinitionId // 🟢 Lägg till denna rad
}).ToList()
}).ToList()
};
_context.BudgetPeriods.Add(newPeriod);
await _context.SaveChangesAsync();
return Ok();
}
}
}