diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx
new file mode 100644
index 00000000..28736195
Binary files /dev/null and b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx differ
diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx
new file mode 100644
index 00000000..e9127a3f
Binary files /dev/null and b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.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
new file mode 100644
index 00000000..b34f8884
Binary files /dev/null and b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx differ
diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx
new file mode 100644
index 00000000..bf68cdc4
Binary files /dev/null and b/Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx differ
diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/settlement_template/settle_01.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/settlement_template/settle_01.docx
new file mode 100644
index 00000000..e69de29b
diff --git a/Plugins/YLErp.Plugins.GuoLian/App_Docs/settlement_template/unwind_01.docx b/Plugins/YLErp.Plugins.GuoLian/App_Docs/settlement_template/unwind_01.docx
new file mode 100644
index 00000000..e69de29b
diff --git a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs
new file mode 100644
index 00000000..5fba935c
--- /dev/null
+++ b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs
@@ -0,0 +1,392 @@
+using Newtonsoft.Json.Linq;
+using YLErp.Core.Helpers;
+using YLErp.DBModels;
+using YLErp.DBModels.Consts;
+using YLErp.DBModels.Enums;
+using YLErp.Modules.UnderlyingModule;
+using YLErp.Plugins.TradeDocGenerator;
+using YLErp.Plugins.TradeDocGenerator.Abstracts;
+
+namespace YLErp.Plugins.GuoLian.DocumentGenerator
+{
+ ///
+ /// 国联证券交易确认书生成器(单个生成)
+ ///
+ internal class TradeConfirmationGenerator : BaseConfirmationGenerator, ITradeConfirmationGenerator
+ {
+ public TradeConfirmationGenerator()
+ : base(ContractTypeEnum.Trade)
+ {
+ }
+
+ ///
+ /// 获取模板文件路径
+ /// 收益互换模板选择逻辑:
+ /// 1. 客户看多/看空:通过持仓方向判断(PositionType: 1=多头/看多,2=空头/看空)
+ /// 2. 标的类型:
+ /// - 债券ETF:.SH后缀且511开头,或.SZ后缀且59开头
+ /// - 现券:其他
+ ///
+ protected override string GetTemplateFilePath()
+ {
+ var trade = Context.Trade;
+
+ if (trade.TradeType == "收益互换")
+ {
+ return GetSwapTemplatePath();
+ }
+ else if (trade.TradeType.Contains("期权"))
+ {
+ // 期权使用期权模板
+ return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template\\option_01.docx");
+ }
+ else
+ {
+ throw new Exception($"不支持的贸易类型: {trade.TradeType}");
+ }
+ }
+
+ ///
+ /// 获取收益互换模板路径
+ ///
+ private string GetSwapTemplatePath()
+ {
+ var trade = Context.Trade;
+ var underlying = Context.GetTradeUnderlying();
+
+ // 判断看多/看空方向
+ bool isLong = IsLongPosition(trade);
+
+ // 判断标的类型(债券ETF vs 现券)
+ bool isEtf = IsBondEtf(underlying?.UnderlyingCode ?? string.Empty);
+
+ // 选择对应模板(使用客户提供的原始文件名)
+ string templateName;
+ if (isLong && !isEtf)
+ templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx";
+ else if (isLong && isEtf)
+ templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx";
+ else if (!isLong && !isEtf)
+ templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx";
+ else
+ templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx";
+
+ return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template", templateName);
+ }
+
+ ///
+ /// 判断是否为多头/看多方向
+ ///
+ private bool IsLongPosition(OtcTradeBase trade)
+ {
+ // 通过BuySell判断:买入=看多,卖出=看空
+ return trade.BuySell == "买入";
+ }
+
+ ///
+ /// 判断是否为债券ETF
+ /// - .SH后缀且511开头 -> 债券ETF
+ /// - .SZ后缀且59开头 -> 债券ETF
+ /// - 其他 -> 现券
+ ///
+ private bool IsBondEtf(string underlyingCode)
+ {
+ if (string.IsNullOrWhiteSpace(underlyingCode))
+ return false;
+
+ underlyingCode = underlyingCode.Trim().ToUpper();
+
+ // 上交所债券ETF:511开头.SH后缀
+ if (underlyingCode.EndsWith(".SH") && underlyingCode.StartsWith("511"))
+ return true;
+
+ // 深交所债券ETF:59开头.SZ后缀
+ if (underlyingCode.EndsWith(".SZ") && underlyingCode.StartsWith("59"))
+ return true;
+
+ return false;
+ }
+
+ ///
+ /// 获取输出文件名
+ ///
+ protected override string GetOutputFileName(string contractNo, string contractIndex)
+ {
+ var trade = Context.Trade;
+ var rule = $"{trade.TradeType}交易确认书_{contractNo}";
+ return $"{rule}.{DocType.ToLower()}";
+ }
+
+ ///
+ /// 获取合同编号
+ ///
+ protected override string GetContractNo(out string contractIndex)
+ {
+ var contractNo = Context.GenerateContractNo(out contractIndex);
+ return contractNo;
+ }
+
+ ///
+ /// 准备视图数据(Word模板数据)
+ ///
+ protected override void PrepareViewData(Dictionary dic)
+ {
+ var trade = Context.Trade;
+ var client = Context.GetClient();
+
+ // 基础信息
+ // 注意:合同编号由基类统一设置,此处不再重复生成
+ dic["交易编号"] = trade.TradeNumber;
+ dic["客户名称"] = client.Name;
+ dic["乙方名称"] = client.Name;
+
+ // 交易日期相关
+ dic["成交日期"] = trade.TradeDate?.ToString("yyyy年M月d日");
+ dic["开始日期"] = trade.StartDate?.ToString("yyyy年M月d日");
+ dic["到期日期"] = trade.ExerciseDate?.ToString("yyyy年M月d日");
+
+ // 根据交易类型填充不同数据
+ switch (trade.TradeType)
+ {
+ case "收益互换":
+ SetSwapData(dic);
+ break;
+ case "香草期权":
+ case "亚式期权":
+ case "障碍期权":
+ SetOptionData(dic);
+ break;
+ default:
+ // 默认基础数据
+ SetBaseData(dic);
+ break;
+ }
+ }
+
+ ///
+ /// 设置收益互换数据
+ ///
+ private void SetSwapData(Dictionary dic)
+ {
+ var trade = Context.Trade;
+ var client = Context.GetClient();
+ var bank = Context.GetClientBankCard(false);
+
+ dic["交易对手方全称"] = client.ClientType != "产品"
+ ? client.Name
+ : $"{client.Manager}作为管理人代表{client.Name}";
+
+ dic["主协议编号"] = client.MainProtocolCode ?? "";
+ dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
+
+ // 银行账户信息
+ dic["户名"] = bank?.ClientName ?? "";
+ dic["银行账号"] = bank?.Card ?? "";
+ dic["开户行"] = bank?.Bank ?? "";
+ dic["大额行号"] = bank?.Payment ?? "";
+
+ // 标的相关的数据可以通过 Context 获取
+ var underlying = Context.GetTradeUnderlying();
+ if (underlying != null)
+ {
+ dic["标的代码"] = underlying.UnderlyingCode;
+ dic["标的名称"] = underlying.UnderlyingName;
+
+ // 标的发行人(从债券信息中获取)
+ var bond = JsonHelper.Deserialize(underlying.ExJson) ?? new UnderlyingBond();
+ dic["参考标的发行人"] = bond.UnderlyingIssuer ?? "";
+ dic["票面利率"] = (bond.CouponRate ?? 0).ToString("N4");
+ dic["参考标的到期日"] = underlying.MaturityDate?.ToString("yyyy年M月d日") ?? "";
+ }
+
+ // 从swap_position获取期初价格、保证金率等信息
+ var swapPositions = Context.GetSwapPositions(trade.id, true);
+ var swapPosition = swapPositions
+ .Where(x => x.PositionType == (int)PositionTypeFlag.Long || x.PositionType == (int)PositionTypeFlag.Short)
+ .FirstOrDefault();
+
+ if (swapPosition != null)
+ {
+ // 期初全价和净价(转换为百分比格式)
+ dic["参考标的期初全价"] = ((double)swapPosition.PosiGrossPrice * 100).ToString("N4");
+ dic["参考标的期初净价"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m) * 100).ToString("N4");
+
+ // 固定收益率(年化)- 债券期初到期收益率
+ dic["固定收益率(年化)"] = swapPosition.InitYtm.HasValue
+ ? ((double)swapPosition.InitYtm.Value * 100).ToString("N4")
+ : "0.0000";
+
+ // 获取客户适用的保证金率
+ var clientMarginRate = UnderlyingHelper.GetApplicableMarginRate(
+ client.id, swapPosition.UnderlyingCode, trade.TradeDate ?? DateTime.Now);
+
+ if (clientMarginRate != null)
+ {
+ dic["初始保障金率"] = ((double)clientMarginRate.init_rate * 100).ToString("N4");
+ dic["维持保障金率"] = ((double)clientMarginRate.maintain_rate * 100).ToString("N4");
+
+ // 期初预付比例和金额
+ dic["期初预付比例"] = ((double)clientMarginRate.init_rate * 100).ToString("N4");
+ dic["期初预付金额"] = ((trade.OriginalStockEqvNotional ?? 0) * (double)clientMarginRate.init_rate).ToString("N2");
+ }
+ else
+ {
+ dic["初始保障金率"] = "0.0000";
+ dic["维持保障金率"] = "0.0000";
+ dic["期初预付比例"] = "0.0000";
+ dic["期初预付金额"] = "0.00";
+ }
+
+ // 期初预付金利率(InterestMode == 初始预付金)
+ var initialMarginPosition = swapPositions
+ .Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金)
+ .FirstOrDefault();
+ dic["期初预付金利率"] = initialMarginPosition != null
+ ? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4")
+ : "0.0000";
+
+ // 利率类型判断(固定/浮动)
+ var interestMargin = swapPositions
+ .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.interest_rest_days != null)
+ .FirstOrDefault();
+ if (interestMargin == null)
+ interestMargin = swapPositions
+ .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && string.IsNullOrWhiteSpace(x.FloatRateUnderlyingCode))
+ .FirstOrDefault();
+ if (interestMargin == null)
+ interestMargin = swapPositions
+ .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode))
+ .FirstOrDefault();
+
+ dic["IsFixed"] = "□";
+ dic["IsFloat"] = "□";
+ if (interestMargin != null)
+ {
+ if (string.IsNullOrEmpty(interestMargin.FloatRateUnderlyingCode))
+ {
+ dic["利率类型"] = "固定利率";
+ dic["IsFixed"] = "☑"; //☑
+ dic["固定利率"] = ((double)interestMargin.InterestRateDefault * 100).ToString("N4");
+ dic["利差"] = "";
+ }
+ else
+ {
+ dic["利率类型"] = "浮动利率";
+ dic["IsFloat"] = "☑";
+ dic["固定利率"] = "";
+ dic["利差"] = ((double)interestMargin.InterestRateDefault * 10000).ToString("N2");
+ dic["重置频率"] = (interestMargin.interest_rest_days ?? 0) + "天";
+ }
+ }
+ else
+ {
+ dic["利率类型"] = "固定利率";
+ dic["固定利率"] = "0.0000";
+ dic["利差"] = "";
+ }
+
+ // 基本费率 = PosiTradingFeePending / 名义本金 * 100
+ var notional = trade.OriginalStockEqvNotional ?? 0;
+ var tradingFee = (double)swapPosition.PosiTradingFeePending;
+ var basicFeeRate = notional == 0 ? 0 : tradingFee / notional * 100;
+ dic["基本费率"] = basicFeeRate.ToString("N4");
+
+ // 期初现金交换比例和金额(使用初始预付金数据)
+ dic["期初现金交换比例"] = initialMarginPosition != null
+ ? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4")
+ : "0.0000";
+ dic["期初现金交换金额"] = initialMarginPosition != null
+ ? ((double)initialMarginPosition.InterestPrincipalFix).ToString("N2")
+ : "0.00";
+ }
+ else
+ {
+ dic["参考标的期初全价"] = "0.0000";
+ dic["参考标的期初净价"] = "0.0000";
+ dic["初始保障金率"] = "0.0000";
+ dic["维持保障金率"] = "0.0000";
+ dic["期初预付比例"] = "0.0000";
+ dic["期初预付金额"] = "0.00";
+ dic["期初预付金利率"] = "0.0000";
+ dic["利率类型"] = "固定利率";
+ dic["固定利率"] = "0.0000";
+ dic["利差"] = "";
+ dic["基本费率"] = "0.0000";
+ dic["期初现金交换比例"] = "0.0000";
+ dic["期初现金交换金额"] = "0.00";
+ }
+
+ // 参考标的券面总额(名义本金)
+ dic["参考标的券面总额"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
+
+ // 参考标的证券全称和参考标的名义份额(复用上方已声明的bond)
+ dic["参考标的证券全称"] = underlying != null
+ ? (JsonHelper.Deserialize(underlying.ExJson)?.UnderlyingFullName ?? underlying.UnderlyingName)
+ : "";
+ dic["参考标的名义份额"] = swapPosition != null
+ ? ((double)swapPosition.PosiQuantity).ToString("N2")
+ : "0.00";
+
+ // 乙方联系人信息
+ var clientDuties = Context.GetClientDuties();
+ var contact = clientDuties.FirstOrDefault();
+ dic["联系人"] = contact?.ContactName ?? "";
+ dic["电子邮件"] = contact?.Email ?? "";
+ dic["电话"] = contact?.PhoneNumber ?? "";
+
+ // 表格数据示例
+ var table1 = new JArray();
+ var row = new JObject
+ {
+ ["序号"] = 1,
+ ["标的代码"] = underlying?.UnderlyingCode ?? "",
+ ["标的名称"] = underlying?.UnderlyingName ?? "",
+ ["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00",
+ ["成交日期"] = trade.TradeDate?.ToString("yyyy年M月d日"),
+ ["到期日期"] = trade.ExerciseDate?.ToString("yyyy年M月d日")
+ };
+ table1.Add(row);
+ dic["table1"] = table1;
+ }
+
+ ///
+ /// 设置期权数据
+ ///
+ private void SetOptionData(Dictionary dic)
+ {
+ var trade = Context.Trade;
+ var client = Context.GetClient();
+ var underlying = Context.GetTradeUnderlying();
+
+ dic["期权类型"] = trade.OptionType;
+ dic["买卖方向"] = trade.BuySell;
+ dic["行权价格"] = trade.Strike?.ToString("N4") ?? "0.0000";
+ dic["名义本金"] = trade.StockEqvNotional.ToString("N2");
+ dic["期权费"] = trade.TradePrice?.ToString("N2") ?? "0.00";
+ dic["期初价格"] = trade.SpotPrice?.ToString("N4") ?? "0.0000";
+
+ if (underlying != null)
+ {
+ dic["标的代码"] = underlying.UnderlyingCode;
+ dic["标的名称"] = underlying.UnderlyingName;
+ dic["报价单位"] = underlying.QuoteUnitString ?? "元";
+ }
+
+ // 美式/欧式
+ dic["行权方式"] = trade.ExerciseMode == "American" ? "美式" : "欧式";
+ }
+
+ ///
+ /// 设置基础数据
+ ///
+ private void SetBaseData(Dictionary dic)
+ {
+ var trade = Context.Trade;
+ var client = Context.GetClient();
+
+ dic["交易类型"] = trade.TradeType;
+ dic["客户编号"] = client.Number;
+ dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
+ }
+ }
+}
diff --git a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeSettleBillGenerator.cs b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeSettleBillGenerator.cs
new file mode 100644
index 00000000..054d3fc5
--- /dev/null
+++ b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeSettleBillGenerator.cs
@@ -0,0 +1,122 @@
+using Newtonsoft.Json.Linq;
+using YLErp.DBModels;
+using YLErp.Models;
+using YLErp.Plugins.TradeDocGenerator;
+using YLErp.Plugins.TradeDocGenerator.Abstracts;
+
+namespace YLErp.Plugins.GuoLian.DocumentGenerator
+{
+ ///
+ /// 国联证券结算单生成器
+ ///
+ internal class TradeSettleBillGenerator : BaseSettleBillGenerator, ITradeSettleBillGenerator
+ {
+ protected override string GetContractNo(out string contractIndex)
+ {
+ var contractNo = Context.GenerateContractNo(out contractIndex);
+ return contractNo;
+ }
+
+ ///
+ /// 获取模板文件路径
+ ///
+ protected override string GetTemplateFilePath()
+ {
+ var templatePath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\settlement_template\\settle_01.docx");
+ return templatePath;
+ }
+
+ ///
+ /// 获取输出文件名
+ ///
+ protected override string GetOutputFileName(string contractNo, string contractIndex)
+ {
+ var client = Context.GetClient();
+ var valueDate = Context.SystemValueDate;
+ var fileName = $"结算单_{client.Name}_{valueDate:yyyyMMdd}_{contractIndex}";
+ return $"{fileName}.{DocType.ToLower()}";
+ }
+
+ ///
+ /// 准备Word模板数据
+ ///
+ protected override void PrepareViewData(Dictionary dic)
+ {
+ var client = Context.GetClient();
+ var trade = Context.Trade;
+ var tradeCash = Context.GetTradeCash();
+
+ // 基础信息
+ // 注意:合同编号由基类统一设置,此处不再重复生成
+ dic["客户名称"] = client.Name;
+ dic["乙方名称"] = client.Name;
+ dic["结算日期"] = tradeCash?.ValueDate.ToString("yyyy年M月d日") ?? DateTime.Now.ToString("yyyy年M月d日");
+
+ // 交易信息
+ dic["交易编号"] = trade.TradeNumber;
+ dic["交易类型"] = trade.TradeType;
+ dic["标的代码"] = trade.UnderlyingCode;
+
+ var underlying = Context.GetTradeUnderlying();
+ if (underlying != null)
+ {
+ dic["标的名称"] = underlying.UnderlyingName;
+ }
+
+ // 结算金额相关
+ if (tradeCash != null)
+ {
+ dic["结算金额"] = tradeCash.Amount.ToString("N2");
+ dic["结算金额大写"] = Context.CmycurD(Math.Abs(tradeCash.Amount));
+ dic["结算价格"] = tradeCash.FinalPrice?.ToString("N4") ?? "0.0000";
+ dic["结算比例"] = tradeCash.UnwindPercentRate.HasValue
+ ? (tradeCash.UnwindPercentRate.Value * 100).ToString("N2") + "%"
+ : "0.00%";
+ }
+
+ // 名义本金
+ dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
+
+ // 表格数据
+ var table1 = new JArray();
+ var row = new JObject
+ {
+ ["交易编号"] = trade.TradeNumber,
+ ["交易类型"] = trade.TradeType,
+ ["标的代码"] = trade.UnderlyingCode,
+ ["标的名称"] = underlying?.UnderlyingName ?? "",
+ ["结算金额"] = tradeCash?.Amount.ToString("N2") ?? "0.00"
+ };
+ table1.Add(row);
+ dic["table1"] = table1;
+ }
+
+ ///
+ /// 准备Excel模板数据(如需要)
+ ///
+ protected override void PrepareViewData(Dictionary dic)
+ {
+ var client = Context.GetClient();
+ var trade = Context.Trade;
+ var tradeCash = Context.GetTradeCash();
+
+ dic["合同编号"] = GetContractNo(out string contractIndex);
+ dic["客户名称"] = client.Name;
+ dic["结算日期"] = tradeCash?.ValueDate.ToString("yyyy-MM-dd") ?? DateTime.Now.ToString("yyyy-MM-dd");
+ dic["交易编号"] = trade.TradeNumber;
+ dic["交易类型"] = trade.TradeType;
+ dic["标的代码"] = trade.UnderlyingCode;
+
+ var underlying = Context.GetTradeUnderlying();
+ dic["标的名称"] = underlying?.UnderlyingName ?? "";
+
+ if (tradeCash != null)
+ {
+ dic["结算金额"] = tradeCash.Amount;
+ dic["结算价格"] = tradeCash.FinalPrice ?? 0;
+ }
+
+ dic["名义本金"] = trade.OriginalStockEqvNotional ?? 0;
+ }
+ }
+}
diff --git a/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeUnwindReportGenerator.cs b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeUnwindReportGenerator.cs
new file mode 100644
index 00000000..19d2bda7
--- /dev/null
+++ b/Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeUnwindReportGenerator.cs
@@ -0,0 +1,142 @@
+using Newtonsoft.Json.Linq;
+using YLErp.DBModels;
+using YLErp.Plugins.TradeDocGenerator;
+using YLErp.Plugins.TradeDocGenerator.Abstracts;
+
+namespace YLErp.Plugins.GuoLian.DocumentGenerator
+{
+ ///
+ /// 国联证券了结报告生成器
+ ///
+ internal class TradeUnwindReportGenerator : BaseUnwindReportGenerator, ITradeUnwindReportGenerator
+ {
+ protected override string GetContractNo(out string contractIndex)
+ {
+ var contractNo = Context.GenerateContractNo(out contractIndex);
+ return contractNo;
+ }
+
+ ///
+ /// 准备Word模板数据
+ ///
+ protected override void PrepareViewData(Dictionary dic)
+ {
+ var table1 = new JArray();
+ var trade = Context.Trade;
+ var client = Context.GetClient();
+ var valueTimeList = new List();
+ var underlying = Context.GetTradeUnderlying();
+ var tradeCash = Context.GetTradeCash();
+ var tradeList = Context.Trades.Union(Context.SubTrades).ToList();
+
+ foreach (var item in Context.Trades)
+ {
+ foreach (var tradeCashItem in Context.TradeCashes[item.id])
+ {
+ var settlementPrice = (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value
+ ? tradeCashItem.UnwindPricePercentRate.OtcFormatPercent()
+ : tradeCashItem.UnwindPrice.OtcFormatMoney());
+
+ var row = new JObject();
+ var currentTrade = tradeList.Find(o => o.id == tradeCashItem.TradeId);
+
+ row["交易编号"] = currentTrade?.TradeNumber ?? "";
+
+ // 买卖方向处理
+ switch (item.BuySell)
+ {
+ case "买入":
+ row["买方"] = (trade.OpponentRole == null || trade.OpponentRole == "乙方") ? "甲方" : "乙方";
+ break;
+ case "卖出":
+ row["买方"] = (trade.OpponentRole == null || trade.OpponentRole == "乙方") ? "乙方" : "甲方";
+ break;
+ default:
+ row["买方"] = "";
+ break;
+ }
+
+ row["交易日期"] = currentTrade?.TradeDate?.ToString("yyyy/MM/dd") ?? "";
+ row["开始日期"] = $"{currentTrade?.StartDate:yyyy/MM/dd}-{currentTrade?.ExerciseDate:yyyy/MM/dd}";
+ row["名义本金"] = currentTrade?.OriginalStockEqvNotional.OtcFormatMoney() ?? "0.00";
+ row["标的代码"] = currentTrade?.UnderlyingCode ?? "";
+ row["期权类型"] = currentTrade?.TradeType ?? "";
+ row["期初价格"] = currentTrade?.TradePrice.OtcFormatMoney() ?? "0.00";
+ row["结算日期"] = tradeCashItem.ValueDate.ToString("yyyy/MM/dd");
+ row["结算价格"] = tradeCashItem.FinalPrice.OtcFormatMoney();
+ row["结算模块"] = (tradeCashItem.UnwindPercentRate * currentTrade?.OriginalNotional * underlying?.CountRatio).OtcFormatMoney() ?? "0.00";
+ row["结算单价"] = settlementPrice;
+
+ var amountQrs = -tradeCashItem.Amount;
+ var amountQrs2 = -(tradeCashItem.Amount + (currentTrade?.TradePrice ?? 0) * (tradeCashItem.UnwindPercentRate ?? 0) * ((currentTrade?.BuySell == "卖出") ? 1 : -1));
+
+ row["结算期权费"] = amountQrs.OtcFormatMoney();
+ row["结算金额"] = amountQrs2.OtcFormatMoney();
+
+ table1.Add(row);
+ valueTimeList.Add(tradeCashItem.ValueDate);
+ }
+ }
+
+ // 合同编号由基类统一设置,此处不再重复生成
+ dic["table1"] = table1;
+ dic["合同日期"] = DateTime.Now.ToString("yyyy/MM/dd");
+ dic["了结日期1"] = valueTimeList.Min().ToString("yyyy/MM/dd");
+ dic["了结日期2"] = valueTimeList.Max().ToString("yyyy/MM/dd");
+
+ // 甲乙方信息
+ var ownerInfo = Context.GetOwner_info("默认");
+ dic["客户名称"] = client?.Name ?? "";
+ dic["公司名称"] = ownerInfo?.FullName ?? "";
+
+ if (trade.OpponentRole == null || trade.OpponentRole == "乙方")
+ {
+ dic["甲方"] = ownerInfo?.FullName ?? "";
+ dic["乙方"] = client?.Name ?? "";
+ }
+ else
+ {
+ dic["甲方"] = client?.Name ?? "";
+ dic["乙方"] = ownerInfo?.FullName ?? "";
+ }
+ }
+
+ ///
+ /// 获取模板文件路径
+ ///
+ protected override string GetTemplateFilePath()
+ {
+ var templatePath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\settlement_template\\unwind_01.docx");
+ return templatePath;
+ }
+
+ ///
+ /// 获取输出文件名
+ ///
+ protected override string GetOutputFileName(string contractNo, string contractIndex)
+ {
+ var client = Context.GetClient();
+ var trade = Context.Trade;
+ var tradeList = Context.Trades.Union(Context.SubTrades).ToList();
+ var ownerInfo = Context.GetOwner_info("默认") ?? new Owner_info();
+
+ var maxTradeDate = tradeList.Max(m => m.TradeDate);
+ var minTradeDate = tradeList.Min(m => m.TradeDate);
+
+ string date;
+ if (maxTradeDate == minTradeDate)
+ {
+ date = trade.TradeDate?.ToString("yyyyMMdd") ?? DateTime.Now.ToString("yyyyMMdd");
+ }
+ else
+ {
+ date = $"{minTradeDate:yyyyMMdd}-{maxTradeDate:yyyyMMdd}";
+ }
+
+ var fullName = ownerInfo?.FullName ?? "";
+ var rule = $"{fullName} {trade.TradeType}结算确认书 {date}";
+ Context.GetcontractIndex(rule, out var contractIndex2);
+ return $"{rule}-{contractIndex2}.{DocType.ToLower()}";
+ }
+ }
+}
diff --git a/Plugins/YLErp.Plugins.GuoLian/YLErp.Plugins.GuoLian.csproj b/Plugins/YLErp.Plugins.GuoLian/YLErp.Plugins.GuoLian.csproj
new file mode 100644
index 00000000..eb416ec0
--- /dev/null
+++ b/Plugins/YLErp.Plugins.GuoLian/YLErp.Plugins.GuoLian.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net6.0
+ enable
+ enable
+ ..\build\GuoLian\
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+ PreserveNewest
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/YLErpDAL/CustomizedBizLogic/BizLogicGuoLian.cs b/YLErpDAL/CustomizedBizLogic/BizLogicGuoLian.cs
new file mode 100644
index 00000000..14301583
--- /dev/null
+++ b/YLErpDAL/CustomizedBizLogic/BizLogicGuoLian.cs
@@ -0,0 +1,124 @@
+using YieldChain.Helpers;
+using YLErp.BLL;
+using YLErp.DBModels;
+using YLErp.Modules;
+
+namespace YLErp.CustomizedBizLogic
+{
+ ///
+ /// 国联证券业务逻辑
+ ///
+ public class BizLogicGuoLian : BizLogicDefault
+ {
+ ///
+ /// 在交易确认前生成交易编号
+ /// 格式:GL-{yyyyMMdd}-{序号}
+ ///
+ public override string GenerateTradeNumberBeforeConfirm(TradeNumberReqModel reqModel, YLContext db)
+ {
+ if (reqModel is null)
+ {
+ throw new ArgumentNullException(nameof(reqModel));
+ }
+
+ // 对于对冲交易、场内交易等没有设置客户的
+ if (reqModel.clientId == 0)
+ {
+ return DateTime.Now.ToString("yyyyMMddHHmmssfff");
+ }
+
+ // 国联格式:GL-{yyyyMMdd}-{4位序号}
+ var leftPart = $"GL-{reqModel.trade.TradeDate:yyyyMMdd}-";
+ var fullSize = leftPart.Length + 4;
+
+ // 获取数据库该用户最大的交易编号记录
+ var tradeNumber = db.trade.Where(x =>
+ x.TradeNumber.StartsWith(leftPart) &&
+ x.TradeNumber.Length == fullSize &&
+ x.ValidState != ConsGlobal.InValid)
+ .OrderByDescending(x => x.TradeNumber)
+ .Select(n => n.TradeNumber)
+ .FirstOrDefault() ?? string.Empty;
+
+ if (reqModel.importTrades != null && reqModel.importTrades.Any())
+ {
+ // 获取导入交易中该客户最大的交易编号记录
+ var importTradeNumber = reqModel.importTrades
+ .Where(x => x.TradeNumber.StartsWith(leftPart) && x.TradeNumber.Length == fullSize)
+ .OrderByDescending(x => x.TradeNumber)
+ .Select(n => n.TradeNumber)
+ .FirstOrDefault() ?? string.Empty;
+ tradeNumber = tradeNumber.CompareTo(importTradeNumber) > 0 ? tradeNumber : importTradeNumber;
+ }
+
+ // 获取总的该用户最大的交易编号记录
+ int startNumber = string.IsNullOrWhiteSpace(tradeNumber)
+ ? 1
+ : (int.Parse(tradeNumber.Substring(leftPart.Length)) + 1);
+
+ return GetTradeNumber(startNumber, leftPart, db);
+ }
+
+ ///
+ /// 获取可用的交易编号(循环实现,避免递归栈溢出)
+ ///
+ private string GetTradeNumber(int startNumber, string leftPart, YLContext db)
+ {
+ for (int newNumber = startNumber; newNumber < 10000; newNumber++)
+ {
+ var candidate = leftPart + newNumber.ToString("D4");
+ var childTradeNumberPart = candidate + "-";
+
+ // 检查是否存在以该编号开头的子交易
+ bool exists = db.trade.Any(x =>
+ EF.Functions.Like(x.TradeNumber, $"{childTradeNumberPart}%") &&
+ x.ValidState != ConsGlobal.InValid);
+
+ if (!exists)
+ {
+ return candidate;
+ }
+ }
+
+ throw new Exception("该客户交易编号已达到最大限制9999,生成交易编号失败");
+ }
+
+ ///
+ /// 获取交易确认书前缀
+ ///
+ public override string GetTradeContractPrefix(string tradeNumber)
+ {
+ // 国联证券确认书前缀
+ return "GL-";
+ }
+
+ ///
+ /// 生成客户编号
+ /// 格式:{年份后两位}{3位序号}{拼音首字母}
+ ///
+ public override void GenerateClientCode(Client clientObj, IQueryable clientDbQuery)
+ {
+ if (string.IsNullOrWhiteSpace(clientObj.Number))
+ {
+ var firstPY = PingYinHelper.GetFirstPinYin_Enhance(clientObj.Name);
+ var year = DateTime.Now.ToString("yy");
+ var result = "";
+ lock (_clientCodeLock)
+ {
+ var existingClientCodes = clientDbQuery
+ .Where(x => x.Number.StartsWith(year))
+ .Select(x => x.Number)
+ .ToList();
+ var number = existingClientCodes.Count();
+ do
+ {
+ number++;
+ result = year + number.ToString("000");
+ } while (existingClientCodes.Any(O => O.StartsWith(result)));
+
+ clientObj.Number = result + firstPY;
+ }
+ }
+ }
+ }
+}
diff --git a/YLErpDAL/CustomizedBizLogic/BizLogicSingleton.cs b/YLErpDAL/CustomizedBizLogic/BizLogicSingleton.cs
index db07ae5e..c4960c2f 100644
--- a/YLErpDAL/CustomizedBizLogic/BizLogicSingleton.cs
+++ b/YLErpDAL/CustomizedBizLogic/BizLogicSingleton.cs
@@ -95,6 +95,8 @@ namespace YLErp.CustomizedBizLogic
return new BizLogicShanXiGuShou();
case CompanyEnum.浙商:
return new BizLogicZheShang();
+ case CompanyEnum.国联:
+ return new BizLogicGuoLian();
case CompanyEnum.中基石化: //暂时:确认书模式 是国君的
default:
return new BizLogicGTJA();