using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Donations.ValueObject;
using Domain.Entities.Members;
namespace Domain.Entities.Donations;
public class WithdrawalRequest
{
[ForeignKey(nameof(ChannelID))]
public virtual Channel? Channel { get; private set; }
[ForeignKey(nameof(MemberID))]
public virtual Member? Member { get; private set; }
[Key]
public int ID { get; private set; }
public int ChannelID { get; private set; }
public int MemberID { get; private set; }
public int RequestedAmount { get; private set; }
///
/// 후원 수익(Donation) 잔액에서 차감되는 금액. RequestedAmount = DonationAmount + StoreRevenueAmount.
/// 정산서 명세에 분리 표시 (출처 보존).
///
public int DonationAmount { get; private set; }
///
/// 상품 판매 수익(StoreRevenue) 잔액에서 차감되는 금액. RequestedAmount = DonationAmount + StoreRevenueAmount.
/// 정산서 명세에 분리 표시 (출처 보존).
///
public int StoreRevenueAmount { get; private set; }
public int IncomeTax { get; private set; }
public int LocalTax { get; private set; }
public int WithholdingTax { get; private set; }
public int NetAmount { get; private set; }
public WithdrawalStatus Status { get; private set; } = WithdrawalStatus.Pending;
public string BankCode { get; private set; } = default!;
public string BankName { get; private set; } = default!;
public string AccountNumber { get; private set; } = default!;
public string AccountHolder { get; private set; } = default!;
public DateTime RequestedAt { get; private set; } = DateTime.UtcNow;
public DateTime? ProcessedAt { get; private set; }
public string? RejectedReason { get; private set; }
public string? AdminMemo { get; private set; }
private WithdrawalRequest() { }
///
/// 기존(후원 단일) 출금 요청 생성. RequestedAmount 전액을 DonationAmount로 분배.
/// 신규 코드(상점 수익 분개)는 분리 시그니처 Create(channelID, memberID, donationAmount, storeRevenueAmount, ...) 사용.
///
public static WithdrawalRequest Create(
int channelID,
int memberID,
int requestedAmount,
string bankCode,
string bankName,
string accountNumber,
string accountHolder
) => Create(
channelID: channelID,
memberID: memberID,
donationAmount: requestedAmount,
storeRevenueAmount: 0,
bankCode: bankCode,
bankName: bankName,
accountNumber: accountNumber,
accountHolder: accountHolder
);
///
/// 후원/상점 분개 출금 요청 생성. 정산서 명세에 두 잔액 출처를 분리 표시.
///
public static WithdrawalRequest Create(
int channelID,
int memberID,
int donationAmount,
int storeRevenueAmount,
string bankCode,
string bankName,
string accountNumber,
string accountHolder
) {
if (donationAmount < 0)
{
throw new ArgumentOutOfRangeException(nameof(donationAmount), "Donation amount must be non-negative.");
}
if (storeRevenueAmount < 0)
{
throw new ArgumentOutOfRangeException(nameof(storeRevenueAmount), "StoreRevenue amount must be non-negative.");
}
var requestedAmount = donationAmount + storeRevenueAmount;
if (requestedAmount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(requestedAmount), "Total withdrawal amount must be positive.");
}
var incomeTax = (int)Math.Floor(requestedAmount * 3m / 100m);
var localTax = (int)Math.Floor(requestedAmount * 3m / 1000m);
var withholdingTax = incomeTax + localTax;
return new WithdrawalRequest
{
ChannelID = channelID,
MemberID = memberID,
RequestedAmount = requestedAmount,
DonationAmount = donationAmount,
StoreRevenueAmount = storeRevenueAmount,
IncomeTax = incomeTax,
LocalTax = localTax,
WithholdingTax = withholdingTax,
NetAmount = requestedAmount - withholdingTax,
BankCode = bankCode,
BankName = bankName,
AccountNumber = accountNumber,
AccountHolder = accountHolder
};
}
public void MarkProcessing(string? adminMemo = null)
{
Status = WithdrawalStatus.Processing;
AdminMemo = adminMemo;
}
public void Complete(string? adminMemo = null)
{
Status = WithdrawalStatus.Completed;
ProcessedAt = DateTime.UtcNow;
AdminMemo = adminMemo;
}
public void Reject(string reason, string? adminMemo = null)
{
Status = WithdrawalStatus.Rejected;
ProcessedAt = DateTime.UtcNow;
RejectedReason = reason;
AdminMemo = adminMemo;
}
}