using OfficeOpenXml; using System.Text; using YLErp.BLL; using YLErp.Configuration; using YLErp.DBModels.Consts; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Modules.AppModule; using YLErp.Modules.ClientModule; using YLErp.Modules.TradeDalModule; using YLErp.Modules.UnderlyingModule; using YLErp.MsOffice; using YLErp.Office; using YLErp.QdpModule; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace YLErp.Modules.ReportModule { /// /// 清算确认书 /// public class SettleBillService : YLBaseService { public SettleBillService(OptUserInfo userInfo) : base(userInfo) { } /// /// 批量发送清算确认书 /// from tradeController.SendEmailSettleBill /// public string SendEmailSettleBill(TradeReq req) { //获取所有客户信息, 排除掉Broker var predicate = PredicateBuilder.Create(c => c.Name != PS.Config.CompanyFullName); if (!string.IsNullOrWhiteSpace(req.ClientIds)) { var clientIds = DataConvert.ConvertCommaValuesToInt32Array(req.ClientIds); predicate = predicate.And(t => clientIds.Contains(t.id)); } //检查实际行权日区间是否为空 if (!req.UnWindDateStart.HasValue || !req.UnWindDateEnd.HasValue) { throw new ServiceException("发送清算确认书实际行权日期区间不得为空!"); } var contractQuery = DbContext.trade_contract_r.AsQueryable(); //获取当日所有清算确认书 var settleDocList = new List(); if (req.TradeCashIdList.Count > 0) { var tempSettleDocQuery = (from tcb in DbContext.trade_contract_document join tcr in contractQuery on tcb.Code equals tcr.ContractCode join tc in DbContext.trade_cash on tcr.TradeCashId equals tc.id where tcb.Type == ContractTypeEnum.Clearing && !tc.IsDeleted && req.TradeCashIdList.Contains(tcr.TradeCashId.Value) && tcb.Type == tcr.Type && tcr.IsValid select new { tc.ValueDate, tcb }); if (req.SealResults != null) { if (req.SealResults.Contains(2)) { tempSettleDocQuery = tempSettleDocQuery.Where(t => req.SealResults.Contains((int)t.tcb.SealResult) || t.tcb.SealResult == null); } else { tempSettleDocQuery = tempSettleDocQuery.Where(t => req.SealResults.Contains((int)t.tcb.SealResult)); } } var tempSettleDocs = tempSettleDocQuery.ToList(); if (tempSettleDocs.Count == 0) { throw new ServiceException($"已勾选的清算确认书不存在!"); } tempSettleDocs.ForEach(O => { O.tcb.ValueDate = O.ValueDate; settleDocList.Add(O.tcb); }); } else { var tempSettleDocQuery = (from tcb in DbContext.trade_contract_document join tcr in contractQuery on tcb.Code equals tcr.ContractCode join tc in DbContext.trade_cash on tcr.TradeCashId equals tc.id where tc.ValueDate >= req.UnWindDateStart.Value && !tc.IsDeleted && tc.ValueDate <= req.UnWindDateEnd.Value && tcb.Type == ContractTypeEnum.Clearing && tcb.Type == tcr.Type && tcr.IsValid select new { tc.ValueDate, tcb }); if (req.SealResults != null) { if (req.SealResults.Contains(2)) { tempSettleDocQuery = tempSettleDocQuery.Where(t => req.SealResults.Contains((int)t.tcb.SealResult) || t.tcb.SealResult == null); } else { tempSettleDocQuery = tempSettleDocQuery.Where(t => req.SealResults.Contains((int)t.tcb.SealResult)); } } var tempSettleDocs = tempSettleDocQuery.ToList(); if (tempSettleDocs.Count == 0) { throw new ServiceException($"实际行权日区间:{req.UnWindDateStart.Value:yyyy-MM-dd}至{req.UnWindDateEnd.Value:yyyy-MM-dd},清算确认书已用印列表为空!"); } tempSettleDocs.ForEach(O => { O.tcb.ValueDate = O.ValueDate; settleDocList.Add(O.tcb); }); } var tempalte = new Lazy(() => new EmailTemplateService(this).GetData("结算确认书")); var stampTemplate = new Lazy(() => new EmailTemplateService(this).GetData("结算确认书(用印)")); Lazy useTemplate; var DateMsgs = new List(); for (var valueDate = req.UnWindDateStart.Value; DateTime.Compare(req.UnWindDateEnd.Value, valueDate) >= 0; valueDate = valueDate.AddDays(1)) { var theDaySettleDocList = settleDocList.Where(t => t.ValueDate == valueDate).ToList(); //当日没有清算书 跳出 if (!theDaySettleDocList.Any()) { continue; } var buyerIds = theDaySettleDocList.Select(t => t.BuyerId ?? 0).ToHashSet(); var sellerIds = theDaySettleDocList.Select(t => t.SellerId ?? 0).ToHashSet(); var tempPredicate = predicate.And(t => buyerIds.Contains(t.id) || sellerIds.Contains(t.id)); var clientListOn = ClientDataQueryService.GetClientsFromDB(tempPredicate); var errorClients = new List(); var successClients = new List(); var errorMsg = new List(); foreach (var client in clientListOn) { useTemplate = tempalte; var SettleBills = theDaySettleDocList.Where(t => t.SellerId == client.id || t.BuyerId == client.id).ToList(); if (SettleBills.Count > 0) { var fileList = new List(); foreach (var docInfo in SettleBills) { var extension = docInfo.FileType == FileTypeEnum.Pdf ? "pdf" : "docx"; if (PS.Config.ErpElement.ConfirmBookStampStatus) { if (docInfo.AbsoluteStampPath.IsNullOrWhiteSpace()) { fileList.Add(Path.ChangeExtension(docInfo.AbsolutePath, extension)); } else { fileList.Add(docInfo.AbsoluteStampPath); if (!string.IsNullOrEmpty(stampTemplate?.Value?.BodyTemplate)) { useTemplate = stampTemplate; } } } else { fileList.Add(Path.ChangeExtension(docInfo.AbsolutePath, extension)); } } //组织邮件主体 var mailInfo = EmailTemplateService.GenerateMailInfo(useTemplate.Value, new MailInfoRequestModel { ClientName = client.Name, TradeDate = valueDate.ToString("yyyy-MM-dd"), DocNumber = SettleBills.Count > 0 ? SettleBills[0].Code : string.Empty }) ?? new MailInfoResultModel { Body = string.Empty, Title = "清算确认书-" + valueDate.ToString("yyyy-MM-dd") }; // 要向该客户的所有订阅了邮件通知的人员发送邮件 var emails = ClientDataQueryService.GetClientEmails(client.id, false, req.receiver); if (emails == null || !emails.Any()) { errorClients.Add(client.Name); errorMsg.Add(client.Name + "用户没未设置可接受邮件人员信息"); continue; } var sendMail = EmailHelper.SendMail(string.Join(";", emails), mailInfo.Title, mailInfo.Body, mailInfo.IsBodyHtml, fileList.Distinct().ToArray()); if (!string.IsNullOrEmpty(sendMail)) { errorClients.Add(client.Name); if (!errorMsg.Contains(sendMail)) { errorMsg.Add(sendMail); continue; } } successClients.Add(client.Name); } } var DateMsg = valueDate.ToString("yyyyMMdd") + ":"; if (successClients.Count > 0 || errorClients.Count > 0) { if (successClients.Count > 0) { DateMsg += $"成功 -- {string.Join(",", successClients.ToArray())}发送报告成功。"; } if (errorClients.Count > 0) { DateMsg += $"错误 -- {string.Join(",", errorClients.ToArray())}发送报告时出错,{string.Join(",", errorMsg.ToArray())}。"; } DateMsgs.Add(DateMsg); } } //判断是否有发送失败的客户 if (DateMsgs.Any(o => o.Contains("错误"))) { throw new ServiceException($"提示!{string.Join(",", DateMsgs.ToArray())}"); } if (req.TradeCashIdList.Count > 0) { return "已勾选的结算报告发送成功!"; } return $"发送客户[{req.UnWindDateStart.Value:yyyy-MM-dd}]至[{req.UnWindDateEnd.Value:yyyy-MM-dd}]结算报告成功。"; } /// /// 国君批量发送清算确认书 /// from tradeController.SendEmailSettleBill /// public string SendEmailSettleBill_Guojun(TradeReq req) { var trades = new List(); var trade_Cashes = new List(); if (!string.IsNullOrWhiteSpace(req.ClientIds)) { var clientIds = DataConvert.ConvertCommaValuesToInt32Array(req.ClientIds); } //检查实际行权日区间是否为空 if (!req.UnWindDateStart.HasValue || !req.UnWindDateEnd.HasValue) { throw new ServiceException("发送清算确认书实际行权日期区间不得为空!"); } //获取当日所有清算确认书 var settleDocList = new List(); if (req.TradeCashIdList.Count > 0) { var tempSettleDocs = (from tcb in DbContext.trade_contract_document join tcr in DbContext.trade_contract_r on tcb.Code equals tcr.ContractCode join tc in DbContext.trade_cash on tcr.TradeCashId equals tc.id join t in DbContext.trade on tcr.TradeId equals t.id where tcb.Type == ContractTypeEnum.Clearing && !tc.IsDeleted && req.TradeCashIdList.Contains(tcr.TradeCashId.Value) && tcb.Type == tcr.Type && tcr.IsValid select new { tc.ValueDate, tcb, t, tc }).ToList(); if (req.TradeCashIdList.Count != tempSettleDocs.Count) { throw new ServiceException($"请勾选已生成过确认书的交易!"); } tempSettleDocs.ForEach(O => { O.tcb.ValueDate = O.ValueDate; settleDocList.Add(O.tcb); trades.Add(O.t); trade_Cashes.Add(O.tc); }); } else { var tempSettleDocs = (from tcb in DbContext.trade_contract_document join tcr in DbContext.trade_contract_r on tcb.Code equals tcr.ContractCode join tc in DbContext.trade_cash on tcr.TradeCashId equals tc.id join t in DbContext.trade on tcr.TradeId equals t.id where tc.ValueDate >= req.UnWindDateStart.Value && !tc.IsDeleted && tc.ValueDate <= req.UnWindDateEnd.Value && tcb.Type == ContractTypeEnum.Clearing && tcb.Type == tcr.Type && tcr.IsValid select new { tc.ValueDate, tcb, t, tc }).ToList(); if (tempSettleDocs.Count == 0) { throw new ServiceException($"实际行权日区间:{req.UnWindDateStart.Value:yyyy-MM-dd}至{req.UnWindDateEnd.Value:yyyy-MM-dd},清算确认书已用印列表为空!"); } tempSettleDocs.ForEach(O => { O.tcb.ValueDate = O.ValueDate; settleDocList.Add(O.tcb); trades.Add(O.t); trade_Cashes.Add(O.tc); }); } var tempalte = new Lazy(() => new EmailTemplateService(this).GetData("结算确认书")); var DateMsgs = new List(); for (var valueDate = req.UnWindDateStart.Value; DateTime.Compare(req.UnWindDateEnd.Value, valueDate) >= 0; valueDate = valueDate.AddDays(1)) { var theDaySettleDocList = settleDocList.Where(t => t.ValueDate == valueDate).ToList(); //当日没有清算书 跳出 if (!theDaySettleDocList.Any()) { continue; } var ts = (from t in trades join tc in trade_Cashes on t.id equals tc.TradeId select new Datas() { trade = t, tradecash = tc }).ToList(); var buyerIds = theDaySettleDocList.Select(t => t.BuyerId ?? 0).Distinct().ToList(); buyerIds.AddRange(theDaySettleDocList.Select(t => t.SellerId ?? 0).Distinct().ToList()); //var sellerIds = theDaySettleDocList.Select(t => t.SellerId ?? 0).ToHashSet(); buyerIds = buyerIds.FindAll(O => O > 0); var errorClients = new List(); var successClients = new List(); var errorMsg = new List(); var clientListOn = ClientDataQueryService.GetClientsFromDB(PredicateBuilder.Create(t => t.Name != PS.Config.CompanyFullName && buyerIds.Contains(t.id))); foreach (var client in clientListOn) { var SettleBills = theDaySettleDocList.Where(t => t.SellerId == client.id || t.BuyerId == client.id).ToList(); var res = ts.Where(O => O.trade.ClientId == client.id).ToList(); if (SettleBills.Count > 0) { var fileList = new List(); foreach (var docInfo in SettleBills) { if (PS.Config.ErpElement.ConfirmBookStampStatus) { if (docInfo.AbsoluteStampPath.IsNullOrWhiteSpace()) { fileList.Add(Path.ChangeExtension(docInfo.AbsolutePath, "pdf")); } else { fileList.Add(docInfo.AbsoluteStampPath); } } else { fileList.Add(Path.ChangeExtension(docInfo.AbsolutePath, "pdf")); } } var sb = new StringBuilder(); var instrumentTypeArr = new List() { ConsGlobal.InstrumentType.Stock, ConsGlobal.InstrumentType.StockIF }; SendEmailSettleBillHtml(sb, res); //组织邮件主体 var mailInfo = EmailTemplateService.GenerateMailInfo(tempalte.Value, new MailInfoRequestModel { ClientName = client.Name, TradeDate = valueDate.ToString("yyyy-MM-dd"), TradeList = sb.ToString(), DocNumber = SettleBills.Count > 0 ? SettleBills[0].Code : string.Empty }) ?? new MailInfoResultModel { Body = string.Empty, Title = "清算确认书-" + valueDate.ToString("yyyy-MM-dd") }; // 要向该客户的所有订阅了邮件通知的人员发送邮件 var emails = ClientDataQueryService.GetClientEmails(client.id, false, req.receiver); if (emails == null || !emails.Any()) { errorClients.Add(client.Name); errorMsg.Add("用户没未设置可接受邮件人员信息:" + client.Name); continue; } var sendMail = EmailHelper.SendMail(string.Join(";", emails), mailInfo.Title, mailInfo.Body, mailInfo.IsBodyHtml, fileList.Distinct().ToArray()); if (!string.IsNullOrEmpty(sendMail)) { errorClients.Add(client.Name); if (!errorMsg.Contains(sendMail)) { errorMsg.Add(sendMail); continue; } } successClients.Add(client.Name); } } var DateMsg = valueDate.ToString("yyyyMMdd") + ":"; if (successClients.Count > 0 || errorClients.Count > 0) { if (successClients.Count > 0) { DateMsg += $"成功 -- {string.Join(",", successClients.ToArray())}发送报告成功。"; } if (errorClients.Count > 0) { DateMsg += $"错误 -- {string.Join(",", errorClients.ToArray())}发送报告时出错,{string.Join(",", errorMsg.ToArray())}。"; } DateMsgs.Add(DateMsg); } } //判断是否有发送失败的客户 if (DateMsgs.Any(o => o.Contains("错误"))) { throw new ServiceException($"提示!{string.Join(",", DateMsgs.ToArray())}"); } if (req.TradeCashIdList.Count > 0) { return "已勾选的结算报告发送成功!"; } return $"发送客户[{req.UnWindDateStart.Value:yyyy-MM-dd}]至[{req.UnWindDateEnd.Value:yyyy-MM-dd}]结算报告成功。"; } public void SendEmailSettleBillHtml(StringBuilder sb, List req) { //var trades = (from td in DbContext.trade // join tc in DbContext.trade_cash on td.id equals tc.TradeId // where req.TradeCashIdList.Contains(tc.id) // select new { tc.ValueDate, tc.FinalPrice, tc.UnwindPrice, tc.UnwindPricePercentRate, tc.UnwindPercentRate, tc.Amount, td }).ToList(); //List instrumentTypeArr = new List() { ConsGlobal.InstrumentType.Stock, ConsGlobal.InstrumentType.StockIF }; //if (!PS.Config.Is国泰君安 && !trades.Any(c => instrumentTypeArr.Contains(c.td.UnderlyingInstrumentType))) //{ // return; //} sb.Append(@""); foreach (var item in req) { var WinLoss = (item.tradecash.Amount + (item.trade.TradePrice ?? 0) * (item.tradecash.UnwindPercentRate ?? 0) * ((item.trade.BuySell == "卖出") ? 1 : -1)).OtcFormatMoney(); var settlementprice = (item.trade.IsUsePremiumRate != null && item.trade.IsUsePremiumRate.Value ? item.tradecash.UnwindPricePercentRate : item.tradecash.UnwindPrice); var buysell = item.trade.BuySell == "买入" ? "甲方" : "乙方"; var CountRatio = DataCacheProvider.GetUnderlyingDataSource().GetData(item.trade.UnderlyingId).CountRatio; sb.AppendFormat(@" ", item.trade.TradeNumber, buysell, item.trade.TradeDate.OtcFormatDate(), item.trade.StartDate.OtcFormatDate() + "-" + item.trade.ExerciseDate.OtcFormatDate(), item.trade.OriginalStockEqvNotional.OtcFormatMoney(), item.trade.UnderlyingCode, item.trade.TradeType, item.trade.TradePrice.OtcFormatMoney(), item.tradecash.ValueDate.OtcFormatDate(), item.tradecash.FinalPrice.OtcFormatMoney(), (item.tradecash.UnwindPercentRate * item.trade.OriginalNotional * CountRatio).OtcFormatMoney(), settlementprice.OtcFormatPercent(), item.tradecash.Amount.OtcFormatUmPrice(), WinLoss); } sb.Append("
交易编号 买方 交易达成日 起始日- 到期日 名义金额 标的资产 期权结构 期初期权费 结算日期 结算价格 结算规模 结算单价 结算期权费 结算金额
{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13}
"); } /// /// 生成单个清算确认书(渤海) 买卖方向在同一份交易确认书中 /// /// /// 甲方 /// 乙方 /// /// /// /// /// /// 客户 /// public trade_contract_document GenerateSettleBillForBuySell(DateTime valueDate, Client FP, Client SP, List tradeList, string contractCode, List bankList, List clientdutyList, Client client, List varietyList, string settleDocName = null, int dayNumber = 0) { var systemDate = valuedateBLL.ValueDate; var FPBank = bankList?.FirstOrDefault(t => t.ClientId == FP.id); var FPclientduty = clientdutyList?.FirstOrDefault(t => t.ClientId == FP.id); var SPBank = bankList?.FirstOrDefault(t => t.ClientId == SP.id); var SPclientduty = clientdutyList?.FirstOrDefault(t => t.ClientId == SP.id); var ClientBank = bankList?.FirstOrDefault(t => t.ClientId == client.id); var Clientduty = clientdutyList?.FirstOrDefault(t => t.ClientId == client.id); //获取清算确认书模板信息 var docPath = PS.Config.ErpElement.DocPath; if (docPath.IsNullOrWhiteSpace()) { //docPath = "SYWG"; docPath = "导出模板"; } var sourcePath = OtcAppContext.MapPath($"~/App_Docs/{docPath}"); settleDocName = settleDocName ?? $"清算确认书.xlsx"; var sourceFileName = Path.Combine(sourcePath, settleDocName); var targetPath = Path.Combine(sourcePath, systemDate.ToString("yyyyMMdd")); if (!Directory.Exists(targetPath)) { Directory.CreateDirectory(targetPath); } var targetFileName = Path.Combine(targetPath, $"{contractCode}.xlsx"); //结算支付日默认为当前valuedate延后三个交易日 //是渤海的要求,目前这个字段只影响渤海的清算确认书中的文字,不参与计算 //如果其它机构也要用这个字段,则需要移入BizLogic类中 var PayDate = QdpCalendarHelper.BizDayShift(valueDate, offset: 2); //组织model的 var isAmerican = ConsGlobal.ExerciseMode.IsAmerican(tradeList[0].trade.ExerciseMode); var isUnwind = tradeList[0].trade_cash.Action == ClientCashInCashOut.系统操作_平仓费; var contractModel = new SettletmentModel { FPCompanyName = FP.Name ?? string.Empty, FPAddress = FP.PostalAddress ?? string.Empty, FPTel = FPclientduty?.PhoneNumber ?? string.Empty, FPFax = FPclientduty?.Fax ?? string.Empty, FPName = FP.Name, FPBankCard = FPBank?.Card ?? string.Empty, FPBankName = FPBank?.Bank ?? string.Empty, FPLinkman = FPclientduty?.ContactName ?? string.Empty, FPEmail = FPclientduty?.Email ?? string.Empty, SPCompanyName = SP.Name ?? string.Empty, SPAddress = SP.PostalAddress ?? string.Empty, SPTel = SPclientduty?.PhoneNumber ?? string.Empty, SPFax = SPclientduty?.Fax ?? string.Empty, SPName = SP.Name ?? string.Empty, SPBankCard = SPBank?.Card ?? string.Empty, SPBankName = SPBank?.Bank ?? string.Empty, SPLinkman = SPclientduty?.ContactName ?? string.Empty, SPEmail = SPclientduty?.Email ?? string.Empty, ClientCompanyName = client.Name ?? string.Empty, ClientAddress = client.PostalAddress ?? string.Empty, ClientTel = Clientduty?.PhoneNumber ?? string.Empty, ClientFax = Clientduty?.Fax ?? string.Empty, ClientName = ClientBank.ClientName ?? string.Empty, ClientBankCard = ClientBank?.Card ?? string.Empty, ClientBankName = ClientBank?.Bank ?? string.Empty, ClientLinkman = Clientduty?.ContactName ?? string.Empty, ClientEmail = Clientduty?.Email ?? string.Empty, ValueDate = valueDate.ToString("yyyy年MM月dd日"), ContractCode = contractCode, //欧式美式行权说明处理 ExerciseDateLabel = isUnwind ? "" : "行权日:", ExerciseDateDesc = isUnwind ? "" : (isAmerican ? "期权买方在生效日和到期日之前任一交易日北京时间 14:40 前要求期权卖方行权的日期。" : "期权买方仅能在到期日收盘后向期权卖方申请行权。"), ExerciseLabel1 = isUnwind ? "" : (isAmerican ? "(1)提前行权:" : "行权说明:"), ExerciseLabel2 = isUnwind ? "" : (isAmerican ? "(2)到期行权:" : ""), ExerciseDesc1 = isUnwind ? "" : (isAmerican ? "卖方应按照买方提前行权时标的商品交易所公布的标的合约最新价作为结算价(St)支付行权收益给买方。 " : "如买方申请行权,卖方应按照到期日标的商品交易所公布的标的合约收盘价作为结算价(St)支付行权收益给买方。"), ExerciseDesc2 = isUnwind ? "" : (isAmerican ? "卖方应按照到期日标的商品交易所公布的标的合约收盘价作为结算价(St)支付行权收益给买方。" : ""), //平仓行权收益盈亏说明 WinLossDescTitle = isUnwind ? "平仓盈亏说明(买卖方向以期初交易方向为准):" : "行权收益的计算:", WinLossDesc1 = isUnwind ? "(1)如买方提前平仓,则平仓盈亏=(平仓单位权利金-开仓单位权利金)*交易数量;" : "(1)看涨期权行权收益=MAX((结算价(St)-执行价格(Sk)),0)*交易数量", WinLossDesc2 = isUnwind ? "(2)如卖方提前平仓,则平仓盈亏=(开仓单位权利金-平仓单位权利金)*交易数量。" : "(2)看跌期权行权收益=MAX((执行价格(Sk)-结算价(St)),0)*交易数量", SettlePriceOrUnwindPriceTitle = isUnwind ? "平仓单位权利金(元)" : "标的结算价格St(元)", SettleProfitTitle = isUnwind ? "平仓权利金总额(元)" : "行权收益(元)", PayDate = PayDate.ToString("yyyy年MM月dd日")//默认为结算日延后三个自然日 }; //交易员角度看,收取的期权费 var TotalPremium = 0.0; var TotalPrinciple = 0.0; var TotalSettleProfit = 0.0; var SettleContractCode = ""; var SettleContractBillList = new List(); var tradeSettleContractCode = new Dictionary(); var tradeCount = tradeList.Count; for (var i = 0; i < tradeCount; i++) { tradeSettleContractCode[tradeList[i].trade_cash.id] = contractCode; if (tradeCount > 1) { SettleContractCode = $"{i}".PadLeft(3, '0'); tradeSettleContractCode[tradeList[i].trade_cash.id] = $"{contractCode}({SettleContractCode})"; } var variety = varietyList.FirstOrDefault(t => t.VarietyCode == tradeList[i].underlying_manager.CommodityCode); var tradeUnit = ConsGlobal.InstrumentType.Stock.Equals(tradeList[i].trade.UnderlyingInstrumentType) ? "元/股" : (variety == null ? "" : variety.QuoteUnit); var quoteUnit = (variety == null ? "" : variety.QuoteUnitSingle); var stockEqvNotional = tradeList[i].trade_cash.UnwindStockEqvNotional ?? 0; var margin = (valuedateBLL.SystemDate.MarginRatio ?? 0.0) * stockEqvNotional; var notional = tradeList[i].trade_cash.UnwindNotional ?? 0.0; var tradeAmount = tradeList[i].trade_cash.UnwindTradeAmount ?? 0.0; var ExecuteType = TradeCashExerciseWayEnum.到期行权; //判断是平仓还是行权 if (tradeList[i].trade_cash.Action == ClientCashInCashOut.系统操作_平仓费) { ExecuteType = "平仓"; } else if (TradeCashExerciseWayEnum.提前终止行权 == tradeList[i].trade_cash.ExerciseWay && tradeList[i].trade_cash.Action == ClientCashInCashOut.系统操作_行权费) { ExecuteType = TradeCashExerciseWayEnum.提前终止行权.ToString(); } var strike = ((tradeList[i].trade.Strike ?? 0.0) * (tradeList[i].trade.IsMoneynessOptionData ? 100 : 1)).ToString("F2"); var strikePrice = (tradeList[i].trade.ActualStrike ?? 0.0).ToString("F2"); SettleContractBillList.Add(new SettleContractBill { SettleContractCode = SettleContractCode, TradeContractCode = tradeList[i].ConfirmContractR?.ContractCode ?? "", SettleDate = valueDate.ToString("yyyy年MM月dd日"), TradeNumber = tradeList[i].trade.TradeNumber, UnderlyingClass = tradeList[i].trade.UnderlyingAssetClass, UnderlyingCode = tradeList[i].trade.UnderlyingCode, UnderlyingName = tradeList[i].trade.UnderlyingAssetName, Notional = notional.ToString("F2"), TradeAmount = tradeAmount.ToString("F2"), TradeQuantity = (tradeList[i].trade.Notional / (variety == null ? 1 : variety.CountRatio)).ToString("F2"), StockEqvNotional = stockEqvNotional.ToString("F2"), InitialSpotPrice = (tradeList[i].trade.SpotPrice ?? 0.0).ToString("F2"), Strike = strike, StrikePrice = strikePrice, InitialTermDay = tradeList[i].trade.StartDate.Value.ToString("yyyy年MM月dd日"), FinalTermDay = tradeList[i].trade.ExerciseDate.Value.ToString("yyyy年MM月dd日"), SpotPrice = (tradeList[i].trade.SpotPrice ?? 0.0).ToString("F2"), //期末结算价格 SettlePrice = (tradeList[i].trade_cash.FinalPrice ?? 0.0).ToString("F2"), SettlePriceOrUnwindPrice = tradeList[i].trade_cash.Action == ClientCashInCashOut.系统操作_平仓费 ? (tradeList[i].trade_cash.UnwindPrice ?? 0.0).ToString("F3") : (tradeList[i].trade_cash.FinalPrice ?? 0.0).ToString("F3"), //因为当前trade_cash中的交易都是针对渠道方,所以生成清算确认书时取客户方向 SettleProfit = (tradeList[i].trade_cash.Amount * (-1)).ToString("F2"), ExerciseType = tradeList[i].trade.TradeStatus, TradeUnit = tradeUnit, QuoteUnit = quoteUnit, SettleSingleProfit = notional != 0 ? (tradeList[i].trade_cash.Amount / notional * (-1)).ToString("F2") : "", Margin = margin.ToString("F2"), TradeDate = tradeList[i].trade.TradeDate.HasValue ? tradeList[i].trade.TradeDate.Value.ToString("yyyy年M月d日") : "", Market = tradeList[i].underlying_manager.MarketName, TradeType = tradeList[i].trade.TradeType == "自定义交易" ? tradeList[i].trade.TradeType : tradeList[i].trade.ExerciseModeCn + tradeList[i].trade.OptionType + "期权", Remark1 = tradeList[i].trade.ExerciseMode == "American" ? (tradeList[i].trade.TradeType == "亚式期权" && tradeList[i].trade.trade_asian_option.StrikeType == "Floating" ? (tradeList[i].trade.CallPut == "Call" ? $"(期末价格 - 浮动行权价) * {notional:F2}" : $"(浮动行权价 - 期末价格 ) * {notional:F2}") : (tradeList[i].trade.CallPut == "Call" ? $"(期末价格 - {strikePrice}) * {notional:F2}" : $"({strikePrice} - 期末价格 ) * {notional:F2}")) : "欧式期权不适用", Remark2 = tradeList[i].trade.TradeType == "亚式期权" && tradeList[i].trade.trade_asian_option.StrikeType == "Floating" ? (tradeList[i].trade.CallPut == "Call" ? $"({(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 - 浮动行权价) * {tradeList[i].trade.Notional:F2}" : $"(浮动行权价 - {(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 ) * {tradeList[i].trade.Notional:F2}") : (tradeList[i].trade.CallPut == "Call" ? $"({(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 - {strikePrice}) * {tradeList[i].trade.Notional:F2}" : $"({strikePrice} - {(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 ) * {tradeList[i].trade.Notional:F2}"), CallPut = tradeList[i].trade.OptionType, BuySell = tradeList[i].trade.BuySell == "买入" ? "卖出" : "买入", ExecuteType = ExecuteType, ABSSettleProfit = (Math.Abs(tradeList[i].trade_cash.Amount)).ToString("F2"), ABSSettleSingleProfit = notional != 0 ? Math.Abs(tradeList[i].trade_cash.Amount / notional).ToString("F2") : "", Premium = (tradeList[i].trade.TradePrice ?? 0.0).ToString("F2"), PremiumSingle = (tradeList[i].trade.TradeSinglePrice ?? 0.0).ToString("F3"), StartDate = tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "", ExerciseDate = tradeList[i].trade.ExerciseDate.HasValue ? tradeList[i].trade.ExerciseDate.Value.ToString("yyyy年M月d日") : "", PremiumPayDate = tradeList[i].trade.PremiumPayDate.HasValue ? tradeList[i].trade.PremiumPayDate.Value.ToString("yyyy年M月d日") : "", Principle = tradeList[i].trade.StockEqvNotional.ToString("F2"), }); TotalPremium += (tradeList[i].trade.TradePrice ?? 0.0) * (tradeList[i].trade.BuySell == "卖出" ? 1 : -1); TotalPrinciple += tradeList[i].trade.StockEqvNotional; TotalSettleProfit += tradeList[i].trade_cash.Amount * (-1); } contractModel.TotalPrinciple = TotalPrinciple.ToString("F2"); contractModel.TotalSettleProfit = TotalSettleProfit.ToString("F2"); contractModel.TotalSettleProfitAbs = Math.Abs(TotalSettleProfit).ToString("F2"); contractModel.TradeList = SettleContractBillList; var payDescPart1 = TotalPremium < 0 ? "此笔交易初始期权费为甲方应向乙方支付" : "此笔交易初始期权费为乙方应向甲方支付"; var payDescPart2 = TotalPremium - TotalSettleProfit < 0 ? "元,经最终轧差之后,甲方应向乙方支付" : "元,经最终轧差之后,乙方应向甲方支付"; //交易盈亏支付总结 contractModel.PayDesc = payDescPart1 + Math.Abs(TotalPremium).ToString("F2") + payDescPart2 + Math.Abs(TotalPremium - TotalSettleProfit).ToString("F2") + "元。"; //申万需求客户为买方时,不需要显示该提示 if (tradeList[0].trade.BuySell == "卖出") { contractModel.PayDesc = ""; } var modelDict = new Dictionary { {"A清算确认书",contractModel},{"清算附页信息",SettleContractBillList} }; //生成清算确认书 var rightAlignedText = $"清算书编号:{contractCode}"; var UseRightAligned = Environment.GetEnvironmentVariable("AppSettings:UseRightAligned"); if (UseRightAligned == null || "否".Equals(UseRightAligned)) { rightAlignedText = null; } var pdffile = ""; if (PS.Config.Is申万) { void generateExcelCallback(ExcelWorksheets sheets) { foreach (var sheet in sheets) { if (sheet.Name == "A清算确认书") { if (SettleContractBillList.Count == 2) { //sheet.Cells[15, 12, 16, 12].Merge = true; sheet.Cells[15, 13, 16, 13].Merge = true; sheet.Cells[15, 14, 16, 14].Merge = true; } if (isUnwind) { sheet.DeleteRow(21, 4); } } } }; pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName, RightAlignedText: rightAlignedText, needLandscape: true, callback: generateExcelCallback); } else { pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName, RightAlignedText: rightAlignedText); } var tradeSettleBill = new trade_contract_document { Code = contractCode, ValueDate = valueDate, BuyerId = SP.id, BuyerName = SP.Name, SellerId = FP.id, SellerName = FP.Name, FileType = FileTypeEnum.Pdf, Status = ContractStatusEnum.Draft, Paths = Path.Combine(DocumentPathConvert.ConvertToUrlPath(targetPath), Path.GetFileName(pdffile)), FileName = Path.GetFileName(pdffile), OptDate = DateTime.Now, OptId = UserId, OptName = UserName, Type = ContractTypeEnum.Clearing, DayNumber = dayNumber, ClientId = client.id, ClientName = client.Name }; if (tradeSettleContractCode.Count > 0) { //申万是单笔结算确认书 if (PS.Config.Is申万) { var trade = tradeList.FirstOrDefault(); //删除对应交易清算确认书 var settleContractr = DbContext.trade_contract_r.Where(t => t.TradeCashId == trade.trade_cash.id && t.IsValid).ToList(); var oldDoc = DbContext.trade_contract_document.Where(t => t.Code == contractCode && (t.Type == ContractTypeEnum.Clearing || t.Type == ContractTypeEnum.UnWind)); var tradeContractR = new trade_contract_r { TradeId = trade.trade.id, TradeNumber = trade.trade.TradeNumber, TradeClearCode = tradeSettleContractCode[trade.trade_cash.id], ContractCode = tradeSettleBill.Code, OptId = UserId, OptName = UserName, OptDate = DateTime.Now, Type = ContractTypeEnum.Clearing, TradeCashId = trade.trade_cash.id, IsValid = true }; //增加清算确认书 //DbContext.trade_contract_r.RemoveRange(settleContractr); settleContractr.ForEach(O => O.IsValid = false); DbContext.trade_contract_document.RemoveRange(oldDoc); DbContext.trade_contract_document.Add(tradeSettleBill); DbContext.trade_contract_r.Add(tradeContractR); DbContext.SaveChanges(); } else { //删除对应交易清算确认书 var tradeIdsList = tradeSettleContractCode.Keys.ToList(); var settleContractr = DbContext.trade_contract_r.Where(t => tradeIdsList.Contains(t.TradeCashId ?? 0) && (t.Type == ContractTypeEnum.Clearing || t.Type == ContractTypeEnum.UnWind) && t.IsValid).ToList(); var oldDoc = DbContext.trade_contract_document.Where(t => t.Code == contractCode && (t.Type == ContractTypeEnum.Clearing || t.Type == ContractTypeEnum.UnWind)); //交易与清算确认书对应关系 var tradeContractRList = new List(); foreach (var tradeCashId in tradeSettleContractCode.Keys) { var trade = tradeList.FirstOrDefault(t => t.trade_cash.id == tradeCashId); tradeContractRList.Add(new trade_contract_r { TradeId = trade.trade.id, TradeNumber = trade.trade.TradeNumber, TradeClearCode = tradeSettleContractCode[tradeCashId], ContractCode = tradeSettleBill.Code, OptId = UserId, OptName = UserName, OptDate = DateTime.Now, Type = ContractTypeEnum.Clearing, TradeCashId = trade.trade_cash.id, IsValid = true }); } //增加清算确认书 //DbContext.trade_contract_r.RemoveRange(settleContractr); settleContractr.ForEach(O => O.IsValid = false); DbContext.trade_contract_document.RemoveRange(oldDoc); DbContext.trade_contract_document.Add(tradeSettleBill); DbContext.trade_contract_r.AddRange(tradeContractRList); DbContext.SaveChanges(); } } return tradeSettleBill; } private static readonly object _LockBatchGenerateSettleBill = new object(); /// /// from tradecontroller.GenerateSettleBillForSingle /// public string GenerateSettleBillForSingle(TradeReq req) { if (req.TradeCashIdList.Count() == 0) { throw new ServiceException("根据当前条件未查找到可以生成清算确认书的交易!"); } lock (_LockBatchGenerateSettleBill) { //当前交易日 var valueDate = valuedateBLL.ValueDate; //交易状态 var tradeStatus = new List { ConsTrade.已到期, ConsTrade.确认成交, ConsTrade.已平仓, ConsTrade.已执行 }; //费用类型 var tradeCashStatus = new List { ClientCashInCashOut.系统操作_平仓费, ClientCashInCashOut.系统操作_行权费 }; //通道模式并且不是申万时 都用可重复生成模式 if (!req.UnWindDateStart.HasValue) { req.UnWindDateStart = valueDate; } if (!req.UnWindDateEnd.HasValue) { req.UnWindDateEnd = valueDate; } //获取当日行权 平仓 到期的(渠道)有效交易 var tradeCashQuery = from tradeCash in DbContext.trade_cash where tradeCash.ValidState != ConsGlobal.InValid && !tradeCash.IsDeleted && tradeCashStatus.Contains(tradeCash.Action) select tradeCash; var tradeList = (from trade in DbContext.trade join tcr in DbContext.trade_contract_r.Where(aa => aa.Type == ContractTypeEnum.Trade && aa.IsValid) on new { id = trade.ParentTradeId > 0 ? trade.ParentTradeId : trade.id } equals new { id = tcr.TradeId } join tc in tradeCashQuery on trade.id equals tc.TradeId join um in DbContext.underlying_manager on trade.UnderlyingId equals um.id where tradeStatus.Contains(trade.TradeStatus) && trade.ValidState != "InValid" && req.TradeCashIdList.Contains(tc.id) && trade.ClientId != 0 select new trade_contract_group { trade = trade, trade_cash = tc, ConfirmContractR = tcr, underlying_manager = um //trade_contract_document = tcd }).ToList(); if (tradeList == null || tradeList.Count == 0) { throw new ServiceException("根据当前条件未查找到可以生成清算确认书的交易"); }; var clientDb = DbContextFactory.GetClientDbContext(OptUser); //客户编号列表 var clientIds = tradeList.Select(t => t.trade.ClientId).Distinct().ToList(); var clientList = clientDb.client.Where(t => clientIds.Contains(t.id)).ToList(); var baseName = PS.Config.CompanyFullName; var baseClient = clientDb.client.FirstOrDefault(t => baseName.Equals(t.Name)); //获取客户区间内所有清算确认书编号 var tcdList = DbContext.trade_contract_document.Where(t => ContractTypeEnum.Clearing.Equals(t.Type) && clientIds.Contains(t.ClientId ?? 0) && t.ValueDate >= req.UnWindDateStart && t.ValueDate <= req.UnWindDateEnd).ToList(); if (req.SealResults != null) { if (req.SealResults.Contains(2)) { tcdList = tcdList.Where(t => req.SealResults.Contains((int)t.SealResult) || t.SealResult == null).ToList(); } else { tcdList = tcdList.Where(t => req.SealResults.Contains((int)t.SealResult)).ToList(); } } //增加通道方客户编码 clientIds.Add(baseClient.id); var bankList = clientDb.bankcard.Where(t => t.ApprovalOrder < 1 && clientIds.Contains(t.ClientId ?? 0)).ToList(); var clientdutyList = clientDb.clientduty.Where(t => t.ApprovalOrder < 1 && clientIds.Contains(t.ClientId ?? 0)).ToList(); //品种类型 var varietyCodes = tradeList.Select(t => t.underlying_manager.CommodityCode).Distinct().ToList(); var varietyList = DbContext.variety.Where(t => varietyCodes.Contains(t.VarietyCode)).ToList(); var useTradeIds = tradeList.Select(t => t.trade.id).ToList(); var settleContractList = new List(); var errorMsg = ""; var notInTradeIds = req.TradeIdList.Where(t => !useTradeIds.Contains(t)).ToList(); if (notInTradeIds.Count > 0) { errorMsg = $"交易:{string.Join(",", notInTradeIds)},未生成交易确认书或者未查找到行权收益。"; } var maxDayNumberDict = new Dictionary(); for (var i = 0; i < tradeList.Count; i++) { var trade = tradeList[i]; var date = trade.trade.UnWindDate; var client = clientList.FirstOrDefault(t => t.id == trade.trade.ClientId); if (!date.HasValue || client == null) { errorMsg += $"交易编号:[{trade.trade.TradeNumber}]实际平仓日或客户信息为找到!"; continue; } var FP = baseClient; var SP = client; //甲方 if (trade.trade.BuySell == "买入") { FP = client; SP = baseClient; } var dayNumber = 1; if (maxDayNumberDict.ContainsKey(client.id + "_" + date.Value.ToString("yyyyMMdd"))) { dayNumber = maxDayNumberDict[client.id + "_" + date.Value.ToString("yyyyMMdd")] + 1; maxDayNumberDict[client.id + "_" + date.Value.ToString("yyyyMMdd")] = dayNumber; } else { var tempTcdList = tcdList.Where(t => t.ClientId == client.id && t.ValueDate == date).ToList(); if (tempTcdList.Any()) { dayNumber = tempTcdList.Max(a => a.DayNumber ?? 0) + 1; } maxDayNumberDict[client.id + "_" + date.Value.ToString("yyyyMMdd")] = dayNumber; } //个股 if (trade.trade.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Stock) { var StockContractCodes = $"{baseClient.Code}-JS-{client.Code}-{date.Value:yyyyMMdd}({dayNumber:D3})STO"; if (PS.Config.Is申万) { var parentTrade = DbContext.trade.FirstOrDefault(x => x.id == trade.trade.ParentTradeId); if (parentTrade != null) { //结构化交易,两腿子交易平仓行权,会算在一起在“L”后面加上操作编号1,2,3,4。。 var otherChildTrade = DbContext.trade.FirstOrDefault(x => x.ParentTradeId == trade.trade.ParentTradeId && x.id != trade.trade.id); var otherChildTradeId = otherChildTrade?.id; var num = DbContext.trade_cash.Where(x => (x.Action == "系统操作-平仓费" || x.Action == "系统操作-行权费") && !x.IsDeleted && (x.TradeId == trade.trade.id || x.TradeId == otherChildTradeId) && x.ValueDate <= trade.trade_cash.ValueDate && x.id < trade.trade_cash.id).Count() + 1; StockContractCodes = (parentTrade != null ? parentTrade.TradeNumber : trade.trade.TradeNumber) + "-L-" + num; } else { var tradeCashs = DbContext.trade_cash.Where(x => x.Action == "系统操作-平仓费" && !x.IsDeleted && x.TradeId == trade.trade.id && x.UnwindNotional < x.Notional); if (!tradeCashs.Any()) { //没有部分平仓的交易,不需要在“L”后面加操作编号 StockContractCodes = trade.trade.TradeNumber + "-L"; } else { //有部分平仓的交易,需要在“L”后面加操作编号 var num = DbContext.trade_cash.Where(x => x.Action == "系统操作-平仓费" && !x.IsDeleted && x.TradeId == trade.trade.id && x.ValueDate <= trade.trade_cash.ValueDate && x.id < trade.trade_cash.id).Count() + 1; StockContractCodes = trade.trade.TradeNumber + "-L-" + num; } } } var genResult = GenerateSettleBillForBuySell(date.Value, FP, SP, new List { trade }, StockContractCodes, bankList, clientdutyList, client, varietyList, $"清算确认书_个股.xlsx", dayNumber: dayNumber); settleContractList.Add(genResult); } else //商品期货 { var FutureContractCodes = $"{baseClient.Code}-JS-{client.Code}-{date.Value:yyyyMMdd}({dayNumber:D3})FUT"; if (PS.Config.Is申万) { var parentTrade = DbContext.trade.FirstOrDefault(x => x.id == trade.trade.ParentTradeId); if (parentTrade != null) { //结构化交易,两腿子交易平仓行权,会算在一起在“L”后面加上操作编号1,2,3,4。。 var otherChildTrade = DbContext.trade.FirstOrDefault(x => x.ParentTradeId == trade.trade.ParentTradeId && x.id != trade.trade.id); var otherChildTradeId = otherChildTrade?.id; var num = DbContext.trade_cash.Where(x => (x.Action == "系统操作-平仓费" || x.Action == "系统操作-行权费") && !x.IsDeleted && (x.TradeId == trade.trade.id || x.TradeId == otherChildTradeId) && x.ValueDate <= trade.trade_cash.ValueDate && x.id < trade.trade_cash.id).Count() + 1; FutureContractCodes = (parentTrade != null ? parentTrade.TradeNumber : trade.trade.TradeNumber) + "-L-" + num; } else { var tradeCashs = DbContext.trade_cash.Where(x => x.Action == "系统操作-平仓费" && !x.IsDeleted && x.TradeId == trade.trade.id && x.UnwindNotional < x.Notional); if (!tradeCashs.Any()) { //没有部分平仓的交易,不需要在“L”后面加操作编号 FutureContractCodes = trade.trade.TradeNumber + "-L"; } else { //有部分平仓的交易,需要在“L”后面加操作编号 var num = DbContext.trade_cash.Where(x => x.Action == "系统操作-平仓费" && !x.IsDeleted && x.TradeId == trade.trade.id && x.ValueDate <= trade.trade_cash.ValueDate && x.id < trade.trade_cash.id).Count() + 1; FutureContractCodes = trade.trade.TradeNumber + "-L-" + num; } } } var genResult = GenerateSettleBillForBuySell(date.Value, FP, SP, new List { trade }, FutureContractCodes, bankList, clientdutyList, client, varietyList, $"清算确认书_商品期货.xlsx", dayNumber: dayNumber); settleContractList.Add(genResult); } } if (errorMsg.Length > 0) { throw new ServiceException(settleContractList.Count > 0 ? $"部分生成成功:{errorMsg}" : "生成失败!"); } return PS.GetErpConfig().IsAutoSealAndUploadFiles && PS.GetErpConfig().IsAutoSealAfterGeneratedBook ? "生成成功,用印等待中..." : "生成成功"; } } /// /// 批量生成结算确认书 /// public string BatchGenerateSettleBill(TradeReq req) { try { lock (_LockBatchGenerateSettleBill) { //当前交易日 var valueDate = valuedateBLL.ValueDate; req.ExerciseDate = req.ExerciseDate ?? valueDate; req.ExerciseDateStart = req.ExerciseDateStart ?? DateTime.MinValue; req.ExerciseDateEnd = req.ExerciseDateEnd ?? DateTime.MaxValue; var bll = new tradeBLL(); //交易状态 var tradeStatus = new List { ConsTrade.已到期, ConsTrade.已平仓, ConsTrade.确认成交, ConsTrade.已执行 }; req.TradeStatusList = tradeStatus; //费用类型 var tradeCashStatus = new List { ClientCashInCashOut.系统操作_平仓费, ClientCashInCashOut.系统操作_行权费 }; //通道模式并且不是申万时 都用可重复生成模式 if (PS.Config.ComponentVersion == ComponentVersion.Broker && PS.Config.Company != CompanyEnum.申万) { if (!req.UnWindDateStart.HasValue) { req.UnWindDateStart = valueDate; } if (!req.UnWindDateEnd.HasValue) { req.UnWindDateEnd = valueDate; } var sList = bll.SearchTradeWithCashList(req, out var gsum, false); var rowList = sList.rows; new TradeDalService(OptUser).SetStructureInfo(rowList.Select(x => x.trade).ToList()); ////获取当日行权 平仓 到期的(渠道)有效交易 //var tradeList = (from trade in DbContext.trade // join tcr in (from aa in DbContext.trade_contract_r // where aa.Type == ContractTypeEnum.Trade // && aa.IsValid // select aa) on trade.id equals tcr.TradeId // join tradeContractDoc in (from tradeContractDocTemp in DbContext.trade_contract_document where tradeContractDocTemp.Type == ContractTypeEnum.Clearing || tradeContractDocTemp.Type == ContractTypeEnum.UnWind select tradeContractDocTemp) // on tcr.ContractCode equals tradeContractDoc.Code into tradeContractDoc1 // from tradeContractDoc2 in tradeContractDoc1.DefaultIfEmpty() // join tc in (from tradeCash in DbContext.trade_cash // where tradeCash.ValidState != ConsGlobal.InValid && !tradeCash.IsDeleted && // tradeCashStatus.Contains(tradeCash.Action) // select tradeCash) // on trade.id equals tc.TradeId // join um in DbContext.underlying_manager on trade.UnderlyingId equals um.id // where // //tradeStatus.Contains(trade.TradeStatus) && // trade.ValidState != "InValid" && // trade.UnWindDate >= req.UnWindDateStart.Value && // trade.UnWindDate <= req.UnWindDateEnd.Value && // trade.ClientId != 0 // select new trade_contract_group // { // trade = trade, // trade_cash = tc, // ConfirmContractR = tcr, // underlying_manager = um // //trade_contract_document = tcd // }).ToList(); ////增加客户过滤条件 //if (req.ClientIdsInt != null && req.ClientIdsInt.Any()) //{ // tradeList = tradeList.Where(t => req.ClientIdsInt.Contains(t.trade.ClientId)).ToList(); //} //if(req.TradeTypesList!=null && req.TradeTypesList.Any()) //{ // tradeList = tradeList.Where(d => req.TradeTypesList.Contains(d.trade.TradeType) || req.TradeTypesList.Contains(d.trade.StructureType)).ToList(); // if (req.TradeTypesList.Contains("远期") && !req.TradeTypesList.Contains("掉期")) // { // tradeList = tradeList.Where(d => d.trade.StructureType != "掉期").ToList(); // } //} //if (req.TradeIdList != null && req.TradeIdList.Any()) //{ // tradeList = tradeList.Where(t => req.TradeIdList.Contains(t.trade.id)).ToList(); //} for (var date = req.UnWindDateStart.Value; DateTime.Compare(req.UnWindDateEnd.Value, date) >= 0; date = date.AddDays(1)) { var tempTradeList = rowList.Where(t => t.trade.UnWindDate == date).ToList(); if (tempTradeList == null || tempTradeList.Count == 0) { continue; } var clientDB = DbContextFactory.GetClientDbContext(OptUser); //客户编号列表 var clientIds = tempTradeList.Select(t => t.trade.ClientId).Distinct().ToList(); var clientList = clientDB.client.Where(t => clientIds.Contains(t.id)).ToList(); var baseName = PS.Config.CompanyFullName; var baseClient = clientDB.client.FirstOrDefault(t => baseName.Equals(t.Name)); var maxDayNumberDict = DbContext.trade_contract_document .Where(t => ContractTypeEnum.Trade.Equals(t.Type)).AsEnumerable().GroupBy(t => t.Code) .ToDictionary(t => t.Key, t => t.Max(a => a.DayNumber ?? 0)); //增加通道方客户编码 clientIds.Add(baseClient.id); var bankList = clientDB.bankcard.Where(t => t.ApprovalOrder < 1 && clientIds.Contains(t.ClientId ?? 0)).ToList(); var clientdutyList = clientDB.clientduty.Where(t => t.ApprovalOrder < 1 && clientIds.Contains(t.ClientId ?? 0)).ToList(); //品种类型 var varietyCodes = tempTradeList.Select(t => t.underlying_manager.CommodityCode).Distinct() .ToList(); var varietyList = DbContext.variety.Where(t => varietyCodes.Contains(t.VarietyCode)).ToList(); var settleContractList = new List(); var forwardTradeList = new List(); if (PS.Config.Is渤海) { forwardTradeList = tempTradeList.Where(t => t.trade.TradeType == "远期").ToList(); tempTradeList = tempTradeList.Where(t => t.trade.TradeType != "远期").ToList(); if (forwardTradeList.Any()) { forwardTradeList.ForEach(a => { settleContractList.Add(BHRSForwardGenerateSettleBill(a, date)); }); } } //根据不同用户 不同交易确认书对应的交易生成清算确认书 foreach (var client in clientList) { //根据客户获取交易确认书列表 var clientTradeList = tempTradeList.Where(t => t.trade.ClientId == client.id).ToList(); if (clientTradeList == null || clientTradeList.Count == 0) { continue; } //个股期权 var StockTradeList = clientTradeList.Where(t => t.trade.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Stock).ToList(); //商品期货期权 var FutureTradeList = clientTradeList.Where(t => t.trade.UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommodityFutures).ToList(); //甲方 var FP = baseClient; var SP = client; //个股 if (StockTradeList.Count > 0) { var StockContractCodes = $"{baseClient.Code}-JS-{client.Code}-{date:yyyyMMdd}STO{client.id}"; var genResult = GenerateSettleBillForBuySell(date, FP, SP, StockTradeList, StockContractCodes, bankList, clientdutyList, client, varietyList, $"清算确认书_个股.xlsx"); settleContractList.Add(genResult); } //商品期货 if (FutureTradeList.Count > 0) { var FutureContractCodes = $"{baseClient.Code}-JS-{client.Code}-{date:yyyyMMdd}FUT{client.id}"; var genResult = GenerateSettleBillForBuySell(date, FP, SP, FutureTradeList, FutureContractCodes, bankList, clientdutyList, client, varietyList, $"清算确认书_商品期货.xlsx"); settleContractList.Add(genResult); } } } } else { //获取当日行权 平仓 到期的(渠道)有效交易 var tradeQuery = (from trade in DbContext.trade join tcr in (from aa in DbContext.trade_contract_r where aa.Type == ContractTypeEnum.Trade && aa.IsValid select aa) on trade.id equals tcr.TradeId //join tcd in DbContext.trade_contract_document on tcr.ContractCode equals tcd.Code join tc in (from tradeCash in DbContext.trade_cash where tradeCash.ValidState != ConsGlobal.InValid && !tradeCash.IsDeleted && tradeCashStatus.Contains(tradeCash.Action) select tradeCash) on trade.id equals tc.TradeId join um in DbContext.underlying_manager on trade.UnderlyingId equals um.id where tradeStatus.Contains(trade.TradeStatus) && trade.ValidState != "InValid" && !(from o in DbContext.trade_contract_r where o.Type == ContractTypeEnum.Clearing && o.IsValid select o.TradeId).Contains(trade.id) && trade.UnWindDate == valueDate && trade.ClientId != 0 select new trade_contract_group { trade = trade, trade_cash = tc, ConfirmContractR = tcr, underlying_manager = um //trade_contract_document = tcd }); if (req.SealResults != null || req.SealDateStart != null || req.SealDateEnd != null) { var docQuery = from t in tradeQuery join doc in DbContext.trade_contract_document on t.ContractCode equals doc.Code into tempDoc from doc in tempDoc.DefaultIfEmpty() select new { t, doc }; if (req.SealResults != null) { if (req.SealResults.Contains(2)) { docQuery = docQuery.Where(t => req.SealResults.Contains((int)t.doc.SealResult) || t.doc.SealResult == null); } else { docQuery = docQuery.Where(t => req.SealResults.Contains((int)t.doc.SealResult)); } } tradeQuery = docQuery.Select(d => d.t); } var tradeList = tradeQuery.ToList(); if (tradeList == null || tradeList.Count == 0) { throw new ServiceException("当日没有待生成清算书的清算记录信息!"); } //客户编号列表 var clientDB = DbContextFactory.GetClientDbContext(OptUser); var clientIds = tradeList.Select(t => t.trade.ClientId).Distinct().ToList(); var clientList = clientDB.client.Where(t => clientIds.Contains(t.id)).ToList(); var baseName = PS.Config.CompanyFullName; var baseClient = clientDB.client.FirstOrDefault(t => baseName.Equals(t.Name)); var maxDayNumberDict = DbContext.trade_contract_document .Where(t => ContractTypeEnum.Trade.Equals(t.Type)).AsEnumerable().GroupBy(t => t.Code) .ToDictionary(t => t.Key, t => t.Max(a => a.DayNumber ?? 0)); //增加主客户 clientIds.Add(baseClient.id); var bankList = clientDB.bankcard.Where(t => t.ApprovalOrder < 1 && clientIds.Contains(t.ClientId ?? 0)).ToList(); var clientdutyList = clientDB.clientduty.Where(t => t.ApprovalOrder < 1 && clientIds.Contains(t.ClientId ?? 0)).ToList(); //品种类型 var varietyCodes = tradeList.Select(t => t.underlying_manager.CommodityCode).Distinct() .ToList(); var varietyList = DbContext.variety.Where(t => varietyCodes.Contains(t.VarietyCode)).ToList(); var settleContractList = new List(); //根据不同用户 不同交易确认书对应的交易生成清算确认书 foreach (var client in clientList) { //根据客户获取交易确认书列表 var clientTradeList = tradeList.Where(t => t.trade.ClientId == client.id).ToList(); if (clientTradeList == null || clientTradeList.Count == 0) { continue; } var contractCodes = clientTradeList.Select(t => t.ConfirmContractR.ContractCode).Distinct() .ToList(); //根据不同的交易确认书编号生成不同的 foreach (var contractCode in contractCodes) { //甲方 var FP = baseClient; var SP = client; var tempTradeList = clientTradeList .Where(t => contractCode.Equals(t.ConfirmContractR.ContractCode)).ToList(); int maxDayNumber; if (maxDayNumberDict.ContainsKey(contractCode)) { maxDayNumber = maxDayNumberDict[contractCode] + 1; } else { maxDayNumber = 1; } maxDayNumberDict[contractCode] = maxDayNumber; //同一个交易编号 交易方向应该是相同的 所以根据第一个交易的买卖方向确定确定当前清算确认书方向 //买入时 客户为甲方 个人为乙方 if ("买入".Equals(tempTradeList.FirstOrDefault().trade.BuySell)) { FP = client; SP = baseClient; } var underlying = tempTradeList.Select(t => t.trade.UnderlyingInstrumentType).Distinct() .ToList(); var usingDoc = $"清算确认书.xlsx"; ; if (underlying.Count == 1) { if ("Stock".Equals(underlying.First())) { usingDoc = $"清算确认书_个股.xlsx"; } else { usingDoc = $"清算确认书_商品期货.xlsx"; } } settleContractList.Add(GenerateSettleBill(valueDate, FP, SP, tempTradeList, contractCode, maxDayNumber, bankList, clientdutyList, client, varietyList, usingDoc)); } } } return PS.GetErpConfig().IsAutoSealAndUploadFiles && PS.GetErpConfig().IsAutoSealAfterGeneratedBook ? "生成成功,用印等待中..." : "生成成功"; } } catch (ServiceException) { throw; } catch (Exception ex) { LogFactory.GetLogger("BatchGenerateSettleBill").Error(ex); throw new ServiceException("发生异常" + ex.Message); } } /// /// 生成单个清算确认书 /// 申万不走该方法 /// /// /// 甲方 /// 乙方 /// /// /// /// /// /// 客户 /// private trade_contract_document GenerateSettleBill(DateTime valueDate, Client FP, Client SP, List tradeList, string contractCode, int dayNumber, List bankList, List clientdutyList, Client client, List varietyList, string settleDocName = null) { var FPBank = bankList?.FirstOrDefault(t => t.ClientId == FP.id) ?? throw new ServiceException($"{FP.Name}的银行账号信息缺失"); var FPclientduty = clientdutyList?.FirstOrDefault(t => t.ClientId == FP.id) ?? throw new ServiceException($"{FP.Name}的客户人员信息缺失"); var SPBank = bankList?.FirstOrDefault(t => t.ClientId == SP.id) ?? throw new ServiceException($"{SP.Name}的银行账号信息缺失"); var SPclientduty = clientdutyList?.FirstOrDefault(t => t.ClientId == SP.id) ?? throw new ServiceException($"{SP.Name}的客户人员信息缺失"); var ClientBank = bankList?.FirstOrDefault(t => t.ClientId == client.id); var Clientduty = clientdutyList?.FirstOrDefault(t => t.ClientId == client.id); //获取清算确认书模板信息 var sourcePath = OtcAppContext.MapPath("~/App_Docs/SYWG"); settleDocName = settleDocName ?? $"清算确认书.xlsx"; var sourceFileName = Path.Combine(sourcePath, settleDocName); var targetPath = Path.Combine(sourcePath, valueDate.ToString("yyyyMMdd")); if (!Directory.Exists(targetPath)) { Directory.CreateDirectory(targetPath); } var targetFileName = Path.Combine(targetPath, $"{contractCode + "L" + dayNumber + "_" + valueDate.ToString("yyyyMMdd")}.xlsx"); //结算支付日默认为当前valuedate延后三个交易日 //是渤海的要求,目前这个字段只影响渤海的清算确认书中的文字,不参与计算 //如果其它机构也要用这个字段,则需要移入BizLogic类中 var PayDate = QdpCalendarHelper.BizDayShift(valueDate, offset: 3); //组织model的 var contractModel = new SettletmentModel { FPCompanyName = FP.Name ?? string.Empty, FPAddress = FP.PostalAddress ?? string.Empty, FPTel = FPclientduty?.PhoneNumber ?? string.Empty, FPFax = FPclientduty?.Fax ?? string.Empty, FPName = FPBank?.ClientName ?? string.Empty, FPBankCard = FPBank?.Card ?? string.Empty, FPBankName = FPBank?.Bank ?? string.Empty, FPLinkman = FPclientduty?.ContactName ?? string.Empty, FPEmail = FPclientduty?.Email ?? string.Empty, SPCompanyName = SP.Name ?? string.Empty, SPAddress = SP.PostalAddress ?? string.Empty, SPTel = SPclientduty?.PhoneNumber ?? string.Empty, SPFax = SPclientduty?.Fax ?? string.Empty, SPName = SPBank?.ClientName ?? string.Empty, SPBankCard = SPBank?.Card ?? string.Empty, SPBankName = SPBank?.Bank ?? string.Empty, SPLinkman = SPclientduty?.ContactName ?? string.Empty, SPEmail = SPclientduty?.Email ?? string.Empty, ClientCompanyName = SP.Name ?? string.Empty, ClientAddress = SP.PostalAddress ?? string.Empty, ClientTel = Clientduty?.PhoneNumber ?? string.Empty, ClientFax = Clientduty?.Fax ?? string.Empty, ClientName = ClientBank?.ClientName ?? string.Empty, ClientBankCard = ClientBank?.Card ?? string.Empty, ClientBankName = ClientBank?.Bank ?? string.Empty, ClientLinkman = Clientduty?.ContactName ?? string.Empty, ClientEmail = Clientduty?.Email ?? string.Empty, ValueDate = valueDate.ToString("yyyy年MM月dd日"), ContractCode = contractCode, PayDate = PayDate.ToString("yyyy年MM月dd日")//默认为结算日延后三个自然日 }; var TotalPrinciple = 0.0; var TotalSettleProfit = 0.0; var SettleContractCode = ""; var SettleContractBillList = new List(); var tradeSettleContractCode = new Dictionary(); var docCode = $"{contractCode + "L" + dayNumber}"; for (var i = 0; i < tradeList.Count; i++) { var um = tradeList[i].underlying_manager; var variety = varietyList.FirstOrDefault(t => t.VarietyCode == um.CommodityCode); var tradeUnit = ConsGlobal.InstrumentType.Stock.Equals(tradeList[i].trade.UnderlyingInstrumentType) ? "元/股" : (variety == null ? "" : variety.QuoteUnit); var quoteUnit = (variety == null ? "" : variety.QuoteUnitSingle); SettleContractCode = $"{valueDate:yyyyMMdd}-{i + 1}"; tradeSettleContractCode[tradeList[i].trade.id] = $"{docCode}({SettleContractCode})"; var stockEqvNotional = tradeList[i].trade_cash.UnwindStockEqvNotional ?? 0; var margin = (valuedateBLL.SystemDate.MarginRatio ?? 0.0) * stockEqvNotional; var notional = tradeList[i].trade_cash.UnwindNotional ?? 0.0; var tradeAmount = tradeList[i].trade_cash.UnwindTradeAmount ?? 0.0; var strike = ((tradeList[i].trade.Strike ?? 0.0) * (tradeList[i].trade.IsMoneynessOptionData ? 100 : 1)).ToString("F2"); var strikePrice = (tradeList[i].trade.ActualStrike ?? 0.0).ToString("F2"); var ExecuteType = TradeCashExerciseWayEnum.到期行权; //判断是平仓还是行权 if (tradeList[i].trade_cash.Action == ClientCashInCashOut.系统操作_平仓费) { ExecuteType = "平仓"; } else if (TradeCashExerciseWayEnum.提前终止行权 == tradeList[i].trade_cash.ExerciseWay && tradeList[i].trade_cash.Action == ClientCashInCashOut.系统操作_行权费) { ExecuteType = TradeCashExerciseWayEnum.提前终止行权.ToString(); } SettleContractBillList.Add(new SettleContractBill { SettleContractCode = SettleContractCode, TradeContractCode = contractCode, SettleDate = valueDate.ToString("yyyy年MM月dd日"), TradeNumber = tradeList[i].trade.TradeNumber, UnderlyingClass = tradeList[i].trade.UnderlyingAssetClass, UnderlyingCode = tradeList[i].trade.UnderlyingCode, UnderlyingName = tradeList[i].trade.UnderlyingAssetName, Notional = notional.ToString("F2"), TradeAmount = tradeAmount.ToString("F2"), TradeQuantity = (tradeList[i].trade.Notional / (variety == null ? 1 : variety.CountRatio)).ToString("F2"), StockEqvNotional = stockEqvNotional.ToString("F2"), InitialSpotPrice = ((tradeList[i].trade.SpotPrice ?? 0.0)).ToString("F2"), Strike = strike, StrikePrice = strikePrice, InitialTermDay = tradeList[i].trade.StartDate.Value.ToString("yyyy年MM月dd日"), FinalTermDay = tradeList[i].trade.ExerciseDate.Value.ToString("yyyy年MM月dd日"), SpotPrice = ((tradeList[i].trade.SpotPrice ?? 0.0)).ToString("F2"), //期末结算价格 SettlePrice = (tradeList[i].trade_cash.FinalPrice ?? 0.0).ToString("F2"), SettlePriceOrUnwindPrice = tradeList[i].trade_cash.Action == ClientCashInCashOut.系统操作_平仓费 ? (tradeList[i].trade_cash.UnwindPrice ?? 0.0).ToString("F3") : (tradeList[i].trade_cash.FinalPrice ?? 0.0).ToString("F3"), //因为当前trade_cash中的交易都是针对渠道方,所以生成清算确认书时取客户方向 SettleProfit = (tradeList[i].trade_cash.Amount * (-1)).ToString("F2"), ExerciseType = tradeList[i].trade.TradeStatus, TradeUnit = tradeUnit, QuoteUnit = quoteUnit, SettleSingleProfit = notional != 0 ? (tradeList[i].trade_cash.Amount / notional * (-1)).ToString("F2") : "", Margin = margin.ToString("F2"), TradeDate = tradeList[i].trade.TradeDate.HasValue ? tradeList[i].trade.TradeDate.Value.ToString("yyyy年M月d日") : "", Market = um.MarketName, TradeType = tradeList[i].trade.ExerciseModeCn + tradeList[i].trade.OptionType + "期权", Remark1 = tradeList[i].trade.ExerciseMode == "American" ? (tradeList[i].trade.TradeType == "亚式期权" && tradeList[i].trade.trade_asian_option.StrikeType == "Floating" ? (tradeList[i].trade.CallPut == "Call" ? $"(期末价格 - 浮动行权价) * {notional:F2}" : $"(浮动行权价 - 期末价格 ) * {notional:F2}") : (tradeList[i].trade.CallPut == "Call" ? $"(期末价格 - {strikePrice}) * {notional:F2}" : $"({strikePrice} - 期末价格 ) * {notional:F2}")) : "欧式期权不适用", Remark2 = tradeList[i].trade.TradeType == "亚式期权" && tradeList[i].trade.trade_asian_option.StrikeType == "Floating" ? (tradeList[i].trade.CallPut == "Call" ? $"({(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 - 浮动行权价) * {tradeList[i].trade.Notional:F2}" : $"(浮动行权价 - {(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 ) * {tradeList[i].trade.Notional:F2}") : (tradeList[i].trade.CallPut == "Call" ? $"({(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 - {strikePrice}) * {tradeList[i].trade.Notional:F2}" : $"({strikePrice} - {(tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "")}{tradeList[i].trade.UnderlyingCode}合约收盘价 ) * {tradeList[i].trade.Notional:F2}"), CallPut = tradeList[i].trade.OptionType, BuySell = tradeList[i].trade.BuySell == "买入" ? "卖出" : "买入", ExecuteType = ExecuteType, ABSSettleProfit = (Math.Abs(tradeList[i].trade_cash.Amount)).ToString("F2"), ABSSettleSingleProfit = notional != 0 ? Math.Abs(tradeList[i].trade_cash.Amount / notional).ToString("F2") : "", Premium = $"{tradeList[i].trade.TradePrice ?? 0.0:F2}元", PremiumSingle = $"{tradeList[i].trade.TradeSinglePrice ?? 0.0:F3}元", StartDate = tradeList[i].trade.StartDate.HasValue ? tradeList[i].trade.StartDate.Value.ToString("yyyy年M月d日") : "", ExerciseDate = tradeList[i].trade.ExerciseDate.HasValue ? tradeList[i].trade.ExerciseDate.Value.ToString("yyyy年M月d日") : "", PremiumPayDate = tradeList[i].trade.PremiumPayDate.HasValue ? tradeList[i].trade.PremiumPayDate.Value.ToString("yyyy年M月d日") : "", Principle = $"{tradeList[i].trade.StockEqvNotional / 10000}万元", }); TotalPrinciple += tradeList[i].trade.StockEqvNotional; TotalSettleProfit += tradeList[i].trade_cash.Amount * (-1); } contractModel.ContractCode = docCode; contractModel.TotalPrinciple = $"{TotalPrinciple / 10000}万元"; contractModel.TotalSettleProfit = TotalSettleProfit.ToString("F2"); contractModel.TradeList = SettleContractBillList; var modelDict = new Dictionary { {"A清算确认书",contractModel},{"清算附页信息",SettleContractBillList} }; //生成清算确认书 var rightAlignedText = $"清算确认书编号:{docCode}"; var UseRightAligned = Environment.GetEnvironmentVariable("AppSettings:UseRightAligned"); if (UseRightAligned == null || "否".Equals(UseRightAligned)) { rightAlignedText = null; } var pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName, RightAlignedText: rightAlignedText); var tradeSettleBill = new trade_contract_document { Code = docCode, ValueDate = valueDate, BuyerId = SP.id, BuyerName = SP.Name, SellerId = FP.id, SellerName = FP.Name, FileType = FileTypeEnum.Pdf, Status = ContractStatusEnum.Draft, Paths = targetPath, FileName = Path.GetFileName(pdffile), OptDate = DateTime.Now, OptId = UserId, OptName = UserName, Type = ContractTypeEnum.Clearing }; if (tradeSettleContractCode.Count > 0) { //修改交易确认书最大值 var contractCodes = tradeList.Where(O => O.ConfirmContractR.Type == ContractTypeEnum.Trade).Select(t => t.ConfirmContractR.ContractCode).ToList(); var updateContractList = DbContext.trade_contract_document.Where(t => contractCode == t.Code && t.Type == ContractTypeEnum.Trade).ToList(); updateContractList.ForEach(t => { t.DayNumber = dayNumber; }); //删除对应交易清算确认书 var tradeIdsList = tradeSettleContractCode.Keys.ToList(); var settleContractr = DbContext.trade_contract_r.Where(t => tradeIdsList.Contains(t.TradeId) && t.Type == ContractTypeEnum.Clearing && t.IsValid).ToList(); var oldDoc = DbContext.trade_contract_document.Where(t => t.Code == docCode && t.Type == ContractTypeEnum.Clearing); //交易与清算确认书对应关系 var tradeContractRList = new List(); foreach (var tradeId in tradeSettleContractCode.Keys) { var trade = tradeList.FirstOrDefault(t => t.trade.id == tradeId); tradeContractRList.Add(new trade_contract_r { TradeId = tradeId, TradeNumber = trade.trade.TradeNumber, TradeClearCode = tradeSettleContractCode[tradeId], ContractCode = tradeSettleBill.Code, OptId = UserId, OptName = UserName, OptDate = DateTime.Now, Type = ContractTypeEnum.Clearing, TradeCashId = trade.trade_cash.id, IsValid = true }); } //增加清算确认书 //DbContext.trade_contract_r.RemoveRange(settleContractr); settleContractr.ForEach(O => O.IsValid = false); DbContext.trade_contract_document.RemoveRange(oldDoc); DbContext.trade_contract_document.Add(tradeSettleBill); DbContext.trade_contract_r.AddRange(tradeContractRList); DbContext.SaveChanges(); } return tradeSettleBill; } #region 渤海远期生成结算确认书 /// /// 生成渤海远期结算确认书 /// /// /// /// private trade_contract_document BHRSForwardGenerateSettleBill(trade_contract_group tcg, DateTime date) { string contractCode = string.IsNullOrEmpty(tcg.trade_contract_r?.ContractCode) ? BHRSForwardGenerateContractCode(date, "JS") : tcg.trade_contract_r.ContractCode; var firstParty = new Client(); var client = new Client(); using (var clientDB = DbContextFactory.GetClientDbContext(OptUser)) { var brokerClientName = PS.Config.CompanyFullName; firstParty = clientDB.client.FirstOrDefault(t => brokerClientName.Equals(t.Name)); client = clientDB.client.FirstOrDefault(t => t.id == tcg.trade.ClientId); } string templateDir = "/App_Docs/SYWG"; // App_Docs\SYWG\远期商品期货清算确认书.docx string templateFilePath = Path.Combine(templateDir, "远期商品期货清算确认书.docx"); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(tcg.trade.UnderlyingId); string marketName = um.MarketName; var marketInfo = DataCacheProvider.GetMarketDataSource().AsQueryable().Where(a => a.MarketName == marketName).FirstOrDefault(); string tradeUnit = ""; if (um.UnderlyingType == "组合标的") { var dto = new UnderlyingDalService(OptUser).GetSyntheticUnderlyingByName(um.UnderlyingCode); tradeUnit = ConsGlobal.InstrumentType.Stock.Equals(tcg.trade.UnderlyingInstrumentType) ? "元/股" : (dto.ContractSize == null ? "" : (dto.ContractSize ?? 0).ToString()); } else { tradeUnit = ConsGlobal.InstrumentType.Stock.Equals(tcg.trade.UnderlyingInstrumentType) ? "元/股" : ("元/" + um.TradeUnitString); } var dic = new Dictionary(); dic["{{乙方公司}}"] = client.Name; dic["{{交易编号}}"] = tcg.trade.TradeNumber; dic["{{交易方向}}"] = ToCallPut(tcg.trade.CallPut, tcg.trade.BuySell, tcg.trade.TradeType); dic["{{结构类型}}"] = tcg.trade.TradeType; dic["{{标的}}"] = tcg.trade.UnderlyingCode + "." + marketInfo.ExchangeNo; dic["{{交易日期}}"] = tcg.trade.TradeDate?.ToString("yyyy/MM/dd"); dic["{{到期日期}}"] = tcg.trade.ExerciseDate?.ToString("yyyy/MM/dd"); dic["{{期初价格}}"] = (tcg.trade.SpotPrice.HasValue ? tcg.trade.SpotPrice?.ToString("F2") : "0") + tradeUnit; dic["{{平仓数量}}"] = tcg.trade_cash.UnwindTradeAmount?.ToString("F2"); var unwindPrice = (tcg.trade_cash.UnwindPrice.HasValue ? Math.Abs(tcg.trade_cash.UnwindPrice ?? 0) : 0) / um.ContractSize; dic["{{平仓单价}}"] = unwindPrice.ToString("F2") + tradeUnit; dic["{{了结日期}}"] = tcg.trade_cash.ValueDate.ToString("yyyy/MM/dd"); dic["{{平仓总额}}"] = (tcg.trade_cash.Amount * -1).ToString("F2"); var tradeSinglePrice = (tcg.trade.TradeSinglePrice.HasValue ? Math.Abs(tcg.trade.TradeSinglePrice ?? 0) : 0) / um.ContractSize; dic["{{开仓单价}}"] = tradeSinglePrice.ToString("F2") + tradeUnit; dic["{{名义本金}}"] = tcg.trade.OriginalStockEqvNotional?.ToString("F2") + " 元"; dic["{{执行价格}}"] = tcg.trade.Strike?.ToString("F2") + tradeUnit; var openFee = tcg.trade.OriginalNotional > 0 ? (tcg.trade.UnWindNotional ?? 0) / tcg.trade.OriginalNotional.Value * (tcg.trade.TradePrice ?? 0) : 0; dic["{{实现盈亏}}"] = ((tcg.trade_cash.Amount + openFee) * -1).ToString("F2") + " 元"; dic["{{最终平仓日期}}"] = tcg.trade_cash.ValueDate.ToString("yyyy年M月d日"); dic["{{乙方收益}}"] = ((tcg.trade_cash.Amount + openFee) * -1).ToString("F2"); string outPath = Path.Combine(@"\App_Docs\SYWG", $"{date:yyyyMMdd}"); string outFileName = $"{contractCode}.pdf"; string outFilePath = Path.Combine(outPath, outFileName); MsWordTempalteHelper.GenerateFromTemplate(OtcAppContext.MapPath(templateFilePath), dic, OtcAppContext.MapPath(outFilePath)); var tradeContract = SaveTradeContract(tcg.trade, tcg.trade_cash, contractCode, date, firstParty, client, "", outFilePath, outFileName, false); return tradeContract; } public string BHRSForwardGenerateContractCode(DateTime date, string field) { string ContractCode = ""; int num = 1; var numberPrefix = $"BHRS-{field}-{date:yyyyMMdd}-"; var codeList = DbContext.trade_contract_document.Where(O => O.Code.StartsWith(numberPrefix)).OrderBy(O => O.id).Select(O => O.Code).ToArray(); do { ContractCode = $"{numberPrefix}{num:00}"; num++; } while (codeList.Contains(ContractCode)); return ContractCode; } /// /// 买卖方向: ///确认书:甲方固定为我方 ///系统:买入多头 确认书:乙方卖出 ///系统:买入空头 确认书:乙方买入 ///系统:卖出多头 确认书:乙方买入 ///系统:卖出空头 确认书: 乙方卖出 /// /// /// /// public string ToCallPut(string CallPut, string BuySell, string TradeType) { if ((BuySell == "买入" && (CallPut == "Call" || CallPut == "看涨")) || (BuySell == "卖出" && (CallPut == "Put" || CallPut == "看跌"))) { return "乙方卖出"; } if ((BuySell == "买入" && (CallPut == "Put" || CallPut == "看跌")) || (BuySell == "卖出" && (CallPut == "Call" || CallPut == "看涨"))) { return "乙方买入"; } return ""; } /// /// 保存合同信息 /// /// /// /// /// /// public trade_contract_document SaveTradeContract(trade t, trade_cash tc, string contractCode, DateTime date, Client firstParty, Client client, string contractVersion, string outPath, string outFileName, bool IsTrade) { string oldContractCode = ""; var contractTradeRelation = IsTrade ? DbContext.trade_contract_r.Where(d => d.Type == ContractTypeEnum.Trade && d.IsValid).FirstOrDefault(x => x.TradeId == t.id) : DbContext.trade_contract_r.Where(d => d.Type == ContractTypeEnum.Clearing && d.IsValid).FirstOrDefault(x => x.TradeId == t.id && x.TradeCashId == tc.id); if (contractTradeRelation == null) { contractTradeRelation = new trade_contract_r(); DbContext.trade_contract_r.Add(contractTradeRelation); } else { oldContractCode = contractTradeRelation.ContractCode; } contractTradeRelation.TradeId = t.id; contractTradeRelation.TradeNumber = t.TradeNumber; contractTradeRelation.ContractCode = contractCode; contractTradeRelation.OptId = OptUser.UserId; contractTradeRelation.OptName = OptUser.UserName; contractTradeRelation.OptDate = DateTime.Now; contractTradeRelation.Type = IsTrade ? ContractTypeEnum.Trade : ContractTypeEnum.Clearing; contractTradeRelation.TradeClearCode = IsTrade ? null : contractCode; contractTradeRelation.TradeCashId = IsTrade ? 0 : tc.id; contractTradeRelation.IsValid = true; if (!string.IsNullOrEmpty(oldContractCode)) { var removeTradeContract = IsTrade ? DbContext.trade_contract_document.Where(x => x.Code == oldContractCode && x.Type == ContractTypeEnum.Trade) : DbContext.trade_contract_document.Where(x => x.Code == oldContractCode && (x.Type == ContractTypeEnum.Clearing || x.Type == ContractTypeEnum.UnWind)); DbContext.trade_contract_document.RemoveRange(removeTradeContract); DbContext.SaveChanges(); } var tradeContract = IsTrade ? DbContext.trade_contract_document.FirstOrDefault(x => x.Code == contractCode && x.Type == ContractTypeEnum.Trade) : DbContext.trade_contract_document.FirstOrDefault(x => x.Code == contractCode && (x.Type == ContractTypeEnum.Clearing || x.Type == ContractTypeEnum.UnWind)); if (tradeContract == null) { tradeContract = new trade_contract_document(); DbContext.trade_contract_document.Add(tradeContract); } tradeContract.Code = contractCode; tradeContract.ValueDate = date; tradeContract.BuyerId = client.id; tradeContract.BuyerName = client.Name; tradeContract.SellerId = firstParty.id; tradeContract.SellerName = firstParty.Name; tradeContract.ContractVersion = contractVersion; tradeContract.FileType = FileTypeEnum.Pdf; tradeContract.Status = ContractStatusEnum.Draft; tradeContract.Paths = outPath; tradeContract.FileName = outFileName; tradeContract.Type = IsTrade ? ContractTypeEnum.Trade : ContractTypeEnum.Clearing; tradeContract.OptId = OptUser.UserId; tradeContract.OptName = OptUser.UserName; tradeContract.OptDate = DateTime.Now; DbContext.SaveChanges(); return tradeContract; } #endregion } }