Files
zszq-trs/YLErpDAL/Modules/TradeModule/DocGenerateModule/BatchSettlementBillGenerateService.cs
T
2024-05-09 14:06:26 +08:00

475 lines
22 KiB
C#

using System.Text.RegularExpressions;
using YLErp.BLL;
using YLErp.BLL.MarginCalculation;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Converts;
using YLErp.Model;
using YLErp.Models;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.TradeDalModule;
using YLErp.MsOffice;
using YLErp.Office.Converters;
using YLErp.Plugins.TradeDocGenerator.Abstracts;
namespace YLErp.Modules.TradeModule.DocGenerateModule
{
/// <summary>
///
/// </summary>
public class BatchSettlementBillGenerateService : YLBaseService
{
public BatchSettlementBillGenerateService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
/// 生成结算确认书
/// </summary>
public IEnumerable<TradeDocGenerateResult> Generate(IEnumerable<int> tradeCashIds, IEnumerable<int> tradeIds, string docType, int userId, string userName)
{
if (tradeCashIds != null && tradeCashIds.Any())
{
tradeIds = DbContext.trade_cash.Where(x => tradeCashIds.Contains(x.id) && !x.IsDeleted).Select(x => x.TradeId).Distinct();
}
if (tradeIds == null)
{
throw new ArgumentNullException(nameof(tradeIds));
}
if (docType == null)
{
throw new ArgumentNullException(nameof(docType));
}
//当前交易日
var valueDate = valuedateBLL.ValueDate;
var tradeStatus = new string[] { ConsTrade.已到期, ConsTrade.已执行 };
var tradeCashStatus = new string[] { ClientCashInCashOut.系统操作_行权费 };
//----------------------------------
// 获取当日行权 平仓 到期的有效交易
//----------------------------------
var service = new TradeDalService(this);
var tradeQuery = service
.GetTradeOrEodTrade(tradeIds, valueDate)
.Where(O =>
tradeStatus.Contains(O.TradeStatus)
&& O.UnWindDate <= valueDate
&& O.ClientId != 0);
//var tradeQuery = from trade in Table
// where tradeIds.Contains(trade.id)
// && tradeStatus.Contains(trade.TradeStatus) && trade.ValidState != "InValid"
// && trade.UnWindDate <= valueDate && trade.ClientId != null
// select trade;
var tradeDic = tradeQuery.ToDictionary(n => n.id, n => new trade_contract_group { trade = n });
if (tradeDic.Count < 1)
{
throw new ServiceException("当日没有要结算的记录信息!");
}
tradeIds = tradeDic.Keys.ToArray();
var parentTradeIds = tradeDic.Values.Where(O => O.trade != null && O.trade.ParentTradeId != 0).ToDictionary(K => K.trade.id, V => V.trade.ParentTradeId);
var parentTrades = service
.GetTradeOrEodTrade(parentTradeIds.Values, valueDate)
.Where(O =>
tradeStatus.Contains(O.TradeStatus)
&& O.UnWindDate <= valueDate
&& O.ClientId != 0)
.ToDictionary(K => K.id, V => V);
//var parentTrades = (from trade in Table
// where parentTradeIds.Values.Contains(trade.id)
// && tradeStatus.Contains(trade.TradeStatus)
// && trade.ValidState != "InValid"
// && trade.UnWindDate <= valueDate
// && trade.ClientId != null
// select trade).ToDictionary(K => K.id, V => V);
//----------------------------------
// 交易确认书数据
//----------------------------------
var tcrQuery = from aa in DbContext.trade_contract_r
where (tradeIds.Contains(aa.TradeId)
|| parentTradeIds.Values.Contains(aa.TradeId))
&& aa.Type == ContractTypeEnum.Trade && aa.IsValid
select aa;
var tcrDatas = tcrQuery.ToDictionary(K => K.TradeId, V => V);
foreach (var data in tradeDic)
{
var id = data.Key;
if (data.Value.trade.ParentTradeId != 0)
{ id = data.Value.trade.ParentTradeId; }
if (tcrDatas.TryGetValue(id, out var tcr))
{ data.Value.ConfirmContractR = tcr; }
}
//----------------------------------
// 交易现金交割记录
//----------------------------------
var cashQuery = from tradeCash in DbContext.trade_cash
where tradeIds.Contains(tradeCash.TradeId) &&
tradeCash.ValidState != ConsGlobal.InValid && !tradeCash.IsDeleted
&& tradeCashStatus.Contains(tradeCash.Action)
&& tradeCash.ExerciseWay == TradeCashExerciseWayEnum.到期行权
select tradeCash;
var cashDatas = cashQuery.ToDictionary(K => K.TradeId, V => V);
foreach (var data in cashDatas)
{
if (tradeDic.TryGetValue(data.Value.TradeId, out var group))
{
if (parentTradeIds.ContainsKey(data.Value.TradeId))
{
var id = parentTradeIds[data.Value.TradeId];
//var subTrades = (from t in DbContext.trade where t.ParentTradeId == id select t).GroupBy(O => O.TradeStatus).ToDictionary(K => K.Key, V => V.ToList());
var subTrades = service.GetStructTradeFromTradeOrEodTrade(new[] { id }, valueDate).GroupBy(O => O.TradeStatus).ToDictionary(K => K.Key, V => V.ToList());
tradeIds = subTrades.First().Value.Select(O => O.id).ToArray();
var cashes = cashQuery.GroupBy(O => new { O.ValueDate, O.Action, O.ExerciseWay, O.Notional, O.UnwindNotional }).ToDictionary(K => K.Key, V => V.ToList());
if (subTrades.Count == 1 && cashes.Count == 1)
{
subTrades.First().Value.ForEach(O => O.trade_cash = cashes.First().Value.First(B => B.TradeId == O.id));
group.trade = parentTrades[group.trade.ParentTradeId];
group.trade.SubTrades = subTrades.First().Value;
}
}
group.trade.trade_cash = data.Value;
group.trade_cash = data.Value;
}
}
//----------------------------------
// 生成交易结算书
//----------------------------------
var resultList = new List<TradeDocGenerateResult>();
AppManager.TryResolve(out ITradeSettleBillGenerator generatorV2);
//客户编号列表
foreach (var tradeContractGroup in tradeDic.Values)
{
if ((tradeContractGroup.trade.TradeType != "结构化交易" && tradeContractGroup.trade.TradeStatus == ConsTrade.已执行 && 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 = "缺少现金交割记录"
});
}
}
if (generatorV2 != null)
{
var result = GenerateSingleV2(generatorV2, tradeDic.Values.Select(O => O.trade).ToList(), tradeCashIds, docType);
resultList.Add(result);
}
else
{
foreach (var item in tradeDic)
{
resultList.Add(GenerateSingleV1(item.Value, OptUser.UserId, OptUser.UserName, docType));
}
}
return resultList;
}
/// <summary>
///
/// </summary>
internal TradeDocGenerateResult GenerateSingleV2(ITradeSettleBillGenerator generator, List<trade> trades, IEnumerable<int> tradeCashIds, string docType = "DOCX", DateTime? startDate = null, DateTime? endDate = null)
{
try
{
var context = new SettlementBillGenerateContext(
tradeCashIds,
trades,
ContractTypeEnum.Clearing, OptUser, startDate, endDate);
generator.Initialize(docType, context);
return generator.GenerateDocument();
}
catch (Exception ex)
{
LogFactory.GetLogger<BatchSettlementBillGenerateService>().Error("GenerateSingleV2", ex);
return new TradeDocGenerateResult(trades)
{
Exception = ex,
ErrorMessage = "出错:" + ex.Message
};
}
}
/// <summary>
///
/// </summary>
internal TradeDocGenerateResult GenerateSingleV1(trade_contract_group contractGroup, int userId, string userName, string docType = "DOCX")
{
if (contractGroup == null)
{
throw new ArgumentNullException(nameof(contractGroup));
}
var contractBLL = new TradeContractBLL(this);
var dic = new Dictionary<string, object>();
var trade = contractGroup.trade;
var tradeCash = contractGroup.trade_cash;
var tradeContractR = contractGroup.ConfirmContractR;
if (tradeContractR == null)
{
throw new Exception(contractGroup.trade.TradeNumber + "该笔交易未生成交易确认书");
}
var needAddData = true;
#region 如果到期结算单已经生成过,则直接返回
var settlementDoc = contractBLL.GetContractDoc(trade.id, ContractTypeEnum.Clearing);
if (settlementDoc != null)
{
if (File.Exists(settlementDoc.AbsolutePath))
{
if (docType == "PDF")
{
var pdfPath = settlementDoc.AbsolutePath.Replace(".docx", ".pdf");
if (!File.Exists(pdfPath))
{
OfficeFileConverter.ConvertDocxToPDF(settlementDoc.AbsolutePath, pdfPath);
}
return new TradeDocGenerateResult(trade)
{
OutputFilePath = $"../App_Docs/GTJA/{settlementDoc.FileName.Replace(".docx", ".pdf")}"
};
}
return new TradeDocGenerateResult(trade)
{
OutputFilePath = $"../App_Docs/GTJA/{settlementDoc.FileName}"
};
}
else
{
needAddData = false;
}
}
#endregion
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); ;
if (underlying == null)
{
return new TradeDocGenerateResult(trade)
{
ErrorMessage = $"标的资产{trade.UnderlyingCode}信息未找到"
};
}
var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId);
var Parentdata = DataCacheProvider.GetClientDataSource().GetData(client.ParentId);
if (client == null)
{
return new TradeDocGenerateResult(trade)
{
ErrorMessage = $"客户(id:{trade.ClientId})信息未找到"
};
}
if (Parentdata == null)
{
dic["{{客户名称}}"] = $"{trade.ClientName ?? ""}";
}
else
{
dic["{{客户名称}}"] = $"{(Parentdata.IsDocShowParentName == 1 ? client.Name : trade.ClientName ?? "")}";
}
dic["{{交易方向}}"] = $"贵司{BuySellConvert.GetClientBuySell(contractGroup.trade.BuySell)}";
var tradeType = trade.TradeType == "香草期权" ? "普通" : (trade.TradeType == "结构化交易" ? trade.StructureType : trade.TradeType.Replace("期权", ""));
dic["{{期权类型}}"] = $"{trade.ExerciseModeCn}{tradeType}{trade.OptionType}期权";
dic["{{到期日}}"] = trade.ExerciseDate?.ToString("yyyy年M月d日");
dic["{{交易日}}"] = trade.TradeDate?.ToString("yyyy年M月d日");
dic["{{合约到期日}}"] = trade.MaturityDate?.ToString("yyyy年M月d日");
var variety = VarietyBLL.GetAllvarietyModel().FirstOrDefault(x => x.id == underlying.UnderlyingTypeId);
if (trade.TradeType == "合成价差期权")
{
dic["{{商品标的名称}}"] = synthetic_underlyingBLL.GetUnderlyingTipsInfo(trade.UnderlyingCode);
}
else
{
//合约代码和名称需要调整
var UnderlyingCode = underlying.UnderlyingCode;
if (Regex.IsMatch(UnderlyingCode, "^[a-zA-Z]+00"))
{
UnderlyingCode = UnderlyingCode.TrimEnd('0');
}
UnderlyingCode += "." + underlying.MarketCode;
dic["{{商品标的名称}}"] = PS.Config.Is东兴 ? UnderlyingCode : trade.UnderlyingCode;
}
var strUnit = variety.QuoteUnitSingle;
var tradeNotional = trade.Notional;
var tradeOriginalNotional = (trade.OriginalNotional ?? 0);
dic["{{商品标的数量}}"] = $"{tradeOriginalNotional.OtcFormatNotional()}{strUnit}";
dic["{{支付时间}}"] = "买方终止 / 到期时支付";
//期末价格
var endPrice = underlying.Price ?? 0;
//交割金额
double tradeCashAmount = 0;
double notional = 0;
if (tradeCash != null)
{
endPrice = tradeCash.FinalPrice ?? 0;
tradeCashAmount = tradeCash.Amount;
notional = tradeCash.Action == "系统操作-行权费" ? tradeCash.Notional : tradeCash.UnwindNotional ?? 0;
}
dic["{{执行价格}}"] = $"{(trade.ActualStrike ?? 0).OtcFormatUmPrice()}元/{strUnit}";
dic["{{期初价格}}"] = $"{(trade.SpotPrice ?? 0).OtcFormatUmPrice()}元/{strUnit}";
dic["{{期权价格}}"] = $"{(trade.TradeSinglePrice ?? 0).OtcFormatMoney(grouping: false)}元/{strUnit}";
dic["{{期权费率}}"] = $"{trade.PremiumRate?.OtcFormat(OtcFormatFlag.premiumRateP)}";
dic["{{期初名义本金}}"] = $"{(trade.OriginalStockEqvNotional ?? 0).OtcFormat(OtcFormatFlag.StockEqvNotional)}元";
dic["{{期末名义本金}}"] = $"{trade.StockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional)}元";
dic["{{期末价格}}"] = $"{endPrice.OtcFormatUmPrice()}元/{strUnit}";
dic["{{期权收益}}"] = $"{Math.Abs(tradeCashAmount / tradeNotional).OtcFormatFlex(2, 4)}元/{strUnit}";
dic["{{期末数量}}"] = $"{tradeNotional.OtcFormatNotional()}{strUnit}";
dic["{{期权费}}"] = $"{(trade.TradePrice ?? 0).OtcFormatMoney(grouping: false)}元";
dic["{{结算价}}"] = $"{Math.Abs(tradeCashAmount).OtcFormatMoney()}元";
dic["{{终止日}}"] = trade.UnWindDate?.ToString("yyyy年M月d日");
var req = new RunMarginCalculationReq(UserInfo)
{
tradeList = new List<trade> { trade },
settleDate = trade.UnWindDate.Value,
PriceProvider = new SinglePriceProvider(trade.UnderlyingCode, endPrice)
};
if (trade.IsGroup == 1)
{
req.tradeList = DbContext.trade.Where(x => x.ParentTradeId == trade.id).ToList();
}
var tradeSpans = MarginDefault.RunMarginCalculation(req);
if (tradeSpans?.Count > 0)
{
var margin = trade.IsGroup == 1 ? tradeSpans.Sum(x => x.WorstCastClientPayable ?? 0) : (tradeSpans[0].WorstCastClientPayable ?? 0);
dic["{{预付金}}"] = $"{Math.Abs(margin).OtcFormatMoney()}元";
}
else
{
dic["{{预付金}}"] = "0元";
}
dic["{{结算价格}}"] = dic["{{期权收益}}"];
var isGJBuyer = trade.BuySell == "买入";
double incomes;
if (isGJBuyer)
{
incomes = tradeCashAmount - (notional * trade.TradeSinglePrice) ?? 0;
}
else
{
incomes = tradeCashAmount + (notional * trade.TradeSinglePrice) ?? 0;
}
dic["{{结算净额}}"] = $"{Math.Abs(incomes).OtcFormatMoney()}元";
dic["{{结算方式}}"] = incomes > 0 ? "贵司向我司支付结算净额" : "我司向贵司支付结算净额";
dic["{{到期盈亏}}"] = $"{Math.Abs(incomes).OtcFormatMoney(grouping: false)}元";
//东兴结算净额处理逻辑(卖价差期权)
dic["{{客户结算净额}}"] = $"{(Math.Abs(tradeCashAmount) - (notional * trade.TradeSinglePrice))?.OtcFormatMoney()}元"; ;
dic["{{机构结算净额}}"] = $"{((notional * trade.TradeSinglePrice) - Math.Abs(tradeCashAmount))?.OtcFormatMoney()}元"; ;
//合同编号
string contractNo;
string outPutFileName;
if (settlementDoc != null)
{
contractNo = settlementDoc.Code;
outPutFileName = settlementDoc.FileName;
}
else
{
var contractIndex = contractBLL.GetContractNo("GTJASettlement");
if (PS.Config.CompanyID == "中财资本")
{
contractNo = $"{trade.TradeNumber}S";
}
else
{
contractNo = $"{client.Number}C{contractIndex}S";
}
outPutFileName = $"{client.Name}到期结算单C{contractIndex}S.docx";
}
dic["{{合同编号}}"] = contractNo;
dic["{{合同时间}}"] = $"{DateTime.Now.ToString("yyyy年M月d日")}";
dic["{{确认书编号}}"] = tradeContractR.ContractCode;
try
{
var templateDir = OtcAppContext.MapPath("~/App_Docs/GTJA");
var templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/到期结算单模板.docx");
if (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value)
{
templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/到期结算单(名义本金)模板.docx");
}
var outPutFilePath = Path.Combine(templateDir, outPutFileName);
MsWordTempalteHelper.GenerateFromTemplate(templatePath, dic, outPutFilePath);
if (docType == "PDF" && !outPutFilePath.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
{
OfficeFileConverter.ConvertDocxToPDF(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)
{
var nowDate = DateTime.Now;
var contractRecord = new trade_contract_document();
DbContext.trade_contract_document.Add(contractRecord);
contractRecord.Code = settlementDoc != null ? settlementDoc.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.Paths = $"App_Docs\\GTJA\\{outPutFileName}";
contractRecord.FileName = outPutFileName;
contractRecord.Type = ContractTypeEnum.Clearing;
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.TradeClearCode = contractRecord.Code;
contractTradeRelation.Type = ContractTypeEnum.Clearing;
contractTradeRelation.OptId = userId;
contractTradeRelation.OptName = userName;
contractTradeRelation.OptDate = nowDate;
contractTradeRelation.TradeCashId = tradeCash.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 ex)
{
LogFactory.GetLogger<SettlementBillGenerateService>().Error("GenerateSingleSettleBill出错", ex);
return new TradeDocGenerateResult(trade)
{
ErrorMessage = "出错:" + ex.Message
};
}
}
}
}