using Fitness_core_BE.DTOs.Ledger; using Fitness_core_BE.Interfaces; using Fitness_core_BE.Models; using Microsoft.EntityFrameworkCore; namespace Fitness_core_BE.Repos { public class ProfitLossRepository : IProfitLossRepository { // Every method here is already scoped to a single caller-owned clientId (resolved // from the ambient tenant), so no cross-tenant fan-out is needed. PaymentTransactions // (member-payment rows), StaffPayments and LedgerEntries are tenant-bucket; // ClientInvoices (platform billing) is central. private readonly TenantDbContext _context; private readonly CentralDbContext _central; public ProfitLossRepository(TenantDbContext context, CentralDbContext central) { _context = context; _central = central; } public async Task> GetDerivedEntriesAsync( Guid clientId, DateTime fromUtc, DateTime toUtc, CancellationToken ct = default) { var memberPayments = await _context.PaymentTransactions .AsNoTracking() .Where(payment => payment.FK_ClientId == clientId && payment.FK_CustomerInvoiceId != null && payment.IsApproved == true && payment.IsActive == true && payment.PayDate != null && payment.PayDate >= fromUtc && payment.PayDate < toUtc) .Select(payment => new LedgerEntryDto { EntryType = "Income", Category = "Member Payments", Description = payment.PayNote, Amount = payment.PayAmount, EntryDate = payment.PayDate!.Value, IsManual = false }) .ToListAsync(ct); var platformInvoices = await _central.ClientInvoices .AsNoTracking() .Where(invoice => invoice.ClientSubscription.FKClientId == clientId && invoice.PaidAmount > 0 && invoice.ClientInvoiceDate >= fromUtc && invoice.ClientInvoiceDate < toUtc) .Select(invoice => new LedgerEntryDto { EntryType = "Expense", Category = "Platform Fees", Description = "HexaFitness platform invoice", Amount = invoice.PaidAmount, EntryDate = invoice.ClientInvoiceDate, IsManual = false }) .ToListAsync(ct); var staffPayments = await _context.StaffPayments .AsNoTracking() .Where(payment => payment.Staff.FKClientId == clientId && payment.PaidAt >= fromUtc && payment.PaidAt < toUtc) .Select(payment => new LedgerEntryDto { EntryType = "Expense", Category = "Staff Payroll", Description = payment.Note, Amount = payment.Amount, EntryDate = payment.PaidAt, IsManual = false }) .ToListAsync(ct); memberPayments.AddRange(platformInvoices); memberPayments.AddRange(staffPayments); return memberPayments; } public Task> GetManualEntriesAsync( Guid clientId, DateTime fromUtc, DateTime toUtc, CancellationToken ct = default) { return _context.LedgerEntries .AsNoTracking() .Where(entry => entry.FKClientId == clientId && entry.EntryDate >= fromUtc && entry.EntryDate < toUtc) .ToListAsync(ct); } public Task GetManualEntryByIdAsync( Guid id, CancellationToken ct = default) { return _context.LedgerEntries.FirstOrDefaultAsync(entry => entry.Id == id, ct); } public async Task AddManualEntryAsync( LedgerEntry entry, CancellationToken ct = default) { await _context.LedgerEntries.AddAsync(entry, ct); } public void RemoveManualEntry(LedgerEntry entry) { _context.LedgerEntries.Remove(entry); } } }