feat: Enhance booking functionality with invoice management and dashboard summary

- 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.
This commit is contained in:
2026-10-01 03:46:03 +05:30
parent 6abef835e8
commit 51f0021e27
16 changed files with 501 additions and 45 deletions
+18 -8
View File
@@ -86,24 +86,34 @@ namespace Fitness_core_BE.Models
public string PaymentStatus { get; set; } = "NotRequired";
public decimal AmountCharged { get; set; } = 0m;
/// <summary>"Cash" | "Bank" -- set alongside PaymentStatus becoming "Paid" (MarkGuestBookingPaid
/// or the Complete checkout flow). Null until then.</summary>
[MaxLength(20)]
public string? PaymentMethod { get; set; }
/// <summary>Set the moment PaymentStatus becomes "Paid" -- distinct from BookedAt/CheckedOutAt,
/// this is specifically when the money was recorded, used to bucket booking revenue into
/// Profit &amp; Loss and to date-stamp the BookingInvoice snapshot.</summary>
public DateTime? PaidAt { get; set; }
// Set only once payment is confirmed for a pay-per-session guest booking, or
// immediately for a member session (no payment gate) -- see PaymentStatus.
public Guid? FKPaymentTransactionId { get; set; }
[ForeignKey(nameof(FKPaymentTransactionId))]
public PaymentTransaction? PaymentTransaction { get; set; }
/// <summary>"Confirmed" | "CheckedIn" | "Cancelled" | "NoShow" | "Completed". NoShow and
/// Completed are reached either by explicit staff action (MarkNoShow/Complete) or lazily,
/// swept by BookingService.SweepOverdueBookingsAsync at the top of every check-in call
/// (not a background job -- overdue bookings are reconciled the next time any check-in
/// happens for this client, across every facility).</summary>
/// <summary>"Confirmed" | "CheckedIn" | "Cancelled" | "NoShow" | "Completed". NoShow is
/// reached either by explicit staff action (MarkNoShow) or lazily, swept by
/// BookingService.SweepOverdueBookingsAsync at the top of every check-in or checkout call
/// (not a background job -- overdue Confirmed bookings are reconciled the next time any
/// check-in/checkout happens for this client, across every facility). Completed is
/// reached ONLY by the explicit Complete action -- a guest booking can't reach it while
/// still Unpaid, so there's no automatic path that would check someone out unpaid.</summary>
public string Status { get; set; } = "Confirmed";
public DateTime BookedAt { get; set; } = DateTime.UtcNow;
public DateTime? CheckedInAt { get; set; }
/// <summary>Set when the booking reaches Completed, whether via the manual Complete
/// action or the lazy overdue-sweep -- the actual moment it was reconciled, not
/// necessarily EndTime.</summary>
/// <summary>Set when the booking reaches Completed via the manual Complete action -- the
/// actual moment it was checked out, not necessarily EndTime.</summary>
public DateTime? CheckedOutAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
+44
View File
@@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Fitness_core_BE.Models
{
/// <summary>A snapshot receipt generated the moment a Booking's payment is recorded (either
/// via MarkGuestBookingPaid or the Complete checkout flow) -- deliberately NOT a live join to
/// Booking/Facility, so it stays stable and printable even if the booking is later reassigned,
/// rescheduled, or the facility renamed. Purpose-built for bookings and kept entirely separate
/// from CustomerInvoice, which is hardwired to the membership/subscription domain
/// (FKCustomerSubscriptionId is required there) and has no concept of a one-off booking
/// charge.</summary>
public class BookingInvoice
{
[Key]
public Guid BookingInvoiceId { get; set; }
public Guid FKBookingId { get; set; }
[ForeignKey(nameof(FKBookingId))]
public Booking? Booking { get; set; }
public Guid FKClientId { get; set; }
[ForeignKey(nameof(FKClientId))]
public Client Client { get; set; }
public decimal Amount { get; set; }
/// <summary>"Cash" | "Bank".</summary>
[MaxLength(20)]
public string PaymentMethod { get; set; } = "Cash";
[MaxLength(200)]
public string GuestName { get; set; } = string.Empty;
[MaxLength(32)]
public string? GuestPhone { get; set; }
[MaxLength(128)]
public string FacilityName { get; set; } = string.Empty;
public DateTime BookingStartTime { get; set; }
public DateTime BookingEndTime { get; set; }
public DateTime IssuedAt { get; set; } = DateTime.UtcNow;
}
}