diff --git a/Framework/YLErp.Core/DBModels/SwapEvent.cs b/Framework/YLErp.Core/DBModels/SwapEvent.cs index ce7f1cc2..eff65bac 100644 --- a/Framework/YLErp.Core/DBModels/SwapEvent.cs +++ b/Framework/YLErp.Core/DBModels/SwapEvent.cs @@ -130,6 +130,11 @@ namespace YLErp.DBModels /// public DateTime ValueDate { get; set; } /// + /// 收益结算日期上限 + /// + [NotMapped] + public DateTime? MaxIncomeValueDate { get; set; } + /// /// 平仓/互换日期 /// public DateTime? UnwindDate { get; set; } diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 8c557d4b..48b18a45 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -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 eventTypes = new List() { (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; } /// @@ -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(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; + } + /// /// 保存平仓/互换事件 /// diff --git a/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml b/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml index 8084253a..578d264e 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml @@ -11,10 +11,17 @@ @section JS { @@ -45,7 +52,7 @@ 起始日期 {{deal.StartDate}} 收益结算日期 - + 支付日期 diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js index 019e302f..09bcf96b 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js @@ -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);