diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx
index 3233881e..e99377d3 100644
Binary files a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx and b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx differ
diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx
index e6ad31f5..423071ed 100644
Binary files a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx and b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx differ
diff --git a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs
index c0cd3c47..d491bf73 100644
--- a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs
+++ b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs
@@ -326,6 +326,8 @@ namespace YLErp.Plugins.GuoLian.DocumentGenerator
dic["IsFixed"] = "□";
dic["IsFloat"] = "□";
+ dic["期初观察日"] = trade.StartDate?.ToString("yyyy年M月d日") ?? "";
+ dic["期末观察日"] = trade.ExerciseDate?.ToString("yyyy年M月d日") ?? "";
if (interestMargin != null)
{
if (string.IsNullOrEmpty(interestMargin.FloatRateUnderlyingCode))
@@ -343,6 +345,13 @@ namespace YLErp.Plugins.GuoLian.DocumentGenerator
dic["利差"] = ((double)interestMargin.InterestRateDefault * 10000).ToString("N0");
dic["重置频率"] = (interestMargin.interest_rest_days ?? 0) + "天";
}
+ if (interestMargin.SwapIntervalList.Count()>0)
+ {
+ var firstInterval = interestMargin.SwapIntervalList.OrderBy(o=>o.Date).First();
+ var lastInterval = interestMargin.SwapIntervalList.OrderBy(o => o.Date).Last();
+ dic["期初观察日"] = firstInterval.Date.ToString("yyyy年M月d日") ?? "";
+ dic["期末观察日"] = firstInterval.Date.ToString("yyyy年M月d日") ?? "";
+ }
}
else
{
diff --git a/YLErpDAL/Modules/SystemModule/ClientEditConfigService.cs b/YLErpDAL/Modules/SystemModule/ClientEditConfigService.cs
index a2868852..0cb6db5f 100644
--- a/YLErpDAL/Modules/SystemModule/ClientEditConfigService.cs
+++ b/YLErpDAL/Modules/SystemModule/ClientEditConfigService.cs
@@ -612,6 +612,8 @@ namespace YLErp.Modules.SystemModule
///
public string dictionaryKey { get; set; }
+ public int? maxLength { get; set; }
+
public override string ToString()
{
return $"{name}-{label}-{type}";
diff --git a/YLErpDAL/Modules/TradeModule/DealModule/TradeContractGenerateService.cs b/YLErpDAL/Modules/TradeModule/DealModule/TradeContractGenerateService.cs
index bac3c424..82428458 100644
--- a/YLErpDAL/Modules/TradeModule/DealModule/TradeContractGenerateService.cs
+++ b/YLErpDAL/Modules/TradeModule/DealModule/TradeContractGenerateService.cs
@@ -273,7 +273,10 @@ namespace YLErp.Modules.TradeModule.DealModule
logger.Info($"[groupGenerateHandle_Date] 未找到客户信息 - ClientId: {firstTrade.ClientId}");
return;
}
-
+ if (string.IsNullOrEmpty(client.Code))
+ {
+ throw new ServiceException($"对手方代码缩写缺失,请联系运营组同事维护: {firstTrade.ClientName}");
+ }
var supProtocolDate = client.SupProtocolDate;
logger.Info($"[groupGenerateHandle_Date] 客户补充协议日期: {supProtocolDate}");
diff --git a/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateContext.cs b/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateContext.cs
index 7894e15b..050fa800 100644
--- a/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateContext.cs
+++ b/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateContext.cs
@@ -1347,6 +1347,9 @@ namespace YLErp.Modules.TradeModule.DocGenerateModule
return Trade.TradeNumber;
}
+ ///
+ /// 生成国贸确认书编号
+ ///
private string GenerateGuolianContractNo()
{
if (_contractType != ContractTypeEnum.Trade)
@@ -1355,13 +1358,11 @@ namespace YLErp.Modules.TradeModule.DocGenerateModule
}
if (Trade.TradeType == "收益互换")
{
- var posi = GetSwapPositions(Trade.id, true).FirstOrDefault(p => p.PositionType > 0);
- var isLong = posi?.PositionType == (int)PositionTypeFlag.Short ? false : true;
-
- return "GLMS-" + _client.Number + "-" + Trade.TradeNumber;
+ return GuolianContractNoGenerator.Generate(DbContext, (trade)Trade, _client.Code);
}
- return Trade.TradeNumber;
+ return Trade.TradeNumber;
}
+
///
/// 生成国贸确认书编号
///
diff --git a/YLErpDAL/Modules/TradeModule/DocGenerateModule/GuolianContractNoGenerator.cs b/YLErpDAL/Modules/TradeModule/DocGenerateModule/GuolianContractNoGenerator.cs
new file mode 100644
index 00000000..18e0312b
--- /dev/null
+++ b/YLErpDAL/Modules/TradeModule/DocGenerateModule/GuolianContractNoGenerator.cs
@@ -0,0 +1,174 @@
+using YLErp.BLL;
+using YLErp.DBModels;
+using YLErp.DBModels.Consts;
+using YLErp.DBModels.Enums;
+
+namespace YLErp.Modules.TradeModule.DocGenerateModule
+{
+ ///
+ /// 国贸交易确认书编号生成器
+ /// 规则:
+ /// - 对客交易(OpponentRole为"甲方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-{标的代码}-{当日第N笔对客交易(数字递增)},从001开始
+ /// - 非对客交易(OpponentRole为"乙方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-{标的代码}-{当日第N笔非对客交易(字母递增)},从A开始
+ ///
+ public class GuolianContractNoGenerator
+ {
+ private static readonly object _syncLock = new object();
+
+ ///
+ /// 生成国贸交易确认书编号
+ ///
+ /// 数据库上下文
+ /// 交易记录
+ /// 客户代码
+ /// 确认书编号
+ public static string Generate(
+ YLContext dbContext,
+ trade trade,
+ string clientCode)
+ {
+ // 根据trade的OpponentRole判断是否为对客交易
+ var isClientTrade = trade.OpponentRole == "甲方";
+ var underlyingCode = trade.UnderlyingCode ?? "";
+ var tradeDate = trade.TradeDate ?? DateTime.MinValue;
+ var tradeDateStr = tradeDate.ToString("yyyyMMdd");
+ var prefix = $"GLMS-{clientCode}-{tradeDateStr}-{underlyingCode}-";
+
+ lock (_syncLock)
+ {
+ var existingForTrade = dbContext.trade_contract_r
+ .Where(r => r.TradeId == trade.id && r.Type == ContractTypeEnum.Trade && r.IsValid)
+ .Select(r => r.ContractCode)
+ .FirstOrDefault();
+
+ if (!string.IsNullOrWhiteSpace(existingForTrade))
+ {
+ return existingForTrade;
+ }
+
+ var existingCodes = dbContext.trade_contract_r
+ .Where(d => d.ContractCode.StartsWith(prefix) && d.Type == ContractTypeEnum.Trade && d.IsValid)
+ .Select(d => d.ContractCode)
+ .ToList();
+
+ var existingCodeSet = new HashSet();
+ for (var i = 0; i < existingCodes.Count; i++)
+ {
+ var code = existingCodes[i];
+ if (!string.IsNullOrWhiteSpace(code))
+ {
+ existingCodeSet.Add(code);
+ }
+ }
+
+ if (isClientTrade)
+ {
+ var maxNo = 0;
+ var prefixLen = prefix.Length;
+ for (var i = 0; i < existingCodes.Count; i++)
+ {
+ var code = existingCodes[i];
+ if (string.IsNullOrEmpty(code) || code.Length <= prefixLen)
+ {
+ continue;
+ }
+
+ var suffix = code.Substring(prefixLen);
+ if (int.TryParse(suffix, out var n) && n > maxNo)
+ {
+ maxNo = n;
+ }
+ }
+
+ var nextNo = maxNo + 1;
+ var contractNo = $"{prefix}{nextNo:D3}";
+ while (existingCodeSet.Contains(contractNo))
+ {
+ nextNo++;
+ contractNo = $"{prefix}{nextNo:D3}";
+ }
+ return contractNo;
+ }
+ else
+ {
+ var maxNo = 0;
+ var prefixLen = prefix.Length;
+ for (var i = 0; i < existingCodes.Count; i++)
+ {
+ var code = existingCodes[i];
+ if (string.IsNullOrEmpty(code) || code.Length <= prefixLen)
+ {
+ continue;
+ }
+
+ var suffix = code.Substring(prefixLen);
+ if (!TryParseLettersToNumber(suffix, out var n))
+ {
+ continue;
+ }
+
+ if (n > maxNo)
+ {
+ maxNo = n;
+ }
+ }
+
+ var nextNo = maxNo + 1;
+ var contractNo = $"{prefix}{NumberToLetter(nextNo)}";
+ while (existingCodeSet.Contains(contractNo))
+ {
+ nextNo++;
+ contractNo = $"{prefix}{NumberToLetter(nextNo)}";
+ }
+ return contractNo;
+ }
+ }
+ }
+
+ private static bool TryParseLettersToNumber(string letters, out int number)
+ {
+ number = 0;
+ if (string.IsNullOrEmpty(letters))
+ {
+ return false;
+ }
+
+ for (var i = 0; i < letters.Length; i++)
+ {
+ var c = letters[i];
+ if (c >= 'a' && c <= 'z')
+ {
+ c = (char)(c - 32);
+ }
+ else if (c < 'A' || c > 'Z')
+ {
+ number = 0;
+ return false;
+ }
+
+ number = (number * 26) + (c - 'A' + 1);
+ }
+ return true;
+ }
+
+ ///
+ /// 将数字转换为字母序列(1=A, 2=B, 26=Z, 27=AA, 28=AB...)
+ ///
+ private static string NumberToLetter(int number)
+ {
+ if (number <= 0)
+ {
+ return "A";
+ }
+
+ string result = "";
+ while (number > 0)
+ {
+ number--; // 调整为从0开始
+ result = (char)('A' + (number % 26)) + result;
+ number /= 26;
+ }
+ return result;
+ }
+ }
+}
diff --git a/YLErpWeb/Views/client/_ClientEditFields.cshtml b/YLErpWeb/Views/client/_ClientEditFields.cshtml
index c8c5a4f2..f8065a25 100644
--- a/YLErpWeb/Views/client/_ClientEditFields.cshtml
+++ b/YLErpWeb/Views/client/_ClientEditFields.cshtml
@@ -95,11 +95,19 @@
-
+
}
else
{
-
+ if (f.maxLength.HasValue)
+ {
+
+ }
+ else
+ {
+
+ }
+
}
@if (f.required)
{