#EQD-7247 客户维度:维持保证金计算不正确
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.Modules.MarginModule;
|
||||
|
||||
namespace YLErp.Modules.CalcModules
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板同层多候选取舍纯函数测试(MarginTemplateV2RateHelper.PickByBookScopePreference,2026-08-28 裁定):
|
||||
/// 簿记条件满足者优先,其次生效日最新。不连库。
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class MarginTemplateV2BookScopePreferenceTest
|
||||
{
|
||||
private static readonly DateTime Newer = new DateTime(2026, 8, 1);
|
||||
private static readonly DateTime Older = new DateTime(2026, 1, 1);
|
||||
|
||||
private static margin_template_v2 Tpl(int id, DateTime valueDate, string bookIds = null)
|
||||
{
|
||||
return new margin_template_v2 { id = id, ValueDate = valueDate, BookIds = bookIds, IsValid = true };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_01_双默认同日_限定簿记匹配_压过id更新的通配()
|
||||
{
|
||||
//复刻 dev 274/275 场景:同生效日按 id 降序,通配模板排前、限定簿记(45)排后;交易簿记=45
|
||||
var candidates = new List<margin_template_v2> { Tpl(275, Older), Tpl(274, Older, "45") };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, 45, out var byBook);
|
||||
Assert.AreEqual(274, picked.id, "簿记条件满足的限定模板应压过同日 id 更新的通配模板");
|
||||
Assert.IsTrue(byBook, "命中由簿记匹配优先产生时应置 preferredByBookScope");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_02_簿记不匹配_通配胜出()
|
||||
{
|
||||
//交易簿记=45 不在限定范围(38)内:限定模板不适用,按序取通配(2573 客需债券簿记场景)
|
||||
var candidates = new List<margin_template_v2> { Tpl(275, Older), Tpl(274, Older, "38") };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, 45, out var byBook);
|
||||
Assert.AreEqual(275, picked.id);
|
||||
Assert.IsFalse(byBook);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_03_无限定候选_取生效日最新()
|
||||
{
|
||||
var candidates = new List<margin_template_v2> { Tpl(2, Newer), Tpl(1, Older) };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, 45, out var byBook);
|
||||
Assert.AreEqual(2, picked.id, "无簿记限定候选时维持原口径:生效日最新胜出");
|
||||
Assert.IsFalse(byBook);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_04_多限定都匹配_取生效日最新()
|
||||
{
|
||||
var candidates = new List<margin_template_v2> { Tpl(2, Newer, "45,46"), Tpl(1, Older, "45") };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, 45, out var byBook);
|
||||
Assert.AreEqual(2, picked.id, "多个簿记条件满足的候选之间仍按生效日最新取第一个");
|
||||
Assert.IsFalse(byBook, "第一个适用候选即簿记匹配时不算'优先压过'");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_05_限定匹配_压过生效日更新的通配()
|
||||
{
|
||||
var candidates = new List<margin_template_v2> { Tpl(2, Newer), Tpl(1, Older, "45") };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, 45, out var byBook);
|
||||
Assert.AreEqual(1, picked.id, "簿记条件满足优先于生效日新旧");
|
||||
Assert.IsTrue(byBook);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_06_全部不适用_返回null()
|
||||
{
|
||||
var candidates = new List<margin_template_v2> { Tpl(1, Newer, "38") };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, 45, out _);
|
||||
Assert.IsNull(picked, "候选均不适用当前簿记账户时应返回 null(穿透/未命中由调用方处理)");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_07_交易无簿记账户_限定视为不适用()
|
||||
{
|
||||
//tradeId 查不到 AssetId 时入参为 null:显式限定模板视为不适用,通配仍可用
|
||||
var candidates = new List<margin_template_v2> { Tpl(2, Newer), Tpl(1, Older, "45") };
|
||||
var picked = MarginTemplateV2RateHelper.PickByBookScopePreference(candidates, null, out var byBook);
|
||||
Assert.AreEqual(2, picked.id);
|
||||
Assert.IsFalse(byBook);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BP_08_空候选或null_返回null()
|
||||
{
|
||||
Assert.IsNull(MarginTemplateV2RateHelper.PickByBookScopePreference(new List<margin_template_v2>(), 45, out _));
|
||||
Assert.IsNull(MarginTemplateV2RateHelper.PickByBookScopePreference(null, 45, out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace YLErp.Modules.CalcModules
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
private margin_template_v2 AddTemplate(string name, bool isDefault, bool isForClient, string tradeTypes = "收益互换", bool isValid = true, int ruleType = (int)MarginRuleTypeEnum.区间追保结构)
|
||||
private margin_template_v2 AddTemplate(string name, bool isDefault, bool isForClient, string tradeTypes = "收益互换", bool isValid = true, int ruleType = (int)MarginRuleTypeEnum.区间追保结构, string bookIds = null, DateTime? valueDate = null)
|
||||
{
|
||||
var t = new margin_template_v2
|
||||
{
|
||||
@@ -65,7 +65,8 @@ namespace YLErp.Modules.CalcModules
|
||||
IsValid = isValid,
|
||||
TradeTypes = tradeTypes,
|
||||
RuleType = ruleType,
|
||||
ValueDate = EffectiveDate
|
||||
ValueDate = valueDate ?? EffectiveDate,
|
||||
BookIds = bookIds
|
||||
};
|
||||
db.margin_template_v2.Add(t);
|
||||
db.SaveChanges();
|
||||
@@ -179,6 +180,54 @@ namespace YLErp.Modules.CalcModules
|
||||
Assert.AreEqual(0.03m, rate.MaintainRate.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 双全局默认并存(2026-08-28 裁定回归,复刻 dev 274/275 结构):
|
||||
/// 同生效日下通配模板(无簿记限制)id 更新排前,限定簿记且匹配的模板排后——
|
||||
/// 原口径"先按生效日/id 取最新再验簿记"使通配恒胜、限定簿记模板永不生效;
|
||||
/// 新口径簿记条件满足者优先。需一笔隔离的真实交易(有簿记账户、无交易绑定,客户无客户级绑定)。
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TF_009_双全局默认_限定簿记匹配优先于通配()
|
||||
{
|
||||
var boundTradeIds = db.trade_margin_template.AsNoTracking().Select(x => x.TradeId).ToList();
|
||||
var boundClientIds = db.client_margin_template.AsNoTracking()
|
||||
.Where(x => x.ClientId > 0).Select(x => x.ClientId).Distinct().ToList();
|
||||
var levelBoundNames = db.client_margin_template.AsNoTracking()
|
||||
.Where(x => x.ClientId == 0 && x.ClientLevel != null && x.ClientLevel != "")
|
||||
.Select(x => x.ClientLevel).Distinct().ToList();
|
||||
int tradeId, assetId, tradeClientId;
|
||||
using (var clientDb = DbContextFactory.GetClientDbContext(OptUserInfo.SystemUser))
|
||||
{
|
||||
var excludedLevelIds = clientDb.clientlevel.AsNoTracking()
|
||||
.Where(l => levelBoundNames.Contains(l.LevelName)).Select(l => l.id).ToList();
|
||||
var excludedClientIds = clientDb.client.AsNoTracking()
|
||||
.Where(c => boundClientIds.Contains(c.id) || (c.LevelId != null && excludedLevelIds.Contains(c.LevelId ?? 0)))
|
||||
.Select(c => c.id).ToList();
|
||||
var picked = db.trade.AsNoTracking()
|
||||
.Where(t => t.AssetId > 0 && t.ValidState != "InValid"
|
||||
&& !boundTradeIds.Contains(t.id) && !excludedClientIds.Contains(t.ClientId))
|
||||
.OrderByDescending(t => t.id)
|
||||
.Select(t => new { t.id, t.AssetId, t.ClientId }).FirstOrDefault();
|
||||
if (picked == null)
|
||||
{
|
||||
Assert.Inconclusive("dev 库无可用的隔离测试交易(有簿记账户且交易/客户均无绑定)");
|
||||
return;
|
||||
}
|
||||
tradeId = picked.id;
|
||||
assetId = picked.AssetId;
|
||||
tradeClientId = picked.ClientId;
|
||||
}
|
||||
|
||||
//同生效日(今日,晚于 dev 既有默认模板)双全局默认:通配后建(id 更大排前)
|
||||
var generic = AddTemplate(Marker + "全局通配", isDefault: true, isForClient: false, valueDate: DateTime.Today);
|
||||
var bookScoped = AddTemplate(Marker + "全局限定簿记", isDefault: true, isForClient: false, bookIds: assetId.ToString(), valueDate: DateTime.Today);
|
||||
|
||||
var resolved = MarginTemplateV2RateHelper.ResolveTieredTemplate(tradeId, tradeClientId, DateTime.Today, db);
|
||||
Assert.IsNotNull(resolved);
|
||||
Assert.IsTrue(resolved.Name.StartsWith(Marker), $"应命中本用例创建的标记模板,实际命中模板{resolved.id}(dev 出现同日/更晚生效默认模板会干扰,请重跑)");
|
||||
Assert.AreEqual(bookScoped.id, resolved.id, "限定簿记且匹配的全局默认应优先于同生效日的通配全局默认");
|
||||
}
|
||||
|
||||
private void AssertGlobal(margin_template_v2 resolved, margin_template_v2 expected)
|
||||
{
|
||||
Assert.IsNotNull(resolved);
|
||||
|
||||
Reference in New Issue
Block a user