feat: 国联民生收益互换按簿记分类生成交易编号 簿记账户新增“簿记分类”字段,支持代客、融券,新增账户默认代客,历史账户统一初始化为代客 簿记账户分类设为必填,列表、编辑及详情页面同步展示 修改已被交易引用的簿记账户分类时增加二次确认,并提示关联交易数量 国联民生收益互换交易编号不再根据甲乙方判断,改为根据簿记账户分类生成 代客使用数字序号,融券使用字母序号,分别从已有同客户同日编号的最大序号继续递增 保留手工交易编号优先逻辑,标准格式的手工编号参与后续最大序号计算 生成候选编号后检查完整交易编号是否已存在,存在则继续递增 新增簿记分类编号格式、最大序号计算及数字字母序列隔离专项单测
179 lines
6.6 KiB
C#
179 lines
6.6 KiB
C#
using YLErp.Configuration;
|
|
using YLErp.DBModels;
|
|
using YLErp.Modules.TradeModule.DocGenerateModule;
|
|
|
|
namespace YLErp.UnitTestProject.Modules.TradeModule.DocGenerateModule
|
|
{
|
|
[TestClass]
|
|
public class GuolianContractNoGeneratorTest
|
|
{
|
|
[TestMethod]
|
|
public void IsGuolianSwapTrade_OnlyMatchesGuolianSwap()
|
|
{
|
|
var swapTrade = CreateSwapTrade();
|
|
|
|
Assert.IsTrue(GuolianContractNoGenerator.IsGuolianSwapTrade(swapTrade, CompanyEnum.国联));
|
|
Assert.IsFalse(GuolianContractNoGenerator.IsGuolianSwapTrade(
|
|
new trade { TradeType = "香草期权" }, CompanyEnum.国联));
|
|
Assert.IsFalse(GuolianContractNoGenerator.IsGuolianSwapTrade(swapTrade, CompanyEnum.国泰君安));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ShouldGenerateTradeNumberAfterSave_OnlyGeneratesForBlankTradeNumber()
|
|
{
|
|
var blankTrade = CreateSwapTrade();
|
|
var manualTrade = CreateSwapTrade();
|
|
manualTrade.TradeNumber = "MANUAL-001";
|
|
|
|
Assert.IsTrue(GuolianContractNoGenerator.ShouldGenerateTradeNumberAfterSave(
|
|
blankTrade, CompanyEnum.国联));
|
|
Assert.IsFalse(GuolianContractNoGenerator.ShouldGenerateTradeNumberAfterSave(
|
|
manualTrade, CompanyEnum.国联));
|
|
Assert.IsFalse(GuolianContractNoGenerator.ShouldGenerateTradeNumberAfterSave(
|
|
blankTrade, CompanyEnum.国泰君安));
|
|
}
|
|
|
|
[DataTestMethod]
|
|
[DataRow("GLMS-st0728-20260729-FICC-Q-210210IB", true, "GLMS-ST0728-20260729-FICC-Q-210210IB")]
|
|
[DataRow("GLMS-st0728-20260729-FICC-Q-210210IB", false, "GLMS-st0728-20260729-FICC-Q-210210IB")]
|
|
public void NormalizeTradeNumberCase_AppliesUpperTradeNumberConfiguration(
|
|
string tradeNumber,
|
|
bool upperTradeNumber,
|
|
string expected)
|
|
{
|
|
var actual = GuolianContractNoGenerator.NormalizeTradeNumberCase(tradeNumber, upperTradeNumber);
|
|
|
|
Assert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void BuildTradeNumber_AgencyTrade_UsesNumericSequenceAndSanitizesUnderlyingCode()
|
|
{
|
|
var trade = CreateSwapTrade();
|
|
trade.OpponentRole = "乙方";
|
|
trade.TradeDate = new DateTime(2026, 7, 24);
|
|
trade.UnderlyingCode = "250210.IB";
|
|
|
|
var number = GuolianContractNoGenerator.BuildTradeNumber(trade, "IS", AssetUnit.BookCategoryAgency, 3);
|
|
|
|
Assert.AreEqual("GLMS-IS-20260724-FICC-03-250210IB", number);
|
|
}
|
|
|
|
[DataTestMethod]
|
|
[DataRow(1, "A")]
|
|
[DataRow(26, "Z")]
|
|
[DataRow(27, "AA")]
|
|
public void BuildTradeNumber_SecuritiesLendingTrade_UsesLetterSequence(int sequenceNo, string sequenceCode)
|
|
{
|
|
var trade = CreateSwapTrade();
|
|
trade.OpponentRole = "甲方";
|
|
trade.TradeDate = new DateTime(2026, 7, 24);
|
|
trade.UnderlyingCode = "180205.IB";
|
|
|
|
var number = GuolianContractNoGenerator.BuildTradeNumber(trade, "glmscounter", AssetUnit.BookCategorySecuritiesLending, sequenceNo);
|
|
|
|
Assert.AreEqual($"GLMS-glmscounter-20260724-FICC-{sequenceCode}-180205IB", number);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetNextSequenceNo_AgencyTrade_UsesMaximumNumericSequence()
|
|
{
|
|
var tradeNumbers = new[]
|
|
{
|
|
"GLMS-1TT-20260804-FICC-01-180205IB",
|
|
"GLMS-1TT-20260804-FICC-02-180205IB",
|
|
"GLMS-1TT-20260804-FICC-03-180205IB",
|
|
"GLMS-1TT-20260804-FICC-A-180205IB",
|
|
"GLMS-1TT-20260804-FICC-CUSTOM-180205IB"
|
|
};
|
|
|
|
var sequenceNo = GuolianContractNoGenerator.GetNextSequenceNo(
|
|
tradeNumbers,
|
|
"1TT",
|
|
new DateTime(2026, 8, 4),
|
|
AssetUnit.BookCategoryAgency);
|
|
|
|
Assert.AreEqual(4, sequenceNo);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetNextSequenceNo_AgencyTrade_StandardManualNumberReservesSequence()
|
|
{
|
|
var tradeNumbers = new[]
|
|
{
|
|
"GLMS-1TT-20260804-FICC-01-180205IB",
|
|
"GLMS-1TT-20260804-FICC-02-180205IB",
|
|
"GLMS-1TT-20260804-FICC-99-180205IB"
|
|
};
|
|
|
|
var sequenceNo = GuolianContractNoGenerator.GetNextSequenceNo(
|
|
tradeNumbers,
|
|
"1tt",
|
|
new DateTime(2026, 8, 4),
|
|
AssetUnit.BookCategoryAgency);
|
|
|
|
Assert.AreEqual(100, sequenceNo);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetNextSequenceNo_SecuritiesLendingTrade_ExtendsLetterSequence()
|
|
{
|
|
var tradeNumbers = new[]
|
|
{
|
|
"GLMS-1TT-20260804-FICC-Z-180205IB",
|
|
"GLMS-1TT-20260804-FICC-AA-180205IB",
|
|
"GLMS-1TT-20260804-FICC-ZZ-180205IB",
|
|
"GLMS-1TT-20260804-FICC-99-180205IB"
|
|
};
|
|
|
|
var sequenceNo = GuolianContractNoGenerator.GetNextSequenceNo(
|
|
tradeNumbers,
|
|
"1TT",
|
|
new DateTime(2026, 8, 4),
|
|
AssetUnit.BookCategorySecuritiesLending);
|
|
var trade = CreateSwapTrade();
|
|
trade.TradeDate = new DateTime(2026, 8, 4);
|
|
trade.UnderlyingCode = "180205.IB";
|
|
|
|
var number = GuolianContractNoGenerator.BuildTradeNumber(
|
|
trade,
|
|
"1TT",
|
|
AssetUnit.BookCategorySecuritiesLending,
|
|
sequenceNo);
|
|
|
|
Assert.AreEqual("GLMS-1TT-20260804-FICC-AAA-180205IB", number);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetNextSequenceNo_SeparatesNumericAndLetterSequences()
|
|
{
|
|
var tradeNumbers = new[]
|
|
{
|
|
"GLMS-1TT-20260804-FICC-01-180205IB",
|
|
"GLMS-1TT-20260804-FICC-02-180205IB",
|
|
"GLMS-1TT-20260804-FICC-A-180205IB",
|
|
"GLMS-1TT-20260804-FICC-B-180205IB"
|
|
};
|
|
|
|
var agencySequenceNo = GuolianContractNoGenerator.GetNextSequenceNo(
|
|
tradeNumbers,
|
|
"1TT",
|
|
new DateTime(2026, 8, 4),
|
|
AssetUnit.BookCategoryAgency);
|
|
var securitiesLendingSequenceNo = GuolianContractNoGenerator.GetNextSequenceNo(
|
|
tradeNumbers,
|
|
"1TT",
|
|
new DateTime(2026, 8, 4),
|
|
AssetUnit.BookCategorySecuritiesLending);
|
|
|
|
Assert.AreEqual(3, agencySequenceNo);
|
|
Assert.AreEqual(3, securitiesLendingSequenceNo);
|
|
}
|
|
|
|
private static trade CreateSwapTrade()
|
|
{
|
|
return new trade { TradeType = "收益互换" };
|
|
}
|
|
}
|
|
}
|