XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

STHApplication

【MAUI】寄当家——安卓端交易管理系统

公开
关注 0 Fork 0 Star 0
UTF-8
namespace JiDangJia.Core;

public record FeeBreakdown(decimal Interest,decimal Storage,decimal Service,decimal Penalty=0)
{
    public decimal Total => Fees.Round(Interest+Storage+Service+Penalty);
}
public static class Fees
{
    public static decimal Round(decimal n) => decimal.Round(n,2,MidpointRounding.AwayFromZero);
    // 原版按 30 天折算月费;固定利息为整期金额,固定保管费按期计费。
    public static FeeBreakdown Period(DbRow ticket,int days,int unitDays=0)
    {
        if(days<=0) throw new ArgumentException("周期必须大于零");
        decimal p=ticket.Money("principal"), months=days/30m;
        var interest=ticket.Money("fixedInterest")>0?ticket.Money("fixedInterest"):p*ticket.Money("monthlyRate")/100*months;
        var storage=ticket.Money("storageFixedFee")>0?ticket.Money("storageFixedFee")*days/(unitDays>0?unitDays:days):p*ticket.Money("storageFeeRate")/100*months;
        return new(Round(interest),Round(storage),Round(p*ticket.Money("feeRate")/100*months));
    }
    public static int CurrentPeriod(DbRow ticket,IReadOnlyList<DbRow> renews)
    {
        var last=renews.OrderBy(r=>r.Text("createdAt")).LastOrDefault();
        return Math.Max(1,last is null?(ticket.Date("dueDate")-ticket.Date("pawnDate")).Days:(last.Date("newDueDate")-last.Date("oldDueDate")).Days);
    }
    public static FeeBreakdown Redeem(DbRow ticket,DateTime date,decimal penaltyRate,int periodDays)
    {
        if(date.Date<ticket.Date("pawnDate").Date) throw new ArgumentException("赎当日期不能早于开当日期");
        var fee=ticket.Text("feeChargeMode")!="POSTPAID"?new FeeBreakdown(0,0,0):Period(ticket,periodDays,periodDays);
        int overdue=Math.Max(0,(date.Date-ticket.Date("dueDate").Date).Days);
        return fee with { Penalty=Round(ticket.Money("principal")*penaltyRate/100*overdue) };
    }
    public static decimal DeadCost(DbRow ticket,DateTime date,IReadOnlyList<DbRow> renews)
    {
        int days=Math.Max(5,(date.Date-ticket.Date("pawnDate").Date).Days),originalDays=Math.Max(1,(ticket.Date("dueDate")-ticket.Date("pawnDate")).Days);
        if(renews.Count>0) originalDays=Math.Max(1,(renews.OrderBy(r=>r.Text("createdAt")).First().Date("oldDueDate")-ticket.Date("pawnDate")).Days);
        decimal collected=ticket.Text("feeChargeMode")!="POSTPAID"?Period(ticket,originalDays,originalDays).Total:0;
        collected+=renews.Sum(r=>r.Money("fee"));
        return Round(ticket.Money("principal")+Math.Max(0,Period(ticket,days,originalDays).Total-collected));
    }
    public static DateTime RenewDue(DateTime due,int days) => days%30==0?due.AddMonths(days/30):due.AddDays(days);
}