diff --git a/Framework/YLErp.Core/DBModels/margin_template_v2.cs b/Framework/YLErp.Core/DBModels/margin_template_v2.cs index f58cb003..c9ea9826 100644 --- a/Framework/YLErp.Core/DBModels/margin_template_v2.cs +++ b/Framework/YLErp.Core/DBModels/margin_template_v2.cs @@ -25,6 +25,19 @@ namespace YLErp.DBModels [Column("book_id")] public string BookIds { get; set; } + private string _bookScopeName; + + /// + /// 适用簿记账户名称,仅用于列表展示,不持久化。 + /// + [NotMapped] + [DisplayName("适用簿记账户")] + public string BookScopeName + { + get => string.IsNullOrWhiteSpace(BookIds) ? "全部" : _bookScopeName ?? string.Empty; + set => _bookScopeName = value; + } + /// /// 将簿记账户ID列表规范化为去空格、去重的逗号分隔字符串。 /// diff --git a/UnitTestProject/Modules/CalcModules/MarginTemplateV2BookScopeTest.cs b/UnitTestProject/Modules/CalcModules/MarginTemplateV2BookScopeTest.cs new file mode 100644 index 00000000..3da20b87 --- /dev/null +++ b/UnitTestProject/Modules/CalcModules/MarginTemplateV2BookScopeTest.cs @@ -0,0 +1,110 @@ +using YLErp.DBModels; +using YLErp.Modules.MarginModule; +using YLErp.Modules.SwapModule; + +namespace YLErp.Modules.CalcModules +{ + [TestClass] + public class MarginTemplateV2BookScopeTest + { + [TestMethod] + public void BS_001_空范围适用全部簿记账户() + { + var template = new margin_template_v2 { BookIds = null }; + + Assert.IsTrue(template.IsApplicableToBook(1)); + Assert.IsTrue(template.IsApplicableToBook(11)); + } + + [TestMethod] + public void BS_002_按完整token精确匹配避免1命中11() + { + var template = new margin_template_v2 { BookIds = "11, 12,11" }; + + Assert.IsFalse(template.IsApplicableToBook(1)); + Assert.IsTrue(template.IsApplicableToBook(11)); + Assert.IsTrue(template.IsApplicableToBook(12)); + Assert.AreEqual("11,12", margin_template_v2.NormalizeBookIds(template.BookIds)); + } + + [TestMethod] + public void BS_003_账户范围只接受正整数并规范前导零() + { + Assert.IsNull(margin_template_v2.NormalizeBookIds(null)); + Assert.IsNull(margin_template_v2.NormalizeBookIds(" ")); + Assert.AreEqual("1,2", margin_template_v2.NormalizeBookIds("001, 2,001")); + + Assert.ThrowsException(() => margin_template_v2.NormalizeBookIds("abc")); + Assert.ThrowsException(() => margin_template_v2.NormalizeBookIds("0")); + Assert.ThrowsException(() => margin_template_v2.NormalizeBookIds("-1")); + Assert.ThrowsException(() => margin_template_v2.NormalizeBookIds("1,,2")); + } + + [TestMethod] + public void BS_004_范围重叠空范围视为全部() + { + Assert.IsTrue(margin_template_v2.AreBookScopesOverlapping(null, "11")); + Assert.IsTrue(margin_template_v2.AreBookScopesOverlapping("11,12", "12,13")); + Assert.IsFalse(margin_template_v2.AreBookScopesOverlapping("1", "11")); + } + + [TestMethod] + public void BS_005_收集所有重叠范围的适用结构() + { + var candidates = new[] + { + new margin_template_v2 { BookIds = "1", TradeTypes = "收益互换" }, + new margin_template_v2 { BookIds = "2", TradeTypes = "香草期权" }, + new margin_template_v2 { BookIds = "3", TradeTypes = "远期" } + }; + + var tradeTypes = margin_template_v2.GetOverlappingTradeTypes(candidates, "1,2").ToList(); + + CollectionAssert.AreEquivalent(new[] { "收益互换", "香草期权" }, tradeTypes); + } + + [TestMethod] + public void BS_006_空交易ID不触发簿记账户查询() + { + Assert.IsNull(MarginTemplateV2RateHelper.GetTradeAssetId(null, null)); + Assert.IsNull(MarginTemplateV2RateHelper.GetTradeAssetId(0, null)); + } + + [TestMethod] + public void LEGACY_001_存量原样未知名称可保留() + { + Assert.IsTrue(SwapTradeService.CanKeepLegacyMarginTemplateName( + false, "历史模板", "历史模板", false)); + } + + [TestMethod] + public void LEGACY_002_新增交易的未知名称不可保留() + { + Assert.IsFalse(SwapTradeService.CanKeepLegacyMarginTemplateName( + true, "历史模板", "历史模板", false)); + } + + [TestMethod] + public void LEGACY_003_修改为不同未知名称不可保留() + { + Assert.IsFalse(SwapTradeService.CanKeepLegacyMarginTemplateName( + false, "历史模板", "另一历史模板", false)); + } + + [TestMethod] + public void LEGACY_004_已存在V2名称不作为历史值() + { + Assert.IsFalse(SwapTradeService.CanKeepLegacyMarginTemplateName( + false, "模板V2", "模板V2", true)); + } + + [TestMethod] + public void LEGACY_005_空名称不作为历史值() + { + Assert.IsFalse(SwapTradeService.CanKeepLegacyMarginTemplateName( + false, null, null, false)); + Assert.IsFalse(SwapTradeService.CanKeepLegacyMarginTemplateName( + false, " ", " ", false)); + } + } +} diff --git a/YLErpDAL/Modules/MarginModule/MarginTemplateService.cs b/YLErpDAL/Modules/MarginModule/MarginTemplateService.cs index 10539fbb..55ff63c5 100644 --- a/YLErpDAL/Modules/MarginModule/MarginTemplateService.cs +++ b/YLErpDAL/Modules/MarginModule/MarginTemplateService.cs @@ -1,4 +1,5 @@ using BaseOUDAL; +using System.Globalization; using YLErp.Model; namespace YLErp.Modules.MarginModule @@ -77,8 +78,67 @@ namespace YLErp.Modules.MarginModule } var retListResult = query.ToSearchList(req); + SetBookScopeNames(retListResult); return retListResult; } + + private void SetBookScopeNames(SearchListResult result) + { + var rows = result?.rows?.ToList(); + if (rows == null || rows.Count == 0) + { + return; + } + + var parsedRows = rows.Select(row => new + { + Row = row, + BookIds = row == null ? new List() : ParseBookIdsForDisplay(row.BookIds) + }).ToList(); + var bookIds = parsedRows.SelectMany(item => item.BookIds).Distinct().ToList(); + var bookNames = bookIds.Count == 0 + ? new Dictionary() + : DbContext.assetunit + .AsNoTracking() + .Where(book => bookIds.Contains(book.id)) + .Select(book => new { book.id, book.Name }) + .ToDictionary(book => book.id, book => book.Name); + + foreach (var item in parsedRows) + { + if (item.Row == null) + { + continue; + } + + item.Row.BookScopeName = string.IsNullOrWhiteSpace(item.Row.BookIds) + ? "全部" + : string.Join(",", item.BookIds + .Where(bookId => bookNames.ContainsKey(bookId)) + .Select(bookId => bookNames[bookId])); + } + + result.rows = rows; + } + + private static List ParseBookIdsForDisplay(string bookIds) + { + var ids = new List(); + if (string.IsNullOrWhiteSpace(bookIds)) + { + return ids; + } + + foreach (var rawId in bookIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (int.TryParse(rawId.Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out var id) && id > 0) + { + ids.Add(id); + } + } + + return ids; + } } } diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientList.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientList.cshtml index 161fb33e..7e0e4226 100644 --- a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientList.cshtml +++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientList.cshtml @@ -50,6 +50,8 @@ name: 'RuleType', label: '预付金规则', index: 'RuleType', width: 150, formatter: ruleTypeFormat }, { name: 'TradeTypes', label: '适用结构', index: 'TradeTypes', width: 460 + }, { + name: 'BookScopeName', label: '适用簿记账户', index: 'BookScopeName', width: 240 }]; function showToolName(cellValue, options, rowObject) { @@ -197,4 +199,4 @@ -@Html.Raw(JqGridSimple.OutTable()) \ No newline at end of file +@Html.Raw(JqGridSimple.OutTable()) diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultList.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultList.cshtml index 2e841f0c..9f183439 100644 --- a/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultList.cshtml +++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultList.cshtml @@ -49,6 +49,8 @@ name: 'RuleType', label: '预付金规则', index: 'RuleType', width: 150, formatter: ruleTypeFormat }, { name: 'TradeTypes', label: '适用结构', index: 'TradeTypes', width: 460 + }, { + name: 'BookScopeName', label: '适用簿记账户', index: 'BookScopeName', width: 240 }, { name: 'BuySellTypeName', label: '适用买卖方向', index: 'BuySellTypeName', width: 150 }, { @@ -201,4 +203,4 @@ -@Html.Raw(JqGridSimple.OutTable()) \ No newline at end of file +@Html.Raw(JqGridSimple.OutTable())