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