1040 lines
46 KiB
C#
1040 lines
46 KiB
C#
using NPOI.Util;
|
||
using Org.BouncyCastle.Asn1.Ocsp;
|
||
using System.Text;
|
||
using YLErp.BLL;
|
||
using YLErp.BLL.GenerateDocument;
|
||
using YLErp.DBModels.Consts;
|
||
using YLErp.Helpers;
|
||
using YLErp.Model;
|
||
using YLErp.Model.Enum;
|
||
using YLErp.Models;
|
||
using YLErp.Modules.TradeDalModule;
|
||
using YLErp.MsOffice;
|
||
using YLErp.Modules.TradeModule.DealModule;
|
||
using YLErp.Office;
|
||
using YLErp.Office.Converters;
|
||
using YLErp.Plugins.TradeDocGenerator.Abstracts;
|
||
|
||
namespace YLErp.Modules.TradeModule.DocGenerateModule
|
||
{
|
||
/// <summary>
|
||
/// 生成交易确认书
|
||
/// </summary>
|
||
public class ConfirmationGenerateService : YLBaseService
|
||
{
|
||
public ConfirmationGenerateService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
|
||
}
|
||
|
||
public ConfirmationGenerateService(YLBaseService baseService) : base(baseService)
|
||
{
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成交易确认书
|
||
/// </summary>
|
||
public List<TradeDocGenerateResult> Generate(IEnumerable<int> tradeIds, string docType)
|
||
{
|
||
var logger = LogFactory.GetLogger("生成确认书详细日志");
|
||
logger.Info($"[Generate] 开始生成确认书 - tradeIds: {string.Join(",", tradeIds)}, docType: {docType}");
|
||
|
||
if (tradeIds == null)
|
||
{
|
||
logger.Error("[Generate] tradeIds 为 null");
|
||
throw new ArgumentNullException(nameof(tradeIds));
|
||
}
|
||
|
||
if (docType == null)
|
||
{
|
||
logger.Error("[Generate] docType 为 null");
|
||
throw new ArgumentNullException(nameof(docType));
|
||
}
|
||
|
||
//----------------------------------
|
||
// 获取当日行权 平仓 到期的有效交易
|
||
//----------------------------------
|
||
List<string> statusList = new List<string>() {
|
||
ConsTrade.确认成交
|
||
};
|
||
if (GlobalConfig.EnableGenerateOnTradeClosed)
|
||
{
|
||
statusList.Add(ConsTrade.已到期);
|
||
statusList.Add(ConsTrade.已平仓);
|
||
statusList.Add(ConsTrade.已执行);
|
||
statusList.Add(ConsTrade.审批中);
|
||
}
|
||
TradeDalService service = new TradeDalService(this);
|
||
|
||
//var tradeQuery = from trad in DbContext.trade
|
||
// where tradeIds.Contains(trad.id)
|
||
// && statusList.Contains(trad.TradeStatus)
|
||
// && trad.ValidState != "InValid" && trad.ClientId != null
|
||
// && (trad.ParentTradeId == null || trad.ParentTradeId < 1)
|
||
// select trad;
|
||
|
||
|
||
//----------------------------------
|
||
// 交易现金交割记录
|
||
//----------------------------------
|
||
logger.Info("[Generate] 开始查询交易数据");
|
||
List<trade> allTradeList = allTradeList = DbContext.trade.Where(x => tradeIds.Contains(x.id)).ToList();
|
||
logger.Info($"[Generate] 查询到交易数: {allTradeList.Count}");
|
||
|
||
if (allTradeList.Count < 1)
|
||
{
|
||
logger.Error("[Generate] 没有找到交易记录");
|
||
throw new ServiceException("没有已确认的记录信息!");
|
||
}
|
||
|
||
var tradeDic = allTradeList.Where(O =>
|
||
statusList.Contains(O.TradeStatus)
|
||
&& O.ClientId != 0
|
||
&& O.ParentTradeId == 0)
|
||
.ToDictionary(n => n.id, n => new trade_contract_group { trade = n });
|
||
var cashQuery = from trade in allTradeList
|
||
join tradeCash in DbContext.trade_cash on trade.id equals tradeCash.TradeId
|
||
where tradeCash.ValidState != ConsGlobal.InValid && !tradeCash.IsDeleted
|
||
&& tradeCash.Action == ClientCashInCashOut.系统操作_期权费
|
||
select tradeCash;
|
||
var cashDatas = cashQuery.ToDictionary(K => K.TradeId, V => V);
|
||
//var cashSwapQuery = from trade in allTradeList
|
||
// join tradeCashSwap in DbContext.swap_event on trade.id equals tradeCashSwap.SwapTradeId
|
||
// where tradeCashSwap.EventType == 9
|
||
// select tradeCashSwap;
|
||
//var cashSwapDatas = cashSwapQuery.ToDictionary(K => K.SwapTradeId, V => V);
|
||
trade_cash cash = null;
|
||
//swap_event cashSwap = null;
|
||
foreach (var item in tradeDic.Values)
|
||
{
|
||
if (item.trade.TradeType == "结构化交易")
|
||
{
|
||
item.trade.SubTrades = allTradeList.Where(O => O.ParentTradeId == item.trade.id);
|
||
List<string> cashTig = new List<string>();
|
||
foreach (var t in item.trade.SubTrades)
|
||
{
|
||
if (cashDatas.TryGetValue(t.id, out cash))
|
||
{
|
||
t.trade_cash = cash;
|
||
item.trade.trade_cash = cash;
|
||
}
|
||
}
|
||
if (item.trade.IsGroup == 1)
|
||
{
|
||
item.trade.SubTrades = DbContext.trade.Where(O => O.ParentTradeId == item.trade.id).ToList();
|
||
foreach (var t in item.trade.SubTrades)
|
||
{
|
||
t.trade_cash = DbContext.trade_cash.FirstOrDefault(l => l.TradeId == t.id);
|
||
}
|
||
if (cashDatas.TryGetValue(item.trade.id, out cash))
|
||
{
|
||
item.trade.trade_cash = cash;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (item.trade.TradeType == "累计期权")
|
||
{
|
||
new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(item.trade, item.trade.TradeDate.Value);
|
||
}
|
||
|
||
if (cashDatas.TryGetValue(item.trade.id, out cash))
|
||
{
|
||
item.trade_cash = cash;
|
||
item.trade.trade_cash = cash;
|
||
}
|
||
}
|
||
}
|
||
|
||
//----------------------------------
|
||
// 生成交易确认书
|
||
//----------------------------------
|
||
|
||
var resultList = new List<TradeDocGenerateResult>();
|
||
|
||
AppManager.TryResolve(out ITradeConfirmationGenerator generatorV2);
|
||
|
||
if (generatorV2 == null && !PS.Config.UseOldGenerateBook)
|
||
{
|
||
var baseGenerate = BatchGenerateFactory.GetInstance(tradeIds, UserId, UserName, ContractTypeEnum.Trade, docType);
|
||
var returnInfo = baseGenerate.GenerateBooks();
|
||
var trad = new trade();
|
||
resultList = returnInfo.Content.Select(n => new TradeDocGenerateResult(trad) { OutputFilePath = n }).ToList();
|
||
if (!returnInfo.Success)
|
||
{
|
||
resultList.Add(new TradeDocGenerateResult(trad) { ErrorMessage = returnInfo.Msg });
|
||
}
|
||
return resultList;
|
||
}
|
||
|
||
var tradeContractGroupTradeIds = tradeDic.Values.Select(x => x.trade.id).ToList();
|
||
//var childs = DbContext.trade.Where(x => tradeContractGroupTradeIds.Contains(x.ParentTradeId ?? 0));
|
||
var childs = service.GetStructTradeFromTradeOrEodTrade(tradeContractGroupTradeIds, SystemValueDate, true);
|
||
LogFactory.GetLogger("交易确认书").Info("交易确认书1:" + string.Join(",", tradeContractGroupTradeIds));
|
||
if (PS.Config.Company == Configuration.CompanyEnum.浙商)
|
||
{
|
||
LogFactory.GetLogger("交易确认书").Info("交易确认书浙商");
|
||
if (generatorV2 != null)
|
||
{
|
||
LogFactory.GetLogger("交易确认书").Info("交易确认书插件");
|
||
var result = GenerateSingleV2(generatorV2, null, docType , allTradeList);
|
||
resultList.Add(result);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
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)))
|
||
{
|
||
resultList.Add(new TradeDocGenerateResult(tradeContractGroup.trade)
|
||
{
|
||
ErrorMessage = "缺少现金交割记录"
|
||
});
|
||
}
|
||
else if (generatorV2 != null && !isOptionTrade)
|
||
{
|
||
var result = GenerateSingleV2(generatorV2, tradeContractGroup, docType);
|
||
resultList.Add(result);
|
||
}
|
||
else
|
||
{
|
||
var result = GenerateSingleV1(tradeContractGroup, docType);
|
||
resultList.Add(result);
|
||
//if (!string.IsNullOrWhiteSpace(result.OutputFilePath))
|
||
//{
|
||
// tradeContractGroup.trade.HasGeneratedConfirmBook = 1;
|
||
// var childTds = childs.Where(x => x.ParentTradeId == tradeContractGroup.trade.id);
|
||
// foreach (var child in childTds)
|
||
// {
|
||
// child.HasGeneratedConfirmBook = 1;
|
||
// }
|
||
//}
|
||
}
|
||
}
|
||
}
|
||
if (generatorV2 == null)
|
||
{
|
||
DbContext.SaveChanges();
|
||
}
|
||
|
||
return resultList;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
private TradeDocGenerateResult GenerateSingleV2(ITradeConfirmationGenerator generator, trade_contract_group tradeContractGroup, string docType = "DOCX", List<trade> allTrades = null)
|
||
{
|
||
try
|
||
{
|
||
//修改调用的构造方法
|
||
List<trade> trades = new List<trade>();
|
||
if (allTrades != null && allTrades.Count() > 0)
|
||
{
|
||
trades = allTrades;
|
||
}else
|
||
{
|
||
trades = new List<trade>() { tradeContractGroup.trade };
|
||
}
|
||
List<int> ids = new List<int>();
|
||
if (tradeContractGroup != null && tradeContractGroup.trade != null && tradeContractGroup.trade.trade_cash != null)
|
||
{
|
||
ids.Add(tradeContractGroup.trade.trade_cash.id);
|
||
}
|
||
var context = new ConfirmationGenerateContext(
|
||
ids,
|
||
trades,
|
||
ContractTypeEnum.Trade, OptUser, null, null);
|
||
generator.Initialize(docType, context);
|
||
LogFactory.GetLogger("交易确认书").Info("交易确认书init");
|
||
return generator.GenerateDocument();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogFactory.GetLogger("GenerateSingleV2").Error(ex);
|
||
|
||
return new TradeDocGenerateResult(tradeContractGroup.trade)
|
||
{
|
||
ErrorMessage = ex.Message
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
private TradeDocGenerateResult GenerateSingleV1(trade_contract_group contractGroup, string docType = "DOCX")
|
||
{
|
||
var logger = LogFactory.GetLogger("生成确认书详细日志");
|
||
logger.Info($"[GenerateSingleV1] 开始处理 - docType: {docType}");
|
||
|
||
if (contractGroup == null)
|
||
{
|
||
logger.Error("[GenerateSingleV1] contractGroup 为 null");
|
||
throw new ArgumentNullException(nameof(contractGroup));
|
||
}
|
||
|
||
var trade = contractGroup.trade;
|
||
logger.Info($"[GenerateSingleV1] 交易信息 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}, TradeType: {trade?.TradeType}");
|
||
|
||
var contractBLL = new TradeContractBLL(this);
|
||
tradeBLL.SetFieldsByTradeType(trade);
|
||
var needAddData = true;
|
||
|
||
#region 如果之前交易确认书已经生成过,则直接返回
|
||
|
||
var confirmDoc = contractBLL.GetContractDoc(trade.id, ContractTypeEnum.Trade);
|
||
if (confirmDoc != null)
|
||
{
|
||
if (File.Exists(confirmDoc.AbsolutePath))
|
||
{
|
||
if (docType == "PDF")
|
||
{
|
||
var pdfPath = confirmDoc.AbsolutePath.Replace(".docx", ".pdf");
|
||
if (!File.Exists(pdfPath))
|
||
{
|
||
OfficeFileConverter.ConvertDocxToPDF(confirmDoc.AbsolutePath, pdfPath);
|
||
}
|
||
return new TradeDocGenerateResult(trade)
|
||
{
|
||
OutputFilePath = YLErp.Helpers.PathHelper.PhysicalPathToWebPath(pdfPath, OtcAppContext.RootPath)
|
||
};
|
||
}
|
||
return new TradeDocGenerateResult(trade)
|
||
{
|
||
OutputFilePath = YLErp.Helpers.PathHelper.PhysicalPathToWebPath(confirmDoc.AbsolutePath.Replace(".pdf", ".docx"), OtcAppContext.RootPath)
|
||
};
|
||
}
|
||
else
|
||
{
|
||
needAddData = false;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
var isGJBuyer = trade.BuySell == "买入";
|
||
|
||
var underlying = underlying_managerBLL.GetByCode(trade.UnderlyingCode);
|
||
if (underlying == null)
|
||
{
|
||
return new TradeDocGenerateResult(trade)
|
||
{
|
||
ErrorMessage = $"标的资产{trade.UnderlyingCode}信息未找到"
|
||
};
|
||
}
|
||
|
||
var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId);
|
||
|
||
if (client == null)
|
||
{
|
||
return new TradeDocGenerateResult(trade)
|
||
{
|
||
ErrorMessage = $"客户{trade.ClientId}信息未找到"
|
||
};
|
||
}
|
||
|
||
var dic = new Dictionary<string, object>
|
||
{
|
||
["{{乙方名称}}"] = client.Name
|
||
};
|
||
|
||
if (isGJBuyer)
|
||
{
|
||
dic["{{甲方方向}}"] = "买方";
|
||
dic["{{乙方方向}}"] = "卖方";
|
||
}
|
||
else
|
||
{
|
||
dic["{{甲方方向}}"] = "卖方";
|
||
dic["{{乙方方向}}"] = "买方";
|
||
}
|
||
|
||
|
||
dic["{{交易日}}"] = trade.TradeDate?.ToString("yyyy年M月d日");
|
||
if (trade.TradeType == "香草期权" || trade.TradeType == "亚式期权")
|
||
{
|
||
dic["{{生效日}}"] = trade.TradeDate?.ToString("yyyy年M月d日");
|
||
}
|
||
else
|
||
{
|
||
dic["{{生效日}}"] = trade.StartDate?.ToString("yyyy年M月d日");
|
||
}
|
||
if (trade.TradeType == "亚式期权")
|
||
{
|
||
dic["{{均价起算日}}"] = trade.trade_asian_option.AveragingPeriodStartDate?.ToString("yyyy年M月d日");
|
||
dic["{{奇异结构}}"] = $"{trade.trade_asian_option.StrikeTypeCn}的{trade.trade_asian_option.PayoffTypeCn}亚式";
|
||
}
|
||
dic["{{到期日}}"] = trade.ExerciseDate?.ToString("yyyy年M月d日");
|
||
dic["{{行权日}}"] = "到期日";
|
||
|
||
dic["{{自动行权}}"] = "到期实值自动行权";
|
||
|
||
//商品标的
|
||
|
||
if (trade.TradeType == "合成价差期权")
|
||
{
|
||
dic["{{商品标的名称}}"] = synthetic_underlyingBLL.GetUnderlyingTipsInfo(trade.UnderlyingCode);
|
||
}
|
||
else
|
||
{
|
||
dic["{{商品标的名称}}"] = trade.UnderlyingCode;
|
||
}
|
||
|
||
string strUnit = underlying.TradeUnitString;
|
||
double notional = trade.Notional;
|
||
|
||
dic["{{报价单位}}"] = strUnit;
|
||
dic["{{商品标的数量}}"] = $"{notional}{strUnit}";
|
||
dic["{{商品标的交易所}}"] = underlying.MarketName;
|
||
|
||
//价格
|
||
if (trade.ActualStrike != null)
|
||
{
|
||
if (trade.IsMoneynessOption == "是")
|
||
{
|
||
dic["{{执行价格}}"] = trade.Strike.OtcFormatUmPrice(true);
|
||
}
|
||
else
|
||
{
|
||
dic["{{执行价格}}"] = $"{trade.ActualStrike.OtcFormatUmPrice()}元/{strUnit}";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dic["{{执行价格}}"] = "不适用";
|
||
}
|
||
|
||
var tradeType = trade.TradeType == "香草期权" ? "普通" : (trade.TradeType == "结构化交易" ? trade.StructureType : trade.TradeType.Replace("期权", ""));
|
||
dic["{{期权类型}}"] = $"{trade.ExerciseModeCn}{tradeType}{trade.OptionType}期权";
|
||
if (trade.TradeType == "远期")
|
||
{
|
||
dic["{{期权费}}"] = $"{(trade.TradePrice * (trade.BuySell == "卖出" ? -1 : 1) ?? 0).OtcFormatMoney(grouping: false)}元";
|
||
}
|
||
else
|
||
{
|
||
dic["{{期权费}}"] = $"{(trade.TradePrice ?? 0).OtcFormatMoney(grouping: false)}元";
|
||
}
|
||
|
||
dic["{{协议编号}}"] = client.Remark;
|
||
|
||
dic["{{期权价格}}"] = $"{(trade.TradeSinglePrice ?? 0).OtcFormat(OtcFormatFlag.tradeSinglePrice)}元/{strUnit}";
|
||
dic["{{支付时间}}"] = trade.PremiumPayDate != null ? "提前终止/到期时支付" : "已支付";
|
||
|
||
dic["{{期权方向}}"] = $"甲方{trade.BuySell}";
|
||
dic["{{名义本金}}"] = trade.StockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional);
|
||
dic["{{期初价格}}"] = trade.SpotPrice.OtcFormatUmPrice();
|
||
dic["{{期权费率}}"] = ((trade.PremiumRate ?? ((trade.TradePrice ?? 0) / trade.StockEqvNotional)) * 100).OtcFormatFlex(2);
|
||
|
||
string settlementType = "价格";
|
||
if (trade.SettlementType == SettlementTypeEnum.SettlePrice)
|
||
{
|
||
settlementType = "结算价";
|
||
}
|
||
else if (trade.SettlementType == SettlementTypeEnum.ClosePrice)
|
||
{
|
||
settlementType = "收盘价";
|
||
}
|
||
|
||
if (trade.ExerciseMode == "American")
|
||
{
|
||
dic["{{美式期权结算价}}"] = $"所属交易所公布的标的合约可成交的最新价";
|
||
dic["{{欧式期权结算价}}"] = $"美式期权不适用";
|
||
}
|
||
else
|
||
{
|
||
dic["{{美式期权结算价}}"] = $"欧式期权不适用";
|
||
dic["{{欧式期权结算价}}"] = $"所属交易所在到期日收盘后公布的标的合约{settlementType}";
|
||
}
|
||
|
||
if (trade.UnderlyingInstrumentType != "CommodityFutures")
|
||
{
|
||
dic["{{证券名称}}"] = underlying.UnderlyingName;
|
||
dic["{{证券代码}}"] = underlying.UnderlyingCode;
|
||
|
||
dic["{{账户信息乙方}}"] = client.Name;
|
||
dic["{{联系方式乙方}}"] = client.Name;
|
||
dic["{{联系方式乙方联系人}}"] = client.Name;
|
||
dic["{{联系方式乙方电话}}"] = client.Phone;
|
||
dic["{{联系方式乙方电子邮件}}"] = client.Email;
|
||
|
||
var bankcard = DbContextFactory.GetClientDbContext(null).bankcard.FirstOrDefault(s => s.ApprovalOrder < 1 && s.ClientId == trade.ClientId && s.ValidState == "Valid");
|
||
|
||
if (bankcard != null)
|
||
{
|
||
dic["{{账户信息乙方户名}}"] = bankcard.ClientName;
|
||
dic["{{账户信息乙方账号}}"] = bankcard.Card;
|
||
dic["{{账户信息乙方开户行}}"] = bankcard.Bank;
|
||
}
|
||
|
||
dic["{{期权方向}}"] = $"甲方{trade.BuySell}";
|
||
dic["{{名义本金}}"] = trade.StockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional);
|
||
}
|
||
|
||
if (trade.OptionType == "看涨")
|
||
{
|
||
if (trade.ExerciseMode == "American")
|
||
{
|
||
dic["{{美式期权结算金额}}"] =
|
||
$"(行权时{trade.UnderlyingCode}合约可成交最新价 - {trade.ActualStrike.OtcFormatUmPrice()})× {notional.OtcFormatNotional()}";
|
||
dic["{{欧式期权结算金额}}"] = $"美式期权不适用";
|
||
}
|
||
else
|
||
{
|
||
dic["{{美式期权结算金额}}"] = $"欧式期权不适用";
|
||
dic["{{欧式期权结算金额}}"] =
|
||
$"({trade.ExerciseDate?.ToString("yyyy年M月d日")}{trade.UnderlyingCode}合约{settlementType} - {trade.ActualStrike.OtcFormatUmPrice()})× {notional.OtcFormatNotional()}";
|
||
}
|
||
}
|
||
else if (trade.OptionType == "看跌")
|
||
{
|
||
if (trade.ExerciseMode == "American")
|
||
{
|
||
dic["{{美式期权结算金额}}"] =
|
||
$"({trade.ActualStrike.OtcFormatUmPrice()} - 行权时{trade.UnderlyingCode}合约可成交最新价)× {notional.OtcFormatNotional()}";
|
||
dic["{{欧式期权结算金额}}"] = $"美式期权不适用";
|
||
}
|
||
else
|
||
{
|
||
dic["{{美式期权结算金额}}"] = $"欧式期权不适用";
|
||
dic["{{欧式期权结算金额}}"] =
|
||
$"({trade.ActualStrike.OtcFormatUmPrice()} - {trade.ExerciseDate?.ToString("yyyy年M月d日")}{trade.UnderlyingCode}合约{settlementType})× {notional.OtcFormatNotional()}";
|
||
}
|
||
}
|
||
|
||
//合同编号
|
||
string contractNo;
|
||
string outPutFileName;
|
||
if (confirmDoc != null)
|
||
{
|
||
contractNo = confirmDoc.Code;
|
||
outPutFileName = confirmDoc.FileName;
|
||
}
|
||
else
|
||
{
|
||
var contractIndex = contractBLL.GetContractNo("GTJA");
|
||
if (PS.Config.CompanyID == "中财资本")
|
||
{
|
||
contractNo = $"{trade.TradeNumber}";
|
||
}
|
||
else
|
||
{
|
||
contractNo = $"{client.Number}C{contractIndex}";
|
||
}
|
||
outPutFileName = $"{client.Name}交易确认书C{contractIndex}.docx";
|
||
}
|
||
dic["{{合同编号}}"] = contractNo;
|
||
dic["{{结算方式}}"] = "人民币现金结算";
|
||
dic["{{补充}}"] = "补充";
|
||
|
||
var confirmLogger = LogFactory.GetLogger("生成确认书详细日志");
|
||
try
|
||
{
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 开始生成 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}, TradeType: {trade?.TradeType}");
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 模板路径配置 - TradeType: {trade.TradeType}, IsUsePremiumRate: {trade.IsUsePremiumRate}");
|
||
|
||
var templateDir = OtcAppContext.MapPath("~/App_Docs/GTJA");
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] templateDir: {templateDir}");
|
||
|
||
var templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书模板.docx");
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 默认模板路径: {templatePath}");
|
||
|
||
if (trade.TradeType == "亚式期权")
|
||
{
|
||
templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书(亚式期权)模板.docx");
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 使用亚式期权模板: {templatePath}");
|
||
}
|
||
|
||
if (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value)
|
||
{
|
||
templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书(名义本金)模板.docx");
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 使用名义本金模板: {templatePath}");
|
||
}
|
||
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 最终模板路径: {templatePath}");
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 输出文件名: {outPutFileName}");
|
||
|
||
// 检查模板文件是否存在
|
||
if (!File.Exists(templatePath))
|
||
{
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 模板文件不存在: {templatePath}");
|
||
return new TradeDocGenerateResult(trade) { ErrorMessage = $"模板文件不存在: {templatePath}" };
|
||
}
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 模板文件存在,准备生成");
|
||
|
||
LogFactory.GetLogger("交易确认书").Info("交易确认书:" + outPutFileName);
|
||
|
||
string outPutFilePath = Path.Combine(templateDir, outPutFileName);
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 输出文件路径: {outPutFilePath}");
|
||
|
||
// 确保输出目录存在
|
||
if (!Directory.Exists(templateDir))
|
||
{
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 创建输出目录: {templateDir}");
|
||
Directory.CreateDirectory(templateDir);
|
||
}
|
||
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] 调用 MsWordTempalteHelper.GenerateFromTemplate");
|
||
MsWordTempalteHelper.GenerateFromTemplate(templatePath, dic, outPutFilePath);
|
||
confirmLogger.Info($"[GenerateSingleConfirmBook] MsWordTempalteHelper.GenerateFromTemplate 完成");
|
||
|
||
if (docType == "PDF" && !outPutFilePath.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
MsWordFormatConverter.ConvertToPDF(outPutFilePath, outPutFilePath.Replace(".docx", ".pdf"));
|
||
}
|
||
|
||
//更新操作时间
|
||
var now = DateTime.Now;
|
||
File.SetLastAccessTime(outPutFilePath, now);
|
||
|
||
#region --- Add trade_contract_document and trade_contract_r records
|
||
|
||
if (needAddData)
|
||
{
|
||
contractBLL.DeleteConfirmBook(trade.id, null);
|
||
|
||
var nowDate = DateTime.Now;
|
||
|
||
var contractRecord = new trade_contract_document();
|
||
DbContext.trade_contract_document.Add(contractRecord);
|
||
contractRecord.Code = dic["{{合同编号}}"].ToString();
|
||
contractRecord.ValueDate = valuedateBLL.ValueDate;
|
||
contractRecord.BuyerId = isGJBuyer ? 0 : trade.ClientId;
|
||
contractRecord.BuyerName = isGJBuyer ? PS.Config.CompanyFullName : trade.ClientName;
|
||
contractRecord.SellerId = isGJBuyer ? trade.ClientId : 0;
|
||
contractRecord.SellerName = isGJBuyer ? trade.ClientName : PS.Config.CompanyFullName;
|
||
contractRecord.FileType = FileTypeEnum.Word;
|
||
contractRecord.Status = ContractStatusEnum.Draft;
|
||
contractRecord.SourceType = 1; // 系统生成
|
||
contractRecord.Paths = $"App_Docs\\GTJA\\{outPutFileName}";
|
||
contractRecord.FileName = outPutFileName;
|
||
contractRecord.Type = ContractTypeEnum.Trade;
|
||
contractRecord.ClientId = trade.ClientId;
|
||
contractRecord.OptId = UserId;
|
||
contractRecord.OptName = UserName;
|
||
contractRecord.OptDate = nowDate;
|
||
|
||
var contractTradeRelation = new trade_contract_r();
|
||
DbContext.trade_contract_r.Add(contractTradeRelation);
|
||
contractTradeRelation.TradeId = trade.id;
|
||
contractTradeRelation.TradeNumber = trade.TradeNumber;
|
||
contractTradeRelation.ContractCode = contractRecord.Code;
|
||
contractTradeRelation.Type = ContractTypeEnum.Trade;
|
||
contractTradeRelation.OptId = UserId;
|
||
contractTradeRelation.OptName = UserName;
|
||
contractTradeRelation.OptDate = nowDate;
|
||
contractTradeRelation.TradeCashId = contractGroup.trade_cash == null ? 0 : contractGroup.trade_cash.id;
|
||
contractTradeRelation.IsValid = true;
|
||
|
||
DbContext.SaveChanges();
|
||
}
|
||
|
||
#endregion --- Add trade_contract_document and trade_contract_r records
|
||
|
||
return new TradeDocGenerateResult(trade)
|
||
{
|
||
OutputFilePath = $"/App_Docs/GTJA/{(docType == "PDF" ? outPutFileName.Replace(".docx", ".pdf") : outPutFileName)}"
|
||
};
|
||
}
|
||
|
||
catch (Exception e)
|
||
{
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 发生异常 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}", e);
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 异常类型: {e.GetType().FullName}");
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 异常消息: {e.Message}");
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 异常堆栈: {e.StackTrace}");
|
||
|
||
// 检查是否是内部异常
|
||
if (e.InnerException != null)
|
||
{
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 内部异常类型: {e.InnerException.GetType().FullName}");
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 内部异常消息: {e.InnerException.Message}");
|
||
confirmLogger.Error($"[GenerateSingleConfirmBook] 内部异常堆栈: {e.InnerException.StackTrace}");
|
||
}
|
||
|
||
LogFactory.GetLogger<ConfirmationGenerateService>().Error("GenerateSingleConfirmBook出错", e);
|
||
return new TradeDocGenerateResult(trade) { ErrorMessage = e.Message };
|
||
}
|
||
}
|
||
|
||
|
||
|
||
public (bool, string, DateTime, string) UploadContractFile(string encryptId, string fileDescription, string ContractCode, bool OurpartySeal, bool CounterpartySeal, UploadFileModel uploadFileModel)
|
||
{
|
||
|
||
try
|
||
{
|
||
var msg = "";
|
||
#region 验证
|
||
if (string.IsNullOrWhiteSpace(encryptId))
|
||
{
|
||
|
||
return (false, "请传入id", DateTime.Now, "");
|
||
}
|
||
|
||
var intid = DataProtectHelper.DecryptInt(encryptId);
|
||
|
||
var db = DbContextFactory.GetYLDbContext();
|
||
|
||
var contractDoc = db.trade_contract_document.Find(intid);
|
||
var contractDocRArr = db.trade_contract_r.Where(O => O.ContractCode == contractDoc.Code && O.Type == contractDoc.Type && O.IsValid).ToArray();
|
||
var contractDocR = contractDocRArr.FirstOrDefault();
|
||
if (contractDoc == null || contractDocR == null)
|
||
{
|
||
return (false, "没有相关的确认书文档", DateTime.Now, "");
|
||
}
|
||
|
||
var isExistCode = true;
|
||
if (!string.IsNullOrWhiteSpace(ContractCode))
|
||
{
|
||
var Codes = new List<string>
|
||
{
|
||
ContractCode
|
||
};
|
||
|
||
var oldDoc = db.trade_contract_document.Where(O => O.Code == ContractCode).FirstOrDefault();
|
||
isExistCode = oldDoc != null ||
|
||
(new TradeContractGenerateService(OptUser).GetExistsContractCode(Codes).Any());
|
||
if (isExistCode)
|
||
{
|
||
if (!contractDoc.Code.Equals(ContractCode))
|
||
{
|
||
if (db.trade_contract_r.Where(O => O.ContractCode == ContractCode && O.IsValid).Any())
|
||
{
|
||
return (false, "合约编号已存在", DateTime.Now, "");
|
||
}
|
||
else
|
||
{
|
||
if (oldDoc != null)
|
||
{
|
||
db.trade_contract_document.Remove(oldDoc);
|
||
isExistCode = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
msg = "修改成功";
|
||
}
|
||
#endregion
|
||
|
||
//var files = Request.Form.Files;
|
||
|
||
if (uploadFileModel?.Length > 0)
|
||
{
|
||
//var file = files[0];
|
||
|
||
|
||
|
||
// var str = Encoding.UTF8.GetString(fileBytes);
|
||
|
||
|
||
#region 验证
|
||
if (PS.Config.ErpElement.SecuritiesEnvironment)
|
||
{
|
||
if (System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".xlsx"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".xls"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".pdf"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".docx"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".doc")
|
||
{
|
||
return (false, "请上传PDF/Word/Excel格式的文件", DateTime.Now, "");
|
||
}
|
||
}
|
||
if (PS.Config.ErpElement.SecuritiesEnvironment && uploadFileModel.Length > Modules.SuperviseReportModule.SAC.Model.ReportStatusModel.MaxAnnexLength)//小于30MB
|
||
{
|
||
return (false, "上传失败,单笔确认书文件大小不应超过30MB", DateTime.Now, "");
|
||
}
|
||
#endregion
|
||
|
||
var tradeNumber = "未知";
|
||
if (contractDocR != null)
|
||
{
|
||
tradeNumber = contractDocR.TradeNumber;
|
||
}
|
||
var filePath = Path.Combine(OtcAppContext.AppDocsPath, "contractdoc", YLErp.PS.Config.Company.ToString(), tradeNumber);
|
||
|
||
if (!Directory.Exists(filePath))
|
||
{
|
||
Directory.CreateDirectory(filePath);
|
||
}
|
||
|
||
#region 设置用印文件名
|
||
|
||
var stampFileFullName = Path.Combine(filePath, uploadFileModel.FileName);
|
||
contractDoc.StampDocumentFileName = stampFileFullName.Replace(OtcAppContext.AppDocsPath, "/App_Docs").Replace("\\", "/");
|
||
|
||
#endregion
|
||
|
||
if (File.Exists(stampFileFullName))
|
||
{
|
||
File.Delete(stampFileFullName);
|
||
}
|
||
|
||
using var fs = new FileStream(stampFileFullName, FileMode.Create, FileAccess.ReadWrite);
|
||
|
||
//using var fs = File.OpenWrite(filePath);
|
||
|
||
using var sr = uploadFileModel.OpenReadStream();
|
||
|
||
sr.Seek(0, SeekOrigin.Begin);
|
||
var tempBytes = new byte[sr.Length];
|
||
sr.Read(tempBytes, 0, tempBytes.Length);
|
||
|
||
////var tempstr = Encoding.UTF8.GetString(tempBytes);
|
||
fs.Write(tempBytes, 0, tempBytes.Length);
|
||
|
||
sr.Close();
|
||
|
||
fs.Close();
|
||
|
||
|
||
|
||
//sr.CopyTo(fs);
|
||
msg = msg.Length > 0 ? msg : "上传成功";
|
||
}
|
||
|
||
#region 更新数据
|
||
contractDoc.Comments = fileDescription;
|
||
if (OurpartySeal && CounterpartySeal)
|
||
{ contractDoc.Status = ContractStatusEnum.BothPartyStamped; }
|
||
else if (OurpartySeal) { contractDoc.Status = ContractStatusEnum.OurPartyStamped; }
|
||
else if (CounterpartySeal) { contractDoc.Status = ContractStatusEnum.CounterPartyStamped; }
|
||
else { contractDoc.Status = ContractStatusEnum.Draft; }
|
||
if (!isExistCode)//默认为存在,判断 要有修改的合约编号且修改后的编号不存在
|
||
{
|
||
//trade_meta
|
||
var meta = db.TradeMeta.Where(O => (O.MetaKey == ConsTradeMetaKey.ContractCode || O.MetaKey.Contains("确认书")) && O.MetaValue == contractDoc.Code).FirstOrDefault();
|
||
if (meta != null)
|
||
{
|
||
meta.MetaValue = ContractCode;
|
||
}
|
||
foreach (var docR in contractDocRArr)
|
||
{
|
||
docR.ContractCode = ContractCode;
|
||
docR.OptDate = DateTime.Now;
|
||
docR.OptId = OptUser.UserId;
|
||
docR.OptName = OptUser.UserName;
|
||
}
|
||
contractDocR.ContractCode = ContractCode;
|
||
contractDocR.TradeClearCode = ContractCode;
|
||
contractDoc.OptDate = DateTime.Now;
|
||
contractDoc.OptId = OptUser.UserId;
|
||
contractDoc.OptName = OptUser.UserName;
|
||
contractDoc.Code = ContractCode;
|
||
}
|
||
|
||
new TradeConfirmService(OptUser, db).EditReportStatus(contractDocR.TradeId, true);
|
||
db.SaveChanges();
|
||
#endregion
|
||
|
||
return (true, msg, contractDoc.OptDate.Value, contractDoc.StampDocumentFileName);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogFactory.GetLogger("UploadContractFile").Error("UploadContractFile出错", ex);
|
||
return (false, "操作出错", DateTime.Now, "");
|
||
}
|
||
}
|
||
|
||
public (bool, string, DateTime, string) UploadContractFileNew(string contractCode, bool isCounterpartyDoc, UploadFileModel uploadFileModel)
|
||
{
|
||
|
||
try
|
||
{
|
||
var msg = "";
|
||
#region 验证
|
||
if (string.IsNullOrWhiteSpace(contractCode))
|
||
{
|
||
|
||
return (false, "请传入参数", DateTime.Now, "");
|
||
}
|
||
|
||
|
||
var db = DbContextFactory.GetYLDbContext();
|
||
|
||
var contractDoc = db.trade_contract_document.FirstOrDefault(d => d.Code == contractCode );
|
||
var contractDocRArr = db.trade_contract_r.Where(O => O.ContractCode == contractDoc.Code && O.Type == contractDoc.Type && O.IsValid).ToArray();
|
||
var contractDocRF = contractDocRArr.FirstOrDefault();
|
||
if (contractDoc == null || contractDocRF == null)
|
||
{
|
||
return (false, "没有相关的确认书文档", DateTime.Now, "");
|
||
}
|
||
#endregion
|
||
|
||
if (uploadFileModel?.Length > 0)
|
||
{
|
||
#region 验证
|
||
if (PS.Config.ErpElement.SecuritiesEnvironment)
|
||
{
|
||
if (System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".xlsx"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".xls"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".pdf"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".docx"
|
||
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".doc")
|
||
{
|
||
return (false, "请上传PDF/Word/Excel格式的文件", DateTime.Now, "");
|
||
}
|
||
}
|
||
if (PS.Config.ErpElement.SecuritiesEnvironment && uploadFileModel.Length > Modules.SuperviseReportModule.SAC.Model.ReportStatusModel.MaxAnnexLength)//小于30MB
|
||
{
|
||
return (false, "上传失败,单笔确认书文件大小不应超过30MB", DateTime.Now, "");
|
||
}
|
||
#endregion
|
||
|
||
var tradeNumber = "未知";
|
||
if (contractDocRF != null)
|
||
{
|
||
tradeNumber = contractDocRF.TradeNumber;
|
||
}
|
||
var filePath = Path.Combine(OtcAppContext.AppDocsPath, "contractdoc", "output");
|
||
|
||
if (!Directory.Exists(filePath))
|
||
{
|
||
Directory.CreateDirectory(filePath);
|
||
}
|
||
|
||
var stampFileFullName = Path.Combine(filePath, uploadFileModel.FileName);
|
||
contractDoc.Paths = stampFileFullName.Replace(OtcAppContext.AppDocsPath, "/App_Docs").Replace("\\", "/");
|
||
contractDoc.FileName = uploadFileModel.FileName;
|
||
if (File.Exists(stampFileFullName))
|
||
{
|
||
File.Delete(stampFileFullName);
|
||
}
|
||
|
||
using var fs = new FileStream(stampFileFullName, FileMode.Create, FileAccess.ReadWrite);
|
||
|
||
using var sr = uploadFileModel.OpenReadStream();
|
||
|
||
sr.Seek(0, SeekOrigin.Begin);
|
||
var tempBytes = new byte[sr.Length];
|
||
sr.Read(tempBytes, 0, tempBytes.Length);
|
||
fs.Write(tempBytes, 0, tempBytes.Length);
|
||
|
||
sr.Close();
|
||
|
||
fs.Close();
|
||
msg = msg.Length > 0 ? msg : "上传成功";
|
||
}
|
||
|
||
#region 更新数据
|
||
contractDoc.SourceType = isCounterpartyDoc ? 2 : 1; // 对方用印文件=人工上传,否则=系统生成
|
||
var confrimService = new TradeConfirmService(OptUser, db);
|
||
foreach (var contractDocR in contractDocRArr)
|
||
{
|
||
new TradeConfirmService(OptUser, db).EditReportStatus(contractDocR.TradeId, true);
|
||
}
|
||
|
||
db.SaveChanges();
|
||
#endregion
|
||
|
||
return (true, msg, contractDoc.OptDate.Value, contractDoc.StampDocumentFileName);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogFactory.GetLogger("UploadContractFileNew").Error("UploadContractFileNew出错", ex);
|
||
return (false, "操作出错", DateTime.Now, "");
|
||
}
|
||
}
|
||
|
||
|
||
public string GetSealErrMsg(string code)
|
||
{
|
||
var db = DbContextFactory.GetYLDbContext();
|
||
|
||
var errMsg = db.trade_contract_document.FirstOrDefault(x => x.Code == code && x.SealResult == 1);
|
||
|
||
return errMsg == null ? "" : errMsg.SealErrMsg;
|
||
}
|
||
|
||
public bool IsSealWaiting(int tradeId)
|
||
{
|
||
var db = DbContextFactory.GetYLDbContext();
|
||
|
||
var code = db.trade_contract_r.FirstOrDefault(x => x.TradeId == tradeId)?.ContractCode;
|
||
|
||
var flag = code != null && db.trade_contract_document.Any(x => x.Code == code && x.SealResult == 3);
|
||
|
||
return flag;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断确认书是否生成过
|
||
/// </summary>
|
||
/// <param name="ids">trade_contract_r.id</param>
|
||
/// <returns></returns>
|
||
public bool IsContractGenerated(List<int> ids,List<string> types)
|
||
{
|
||
var db = DbContextFactory.GetYLDbContext();
|
||
var flag = true;
|
||
var tradeContractList = (from tradeContractR in db.trade_contract_r
|
||
join tradeContractDoc in (from tradeContractDocTemp in db.trade_contract_document select tradeContractDocTemp)
|
||
on tradeContractR.ContractCode equals tradeContractDoc.Code into tradeContractDoc1
|
||
from tradeContractDoc2 in tradeContractDoc1.DefaultIfEmpty()
|
||
where ids.Contains(tradeContractR.id) && types.Contains(tradeContractR.Type) && tradeContractR.IsValid
|
||
select new
|
||
{
|
||
tradeContractR.id,
|
||
tradeContractR.TradeId,
|
||
tradeContractR.ContractCode,
|
||
tradeContractDoc = tradeContractDoc2
|
||
}).ToList();
|
||
if (tradeContractList.Count == 0 || tradeContractList.Any(x => x.tradeContractDoc == null || string.IsNullOrWhiteSpace(x.tradeContractDoc.Paths)))
|
||
{
|
||
flag = false;
|
||
return flag;
|
||
}
|
||
foreach (var item in tradeContractList)
|
||
{
|
||
var path = Path.ChangeExtension(item.tradeContractDoc.AbsolutePath, "pdf");
|
||
if (!File.Exists(path))
|
||
{
|
||
flag = false;
|
||
break;
|
||
}
|
||
}
|
||
return flag;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量用印
|
||
/// </summary>
|
||
/// <param name="ids">trade_contract_r.id</param>
|
||
/// <param name="type"></param>
|
||
public void SealContracts(List<int> ids,List<string> types)
|
||
{
|
||
var db = DbContextFactory.GetYLDbContext();
|
||
var contract = (from tradeContractR in db.trade_contract_r
|
||
join tradeContractDoc in (from tradeContractDocTemp in db.trade_contract_document
|
||
select tradeContractDocTemp)
|
||
on tradeContractR.ContractCode equals tradeContractDoc.Code into tradeContractDoc1
|
||
from tradeContractDoc2 in tradeContractDoc1.DefaultIfEmpty()
|
||
where ids.Contains(tradeContractR.id) && types.Contains(tradeContractR.Type) && tradeContractR.IsValid
|
||
select new
|
||
{
|
||
tradeContractR.id,
|
||
tradeContractR.TradeId,
|
||
tradeContractR.ContractCode,
|
||
tradeContractDoc = tradeContractDoc2
|
||
}).ToList();
|
||
foreach (var item in contract)
|
||
{
|
||
|
||
if (item.tradeContractDoc != null)
|
||
{
|
||
GlobalConfig.SealHandler.RemoveQueue(item.TradeId);
|
||
|
||
GlobalConfig.SealHandler.AddQueue(item.tradeContractDoc, item.TradeId);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
}
|