diff --git a/UnitTestProject/Modules/SwapModule/FundTagCalcTest.cs b/UnitTestProject/Modules/SwapModule/FundTagCalcTest.cs index 41df8245..460c1963 100644 --- a/UnitTestProject/Modules/SwapModule/FundTagCalcTest.cs +++ b/UnitTestProject/Modules/SwapModule/FundTagCalcTest.cs @@ -11,7 +11,7 @@ namespace YLErp.Modules.SwapModule [TestClass] public class FundTagCalcTest { - private static LegAmount Leg(long id, double amount, bool preferCredit) + private static LegAmount Leg(long id, decimal amount, bool preferCredit) => new() { Leg = new swap_position { id = id }, Amount = amount, PreferCredit = preferCredit }; // ================================================================ @@ -273,7 +273,7 @@ namespace YLErp.Modules.SwapModule InterestPrincipalFix = fix, FundTag = preferCredit ? ConsFundTag.Credit : ConsFundTag.Cash }, - Amount = (double)fix, + Amount = fix, PreferCredit = preferCredit }; diff --git a/UnitTestProject/Modules/SwapModule/Margin/SwapAdditionalMarginLegTest.cs b/UnitTestProject/Modules/SwapModule/Margin/SwapAdditionalMarginLegTest.cs index 84c0aa6e..60de9711 100644 --- a/UnitTestProject/Modules/SwapModule/Margin/SwapAdditionalMarginLegTest.cs +++ b/UnitTestProject/Modules/SwapModule/Margin/SwapAdditionalMarginLegTest.cs @@ -71,8 +71,8 @@ namespace YLErp.Modules.SwapModule new List { new() { Leg = leg, Amount = 1000, PreferCredit = true } }, creditAvailable: 5_000, ignoreMoneyCheck: false); - Assert.AreEqual(1000, plans[0].CreditAmount, 1e-6); - Assert.AreEqual(0, plans[0].CashAmount, 1e-6); + Assert.AreEqual(1000, plans[0].CreditAmount); + Assert.AreEqual(0, plans[0].CashAmount); Assert.IsFalse(plans[0].NeedSplit); //CashAmount==0 → ApplyMarginFundTags 不写现金流水;占用 remark 前缀断言 Assert.IsTrue((ClientCreditInoutService.AdditionalMarginRemark + "占用") @@ -94,8 +94,8 @@ namespace YLErp.Modules.SwapModule var legs = new List { new() { Leg = leg, Amount = 1000, PreferCredit = true } }; var plans = FundTagCalc.AllocateByLegPreference(legs, creditAvailable: 300, ignoreMoneyCheck: false); Assert.IsTrue(plans[0].NeedSplit); - Assert.AreEqual(300, plans[0].CreditAmount, 1e-6); - Assert.AreEqual(700, plans[0].CashAmount, 1e-6); + Assert.AreEqual(300, plans[0].CreditAmount); + Assert.AreEqual(700, plans[0].CashAmount); //ApplySaveTimeSplit 与 SplitLeg 同口径(原腿=授信部分、克隆现金腿倒挤守恒) var newLegs = FundTagCalc.ApplySaveTimeSplit(legs, plans); @@ -126,8 +126,8 @@ namespace YLErp.Modules.SwapModule new List { new() { Leg = leg, Amount = 1000, PreferCredit = false } }, creditAvailable: 5_000, ignoreMoneyCheck: false); - Assert.AreEqual(0, plans[0].CreditAmount, 1e-6); - Assert.AreEqual(1000, plans[0].CashAmount, 1e-6); + Assert.AreEqual(0, plans[0].CreditAmount); + Assert.AreEqual(1000, plans[0].CashAmount); Assert.IsFalse(plans[0].NeedSplit); } diff --git a/UnitTestProject/Modules/SwapModule/TestableSwapDealService.cs b/UnitTestProject/Modules/SwapModule/TestableSwapDealService.cs index c028b515..3fbd301d 100644 --- a/UnitTestProject/Modules/SwapModule/TestableSwapDealService.cs +++ b/UnitTestProject/Modules/SwapModule/TestableSwapDealService.cs @@ -141,10 +141,10 @@ namespace YLErp.Modules.SwapModule return ReleaseMarginByFundTagResult ?? new UnwindTagSplit { - CashMargin = Convert.ToDouble(marginAmount), - CashRebate = Convert.ToDouble(marginLegs.Any() + CashMargin = marginAmount, + CashRebate = marginLegs.Any() ? marginLegs.Sum(x => x.InterestClosePnL) - : marginRebate) + : marginRebate }; } diff --git a/YLErpDAL/Modules/SwapModule/Margin/FundTagCalc.cs b/YLErpDAL/Modules/SwapModule/Margin/FundTagCalc.cs index 61f1c6d0..58af47d9 100644 --- a/YLErpDAL/Modules/SwapModule/Margin/FundTagCalc.cs +++ b/YLErpDAL/Modules/SwapModule/Margin/FundTagCalc.cs @@ -16,14 +16,14 @@ public static class FundTagCalc /// 客户净收取(负金额)的腿不参与授信分配。 /// legs 需按预期占用顺序传入(HappenDate、id)。 /// - public static List AllocateByLegPreference(List legs, double creditAvailable, bool ignoreMoneyCheck) + public static List AllocateByLegPreference(List legs, decimal creditAvailable, bool ignoreMoneyCheck) { var plans = new List(); - var remaining = Math.Round(Math.Max(creditAvailable, 0), 2, MidpointRounding.AwayFromZero); + var remaining = Math.Round(Math.Max(creditAvailable, 0m), 2, MidpointRounding.AwayFromZero); foreach (var leg in legs) { var amount = Math.Round(leg.Amount, 2, MidpointRounding.AwayFromZero); - var credit = 0.0; + var credit = 0m; if (!ignoreMoneyCheck && leg.PreferCredit && amount > 0) { credit = Math.Min(amount, remaining); @@ -64,7 +64,7 @@ public static class FundTagCalc //应付额 = fix × (dir==1 ? 1 : -1),反推 fix 用同一比例(±1 自反) var payableRatio = position.InterestDirection == 1 ? 1 : -1; var originalFix = position.InterestPrincipalFix; - position.InterestPrincipalFix = Math.Round(Convert.ToDecimal(plan.CreditAmount) * payableRatio, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + position.InterestPrincipalFix = Math.Round(plan.CreditAmount * payableRatio, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); position.FundTag = ConsFundTag.Credit; var cashLeg = position.Clone(); cashLeg.id = 0; @@ -93,8 +93,8 @@ public static class FundTagCalc var result = new UnwindTagSplit(); foreach (var s in settlements) { - var margin = Convert.ToDouble(s.MarginAmount); - var rebate = Convert.ToDouble(s.RebateAmount); + var margin = s.MarginAmount; + var rebate = s.RebateAmount; if (s.Tag == ConsFundTag.Credit) { result.CreditMargin += margin; @@ -128,7 +128,7 @@ public static class FundTagCalc public class LegAmount { public swap_position Leg { get; set; } - public double Amount { get; set; } + public decimal Amount { get; set; } /// 腿上是否选了授信(swap_position.fund_tag=='Credit') public bool PreferCredit { get; set; } } @@ -140,11 +140,11 @@ public class LegFundPlan { public swap_position Leg { get; set; } /// 腿原簿记金额 - public double Amount { get; set; } + public decimal Amount { get; set; } /// 授信部分金额 - public double CreditAmount { get; set; } + public decimal CreditAmount { get; set; } /// 现金部分金额 - public double CashAmount { get; set; } + public decimal CashAmount { get; set; } /// 拆单时新拆出的现金腿(授信不足的差额;占用记录绑原腿、现金流水绑它) public swap_position CashLeg { get; set; } public bool NeedSplit => CreditAmount > 0 && CashAmount > 0; @@ -198,7 +198,7 @@ public static class MarginSettlementBuilder public class TagRelease { public long PositionId { get; set; } - public double Amount { get; set; } + public decimal Amount { get; set; } } /// @@ -207,13 +207,13 @@ public class TagRelease public class UnwindTagSplit { /// 现金部分返还本金(产生 应付预付金 资金流水) - public double CashMargin { get; set; } + public decimal CashMargin { get; set; } /// 授信部分返还本金(写授信出入表"释放",不产生资金流水) - public double CreditMargin { get; set; } + public decimal CreditMargin { get; set; } /// 现金部分预付金返息(产生 预付金返息 资金流水) - public double CashRebate { get; set; } + public decimal CashRebate { get; set; } /// 授信部分预付金返息(授信不进资金,不产生资金流水) - public double CreditRebate { get; set; } + public decimal CreditRebate { get; set; } /// 按腿的授信释放明细(position_id 匹配原占用记录) public List Releases { get; set; } = new List(); } diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 66578dc1..0961dff0 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -1541,11 +1541,11 @@ namespace YLErp.Modules.SwapModule var split = interestEvents != null && interestEvents.Any(x => string.IsNullOrEmpty(x.UnderlyingCode)) ? ReleaseMarginByFundTag(td, valueDate, interestEvents, marginAmount, marginRebate) //无预付金腿结算事件(如金额手工归一化/无腿场景)——退化原逻辑,全额现金 - : new UnwindTagSplit { CashMargin = Convert.ToDouble(marginAmount), CashRebate = Convert.ToDouble(marginRebate) }; + : new UnwindTagSplit { CashMargin = marginAmount, CashRebate = marginRebate }; if (split.CashMargin != 0) - writeCash(td, split.CashMargin, ClientCashInCashOut.系统操作_应付预付金, valueDate); + writeCash(td, (double)split.CashMargin, ClientCashInCashOut.系统操作_应付预付金, valueDate); if (split.CashRebate != 0) - writeCash(td, -split.CashRebate, ClientCashInCashOut.系统操作_预付金返息, valueDate); + writeCash(td, (double)(-split.CashRebate), ClientCashInCashOut.系统操作_预付金返息, valueDate); } /// @@ -1561,7 +1561,7 @@ namespace YLErp.Modules.SwapModule //结算事件缺失(异常数据)时保底按传入总额走现金,不丢资金记录 if (settlements.Count == 0) { - return new UnwindTagSplit { CashMargin = Convert.ToDouble(marginAmount), CashRebate = Convert.ToDouble(marginRebate) }; + return new UnwindTagSplit { CashMargin = marginAmount, CashRebate = marginRebate }; } return split; } diff --git a/YLErpDAL/Modules/SwapModule/SwapFundTagService.cs b/YLErpDAL/Modules/SwapModule/SwapFundTagService.cs index e4caced8..68570f69 100644 --- a/YLErpDAL/Modules/SwapModule/SwapFundTagService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapFundTagService.cs @@ -78,7 +78,7 @@ namespace YLErp.Modules.SwapModule .Select(x => new LegAmount { Leg = x, - Amount = Convert.ToDouble(x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1)), + Amount = x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1), PreferCredit = true }) .Where(x => x.Amount > 0) @@ -89,7 +89,7 @@ namespace YLErp.Modules.SwapModule { return; } - var plans = FundTagCalc.AllocateByLegPreference(allocateLegs, creditAvailable, ignoreMoneyCheck: false); + var plans = FundTagCalc.AllocateByLegPreference(allocateLegs, (decimal)creditAvailable, ignoreMoneyCheck: false); //授信不足的腿 = 偏好授信但授信没覆盖全额(含额度为0/被前腿耗尽的整体转现金) var shortPlans = plans.Where(p => p.CreditAmount < p.Amount).ToList(); if (shortPlans.Count == 0) @@ -157,7 +157,7 @@ namespace YLErp.Modules.SwapModule .Select(x => new LegAmount { Leg = x, - Amount = Convert.ToDouble(x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1)), + Amount = x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1), //优先级:腿上显式选择 > 交易级 margin_fund_source 回退(§2.3 情形1)> 默认现金, //与 TradeCanBeConfirm 校验分流共用 ConsFundTag.PreferCredit 保证口径一致 PreferCredit = ConsFundTag.PreferCredit(x.FundTag, td.MarginFundSource) @@ -166,7 +166,7 @@ namespace YLErp.Modules.SwapModule .OrderBy(x => x.Leg.HappenDate ?? DateTime.MaxValue) .ThenBy(x => x.Leg.id) .ToList(); - var plans = FundTagCalc.AllocateByLegPreference(allocateLegs, creditAvailable, ignoreMoneyCheck); + var plans = FundTagCalc.AllocateByLegPreference(allocateLegs, (decimal)creditAvailable, ignoreMoneyCheck); //先落库拆分的新现金腿(需要 id 才能绑定现金流水) foreach (var plan in plans.Where(p => p.NeedSplit)) @@ -198,13 +198,13 @@ namespace YLErp.Modules.SwapModule { //授信部分(整腿授信 或 拆单后保留在原腿的可用额度部分):不产生资金流水,只写占用(占用绑原腿)。 //占用记正数(BUG-01 修正:已使用授信=Σ(amount) 占用上升;2026-08-20"与资金流水同号入金负"口径已废弃) - creditService.Occupy(td.ClientId, leg.id, td.id, plan.CreditAmount, happenDate, + creditService.Occupy(td.ClientId, leg.id, td.id, (double)plan.CreditAmount, happenDate, plan.NeedSplit ? splitOccupyRemark : occupyRemark); } //资金记录沿用既有符号口径(客户付钱为负 = -应付额):授信部分不产生流水, //现金部分按差额产生——拆单腿的流水绑新拆出的现金腿,整腿现金/负应付腿绑原腿 var recordAmount = plan != null - ? -plan.CashAmount + ? (double)(-plan.CashAmount) : Convert.ToDouble(leg.InterestPrincipalFix * (leg.InterestDirection == 1 ? -1 : 1)); if (recordAmount != 0) { @@ -256,7 +256,7 @@ namespace YLErp.Modules.SwapModule var creditService = new ClientCreditInoutService(this); foreach (var release in split.Releases) { - creditService.Release(td.ClientId, release.PositionId, td.id, release.Amount, valueDate, "平仓/到期释放"); + creditService.Release(td.ClientId, release.PositionId, td.id, (double)release.Amount, valueDate, "平仓/到期释放"); } } return split;