using Koogle.Domain.Entities; using Koogle.Domain.Enums; using Koogle.Domain.Interfaces; using KoogleApp.Data; using Microsoft.EntityFrameworkCore; namespace Koogle.Infrastructure.Repositories; /// /// Repository implementation for managing cash book entry entities. /// /// The database context factory for creating scoped contexts. public class CashBookEntryRepository(IDbContextFactory contextFactory) : ICashBookEntryRepository { /// public async Task> GetByClubIdAsync(Guid clubId, DateTime? from, DateTime? to, CancellationToken ct = default) { await using var context = await contextFactory.CreateDbContextAsync(ct); IQueryable query = context.CashBookEntries .Where(e => e.ClubId == clubId && !e.IsDeleted); if (from.HasValue) { query = query.Where(e => e.BookingDate >= from.Value); } if (to.HasValue) { query = query.Where(e => e.BookingDate <= to.Value); } return await query .Include(e => e.Category) .Include(e => e.Person) .Include(e => e.Day) .OrderByDescending(e => e.BookingDate) .ThenByDescending(e => e.CreatedAt) .ToListAsync(ct); } /// public async Task GetByIdAsync(Guid id, CancellationToken ct = default) { await using var context = await contextFactory.CreateDbContextAsync(ct); return await context.CashBookEntries .Include(e => e.Category) .Include(e => e.Person) .Include(e => e.Day) .FirstOrDefaultAsync(e => e.Id == id && !e.IsDeleted, ct); } /// public async Task GetBalanceAsync(Guid clubId, DateTime? asOfDate = null, CancellationToken ct = default) { await using var context = await contextFactory.CreateDbContextAsync(ct); // Get initial balance from club var club = await context.Clubs.FirstOrDefaultAsync(c => c.Id == clubId, ct); var initialBalance = club?.InitialBalance ?? 0m; var query = context.CashBookEntries .Where(e => e.ClubId == clubId && !e.IsDeleted); if (asOfDate.HasValue) { query = query.Where(e => e.BookingDate <= asOfDate.Value); } var entries = await query.ToListAsync(ct); var income = entries .Where(e => e.EntryType == CashBookEntryType.Income) .Sum(e => e.Amount); var expense = entries .Where(e => e.EntryType == CashBookEntryType.Expense) .Sum(e => e.Amount); return initialBalance + income - expense; } /// public async Task AddAsync(CashBookEntry entity, CancellationToken ct = default) { await using var context = await contextFactory.CreateDbContextAsync(ct); entity.Id = Guid.NewGuid(); entity.CreatedAt = DateTime.UtcNow; await context.CashBookEntries.AddAsync(entity, ct); await context.SaveChangesAsync(ct); return entity; } /// public async Task> GetByDayIdAsync(Guid dayId, CancellationToken ct = default) { await using var context = await contextFactory.CreateDbContextAsync(ct); return await context.CashBookEntries .Where(e => e.DayId == dayId && !e.IsDeleted) .Include(e => e.Category) .Include(e => e.Person) .OrderByDescending(e => e.CreatedAt) .ToListAsync(ct); } /// public async Task ExistsMembershipFeeForMonthAsync(Guid clubId, int year, int month, CancellationToken ct = default) { await using var context = await contextFactory.CreateDbContextAsync(ct); // Get the system category for membership fees var membershipCategory = await context.BookingCategories .FirstOrDefaultAsync(c => c.ClubId == clubId && c.IsSystemCategory && c.Name == "Mitgliedsbeitrag" && !c.IsDeleted, ct); if (membershipCategory == null) { return false; } var startOfMonth = new DateTime(year, month, 1); var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1); return await context.CashBookEntries .AnyAsync(e => e.ClubId == clubId && e.CategoryId == membershipCategory.Id && e.BookingDate >= startOfMonth && e.BookingDate <= endOfMonth && !e.IsDeleted, ct); } }