using BudgetApp.PublicModels; using BudgetApp.Storage; using Microsoft.EntityFrameworkCore; namespace BudgetApp.Services { public class TransactionService(BudgetContext context) : ITransactionService { private readonly BudgetContext _context = context ?? throw new ArgumentNullException(nameof(context)); public async Task> GetTransactions(CancellationToken cancellationToken = default) { var response = new ListResponse(); var transactions = await _context.Transactions .AsNoTracking() .OrderBy(t => t.Date) .ToListAsync(cancellationToken) .ConfigureAwait(false); var years = transactions .GroupBy(t => t.Date.Year) .OrderBy(g => g.Key) .Select(yearGroup => { var months = yearGroup .GroupBy(t => t.Date.Month) .OrderBy(monthGroup => monthGroup.Key) .Select(monthGroup => { var days = monthGroup .GroupBy(t => t.Date.Day) .OrderBy(dayGroup => dayGroup.Key) .Select(dayGroup => { var dayTransactions = dayGroup .OrderBy(t => t.Date) .ThenBy(t => t.Created) .Select(MapTransaction) .ToList(); var dayBalance = dayTransactions.Sum(t => t.Amount); return new DaySummaryDto { Date = dayTransactions.First().Date, Balance = dayBalance, Transactions = dayTransactions }; }) .ToList(); var monthBalance = days.Sum(d => d.Balance); return new MonthSummaryDto { Month = monthGroup.Key, Balance = monthBalance, Days = days }; }) .ToList(); var yearBalance = months.Sum(m => m.Balance); return new YearSummaryDto { Year = yearGroup.Key, Balance = yearBalance, Months = months }; }) .ToList(); response.Data = years; response.Total = transactions.Count; return response; } private static TransactionDto MapTransaction(Transaction source) { return new TransactionDto { Id = source.Id, Date = source.Date, Amount = source.Amount, Balance = source.Balance, OperationDescription = source.OperationDescription, Note = source.Note, Category = source.Category, TransactionId = source.TransactionId, Created = source.Created, Updated = source.Updated, Deleted = source.Deleted }; } } public interface ITransactionService { Task> GetTransactions(CancellationToken cancellationToken = default); } }