新增国联证券专用业务逻辑及文档生成插件
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,392 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using YLErp.Core.Helpers;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Consts;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Modules.UnderlyingModule;
|
||||
using YLErp.Plugins.TradeDocGenerator;
|
||||
using YLErp.Plugins.TradeDocGenerator.Abstracts;
|
||||
|
||||
namespace YLErp.Plugins.GuoLian.DocumentGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 国联证券交易确认书生成器(单个生成)
|
||||
/// </summary>
|
||||
internal class TradeConfirmationGenerator : BaseConfirmationGenerator, ITradeConfirmationGenerator
|
||||
{
|
||||
public TradeConfirmationGenerator()
|
||||
: base(ContractTypeEnum.Trade)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模板文件路径
|
||||
/// 收益互换模板选择逻辑:
|
||||
/// 1. 客户看多/看空:通过持仓方向判断(PositionType: 1=多头/看多,2=空头/看空)
|
||||
/// 2. 标的类型:
|
||||
/// - 债券ETF:.SH后缀且511开头,或.SZ后缀且59开头
|
||||
/// - 现券:其他
|
||||
/// </summary>
|
||||
protected override string GetTemplateFilePath()
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
|
||||
if (trade.TradeType == "收益互换")
|
||||
{
|
||||
return GetSwapTemplatePath();
|
||||
}
|
||||
else if (trade.TradeType.Contains("期权"))
|
||||
{
|
||||
// 期权使用期权模板
|
||||
return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template\\option_01.docx");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"不支持的贸易类型: {trade.TradeType}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取收益互换模板路径
|
||||
/// </summary>
|
||||
private string GetSwapTemplatePath()
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
var underlying = Context.GetTradeUnderlying();
|
||||
|
||||
// 判断看多/看空方向
|
||||
bool isLong = IsLongPosition(trade);
|
||||
|
||||
// 判断标的类型(债券ETF vs 现券)
|
||||
bool isEtf = IsBondEtf(underlying?.UnderlyingCode ?? string.Empty);
|
||||
|
||||
// 选择对应模板(使用客户提供的原始文件名)
|
||||
string templateName;
|
||||
if (isLong && !isEtf)
|
||||
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx";
|
||||
else if (isLong && isEtf)
|
||||
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx";
|
||||
else if (!isLong && !isEtf)
|
||||
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx";
|
||||
else
|
||||
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx";
|
||||
|
||||
return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template", templateName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否为多头/看多方向
|
||||
/// </summary>
|
||||
private bool IsLongPosition(OtcTradeBase trade)
|
||||
{
|
||||
// 通过BuySell判断:买入=看多,卖出=看空
|
||||
return trade.BuySell == "买入";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否为债券ETF
|
||||
/// - .SH后缀且511开头 -> 债券ETF
|
||||
/// - .SZ后缀且59开头 -> 债券ETF
|
||||
/// - 其他 -> 现券
|
||||
/// </summary>
|
||||
private bool IsBondEtf(string underlyingCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(underlyingCode))
|
||||
return false;
|
||||
|
||||
underlyingCode = underlyingCode.Trim().ToUpper();
|
||||
|
||||
// 上交所债券ETF:511开头.SH后缀
|
||||
if (underlyingCode.EndsWith(".SH") && underlyingCode.StartsWith("511"))
|
||||
return true;
|
||||
|
||||
// 深交所债券ETF:59开头.SZ后缀
|
||||
if (underlyingCode.EndsWith(".SZ") && underlyingCode.StartsWith("59"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取输出文件名
|
||||
/// </summary>
|
||||
protected override string GetOutputFileName(string contractNo, string contractIndex)
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
var rule = $"{trade.TradeType}交易确认书_{contractNo}";
|
||||
return $"{rule}.{DocType.ToLower()}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取合同编号
|
||||
/// </summary>
|
||||
protected override string GetContractNo(out string contractIndex)
|
||||
{
|
||||
var contractNo = Context.GenerateContractNo(out contractIndex);
|
||||
return contractNo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备视图数据(Word模板数据)
|
||||
/// </summary>
|
||||
protected override void PrepareViewData(Dictionary<string, JToken> dic)
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
var client = Context.GetClient();
|
||||
|
||||
// 基础信息
|
||||
// 注意:合同编号由基类统一设置,此处不再重复生成
|
||||
dic["交易编号"] = trade.TradeNumber;
|
||||
dic["客户名称"] = client.Name;
|
||||
dic["乙方名称"] = client.Name;
|
||||
|
||||
// 交易日期相关
|
||||
dic["成交日期"] = trade.TradeDate?.ToString("yyyy年M月d日");
|
||||
dic["开始日期"] = trade.StartDate?.ToString("yyyy年M月d日");
|
||||
dic["到期日期"] = trade.ExerciseDate?.ToString("yyyy年M月d日");
|
||||
|
||||
// 根据交易类型填充不同数据
|
||||
switch (trade.TradeType)
|
||||
{
|
||||
case "收益互换":
|
||||
SetSwapData(dic);
|
||||
break;
|
||||
case "香草期权":
|
||||
case "亚式期权":
|
||||
case "障碍期权":
|
||||
SetOptionData(dic);
|
||||
break;
|
||||
default:
|
||||
// 默认基础数据
|
||||
SetBaseData(dic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置收益互换数据
|
||||
/// </summary>
|
||||
private void SetSwapData(Dictionary<string, JToken> dic)
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
var client = Context.GetClient();
|
||||
var bank = Context.GetClientBankCard(false);
|
||||
|
||||
dic["交易对手方全称"] = client.ClientType != "产品"
|
||||
? client.Name
|
||||
: $"{client.Manager}作为管理人代表{client.Name}";
|
||||
|
||||
dic["主协议编号"] = client.MainProtocolCode ?? "";
|
||||
dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
|
||||
|
||||
// 银行账户信息
|
||||
dic["户名"] = bank?.ClientName ?? "";
|
||||
dic["银行账号"] = bank?.Card ?? "";
|
||||
dic["开户行"] = bank?.Bank ?? "";
|
||||
dic["大额行号"] = bank?.Payment ?? "";
|
||||
|
||||
// 标的相关的数据可以通过 Context 获取
|
||||
var underlying = Context.GetTradeUnderlying();
|
||||
if (underlying != null)
|
||||
{
|
||||
dic["标的代码"] = underlying.UnderlyingCode;
|
||||
dic["标的名称"] = underlying.UnderlyingName;
|
||||
|
||||
// 标的发行人(从债券信息中获取)
|
||||
var bond = JsonHelper.Deserialize<UnderlyingBond>(underlying.ExJson) ?? new UnderlyingBond();
|
||||
dic["参考标的发行人"] = bond.UnderlyingIssuer ?? "";
|
||||
dic["票面利率"] = (bond.CouponRate ?? 0).ToString("N4");
|
||||
dic["参考标的到期日"] = underlying.MaturityDate?.ToString("yyyy年M月d日") ?? "";
|
||||
}
|
||||
|
||||
// 从swap_position获取期初价格、保证金率等信息
|
||||
var swapPositions = Context.GetSwapPositions(trade.id, true);
|
||||
var swapPosition = swapPositions
|
||||
.Where(x => x.PositionType == (int)PositionTypeFlag.Long || x.PositionType == (int)PositionTypeFlag.Short)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (swapPosition != null)
|
||||
{
|
||||
// 期初全价和净价(转换为百分比格式)
|
||||
dic["参考标的期初全价"] = ((double)swapPosition.PosiGrossPrice * 100).ToString("N4");
|
||||
dic["参考标的期初净价"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m) * 100).ToString("N4");
|
||||
|
||||
// 固定收益率(年化)- 债券期初到期收益率
|
||||
dic["固定收益率(年化)"] = swapPosition.InitYtm.HasValue
|
||||
? ((double)swapPosition.InitYtm.Value * 100).ToString("N4")
|
||||
: "0.0000";
|
||||
|
||||
// 获取客户适用的保证金率
|
||||
var clientMarginRate = UnderlyingHelper.GetApplicableMarginRate(
|
||||
client.id, swapPosition.UnderlyingCode, trade.TradeDate ?? DateTime.Now);
|
||||
|
||||
if (clientMarginRate != null)
|
||||
{
|
||||
dic["初始保障金率"] = ((double)clientMarginRate.init_rate * 100).ToString("N4");
|
||||
dic["维持保障金率"] = ((double)clientMarginRate.maintain_rate * 100).ToString("N4");
|
||||
|
||||
// 期初预付比例和金额
|
||||
dic["期初预付比例"] = ((double)clientMarginRate.init_rate * 100).ToString("N4");
|
||||
dic["期初预付金额"] = ((trade.OriginalStockEqvNotional ?? 0) * (double)clientMarginRate.init_rate).ToString("N2");
|
||||
}
|
||||
else
|
||||
{
|
||||
dic["初始保障金率"] = "0.0000";
|
||||
dic["维持保障金率"] = "0.0000";
|
||||
dic["期初预付比例"] = "0.0000";
|
||||
dic["期初预付金额"] = "0.00";
|
||||
}
|
||||
|
||||
// 期初预付金利率(InterestMode == 初始预付金)
|
||||
var initialMarginPosition = swapPositions
|
||||
.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金)
|
||||
.FirstOrDefault();
|
||||
dic["期初预付金利率"] = initialMarginPosition != null
|
||||
? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4")
|
||||
: "0.0000";
|
||||
|
||||
// 利率类型判断(固定/浮动)
|
||||
var interestMargin = swapPositions
|
||||
.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.interest_rest_days != null)
|
||||
.FirstOrDefault();
|
||||
if (interestMargin == null)
|
||||
interestMargin = swapPositions
|
||||
.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && string.IsNullOrWhiteSpace(x.FloatRateUnderlyingCode))
|
||||
.FirstOrDefault();
|
||||
if (interestMargin == null)
|
||||
interestMargin = swapPositions
|
||||
.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode))
|
||||
.FirstOrDefault();
|
||||
|
||||
dic["IsFixed"] = "□";
|
||||
dic["IsFloat"] = "□";
|
||||
if (interestMargin != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(interestMargin.FloatRateUnderlyingCode))
|
||||
{
|
||||
dic["利率类型"] = "固定利率";
|
||||
dic["IsFixed"] = "☑"; //☑
|
||||
dic["固定利率"] = ((double)interestMargin.InterestRateDefault * 100).ToString("N4");
|
||||
dic["利差"] = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
dic["利率类型"] = "浮动利率";
|
||||
dic["IsFloat"] = "☑";
|
||||
dic["固定利率"] = "";
|
||||
dic["利差"] = ((double)interestMargin.InterestRateDefault * 10000).ToString("N2");
|
||||
dic["重置频率"] = (interestMargin.interest_rest_days ?? 0) + "天";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dic["利率类型"] = "固定利率";
|
||||
dic["固定利率"] = "0.0000";
|
||||
dic["利差"] = "";
|
||||
}
|
||||
|
||||
// 基本费率 = PosiTradingFeePending / 名义本金 * 100
|
||||
var notional = trade.OriginalStockEqvNotional ?? 0;
|
||||
var tradingFee = (double)swapPosition.PosiTradingFeePending;
|
||||
var basicFeeRate = notional == 0 ? 0 : tradingFee / notional * 100;
|
||||
dic["基本费率"] = basicFeeRate.ToString("N4");
|
||||
|
||||
// 期初现金交换比例和金额(使用初始预付金数据)
|
||||
dic["期初现金交换比例"] = initialMarginPosition != null
|
||||
? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4")
|
||||
: "0.0000";
|
||||
dic["期初现金交换金额"] = initialMarginPosition != null
|
||||
? ((double)initialMarginPosition.InterestPrincipalFix).ToString("N2")
|
||||
: "0.00";
|
||||
}
|
||||
else
|
||||
{
|
||||
dic["参考标的期初全价"] = "0.0000";
|
||||
dic["参考标的期初净价"] = "0.0000";
|
||||
dic["初始保障金率"] = "0.0000";
|
||||
dic["维持保障金率"] = "0.0000";
|
||||
dic["期初预付比例"] = "0.0000";
|
||||
dic["期初预付金额"] = "0.00";
|
||||
dic["期初预付金利率"] = "0.0000";
|
||||
dic["利率类型"] = "固定利率";
|
||||
dic["固定利率"] = "0.0000";
|
||||
dic["利差"] = "";
|
||||
dic["基本费率"] = "0.0000";
|
||||
dic["期初现金交换比例"] = "0.0000";
|
||||
dic["期初现金交换金额"] = "0.00";
|
||||
}
|
||||
|
||||
// 参考标的券面总额(名义本金)
|
||||
dic["参考标的券面总额"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
|
||||
|
||||
// 参考标的证券全称和参考标的名义份额(复用上方已声明的bond)
|
||||
dic["参考标的证券全称"] = underlying != null
|
||||
? (JsonHelper.Deserialize<UnderlyingBond>(underlying.ExJson)?.UnderlyingFullName ?? underlying.UnderlyingName)
|
||||
: "";
|
||||
dic["参考标的名义份额"] = swapPosition != null
|
||||
? ((double)swapPosition.PosiQuantity).ToString("N2")
|
||||
: "0.00";
|
||||
|
||||
// 乙方联系人信息
|
||||
var clientDuties = Context.GetClientDuties();
|
||||
var contact = clientDuties.FirstOrDefault();
|
||||
dic["联系人"] = contact?.ContactName ?? "";
|
||||
dic["电子邮件"] = contact?.Email ?? "";
|
||||
dic["电话"] = contact?.PhoneNumber ?? "";
|
||||
|
||||
// 表格数据示例
|
||||
var table1 = new JArray();
|
||||
var row = new JObject
|
||||
{
|
||||
["序号"] = 1,
|
||||
["标的代码"] = underlying?.UnderlyingCode ?? "",
|
||||
["标的名称"] = underlying?.UnderlyingName ?? "",
|
||||
["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00",
|
||||
["成交日期"] = trade.TradeDate?.ToString("yyyy年M月d日"),
|
||||
["到期日期"] = trade.ExerciseDate?.ToString("yyyy年M月d日")
|
||||
};
|
||||
table1.Add(row);
|
||||
dic["table1"] = table1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置期权数据
|
||||
/// </summary>
|
||||
private void SetOptionData(Dictionary<string, JToken> dic)
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
var client = Context.GetClient();
|
||||
var underlying = Context.GetTradeUnderlying();
|
||||
|
||||
dic["期权类型"] = trade.OptionType;
|
||||
dic["买卖方向"] = trade.BuySell;
|
||||
dic["行权价格"] = trade.Strike?.ToString("N4") ?? "0.0000";
|
||||
dic["名义本金"] = trade.StockEqvNotional.ToString("N2");
|
||||
dic["期权费"] = trade.TradePrice?.ToString("N2") ?? "0.00";
|
||||
dic["期初价格"] = trade.SpotPrice?.ToString("N4") ?? "0.0000";
|
||||
|
||||
if (underlying != null)
|
||||
{
|
||||
dic["标的代码"] = underlying.UnderlyingCode;
|
||||
dic["标的名称"] = underlying.UnderlyingName;
|
||||
dic["报价单位"] = underlying.QuoteUnitString ?? "元";
|
||||
}
|
||||
|
||||
// 美式/欧式
|
||||
dic["行权方式"] = trade.ExerciseMode == "American" ? "美式" : "欧式";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置基础数据
|
||||
/// </summary>
|
||||
private void SetBaseData(Dictionary<string, JToken> dic)
|
||||
{
|
||||
var trade = Context.Trade;
|
||||
var client = Context.GetClient();
|
||||
|
||||
dic["交易类型"] = trade.TradeType;
|
||||
dic["客户编号"] = client.Number;
|
||||
dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.Models;
|
||||
using YLErp.Plugins.TradeDocGenerator;
|
||||
using YLErp.Plugins.TradeDocGenerator.Abstracts;
|
||||
|
||||
namespace YLErp.Plugins.GuoLian.DocumentGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 国联证券结算单生成器
|
||||
/// </summary>
|
||||
internal class TradeSettleBillGenerator : BaseSettleBillGenerator, ITradeSettleBillGenerator
|
||||
{
|
||||
protected override string GetContractNo(out string contractIndex)
|
||||
{
|
||||
var contractNo = Context.GenerateContractNo(out contractIndex);
|
||||
return contractNo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模板文件路径
|
||||
/// </summary>
|
||||
protected override string GetTemplateFilePath()
|
||||
{
|
||||
var templatePath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\settlement_template\\settle_01.docx");
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取输出文件名
|
||||
/// </summary>
|
||||
protected override string GetOutputFileName(string contractNo, string contractIndex)
|
||||
{
|
||||
var client = Context.GetClient();
|
||||
var valueDate = Context.SystemValueDate;
|
||||
var fileName = $"结算单_{client.Name}_{valueDate:yyyyMMdd}_{contractIndex}";
|
||||
return $"{fileName}.{DocType.ToLower()}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备Word模板数据
|
||||
/// </summary>
|
||||
protected override void PrepareViewData(Dictionary<string, JToken> dic)
|
||||
{
|
||||
var client = Context.GetClient();
|
||||
var trade = Context.Trade;
|
||||
var tradeCash = Context.GetTradeCash();
|
||||
|
||||
// 基础信息
|
||||
// 注意:合同编号由基类统一设置,此处不再重复生成
|
||||
dic["客户名称"] = client.Name;
|
||||
dic["乙方名称"] = client.Name;
|
||||
dic["结算日期"] = tradeCash?.ValueDate.ToString("yyyy年M月d日") ?? DateTime.Now.ToString("yyyy年M月d日");
|
||||
|
||||
// 交易信息
|
||||
dic["交易编号"] = trade.TradeNumber;
|
||||
dic["交易类型"] = trade.TradeType;
|
||||
dic["标的代码"] = trade.UnderlyingCode;
|
||||
|
||||
var underlying = Context.GetTradeUnderlying();
|
||||
if (underlying != null)
|
||||
{
|
||||
dic["标的名称"] = underlying.UnderlyingName;
|
||||
}
|
||||
|
||||
// 结算金额相关
|
||||
if (tradeCash != null)
|
||||
{
|
||||
dic["结算金额"] = tradeCash.Amount.ToString("N2");
|
||||
dic["结算金额大写"] = Context.CmycurD(Math.Abs(tradeCash.Amount));
|
||||
dic["结算价格"] = tradeCash.FinalPrice?.ToString("N4") ?? "0.0000";
|
||||
dic["结算比例"] = tradeCash.UnwindPercentRate.HasValue
|
||||
? (tradeCash.UnwindPercentRate.Value * 100).ToString("N2") + "%"
|
||||
: "0.00%";
|
||||
}
|
||||
|
||||
// 名义本金
|
||||
dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
|
||||
|
||||
// 表格数据
|
||||
var table1 = new JArray();
|
||||
var row = new JObject
|
||||
{
|
||||
["交易编号"] = trade.TradeNumber,
|
||||
["交易类型"] = trade.TradeType,
|
||||
["标的代码"] = trade.UnderlyingCode,
|
||||
["标的名称"] = underlying?.UnderlyingName ?? "",
|
||||
["结算金额"] = tradeCash?.Amount.ToString("N2") ?? "0.00"
|
||||
};
|
||||
table1.Add(row);
|
||||
dic["table1"] = table1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备Excel模板数据(如需要)
|
||||
/// </summary>
|
||||
protected override void PrepareViewData(Dictionary<string, object> dic)
|
||||
{
|
||||
var client = Context.GetClient();
|
||||
var trade = Context.Trade;
|
||||
var tradeCash = Context.GetTradeCash();
|
||||
|
||||
dic["合同编号"] = GetContractNo(out string contractIndex);
|
||||
dic["客户名称"] = client.Name;
|
||||
dic["结算日期"] = tradeCash?.ValueDate.ToString("yyyy-MM-dd") ?? DateTime.Now.ToString("yyyy-MM-dd");
|
||||
dic["交易编号"] = trade.TradeNumber;
|
||||
dic["交易类型"] = trade.TradeType;
|
||||
dic["标的代码"] = trade.UnderlyingCode;
|
||||
|
||||
var underlying = Context.GetTradeUnderlying();
|
||||
dic["标的名称"] = underlying?.UnderlyingName ?? "";
|
||||
|
||||
if (tradeCash != null)
|
||||
{
|
||||
dic["结算金额"] = tradeCash.Amount;
|
||||
dic["结算价格"] = tradeCash.FinalPrice ?? 0;
|
||||
}
|
||||
|
||||
dic["名义本金"] = trade.OriginalStockEqvNotional ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.Plugins.TradeDocGenerator;
|
||||
using YLErp.Plugins.TradeDocGenerator.Abstracts;
|
||||
|
||||
namespace YLErp.Plugins.GuoLian.DocumentGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 国联证券了结报告生成器
|
||||
/// </summary>
|
||||
internal class TradeUnwindReportGenerator : BaseUnwindReportGenerator, ITradeUnwindReportGenerator
|
||||
{
|
||||
protected override string GetContractNo(out string contractIndex)
|
||||
{
|
||||
var contractNo = Context.GenerateContractNo(out contractIndex);
|
||||
return contractNo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备Word模板数据
|
||||
/// </summary>
|
||||
protected override void PrepareViewData(Dictionary<string, JToken> dic)
|
||||
{
|
||||
var table1 = new JArray();
|
||||
var trade = Context.Trade;
|
||||
var client = Context.GetClient();
|
||||
var valueTimeList = new List<DateTime>();
|
||||
var underlying = Context.GetTradeUnderlying();
|
||||
var tradeCash = Context.GetTradeCash();
|
||||
var tradeList = Context.Trades.Union(Context.SubTrades).ToList();
|
||||
|
||||
foreach (var item in Context.Trades)
|
||||
{
|
||||
foreach (var tradeCashItem in Context.TradeCashes[item.id])
|
||||
{
|
||||
var settlementPrice = (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value
|
||||
? tradeCashItem.UnwindPricePercentRate.OtcFormatPercent()
|
||||
: tradeCashItem.UnwindPrice.OtcFormatMoney());
|
||||
|
||||
var row = new JObject();
|
||||
var currentTrade = tradeList.Find(o => o.id == tradeCashItem.TradeId);
|
||||
|
||||
row["交易编号"] = currentTrade?.TradeNumber ?? "";
|
||||
|
||||
// 买卖方向处理
|
||||
switch (item.BuySell)
|
||||
{
|
||||
case "买入":
|
||||
row["买方"] = (trade.OpponentRole == null || trade.OpponentRole == "乙方") ? "甲方" : "乙方";
|
||||
break;
|
||||
case "卖出":
|
||||
row["买方"] = (trade.OpponentRole == null || trade.OpponentRole == "乙方") ? "乙方" : "甲方";
|
||||
break;
|
||||
default:
|
||||
row["买方"] = "";
|
||||
break;
|
||||
}
|
||||
|
||||
row["交易日期"] = currentTrade?.TradeDate?.ToString("yyyy/MM/dd") ?? "";
|
||||
row["开始日期"] = $"{currentTrade?.StartDate:yyyy/MM/dd}-{currentTrade?.ExerciseDate:yyyy/MM/dd}";
|
||||
row["名义本金"] = currentTrade?.OriginalStockEqvNotional.OtcFormatMoney() ?? "0.00";
|
||||
row["标的代码"] = currentTrade?.UnderlyingCode ?? "";
|
||||
row["期权类型"] = currentTrade?.TradeType ?? "";
|
||||
row["期初价格"] = currentTrade?.TradePrice.OtcFormatMoney() ?? "0.00";
|
||||
row["结算日期"] = tradeCashItem.ValueDate.ToString("yyyy/MM/dd");
|
||||
row["结算价格"] = tradeCashItem.FinalPrice.OtcFormatMoney();
|
||||
row["结算模块"] = (tradeCashItem.UnwindPercentRate * currentTrade?.OriginalNotional * underlying?.CountRatio).OtcFormatMoney() ?? "0.00";
|
||||
row["结算单价"] = settlementPrice;
|
||||
|
||||
var amountQrs = -tradeCashItem.Amount;
|
||||
var amountQrs2 = -(tradeCashItem.Amount + (currentTrade?.TradePrice ?? 0) * (tradeCashItem.UnwindPercentRate ?? 0) * ((currentTrade?.BuySell == "卖出") ? 1 : -1));
|
||||
|
||||
row["结算期权费"] = amountQrs.OtcFormatMoney();
|
||||
row["结算金额"] = amountQrs2.OtcFormatMoney();
|
||||
|
||||
table1.Add(row);
|
||||
valueTimeList.Add(tradeCashItem.ValueDate);
|
||||
}
|
||||
}
|
||||
|
||||
// 合同编号由基类统一设置,此处不再重复生成
|
||||
dic["table1"] = table1;
|
||||
dic["合同日期"] = DateTime.Now.ToString("yyyy/MM/dd");
|
||||
dic["了结日期1"] = valueTimeList.Min().ToString("yyyy/MM/dd");
|
||||
dic["了结日期2"] = valueTimeList.Max().ToString("yyyy/MM/dd");
|
||||
|
||||
// 甲乙方信息
|
||||
var ownerInfo = Context.GetOwner_info("默认");
|
||||
dic["客户名称"] = client?.Name ?? "";
|
||||
dic["公司名称"] = ownerInfo?.FullName ?? "";
|
||||
|
||||
if (trade.OpponentRole == null || trade.OpponentRole == "乙方")
|
||||
{
|
||||
dic["甲方"] = ownerInfo?.FullName ?? "";
|
||||
dic["乙方"] = client?.Name ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
dic["甲方"] = client?.Name ?? "";
|
||||
dic["乙方"] = ownerInfo?.FullName ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模板文件路径
|
||||
/// </summary>
|
||||
protected override string GetTemplateFilePath()
|
||||
{
|
||||
var templatePath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\settlement_template\\unwind_01.docx");
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取输出文件名
|
||||
/// </summary>
|
||||
protected override string GetOutputFileName(string contractNo, string contractIndex)
|
||||
{
|
||||
var client = Context.GetClient();
|
||||
var trade = Context.Trade;
|
||||
var tradeList = Context.Trades.Union(Context.SubTrades).ToList();
|
||||
var ownerInfo = Context.GetOwner_info("默认") ?? new Owner_info();
|
||||
|
||||
var maxTradeDate = tradeList.Max(m => m.TradeDate);
|
||||
var minTradeDate = tradeList.Min(m => m.TradeDate);
|
||||
|
||||
string date;
|
||||
if (maxTradeDate == minTradeDate)
|
||||
{
|
||||
date = trade.TradeDate?.ToString("yyyyMMdd") ?? DateTime.Now.ToString("yyyyMMdd");
|
||||
}
|
||||
else
|
||||
{
|
||||
date = $"{minTradeDate:yyyyMMdd}-{maxTradeDate:yyyyMMdd}";
|
||||
}
|
||||
|
||||
var fullName = ownerInfo?.FullName ?? "";
|
||||
var rule = $"{fullName} {trade.TradeType}结算确认书 {date}";
|
||||
Context.GetcontractIndex(rule, out var contractIndex2);
|
||||
return $"{rule}-{contractIndex2}.{DocType.ToLower()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>..\build\GuoLian\</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Framework\YLErp.Core\YLErp.Core.csproj" />
|
||||
<ProjectReference Include="..\..\YLErpDAL\YLErpDAL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- 收益互换交易确认书模板:4种组合(客户提供的原始文件名) -->
|
||||
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<!-- 结算单模板 -->
|
||||
<None Update="App_Docs\settlement_template\settle_01.docx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<!-- 了结报告模板 -->
|
||||
<None Update="App_Docs\settlement_template\unwind_01.docx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,124 @@
|
||||
using YieldChain.Helpers;
|
||||
using YLErp.BLL;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.Modules;
|
||||
|
||||
namespace YLErp.CustomizedBizLogic
|
||||
{
|
||||
/// <summary>
|
||||
/// 国联证券业务逻辑
|
||||
/// </summary>
|
||||
public class BizLogicGuoLian : BizLogicDefault
|
||||
{
|
||||
/// <summary>
|
||||
/// 在交易确认前生成交易编号
|
||||
/// 格式:GL-{yyyyMMdd}-{序号}
|
||||
/// </summary>
|
||||
public override string GenerateTradeNumberBeforeConfirm(TradeNumberReqModel reqModel, YLContext db)
|
||||
{
|
||||
if (reqModel is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(reqModel));
|
||||
}
|
||||
|
||||
// 对于对冲交易、场内交易等没有设置客户的
|
||||
if (reqModel.clientId == 0)
|
||||
{
|
||||
return DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||||
}
|
||||
|
||||
// 国联格式:GL-{yyyyMMdd}-{4位序号}
|
||||
var leftPart = $"GL-{reqModel.trade.TradeDate:yyyyMMdd}-";
|
||||
var fullSize = leftPart.Length + 4;
|
||||
|
||||
// 获取数据库该用户最大的交易编号记录
|
||||
var tradeNumber = db.trade.Where(x =>
|
||||
x.TradeNumber.StartsWith(leftPart) &&
|
||||
x.TradeNumber.Length == fullSize &&
|
||||
x.ValidState != ConsGlobal.InValid)
|
||||
.OrderByDescending(x => x.TradeNumber)
|
||||
.Select(n => n.TradeNumber)
|
||||
.FirstOrDefault() ?? string.Empty;
|
||||
|
||||
if (reqModel.importTrades != null && reqModel.importTrades.Any())
|
||||
{
|
||||
// 获取导入交易中该客户最大的交易编号记录
|
||||
var importTradeNumber = reqModel.importTrades
|
||||
.Where(x => x.TradeNumber.StartsWith(leftPart) && x.TradeNumber.Length == fullSize)
|
||||
.OrderByDescending(x => x.TradeNumber)
|
||||
.Select(n => n.TradeNumber)
|
||||
.FirstOrDefault() ?? string.Empty;
|
||||
tradeNumber = tradeNumber.CompareTo(importTradeNumber) > 0 ? tradeNumber : importTradeNumber;
|
||||
}
|
||||
|
||||
// 获取总的该用户最大的交易编号记录
|
||||
int startNumber = string.IsNullOrWhiteSpace(tradeNumber)
|
||||
? 1
|
||||
: (int.Parse(tradeNumber.Substring(leftPart.Length)) + 1);
|
||||
|
||||
return GetTradeNumber(startNumber, leftPart, db);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取可用的交易编号(循环实现,避免递归栈溢出)
|
||||
/// </summary>
|
||||
private string GetTradeNumber(int startNumber, string leftPart, YLContext db)
|
||||
{
|
||||
for (int newNumber = startNumber; newNumber < 10000; newNumber++)
|
||||
{
|
||||
var candidate = leftPart + newNumber.ToString("D4");
|
||||
var childTradeNumberPart = candidate + "-";
|
||||
|
||||
// 检查是否存在以该编号开头的子交易
|
||||
bool exists = db.trade.Any(x =>
|
||||
EF.Functions.Like(x.TradeNumber, $"{childTradeNumberPart}%") &&
|
||||
x.ValidState != ConsGlobal.InValid);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("该客户交易编号已达到最大限制9999,生成交易编号失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取交易确认书前缀
|
||||
/// </summary>
|
||||
public override string GetTradeContractPrefix(string tradeNumber)
|
||||
{
|
||||
// 国联证券确认书前缀
|
||||
return "GL-";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成客户编号
|
||||
/// 格式:{年份后两位}{3位序号}{拼音首字母}
|
||||
/// </summary>
|
||||
public override void GenerateClientCode(Client clientObj, IQueryable<Client> clientDbQuery)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(clientObj.Number))
|
||||
{
|
||||
var firstPY = PingYinHelper.GetFirstPinYin_Enhance(clientObj.Name);
|
||||
var year = DateTime.Now.ToString("yy");
|
||||
var result = "";
|
||||
lock (_clientCodeLock)
|
||||
{
|
||||
var existingClientCodes = clientDbQuery
|
||||
.Where(x => x.Number.StartsWith(year))
|
||||
.Select(x => x.Number)
|
||||
.ToList();
|
||||
var number = existingClientCodes.Count();
|
||||
do
|
||||
{
|
||||
number++;
|
||||
result = year + number.ToString("000");
|
||||
} while (existingClientCodes.Any(O => O.StartsWith(result)));
|
||||
|
||||
clientObj.Number = result + firstPY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,8 @@ namespace YLErp.CustomizedBizLogic
|
||||
return new BizLogicShanXiGuShou();
|
||||
case CompanyEnum.浙商:
|
||||
return new BizLogicZheShang();
|
||||
case CompanyEnum.国联:
|
||||
return new BizLogicGuoLian();
|
||||
case CompanyEnum.中基石化: //暂时:确认书模式 是国君的
|
||||
default:
|
||||
return new BizLogicGTJA();
|
||||
|
||||
Reference in New Issue
Block a user