1996 lines
98 KiB
JavaScript
1996 lines
98 KiB
JavaScript
//otcformat禁止千分位分组
|
||
window.otcformat.options.disableGrouping = true;
|
||
// 模块加载横幅(排查用):仅在 ?otcdebug=1 或 localStorage.otcdebug=1 开启时打印,含 bundle 版本+git sha。
|
||
// otcDebug 由 main.js 提供(在 bundle.js 内),本文件是独立 <script> 且晚于 bundle.js 加载,故 otcDebug 必已就绪。
|
||
otcDebug.banner('swapTradeEdit.js', '1.4.2');
|
||
|
||
const consClients = ylotc.clients;
|
||
const consTraders = ylotc.traders;
|
||
const consAssetUnits = page.canAddNewTrader ? ylotc.assetunits
|
||
: ylotc.assetunits.filter(x => x.TraderIds.includes(page.Trade.TraderId));
|
||
|
||
const inputFormatInteger = Object.freeze({ precision: 0, append: '' });
|
||
const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '' });
|
||
const inputFormatSwapRate = swapPricePrecision.getCommonInputFormat('rate', { negative: true, append: '%' });
|
||
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, negative: true, append: '' });
|
||
const inputFormatTradeSinglePrice = swapPricePrecision.getCommonInputFormat('amount', { negative: true, append: '', percent: false });
|
||
const inputFormatTradeSinglePriceFixed2 = swapPricePrecision.getCommonInputFormat('amount', { negative: true, append: '', percent: false });
|
||
const inputFormatPosiFeePercent = Object.freeze({ precision: 4, negative: true, append: '%' });
|
||
const inputFormatPosiFeeUnit = Object.freeze({ precision: 6, negative: true, append: '' });
|
||
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
|
||
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false });
|
||
const consPosiFeeType = Object.freeze({ Percent: 0, Unit: 1 });
|
||
const observationRatePrecision = 12;
|
||
const roundObservationRate = function (value) {
|
||
const rounded = swapPricePrecision.roundDecimal(value, observationRatePrecision);
|
||
return rounded === null || rounded === '' ? value : rounded;
|
||
};
|
||
const observationRateFromPercent = function (value) {
|
||
const rate = roundObservationRate(swapPricePrecision.shiftDecimal(value, -2));
|
||
return rate === null || rate === '' ? value : Number(rate);
|
||
};
|
||
const observationRateTextFromPercent = function (value) {
|
||
return roundObservationRate(swapPricePrecision.shiftDecimal(value, -2));
|
||
};
|
||
const formatObservationRate = function (value) {
|
||
if (value === null || value === undefined || value === '') return '';
|
||
const number = Number(value);
|
||
return Number.isFinite(number) ? number.toFixed(swapPricePrecision.getCommonPrecision('rate')) : value;
|
||
};
|
||
const swapPosiFeeCalc = Object.freeze({
|
||
normalizeFeeType(feeType) {
|
||
return Number(feeType) === consPosiFeeType.Unit ? consPosiFeeType.Unit : consPosiFeeType.Percent;
|
||
},
|
||
calcPending(feeType, feeUnit, stockEqvNotional, quantity) {
|
||
const normalizedFeeType = this.normalizeFeeType(feeType);
|
||
const normalizedFeeUnit = Number(feeUnit) || 0;
|
||
const normalizedNotional = Number(stockEqvNotional) || 0;
|
||
const normalizedQuantity = Number(quantity) || 0;
|
||
const tradingFeePending = normalizedFeeType === consPosiFeeType.Percent
|
||
? normalizedFeeUnit / 100 * normalizedNotional
|
||
: normalizedFeeUnit * normalizedQuantity;
|
||
return otcformat.trading.tradeSinglePrice(tradingFeePending);
|
||
},
|
||
calcFeeUnit(feeType, tradingFeePending, stockEqvNotional, quantity) {
|
||
const normalizedFeeType = this.normalizeFeeType(feeType);
|
||
const normalizedTradingFeePending = Number(tradingFeePending) || 0;
|
||
const normalizedNotional = Number(stockEqvNotional) || 0;
|
||
const normalizedQuantity = Number(quantity) || 0;
|
||
if (normalizedFeeType === consPosiFeeType.Percent) {
|
||
return normalizedNotional === 0 ? 0 : _.round(normalizedTradingFeePending / normalizedNotional * 100, inputFormatPosiFeePercent.precision);
|
||
}
|
||
return normalizedQuantity === 0 ? 0 : _.round(normalizedTradingFeePending / normalizedQuantity, inputFormatPosiFeeUnit.precision);
|
||
}
|
||
});
|
||
|
||
const consUnderlyingFlagBase = (function () {
|
||
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
|
||
return unSelFlag.UseForTrading | unSelFlag.IncludeMatured | unSelFlag.UsePinYinFilter | unSelFlag.IncludeBasket | unSelFlag.IncludeSynthetic | unSelFlag.CheckLaunch;
|
||
}());
|
||
const consUnderlyingFlagNonBond = (function () {
|
||
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
|
||
//临时注释,保证业务不受影响
|
||
//return consUnderlyingFlagBase | unSelFlag.Stock | unSelFlag.StockIndex | unSelFlag.StockIF | unSelFlag.Fund | unSelFlag.CommodityFutures | unSelFlag.CommoditySpot;
|
||
return consUnderlyingFlagBase;
|
||
}());
|
||
const consUnderlyingFlagBond = (function () {
|
||
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
|
||
const consUnderlyingFlagNonBond = (function () {
|
||
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
|
||
//临时注释,保证业务不受影响
|
||
//return consUnderlyingFlagBase | unSelFlag.Stock | unSelFlag.StockIndex | unSelFlag.StockIF | unSelFlag.Fund | unSelFlag.CommodityFutures | unSelFlag.CommoditySpot;
|
||
return consUnderlyingFlagBase;
|
||
}());
|
||
}());
|
||
|
||
const inputFormatDouble2 = Object.freeze({ precision: 2, append: '' });
|
||
var autoMarginTemplateName;
|
||
|
||
//定价格式化(来自配置)
|
||
const InputFormatPercent = Object.freeze({ precision: 2, append: '%' });
|
||
|
||
const consMetaDic = {
|
||
'组合标的': JSON.parse(page.Trade.MetaDic['组合标的'] || '{}'),
|
||
'组合标的2': JSON.parse(page.Trade.MetaDic['组合标的2'] || '{}')
|
||
};
|
||
|
||
//定价格式化(来自配置)
|
||
const pricingFormat = otcformat.trading;
|
||
|
||
//数字格式化
|
||
const consNumberFormat = Object.freeze(new function () {
|
||
this.umpriceP = pricingFormat.umpriceP;
|
||
this.umprice = pricingFormat.umprice;
|
||
this.premiumRateP = pricingFormat.premiumRateP;
|
||
return this;
|
||
}());
|
||
|
||
const createVueUnderlying = function (selectFlag) {
|
||
return {
|
||
props: ['value', 'index'],
|
||
data() {
|
||
return { autoUnderlying: null };
|
||
},
|
||
mounted() {
|
||
var selFlag = tradeHelper.UnderlyingSelectFlag;
|
||
this.autoUnderlying = tradeHelper.UnderlyingAutoComplete(this.$el, { SelectFlag: selFlag.UsePinYinFilter |
|
||
selFlag.UseForTrading |
|
||
selFlag.IncludeSynthetic |
|
||
selFlag.IncludeMatured |
|
||
selFlag.IncludeBasket |
|
||
selFlag.CheckLaunch, BlackLimit: 2, });
|
||
this.autoUnderlying.onSelect(this.onchange);
|
||
this.value && this.autoUnderlying.selectByCode(this.value);
|
||
},
|
||
methods: {
|
||
onchange(data) {
|
||
this.$emit('change', data);
|
||
this.$emit('input', data.Code);
|
||
}
|
||
},
|
||
destroyed() {
|
||
this.autoUnderlying && this.autoUnderlying.dispose();
|
||
},
|
||
template: '<input type="text" v-model="value" />'
|
||
};
|
||
};
|
||
const vueUnderlyingNonBond = function () {
|
||
return createVueUnderlying(consUnderlyingFlagNonBond);
|
||
};
|
||
const vueUnderlyingBond = function () {
|
||
return createVueUnderlying(consUnderlyingFlagBond);
|
||
};
|
||
|
||
//标的选择组件 银行间回购定盘&其他利率
|
||
const vueUnderlyingRate = function () {
|
||
return {
|
||
props: ['value', 'index'],
|
||
data() {
|
||
return { autoUnderlying: null };
|
||
},
|
||
mounted() {
|
||
var selFlag = tradeHelper.UnderlyingSelectFlag;
|
||
var instrumentTypes = ["FixingRepoRate", "OtherRate"];
|
||
this.autoUnderlying = tradeHelper.UnderlyingAutoComplete(this.$el,
|
||
{ SelectFlag: selFlag.UsePinYinFilter |
|
||
selFlag.UseForTrading |
|
||
selFlag.IncludeSynthetic |
|
||
selFlag.IncludeMatured |
|
||
selFlag.IncludeBasket |
|
||
selFlag.CheckLaunch, BlackLimit: 2, InstrumentTypes: instrumentTypes, UseAll: true, UseSignle: true });
|
||
this.autoUnderlying.onSelect(this.onchange);
|
||
this.value && this.autoUnderlying.selectByCode(this.value);
|
||
},
|
||
methods: {
|
||
onchange(data) {
|
||
data.index = this.index;
|
||
if (this.value !== data.Code) {
|
||
this.$emit('change', data);
|
||
this.$emit('input', data.Code);
|
||
}
|
||
}
|
||
},
|
||
destroyed() {
|
||
this.autoUnderlying && this.autoUnderlying.dispose();
|
||
},
|
||
template: '<input type="text" v-model="value" />'
|
||
};
|
||
};
|
||
|
||
|
||
//ESC
|
||
$(document).keyup(function (event) {
|
||
if (event.keyCode === 27) {
|
||
window.parent.layer.closeAll();
|
||
}
|
||
});
|
||
const vue = new Vue({
|
||
el: '#tradeEdit',
|
||
data: {
|
||
page: page,
|
||
trade: _.cloneDeep(page.Trade),
|
||
viewState: {
|
||
variety: tradeHelper.getEmptyVariety(),
|
||
underlying: tradeHelper.getEmptyUnderlying(),
|
||
synthetic: null,
|
||
Extend: { TradingPlace: "柜台市场", ClearingAgency: "交易员方" }
|
||
},
|
||
currencys: page.currencys,
|
||
getNotionalSingleFee: 0,
|
||
isSingleFee: page.Trade.trade_extend.ExtendObj.OpenFeeType == 0,
|
||
posiFeeModePercent: true,
|
||
observation: {//互换观察日
|
||
ObservationInterval: "",
|
||
IntervalList: [],
|
||
ObservationNum: 1,
|
||
ObservationUnit: 'D',
|
||
ObservationHolidayType: 'Following',
|
||
ObservationAlignEnd: true,
|
||
ObservationCalendar: 'Chn',
|
||
ObservationSettlementRules: 0,
|
||
DefaultTitle1Value: 0.1,
|
||
ObservationDataList: [],
|
||
CheckedAll: false,
|
||
index: 0,
|
||
IsDeductPrincipal: true,
|
||
ObservationStart: page.Trade.TradeDate
|
||
},
|
||
observationHolidayTypes: [
|
||
{ text: "向后调整", value: "Following" },
|
||
{ text: "向前调整", value: "Previous" },
|
||
{ text: "不调整", value: "None" }
|
||
],
|
||
alignEndTypes: [
|
||
{ text: "向到期日对齐", value: true },
|
||
{ text: "向开始日对齐", value: false }
|
||
],
|
||
marginSwapList: [],
|
||
getSwapList: [],//利息端
|
||
paySwapList: [],//浮动端,
|
||
observationType: 0,
|
||
client: null,//客户信息
|
||
StockEqvNotional: 0,
|
||
underlying: {
|
||
UnderlyingCode: '',
|
||
UnderlyingName: '',
|
||
UnderlyingIssuer: '',
|
||
IssueSize: '',
|
||
MaturityDate: '',
|
||
Price: '',
|
||
UnderlyingInstrumentType: '',
|
||
QuoteUnitString: ''
|
||
}
|
||
},
|
||
watch: {
|
||
},
|
||
created: function () {
|
||
if (this.trade.MetaDic['组合标的']) {
|
||
this.viewState.synthetic = JSON.parse(this.trade.MetaDic['组合标的']);
|
||
}
|
||
if (this.trade.MetaDic["交易场所"]) {
|
||
this.viewState.Extend.TradingPlace = this.trade.MetaDic["交易场所"];
|
||
}
|
||
if (this.trade.MetaDic["清算机构"]) {
|
||
this.viewState.Extend.ClearingAgency = this.trade.MetaDic["清算机构"];
|
||
}
|
||
},
|
||
mounted() {
|
||
__init(this);
|
||
$("#MainProtocolCode").val(this.trade.MetaDic["主协议编号"]);
|
||
$("#SupProtocolCode").val(this.trade.MetaDic["补充协议编号"]);
|
||
this.initSwapRateList();
|
||
// 初始化到期日期选择器下限
|
||
this.$nextTick(() => {
|
||
if (this.$refs.exerciseDatePicker) {
|
||
var minDate = this.trade.StartDate > this.trade.TradeDate ? this.trade.StartDate : this.trade.TradeDate;
|
||
this.$refs.exerciseDatePicker.refresh(minDate, null);
|
||
}
|
||
});
|
||
},
|
||
methods: {
|
||
getQuantityInputFormat(item) {
|
||
return swapPricePrecision.getCommonInputFormat(
|
||
'quantity',
|
||
{ append: '' },
|
||
item && item.UnderlyingInstrumentType);
|
||
},
|
||
getPosiPriceInputFormat(item, field) {
|
||
return swapPricePrecision.getInputFormat(
|
||
item && item.UnderlyingInstrumentType,
|
||
field,
|
||
Object.assign({}, inputFormatSwapDeliveryPrice, {
|
||
percent: this.trade.StructureType !== '普通收益互换'
|
||
}));
|
||
},
|
||
roundStoragePrice(item, price, field) {
|
||
const storagePrecisionOffset = tradeHelper.IsBond(item && item.UnderlyingInstrumentType) ? 2 : 0;
|
||
return swapPricePrecision.roundForSubmit(
|
||
price,
|
||
item && item.UnderlyingInstrumentType,
|
||
field,
|
||
storagePrecisionOffset);
|
||
},
|
||
getPosiPriceFormatKey(item, field) {
|
||
const index = item && item.index != null ? item.index : '';
|
||
const instrumentType = item && item.UnderlyingInstrumentType ? item.UnderlyingInstrumentType : 'unknown';
|
||
return `${index}-${field}-${instrumentType}`;
|
||
},
|
||
getCurrentPosiFeeType() {
|
||
return this.posiFeeModePercent ? consPosiFeeType.Percent : consPosiFeeType.Unit;
|
||
},
|
||
normalizePosiFeeType(feeType) {
|
||
return swapPosiFeeCalc.normalizeFeeType(feeType);
|
||
},
|
||
syncPosiFeeModeByItem(item) {
|
||
this.posiFeeModePercent = this.normalizePosiFeeType(item && item.PosiFeeType) !== consPosiFeeType.Unit;
|
||
},
|
||
syncPayItemFeeType(item) {
|
||
item.PosiFeeType = this.getCurrentPosiFeeType();
|
||
},
|
||
refreshTradingFeePendingByUnit(item) {
|
||
this.syncPayItemFeeType(item);
|
||
item.PosiTradingFeePending = swapPosiFeeCalc.calcPending(
|
||
item.PosiFeeType,
|
||
item.PosiTradingFeeUnit,
|
||
this.trade.StockEqvNotional,
|
||
item.PosiQuantity
|
||
);
|
||
},
|
||
refreshTradingFeeUnitByPending(item) {
|
||
this.syncPayItemFeeType(item);
|
||
item.PosiTradingFeeUnit = swapPosiFeeCalc.calcFeeUnit(
|
||
item.PosiFeeType,
|
||
item.PosiTradingFeePending,
|
||
this.trade.StockEqvNotional,
|
||
item.PosiQuantity
|
||
);
|
||
},
|
||
refreshPayTradingFeesByUnit() {
|
||
this.paySwapList.forEach(item => {
|
||
this.refreshTradingFeePendingByUnit(item);
|
||
});
|
||
},
|
||
changeStructureType() {
|
||
this.trade.StockEqvNotional = 0;
|
||
let direction = this.trade.trade_extend.ExtendObj.Direction;
|
||
if (page.swapFloatingIncomeReceiveOnlyMode) {
|
||
direction = 1;
|
||
this.trade.trade_extend.ExtendObj.Direction = direction;
|
||
} else if (direction !== 1 && direction !== 2) {
|
||
direction = 2;
|
||
this.trade.trade_extend.ExtendObj.Direction = direction;
|
||
}
|
||
this.paySwapList = [];
|
||
this.addSwapFloat(direction);
|
||
//this.trade.trade_extend.ExtendObj.FlowBookMode = 0;
|
||
},
|
||
//变更初始预付金收取方向
|
||
changeInitialMargin() {
|
||
if (this.trade.trade_Initial_Margin.Direction == 1) {
|
||
this.trade.trade_Initial_Margin.Direction = 2;
|
||
} else {
|
||
this.trade.trade_Initial_Margin.Direction = 1;
|
||
}
|
||
|
||
},
|
||
//变更交易场所
|
||
changeTradingPlace() {
|
||
this.trade.MetaDic["交易场所"] = this.viewState.Extend.TradingPlace;
|
||
},
|
||
//变更清算机构
|
||
changeClearingAgency() {
|
||
this.trade.MetaDic["清算机构"] = this.viewState.Extend.ClearingAgency;
|
||
},
|
||
//交互约定1+2:在净价/全价/收益率任一输入框【敲回车】→ 以该字段为源调计算器反算另两个。
|
||
// 成功:源字段标"源",另两个标"AUTO"(SwapCalc.applyBondCalcSuccess);
|
||
// 失败:保留源字段值、另两个清空、三字段标识全清(SwapCalc.applyBondCalcFailure)。
|
||
// 注意:计算触发只在回车(由 vue-number-input 的 enter 事件驱动),失焦不触发(见 onBondPriceEdit 约定3)。
|
||
onBondPriceEnter(item, type) { // type: 'CP'净价 / 'DP'全价 / 'YD'收益率
|
||
if (!item.isBond) return;
|
||
if (!item.UnderlyingCode) { main.message("请先选择债券标的"); return; }
|
||
var hasVal = function (v) { return v !== null && v !== '' && !isNaN(v); };
|
||
var price = type === 'CP' ? item.PosiNetNoFeePrice
|
||
: type === 'DP' ? item.PosiGrossPrice
|
||
: item.InitYtm;
|
||
if (!hasVal(price)) return; // 空/非数:不触发计算器
|
||
this.calcBondForItem(item, type);
|
||
},
|
||
//交互约定3:编辑某字段(失焦/输入)但未回车 → 不联动另两字段,清掉 源/AUTO 标识,
|
||
// 仅本字段标"REV"(人工输入),由 SwapCalc.applyBondManualEdit 落地。
|
||
// 由 vue-number-input 的 input 事件(失焦/回车都会触发)驱动;回车时 enter 事件会随后把标识修正为 源/AUTO。
|
||
onBondPriceEdit(item, type) {
|
||
if (!item.isBond) return;
|
||
SwapCalc.applyBondManualEdit(item, type);
|
||
this.syncBondFlags(item);
|
||
otcDebug.log('[onBondPriceEdit] type=' + type + ' after: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev));
|
||
},
|
||
//交互约定3(按键实时触发):vue-number-input 的 input 事件只在失焦/回车时触发,
|
||
// 按键输入过程中不触发,导致用户输入时 源/AUTO 标识不会消失。
|
||
// 此方法绑在 v-on:keydown 上(组件内部 addEventListener + $emit),按键即清 源/AUTO 标识、标本字段 REV。
|
||
// 回车键(13/108)除外——回车由 v-on:enter 驱动 calcBondForItem,会把标识修正为 源/AUTO。
|
||
// 修饰键(Shift/Ctrl/Alt/Tab/方向键/F键等)除外——这些不改变数值,不应清标识。
|
||
onBondPriceKeydown(item, type, event) {
|
||
if (!item.isBond) return;
|
||
var kc = event.which || event.keyCode;
|
||
// 仅"会改变数值"的按键才清 源/AUTO 标识并标本字段 REV;
|
||
// 回车/修饰键/导航键/功能键/纯 Shift 不改标识(见 SwapCalc.isBondPriceValueKey)。
|
||
// 修复:此前守卫漏掉 ↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)/F键/Shift(16),
|
||
// 导致"鼠标放进去没改值、只按了方向键/功能键"也会误标 REV。
|
||
if (!SwapCalc.isBondPriceValueKey(event)) return;
|
||
// 调试埋点(otcDebug 仅在 ?otcdebug=1 / localStorage.otcdebug=1 时输出,平时零噪音):
|
||
// 抓"逐键前后 driver/rev 变化",用于定位 REV 标记异常。
|
||
otcDebug.log('[onBondPriceKeydown] type=' + type + ' kc=' + kc +
|
||
' before: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev));
|
||
SwapCalc.applyBondManualEdit(item, type);
|
||
this.syncBondFlags(item);
|
||
otcDebug.log('[onBondPriceKeydown] after: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev));
|
||
},
|
||
//确保 bondDriverType/bondAuto/bondRev 的变更触发 Vue 响应式更新。
|
||
//swapCalc.js 的纯函数直接对 state 赋值,但这些属性不在后端数据中(非响应式),
|
||
//直接赋值不触发视图更新。$set 设置相同值也不会触发更新,所以每次都创建新对象引用。
|
||
syncBondFlags(item) {
|
||
// 用全新对象引用强制触发 Vue 2 响应式更新(旧引用相等时 Vue 会跳过更新)
|
||
this.$set(item, 'bondDriverType', item.bondDriverType === undefined ? null : item.bondDriverType);
|
||
this.$set(item, 'bondAuto', { CP: !!item.bondAuto.CP, DP: !!item.bondAuto.DP, YD: !!item.bondAuto.YD });
|
||
this.$set(item, 'bondRev', { CP: !!item.bondRev.CP, DP: !!item.bondRev.DP, YD: !!item.bondRev.YD });
|
||
// $forceUpdate 确保视图刷新(兜底:即使 $set 因相同值未触发也能更新)
|
||
this.$forceUpdate();
|
||
},
|
||
//全价列:普通标的重算名义本金;债券标的按交互约定3 标 REV(不联动)。
|
||
// 回车计算由独立的 v-on:enter="onBondPriceEnter(item,'DP')" 负责。
|
||
onDpPriceInput(item) {
|
||
this.changeSpotPrice(item); // 名义本金随全价变化(非债券标的的既有逻辑)
|
||
if (item.isBond) {
|
||
SwapCalc.applyBondManualEdit(item, 'DP'); // 交互约定3:失焦未回车→REV
|
||
this.syncBondFlags(item);
|
||
}
|
||
},
|
||
//放弃手动标记、以当前某字段为源重新自动反算("重算"按钮)。
|
||
resetBondCalc(item) {
|
||
if (!item.isBond) return;
|
||
// 优先沿用用户最后一次回车锁定的源(bondDriverType),避免"净价残留脏值"时误以净价为源反算出连锁错误;
|
||
// 无历史源时再按 净价→全价→收益率 回退挑一个有值的字段。
|
||
var hasVal = function (v) { return v !== null && v !== '' && !isNaN(v); };
|
||
var driver = item.bondDriverType
|
||
|| (hasVal(item.PosiNetNoFeePrice) ? 'CP'
|
||
: hasVal(item.PosiGrossPrice) ? 'DP'
|
||
: hasVal(item.InitYtm) ? 'YD' : null);
|
||
if (!driver) return;
|
||
this.calcBondForItem(item, driver);
|
||
},
|
||
//以 driver(回车字段) 为源调 /Bond/CalcBond;回写时跳过源字段(保留用户手输值)、覆盖另两个。
|
||
//错误处理(对齐 bond-oms-ui / zszq-bond-oms):
|
||
// - 业务/参数错误(success=false 或 errCode!=0,如债券不存在/信息不全/参数非法)
|
||
// 以非阻塞 toast(main.message) 反馈真实原因,绝不回写、不弹模态框、不阻止手工输入;
|
||
// - 网络/服务异常:框架 main.post 已弹"请求失败",此处不再二次提示。
|
||
// ⚠️【部署 / 踩坑必读】本文件 swapTradeEdit.js 是【独立 <script> 引用】,并不在 bundle.js 里拼接!
|
||
// 页面用 ?v=JsVersion 缓存键引用(JsVersion = bin/YLErp* DLL 最大 mtime)。
|
||
// 因此:改了本文件后必须 dotnet publish(重编 DLL 才会刷新 JsVersion 缓存键),
|
||
// 否则浏览器仍读旧缓存 —— 表现为"本地/某次提交明明改了却没生效"。
|
||
// ⚠️【接线 / 踩坑必读】计算器成败路由见 base/main.js __post 注释:业务错误走 reject。
|
||
// 本函数曾把"约定2补充(算不出→清另两字段+清标识)"只写在 .done 内,
|
||
// 而 .done 在 reject 时不触发 → 那段是死代码、长期静默失效;现已改到 .fail(见下)。
|
||
calcBondForItem(item, explicitDriver) {
|
||
var driver = explicitDriver || item.bondDriverType;
|
||
if (!item.isBond || !driver) return;
|
||
item.bondDriverType = driver; // 先定源,回写时 applyBondCalcResult 会跳过该字段
|
||
if (!item.UnderlyingCode) { main.message("请先选择债券标的"); return; }
|
||
var price = driver === 'CP' ? item.PosiNetNoFeePrice
|
||
: driver === 'DP' ? item.PosiGrossPrice
|
||
: item.InitYtm;
|
||
if (price === null || price === '' || isNaN(price)) {
|
||
// 交互约定2补充:源字段为空/非数也算"算不出来",清另两字段+所有标识
|
||
SwapCalc.applyBondCalcFailure(item, driver);
|
||
self.syncBondFlags(item);
|
||
return;
|
||
}
|
||
var priceType = driver === 'CP' ? 'CP' : driver === 'DP' ? 'DP' : 'YD';
|
||
var self = this;
|
||
// 估值日:债券互换的"期初"净价/全价/收益率应以【互换起始日 StartDate】估值。
|
||
// 该字段在页面上本就是可选择的日期控件(vue-datepicker),用户可改;现已默认即有。
|
||
// 不再"悄悄回退到交易日(TradeDate)"——真的为空时直接报错提示,由用户填写/手动填三数(需求2)。
|
||
var targetDate = (this.trade && this.trade.StartDate) ? this.trade.StartDate : null;
|
||
// 调试埋点(otcDebug 仅在 ?otcdebug=1 时输出):抓"回车→调计算器→源/结果"全链路,定位三字段互算异常。
|
||
otcDebug.log('[calcBondForItem] enter driver=' + driver + ' priceType=' + priceType +
|
||
' price(sent)=' + SwapCalc.bondPriceToCalc(price) + ' targetDate=' + targetDate);
|
||
var sdMsg = SwapCalc.getBondStartDateMissingMsg(targetDate);
|
||
if (sdMsg) {
|
||
// D3 同款去重:开始日缺失时用户在价格格逐键输入会连刷相同提示,故同一文案只弹一次
|
||
if (SwapCalc.shouldShowBondErr(item, sdMsg)) main.message(sdMsg);
|
||
// 交互约定2补充:开始日缺失也算"算不出来",清另两字段+所有标识
|
||
SwapCalc.applyBondCalcFailure(item, driver);
|
||
self.syncBondFlags(item);
|
||
return; // 估值日缺失:不调用计算器、不覆盖任何手工输入
|
||
}
|
||
main.post('/Bond/CalcBond', {
|
||
underlyingCode: item.UnderlyingCode,
|
||
// 模型 item.PosiGrossPrice/PosiNetNoFeePrice/InitYtm 存的是【存储态小数】(如 0.995),
|
||
// 而 bond-calc 要的是【展示态/每百元百分比】(如 99.5)。这俩靠 BondPriceConverter.ToDisplay(×100)/ToStorage(÷100) 对齐。
|
||
// 此处传给计算器前必须 bondPriceToCalc(存储→展示);回写时再 bondCalcPriceToStorage(展示→存储),
|
||
// 否则计算器拿到 0.995≈1 当 1% of par 直接发散成离谱值。
|
||
price: SwapCalc.bondPriceToCalc(price),
|
||
priceType: priceType,
|
||
targetDate: targetDate
|
||
}, { alertFn: main.message }).done(function (resp) { // ⚠️ .done 仅在 success(=resolve) 时触发;业务失败/网络异常请走下方 .fail,此处写失败处理是死代码
|
||
if (!resp || !resp.obj) {
|
||
// 网络错误/响应异常:框架已弹"请求失败",按交互约定2补充清另两字段+标识
|
||
SwapCalc.applyBondCalcFailure(item, driver);
|
||
self.syncBondFlags(item);
|
||
return;
|
||
}
|
||
// 业务层错误(债券不存在/信息不全/参数非法):仅提示,绝不清空手工输入
|
||
var err = SwapCalc.getBondCalcErrorMessage(resp.obj);
|
||
if (err) {
|
||
// D3 修复:不可算债券上用户逐键手填时,每次按键都触发一次失败计算并弹 toast,会连刷相同提示;
|
||
// 同一错误文案连续出现只弹一次(shouldShowBondErr 维护 item._lastBondErr),抑制噪声、不影响任何计算逻辑。
|
||
if (SwapCalc.shouldShowBondErr(item, err)) main.message(err);
|
||
SwapCalc.applyBondCalcFailure(item, driver); // 交互约定2:失败→清另两字段+标识
|
||
self.syncBondFlags(item);
|
||
otcDebug.log('[calcBondForItem] calc-error driver=' + driver + ' msg=' + err);
|
||
return;
|
||
}
|
||
item._lastBondErr = null; // 成功则清标记,便于下次真出不同错误时仍能提示
|
||
// 以既有三字段为代理,调用纯函数;手工输入字段保留原始十进制字符串,避免经 Number 参与计算后丢失末位。
|
||
// 关键:proxy 内必须统一为【展示态】(per-100-face),因为 applyBondCalcResult 写入的是计算器返回的展示态。
|
||
// 模型字段是【存储态小数】(percent:true 下 1.00 对应界面 100),所以初始化时要 bondPriceToCalc(×100);
|
||
// 若直接用存储态初始化,则源字段被 applyBondCalcResult 跳过后,proxy 中仍残留存储态,
|
||
// 后续再 ÷100 就会导致该字段被二次缩小(如输入 100 变成 1)。
|
||
var proxy = {
|
||
cleanPrice: SwapCalc.bondPriceToCalc(item.PosiNetNoFeePrice),
|
||
dirtyPrice: SwapCalc.bondPriceToCalc(item.PosiGrossPrice),
|
||
ytm: SwapCalc.bondPriceToCalc(item.InitYtm)
|
||
};
|
||
SwapCalc.applyBondCalcResult(proxy, resp.obj, driver);
|
||
// 回写前 bondCalcPriceToStorage(÷100)(展示态→存储态小数):proxy 里均为展示态;
|
||
// 模型字段存存储态(0.995),须 ÷100 落回模型,否则配合 percent:true 显示会 ×100 成离谱值。
|
||
item.PosiNetNoFeePrice = self.roundStoragePrice(item, SwapCalc.bondCalcPriceToStorage(proxy.cleanPrice), 'netPrice');
|
||
item.PosiGrossPrice = self.roundStoragePrice(item, SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice), 'grossPrice');
|
||
item.InitYtm = self.roundStoragePrice(item, SwapCalc.bondCalcPriceToStorage(proxy.ytm), 'yield');
|
||
SwapCalc.applyBondCalcSuccess(item, driver); // 交互约定2:成功→标 源/AUTO
|
||
self.syncBondFlags(item);
|
||
otcDebug.log('[calcBondForItem] success driver=' + driver + ' auto=' + JSON.stringify(item.bondAuto) + ' rev=' + JSON.stringify(item.bondRev));
|
||
// 名义本金依赖全价(PosiGrossPrice):以净价/收益率为源反算出的全价被回写后,
|
||
// 直接赋值不会触发组件 input 事件,需在此显式重算,保持名义本金与全价一致。
|
||
if (self.calcNotional) self.calcNotional();
|
||
}).fail(function (resp) {
|
||
// ⚠️【踩坑记录 / 约定2补充 为何一度不生效】main.post(__post) 在 resp.success===false
|
||
// (业务错误=计算器算不出来)或网络异常时调用 deferred.reject(),Promise 进入 rejected 态;
|
||
// 而 .done() 只在 resolved 态触发 —— 历史上把 applyBondCalcFailure(清另两字段+清标识)
|
||
// 写在 .done 内的「业务错误分支」里,业务失败时那段根本不执行,表现就是
|
||
// "约定2补充静默失效、auto/源标识纹丝不动"。正确做法就是像现在这样统一在 .fail 里落地。
|
||
// 交互约定2补充:计算器算不出来 → 保留源字段值、另两字段清空为空白、三个数值旁标识(源/AUTO/REV)全清。
|
||
// 错误文案已由 __post 的 alertFn(main.message) 非阻塞提示,此处不重复弹,仅清数值+清标识
|
||
// (与 .done 内 !resp.obj 分支行为一致;左侧 .done 的 getBondCalcErrorMessage 分支为兼容冗余,主路径在此)。
|
||
SwapCalc.applyBondCalcFailure(item, driver);
|
||
self.syncBondFlags(item);
|
||
otcDebug.log('[calcBondForItem] post-fail/reject driver=' + driver);
|
||
});
|
||
},
|
||
//变更收费基本单位
|
||
changeOpenFeeType() {
|
||
var thisObj = this;
|
||
thisObj.paySwapList.forEach(item => {
|
||
thisObj.changeTradingFeeUnit(item);
|
||
})
|
||
},
|
||
//变更数量
|
||
changeQuantity(item) {
|
||
this.calcNotional();
|
||
},
|
||
//变更标的单价
|
||
changeSpotPrice(item) {
|
||
this.calcNotional();
|
||
},
|
||
//变更标的合约乘数
|
||
changeContractSize(item) {
|
||
this.calcNotional();
|
||
},
|
||
//变更名义本金(仅格式化,不反算数量)
|
||
changeStockEqvNotional() {
|
||
this.trade.StockEqvNotional = swapPricePrecision.normalizeCommon('amount', this.trade.StockEqvNotional);
|
||
this.refreshPayTradingFeesByUnit();
|
||
//计算数量
|
||
// if (this.paySwapList.length > 0) {
|
||
// var item = this.paySwapList[0];
|
||
// var notional = deliveryPrice * item.ContractSize;
|
||
// item.PosiQuantity = notional == 0 ? 0 : _.round(this.trade.StockEqvNotional / notional, page.otcFormatConfig.StockEqvNotional.precision);
|
||
// this.calcNotional();
|
||
// }
|
||
|
||
},
|
||
//变更初始预付金 为¥
|
||
showAbsPrice() {
|
||
this.trade.trade_Initial_Margin.MarginType = 1;
|
||
this.trade.trade_Initial_Margin.MarginValue = this.trade.trade_Initial_Margin.MarginValue * this.trade.StockEqvNotional;
|
||
},
|
||
//变更单位交易费用 为%
|
||
showPercentPrice() {
|
||
this.trade.trade_Initial_Margin.MarginType = 0;
|
||
this.trade.trade_Initial_Margin.MarginValue = this.trade.StockEqvNotional == 0 ? 0 : this.trade.trade_Initial_Margin.MarginValue / this.trade.StockEqvNotional;
|
||
|
||
},
|
||
//成交日期变更时自动修正联动日期
|
||
onTradeDateChange() {
|
||
if (this.trade.StartDate < this.trade.TradeDate) {
|
||
// 开始日期 = 成交日期 + 1天,跳过节假日
|
||
var d = new Date(this.trade.TradeDate);
|
||
d.setDate(d.getDate() + 1);
|
||
while (ylotc.isHoliday(d)) {
|
||
d.setDate(d.getDate() + 1);
|
||
}
|
||
this.trade.StartDate = this.formatDate(d);
|
||
}
|
||
// 到期日期必须 ≥ 成交日期 且 ≥ 开始日期
|
||
if (this.trade.ExerciseDate < this.trade.TradeDate || this.trade.ExerciseDate < this.trade.StartDate) {
|
||
// 到期日期 = 开始日期 + 14天,跳过节假日
|
||
var sd = new Date(this.trade.StartDate);
|
||
sd.setDate(sd.getDate() + 14);
|
||
while (ylotc.isHoliday(sd)) {
|
||
sd.setDate(sd.getDate() + 1);
|
||
}
|
||
this.trade.ExerciseDate = this.formatDate(sd);
|
||
}
|
||
this.changeTradeDate();
|
||
},
|
||
//开始日期变更时检查到期日期
|
||
onStartDateChange() {
|
||
if (this.trade.ExerciseDate < this.trade.StartDate) {
|
||
var sd = new Date(this.trade.StartDate);
|
||
sd.setDate(sd.getDate() + 14);
|
||
while (ylotc.isHoliday(sd)) {
|
||
sd.setDate(sd.getDate() + 1);
|
||
}
|
||
this.trade.ExerciseDate = this.formatDate(sd);
|
||
}
|
||
this.changeTradeDate();
|
||
},
|
||
//变更交易日期
|
||
changeTradeDate(force) {
|
||
this.changeObservationStart();
|
||
if (this.Obervation) {
|
||
this.Obervation.ObservationStart = this.trade.StartDate;
|
||
}
|
||
this.calcNotional();
|
||
this.refreshDatepicker();
|
||
this.changeMarginDate();
|
||
// 动态更新开始日期和到期日期的下限
|
||
this.$nextTick(() => {
|
||
if (this.$refs.startDatePicker) {
|
||
this.$refs.startDatePicker.refresh(this.trade.TradeDate, null);
|
||
}
|
||
if (this.$refs.exerciseDatePicker) {
|
||
var minExerciseDate = this.trade.StartDate > this.trade.TradeDate ? this.trade.StartDate : this.trade.TradeDate;
|
||
this.$refs.exerciseDatePicker.refresh(minExerciseDate, null);
|
||
}
|
||
});
|
||
//this.initMarginRate();
|
||
},
|
||
calcNotional(calcPrice) {
|
||
if (this.paySwapList.length > 0) {
|
||
var payItem = this.paySwapList[0];
|
||
if (calcPrice) {
|
||
this.getSpotPrice(payItem.UnderlyingCode, this.trade.StartDate, payItem);
|
||
}
|
||
// 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional
|
||
var deliveryPrice = this.roundStoragePrice(payItem, payItem.PosiGrossPrice, 'grossPrice');
|
||
var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, payItem.PosiQuantity, payItem.ContractSize);//名义本金=期初价格*数量*乘数
|
||
this.trade.StockEqvNotional = swapPricePrecision.normalizeCommon('amount', stockEqvNotional);
|
||
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
|
||
this.refreshPayTradingFeesByUnit();
|
||
}
|
||
},
|
||
//变更到期日
|
||
changeExerciseDate() {
|
||
this.refreshDatepicker();
|
||
this.changeMarginDate();
|
||
},
|
||
//变更预付金生效日期
|
||
changeMarginDate() {
|
||
var thisObj = this;
|
||
thisObj.observation.ObservationStart = thisObj.trade.StartDate;
|
||
var startDate = thisObj.trade.StartDate;
|
||
var endDate = thisObj.trade.ExerciseDate;
|
||
if (thisObj.marginSwapList) {
|
||
thisObj.marginSwapList.forEach((item, index) => {
|
||
if (item.HappenDate < startDate) {
|
||
thisObj.marginSwapList[index].HappenDate = startDate;
|
||
thisObj.changeStartMarinRateDate(thisObj.marginSwapList[index]);
|
||
}
|
||
else if (item.HappenDate > endDate) {
|
||
thisObj.marginSwapList[index].HappenDate = endDate;
|
||
thisObj.changeStartMarinRateDate(thisObj.marginSwapList[index]);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
// 变更预付金结算规则起息日期
|
||
changeStartMarinRateDate(item) {
|
||
if (item.Obervation) {
|
||
item.Obervation.ObservationStart = item.HappenDate;
|
||
}
|
||
|
||
},
|
||
//变更单位交易费用
|
||
changeTradingFeeUnit(item) {
|
||
this.refreshTradingFeePendingByUnit(item);
|
||
},
|
||
//变更交易费用
|
||
changeTradingFee(item) {
|
||
this.refreshTradingFeeUnitByPending(item);
|
||
},
|
||
showPayAbsPrice() {
|
||
this.posiFeeModePercent = false;
|
||
this.paySwapList.forEach(item => {
|
||
item.PosiFeeType = consPosiFeeType.Unit;
|
||
item.PosiTradingFeeUnit = 0;
|
||
item.PosiTradingFeePending = 0;
|
||
});
|
||
},
|
||
showPayPercentPrice() {
|
||
this.posiFeeModePercent = true;
|
||
this.paySwapList.forEach(item => {
|
||
item.PosiFeeType = consPosiFeeType.Percent;
|
||
item.PosiTradingFeeUnit = 0;
|
||
item.PosiTradingFeePending = 0;
|
||
});
|
||
},
|
||
savetrade() {
|
||
if (!this.checkSubmitData()) {
|
||
return false;
|
||
}
|
||
this.savetrade_inner();
|
||
},
|
||
savetrade_inner() {
|
||
var thisObj = this;
|
||
//this.paySwapList.forEach(x => {
|
||
// x.SwapIntervals = this.trade.ExerciseDate + ";" + x.InterestRate;
|
||
//});
|
||
this.trade.IsUsePremiumRate = true;
|
||
this.trade.IsTradePricePayType = true;
|
||
this.trade.UnderlyingInstrumentType = "Stock";
|
||
this.trade.swap_positions = [];
|
||
var date = thisObj.trade.ExerciseDate;
|
||
var errorcount = 0;
|
||
if (this.trade.trade_extend.ExtendObj.InterestCalcMode == "00" || this.trade.trade_extend.ExtendObj.InterestCalcMode == "10") {
|
||
date = moment(date).add(-1, 'days').format('YYYY-MM-DD');
|
||
}
|
||
const serializeSwapIntervals = function (position) {
|
||
position.SwapIntervalList.forEach(interval => {
|
||
const rate = roundObservationRate(interval.Rate);
|
||
if (rate !== null && rate !== '') {
|
||
interval.Rate = Number(rate);
|
||
}
|
||
});
|
||
position.InterestSwapInterval = JSON.stringify(position.SwapIntervalList);
|
||
};
|
||
this.getSwapList.forEach((x,index) => {
|
||
if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.SwapIntervalList.length == 0) {
|
||
var interval = {
|
||
Date: date,
|
||
Rate: x.InterestRateDefault,
|
||
Settlement: 0
|
||
}
|
||
x.SwapIntervalList.push(interval);
|
||
}
|
||
if (x.interest_rest_days != null && x.interest_rest_days <= 0) {
|
||
main.message("利息端第" + (index + 1) + "行重置频率必须大于0");
|
||
errorcount++;
|
||
return false;
|
||
}
|
||
x.FloatRateUnderlyingCode = x.FloatRateUnderlyingCode == "--" ? null : x.FloatRateUnderlyingCode;
|
||
serializeSwapIntervals(x);
|
||
thisObj.trade.swap_positions.push(x);
|
||
});
|
||
if (errorcount > 0) {
|
||
return;
|
||
}
|
||
var margin = 0;
|
||
this.marginSwapList.forEach((x, index) => {
|
||
if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.SwapIntervalList.length == 0) {
|
||
var interval = {
|
||
Date: date,
|
||
Rate: x.InterestRateDefault,
|
||
Settlement: 0
|
||
}
|
||
x.SwapIntervalList.push(interval);
|
||
}
|
||
if (!x.HappenDate) {
|
||
main.message("预付金/预付金第" + (index + 1) + "行生效日期不能为空");
|
||
errorcount++;
|
||
return false;
|
||
}
|
||
x.FloatRateUnderlyingCode = x.FloatRateUnderlyingCode == "--" ? null : x.FloatRateUnderlyingCode;
|
||
margin = margin + x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1);
|
||
serializeSwapIntervals(x);
|
||
thisObj.trade.swap_positions.push(x);
|
||
});
|
||
if (errorcount > 0) {
|
||
return;
|
||
}
|
||
thisObj.trade.trade_Initial_Margin.MarginType = 1;
|
||
thisObj.trade.trade_Initial_Margin.MarginValue = margin;
|
||
thisObj.trade.trade_Initial_Margin.Direction = margin > 0 ? 1 : 2;
|
||
if (this.trade.StructureType != "多空组合") {
|
||
this.paySwapList.forEach((x, index) => {
|
||
if (!x.UnderlyingCode) {
|
||
main.message("浮动端第" + (index + 1) + "行标的不能为空");
|
||
errorcount++;
|
||
return false;
|
||
}
|
||
if (!x.PosiQuantity > 0) {
|
||
main.message("浮动端第" + (index + 1) + "数量不能为0");
|
||
errorcount++;
|
||
return false;
|
||
}
|
||
|
||
if (!x.PosiGrossPrice) {
|
||
main.message("浮动端第" + (index + 1) + "期初价格不能为空");
|
||
errorcount++;
|
||
return false;
|
||
}
|
||
x.PosiGrossPrice = thisObj.roundStoragePrice(x, x.PosiGrossPrice, 'grossPrice');
|
||
x.PosiNetNoFeePrice = thisObj.roundStoragePrice(x, x.PosiNetNoFeePrice, 'netPrice');
|
||
x.InitYtm = x.InitYtm == null ? null : thisObj.roundStoragePrice(x, x.InitYtm, 'yield');
|
||
x.PosiFeeType = thisObj.normalizePosiFeeType(x.PosiFeeType);
|
||
thisObj.trade.swap_positions.push(x);
|
||
});
|
||
} else {
|
||
this.trade.trade_extend.ExtendObj.Direction = this.paySwapList[0].PosiDirection;
|
||
}
|
||
if (errorcount > 0) {
|
||
return;
|
||
}
|
||
this.trade.MetaDic = {
|
||
'组合标的': consMetaDic["组合标的"] ? JSON.stringify(consMetaDic["组合标的"]) : '',
|
||
'组合标的2': consMetaDic["组合标的2"] ? JSON.stringify(consMetaDic["组合标的2"]) : '',
|
||
'交易场所': this.viewState.Extend.TradingPlace,
|
||
'清算机构': this.viewState.Extend.ClearingAgency,
|
||
"主协议编号": $("#MainProtocolCode").val(),
|
||
"补充协议编号": $("#SupProtocolCode").val()
|
||
};
|
||
this.trade.trade_extend.ExtendJson = JSON.stringify(this.trade.trade_extend.ExtendObj);
|
||
main.post("/swaptrade2/tradeEditJson", this.trade).done(function (resp) {
|
||
window.location.href = "/swaptrade2/tradeview?enid=" + resp.obj.EncryptId;
|
||
if (window.parent && window.parent.reloadtrade) {
|
||
window.parent.reloadtrade();
|
||
}
|
||
});
|
||
},
|
||
changeMainProtocolCode() {
|
||
this.getSupProtocolCode();
|
||
},
|
||
getMainProtocolCode() {
|
||
var thisObj = this;
|
||
$("#MainProtocolCode option").remove();
|
||
main.post("/Client/getMainProtocolCodes", { clientId: $("#ClientId").val() }, { async: false }).done(function (resp) {
|
||
var obj = $("#MainProtocolCode");
|
||
if (thisObj.client.MainProtocolCode != null && thisObj.client.MainProtocolCode != '') {
|
||
obj.append("<option >" + thisObj.client.MainProtocolCode + "</option>");
|
||
}
|
||
_.forEach(resp, (v) => {
|
||
obj.append("<option >" + v.Text + "</option>");
|
||
})
|
||
});
|
||
this.getSupProtocolCode()
|
||
},
|
||
getSupProtocolCode() {
|
||
$("#SupProtocolCode option").remove();
|
||
if ($("#MainProtocolCode").val()) {
|
||
main.post("/Client/getSideProtocols", { mainProtocol: $("#MainProtocolCode").val() }, { async: false }).done(function (resp) {
|
||
var obj = $("#SupProtocolCode");
|
||
if (thisObj.client.SupProtocolCode != null && thisObj.client.SupProtocolCode != '') {
|
||
obj.append("<option >" + thisObj.client.SupProtocolCode + "</option>");
|
||
}
|
||
_.forEach(resp, (v) => {
|
||
obj.append("<option >" + v + "</option>");
|
||
})
|
||
});
|
||
}
|
||
},
|
||
changeInterestMode(item) {
|
||
},
|
||
changeInterestType(item) {
|
||
if (!item.interest_rest_days) {
|
||
item.interest_rest_days = 7;
|
||
}
|
||
},
|
||
setUnderlyingCode(data, item) {
|
||
if (!data || !data.Code) return;
|
||
var underlyingCode = item.UnderlyingCode;
|
||
if (underlyingCode != data.Code) {
|
||
// 簿记基础逻辑1:新增互换交易选择标的后,不自动填充价格等数值。
|
||
// 切换标的时清空净价/全价/收益率三字段,由用户手填或回车调计算器反算。
|
||
// 历史上此处调 getSpotPrice 自动回填,会导致用户未确认的价格被静默填入。
|
||
item.PosiNetNoFeePrice = null;
|
||
item.PosiGrossPrice = null;
|
||
item.InitYtm = null;
|
||
}
|
||
item.CountRatio = data.CountRatio;
|
||
item.ContractSize = data.ContractSize;
|
||
item.UnderlyingInstrumentType = data.InstrumentType;
|
||
item.UnderlyingCode = data.Code;
|
||
item.underlying = { ...this.underlying };
|
||
item.underlying.UnderlyingCode = data.Code;
|
||
item.underlying.UnderlyingName = data.Name;
|
||
item.underlying.UnderlyingIssuer = data.UnderlyingIssuer;
|
||
item.underlying.IssueSize = data.IssueSize;
|
||
item.underlying.MaturityDate = data.MaturityDate;
|
||
item.underlying.Price = data.Price;
|
||
item.underlying.UnderlyingInstrumentType = data.InstrumentType;
|
||
item.underlying.QuoteUnitString = data.QuoteUnitString;
|
||
this.trade.UnderlyingCode = data.Code;
|
||
// 债券标的:标记该浮动腿可启用净价/全价/收益率互算
|
||
this.$set(item, 'isBond', tradeHelper.IsBond(data.InstrumentType));
|
||
// 切换标的时清空债券三字段互算的手动/源标志(D1 修复:避免旧债券手填状态污染新债券)
|
||
SwapCalc.clearBondCalcFlags(item);
|
||
this.syncBondFlags(item);
|
||
// 簿记基础逻辑1:不再自动调 getSpotPrice 回填价格,由用户手填或回车调计算器
|
||
// 续发(renewal)场景的初始价恢复由 refreshInitialUnderlyingPrices 独立处理
|
||
//this.initMarginRate();
|
||
},
|
||
setFloatRateUnderlyingCode(data, item) {
|
||
item.FloatRateUnderlyingCode = data.Code;
|
||
if (data.Code.length > 0 && data.Code != '--') {
|
||
item.IsAnnualized = true;
|
||
item.interest_rest_days = 7;
|
||
item.interest_rule = data.Code === 'FR007' ? -1 : 0;
|
||
} else {
|
||
item.interest_rule = null;
|
||
}
|
||
//this.getFloatRate(item);
|
||
},
|
||
getFloatRate(item) {
|
||
var thisObj = this;
|
||
if (item.FloatRateUnderlyingCode == null || item.FloatRateUnderlyingCode == '' || item.FloatRateUnderlyingCode == '--' || thisObj.paySwapList.length == 0) {
|
||
item.InterestRateDefault = 0;
|
||
blurSwapRate(item);
|
||
return;
|
||
}
|
||
var floatPositionType = thisObj.paySwapList[0].PositionType;
|
||
main.post("/SwapFloatRate/MatchRate", { clientId: thisObj.trade.ClientId, underlyingCode: item.FloatRateUnderlyingCode, startDate: thisObj.trade.StartDate, endDate: thisObj.trade.ExerciseDate })
|
||
.done(function (resp) {
|
||
if (resp.success) {
|
||
if (resp.obj != null) {
|
||
item.InterestRateDefault = floatPositionType == 1 ? resp.obj.LongPricePoint : resp.obj.ShortPricePoint;
|
||
item.InterestRateDefault = item.InterestRateDefault * 0.0001;
|
||
} else {
|
||
item.InterestRateDefault = 0;
|
||
}
|
||
thisObj.blurSwapRate(item);
|
||
}
|
||
});
|
||
},
|
||
getSpotPrice(underlyingCode, StartDate, item) {
|
||
var thisObj = this;
|
||
if (!underlyingCode) return;
|
||
main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: underlyingCode, tradeDate: StartDate })
|
||
.done(function (resp) {
|
||
if (!resp || !resp.success || !resp.obj) {
|
||
main.message("未获取到标的价格");
|
||
return;
|
||
}
|
||
// 行情接口(AjaxGetUnderlyingPrice)对债券返回的 price/netPrice 本就是【存储态小数】(1.0 代表 100 元),
|
||
// 与 PosiGrossPrice/PosiNetNoFeePrice 模型字段同量纲(见 PricingController: EodPrice 直接返回、
|
||
// 非 EodPrice 分支 ×bondPriceMultiple=0.01)。故此处仅做精度格式化,**不可**再 bondCalcPriceToStorage(÷100),
|
||
// 否则默认价 1.0 被除成 0.01,界面 percent:true 再 ×100 显示为 1("被自动除以100"bug)。
|
||
// 计算器(/Bond/CalcBond)返回的才是展示态,其 ÷100 落库逻辑在 calcBondForItem 内处理。
|
||
item.PosiNetNoFeePrice = thisObj.roundStoragePrice(item, resp.obj.netPrice, 'netPrice');
|
||
item.PosiGrossPrice = thisObj.roundStoragePrice(item, resp.obj.price, 'grossPrice');
|
||
thisObj.calcNotional();
|
||
});
|
||
},
|
||
//年化利率变更同步修改 同行观察日利率
|
||
blurSwapRate(item) {
|
||
//if (item.InterestRateDefault != 0) {
|
||
// item.IsAnnualized = true;
|
||
//}
|
||
if (item.SwapIntervalList.length > 0) {
|
||
var swapRate = item.InterestRateDefault;
|
||
var same = true;
|
||
var rate1 = item.SwapIntervalList[0].Rate;
|
||
item.SwapIntervalList.forEach(function (item) {
|
||
if (item.Rate != rate1) {
|
||
same = false;
|
||
}
|
||
});
|
||
if (same) {
|
||
var confirmMsg = "检测到互换利率发生变化,将自动覆盖观察列表";
|
||
main.confirm(confirmMsg, function () {
|
||
item.SwapIntervalList.forEach(function (element, index, arr) {
|
||
arr[index].Rate = swapRate;
|
||
});
|
||
var observationDates = JSON.stringify(item.SwapIntervalList);
|
||
item.InterestSwapInterval = observationDates;
|
||
|
||
});
|
||
} else {
|
||
main.message("检测到互换利率与互换利率列表不一致,请手动重新生成互换列表");
|
||
}
|
||
}
|
||
|
||
},
|
||
changeObservationStart() {
|
||
var thisObj = this;
|
||
thisObj.getSwapList.forEach((item, index) => {
|
||
if (item.Obervation) {
|
||
thisObj.getSwapList[index].Obervation.ObservationStart = thisObj.trade.TradeDate;
|
||
}
|
||
});
|
||
},
|
||
//设置观察日弹框
|
||
formatObservationRateInput(item) {
|
||
item.val = formatObservationRate(item.val);
|
||
},
|
||
initObservationDates(item, type) {
|
||
this.observationType = type;
|
||
var thisObj = this;
|
||
var observation = item.Obervation;
|
||
thisObj.observation.index = item.index;
|
||
thisObj.observation.IntervalList = item.SwapIntervalList;
|
||
thisObj.observation.DefaultTitle1Value = item.InterestRateDefault;
|
||
thisObj.observation.ObservationNum = observation ? observation.ObservationNum : 1;
|
||
thisObj.observation.ObservationUnit = observation ? observation.ObservationUnit : 'D';
|
||
thisObj.observation.ObservationHolidayType = observation ? observation.ObservationHolidayType : 'Following';
|
||
thisObj.observation.ObservationAlignEnd = observation ? observation.ObservationAlignEnd : true;
|
||
if (type == 1) {
|
||
thisObj.observation.ObservationCalendar = observation ? observation.ObservationCalendar : 'Chn';
|
||
thisObj.observation.ObservationSettlementRules = observation ? observation.ObservationSettlementRules : 0;
|
||
}
|
||
thisObj.observation.IsDeductPrincipal = observation ? observation.IsDeductPrincipal : true;
|
||
if (type == 1) {
|
||
thisObj.observation.IsDeductPrincipal = false;
|
||
}
|
||
var startTime = thisObj.trade.TradeDate;
|
||
var endTime = thisObj.trade.ExerciseDate;
|
||
if (main.isEmpty(startTime)) {
|
||
main.message("请输入开始日期");
|
||
return false;
|
||
}
|
||
if (main.isEmpty(endTime)) {
|
||
main.message("请输入到期日期");
|
||
return false;
|
||
}
|
||
if (thisObj.observation.ObservationStart < startTime) {
|
||
thisObj.observation.ObservationStart = startTime;
|
||
}
|
||
if (thisObj.observation.ObservationStart > endTime) {
|
||
thisObj.observation.ObservationStart = endTime;
|
||
}
|
||
thisObj.observation.ObservationDataList = [];
|
||
var floatRateCode = type == 1 ? (item.FloatRateUnderlyingCode || '--') : '';
|
||
thisObj.observation.IntervalList.forEach((value, num, arr) => {
|
||
var val = value.Rate;
|
||
var _date = "";
|
||
var m = new moment(value.Date);
|
||
if (!isNaN(m.date())) {
|
||
_date = m.format("YYYY-MM-DD");
|
||
}
|
||
var itemChecked = thisObj.observation.IntervalList.length >= num ? (value.Settlement == 1 ? true : false) : false;
|
||
var disabled = false;
|
||
if (_date == thisObj.trade.ExerciseDate) {
|
||
disabled = true;
|
||
itemChecked = false;
|
||
}
|
||
var obdate = {};
|
||
if (type == 1) {
|
||
var settlementDate = value.Date;
|
||
if (value.SettlementDate!=null) {
|
||
settlementDate = value.SettlementDate;
|
||
}
|
||
var m = new moment(settlementDate);
|
||
obdate = {
|
||
date: _date,
|
||
SettlementDate: m.format("YYYY-MM-DD"),
|
||
floatRateCode: floatRateCode,
|
||
val: formatObservationRate(_.toString(val) ? swapPricePrecision.shiftDecimal(val, 2) : ""),
|
||
itemChecked: itemChecked,
|
||
disabled: disabled
|
||
};
|
||
} else {
|
||
obdate = {
|
||
date: _date,
|
||
val: formatObservationRate(_.toString(val) ? swapPricePrecision.shiftDecimal(val, 2) : ""),
|
||
itemChecked: itemChecked,
|
||
disabled: disabled
|
||
};
|
||
}
|
||
thisObj.observation.ObservationDataList.push(obdate);
|
||
});
|
||
var area = type == 1 ? ['800px', '600px'] : ['750px', '600px'];
|
||
layer.open({
|
||
type: 1,
|
||
area: area,
|
||
title: "设置互换日期",
|
||
shadeClose: false,
|
||
shade: 0.4,
|
||
content: $("#observationInfosEdit")
|
||
});
|
||
},
|
||
//计算结算日期
|
||
calcSettleDate(observationDate, settlementRules) {
|
||
if (!observationDate) return "";
|
||
var m = new moment(observationDate);
|
||
var settleDate = m.add(settlementRules, 'days').format("YYYY-MM-DD");
|
||
return settleDate;
|
||
},
|
||
//生成观察日操作
|
||
GetObservationDates() {
|
||
var thisObj = this;
|
||
var observationNum = thisObj.observation.ObservationNum;
|
||
var observationUnit = thisObj.observation.ObservationUnit;
|
||
var observationHolidayType = thisObj.observation.ObservationHolidayType;
|
||
var alignEnd = thisObj.observation.ObservationAlignEnd;
|
||
var settlementRules = thisObj.observation.ObservationSettlementRules;
|
||
var calendar = thisObj.observation.ObservationCalendar;
|
||
var floatRateCode = this.observationType == 1 ? (thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--') : '';
|
||
|
||
thisObj.observation.ObservationDataList = [];
|
||
|
||
if (this.observationType == 1) {
|
||
var postData = {
|
||
startDate: thisObj.observation.ObservationStart,
|
||
endDate: thisObj.trade.ExerciseDate,
|
||
termStr: observationNum + observationUnit,
|
||
holidayAdjustment: observationHolidayType,
|
||
alignEnd: alignEnd,
|
||
calcMode: thisObj.trade.trade_swap.RateCalcMode,
|
||
calendar: calendar,
|
||
settlementRules: settlementRules
|
||
};
|
||
main.post("/trade/GetSwapObservationDateList", postData).done(
|
||
function (res) {
|
||
$.each(res.obj, function (i) {
|
||
var _date = "";
|
||
var m = new moment(this.ObservationDate);
|
||
if (!isNaN(m.date())) {
|
||
_date = m.format("YYYY-MM-DD");
|
||
}
|
||
var _settleDate = "";
|
||
var sm = new moment(this.SettleDate);
|
||
if (!isNaN(sm.date())) {
|
||
_settleDate = sm.format("YYYY-MM-DD");
|
||
}
|
||
var val = swapPricePrecision.shiftDecimal(thisObj.observation.DefaultTitle1Value, 2);
|
||
var itemChecked = true;
|
||
var disabled = false;
|
||
if (_date == thisObj.trade.ExerciseDate) {
|
||
disabled = true;
|
||
itemChecked = false;
|
||
}
|
||
var obdate = {
|
||
date: _date,
|
||
SettlementDate: _settleDate,
|
||
floatRateCode: floatRateCode,
|
||
val: formatObservationRate(val),
|
||
itemChecked: itemChecked,
|
||
disabled: disabled
|
||
};
|
||
thisObj.observation.ObservationDataList.push(obdate);
|
||
});
|
||
thisObj.initObservationCheckedAll();
|
||
});
|
||
} else {
|
||
var postData = {
|
||
startDate: thisObj.observation.ObservationStart,
|
||
endDate: thisObj.trade.ExerciseDate,
|
||
termStr: observationNum + observationUnit,
|
||
holidayAdjustment: observationHolidayType,
|
||
alignEnd: alignEnd,
|
||
calcMode: thisObj.trade.trade_swap.RateCalcMode
|
||
};
|
||
main.post("/trade/GetObservationDateList", postData).done(
|
||
function (res) {
|
||
$.each(res.obj, function (i) {
|
||
var _date = "";
|
||
var m = new moment(this);
|
||
if (!isNaN(m.date())) {
|
||
_date = m.format("YYYY-MM-DD");
|
||
}
|
||
var val = swapPricePrecision.shiftDecimal(thisObj.observation.DefaultTitle1Value, 2);
|
||
var itemChecked = true;
|
||
var disabled = false;
|
||
if (_date == thisObj.trade.ExerciseDate) {
|
||
disabled = true;
|
||
itemChecked = false;
|
||
}
|
||
var obdate = {
|
||
date: _date,
|
||
val: formatObservationRate(val),
|
||
itemChecked: itemChecked,
|
||
disabled: disabled
|
||
};
|
||
thisObj.observation.ObservationDataList.push(obdate);
|
||
});
|
||
thisObj.initObservationCheckedAll();
|
||
});
|
||
}
|
||
},
|
||
//编辑观察日功能数据处理
|
||
SetObservationDates() {
|
||
var observationStr = "";
|
||
if (this.observation.ObservationDataList != null) {
|
||
this.observation.ObservationDataList.forEach(item => {
|
||
observationStr = observationStr + item.date + ", " + item.SettlementDate + ", " + observationRateTextFromPercent(item.val) + ", " + item.itemChecked + ";\n";
|
||
});
|
||
}
|
||
this.observation.ObservationInterval = observationStr;
|
||
$("#myModal").modal("show");
|
||
},
|
||
// 粘贴功能
|
||
pasteMe(e, rowData, index) {
|
||
let copyData = '';
|
||
var thisObj = this;
|
||
if (e.clipboardData || e.originalEvent) {
|
||
var clipboardData = (e.clipboardData || e.originalEvent.clipboardData);
|
||
copyData = clipboardData.getData('Text');
|
||
var arr = copyData.replace(/\t/g, ', ').replace(/\r/g, '').split('\n');
|
||
arr.forEach((v, i) => {
|
||
if (v && !v.trim().endsWith(';')) {
|
||
arr[i] = v + ";";
|
||
}
|
||
});
|
||
thisObj.observation.ObservationInterval = arr.join('\r\n');
|
||
e.preventDefault();
|
||
}
|
||
},
|
||
//编辑观察日模态框保存操作
|
||
saveObservationModal() {
|
||
this.initObservationDataList();
|
||
$("#myModal").modal("hide");
|
||
},
|
||
//编辑观察日模态框保存 数据转换
|
||
initObservationDataList() {
|
||
var thisObj = this;
|
||
thisObj.observation.ObservationInterval = thisObj.observation.ObservationInterval.trim().replaceAll("\n", "").replaceAll(" ", "");
|
||
var observationDates = thisObj.observation.ObservationInterval;
|
||
var items = observationDates.split(";").filter(o => o);
|
||
thisObj.observation.ObservationDataList = [];
|
||
var floatRateCode = thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--';
|
||
items.forEach(function (item) {
|
||
if (item) {
|
||
var values = item.split(",");
|
||
var itemChecked = JSON.parse(values[3].trim());
|
||
var disabled = false;
|
||
if (values[0] == thisObj.trade.ExerciseDate) {
|
||
disabled = true;
|
||
itemChecked = false;
|
||
}
|
||
var val = values[2].trim();
|
||
var obdate = {
|
||
date: values[0],
|
||
SettlementDate: values[1] || "",
|
||
floatRateCode: floatRateCode,
|
||
val: formatObservationRate(_.toString(val) ? swapPricePrecision.shiftDecimal(val, 2) : ""),
|
||
itemChecked: itemChecked,
|
||
disabled: disabled
|
||
}
|
||
thisObj.observation.ObservationDataList.push(obdate);
|
||
}
|
||
});
|
||
thisObj.initObservationCheckedAll();
|
||
},
|
||
//观察日列表初始化是否全选
|
||
initObservationCheckedAll() {
|
||
var thisObj = this;
|
||
var noCheckedItems = thisObj.observation.ObservationDataList.filter(item => {
|
||
if ((item.itemChecked == false) && item.date != thisObj.trade.ExerciseDate) {
|
||
return item;
|
||
}
|
||
});
|
||
this.observation.CheckedAll = noCheckedItems.length == 0 && thisObj.observation.ObservationDataList.length > 0;
|
||
},
|
||
//删除单行观察日
|
||
deleteObItem(item) {
|
||
var thisObj = this;
|
||
var curindex = 0;
|
||
for (var i = 0; i < thisObj.observation.ObservationDataList.length; i++) {
|
||
if (thisObj.observation.ObservationDataList[i].date == item.date) {
|
||
curindex = i;
|
||
break;
|
||
}
|
||
}
|
||
thisObj.observation.ObservationDataList.splice(curindex, 1);
|
||
},
|
||
//添加观察日
|
||
addnewitem() {
|
||
var thisObj = this;
|
||
var obdate = {};
|
||
if (this.observationType == 1) {
|
||
var floatRateCode = thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--';
|
||
obdate = {
|
||
date: '',
|
||
SettlementDate: '',
|
||
floatRateCode: floatRateCode,
|
||
val: formatObservationRate(0),
|
||
itemChecked: true,
|
||
disabled: false
|
||
};
|
||
} else {
|
||
obdate = {
|
||
date: '',
|
||
val: formatObservationRate(0),
|
||
itemChecked: true,
|
||
disabled: false
|
||
};
|
||
}
|
||
thisObj.observation.ObservationDataList.push(obdate);
|
||
},
|
||
//编辑观察日模态框关闭
|
||
closeObservationModal() {
|
||
$('#myModal').modal('hide');
|
||
},
|
||
//关闭互换layer层
|
||
closeParentWindow() {
|
||
layer.closeAll();
|
||
},
|
||
//观察日列表确认
|
||
ObservationConfirm() {
|
||
var thisObj = this;
|
||
var hasTimeError = false;
|
||
var hasNumberError = false;
|
||
var observationArr = [];
|
||
thisObj.observation.ObservationDataList.forEach(item => {
|
||
var m = new moment(item.date);
|
||
if (isNaN(m.date())) {
|
||
hasTimeError = true;
|
||
}
|
||
var dateFormat = /^(-?\d+)(\.\d+)?$/;
|
||
if (!dateFormat.test(item.val)) {
|
||
hasNumberError = true;
|
||
}
|
||
var observation = {
|
||
Date: item.date,
|
||
Rate: observationRateFromPercent(item.val),
|
||
Settlement: item.itemChecked ? 1 : 0,
|
||
SettlementDate: item.SettlementDate || null // 结算日期
|
||
}
|
||
observationArr.push(observation);
|
||
|
||
});
|
||
if (hasTimeError) {
|
||
alert("日期格式有误,请输入正确格式");
|
||
return false;
|
||
}
|
||
if (hasNumberError) {
|
||
alert("请输入正确的数字格式");
|
||
return false;
|
||
}
|
||
if (this.observationType == 1) {
|
||
thisObj.getSwapList.forEach((val, num, arr) => {
|
||
if (val.index == thisObj.observation.index) {
|
||
arr[num].InterestSwapInterval = JSON.stringify(observationArr);
|
||
thisObj.observation.ObservationInterval = arr[num].InterestSwapInterval;
|
||
arr[num].SwapIntervalList = observationArr;
|
||
arr[num].Obervation = JSON.parse(JSON.stringify(thisObj.observation));
|
||
}
|
||
});
|
||
} else {
|
||
thisObj.marginSwapList.forEach((val, num, arr) => {
|
||
if (val.index == thisObj.observation.index) {
|
||
arr[num].InterestSwapInterval = JSON.stringify(observationArr);
|
||
thisObj.observation.ObservationInterval = arr[num].InterestSwapInterval;
|
||
arr[num].SwapIntervalList = observationArr;
|
||
arr[num].Obervation = JSON.parse(JSON.stringify(thisObj.observation));
|
||
}
|
||
});
|
||
}
|
||
this.closeParentWindow();
|
||
},
|
||
//互换提交数据有效性检查
|
||
checkSubmitData() {
|
||
//验证输入的日期格式是否正确(包括交易日期、开始日、到期日)
|
||
var ExerciseDate = this.trade.ExerciseDate;
|
||
var regTime = /^[0-9]{4}-[0-1]?[0-9]{1}-[0-3]?[0-9]{1}$/;
|
||
if (main.isEmpty(ExerciseDate)) {
|
||
main.message("请输入到期日期");
|
||
return false;
|
||
}
|
||
if (!regTime.test(ExerciseDate)) {
|
||
main.message("请输入正确的到期日期格式");
|
||
return false;
|
||
}
|
||
|
||
var TradeDate = this.trade.TradeDate;
|
||
if (main.isEmpty(TradeDate)) {
|
||
main.message("请输入成交日期");
|
||
return false;
|
||
}
|
||
if (!regTime.test(TradeDate)) {
|
||
main.message("请输入正确的成交日期格式");
|
||
return false;
|
||
}
|
||
|
||
if (TradeDate > ExerciseDate) {
|
||
main.message("到期日期不能早于成交日期");
|
||
return false;
|
||
}
|
||
var StartDate = this.trade.StartDate;
|
||
if (main.isEmpty(StartDate)) {
|
||
main.message("请输入开始日期");
|
||
return false;
|
||
}
|
||
if (!regTime.test(StartDate)) {
|
||
main.message("请输入正确的开始日期格式");
|
||
return false;
|
||
}
|
||
if (StartDate < TradeDate) {
|
||
main.message("开始日期不能早于成交日期");
|
||
return false;
|
||
}
|
||
if (StartDate > ExerciseDate) {
|
||
main.message("到期日期不能早于开始日期");
|
||
return false;
|
||
}
|
||
if (!this.trade.AssetId) {
|
||
main.message("请设置簿记账户");
|
||
return false;
|
||
}
|
||
|
||
if (!this.trade.ClientId) {
|
||
main.message("请设置交易对手方");
|
||
return false;
|
||
}
|
||
|
||
if (this.trade.StructureType!="多空组合"&&(!this.trade.StockEqvNotional > 0 || this.trade.StockEqvNotional == null)) {
|
||
main.message("请设置名义本金");
|
||
return false;
|
||
}
|
||
|
||
if (!this.trade.trade_extend.ExtendObj.AnnualDays > 0) {
|
||
main.message("请设置年化天数");
|
||
return false;
|
||
}
|
||
if (this.trade.StructureType == "多空组合"&&!this.checkSwapRateList()) {
|
||
return false;
|
||
}
|
||
if (!this.checkObservationDates()) {
|
||
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
//观察日校验
|
||
checkObservationDates() {
|
||
var checkGet = true;
|
||
var thisObj = this;
|
||
if (thisObj.getSwapList != null) {
|
||
thisObj.getSwapList.forEach((val, num, arr) => {
|
||
if (checkGet && val.SwapIntervalList.length > 0) {
|
||
checkGet = this.checkSwapTimeRates(val.SwapIntervalList, num + 1, '利息端');
|
||
}
|
||
|
||
});
|
||
}
|
||
if (!checkGet) {
|
||
return false;
|
||
}
|
||
if (thisObj.marginSwapList != null) {
|
||
thisObj.marginSwapList.forEach((val, num, arr) => {
|
||
if (checkGet && val.SwapIntervalList.length > 0) {
|
||
checkGet = this.checkSwapTimeRates(val.SwapIntervalList, num + 1, '预付金/预付金');
|
||
}
|
||
|
||
});
|
||
}
|
||
if (!checkGet) {
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
// 多空组合利息腿校验
|
||
checkSwapRateList() {
|
||
var check = true;
|
||
return check;
|
||
},
|
||
//观察日起始日期跟交易起始日期检查
|
||
checkSwapTimeRates(SwapIntervalList, num, title) {
|
||
var StartDate = this.trade.StartDate;
|
||
var ExerciseDate = this.trade.ExerciseDate;
|
||
var check = true;
|
||
SwapIntervalList.forEach(item => {
|
||
var _date = "";
|
||
var m = new moment(item.Date);
|
||
if (!isNaN(m.date())) {
|
||
_date = m.format("YYYY-MM-DD");
|
||
}
|
||
if (_date < StartDate) {
|
||
main.message(title + "第" + num + "行互换观察日不可以早于交易开始日");
|
||
check = false;
|
||
} else if (_date > ExerciseDate) {
|
||
main.message(title + "第" + num + "行互换观察日不可以晚于交易到期日");
|
||
check = false;
|
||
}
|
||
});
|
||
return check;
|
||
},
|
||
changeClient: function (client) {
|
||
this.trade.ClientId = client.id;
|
||
this.client = client;
|
||
//if (this.client.SwapTradeType == 0) {//非DMA客户
|
||
// this.trade.trade_extend.ExtendObj.FlowBookMode = 3;//流水簿记模式为重置
|
||
//} else {
|
||
// this.trade.trade_extend.ExtendObj.FlowBookMode = 2;//流水簿记模式为加权平均 暂定
|
||
//}
|
||
//this.initMarginRate();
|
||
},
|
||
initMarginRate() { //初始预付金 初始话
|
||
_that = this;
|
||
var swapType = this.trade.StructureType;
|
||
var UnderlyingCode = "";
|
||
if (swapType != "多空组合") {
|
||
swapType = "普通";
|
||
UnderlyingCode = this.paySwapList[0].UnderlyingCode;
|
||
}
|
||
if (page.isAdd && this.trade.ClientId != null && ((swapType == "普通" && UnderlyingCode != "") || this.trade.trade_swap.SwapType == "多空组合")) {
|
||
main.post("/swapTrade/GetInitMarginRate", { ClientId: this.trade.ClientId, Type: swapType, UnderlyingCode: UnderlyingCode, tradeDate: this.trade.TradeDate })
|
||
.done(function (resp) {
|
||
if (resp.obj > 0) {
|
||
_that.trade.trade_Initial_Margin.MarginValue = resp.obj
|
||
}
|
||
});
|
||
}
|
||
},
|
||
//交易审批
|
||
submit(status) {
|
||
var pop = '';
|
||
if (status === 'pass') {
|
||
pop = "确认通过审批?";
|
||
}
|
||
if (status === 'reject') {
|
||
pop = "确认拒绝?";
|
||
}
|
||
var confirmFunc = function (additionalProcessing) {
|
||
var pData = { tradeId: page.Trade.id, status: status, text: "" };
|
||
if (!main.isEmpty(additionalProcessing)) {
|
||
pData.additionalProcessing = additionalProcessing;
|
||
}
|
||
main.post("/processtradelog/UpdateTradeProcessLog", pData).done(
|
||
function (data) {
|
||
if (data.obj && data.obj.proccessType == "AdditionalProcessing") {
|
||
if (data.obj.type == "LackOfMoney") {
|
||
var htmlContent = `<div style="padding:10px">${data.obj.message}</div>`;
|
||
var lackMoneyConfirmLayer = main.open2("提示",
|
||
htmlContent,
|
||
{
|
||
area: ["430px", "175px"],
|
||
btn: ['交易特批', '取消'],
|
||
yes: function (index, layero) {
|
||
var layerIndex = lackMoneyConfirmLayer;
|
||
main.confirm("客户资金或授信不足,强制成交会导致本机构产生风险!要继续审批通过?", function () {
|
||
layer.close(layerIndex);
|
||
confirmFunc("LackOfMoney");
|
||
});
|
||
},
|
||
cancel: function (index, layero) {
|
||
if (window.parent && window.parent.reloadtrade) {
|
||
window.parent.reloadtrade();
|
||
}
|
||
(parent || window).layer.closeAll();
|
||
}
|
||
});
|
||
}
|
||
return;
|
||
}
|
||
(parent || window).main.message(data.msg);
|
||
if (page.generateAmendDoc) {
|
||
if (data.obj && data.obj.generateChangeSuccess) {
|
||
main.downloadFiles(data.obj.url);
|
||
try { window.parent.reloadtrade(); } catch (e) { }
|
||
}
|
||
}
|
||
if (page.needSendChangeEmail) {
|
||
if (data.obj && data.obj.generateChangeSuccess) {
|
||
main.confirm("确认发送变更确认书?",
|
||
function () {
|
||
main.post("/trade/SendChangeConfirmEmails", { tradeids: tradeId }).done(
|
||
function (data) {
|
||
if (data.success) {
|
||
main.message("发送成功");
|
||
} else {
|
||
main.message(data.msg);
|
||
}
|
||
try {
|
||
window.parent.reloadtrade();
|
||
} catch (e) {
|
||
}
|
||
});
|
||
});
|
||
} else {
|
||
try {
|
||
window.parent.reloadtrade();
|
||
} catch (e) {
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
try { window.parent.reloadtrade(); } catch (e) { }
|
||
}
|
||
if (parent) {
|
||
parent.layer.closeAll();
|
||
}
|
||
});
|
||
}
|
||
main.confirm(pop, confirmFunc);
|
||
},
|
||
//初始化利息端列表
|
||
initSwapRateList() {
|
||
var thisObj = this;
|
||
thisObj.getSwapList = thisObj.trade.swap_positions.filter(x => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.IsInitial && (x.InterestMode == 1 || x.InterestMode == 2 || x.InterestMode == 9)) return x; });
|
||
thisObj.getSwapList.forEach((val, num, arr) => {
|
||
arr[num].index = num;
|
||
arr[num].category_tag = arr[num].category_tag || '互换利率';
|
||
// 解析 InterestSwapInterval 为 SwapIntervalList
|
||
if (arr[num].InterestSwapInterval && !arr[num].SwapIntervalList) {
|
||
try {
|
||
arr[num].SwapIntervalList = JSON.parse(arr[num].InterestSwapInterval);
|
||
} catch (e) { }
|
||
}
|
||
});
|
||
thisObj.marginSwapList = thisObj.trade.swap_positions.filter(x => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.IsInitial && (x.InterestMode == 5 || x.InterestMode == 6)) return x; });
|
||
thisObj.marginSwapList.forEach((val, num, arr) => {
|
||
arr[num].index = 1000 + num; // 保证金列表使用 1000+ 偏移,避免与利息腿冲突
|
||
arr[num].HappenDate = thisObj.formatDate(arr[num].HappenDate);
|
||
// 解析 InterestSwapInterval 为 SwapIntervalList
|
||
if (arr[num].InterestSwapInterval && !arr[num].SwapIntervalList) {
|
||
try {
|
||
arr[num].SwapIntervalList = JSON.parse(arr[num].InterestSwapInterval);
|
||
} catch (e) { }
|
||
}
|
||
});
|
||
if (thisObj.trade.StructureType == '多空组合') {
|
||
thisObj.paySwapList = [];
|
||
} else {
|
||
thisObj.paySwapList = thisObj.trade.swap_positions.filter(x => { if (x.UnderlyingCode != null && x.UnderlyingCode.length != 0 && x.IsInitial) return x; });
|
||
thisObj.paySwapList.forEach((val, num, arr) => {
|
||
arr[num].index = num;
|
||
arr[num].PosiFeeType = thisObj.normalizePosiFeeType(arr[num].PosiFeeType);
|
||
this.StockEqvNotional = val.ContractSize * val.PosiQuantity * val.PosiGrossPrice;
|
||
// 加载(重开/刷新)已保存的债券成交单:三字段互算状态标识全部清空,纯展示保存值、无任何 源/AUTO/REV 标记。
|
||
// 交互约定3规定"编辑未回车不联动",故用户载入后即使改某格(未回车)也不会反算另两格;
|
||
// 只有用户主动回车某格时,才以该格为源重新推导(符合交互约定1)。
|
||
var hasV = function (v) { return v !== null && v !== undefined && v !== '' && !isNaN(Number(v)); };
|
||
var isBond = val.isBond || tradeHelper.IsBond(val.UnderlyingInstrumentType);
|
||
if (isBond && hasV(val.PosiNetNoFeePrice) && hasV(val.PosiGrossPrice) && hasV(val.InitYtm)) {
|
||
SwapCalc.clearBondCalcMarksOnLoad(arr[num]);
|
||
thisObj.syncBondFlags(arr[num]);
|
||
}
|
||
});
|
||
if (thisObj.paySwapList.length > 0) {
|
||
thisObj.syncPosiFeeModeByItem(thisObj.paySwapList[0]);
|
||
}
|
||
}
|
||
|
||
if (page.isAdd && page.swapFloatingIncomeReceiveOnlyMode) {
|
||
thisObj.trade.trade_extend.ExtendObj.Direction = 1;
|
||
thisObj.paySwapList.forEach(x => x.PosiDirection = 1);
|
||
}
|
||
|
||
if (thisObj.paySwapList.length == 0) {
|
||
thisObj.trade.trade_extend.ExtendObj.Direction = thisObj.trade.trade_extend.ExtendObj.Direction == 0 ? 2 : thisObj.trade.trade_extend.ExtendObj.Direction;
|
||
var direction = thisObj.trade.trade_extend.ExtendObj.Direction;
|
||
thisObj.addSwapFloat(direction);
|
||
}
|
||
if (thisObj.getSwapList.length == 0) {
|
||
thisObj.addGetSwapRate();
|
||
}
|
||
//if (thisObj.marginSwapList.length == 0) {
|
||
// thisObj.addMarginSwapRate();
|
||
//}
|
||
|
||
},
|
||
// A renewal opens as a new trade, so the underlying autocomplete only restores
|
||
// its code and does not emit change. Historically fetched the initial price explicitly.
|
||
// 簿记基础逻辑1:续发场景也不自动填充价格,由用户手填或回车调计算器反算。
|
||
refreshInitialUnderlyingPrices() {
|
||
if (!page.isAdd) return;
|
||
// 不再自动调 getSpotPrice 回填价格;用户可手动回车调计算器或手填
|
||
},
|
||
//利息端添加行
|
||
addGetSwapRate() {
|
||
var InterestMode = 9;
|
||
var thisObj = this;
|
||
if (thisObj.trade.StructureType == '多空组合') {
|
||
InterestMode = 1;
|
||
}
|
||
var getSwap = {
|
||
index: thisObj.getSwapList.length,//利息序号,做删除以及观察日用
|
||
id: 0,
|
||
PosiDirection: 0,//浮动收支方式
|
||
PositionType: 0,//多空方向 1:long,2:short
|
||
UnderlyingCode: "",//标的代码
|
||
UnderlyingInstrumentType: "",
|
||
CountRatio: 0,//乘积因子
|
||
ContractSize: 0,//合约乘数
|
||
PosiNetPrice: 0,//期初标的价格含费
|
||
PosiGrossPrice: 0,//期初标的价格不含费
|
||
PosiNetFeePrice: 0,//净价含费
|
||
PosiNetNoFeePrice: 0,//净价不含费
|
||
PosiQuantity: 0,//持仓数量
|
||
PosiTradingFeePending: 0,//交易费用后付
|
||
PosiTradingFee: 0,//交易费用
|
||
PosiTradingFeeUnit: 0,//单位交易费用
|
||
InterestDirection: 1,//利息收支方式
|
||
InterestRateDefault: 0,//计息利率
|
||
InterestMode: InterestMode,//计息基本类型
|
||
InterestPrincipalFix: 0,//计息基准
|
||
FloatRate: 0,// 浮动利率
|
||
FloatRateUnderlyingCode: "",//浮动利率标的
|
||
InterestType: 1,//计息方式 0 单利,1:复利
|
||
InterestSwapInterval: "",//观察间隔
|
||
SwapIntervalList: [],//观察间隔集合
|
||
observation: null,//观察信息,
|
||
IsAnnualized: true,//是否年化
|
||
HappenDate: null,//发生日期,
|
||
Currency: 'CNY',//币种
|
||
interest_rest_days: 7,//重置频率
|
||
interest_rule: null,//利率准则
|
||
category_tag: '互换利率'//类别
|
||
}
|
||
thisObj.getSwapList.push(getSwap);
|
||
},
|
||
//预付金添加行
|
||
addMarginSwapRate() {
|
||
var thisObj = this;
|
||
var getSwap = {
|
||
index: thisObj.marginSwapList.length,//利息序号,做删除以及观察日用
|
||
id: 0,
|
||
PosiDirection: 0,//浮动收支方式
|
||
PositionType: 0,//多空方向 1:long,2:short
|
||
UnderlyingCode: "",//标的代码
|
||
UnderlyingInstrumentType:"",
|
||
CountRatio: 0,//乘积因子
|
||
ContractSize: 0,//合约乘数
|
||
PosiNetPrice: 0,//期初标的价格含费
|
||
PosiGrossPrice: 0,//期初标的价格不含费
|
||
PosiNetFeePrice: 0,//净价含费
|
||
PosiNetNoFeePrice: 0,//净价不含费
|
||
PosiQuantity: 0,//持仓数量
|
||
PosiTradingFeePending: 0,//交易费用后付
|
||
PosiTradingFee: 0,//交易费用
|
||
PosiTradingFeeUnit: 0,//单位交易费用
|
||
InterestDirection: 1,//利息收支方式
|
||
InterestRateDefault: 0,//计息利率
|
||
InterestMode: 5,//计息基本类型
|
||
InterestPrincipalFix: 0,//计息基准
|
||
FloatRate: 0,// 浮动利率
|
||
FloatRateUnderlyingCode: "",//浮动利率标的
|
||
InterestType: 0,//计息方式 0 单利,1:复利
|
||
InterestSwapInterval: "",//观察间隔
|
||
SwapIntervalList: [],//观察间隔集合
|
||
observation: null,//观察信息,
|
||
IsAnnualized: true,//是否年化,
|
||
HappenDate: thisObj.trade.TradeDate,//发生日期,
|
||
Currency: 'CNY',//币种
|
||
FundTag: '',//资金标签(R4):空=默认现金,可选授信,录入存选择、确认时系统定稿
|
||
interest_rest_days: 7,//重置频率
|
||
interest_rule: null//利率准则
|
||
}
|
||
thisObj.marginSwapList.push(getSwap);
|
||
},
|
||
//浮动端添加行
|
||
addSwapFloat(direction) {
|
||
var thisObj = this;
|
||
var petSwap = {
|
||
index: thisObj.paySwapList.length,//利息序号,做删除以及观察日用
|
||
id: 0,
|
||
PosiDirection: direction,//浮动收支方式
|
||
PositionType: 1,//多空方向 1:long,2:short
|
||
UnderlyingCode: "",//标的代码
|
||
underlying: {
|
||
UnderlyingCode: '',
|
||
UnderlyingName: '',
|
||
UnderlyingIssuer: '',
|
||
IssueSize: '',
|
||
MaturityDate: '',
|
||
Price: '',
|
||
UnderlyingInstrumentType: '',
|
||
QuoteUnitString: ''
|
||
},//标的信息
|
||
UnderlyingInstrumentType: "",
|
||
CountRatio: 1,//乘积因子
|
||
ContractSize: 1,//合约乘数
|
||
PosiNetPrice: 0,//期初标的价格含费
|
||
PosiGrossPrice: 0,//期初标的价格不含费
|
||
PosiNetFeePrice: 0,//净价含费
|
||
PosiNetNoFeePrice: 0,//净价不含费
|
||
PosiQuantity: 0,//持仓数量
|
||
PosiTradingFee: 0,//交易费用
|
||
PosiTradingFeePending: 0,//交易费用后付
|
||
PosiTradingFeeUnit: 0,//单位交易费用
|
||
PosiFeeType: thisObj.getCurrentPosiFeeType(),//单位交易费用模式
|
||
InterestDirection: 0,//利息收支方式
|
||
InterestRateDefault: 0,//计息利率
|
||
InterestMode: 0,//计息基本类型
|
||
InterestPrincipalFix: 0,//计息基准
|
||
FloatRate: 0,// 浮动利率
|
||
FloatRateUnderlyingCode: "",//浮动利率标的
|
||
InterestType: 0,//计息方式 0 单利,1:复利
|
||
InterestSwapInterval: "",//观察间隔
|
||
SwapIntervalList: [],//观察间隔集合
|
||
observation: null,//观察信息
|
||
IsAnnualized: false,//是否年化
|
||
HappenDate: null,//发生日期,
|
||
Currency: 'CNY',//币种
|
||
interest_rest_days: 7,//重置频率
|
||
interest_rule: null//利率准则
|
||
}
|
||
thisObj.paySwapList.push(petSwap);
|
||
},
|
||
//利息端删除行
|
||
deleteSwapRate(index) {
|
||
var thisObj = this;
|
||
thisObj.getSwapList.splice(index, 1);
|
||
thisObj.getSwapList.forEach((val, num, arr) => {
|
||
arr[num].index = num;
|
||
});
|
||
},
|
||
//利息端删除行
|
||
deleteMarginSwapRate(index) {
|
||
var thisObj = this;
|
||
thisObj.marginSwapList.splice(index, 1);
|
||
thisObj.marginSwapList.forEach((val, num, arr) => {
|
||
arr[num].index = num;
|
||
});
|
||
},
|
||
//浮动端删除行
|
||
deleteSwapFloat(index) {
|
||
var thisObj = this;
|
||
thisObj.paySwapList.splice(index, 1);
|
||
thisObj.paySwapList.forEach((val, num, arr) => {
|
||
arr[num].index = num;
|
||
});
|
||
},
|
||
formatDate(time, formatStr) {
|
||
if (!formatStr) {
|
||
formatStr = "YYYY-MM-DD";
|
||
}
|
||
var momDate = new moment(time);
|
||
if (!momDate.isValid() || typeof time === "undefined") return "/";
|
||
return momDate.format(formatStr);
|
||
},
|
||
showUnderlyingInfo(item) {
|
||
var underlying = item.underlying;
|
||
var html = '<div class="row no-gutters">'
|
||
+ '<ul> '
|
||
+ '<li class="font16" ><span class="font_title">' + underlying.UnderlyingCode + '</span><span class="font_desc"> ' + underlying.UnderlyingName + ' </span>'
|
||
+ '<a href="javascript:void(0)" onclick="hideUnderlyingInfo(' + item.index + ')"><span class="glyphicon glyphicon-remove" style="color:#ff0000"></span></a></li>'
|
||
+ '<li class="font_desc"> 发行人:' + underlying.UnderlyingIssuer + '</li>'
|
||
+ '<li class="font_desc">面额:' + underlying.Price + '元</li>'
|
||
+ '<li class="font_desc">债券规模:' + (underlying.IssueSize ? underlying.IssueSize : '') + '亿</li>'
|
||
+ '<li class="font_desc">到期日:' + this.formatDate(underlying.MaturityDate, 'YYYY年MM月DD日') + '</li>'
|
||
+ '</ul>'
|
||
+ '</div>'
|
||
$(".un").popover('show');
|
||
$(".popover-body").html(html);
|
||
//$(".un").popover('show');
|
||
},
|
||
refreshDatepicker() {
|
||
var thisObj = this;
|
||
var startDate = thisObj.trade.StartDate;
|
||
var endDate = thisObj.trade.ExerciseDate;
|
||
if (this.$refs.happenDateRefs) {
|
||
for (let i = 0; i < this.$refs.happenDateRefs.length; i++) {
|
||
this.$refs.happenDateRefs[i].refresh(startDate, endDate);
|
||
}
|
||
}
|
||
this.$refs.observationStartRefs.refresh(startDate, endDate);
|
||
},
|
||
IsBond(instType) {
|
||
return tradeHelper.IsBond(instType);
|
||
}
|
||
},
|
||
computed: {
|
||
maxTradeDate() {
|
||
let exerciseDate = this.trade.ExerciseDate;
|
||
return exerciseDate && exerciseDate <= page.valuedate ? exerciseDate : page.valuedate;
|
||
},
|
||
},
|
||
components: {
|
||
'vue-datepicker': FastVue.vueDatePicker(),
|
||
'vue-number-input': FastVue.vueNumberInput(),
|
||
'vue-swap-price-input': swapPricePrecision.createVueInputComponent(),
|
||
'vue-underlying-nonbond': vueUnderlyingNonBond(),
|
||
'vue-underlying-bond': vueUnderlyingBond(),
|
||
'vue-underlying-rate': vueUnderlyingRate()
|
||
},
|
||
destroyed() {
|
||
|
||
}
|
||
});
|
||
function hideUnderlyingInfo(index) {
|
||
$(".un").popover('hide');
|
||
}
|
||
function __init(vue) {
|
||
//交易员
|
||
if (page.canAddNewTrader) {
|
||
autoTrader = FastVue.autocomplete(document.getElementById('TraderId'), {
|
||
nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'], lookup: consTraders,
|
||
onSelect(data) {
|
||
vue.trade.TraderId = data.id;
|
||
vue.trade.TraderName = data.Name;
|
||
}
|
||
});
|
||
let traderId = page.Trade.TraderId;
|
||
let trader = traderId ? consTraders.find(x => x.id === traderId) : null;
|
||
if (!trader && page.IsNew) {
|
||
trader = consTraders[0];
|
||
trader && (vue.trade.TraderId = trader.id);
|
||
}
|
||
autoTrader.setData(trader);
|
||
}
|
||
|
||
//簿记账户
|
||
let autoAssetUnit = FastVue.autocomplete(document.getElementById('AssetId'), {
|
||
nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'], lookup: consAssetUnits,
|
||
onSelect(data) {
|
||
vue.trade.AssetId = data.id;
|
||
}
|
||
});
|
||
let assetId = page.Trade.AssetId;
|
||
let asset = assetId ? consAssetUnits.find(x => x.id === assetId) : null;
|
||
if (!asset && page.IsNew) {
|
||
asset = consAssetUnits[0];
|
||
asset && (vue.trade.AssetId = asset.id);
|
||
}
|
||
autoAssetUnit.setData(asset);
|
||
|
||
//客户名称
|
||
if (page.canChangeClient) {
|
||
let autoClient = FastVue.autocomplete(document.getElementById('ClientId'), {
|
||
nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'],
|
||
lookup: consClients, onSelect: function (rep) {
|
||
vue.changeClient(rep);
|
||
|
||
_getMainProtocolCode(rep);
|
||
}
|
||
});
|
||
let clientId = page.Trade.ClientId;
|
||
let client = clientId ? consClients.find(x => x.id === clientId) : null;
|
||
!client && page.IsNew && (client = consClients[0]);
|
||
autoClient.setData(client);
|
||
_getMainProtocolCode(client);
|
||
}
|
||
}
|
||
function _getMainProtocolCode(client) {
|
||
$("#MainProtocolCode option").remove();
|
||
if (client) {
|
||
let clientId = typeof client === 'object' ? client.id : client;
|
||
main.post("/Client/getMainProtocolCodes", { clientId: clientId }, { async: false }).done(function (resp) {
|
||
var obj = $("#MainProtocolCode");
|
||
if (client.MainProtocolCode != null && client.MainProtocolCode != '') {
|
||
obj.append("<option >" + client.MainProtocolCode + "</option>");
|
||
}
|
||
_.forEach(resp, (v) => {
|
||
obj.append("<option >" + v.Text + "</option>");
|
||
})
|
||
});
|
||
}
|
||
$("#SupProtocolCode option").remove();
|
||
if ($("#MainProtocolCode").val()) {
|
||
main.post("/Client/getSideProtocols", { mainProtocol: $("#MainProtocolCode").val() }, { async: false }).done(function (resp) {
|
||
var obj = $("#SupProtocolCode");
|
||
if (client.SupProtocolCode != null && client.SupProtocolCode != '') {
|
||
obj.append("<option >" + client.SupProtocolCode + "</option>");
|
||
}
|
||
_.forEach(resp, (v) => {
|
||
obj.append("<option >" + v + "</option>");
|
||
})
|
||
});
|
||
}
|
||
}
|
||
var getObservationDatesSetting = vue.getObservationDatesSetting;
|
||
$(function () {
|
||
$(".un").popover({
|
||
html: true,
|
||
title: function () {
|
||
return "发行情况";
|
||
},
|
||
content: function () {
|
||
return "";
|
||
},
|
||
placement: "right"
|
||
|
||
});
|
||
$(".datepicker").change(function () {
|
||
var dateVal = $(this).val();
|
||
if (!dateVal) return;
|
||
dateVal = dateVal.replace(/\D/g, '').padStart(4, '0');
|
||
switch (dateVal.length) {
|
||
case 4:
|
||
dateVal = new Date().getFullYear() + dateVal; break;
|
||
case 6:
|
||
dateVal = '20' + dateVal; break;
|
||
case 8: break;
|
||
default:
|
||
dateVal = dateVal.length > 8 ? dateVal.substring(0, 8) : ''; break;
|
||
}
|
||
|
||
if (dateVal) {
|
||
dateVal = moment(dateVal).format('YYYY-MM-DD');
|
||
!/^\d+/.test(dateVal) && (dateVal = '');
|
||
}
|
||
|
||
$(this).datepicker("setDate", dateVal);
|
||
});
|
||
});
|