问题:GLMS-20260105-0004 在 2/28 部分平仓 40%、3/2 登记日互换后,3/3 继续平仓/互换时 "浮动端平仓盈亏·分红"误显 -36,160(应为 0)。根因:前端 getDivindIn 用期初全额持仓 ×totalInterest 重算分红(-90,400),扣除 consumedDividend(-54,240) 后余 -36,160, 恰为已平仓 40%(登记日前已不持有、不享有该笔分红)部分被重复计入。 方案C:分红收益改由后端读上一收盘日 eod_swap_position.PosiDividendSum 提供(单一可信源), 前端不再自算。EOD 的 PosiDividendSum 已按"实际持仓递推 + 当日实现扣除"准确算出待实现分红, 3/2 互换后为 0。因手动平仓/互换后端不重算 DividendIn、直接落库前端值,后端 Init 阶段给出 正确值可同时修正展示与落库。 改动: - SwapDealService 新增 GetPreEodDividendSum(复用 GetUnwindInterests:624 的上一 EOD 日期推导 + SwapEodPositionService.GetPreEodPositions),protected virtual 便于测试覆写、无 EOD 返回 0 - InitUnwind/InitIncome 的 floatEvent.DividendIn 从硬编码 0 改为 GetPreEodDividendSum 返回值 - unwindSwapTrade.js/incomeSwapTrade.js getDivindIn 删除 GetBondPayMentInterest 调用与全部自算, 保留后端 DividendIn 值、DividendPending=0 - GetBondPayMentInterest 加废弃文档注释(保留接口供历史调用,consumedDividend 无日期过滤隐患随废弃消解) 测试:GLMS20260105PartialCloseDividendBugTest 连真实库验收——3/3 平仓/互换 DividendIn=0(修复前 -36,160)、无 EOD 防御返回 0。全量构建 0 错误。
121 lines
4.7 KiB
C#
121 lines
4.7 KiB
C#
using Org.BouncyCastle.Ocsp;
|
|
using YLErp.DBModels;
|
|
using YLErp.Modules.EodModule;
|
|
|
|
namespace YLErp.Web.Controllers
|
|
{
|
|
public class BondPaymentController : BaseController
|
|
{
|
|
readonly IViewRenderService _viewRenderer;
|
|
|
|
public BondPaymentController(IViewRenderService viewRenderer)
|
|
{
|
|
_viewRenderer = viewRenderer;
|
|
}
|
|
|
|
[MyAuthorize("结算管理-债券付息数据查看")]
|
|
public ActionResult BondPaymentList()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
[HttpPost]
|
|
public JsonResult BondPaymentQuery(BondPaymentReq req)
|
|
{
|
|
var sList = new BondPaymentService(CurUser).SearchList(req);
|
|
return Json(sList);
|
|
}
|
|
|
|
public ActionResult BondPaymentView(string enid)
|
|
{
|
|
var intid = DecryptLong(enid);
|
|
var r = yldb.bondPayment.Find(intid);
|
|
var um = yldb.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == r.underlyingCode);
|
|
r.symbol = um?.UnderlyingName;
|
|
r.security_id = um?.UnderlyingCode;
|
|
return View(r);
|
|
}
|
|
|
|
public ActionResult BondPaymentEdit(string enid)
|
|
{
|
|
if (string.IsNullOrEmpty(enid) || enid == "0")
|
|
{
|
|
return View(new BondPayment());
|
|
}
|
|
var intid = DecryptLong(enid);
|
|
var dbmodel = yldb.bondPayment.Find(intid);
|
|
if (dbmodel == null)
|
|
{
|
|
return ShowError("找不到数据");
|
|
}
|
|
var um = yldb.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == dbmodel.security_id);
|
|
dbmodel.symbol = um?.UnderlyingName;
|
|
return View(dbmodel);
|
|
}
|
|
|
|
public JsonResult BondPaymentEditJson(BondPayment req)
|
|
{
|
|
if (!string.IsNullOrEmpty(req.EncryptId))
|
|
{
|
|
req.id = DecryptLong(req.EncryptId);
|
|
}
|
|
var r = new BondPaymentService(CurUser).SaveBondPayment(req);
|
|
|
|
return JsonSuccess("更新成功", r);
|
|
}
|
|
|
|
[HttpPost]
|
|
public JsonResult DeletBondPayment(string id)
|
|
{
|
|
var intid = DecryptLong(id);
|
|
var r = yldb.bondPayment.Find(intid);
|
|
if (r == null)
|
|
{
|
|
return JsonError("找不到债券期间付息信息");
|
|
}
|
|
yldb.bondPayment.Remove(r);
|
|
yldb.SaveChanges();
|
|
return JsonSuccess("删除成功");
|
|
}
|
|
/// <summary>
|
|
/// 获取某债券期间付息,扣除当天已消费部分
|
|
/// <para>【已废弃·不再用于平仓/互换预览】分红展示改由后端 SwapDealService.GetPreEodDividendSum
|
|
/// 读 EOD PosiDividendSum 提供(方案C,单一可信源)。前端 unwindSwapTrade.js / incomeSwapTrade.js
|
|
/// 的 getDivindIn 不再调用本接口。本接口仅保留供历史调用方,consumedDividend 查询无日期过滤的
|
|
/// 隐患随废弃自然消解,不再单独修复。</para>
|
|
/// </summary>
|
|
/// <param name="startDate"></param>
|
|
/// <param name="endDate"></param>
|
|
/// <param name="underlyingCode"></param>
|
|
/// <param name="tradeId">交易ID(可选,用于扣除已消费分红)</param>
|
|
/// <param name="unwindDate">结算日期(可选)</param>
|
|
/// <returns></returns>
|
|
public JsonResult GetBondPayMentInterest(DateTime startDate, DateTime endDate, string underlyingCode, int? tradeId = null, DateTime? unwindDate = null)
|
|
{
|
|
var payments = new BondPaymentService(CurUser).GetBondPayments(underlyingCode, startDate, endDate);
|
|
decimal totalInterest = payments.Sum(s => s.payment_interest ?? 0) * 0.01m;
|
|
|
|
// 考虑增值税,与 EOD UpdateEodPosition 口径一致
|
|
var um = yldb.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == underlyingCode);
|
|
if (um != null)
|
|
{
|
|
decimal tax = um.ValueAddedTax ?? 0;
|
|
totalInterest = totalInterest / (1 + tax) * (1 - tax);
|
|
}
|
|
|
|
decimal consumedDividend = 0;
|
|
if (tradeId.HasValue)
|
|
{
|
|
List<int> swapEventTypes = new List<int>() { (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换,(int)SwapEventTypeEnum.平仓 };
|
|
consumedDividend = yldb.swap_flow_event
|
|
.Where(x => x.SwapTradeId == tradeId.Value
|
|
&& swapEventTypes.Contains(x.EventType)
|
|
&& x.DataState == (int)SwapFlowDateStateEnum.完成)
|
|
.Sum(s => s.DividendIn);
|
|
}
|
|
|
|
return JsonSuccess("", new { totalInterest, consumedDividend });
|
|
}
|
|
}
|
|
}
|