51f0021e27
- Added customerId to guest booking requests for better identification. - Implemented new endpoints for managing booking invoices and dashboard summaries. - Created BookingInvoice and BookingDashboardSummary DTOs to support new features. - Updated Booking and BookingInvoice models to include payment method and timestamps. - Enhanced BookingService to handle invoice creation upon payment confirmation. - Modified BookingRepository to support CRUD operations for BookingInvoice. - Updated TenantDbContext to include BookingInvoice DbSet and relevant configurations. - Improved error handling and validation for booking completion and payment processes.
149 lines
5.7 KiB
C#
149 lines
5.7 KiB
C#
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<List<LedgerEntryDto>> 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);
|
|
|
|
// Paid facility bookings (guest/walk-in) -- a plain Booking read, not a
|
|
// PaymentTransaction, since Booking has no invoice at all (see BookingInvoice, kept
|
|
// deliberately separate from CustomerInvoice/PaymentTransaction's membership-only
|
|
// wiring). PaidAt is the recognition date, not BookedAt/CheckedOutAt.
|
|
var bookingPayments = await _context.Bookings
|
|
.AsNoTracking()
|
|
.Where(booking =>
|
|
booking.FKClientId == clientId &&
|
|
booking.PaymentStatus == "Paid" &&
|
|
booking.PaidAt != null &&
|
|
booking.PaidAt >= fromUtc &&
|
|
booking.PaidAt < toUtc)
|
|
.Select(booking => new LedgerEntryDto
|
|
{
|
|
EntryType = "Income",
|
|
Category = "Facility Bookings",
|
|
Description = booking.GuestName,
|
|
Amount = booking.AmountCharged,
|
|
EntryDate = booking.PaidAt!.Value,
|
|
IsManual = false
|
|
})
|
|
.ToListAsync(ct);
|
|
|
|
memberPayments.AddRange(platformInvoices);
|
|
memberPayments.AddRange(staffPayments);
|
|
memberPayments.AddRange(bookingPayments);
|
|
return memberPayments;
|
|
}
|
|
|
|
public Task<List<LedgerEntry>> 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<LedgerEntry?> 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);
|
|
}
|
|
}
|
|
}
|