diff --git a/Aberwyn/Controllers/BudgetApiController.cs b/Aberwyn/Controllers/BudgetApiController.cs index 4897967..d8c14bf 100644 --- a/Aberwyn/Controllers/BudgetApiController.cs +++ b/Aberwyn/Controllers/BudgetApiController.cs @@ -672,7 +672,7 @@ string targetName, await CopyBudgetAsync(targetPeriod, sourcePeriod); return Ok(new { id = targetPeriod.Id }); } - private async Task FindSourcePeriod( + private async Task FindSourcePeriod( string? from, int? fromYear, int? fromMonth, @@ -707,7 +707,205 @@ string targetName, return sourcePeriod; } + [HttpGet("metadata")] + public async Task GetMetadata([FromQuery] int? year, [FromQuery] int? month) + { + var categoriesQuery = _context.BudgetCategories + .Include(c => c.BudgetPeriod) + .Include(c => c.Items) + .ThenInclude(i => i.BudgetItemDefinition) + .AsQueryable(); + + if (year.HasValue) + categoriesQuery = categoriesQuery.Where(c => c.BudgetPeriod.Year == year.Value); + if (month.HasValue) + categoriesQuery = categoriesQuery.Where(c => c.BudgetPeriod.Month == month.Value); + + var categories = await categoriesQuery.ToListAsync(); + + var categoryDefs = categories + .Where(c => c.BudgetCategoryDefinitionId.HasValue) + .GroupBy(c => c.Definition?.Name ?? c.Name) + .Select(g => new { + Name = g.Key, + Color = g.First().Color ?? "#cccccc" // direkt från kategorin + }) + .ToList(); + + var categoryLabels = categories + .GroupBy(c => c.Name) + .Select(g => new { + Name = g.Key, + Color = g.First().Color ?? "#cccccc" + }) + .ToList(); + + + var itemDefs = categories + .SelectMany(c => c.Items) + .Select(i => i.BudgetItemDefinition?.Name ?? i.Name) + .Distinct() + .OrderBy(n => n) + .ToList(); + + var itemLabels = categories + .SelectMany(c => c.Items) + .Select(i => i.Name) + .Distinct() + .OrderBy(n => n) + .ToList(); + + Console.WriteLine($"Metadata: {categories.Count} categories, {itemLabels.Count} items"); + + return Ok(new + { + CategoryDefinitions = categoryDefs, + CategoryLabels = categoryLabels, + ItemDefinitions = itemDefs, + ItemLabels = itemLabels + }); + } + + + [HttpGet("report/spreadsheet")] + public async Task GetBudgetReport( + [FromQuery] int? year = null, + [FromQuery] List? itemDefinitionIds = null, + [FromQuery] List? categoryDefinitionIds = null, + [FromQuery] string? itemLabel = null, + [FromQuery] string? categoryLabel = null, + [FromQuery] bool includeCategoryDefinitions = false, + [FromQuery] bool includeCategoryLabels = false, + [FromQuery] bool includeItemDefinitions = true, + [FromQuery] bool includeItemLabels = false) + { + try + { + // ✅ Ladda navigationsproperties för definitions + var query = _context.BudgetPeriods + .Include(p => p.Categories) + .ThenInclude(c => c.Definition) // CategoryDefinition + .Include(p => p.Categories) + .ThenInclude(c => c.Items) + .ThenInclude(i => i.BudgetItemDefinition) // ItemDefinition + .Where(p => p.Year.HasValue && p.Month.HasValue) + .AsQueryable(); + + if (year.HasValue) + query = query.Where(p => p.Year == year); + + var periods = await query + .OrderByDescending(p => p.Year) + .ThenByDescending(p => p.Month) + .ToListAsync(); + + var reportData = periods.Select(p => + { + var filteredCategories = p.Categories + .Where(c => + (categoryDefinitionIds == null || categoryDefinitionIds.Count == 0 || + (c.BudgetCategoryDefinitionId.HasValue && + categoryDefinitionIds.Contains(c.BudgetCategoryDefinitionId.Value))) && + (string.IsNullOrEmpty(categoryLabel) || + (c.Name ?? string.Empty).Contains(categoryLabel, StringComparison.OrdinalIgnoreCase)) + ) + .ToList(); // Viktigt: gå över till LINQ to Objects + + var filteredItems = filteredCategories + .SelectMany(c => c.Items) + .Where(i => + i.IncludeInSummary && + (itemDefinitionIds == null || itemDefinitionIds.Count == 0 || + (i.BudgetItemDefinitionId.HasValue && + itemDefinitionIds.Contains(i.BudgetItemDefinitionId.Value))) && + (string.IsNullOrEmpty(itemLabel) || + (i.Name ?? string.Empty).Contains(itemLabel, StringComparison.OrdinalIgnoreCase)) + ) + .ToList(); + + // 🔹 Skapa kolumner + var itemDefColumns = new Dictionary(); + var itemLabelColumns = new Dictionary(); + var catDefColumns = new Dictionary(); + var catLabelColumns = new Dictionary(); + + if (includeItemDefinitions) + { + foreach (var g in filteredItems + .Where(i => i.BudgetItemDefinition != null) + .GroupBy(i => i.BudgetItemDefinition!.Name)) + { + if (!string.IsNullOrEmpty(g.Key)) + itemDefColumns[g.Key] = g.Sum(i => i.IsExpense ? -i.Amount : i.Amount); + } + } + + if (includeItemLabels) + { + foreach (var g in filteredItems + .Where(i => !string.IsNullOrEmpty(i.Name)) + .GroupBy(i => i.Name)) + { + itemLabelColumns[g.Key] = g.Sum(i => i.IsExpense ? -i.Amount : i.Amount); + } + } + + if (includeCategoryDefinitions) + { + foreach (var g in filteredCategories + .Where(c => c.Definition != null) + .GroupBy(c => c.Definition!.Name)) + { + if (!string.IsNullOrEmpty(g.Key)) + { + var total = g.SelectMany(c => c.Items) + .Where(i => i.IncludeInSummary) + .Sum(i => i.IsExpense ? -i.Amount : i.Amount); + catDefColumns[g.Key] = total; + } + } + } + + if (includeCategoryLabels) + { + foreach (var g in filteredCategories + .Where(c => !string.IsNullOrEmpty(c.Name)) + .GroupBy(c => c.Name)) + { + var total = g.SelectMany(c => c.Items) + .Where(i => i.IncludeInSummary) + .Sum(i => i.IsExpense ? -i.Amount : i.Amount); + catLabelColumns[g.Key] = total; + } + } + + return new + { + id = p.Id, + year = p.Year ?? 0, + month = p.Month ?? 0, + income = filteredItems.Where(i => !i.IsExpense).Sum(i => i.Amount), + expense = filteredItems.Where(i => i.IsExpense).Sum(i => i.Amount), + net = filteredItems.Sum(i => i.IsExpense ? -i.Amount : i.Amount), + itemDefinitions = itemDefColumns, + itemLabels = itemLabelColumns, + categoryDefinitions = catDefColumns, + categoryLabels = catLabelColumns + }; + }); + + return Ok(reportData); + } + catch (Exception ex) + { + Console.Error.WriteLine($"[GetBudgetReport] {ex}"); + return StatusCode(500, $"Ett fel uppstod i rapportgenereringen: {ex.Message}"); + } + } + + } + } diff --git a/Aberwyn/Controllers/BudgetController.cs b/Aberwyn/Controllers/BudgetController.cs index ca056a3..254a7cb 100644 --- a/Aberwyn/Controllers/BudgetController.cs +++ b/Aberwyn/Controllers/BudgetController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Authorization; +using Aberwyn.Models; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Aberwyn.Controllers @@ -34,5 +35,7 @@ namespace Aberwyn.Controllers var now = DateTime.Now; return RedirectToAction("Index", new { year = now.Year, month = now.Month }); } + + } } diff --git a/Aberwyn/Controllers/ReportController.cs b/Aberwyn/Controllers/ReportController.cs index e62f90c..6a1a72f 100644 --- a/Aberwyn/Controllers/ReportController.cs +++ b/Aberwyn/Controllers/ReportController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Authorization; +using Aberwyn.Models; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Aberwyn.Controllers @@ -6,9 +7,9 @@ namespace Aberwyn.Controllers [Authorize(Roles = "Budget")] public class ReportController : Controller { - public IActionResult BudgetReport() + public IActionResult Budget() { - return View("BudgetReport"); + return View(new BudgetReportViewModel()); } } } diff --git a/Aberwyn/Models/ReportModel.cs b/Aberwyn/Models/ReportModel.cs index 9b7f3ca..65f1ed8 100644 --- a/Aberwyn/Models/ReportModel.cs +++ b/Aberwyn/Models/ReportModel.cs @@ -1,25 +1,13 @@ namespace Aberwyn.Models { - public class BudgetReportRequestDto + public class BudgetReportViewModel { - public List DefinitionIds { get; set; } = new(); - public int StartYear { get; set; } - public int StartMonth { get; set; } - public int EndYear { get; set; } - public int EndMonth { get; set; } + public int? Year { get; set; } + public string GroupBy { get; set; } = "month"; + public string? ItemLabel { get; set; } + public string? CategoryLabel { get; set; } + public List ItemDefinitionIds { get; set; } = new(); + public List CategoryDefinitionIds { get; set; } = new(); } - public class BudgetReportResultDto - { - public int Year { get; set; } - public int Month { get; set; } - public List Definitions { get; set; } = new(); - } - - public class DefinitionSumDto - { - public int DefinitionId { get; set; } - public string DefinitionName { get; set; } - public decimal TotalAmount { get; set; } - } } diff --git a/Aberwyn/Views/Budget/Index.cshtml b/Aberwyn/Views/Budget/Index.cshtml index 063cf62..211f3dd 100644 --- a/Aberwyn/Views/Budget/Index.cshtml +++ b/Aberwyn/Views/Budget/Index.cshtml @@ -40,6 +40,8 @@ + + @@ -148,6 +150,10 @@ {{ item.name }} + {{ item.amount | number:0 }} diff --git a/Aberwyn/Views/Report/Budget.cshtml b/Aberwyn/Views/Report/Budget.cshtml new file mode 100644 index 0000000..2744ceb --- /dev/null +++ b/Aberwyn/Views/Report/Budget.cshtml @@ -0,0 +1,200 @@ + + + + + Budget Översikt + + + + + + +

Visa kolumner

+ +
+ +
+ Kategori-definitioner: + +
+ +
+
+ + +
+ Kategori-namn: + +
+ +
+
+ + +
+ Item-definitioner: + +
+ +
+
+ + +
+ Item-namn: + +
+ +
+
+ + +
+ Visa månader: + +
+ +
+
+
+
+ +
+ + + +
Laddar data…
+ + +
+
{{ yearData.year }}
+ + +
+
Månad
+
Inkomst
+
Utgift
+
Netto
+ +
+
+ {{ colName }} +
+
+ {{ colName }} +
+
+ {{ colName }} +
+
+ {{ colName }} +
+
+
+ + +
+
{{ month.year }} - {{ monthNames[month.month-1] }}
+
+ {{ month.income | number:0 }} +
+
+ {{ month.expense | number:0 }} +
+
+ {{ month.net | number:0 }} +
+ +
+
+ {{ month.categoryDefinitions[colName] || 0 | number:0 }} +
+
+ {{ month.categoryLabels[colName] || 0 | number:0 }} +
+
+ {{ month.itemDefinitions[colName] || 0 | number:0 }} +
+
+ {{ month.itemLabels[colName] || 0 | number:0 }} +
+
+
+ + +
+
Total
+
{{ getYearTotal(yearData.months, 'income') | number:0 }}
+
{{ getYearTotal(yearData.months, 'expense') | number:0 }}
+
{{ getYearTotal(yearData.months, 'net') | number:0 }}
+ +
+
+ {{ getYearTotal(yearData.months, 'categoryDefinitions', colName) | number:0 }} +
+
+ {{ getYearTotal(yearData.months, 'categoryLabels', colName) | number:0 }} +
+
+ {{ getYearTotal(yearData.months, 'itemDefinitions', colName) | number:0 }} +
+
+ {{ getYearTotal(yearData.months, 'itemLabels', colName) | number:0 }} +
+
+
+
+ + + + + + diff --git a/Aberwyn/Views/Report/BudgetReport.cshtml b/Aberwyn/Views/Report/BudgetReport.cshtml deleted file mode 100644 index 3a119e1..0000000 --- a/Aberwyn/Views/Report/BudgetReport.cshtml +++ /dev/null @@ -1,61 +0,0 @@ -@{ - ViewData["Title"] = "Budgetrapport"; -} - -
-

Budgetrapport

- -
-
- - - - - - -
- -
- -
- -
-
- - -
- -
-

Resultat

- - - - - - - - - - - - - - - - - - - - -
ÅrMånad{{ def.Name }}Totalt
{{ row.Year }}{{ monthName(row.Month) }} - {{ getAmount(row, def.Id) | number:0 }} - {{ getRowTotal(row) | number:0 }}
-
-
- - - - - diff --git a/Aberwyn/Views/Shared/_Layout.cshtml b/Aberwyn/Views/Shared/_Layout.cshtml index ed1e4bf..0e158ff 100644 --- a/Aberwyn/Views/Shared/_Layout.cshtml +++ b/Aberwyn/Views/Shared/_Layout.cshtml @@ -61,6 +61,19 @@ } + + @if (User.IsInRole("Budget")) + { + + } + @if (User.IsInRole("Chef")) {