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())