From 9be15621f58a7b0f2866ca5e405e6fff9aab0eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=94=A6=E9=BA=9F=20=E7=8E=8B?= Date: Tue, 24 Mar 2026 13:41:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=88=90=E4=BB=85=E6=9C=9F?= =?UTF-8?q?=E6=9D=83=E7=A1=AE=E8=AE=A4=E4=B9=A6=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConfirmationGenerateService.cs | 6 +- YLErpWeb/App_Data/FunctionRight.xml | 1 + YLErpWeb/App_Data/Menus.txt | 1 + YLErpWeb/Common/UserInfoRight.cs | 1 + .../Controllers/TradeConfirmBookController.cs | 99 +++++++++++++++++ YLErpWeb/Controllers/clientController.cs | 16 +-- .../Views/TradeConfirmBookOption/Index.cshtml | 102 ++++++++++++++++++ .../Scripts/app/trade/TradeConfirmBook.js | 2 +- 8 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 YLErpWeb/Views/TradeConfirmBookOption/Index.cshtml diff --git a/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateService.cs b/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateService.cs index 004669e8..f8b0001d 100644 --- a/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateService.cs +++ b/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateService.cs @@ -177,6 +177,9 @@ namespace YLErp.Modules.TradeModule.DocGenerateModule { foreach (var tradeContractGroup in tradeDic.Values) { + //判断是否为期权交易(非收益互换的交易强制走默认逻辑V1,不走插件) + var isOptionTrade = tradeContractGroup.trade.TradeType != "收益互换"; + //插件版本(期权交易强制使用V1默认逻辑) if ((tradeContractGroup.trade.TradeType != "结构化交易" && tradeContractGroup.trade.TradeType != "收益互换" && tradeContractGroup.trade_cash == null) || (tradeContractGroup.trade.TradeType == "结构化交易" && tradeContractGroup.trade.IsGroup == 0 && tradeContractGroup.trade.SubTrades.Any(O => O.trade_cash == null))) { @@ -185,8 +188,7 @@ namespace YLErp.Modules.TradeModule.DocGenerateModule ErrorMessage = "缺少现金交割记录" }); } - //插件版本 - else if (generatorV2 != null) + else if (generatorV2 != null && !isOptionTrade) { var result = GenerateSingleV2(generatorV2, tradeContractGroup, docType); resultList.Add(result); diff --git a/YLErpWeb/App_Data/FunctionRight.xml b/YLErpWeb/App_Data/FunctionRight.xml index 0186f1f5..1a90daf6 100644 --- a/YLErpWeb/App_Data/FunctionRight.xml +++ b/YLErpWeb/App_Data/FunctionRight.xml @@ -79,6 +79,7 @@ + diff --git a/YLErpWeb/App_Data/Menus.txt b/YLErpWeb/App_Data/Menus.txt index 8acaf118..2af376bc 100644 --- a/YLErpWeb/App_Data/Menus.txt +++ b/YLErpWeb/App_Data/Menus.txt @@ -42,6 +42,7 @@ {Name:"资金审批",Rights:["结算管理-资金审批页面"],Url:"entryexit/entryexitApproval"}, {Name:"收盘操作",Rights:["结算管理-收盘操作"],Url:"eod_trade_value/eodExec"}, {Name:"交易确认书",Rights:["结算管理-交易确认书"],Url:"TradeConfirmBook/ConfirmBookListByClient"}, + {Name:"交易确认书_期权",Rights:["结算管理-交易确认书-期权"],Url:"TradeConfirmBook/IndexOption"}, {Name:"结算确认书",Rights:["结算管理-结算确认书"],Url:"swaptrade2/tradeEndConfirmList"}, {Name:"每日估值报告",Rights:["结算管理-每日估值报告"],Url:"clientbalance/TradeMarketReport"}, {Name:"其他报告",Rights:["结算管理-其他报告"],Url:"supervise_report/ExtendReport"}, diff --git a/YLErpWeb/Common/UserInfoRight.cs b/YLErpWeb/Common/UserInfoRight.cs index 14afa387..1813068b 100644 --- a/YLErpWeb/Common/UserInfoRight.cs +++ b/YLErpWeb/Common/UserInfoRight.cs @@ -636,6 +636,7 @@ namespace YLErp.Web public bool 结算管理_交易确认书生成 => HasRight("结算管理-交易确认书生成"); public bool 结算管理_交易确认书查看 => HasRight("结算管理-交易确认书"); public bool 结算管理_交易确认书邮件发送 => HasRight("结算管理-交易确认书邮件发送"); + public bool 结算管理_交易确认书期权查看 => HasRight("结算管理-交易确认书-期权"); public bool 结算管理_结算确认书生成 => HasRight("结算管理-结算确认书生成"); public bool 结算管理_结算确认书重置 => HasRight("结算管理-结算确认书重置"); public bool 结算管理_结算确认书邮件发送 => HasRight("结算管理-结算确认书邮件发送"); diff --git a/YLErpWeb/Controllers/TradeConfirmBookController.cs b/YLErpWeb/Controllers/TradeConfirmBookController.cs index f6cab407..058e737b 100644 --- a/YLErpWeb/Controllers/TradeConfirmBookController.cs +++ b/YLErpWeb/Controllers/TradeConfirmBookController.cs @@ -64,6 +64,37 @@ namespace YLErp.Web.Controllers return View(); } + // GET - 期权交易确认书页面 + [MyAuthorize("结算管理-交易确认书-期权")] + public ActionResult IndexOption() + { + ViewBag.ValueDate = valuedateBLL.ValueDate.ToString("yyyy-MM-dd"); + if (GlobalConfig.SelectConfirmationBookTemplate) + { + try + { + if (string.IsNullOrWhiteSpace(GlobalConfig.PluginFolder)) + { throw new Exception("未找到插件路径"); } + var dirPath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs", ContractTypeEnum.Trade); + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + var paths = Directory.GetFiles(dirPath, "*.docx"); + for (var i = 0; i < paths.Length; i++) + { + paths[i] = Path.GetFileName(paths[i]); + } + ViewBag.TemplateNames = paths; + } + catch (Exception ex) + { + LogFactory.GetLogger("SelectConfirmationBookTemplate").Error(ex); + } + } + return View("~/Views/TradeConfirmBookOption/Index.cshtml"); + } + public JsonResult ToPdf(string enid) { var intid = DataProtectHelper.DecryptInt(enid); @@ -87,6 +118,74 @@ namespace YLErp.Web.Controllers return Json(result); } + /// + /// 期权确认书查询(排除收益互换) + /// + public JsonResult OptionQuery(TradeReq req) + { + req.AssetIdList = AssetUnitModel.IntersectAssetUnits(req.AssetIdGroupList, req.AssetIdList).ToList(); + req.UserAssets = CurUser.GetAssetUnitIds(); + req.UserClients = CurUser.GetClientIdsByCurUser(CurUser.交易管理_查看所有交易); + req.CurUserTradeIds = CurUser.GetTradeIdsByCurUser(); + // 排除收益互换 + req.NotInTradeTypes = new List { "收益互换" }; + var sList = new tradeBLL().SearchListForTradeConfirm(req); + var rowList = sList.rows; + // 增加交易确认书编号 + if (rowList != null && rowList.Any()) + { + var tradeIds = rowList.Select(x => x.id).ToList(); + var tradeContractCodeList = (from tradeContractR in yldb.trade_contract_r + join tradeContractDoc in (from tradeContractDocTemp in yldb.trade_contract_document where ContractTypeEnum.Trade == tradeContractDocTemp.Type select tradeContractDocTemp) + on tradeContractR.ContractCode equals tradeContractDoc.Code into tradeContractDoc1 + from tradeContractDoc2 in tradeContractDoc1.DefaultIfEmpty() + where tradeIds.Contains(tradeContractR.TradeId) && ContractTypeEnum.Trade == tradeContractR.Type && tradeContractR.Type == tradeContractDoc2.Type && tradeContractR.IsValid + select new + { + tradeContractR.id, + tradeContractR.TradeId, + tradeContractR.ContractCode, + tradeContractR.send_email_result, + tradeContractDoc = tradeContractDoc2 + }).ToList(); + + foreach (var trade in rowList) + { + if (trade.TradeType == "合成价差期权") + { + trade.SyntheticUnderlyingTipsInfo = synthetic_underlyingBLL.GetUnderlyingTipsInfo(trade.UnderlyingCode); + } + var tradeContractinfo = tradeContractCodeList.FirstOrDefault(t => t.TradeId == trade.id); + trade.MetaDic["send_email_result"] = ""; + if (tradeContractinfo != null) + { + trade.ContractCode = tradeContractinfo.ContractCode; + if (null != tradeContractinfo.tradeContractDoc) + { + trade.MetaDic["ContractEncryptId"] = tradeContractinfo.tradeContractDoc.EncryptId; + trade.MetaDic["ContractRId"] = tradeContractinfo.id.ToString(); + trade.MetaDic["ContractDocUrl"] = tradeContractinfo.tradeContractDoc.RelativePath; + trade.MetaDic["HasGeneratedConfirmBook"] = "True"; + trade.MetaDic["send_email_result"] = tradeContractinfo.send_email_result; + } + else + { + trade.MetaDic["ContractEncryptId"] = ""; + trade.MetaDic["ContractRId"] = tradeContractinfo.id.ToString(); + trade.MetaDic["HasGeneratedConfirmBook"] = "False"; + } + } + else + { + trade.MetaDic["ContractEncryptId"] = ""; + trade.MetaDic["ContractRId"] = ""; + trade.MetaDic["HasGeneratedConfirmBook"] = "False"; + } + } + } + return Json(sList); + } + public async Task SentMail(string enid, List reciver) { var result = await DoSendMailSywg(enid, reciver); diff --git a/YLErpWeb/Controllers/clientController.cs b/YLErpWeb/Controllers/clientController.cs index 29bb697a..93f8c645 100644 --- a/YLErpWeb/Controllers/clientController.cs +++ b/YLErpWeb/Controllers/clientController.cs @@ -1464,6 +1464,8 @@ namespace YLErp.Web.Controllers for (var i = 0; i < Request.Form.Files.Count; i++) { var file = Request.Form.Files[i]; + // 安全处理:使用 Path.GetFileName 提取纯文件名,防止路径遍历攻击 + var safeFileName = Path.GetFileName(file.FileName); var protocolTypes = new List() { "主协议附件(PDF)", "补充协议附件(PDF)", "履约协议附件(PDF)", "代签产品附件(PDF)" }; var filePath = isSuitably == "true" ? $"~/App_Docs/ClientFiles/SuitablyFiles/{clientPath}" : $"~/App_Docs/ClientFiles/{clientPath}"; if (!Directory.Exists(filePath)) @@ -1471,13 +1473,13 @@ namespace YLErp.Web.Controllers Directory.CreateDirectory(Server.MapPath(filePath)); } - var oldName = $"{fileDesc}-{file.FileName}"; + var oldName = $"{fileDesc}-{safeFileName}"; //check fileName protocol if (!string.IsNullOrWhiteSpace(ProtocolNumber)) { var OtherClientFilesName = clientDB.client_file.Where(o => o.ApprovalOrder < 1 && o.ClientId != client.id && o.ProtocolNumber != null && o.ProtocolNumber != "" && o.IsValid).Select(o => new { o.FileName, o.FileDesc }).ToList(); - if (OtherClientFilesName.Where(o => o.FileName.ToUpper() == oldName.ToUpper() || o.FileName.ToUpper() == file.FileName.ToUpper()).Any()) + if (OtherClientFilesName.Where(o => o.FileName.ToUpper() == oldName.ToUpper() || o.FileName.ToUpper() == safeFileName.ToUpper()).Any()) { return JsonError("其他客户有协议使用的文件与该文件重名,请修改文件名后上传。"); } @@ -1491,7 +1493,7 @@ namespace YLErp.Web.Controllers { return JsonError("该协议编号已存在,且已废弃删除,请修改协议编号后上传。"); } - if (clientDB.client_file.Where(O => O.ApprovalOrder < 1 && (O.FileName == file.FileName || O.FileName == oldName) && O.ProtocolNumber != null && O.ProtocolNumber != "" && O.ProtocolNumber != ProtocolNumber && O.IsValid).Any()) + if (clientDB.client_file.Where(O => O.ApprovalOrder < 1 && (O.FileName == safeFileName || O.FileName == oldName) && O.ProtocolNumber != null && O.ProtocolNumber != "" && O.ProtocolNumber != ProtocolNumber && O.IsValid).Any()) { return JsonError("该文件已属于其他协议,请上传未使用的文件。"); } @@ -1554,22 +1556,22 @@ namespace YLErp.Web.Controllers clientFile.MainProtocolNumber = MainProtocolNumber; } - clientFile.FileName = file.FileName; + clientFile.FileName = safeFileName; clientFile.FileDesc = fileDesc; clientFile.ClientId = client.id; clientFile.FileDesc = fileDesc; clientFile.OptId = CurUser.UserId; clientFile.OptName = CurUser.UserName; - clientFile.OptDate = optDateTime; // DateTime.Now + clientFile.OptDate = optDateTime; // DateTime.Now clientFile.ProtocolNumber = ProtocolNumber; clientFile.HasSent = false; - clientFile.FilePath = Path.Combine(filePath.TrimStart('~'), file.FileName); + clientFile.FilePath = Path.Combine(filePath.TrimStart('~'), safeFileName); clientFile.IsValid = true; var ret = new ClientFileService(CurUser).UploadFile(clientFile); if (string.IsNullOrWhiteSpace(ret) || ret == "修改成功") { - file.SaveAs(Path.Combine(Server.MapPath(filePath), file.FileName)); + file.SaveAs(Path.Combine(Server.MapPath(filePath), safeFileName)); LogFactory.GetLogger().Info($"客户{client.Name}(id:{client.id})上传文件({file.FileName}),真实上传时间:{DateTime.Now},客户选择的日期:{optDateTime}"); if (ret == "修改成功") { diff --git a/YLErpWeb/Views/TradeConfirmBookOption/Index.cshtml b/YLErpWeb/Views/TradeConfirmBookOption/Index.cshtml new file mode 100644 index 00000000..8d27bc0f --- /dev/null +++ b/YLErpWeb/Views/TradeConfirmBookOption/Index.cshtml @@ -0,0 +1,102 @@ +@{ + ViewBag.Title = "交易确认书_期权"; + Layout = "~/Views/Shared/_MainLayout.cshtml"; + + var pageObj = new + { + isGDGZ = YLErp.PS.Config.Company == CompanyEnum.光大光子, + isGTJA = YLErp.PS.Config.Company == CompanyEnum.国泰君安, + isHaiTong = YLErp.PS.Config.Company == YLErp.Configuration.CompanyEnum.海通, + isRuiDa = YLErp.PS.Config.Company == YLErp.Configuration.CompanyEnum.瑞达, + isBoHai = YLErp.PS.Config.Company == YLErp.Configuration.CompanyEnum.渤海, + isWuChan = YLErp.PS.Config.Company == YLErp.Configuration.CompanyEnum.物产中大, + ConfirmBookStampStatus = PS.Config.ErpElement.ConfirmBookStampStatus, + valuedate = valuedateBLL.ValueDate.ToString("yyyy-MM-dd"), + configcolumn = configcolumn_data.交易确认书, + SettleDocModeMultiple = valuedate.SettleDocModeMultiple.Equals(valuedateBLL.SystemDate.SettleDocMode), + BrokerMode = YLErp.PS.Config.ComponentVersion == YLErp.Configuration.ComponentVersion.Broker, + MarketMaker = YLErp.PS.Config.ComponentVersion == YLErp.Configuration.ComponentVersion.MarketMaker, + reqTradeStatus = $"{ConsTrade.确认成交},{ConsTrade.已到期},{ConsTrade.已平仓},{ConsTrade.已执行}", + SelectConfirmationBookTemplate = GlobalConfig.SelectConfirmationBookTemplate == true, + templateNames = ViewBag.TemplateNames, + //初始化时使用空页面(清楚预置查询条件及初始不加载数据) + initEmptyView = YLErp.PS.Config.Company == YLErp.Configuration.CompanyEnum.弘业, + IsAutoSealAndUploadFiles = YLErp.PS.GetErpConfig().IsAutoSealAndUploadFiles, + IsZheQi = PS.Config.Is浙期, + IsAutoSealAfterGeneratedBook = PS.GetErpConfig().IsAutoSealAfterGeneratedBook, + canGenerate = CurUser.结算管理_交易确认书生成, + canSendEmail = CurUser.结算管理_交易确认书邮件发送, + // 标识为期权确认书页面 + isOptionPage = true + }; + // 期权交易类型:排除收益互换、远期、掉期等非期权类型 + var tradeTypes = ConsTrade.AllTradeTypes + .Except(new List { "场内期权", "股票", "收益互换", "远期", "掉期", "商品期货", "商品现货" }) + .Concat(new List { "黑箱结构" }); +} + +@section CSS{ + +} +@section JS{ + + + + +} + +
+ @Html.ShortInput("TradeNumber", "交易编号") + @Html.ShortInput("ContractCode", "合约编号") + @Html.MyAceDropdownInput("ClientId", "客户名称", ClientDataModel.GetAllClient()) + @Html.MyAceDropdownInput("AssetId", "簿记账户", AssetunitController.GetClientassetunit()) + @Html.MyAceDropdownInput("GroupId", "簿记账户组", AssetUnitModel.GetAllAssetUnitGroupItem()) + +
+ 标的代码 + +
+ @Html.MyAceDropdownInput2("BuySell", "交易方向", GlobalData.GetSelectItems(new List { "买入", "卖出" })) + @Html.SearchDateRange("ExerciseDate", "到期日期") + @Html.SearchDateRange("TradeDate", "交易日期") + @Html.MyAceDropdownInput("TradeStatus", "交易状态", GlobalData.TradeStatusAfterConfirm(), true) + + @if (pageObj.IsAutoSealAndUploadFiles){ + @Html.MyAceDropdownInput("SealResults", "用印结果", GlobalData.GetSealResultItems()) + @Html.SearchDateRange("SealDate", "用印日期") + } + + @Html.ShortInput("Comments", "备注") +
+ @MyControls.SearchBtn() + @if (pageObj.canGenerate) + { + @MyControls.Btn("批量生成交易确认书", "BatchCreateContracts()") + } + @MyControls.Btn("批量下载交易确认书", "BatchDownLoadDoc()") + + @if (PS.GetErpConfig().IsAutoSealAndUploadFiles && !PS.GetErpConfig().IsAutoSealAfterGeneratedBook) + { + @MyControls.Btn("批量确认书用印", "BatchSealContracts()") + } +
+ +
+
+ +@Html.Raw(JqGridSimple.OutTable()) + + +@await Html.PartialAsync("/Views/Common/_chooseContractType.cshtml", "sendReport()") diff --git a/YLErpWeb/wwwroot/Scripts/app/trade/TradeConfirmBook.js b/YLErpWeb/wwwroot/Scripts/app/trade/TradeConfirmBook.js index 2674908f..7a24cc74 100644 --- a/YLErpWeb/wwwroot/Scripts/app/trade/TradeConfirmBook.js +++ b/YLErpWeb/wwwroot/Scripts/app/trade/TradeConfirmBook.js @@ -420,7 +420,7 @@ function __initJqGrid() { onJqgridPaging: typeof onJqgridPaging !== 'undefined' ? onJqgridPaging : void 0 }; var grid = jQuery('#listGrid').jqGrid({ - url: '/trade/tradeConfirmEmailQuery', + url: page.isOptionPage ? '/TradeConfirmBook/OptionQuery' : '/trade/tradeConfirmEmailQuery', datatype: page.initEmptyView ? 'local' : 'json', height: 'auto', width: '90%',