refactor(report): 修改山西结算报告文件生成方法 - 在GenerateFileEntry方法中添加applyFrontendColumnConfig参数,默认值为true - 更新GenerateReportExcel方法签名以接收applyFrontendColumnConfig参数 - 将ApplySwapValuationColumnConfig调用包装在条件判断中 - 修正PDF和Excel文件生成时的参数传递 - 更新XML文档注释以反映新参数的作用 ```
687 lines
35 KiB
C#
687 lines
35 KiB
C#
using BaseOUDAL;
|
|
using Newtonsoft.Json;
|
|
using OfficeOpenXml;
|
|
using OfficeOpenXml.Style;
|
|
using Org.BouncyCastle.Ocsp;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Web;
|
|
using YieldChain.Helpers;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.EodSettlement;
|
|
using YLErp.Configuration;
|
|
using YLErp.Core.Helpers;
|
|
using YLErp.DBModels;
|
|
using YLErp.Enums;
|
|
using YLErp.Helpers;
|
|
using YLErp.Model;
|
|
using YLErp.Modules.ClientModule;
|
|
using YLErp.Modules.DataCacheModule;
|
|
using YLErp.Modules.SwapModule;
|
|
using YLErp.Office;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.ReportModule.SettlementReportModule
|
|
{
|
|
/// <summary>
|
|
/// 山西固收结算报告
|
|
/// </summary>
|
|
public class SettlementReportFotShanXiService : YLBaseService
|
|
{
|
|
public SettlementReportFotShanXiService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
public ClientDingShiReport_ShanXi GetReportData(DingShiReportEmail emailData, IEnumerable<int> userAssetUnits, string template = "")
|
|
{
|
|
var report = new ClientDingShiReport_ShanXi() { ReportFrom = emailData.From, ReportEnd = emailData.To };
|
|
report.client = DataCacheProvider.GetClientDataSource().GetData(emailData.ClientId);
|
|
report.CurUserName = emailData.CurUserName;
|
|
report.OptUserInfo = this.UserInfo;
|
|
report.ClientBank = new ClientDBContext().bankcard.Where(x => x.ApprovalOrder < 1 && x.ClientId == emailData.ClientId && x.ValidState != "InValid").ToList();
|
|
var swapEodPositionService = new SwapEodPositionService(this);
|
|
#region 账户状况
|
|
if (emailData.SendContent.Contains("账户状况"))
|
|
{
|
|
|
|
var IsGap = false;
|
|
var IsOuter = false;
|
|
if (emailData.ReportType == "ClientBalanceGap")
|
|
{
|
|
IsGap = true;
|
|
}
|
|
if (emailData.ReportType == "GetOuterMargin")
|
|
{
|
|
IsOuter = true;
|
|
}
|
|
report.FundReportModel = GetFundReport(emailData,IsGap,IsOuter);
|
|
}
|
|
#endregion
|
|
if (emailData.SendContent.Contains("互换估值"))
|
|
{
|
|
var eodReq = new ClientSwapPositionRequest
|
|
{
|
|
ClientId = emailData.ClientId,
|
|
BookId = emailData.BookId,
|
|
ValueDate = emailData.To,
|
|
ValueDateFrom = emailData.From,
|
|
page = 1,
|
|
rows = 10000,
|
|
StructureType = "普通债券类收益互换"
|
|
};
|
|
report.EodSwapPositions = swapEodPositionService.SearchEodPositionList(eodReq).rows.ToList();
|
|
}
|
|
if (emailData.SendContent.Contains("互换持仓明细"))
|
|
{
|
|
var eodReq = new ClientSwapPositionRequest { ClientId = emailData.ClientId, ValueDate = emailData.To, ValueDateFrom = emailData.From, page = 1, rows = 10000 };
|
|
report.SwapPositions = swapEodPositionService.SearchPositionList(eodReq).rows.ToList();
|
|
}
|
|
if (emailData.SendContent.Contains("互换交易流水"))
|
|
{
|
|
var eodReq = new ClientSwapPositionRequest { ClientId = emailData.ClientId, ValueDate = emailData.To, ValueDateFrom = emailData.From, page = 1, rows = 10000 };
|
|
report.clientSwapPositions = new SwapFlowEventService(UserInfo).SearchPositionFlowEvent(eodReq).rows.ToList();
|
|
}
|
|
if (emailData.SendContent.Contains("资金明细"))
|
|
{
|
|
var eereq = new EntryExitReq() { ClientId = emailData.ClientId, HappenDateStart = emailData.From, HappenDateEnd = emailData.To, ParentFlag = emailData.ParentFlag };
|
|
report.ClientCashInCashOutExtendList = SearchListExtendOnly(eereq);
|
|
}
|
|
|
|
if (emailData.SendContent.Contains("质押记录"))
|
|
{
|
|
var productReq = new clientcashincashout_productReq() { ClientId = emailData.ClientId, ValueDate = emailData.To, ParentFlag = emailData.ParentFlag };
|
|
report.clientcashincashout_productLinq = SearchListOnlyForMarketReport(productReq);
|
|
}
|
|
//txt报告特殊处理用到
|
|
var desc = DBCacheManager.Single.GetStr(CacheTable.DingShiDesc, template: template);
|
|
if (string.IsNullOrWhiteSpace(desc))
|
|
{
|
|
desc = DBCacheManager.Single.GetStr(CacheTable.DingShiDesc);
|
|
}
|
|
var descList = Regex.Split(desc, "</p>", RegexOptions.IgnoreCase).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
|
|
var newDescList = new List<string>();
|
|
descList.ForEach(x =>
|
|
{
|
|
x = Regex.Replace(x, "<[^>]+>", "");
|
|
x = Regex.Replace(x, "&[^;]+;", "");
|
|
newDescList.Add(x);
|
|
});
|
|
report.downloadFilePath = emailData.DownloadFilePath;
|
|
report.descList = newDescList;
|
|
report.desc = desc;//string.Join("\n", newDescList);
|
|
if (string.IsNullOrWhiteSpace(emailData.Title))
|
|
{
|
|
emailData.Title = DBCacheManager.Single.GetStr(CacheTable.ClientBalanceReportTiltle, template: template);
|
|
}
|
|
report.Title = ReplaceWildcard(emailData.Title, report);
|
|
return report;
|
|
}
|
|
/// <summary>
|
|
/// 发送邮件
|
|
/// </summary>
|
|
/// <param name="emailData"></param>
|
|
/// <param name="report"></param>
|
|
/// <param name="template"></param>
|
|
/// <param name="pdfHtml"></param>
|
|
/// <param name="txtHtml"></param>
|
|
/// <param name="IsSkipCheckMarginCall"></param>
|
|
/// <param name="recevier"></param>
|
|
/// <returns></returns>
|
|
public (EmailTradeConfirmResultType status, string message) SendSettlementReports(DingShiReportEmail emailData, ClientDingShiReport_ShanXi report, string template, bool IsSkipCheckMarginCall = false, List<string> recevier = null)
|
|
{
|
|
LogFactory.GetLogger().Info("开始发邮件[info]SendSettlementReports");
|
|
string message = null;
|
|
var attachFiles = new List<string>();
|
|
List<string> fileTypes=new List<string>();
|
|
emailData.FileTypes.ForEach(type =>
|
|
{
|
|
if (type.@checked)
|
|
{
|
|
fileTypes.Add(type.type.ToLower());
|
|
}
|
|
});
|
|
if (fileTypes.Contains("pdf"))
|
|
{
|
|
var filepath = GenerateFileEntry(report, "pdf", applyFrontendColumnConfig: false);
|
|
if (!string.IsNullOrEmpty(filepath))
|
|
{
|
|
attachFiles.Add(filepath);
|
|
}
|
|
if (fileTypes.Contains("excel"))
|
|
{
|
|
filepath = filepath.Replace(".pdf",".xlsx");
|
|
attachFiles.Add(filepath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var filepath = GenerateFileEntry(report, "excel", applyFrontendColumnConfig: false);
|
|
if (!string.IsNullOrEmpty(filepath))
|
|
{
|
|
attachFiles.Add(filepath);
|
|
}
|
|
}
|
|
// 要向该客户的所有订阅了邮件通知的人员发送邮件
|
|
var emails = ClientDataQueryService.GetClientEmails(report.client.id, false, recevier,true);
|
|
var status = EmailTradeConfirmResultType.Succeed;
|
|
if (emails.All(o => string.IsNullOrWhiteSpace(o)))
|
|
{
|
|
status = EmailTradeConfirmResultType.NoEmailSetting;
|
|
}
|
|
else
|
|
{
|
|
emails = emails.Where(o => !string.IsNullOrWhiteSpace(o));
|
|
|
|
var title = report.Title;
|
|
var detail = emailData.Detail;
|
|
detail = ReplaceWildcard(detail, report);
|
|
var mailFrom = DBCacheManager.Single.GetStr(CacheTable.TradeMarketSendUser, template);
|
|
if (string.IsNullOrWhiteSpace(mailFrom))
|
|
{
|
|
mailFrom = DBCacheManager.Single.GetStr(CacheTable.TradeMarketSendUser);
|
|
}
|
|
|
|
LogFactory.GetLogger().Info("开始发邮件[info]SendSettlementReports_start");
|
|
message = EmailHelper.SendMail(string.Join(";", emails), title, detail, true, attachFiles, emailData.CCEmail, mailFrom);
|
|
LogFactory.GetLogger().Info($"开始发邮件[info]SendSettlementReports_end {message}");
|
|
|
|
if (!string.IsNullOrEmpty(message))
|
|
{
|
|
status = EmailTradeConfirmResultType.EmailSentFailed;
|
|
}
|
|
|
|
}
|
|
return (status, message);
|
|
}
|
|
/// <summary>
|
|
/// 生成附件
|
|
/// </summary>
|
|
/// <param name="report"></param>
|
|
/// <param name="type"></param>
|
|
/// <param name="applyFrontendColumnConfig">是否按当前用户的前端列配置隐藏并重排互换估值列</param>
|
|
/// <returns></returns>
|
|
public string GenerateFileEntry(ClientDingShiReport_ShanXi report, string type, bool applyFrontendColumnConfig = true)
|
|
{
|
|
var filepath = GenerateReportExcel(report, type.ToLower() == "pdf", applyFrontendColumnConfig);
|
|
return filepath;
|
|
}
|
|
|
|
public string GenerateReportExcel(ClientDingShiReport_ShanXi report, bool needToPdf, bool applyFrontendColumnConfig = true)
|
|
{
|
|
var tempFolder = OtcAppContext.MapPath("~/App_Docs/Temp/结算报告");
|
|
tempFolder = MosPathHelper.Combine(tempFolder, "");
|
|
var targetPath = string.IsNullOrEmpty(report.downloadFilePath) ? Path.Combine(tempFolder, report.ReportEnd.ToString("yyyyMMdd")) : report.downloadFilePath;
|
|
if (!Directory.Exists(targetPath))
|
|
{
|
|
Directory.CreateDirectory(targetPath);
|
|
}
|
|
var clientName = report.client.Name;
|
|
var fileName = $"{clientName}_每日估值报告_{report.ReportEnd:yyyyMMdd}";
|
|
var targetFileName = Path.Combine(targetPath, $"{fileName}.xlsx");
|
|
|
|
var excelDeclareModel = new ExcelDeclareModel()
|
|
{
|
|
Company = PS.Config.CompanyFullName,// PS.Config.Company.ToString() + "风险管理有限公司",
|
|
CurDay = DateTime.Now.Date.ToString("yyyy年MM月dd日"),
|
|
CurTime = DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss"),
|
|
CurUser = string.IsNullOrEmpty(report.CurUserName) ? OptUser.UserName : report.CurUserName,
|
|
Name = report.client.Name,
|
|
Number = report.client.Number,
|
|
ReportEnd = report.ReportEnd.ToString("yyyy年MM月dd日"),
|
|
ReportFrom = report.ReportFrom.ToString("yyyy年MM月dd日") == "0001年01月01日" ? "" : report.ReportFrom.ToString("yyyy年MM月dd日"),
|
|
Title = report.Title,
|
|
DescList=report.descList,
|
|
Desc=report.desc
|
|
};
|
|
|
|
var modelDict = new Dictionary<string, object>();
|
|
|
|
if (report.FundReportModel != null)
|
|
{
|
|
report.FundReportModel.ExcelDeclareModel = excelDeclareModel;
|
|
|
|
modelDict.Add("账户状况", report.FundReportModel);
|
|
}
|
|
if (report.EodSwapPositions != null)
|
|
{
|
|
modelDict.Add("互换估值", new
|
|
{
|
|
// 明细列表供模板渲染;以下 *Sum 字段用于“互换估值”页签合计行。
|
|
EodSwapPositions = report.EodSwapPositions,
|
|
// 持仓规模、期间收益和利息/分红类金额合计。
|
|
PosiNotionalValueSum= report.EodSwapPositions.Sum(x => x.position.PosiNotionalValue),
|
|
PeriodAmountSum= report.EodSwapPositions.Sum(x => x.PeriodAmount) ?? 0m,
|
|
DividendAmountSum = report.EodSwapPositions.Sum(x => x.DividendAmount) ?? 0m,
|
|
InterestAmountSum= report.EodSwapPositions.Sum(x => x.InterestAmount),
|
|
PosiFeePendingSum = report.EodSwapPositions.Sum(x => x.position.PosiFeePending),
|
|
PosiProfitSum= report.EodSwapPositions.Sum(x => x.position.PosiProfitSum),
|
|
// 保证金相关收益和保证金占用金额合计。
|
|
MarginInterestAmountSum = report.EodSwapPositions.Sum(x => x.MarginInterestAmount),
|
|
OpenMarginAmountSum = report.EodSwapPositions.Sum(x => x.OpenMarginAmount),
|
|
AdditionalMarginAmountSum = report.EodSwapPositions.Sum(x => x.AdditionalMarginAmount),
|
|
// 净结算金额为日终估值口径;TRS价值在净结算金额基础上叠加期初/追加保证金。
|
|
NetSettmentAmountSum= report.EodSwapPositions.Sum(x => x.NetSettmentAmount),
|
|
TrsValueSum = report.EodSwapPositions.Sum(x => x.TrsValue),
|
|
});
|
|
}
|
|
if (report.SwapPositions != null)
|
|
{
|
|
modelDict.Add("互换持仓明细", report.SwapPositions);
|
|
}
|
|
if (report.clientSwapPositions != null)
|
|
{
|
|
modelDict.Add("互换交易流水", new
|
|
{
|
|
ClientSwapPositions = report.clientSwapPositions,
|
|
TradeFeeSum= report.clientSwapPositions.Sum(x => x.TradeFee),
|
|
TradingFeeSum= report.clientSwapPositions.Sum(x => x.TradingFee),
|
|
PosiPnlSum = report.clientSwapPositions.Sum(x => x.PosiPnl),
|
|
DividendInSum= report.clientSwapPositions.Sum(x => x.FlowEvent.DividendIn),
|
|
InterestAmountSum= report.clientSwapPositions.Sum(x => x.FlowEvent.InterestClosePnL),
|
|
InterestFeeSum= report.clientSwapPositions.Sum(x => x.FlowEvent.InterestFee),
|
|
NetSettmentAmountSum = report.clientSwapPositions.Sum(x => x.NetSettmentAmount),
|
|
});
|
|
}
|
|
if (report.ClientCashInCashOutExtendList != null)
|
|
{
|
|
modelDict.Add("资金明细",
|
|
new
|
|
{
|
|
ClientCashInCashOutList = report.ClientCashInCashOutExtendList,
|
|
MoneyToShowSum = report.ClientCashInCashOutExtendList.Sum(x => x.MoneyToShow),
|
|
MoneyInOutSum = report.ClientCashInCashOutExtendList.Sum(x => x.MoneyInOut),
|
|
MoneyPriceSum = report.ClientCashInCashOutExtendList.Sum(x => x.MoneyPrice),
|
|
MoneyUnwindExerSum = report.ClientCashInCashOutExtendList.Sum(x => x.MoneyUnwindExer),
|
|
MoneyOtherSum = report.ClientCashInCashOutExtendList.Sum(x => x.MoneyOther)
|
|
});
|
|
}
|
|
if (report.clientcashincashout_productLinq != null)
|
|
{
|
|
modelDict.Add("质押记录", report.clientcashincashout_productLinq);
|
|
}
|
|
var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板");
|
|
string sourceFileName = Path.Combine(sourcePath, $"结算报告模板.xlsx");
|
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
|
var pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName
|
|
, shouldDeleteSheet: true, needToPdf: false, callback: sheets =>
|
|
{
|
|
FormatSwapValuationDisplayCells(sheets, report.EodSwapPositions);
|
|
if (applyFrontendColumnConfig)
|
|
{
|
|
ApplySwapValuationColumnConfig(sheets);
|
|
}
|
|
});
|
|
if (needToPdf)
|
|
{
|
|
var targetPdfFileName = FileHelper.ReplaceExtension(targetFileName, ".pdf");
|
|
ConvertToPdfHelper.ConvertPDFByApi(HttpUtility.UrlEncode(targetFileName), HttpUtility.UrlEncode(targetPdfFileName));
|
|
return targetPdfFileName;
|
|
}
|
|
return Path.Combine(targetPath, targetFileName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 部分 Office 版本会将可选小数格式(例如 <c>#,##0.##</c>)错误显示为 <c>20,000.</c>。
|
|
/// 互换估值中的这些列是展示字段,不参与 Excel 公式计算,因此在模板替换完成后写为已格式化文本,
|
|
/// 以遵守各字段的去尾零或固定小数位展示口径,且避免留下孤立的小数点。
|
|
/// </summary>
|
|
private static void FormatSwapValuationDisplayCells(IEnumerable<ExcelWorksheet> sheets, IEnumerable<EodSwapPositionResponse> positions)
|
|
{
|
|
var worksheet = sheets.FirstOrDefault(x => x.Name == "互换估值");
|
|
var positionList = positions?.ToList() ?? new List<EodSwapPositionResponse>();
|
|
if (worksheet == null || !positionList.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int dataStartRow = 2;
|
|
for (var index = 0; index < positionList.Count; index++)
|
|
{
|
|
var row = dataStartRow + index;
|
|
var item = positionList[index];
|
|
SetTrimmedExcelText(worksheet.Cells[row, 8], item.position.PosiNotionalValue, 2);
|
|
SetTrimmedExcelText(worksheet.Cells[row, 9], item.position.PosiQuantity, 9);
|
|
SetTrimmedExcelText(worksheet.Cells[row, 10], item.PeriodAmount, 2);
|
|
SetTrimmedExcelText(worksheet.Cells[row, 11], item.DividendAmount, 2);
|
|
SetTrimmedExcelText(worksheet.Cells[row, 13], item.InitYtm, 4, percent: true);
|
|
// 浮动利率(绝对)和预付金利率要求固定保留四位小数,不去尾零。
|
|
// 模板首列为空白占位列,渲染后的工作表会移除该列;最终文件中两列分别为 P、T。
|
|
SetFixedExcelText(worksheet.Cells[row, 16], item.FloatRateAbs, 4, percent: true);
|
|
SetFixedExcelText(worksheet.Cells[row, 20], item.OpenMarginRate, 4, percent: true);
|
|
}
|
|
|
|
var totalRow = dataStartRow + positionList.Count;
|
|
SetTrimmedExcelText(worksheet.Cells[totalRow, 8], positionList.Sum(x => x.position.PosiNotionalValue), 2);
|
|
SetTrimmedExcelText(worksheet.Cells[totalRow, 10], positionList.Sum(x => x.PeriodAmount) ?? 0m, 2);
|
|
SetTrimmedExcelText(worksheet.Cells[totalRow, 11], positionList.Sum(x => x.DividendAmount) ?? 0m, 2);
|
|
}
|
|
|
|
private static void SetTrimmedExcelText(ExcelRange cell, decimal? value, int decimalPlaces, bool percent = false)
|
|
{
|
|
if (!value.HasValue)
|
|
{
|
|
cell.Value = null;
|
|
return;
|
|
}
|
|
|
|
var displayValue = percent ? value.Value * 100m : value.Value;
|
|
var format = "#,##0." + new string('#', decimalPlaces);
|
|
var text = displayValue.ToString(format, CultureInfo.InvariantCulture).TrimEnd('.');
|
|
cell.Value = percent ? text + "%" : text;
|
|
cell.Style.Numberformat.Format = "@";
|
|
}
|
|
|
|
private static void SetFixedExcelText(ExcelRange cell, decimal? value, int decimalPlaces, bool percent = false)
|
|
{
|
|
if (!value.HasValue)
|
|
{
|
|
cell.Value = null;
|
|
return;
|
|
}
|
|
|
|
var displayValue = percent ? value.Value * 100m : value.Value;
|
|
var text = displayValue.ToString("F" + decimalPlaces, CultureInfo.InvariantCulture);
|
|
cell.Value = percent ? text + "%" : text;
|
|
cell.Style.Numberformat.Format = "@";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 每日估值报告的互换估值导出使用当前用户已保存的列配置,
|
|
/// 同时同步业务字段的显示状态和列顺序。
|
|
/// </summary>
|
|
private void ApplySwapValuationColumnConfig(IEnumerable<ExcelWorksheet> sheets)
|
|
{
|
|
var worksheet = sheets.FirstOrDefault(x => x.Name == "互换估值");
|
|
if (worksheet == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var config = configcolumnBLL.GetData(UserId, configcolumn_data.互换估值V1);
|
|
if (string.IsNullOrWhiteSpace(config?.data))
|
|
{
|
|
return;
|
|
}
|
|
|
|
List<columnmodel> columns;
|
|
try
|
|
{
|
|
columns = JsonConvert.DeserializeObject<List<columnmodel>>(config.data);
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
// 列配置损坏时保留完整的模板字段,不能导致每日估值报告导出失败。
|
|
return;
|
|
}
|
|
|
|
if (columns == null || !columns.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var columnIndexByName = new Dictionary<string, int>
|
|
{
|
|
["ConfrimNo"] = 1,
|
|
["TradeNumber"] = 2,
|
|
["position.PosiStartDate"] = 3,
|
|
["MaturitySettlementDate"] = 4,
|
|
["position.ValueDate"] = 5,
|
|
["position.UnderlyingCode"] = 6,
|
|
["InterestRate"] = 7,
|
|
["position.PosiNotionalValue"] = 8,
|
|
["position.PosiQuantity"] = 9,
|
|
["PeriodAmount"] = 10,
|
|
["DividendAmount"] = 11,
|
|
["position.PosiGrossPrice"] = 12,
|
|
["InitYtm"] = 13,
|
|
["position.UnderlyingPrice"] = 14,
|
|
["DayCount"] = 15,
|
|
["FloatRateAbs"] = 16,
|
|
["InterestAmount"] = 17,
|
|
["position.PosiProfitSum"] = 18,
|
|
["position.PosiFeePending"] = 19,
|
|
["OpenMarginRate"] = 20,
|
|
["MarginInterestAmount"] = 21,
|
|
["OpenMarginAmount"] = 22,
|
|
["AdditionalMarginAmount"] = 23,
|
|
["NetSettmentAmount"] = 24,
|
|
["TrsValue"] = 25
|
|
};
|
|
// 前端 _.uniqBy 保留首次出现的配置项,导出按相同规则过滤重复字段,
|
|
// 并保留该列表的原始顺序作为 Excel 列顺序。
|
|
var visibleColumnIndexes = new List<int>();
|
|
var configuredNames = new HashSet<string>();
|
|
foreach (var column in columns)
|
|
{
|
|
if (column == null || string.IsNullOrEmpty(column.name) || !configuredNames.Add(column.name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!column.hidden && columnIndexByName.TryGetValue(column.name, out var index))
|
|
{
|
|
visibleColumnIndexes.Add(index);
|
|
}
|
|
}
|
|
|
|
// 与前端一致:旧配置缺少的新增字段默认隐藏;若没有任何业务列可见,
|
|
// 前端会回退展示全部默认列,导出保持模板默认顺序。
|
|
if (!visibleColumnIndexes.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ReorderSwapValuationColumns(worksheet, visibleColumnIndexes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用临时工作表保存可见列,再按目标顺序复制回原工作表。
|
|
/// 原工作表始终保持在模板的 25 列范围内,避免 EPPlus 扩列时触发 ColumnMax 冲突。
|
|
/// </summary>
|
|
private static void ReorderSwapValuationColumns(ExcelWorksheet worksheet, IReadOnlyList<int> visibleSourceIndexes)
|
|
{
|
|
var lastRow = worksheet.Dimension.End.Row;
|
|
var originalColumnCount = worksheet.Dimension.End.Column;
|
|
var workbook = worksheet.Workbook;
|
|
var bufferName = "__swap_cols_" + Guid.NewGuid().ToString("N").Substring(0, 12);
|
|
var buffer = workbook.Worksheets.Add(bufferName);
|
|
try
|
|
{
|
|
for (var targetIndex = 0; targetIndex < visibleSourceIndexes.Count; targetIndex++)
|
|
{
|
|
var sourceColumn = visibleSourceIndexes[targetIndex];
|
|
var bufferColumn = targetIndex + 1;
|
|
worksheet.Cells[1, sourceColumn, lastRow, sourceColumn]
|
|
.Copy(buffer.Cells[1, bufferColumn, lastRow, bufferColumn]);
|
|
}
|
|
|
|
for (var targetIndex = 0; targetIndex < visibleSourceIndexes.Count; targetIndex++)
|
|
{
|
|
var targetColumn = targetIndex + 1;
|
|
buffer.Cells[1, targetColumn, lastRow, targetColumn]
|
|
.Copy(worksheet.Cells[1, targetColumn, lastRow, targetColumn]);
|
|
}
|
|
|
|
var columnsToDelete = originalColumnCount - visibleSourceIndexes.Count;
|
|
if (columnsToDelete > 0)
|
|
{
|
|
worksheet.DeleteColumn(visibleSourceIndexes.Count + 1, columnsToDelete);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
workbook.Worksheets.Delete(buffer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 财务状况
|
|
/// </summary>
|
|
/// <param name="emailData"></param>
|
|
/// <param name="IsGap"></param>
|
|
/// <param name="IsOuter"></param>
|
|
/// <returns></returns>
|
|
private FundReportModel GetFundReport(DingShiReportEmail emailData,bool IsGap,bool IsOuter)
|
|
{
|
|
var clientBalance = ClientBalanceUtility.GetClientBanlances(new List<int> { emailData.ClientId }, emailData.From, emailData.To, IsClientBalanceGap: IsGap, IsGetOuterMarginGap: IsOuter, ParentFlag: emailData.ParentFlag).FirstOrDefault();
|
|
var FundReportModel = new FundReportModel()
|
|
{
|
|
LastDayRemainFund = clientBalance?.LastDayRemainFund ?? 0,
|
|
LastDayRemainFundWithProduct = clientBalance?.LastDayRemainFundWithProduct ?? 0,
|
|
CashInCashOutChange = clientBalance?.NetFundAll ?? 0,
|
|
CashInChange = clientBalance?.InFund ?? 0,
|
|
CashOutChange = clientBalance?.OutFund ?? 0,
|
|
CashInCashOutOther = clientBalance?.OtherFund ?? 0,
|
|
CashInCashOutProductChange = clientBalance?.CashInCashOutProductChange ?? 0,
|
|
OptionPremium = clientBalance?.OptionPremium ?? 0,
|
|
OptionPremiumSwap = clientBalance?.OptionPremiumSwap ?? 0,
|
|
SettlementBalance = clientBalance?.SettlementBalance ?? 0,
|
|
UnwindBalance = clientBalance?.UnwindBalance ?? 0,
|
|
ExerciseBalance = clientBalance?.ExerciseBalance ?? 0,
|
|
SwapBalance = clientBalance?.SwapBalance ?? 0,
|
|
Coupon = clientBalance?.Coupon ?? 0,
|
|
TodayRemianFund = clientBalance?.AmountFund ?? 0,
|
|
TodayRemianFundProduct = clientBalance?.AmountFundWithProduct ?? 0,
|
|
GuaranteesTotalAmount = clientBalance?.GuaranteesTotalAmount ?? 0,
|
|
WorstCastClientPayable = clientBalance?.MinusPayableMarginTotal ?? 0,
|
|
AvailableFund = clientBalance?.AvailableAmount ?? 0,
|
|
TotalMargin = clientBalance?.TotalMarginTotal,
|
|
Credit = clientBalance?.TotalCredit ?? 0,
|
|
CreditRatio = clientBalance?.CreditUsed ?? 0,
|
|
Margin = clientBalance?.MarginByPayableMarginTotal ?? 0,
|
|
Amount = (PS.Config.IsPVRounded ? clientBalance?.RoundedTotalAmountTotal : clientBalance?.TotalAmountTotal) ?? 0,
|
|
TotalPnl = (PS.Config.IsPVRounded ? (decimal)(clientBalance?.RoundedPositionPnl ?? 0) : (decimal)(clientBalance?.PositionPnl ?? 0)),
|
|
PositionPremiumNetCash = clientBalance?.PositionPremiumNetCash ?? 0,
|
|
SellTradePrice = clientBalance?.SellTradePrice,
|
|
LastDayPositionPremiumNetCash = clientBalance?.LastDayPositionPremiumNetCash ?? 0,
|
|
WinLoss =Math.Round( clientBalance?.WinLoss ?? 0,ConsGlobal.MoneyRound,MidpointRounding.AwayFromZero),
|
|
ClosedTradeFundGap = clientBalance?.ClosedTradeFundGap ?? 0,
|
|
ClosedTradePayableFund = clientBalance?.ClosedTradePayableFundTotal ?? 0,
|
|
PositionTradePayableFund = clientBalance?.MarginByPayableMarginTotal ?? 0,
|
|
DesirableFund = clientBalance?.DesirableFundTotal ?? 0,
|
|
PayableFund = emailData.PayableFund >= 0 ? emailData.PayableFund : (clientBalance?.PayableFundTotal ?? 0),
|
|
PositionPv = PS.Config.IsPVRounded ? (clientBalance?.RoundedPositionPv ?? 0) : (clientBalance?.PositionPv ?? 0),
|
|
PrepaymentAmount = clientBalance?.PrepaymentAmount,
|
|
PositionPnl = PS.Config.IsPVRounded ? (clientBalance?.RoundedPositionPnl ?? 0) : (clientBalance?.PositionPnl ?? 0),
|
|
TotalNetSettlement = clientBalance?.TotalNetSettlementTotal ?? 0,
|
|
ClientSellPositionPnl = clientBalance?.ClientSellPositionPnl,
|
|
FreezePremium = clientBalance?.FreezePremium,
|
|
EndDesirableFund = clientBalance?.EndDesirableFund ?? 0,
|
|
MySideMargin= clientBalance?.MySideMargin,
|
|
MarginBalance= clientBalance?.MarginBalance,
|
|
NetFundAll = clientBalance?.NetFundAll,
|
|
SwapMarketAmount= clientBalance?.SwapMarketAmount,
|
|
VmFundSum= clientBalance?.VmFundSum,
|
|
MaintenanceMargin=clientBalance?.MaintenanceMargin,
|
|
};
|
|
return FundReportModel;
|
|
}
|
|
/// <summary>
|
|
/// 资金明细
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
private List<ClientCashInCashOutExtend> SearchListExtendOnly(EntryExitReq req)
|
|
{
|
|
req.State = "已确认,已结算";
|
|
req.Direction = $"入金,出金,其他收入,其他支出,应收";
|
|
req.TradeAction =
|
|
$"{ClientCashInCashOut.系统操作_行权费},{ClientCashInCashOut.系统操作_平仓费},{ClientCashInCashOut.系统操作_期权费},{ClientCashInCashOut.系统操作_票息},{ClientCashInCashOut.系统操作_互换},{ClientCashInCashOut.系统操作_应付预付金},{ClientCashInCashOut.系统操作_预付金返息}";
|
|
req.IsMoneyNotEqualsZero = true;
|
|
var sList = new EntryExitBLL().SearchListExtendOnly(req);
|
|
|
|
return sList;
|
|
}
|
|
/// <summary>
|
|
/// 质押记录
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
private List<clientcashincashout_productLinq> SearchListOnlyForMarketReport(clientcashincashout_productReq req)
|
|
{
|
|
var bll = new clientcashincashout_productBLL();
|
|
var sList = bll.SearchListOnlyForMarketReport(req);
|
|
return sList;
|
|
}
|
|
private string ReplaceWildcard(string input, ClientDingShiReport_ShanXi report)
|
|
{
|
|
if (!string.IsNullOrEmpty(input))
|
|
{
|
|
input = Regex.Replace(input, @"\{\{(.*?)\}\}", m =>
|
|
{
|
|
switch (m.Groups[1].Value)
|
|
{
|
|
case "客户名称": return report.client.Name;
|
|
case "客户编号": return report.client.Number;
|
|
case "支付截止时间":
|
|
if (PS.Config.Company == CompanyEnum.宏源)
|
|
{
|
|
return DateTime.Now.Hour >= 15 ? QdpCalendarHelper.GetNonHoliday(DateTime.Now.AddDays(1)).ToString("yyyy年MM月dd日") + "上午11时" : QdpCalendarHelper.GetNonHoliday(DateTime.Now).ToString("yyyy年MM月dd日") + "下午16:30";
|
|
}
|
|
else
|
|
if (PS.Config.Company == CompanyEnum.浙期)
|
|
{
|
|
return QdpCalendarHelper.GetNonHoliday(DateTime.Now < DateTime.Now.Date.AddHours(11).AddMinutes(30) ? DateTime.Now : DateTime.Now.AddDays(1)).ToString("yyyy年MM月dd日") + "上午11:30";
|
|
}
|
|
else
|
|
{
|
|
return QdpCalendarHelper.GetNonHoliday(DateTime.Now < DateTime.Now.Date.AddHours(9).AddMinutes(30) ? DateTime.Now : DateTime.Now.AddDays(1)).ToString("yyyy年MM月dd日") + "上午9:30";
|
|
}
|
|
case "追保金额": return report.FundReportModel.PositionTradePayableFundString;
|
|
case "大写追保金额": return NumberHelper.CmycurD(report.FundReportModel.PositionTradePayableFund ?? 0);
|
|
case "预付金余额": return report.FundReportModel.MarginBalanceString;
|
|
case "初始保证金金额": return report.FundReportModel.MySideMarginString;
|
|
case "发送日期": return DateTime.Now.ToString("yyyy-MM-dd");
|
|
case "可用资金": return report.FundReportModel.AvailableFundString;
|
|
case "可取资金": return report.FundReportModel.DesirableFundString;
|
|
case "起始日期": return report.ReportFrom.ToString("yyyy-MM-dd");
|
|
case "结束日期": return report.ReportEnd.ToString("yyyy-MM-dd");
|
|
case "开户行":
|
|
if (report.ClientBank.Any())
|
|
{
|
|
var Bank = new StringBuilder();
|
|
report.ClientBank.ForEach(x =>
|
|
{
|
|
Bank.Append(x.Bank).Append(",");
|
|
});
|
|
return Bank.ToString().Substring(0, Bank.ToString().Length - 1);
|
|
}
|
|
return string.Empty;
|
|
case "户名":
|
|
if (report.ClientBank.Any())
|
|
{
|
|
var ClientName = new StringBuilder();
|
|
report.ClientBank.ForEach(x =>
|
|
{
|
|
ClientName.Append(x.ClientName).Append(",");
|
|
});
|
|
return ClientName.ToString().Substring(0, ClientName.ToString().Length - 1);
|
|
}
|
|
return string.Empty;
|
|
case "账号":
|
|
if (report.ClientBank.Any())
|
|
{
|
|
var Card = new StringBuilder();
|
|
report.ClientBank.ForEach(x =>
|
|
{
|
|
Card.Append(x.Card).Append(",");
|
|
});
|
|
return Card.ToString().Substring(0, Card.ToString().Length - 1);
|
|
}
|
|
return string.Empty;
|
|
case "期末结存":
|
|
return report.FundReportModel.TodayRemianFundString;
|
|
default: return string.Empty;
|
|
}
|
|
});
|
|
}
|
|
return input;
|
|
}
|
|
|
|
}
|
|
}
|