feat(swaptrade): 添加收益结算日期验证和限制功能
- 新增 MaxIncomeValueDate 属性用于限制收益结算日期上限 - 实现 parseLocalDate 函数用于解析本地日期格式 - 在前端添加日期选择器的最大日期限制和验证逻辑 - 添加 isAfterMaxIncomeValueDate 和 validateIncomeValueDate 方法进行日期验证 - 在后端添加 GetMaxIncomeValueDate 方法计算最大收益结算日期 - 实现 ValidateIncomeValueDate 对收益结算日期进行服务器端验证 - 添加 NormalizeIncomeUnwindDate 方法统一处理平仓日期 - 在模型中新增 MaxIncomeValueDate 非映射属性用于前端显示 - 更新日期格式化函数确保日期字符串的一致性处理
This commit is contained in:
@@ -130,6 +130,11 @@ namespace YLErp.DBModels
|
||||
/// </summary>
|
||||
public DateTime ValueDate { get; set; }
|
||||
/// <summary>
|
||||
/// 收益结算日期上限
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public DateTime? MaxIncomeValueDate { get; set; }
|
||||
/// <summary>
|
||||
/// 平仓/互换日期
|
||||
/// </summary>
|
||||
public DateTime? UnwindDate { get; set; }
|
||||
|
||||
@@ -113,6 +113,11 @@ namespace YLErp.Modules.SwapModule
|
||||
new TradeUnwindService(this).CloseReCheck_SetTrade(swapTradeId, isSwap, needCheck);
|
||||
}
|
||||
|
||||
protected virtual DateTime GetMaxIncomeValueDate(trade td)
|
||||
{
|
||||
return QdpCalendarHelper.GetNonHolidayDefore(td.ExerciseDate.Value.AddDays(-1));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public SwapDealService(OptUserInfo optUser) : base(optUser)
|
||||
@@ -394,7 +399,8 @@ namespace YLErp.Modules.SwapModule
|
||||
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid);
|
||||
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||||
List<int> eventTypes = new List<int>() { (int)SwapFlowEventTypeEnum.互换, (int)SwapFlowEventTypeEnum.自动互换 };
|
||||
var dealDate = valuedateBLL.ValueDate < td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
|
||||
var maxIncomeValueDate = GetMaxIncomeValueDate(td);
|
||||
var dealDate = valuedateBLL.ValueDate.Date > maxIncomeValueDate.Date ? maxIncomeValueDate : valuedateBLL.ValueDate;
|
||||
// 收益结算不检查收盘限制
|
||||
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
|
||||
td.trade_extend = tradeExtend;
|
||||
@@ -425,7 +431,7 @@ namespace YLErp.Modules.SwapModule
|
||||
unwindData.UnwindDate = dealDate;
|
||||
floatEvent.UnwindDate = unwindData.UnwindDate;
|
||||
floatEvent.EventDate = dealDate;
|
||||
unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules));
|
||||
unwindData.PayDate = valuedateBLL.ValueDate;
|
||||
floatEvent.PayDate = unwindData.PayDate;
|
||||
floatEvent.SwapTradeId = tradeId;
|
||||
floatEvent.SwapTradeNo = td.TradeNumber;
|
||||
@@ -464,6 +470,7 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
unwindData.FlowEvents.Add(floatEvent);
|
||||
}
|
||||
unwindData.MaxIncomeValueDate = maxIncomeValueDate;
|
||||
return unwindData;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -1645,6 +1652,8 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
NormalizeIncomeUnwindDate(unwindData);
|
||||
ValidateIncomeValueDate(unwindData, td);
|
||||
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
|
||||
ValidateFrontendPnL(unwindData, isIncome: true); // 只读校验告警,不阻断交易
|
||||
ExecuteInTransaction(() =>
|
||||
@@ -1683,6 +1692,11 @@ namespace YLErp.Modules.SwapModule
|
||||
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
|
||||
}
|
||||
swapEvent.unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
|
||||
if (eventType == (int)SwapEventTypeEnum.互换)
|
||||
{
|
||||
NormalizeIncomeUnwindDate(swapEvent.unwindData);
|
||||
ValidateIncomeValueDate(swapEvent.unwindData, td);
|
||||
}
|
||||
var flowList = FindFlowEventsByEventId(swapEvent.id);
|
||||
string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费;
|
||||
int clientCashId = AddClientCash(td, Convert.ToDouble(-swapEvent.unwindData.SwapRealizedPnL), action, swapEvent.unwindData.ValueDate);
|
||||
@@ -1726,6 +1740,11 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
if (eventType == (int)SwapEventTypeEnum.互换)
|
||||
{
|
||||
NormalizeIncomeUnwindDate(unwindData);
|
||||
ValidateIncomeValueDate(unwindData, td);
|
||||
}
|
||||
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
|
||||
string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费;
|
||||
ExecuteInTransaction(() =>
|
||||
@@ -1735,6 +1754,20 @@ namespace YLErp.Modules.SwapModule
|
||||
SaveAllChanges();
|
||||
});
|
||||
}
|
||||
private void ValidateIncomeValueDate(UnwindData unwindData, trade td)
|
||||
{
|
||||
var maxIncomeValueDate = GetMaxIncomeValueDate(td).Date;
|
||||
if (unwindData.ValueDate.Date > maxIncomeValueDate)
|
||||
{
|
||||
throw new ServiceException($"手动互换结算日期不能晚于当前交易结束日期T-1:{maxIncomeValueDate:yyyy-MM-dd}");
|
||||
}
|
||||
}
|
||||
|
||||
private void NormalizeIncomeUnwindDate(UnwindData unwindData)
|
||||
{
|
||||
unwindData.UnwindDate = unwindData.ValueDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存平仓/互换事件
|
||||
/// </summary>
|
||||
|
||||
@@ -11,10 +11,17 @@
|
||||
@section JS
|
||||
{
|
||||
<script>
|
||||
function formatDateText(value) {
|
||||
return value ? value.toString().substr(0, 10) : "";
|
||||
}
|
||||
var model = @Json.Serialize(Model);
|
||||
model.StartDate = model.StartDate ? model.StartDate.substr(0, 10) : "";
|
||||
model.ValueDate = model.ValueDate ? model.ValueDate.substr(0, 10) : "";
|
||||
model.PayDate = model.PayDate ? model.PayDate.substr(0, 10) : "";
|
||||
model.StartDate = formatDateText(model.StartDate);
|
||||
model.ValueDate = formatDateText(model.ValueDate);
|
||||
model.PayDate = formatDateText(model.PayDate);
|
||||
model.MaxIncomeValueDate = formatDateText(model.MaxIncomeValueDate);
|
||||
if (model.ValueDate && model.MaxIncomeValueDate && model.ValueDate > model.MaxIncomeValueDate) {
|
||||
model.ValueDate = model.MaxIncomeValueDate;
|
||||
}
|
||||
var isUseApproval = "@isUseApproval"=="True";
|
||||
var g_isShowReCheckClose = "@isShowReCheckClose" == "True";
|
||||
</script>
|
||||
@@ -45,7 +52,7 @@
|
||||
<td>起始日期</td>
|
||||
<td> {{deal.StartDate}}</td>
|
||||
<td>收益结算日期</td>
|
||||
<td> <vue-datepicker :maxdate="maxUnwindDate" :mindate="minStartDate" :holiday="1" v-model="deal.ValueDate" v-on:input="setValueDate" /></td>
|
||||
<td> <vue-datepicker ref="incomeValueDatePicker" :maxdate="maxUnwindDate" :mindate="minStartDate" :holiday="1" v-model="deal.ValueDate" v-on:input="setValueDate" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>支付日期</td>
|
||||
|
||||
@@ -9,6 +9,19 @@ const inputFormatDividend = Object.freeze({ precision: 2, append: '', negative:
|
||||
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
|
||||
const inputFormatMarginRateNoPercent = Object.freeze({ precision: otcformat.trading.umpriceP.precision, append: '', percent: true });
|
||||
let ValueDate = model.ValueDate;
|
||||
let MaxIncomeValueDate = model.MaxIncomeValueDate ? model.MaxIncomeValueDate.substr(0, 10) : ValueDate;
|
||||
|
||||
function parseLocalDate(value) {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
var parts = value.substr(0, 10).split('-');
|
||||
if (parts.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
|
||||
}
|
||||
|
||||
const vue = new Vue({
|
||||
el: '#vueDiv',
|
||||
data: {
|
||||
@@ -22,7 +35,7 @@ const vue = new Vue({
|
||||
},
|
||||
computed: {
|
||||
maxUnwindDate() {
|
||||
return ValueDate;
|
||||
return MaxIncomeValueDate;
|
||||
},
|
||||
minStartDate() {
|
||||
return this.deal.StartDate;
|
||||
@@ -33,7 +46,32 @@ const vue = new Vue({
|
||||
this.initDeal();
|
||||
this.setValueDate();
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
var $incomeValueDatePicker = $(this.$refs.incomeValueDatePicker.$el);
|
||||
$incomeValueDatePicker.datepicker("option", "maxDate", parseLocalDate(MaxIncomeValueDate));
|
||||
if (this.isAfterMaxIncomeValueDate(this.deal.ValueDate)) {
|
||||
this.validateIncomeValueDate(this.deal.ValueDate);
|
||||
} else {
|
||||
$incomeValueDatePicker.val(this.deal.ValueDate);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
isAfterMaxIncomeValueDate(valueDate) {
|
||||
return valueDate && MaxIncomeValueDate && valueDate > MaxIncomeValueDate;
|
||||
},
|
||||
validateIncomeValueDate(valueDate) {
|
||||
if (!this.isAfterMaxIncomeValueDate(valueDate)) {
|
||||
return true;
|
||||
}
|
||||
main.message("手动互换结算日期不能晚于当前交易结束日期T-1:" + MaxIncomeValueDate);
|
||||
this.deal.ValueDate = MaxIncomeValueDate;
|
||||
this.deal.UnwindDate = MaxIncomeValueDate;
|
||||
this.floatPosition.UnwindDate = MaxIncomeValueDate;
|
||||
$(this.$refs.incomeValueDatePicker.$el).val(MaxIncomeValueDate);
|
||||
return false;
|
||||
},
|
||||
getPriceScale() {
|
||||
return this.multiplier == 100 ? 0.01 : 1;
|
||||
},
|
||||
@@ -93,6 +131,9 @@ const vue = new Vue({
|
||||
},
|
||||
setValueDate(e) {//修改平仓日期
|
||||
if (e) {
|
||||
if (!this.validateIncomeValueDate(e)) {
|
||||
e = MaxIncomeValueDate;
|
||||
}
|
||||
this.deal.ValueDate = e;
|
||||
this.deal.UnwindDate = e;
|
||||
this.floatPosition.UnwindDate = e;
|
||||
@@ -212,6 +253,9 @@ const vue = new Vue({
|
||||
main.message("请输入平仓日期");
|
||||
return;
|
||||
}
|
||||
if (!thisObj.validateIncomeValueDate(thisObj.deal.ValueDate)) {
|
||||
return;
|
||||
}
|
||||
let reqObj = _.cloneDeep(thisObj.deal);
|
||||
let marginCloneList = _.cloneDeep(thisObj.marginList);
|
||||
reqObj.FlowEvents = _.cloneDeep(thisObj.interestList);
|
||||
|
||||
Reference in New Issue
Block a user