feat(swap): 更新互换估值功能并优化数据模型
- 添加互换估值V1配置常量以支持新版本 - 将PeriodAmount和DividendAmount字段改为可空decimal类型 - 更新字段注释说明预付金利率和利息的计算方式 - 修改净额结算金额定义,排除期初和追加预付金本金 - 在报表服务中添加安全求和操作防止空值异常 - 增加多个汇总字段包括股息、保证金利息等统计 - 重构预付金处理逻辑,改用加权平均计算方式 - 新增加权保证金利率和利息计算辅助方法 - 调整前端表格列顺序,优化交易编号显示位置 - 更新数值格式化器以支持空值处理和精度控制 - 添加文档列顺序恢复功能确保表格一致性
This commit is contained in:
@@ -28,6 +28,13 @@
|
||||
/// </summary>
|
||||
public string MarginDetail { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每日估值报告页面选择的簿记账户。为空时按客户维度生成全量报告;
|
||||
/// 有值时仅筛选互换估值页的交易所属账户。
|
||||
/// </summary>
|
||||
public int? BookId { get; set; }
|
||||
|
||||
public DateTime From { get; set; }
|
||||
public DateTime To { get; set; }
|
||||
public double PayableMargin { get; set; }
|
||||
|
||||
+74
-5
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -14,6 +15,7 @@ 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;
|
||||
@@ -60,7 +62,16 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
#endregion
|
||||
if (emailData.SendContent.Contains("互换估值"))
|
||||
{
|
||||
var eodReq = new ClientSwapPositionRequest { ClientId = emailData.ClientId, ValueDate = emailData.To, ValueDateFrom = emailData.From,page=1, rows=10000,StructureType= "普通债券类收益互换" };
|
||||
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("互换持仓明细"))
|
||||
@@ -208,7 +219,7 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
Directory.CreateDirectory(targetPath);
|
||||
}
|
||||
var clientName = report.client.Name;
|
||||
var fileName = report.ReportFrom == DateTime.MinValue ? $"证券_估值表_{report.ReportEnd:yyyyMMdd}_{clientName}" : $"证券_估值表_{report.ReportFrom:yyyyMMdd}_{report.ReportEnd:yyyyMMdd}_{clientName}";
|
||||
var fileName = $"{clientName}_每日估值报告_{report.ReportEnd:yyyyMMdd}";
|
||||
var targetFileName = Path.Combine(targetPath, $"{fileName}.xlsx");
|
||||
|
||||
var excelDeclareModel = new ExcelDeclareModel()
|
||||
@@ -238,14 +249,22 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
{
|
||||
modelDict.Add("互换估值", new
|
||||
{
|
||||
// 明细列表供模板渲染;以下 *Sum 字段用于“互换估值”页签合计行。
|
||||
EodSwapPositions = report.EodSwapPositions,
|
||||
// 持仓规模、期间收益和利息/分红类金额合计。
|
||||
PosiNotionalValueSum= report.EodSwapPositions.Sum(x => x.position.PosiNotionalValue),
|
||||
PosiQuantitySum= report.EodSwapPositions.Sum(x => x.position.PosiQuantity),
|
||||
PeriodAmountSum= report.EodSwapPositions.Sum(x => x.PeriodAmount),
|
||||
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)
|
||||
@@ -287,7 +306,10 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
string sourceFileName = Path.Combine(sourcePath, $"结算报告模板.xlsx");
|
||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
var pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName
|
||||
, shouldDeleteSheet: true, needToPdf: false);
|
||||
, shouldDeleteSheet: true, needToPdf: false, callback: sheets =>
|
||||
{
|
||||
FormatSwapValuationDisplayCells(sheets, report.EodSwapPositions);
|
||||
});
|
||||
if (needToPdf)
|
||||
{
|
||||
var targetPdfFileName = FileHelper.ReplaceExtension(targetFileName, ".pdf");
|
||||
@@ -297,6 +319,53 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
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);
|
||||
}
|
||||
|
||||
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 = "@";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 财务状况
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user