using General_Ledger_Service.DTOs.Reports; using General_Ledger_Service.Infra; using General_Ledger_Service.Interfaces.Repositories; using General_Ledger_Service.Models; using Microsoft.EntityFrameworkCore; namespace General_Ledger_Service.Repositories { public class BudgetRepository(AppDBContext context) : IBudgetRepository { private readonly AppDBContext _context = context; public Task GetByIdAsync(long budgetId) => RepositoryHelpers.GetByIdAsync(_context.Budgets, budgetId); public Task AddAsync(Budget budget) => RepositoryHelpers.AddAsync(_context.Budgets, budget); public async Task AddLinesAsync(IEnumerable lines) => await _context.BudgetLines.AddRangeAsync(lines); public async Task> GetAllAsync() => await _context.Budgets.OrderBy(b => b.Name).ToListAsync(); public async Task> GetBudgetVsActualAsync(long budgetId) { var budgetLines = await _context.BudgetLines .Where(bl => bl.BudgetId == budgetId) .OrderBy(bl => bl.Account.AccountCode) .Select(bl => new { bl.BudgetLineId, bl.AccountId, bl.Account.AccountCode, bl.Account.AccountName, bl.PeriodId, bl.BudgetedAmount }) .ToListAsync(); var accountIds = budgetLines.Select(bl => bl.AccountId).Distinct().ToList(); var periodIds = budgetLines.Select(bl => bl.PeriodId).Distinct().ToList(); // Actual = posted base debit - credit per (account, period), summed in the database. var actuals = await _context.JournalEntryLines .Where(l => l.Journal.IsPosted && accountIds.Contains(l.AccountId) && periodIds.Contains(l.Journal.PeriodId)) .GroupBy(l => new { l.AccountId, l.Journal.PeriodId }) .Select(g => new { g.Key.AccountId, g.Key.PeriodId, Amount = g.Sum(l => l.BaseDebit - l.BaseCredit) }) .ToListAsync(); var actualByAccountAndPeriod = actuals.ToDictionary(a => (a.AccountId, a.PeriodId), a => a.Amount); return [.. budgetLines.Select(bl => { actualByAccountAndPeriod.TryGetValue((bl.AccountId, bl.PeriodId), out var actual); return new BudgetVsActualRowDto { BudgetLineId = bl.BudgetLineId, AccountCode = bl.AccountCode, AccountName = bl.AccountName, PeriodId = bl.PeriodId, BudgetedAmount = bl.BudgetedAmount, ActualAmount = actual, Variance = bl.BudgetedAmount - actual }; })]; } } }