using BaseOUDAL;
using DocumentFormat.OpenXml.Office2010.Excel;
using OfficeOpenXml.Drawing;
using Org.BouncyCastle.Ocsp;
using System.Text;
using YieldChain.Helpers;
using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.DBModels.Enums;
using YLErp.Model.Enum;
using YLErp.Modules.RiskModule;
using YLErp.Modules.SalesModule;
using YLErp.Modules.SwapModule;
using YLErp.Modules.SystemModule;
namespace YLErp.Modules.TradeModule.DealModule
{
///
/// 交易确认服务
///
public class TradeConfirmService : TradeServiceBase
{
readonly DateTime confirmDate;
readonly ClientDBContext clientDB;
public TradeConfirmService(YLBaseService baseService) : base(baseService)
{
confirmDate = SystemValueDate;
clientDB = DbContextFactory.GetClientDbContext(OptUser);
}
public TradeConfirmService(OptUserInfo userInfo) : base(userInfo)
{
confirmDate = SystemValueDate;
clientDB = DbContextFactory.GetClientDbContext(OptUser);
}
public TradeConfirmService(OptUserInfo userInfo, YLContext dbContext) : base(userInfo, dbContext)
{
confirmDate = SystemValueDate;
clientDB = DbContextFactory.GetClientDbContext(OptUser);
}
///
/// 2021-02-04改造:批量确认因为耗时太长,用户多页面(交易列表,今日交易)并发确认导致生成多条期权费资金记录
///
public TradeConfirmResultModel tradeConfirm(IEnumerable tradeids, bool ignoreMoneyCheck = false, bool isSkipApproval = false)
{
if (tradeids is null || !tradeids.Any(n => n > 0))
{
throw new ArgumentException("tradeids 参数无效", nameof(tradeids));
}
//将交易ID集合转换为Set结构
var idSet = tradeids.ToHashSet();
var isBatch = idSet.Count > 1;
//首先查找是否有父交易ID存在的情况,找出父交易下面的所有子交易ID集合
var subTds = DbContext.trade.Where(n => n.ParentTradeId > 0 && n.ValidState != "InValid" && idSet.Contains(n.ParentTradeId) && (n.IsGroup == 0 || n.IsGroup == 2 && n.TradeType == "收益互换") && n.TradeStatus != "确认成交")
.Select(n => new { n.id, n.ParentTradeId, n.IsGroup }).ToArray();
if (subTds.Any())
{
foreach (var std in subTds)
{
idSet.Add(std.id);
if (std.IsGroup == 0)
{
idSet.Remove(std.ParentTradeId);
}
}
}
var hasTradeProcess = HasTradeProcess();
var sbError = new StringBuilder();
var result = new TradeConfirmResultModel
{
confirmedTradeIds = new List(),
changeConfirmPaths = new List()
};
HashSet idCache = new HashSet();
foreach (var tid in idSet)
{
var lockKeyId = "tradeConfirm_" + tid;
//对每个交易单独处理基本上可以解决并发问题
//为了更加保险增加多线程并发锁定
lock (Helpers.LockHelper.Acquire(lockKeyId, 300))
{
var pid = subTds.Where(O => O.id == tid).FirstOrDefault()?.ParentTradeId ?? 0;
var temp = ConfirmTrade(tid: tid,
isBatch: isBatch,
hasTradeProcess: hasTradeProcess,
ignoreMoneyCheck: ignoreMoneyCheck,
isSkipApproval: isSkipApproval,
confirmedTradeIds: result.confirmedTradeIds,
changeConfirmPaths: result.changeConfirmPaths,
continueQuotaCheck: idCache.Contains(pid));
if (pid > 0)
{
idCache.Add(pid);
}
result.type = temp.RetCode.ToString();
if (temp.RetCode == TradeOpenRetCode.LackOfMoney)
{
sbError.AppendLine(temp.ErrorMsg);
result.TradeId = temp.Trade.id;
result.LackOfMoney = true;
break;
}
if (temp.RetCode == TradeOpenRetCode.QuotaTrialError || temp.RetCode == TradeOpenRetCode.FundStatus)
{
result.LackOfMoney = true;
result.TrialDataId = temp.TrialDataId;
result.TradeId = temp.Trade.id;
sbError.AppendLine(temp.ErrorMsg);
break;
}
if (!string.IsNullOrEmpty(temp.ErrorMsg))
{
sbError.AppendLine(temp.ErrorMsg);
}
Helpers.LockHelper.Release(lockKeyId);
}
}
DbContext.SaveChanges();
result.errorMsg = sbError.ToString();
return result;
}
///
/// 确认交易
///
/// 交易Id
///
///
/// 忽略交易限额/资金和检查
///
///
///
///
///
private TradeOpenResult ConfirmTrade(int tid, bool isBatch, bool hasTradeProcess,
bool ignoreMoneyCheck, bool isSkipApproval, List confirmedTradeIds, List changeConfirmPaths, bool continueQuotaCheck = false)
{
var td = DbContext.trade.FirstOrDefault(t => t.id == tid);
var result = new TradeOpenResult(td ?? new trade());
if (td == null)
{
result.ErrorMsg = $"系统编号:{tid},找不到交易数据,不能被确认";
return result;
}
if (td.TradeType == "结构化交易" && td.IsGroup == 0)
{
result.ErrorMsg = $"系统内部错误,不应出现结构化交易类型的确认处理,交易编号:{td.TradeNumber}";
return result;
}
//判断如果为待确认的则修改状态为确认状态
if (!ConsTrade.UnConfirmTradeStatusList.Contains(td.TradeStatus) && ((td.TradeStatus == "审批中" && !isSkipApproval) || td.TradeStatus == "确认成交"))
{
//result.ErrorMsg = $"客户:{td.ClientName},交易编号:{td.TradeNumber},状态为:{td.TradeStatus},不能被确认";
result.RetCode = TradeOpenRetCode.Success;
return result;
}
if (ConsTrade.TradeStatusAfterAndWithOutConfirmed.Contains(td.TradeStatus))
{
result.ErrorMsg = $"客户:{td.ClientName},交易编号:{td.TradeNumber},状态为:{td.TradeStatus},不能被确认";
return result;
}
//判断交易对手方信息是否允许确认
var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
if (client == null)
{
result.ErrorMsg = $"未找到客户信息:{td.ClientName}";
return result;
}
if (!ignoreMoneyCheck && !continueQuotaCheck)
{
result = CheckTradeConfirmRule(result, td, isSkipApproval);
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
{
return result;
}
result = CheckClientRating(result, client, td);
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
{
return result;
}
var trialService = new QuotaMonitorService(this);
if (!trialService.QuotaCheck(ref result))
{
return result;
}
}
var history = "确认交易";
if (ignoreMoneyCheck)
{
history = "交易特批-" + history;
if (isBatch)
{
history = "特权账户批量确认自动" + history;
}
}
//进入交易审批流程
if (hasTradeProcess && !isSkipApproval)
{
td.TradeOldStatus = td.TradeStatus;
td.OptId = UserId;
td.OptName = UserName;
td.OptDate = OptDate;
int passStatus = new ApprovalProcessService(OptUser).CheckTradeApprovalStep(td, OptUser.UserId);
if (passStatus == 0)
{
td.TradeStatus = ConsTrade.确认成交;
//删掉trade_check相关的复核数据
new TradeCheckService(this).DeleteTradeCheck(td.id);
var p = new ProcessTradeLog()
{
TradeId = td.id,
ProcessType = "TradeProcess",
ProcessStatus = ProcessTradeStatus.通过审批.ToString(),
Log = "",
OptId = UserId,
CreateTime = DateTime.Now
};
DbContext.processtradelog.Add(p);
new SalesCommissionDataService(OptUser).SetCommissionVaild(td.id);
EditReportStatus(td.id);
SwapTradeConfirm(td, history,false,optlog:"手工操作");
}
else if (passStatus == -3)
{
result.ErrorMsg = $"当前存在分支交易审批流程,检测到申请人不满足任意一个分支进入条件,无法提交审批,请更换申请人或调整审批组";
return result;
}
}
else
{
td.TradeOldStatus = td.TradeStatus;
td.OptId = UserId;
td.OptName = UserName;
td.OptDate = OptDate;
td.TradeStatus = ConsTrade.确认成交;
//删掉trade_check相关的复核数据
new TradeCheckService(this).DeleteTradeCheck(td.id);
td.ProcessOrderId = ProcessTradeLog.审批通过;
td.CheckTradeUpdate = Convert.ToInt32(TradeCheckEnum.StatusOfNew);
td.ProcessOptDate = DateTime.Now;
td.ProcessStatus = ProcessTradeStatus.通过审批.ToString();
var p = new ProcessTradeLog()
{
TradeId = td.id,
ProcessType = "TradeProcess",
ProcessStatus = ProcessTradeStatus.通过审批.ToString(),
Log = "",
OptId = UserId,
CreateTime = DateTime.Now
};
DbContext.processtradelog.Add(p);
new SalesCommissionDataService(OptUser).SetCommissionVaild(td.id);
EditReportStatus(td.id);
SwapTradeConfirm(td, history, false, optlog: "手工操作");
}
new ClientCashInCashOutService(this).SaveClientCashInCashOut(td);
DbContext.SaveChanges();
//生成变更确认书(如果走审批流程应该在审批流程走完以后生成才对!!!)
if (PS.Config.TradeElement.IsGenerateAmendDoc)
{
try
{
var filepath = new ChangeConfirmBookGenerator(this).GenerateChangeConfirmBook(td.id, out _);
if (!string.IsNullOrEmpty(filepath))
{
changeConfirmPaths.Add(filepath);
}
}
catch (Exception ex)
{
LogFactory.GetLogger("生成变更确认书").Error(ex);
}
}
if (td.TradeStatus == ConsTrade.确认成交)
{
confirmedTradeIds.Add(td.id);
}
AddTradeOperationHistoryAndSetParentTradeInfo(false, td, history, td.TradeOldStatus);
result.RetCode = TradeOpenRetCode.Success;
return result;
}
///
/// 新版互换确认
///
///
public void SwapTradeConfirm(trade td,string history,bool addlog,DateTime? openDate=null,string optlog="")
{
if (td.TradeType != "收益互换")
{
return;
}
openDate = openDate == null ? td.TradeDate : openDate;
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
var marginModes = new int[]{ (int)InterestModeEnum.追加预付金, (int)InterestModeEnum.初始预付金 };
var positions= DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.IsInitial && !x.Invalid ).ToList();
td.swap_positions = positions.Where(x =>marginModes.Contains(x.InterestMode)).ToList();
var posiPositions= positions.Where(x => x.PosiDirection>0).ToList();
SwapTradeService swapTradeService = new SwapTradeService(this);
var swapEventService = new SwapEventService(this);
swapTradeService.InitialPosition(td);
swapTradeService.AddPositionEvent(td, optlog);
//new SwapEodPositionService(this).InitSaveEodSwapPosition(td);
var happenDate = td.TradeDate.Value;
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
{
happenDate = td.ExerciseDate.Value;
}
var cashService = new ClientCashInCashOutService(this);
cashService.SaveSwapTradeClientCash(td, td.TradePrice ?? 0, happenDate,0);
foreach (var marginPositions in td.swap_positions.GroupBy(g=>g.HappenDate))
{
var marginHappenDate = marginPositions.Key;
foreach (var marginPosition in marginPositions)
{
marginPosition.Obervation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == marginPosition.id);
bool generateMargin = true;
if (marginPosition.Obervation != null)
{
if (!marginPosition.Obervation.IsDeductPrincipal)
{
generateMargin = false;
}
}
if (generateMargin)
{
var marginAmount = marginPosition.InterestPrincipalFix * (marginPosition.InterestDirection == 1 ? -1 : 1);
cashService.SaveSwapTradeClientCash(td,Convert.ToDouble( marginAmount), marginHappenDate.HasValue ? marginHappenDate.Value : td.TradeDate.Value, marginPosition.id, ClientCashInCashOut.系统操作_应付预付金);
}
}
}
var swap_event= swapEventService.UpdateSwapEvent();
swapEventService.AddSwapEventDate(openDate.Value, td.id, (int)SwapEventTypeEnum.确认交易, "", 0, true, history);
if (addlog)
{
var swapInsertEnum = SwapPushDataEnum.收益互换交易新增;
var posiInsertEnum = SwapPushDataEnum.互换资产交易新增;
var marginInsertEnum = SwapPushDataEnum.预付金交易新增;
}
else
{
Task.Run(() =>
{
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
});
}
var interestDirection = (int)SwapDirectionEnum.收取;
if (td.swap_positions.Any())
{
interestDirection = td.swap_positions.FirstOrDefault().InterestDirection;
}
InitTradeReport(td, interestDirection);
}
///
/// 初始化报送字段填写
///
///
private void InitTradeReport(trade td,int marginDiretion)
{
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= td.TradeDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList();
var marinRate = clientMarginTemplates.Where(x => x.ClientId == td.ClientId).FirstOrDefault();
if (marinRate == null)
{
marinRate = clientMarginTemplates.Where(x => x.ClientId == 0).FirstOrDefault();
}
if (marinRate == null)
{
marinRate = new client_marginrate
{
InitMarginRate = 1,
MaintenanceRate = 1,
};
}
var trade_Report = DbContext.trade_Report.Where(x => x.TradeId == td.id).FirstOrDefault();
if (trade_Report == null)
{
trade_Report = new trade_report();
trade_Report.TradeId = td.id;
DbContext.trade_Report.Add(trade_Report);
}
trade_Report.FillingPartyRole = td.OpponentRole == "乙方" ? "甲方" : "乙方";
trade_Report.PerformanceGuarantee = "部分担保";
trade_Report.PerformanceCollProvider = marginDiretion==(int)SwapDirectionEnum.收取? "乙方" : "甲方";
trade_Report.IsUsePerformanceColl = "否";
trade_Report.PerformanceExplain = "详见交易确认书。";
trade_Report.IsPerformanceCollInterest = "否";
trade_Report.PerformanceCollType = "现金";
trade_Report.PerformanceCollRange = "仅本笔交易";
trade_Report.InitialPerformanceColl = (decimal)marinRate.InitMarginRate*100;
trade_Report.RecoveryPerformanceColl = (decimal)marinRate.MaintenanceRate * 100;
trade_Report.UnwindPerformanceColl = trade_Report.RecoveryPerformanceColl;
trade_Report.EodPerformanceColl = trade_Report.RecoveryPerformanceColl;
trade_Report.PerformanceRemark = "详见交易确认书及定义性文件";
trade_Report.SetOpt(OptUser);
var tradeauditing = new TradeAuditLog
{
TradeId = td.id,
Changes = "",
DataType = "C02",
OptId = UserId,
OptName = UserName,
OptDate = OptDate,
OptType = "修改报送相关字段",
AuditFlag = TradeAuditFlag.operation
};
//记录审核日志
DbContext.TradeAuditLog.Add(tradeauditing);
DbContext.SaveChanges();
}
private TradeOpenResult CheckClient(Client client, trade td, bool ignoreMoneyCheck, bool isSkipApproval)
{
var result = new TradeOpenResult(td);
//if (client.PendingMarginCallPayment == 1)
//{
// return $"交易'{td.TradeNumber}'确认失败,客户需要追保!";
//}
result = CheckIsMoneyLack(result, client, td, ignoreMoneyCheck);
if (result.RetCode == TradeOpenRetCode.LackOfMoney)
{
return result;
}
result.RetCode = TradeOpenRetCode.Success;
return result;
}
private TradeOpenResult CheckIsMoneyLack(TradeOpenResult result, Client client, trade td, bool ignoreMoneyCheck)
{
//增加实时资金查询 查看资金资金是否足够支付期权费
tradeBLL.SetFieldsByTradeType(td);
//!!!这个逻辑有问题,结构化交易应该是检查所有总和才对
if (!(ignoreMoneyCheck || td.IsGroup == 1) && !RealtimePnlCalc.TradeCanBeConfirm(client.id, td, out var errorMsg))
{
result.RetCode = TradeOpenRetCode.LackOfMoney;
result.ErrorMsg = $"客户:{client.Name},交易编号:{td.TradeNumber},{errorMsg}";
return result;
}
return result;
}
private TradeOpenResult CheckTradeConfirmRule(TradeOpenResult result, trade td, bool isSkipApproval)
{
if (td.TradeDate == SystemValueDate)
{
var invalidTradeConfirmRuleName = new TradeConfirmRuleService(this).checkTradeConfirmRule(td, UserId);
if (!string.IsNullOrEmpty(invalidTradeConfirmRuleName) && !isSkipApproval)
{
result.ErrorMsg = $"该交易'{td.TradeNumber}'不能满足交易确认规则{invalidTradeConfirmRuleName}";
return result;
}
}
return result;
}
private TradeOpenResult CheckClientRating(TradeOpenResult result, Client client, trade td)
{
var clientRatingQuery = clientDB.Client_Rating.Where(x => x.ClientId == client.id && !x.IsDeleted);
//jira OTC-11345
//只有资信评级在有效期内且资信评级禁止交易时才阻止交易
if (clientRatingQuery.Any())
{
//if (!clientRatingQuery.Any(cr => cr.ProcessStatus == "已审批"))
//{
// return $"客户:{td.ClientName},资信等级未审批!";
//}
var query2 = from clientR in clientRatingQuery
join creditR in clientDB.credit_rating on clientR.CreditRatingId equals creditR.id into creditR_t
from creditR in creditR_t.DefaultIfEmpty()
where clientR.RatingDeadLine >= confirmDate
&& clientR.RatingStartDate <= confirmDate && clientR.ProcessStatus == "已审批"
orderby clientR.RatingDeadLine descending, clientR.ProcessOptDate descending
select new
{
clientR.id,
credit = creditR == null ? null : new
{
creditR.IsSuspend,
creditR.ValidState
}
};
var rating = query2.FirstOrDefault();
if (rating == null)
{
//return $"客户:{td.ClientName},资信评级不存在!";
}
else if (rating.credit == null)
{
//return $"客户:{td.ClientName},资信等级不存在!";
}
else if (rating.credit.ValidState == "InValid")
{
//return $"客户:{td.ClientName},资信等级无效,请重新设置该客户资信等级!";
}
else if (rating.credit.IsSuspend)
{
result.ErrorMsg = $"客户:{td.ClientName},资信等级为暂停交易! ";
return result;
}
}
else
{
//没有的话,资信评级不做限制。
//return $"客户:{td.ClientName},{confirmDate:yyyy-MM-dd}的资信评级未找到!请设置! ";
}
result.RetCode = TradeOpenRetCode.Success;
return result;
}
public void EditReportStatus(int id, bool editConfirmBook = false)
{
var tcList = DbContext.trade_cash.Where(O => O.TradeId == id && O.Action == "系统操作-期权费").ToList();
foreach (var item in tcList)
{
item.OptDate = DateTime.Now;
}
var contractCodeList = DbContext.trade_contract_r.Where(O => O.TradeId == id && O.Type == "交易确认书").Select(O => O.ContractCode).ToList();
foreach (var code in contractCodeList)
{
//如果查不到,说明是新交易,不用修改报送状态;
if (string.IsNullOrWhiteSpace(code))
{ continue; }
var infoTag = $"_{code.Replace("_", "-")}_成交_";
if (editConfirmBook)
{
infoTag += "确认书";
}
var notes = DbContext.sac_report_notes.Where(O => O.InfoTag.Contains(infoTag)).ToArray();
foreach (var item in notes)
{
item.changeStatus = true;
}
}
}
///
/// from tradecontroller.TerminationBackToConfirmed
///
public void TerminationBackToConfirmed(int tradeId)
{
var td = DbContext.trade.Find(tradeId);
if (td == null || td.ValidState == ConsGlobal.InValid)
{
throw new ServiceException("系统中没有相关的交易");
}
if (td.TradeStatus != ConsTrade.提前终止拒绝)
{
throw new ServiceException("只有提前终止拒绝状态的交易可以执行此操作");
}
td.TradeStatus = ConsTrade.确认成交;
td.OptId = UserId;
td.OptName = UserName;
td.OptDate = DateTime.Now;
AddTradeOperationHistoryAndSetParentTradeInfo(false, td, "提前终止放弃");
DbContext.SaveChanges();
}
}
///
/// 交易确认结果
///
public class TradeConfirmResultModel
{
public bool LackOfMoney;
public int TrialDataId;
public int TradeId;
public List confirmedTradeIds;
public List changeConfirmPaths;
public string errorMsg;
public string type;
}
}