KoogleApp/src/Koogle.Infrastructure/Repositories/CashBookEntryRepository.cs

130 lines
4.6 KiB
C#

using Koogle.Domain.Entities;
using Koogle.Domain.Enums;
using Koogle.Domain.Interfaces;
using KoogleApp.Data;
using Microsoft.EntityFrameworkCore;
namespace Koogle.Infrastructure.Repositories;
/// <summary>
/// Repository implementation for managing cash book entry entities.
/// </summary>
/// <param name="contextFactory">The database context factory for creating scoped contexts.</param>
public class CashBookEntryRepository(IDbContextFactory<AppDbContext> contextFactory) : ICashBookEntryRepository
{
/// <inheritdoc />
public async Task<List<CashBookEntry>> GetByClubIdAsync(Guid clubId, DateTime? from, DateTime? to, CancellationToken ct = default)
{
await using var context = await contextFactory.CreateDbContextAsync(ct);
IQueryable<CashBookEntry> 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);
}
/// <inheritdoc />
public async Task<CashBookEntry?> 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);
}
/// <inheritdoc />
public async Task<decimal> 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;
}
/// <inheritdoc />
public async Task<CashBookEntry> 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;
}
/// <inheritdoc />
public async Task<List<CashBookEntry>> 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);
}
/// <inheritdoc />
public async Task<bool> 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);
}
}