using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Shared.Abstractions.Entities;
namespace TakeoutSaaS.Domain.Tenants.Entities;
///
/// 租户账单,用于呈现周期性收费。
///
public sealed class TenantBillingStatement : MultiTenantEntityBase
{
///
/// 账单编号,供对账查询。
///
public string StatementNo { get; set; } = string.Empty;
///
/// 账单类型(订阅账单/配额包账单/手动账单/续费账单)。
///
public BillingType BillingType { get; set; } = BillingType.Subscription;
///
/// 关联的订阅 ID(仅当 BillingType 为 Subscription 或 Renewal 时有值)。
///
public long? SubscriptionId { get; set; }
///
/// 账单周期开始时间。
///
public DateTime PeriodStart { get; set; }
///
/// 账单周期结束时间。
///
public DateTime PeriodEnd { get; set; }
///
/// 应付金额(原始金额)。
///
public decimal AmountDue { get; set; }
///
/// 折扣金额。
///
public decimal DiscountAmount { get; set; }
///
/// 税费金额。
///
public decimal TaxAmount { get; set; }
///
/// 实付金额。
///
public decimal AmountPaid { get; set; }
///
/// 货币类型(默认 CNY)。
///
public string Currency { get; set; } = "CNY";
///
/// 当前付款状态。
///
public TenantBillingStatus Status { get; set; } = TenantBillingStatus.Pending;
///
/// 到期日。
///
public DateTime DueDate { get; set; }
///
/// 提醒发送时间(续费提醒、逾期提醒等)。
///
public DateTime? ReminderSentAt { get; set; }
///
/// 逾期通知时间。
///
public DateTime? OverdueNotifiedAt { get; set; }
///
/// 账单明细 JSON,记录各项费用。
///
public string? LineItemsJson { get; set; }
///
/// 备注信息(如:人工备注、取消原因等)。
///
public string? Notes { get; set; }
///
/// 计算总金额(应付金额 - 折扣 + 税费)。
///
/// 总金额。
public decimal CalculateTotalAmount()
{
return AmountDue - DiscountAmount + TaxAmount;
}
///
/// 标记为已支付(直接结清)。
///
public void MarkAsPaid()
{
// 1. 计算剩余应付金额
var remainingAmount = CalculateTotalAmount() - AmountPaid;
// 2. 若已结清则直接返回
if (remainingAmount <= 0)
{
Status = TenantBillingStatus.Paid;
return;
}
// 3. 补足剩余金额并标记为已支付
MarkAsPaid(remainingAmount, string.Empty);
}
///
/// 标记为已支付。
///
/// 支付金额。
/// 交易号。
public void MarkAsPaid(decimal amount, string transactionNo)
{
if (Status == TenantBillingStatus.Paid)
{
throw new InvalidOperationException("账单已经处于已支付状态,不能重复标记。");
}
if (Status == TenantBillingStatus.Cancelled)
{
throw new InvalidOperationException("已取消的账单不能标记为已支付。");
}
// 1. 累加支付金额
AmountPaid += amount;
// 2. 如果实付金额大于等于应付总额,则标记为已支付
if (AmountPaid >= CalculateTotalAmount())
{
Status = TenantBillingStatus.Paid;
}
}
///
/// 标记为逾期。
///
public void MarkAsOverdue()
{
// 1. 仅待支付账单允许标记逾期
if (Status != TenantBillingStatus.Pending)
{
return;
}
// 2. 未超过到期日则不处理
if (DateTime.UtcNow <= DueDate)
{
return;
}
// 3. 标记为逾期(通知时间由外部流程在发送通知时写入)
Status = TenantBillingStatus.Overdue;
}
///
/// 取消账单。
///
public void Cancel()
{
Cancel(null);
}
///
/// 取消账单。
///
/// 取消原因。
public void Cancel(string? reason)
{
if (Status == TenantBillingStatus.Paid)
{
throw new InvalidOperationException("已支付的账单不能取消。");
}
if (Status == TenantBillingStatus.Cancelled)
{
throw new InvalidOperationException("账单已经处于取消状态。");
}
// 1. 变更状态
Status = TenantBillingStatus.Cancelled;
// 2. 记录取消原因(可选)
if (!string.IsNullOrWhiteSpace(reason))
{
Notes = string.IsNullOrWhiteSpace(Notes) ? $"[取消原因] {reason}" : $"{Notes}\n[取消原因] {reason}";
}
}
}