Files
baya-monorepo/server/src/Infrastructure/Baya.Infrastructure.Persistence/Configuration/RefundsConfig/RefundConfig.cs
T
2026-07-13 17:03:45 +03:30

54 lines
2.8 KiB
C#

using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Messaging;
using Baya.Domain.Entities.Payments;
using Baya.Domain.Entities.Refunds;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Baya.Infrastructure.Persistence.Configuration.RefundsConfig;
/// <summary>
/// <c>refunds</c> — 1:N per <c>payment_transaction</c>. The <c>amount = fee_leg + payout_leg</c> reconciliation
/// is a DB CHECK (the "Σ refunded ≤ captured" invariant is a handler check under the booking refund lock, not a
/// single-row constraint). <c>ticket_id</c> is a nullable FK to <c>messaging.Tickets</c> (b15 shipped the table;
/// refinement-phase-6 added the constraint, <c>ON DELETE NO ACTION</c>).
/// </summary>
internal sealed class RefundConfig : IEntityTypeConfiguration<Refund>
{
public void Configure(EntityTypeBuilder<Refund> builder)
{
builder.ToTable("Refunds", "payments", t => t.HasCheckConstraint(
"CK_Refunds_LegSplit",
"[Amount] = [PlatformFeeRefundedIrr] + [NursePayoutRefundedIrr] " +
"AND [Amount] >= 0 AND [PlatformFeeRefundedIrr] >= 0 AND [NursePayoutRefundedIrr] >= 0"));
builder.Property(r => r.RefundChannel).HasMaxLength(20).IsRequired();
builder.Property(r => r.Status).HasMaxLength(20).IsRequired();
builder.Property(r => r.ReasonCategory).HasMaxLength(50);
builder.Property(r => r.ReasonNotes).HasMaxLength(1000);
builder.Property(r => r.AdminNotes).HasMaxLength(1000);
builder.Property(r => r.RejectedReason).HasMaxLength(500);
builder.Property(r => r.GatewayRefundReference).HasMaxLength(200);
builder.Property(r => r.ExternalRevertReference).HasMaxLength(200);
builder.Property(r => r.CancellationPolicyCode).HasMaxLength(50);
builder.Property(r => r.RefundPercentage).HasPrecision(6, 4);
builder.Property(r => r.RefundPercentageApplied).HasPrecision(5, 2);
builder.HasIndex(r => r.PaymentTransactionId);
builder.HasIndex(r => r.BookingId);
builder.HasIndex(r => r.RequestedByCustomerId);
builder.HasIndex(r => r.Status);
builder.HasIndex(r => r.TicketId);
builder.HasOne<PaymentTransaction>().WithMany().HasForeignKey(r => r.PaymentTransactionId).IsRequired();
builder.HasOne<Booking>().WithMany().HasForeignKey(r => r.BookingId).IsRequired();
builder.HasOne<CustomerProfile>().WithMany().HasForeignKey(r => r.RequestedByCustomerId).IsRequired();
// Forward-dep FK to the b15 tickets table (refinement-phase-6). Optional (nullable); NO ACTION so deleting
// a ticket never cascades into money rows.
builder.HasOne<Ticket>().WithMany().HasForeignKey(r => r.TicketId).OnDelete(DeleteBehavior.NoAction);
builder.HasQueryFilter(r => r.DeletedAt == null);
}
}