feat(swaptrade): 债券三字段计算器交互改为回车触发并加源/AUTO/REV 三态标识
- 约定1:仅在净价/全价/收益率输入框敲回车触发计算器,失焦不触发 - 约定2:计算成功->源字段标"源"、另两字段标"AUTO";失败->保留源值、清空另两字段与标识 - 约定3:编辑某字段未回车->不联动另两字段,清源/AUTO、仅本字段标"REV" - swapCalc.js 新增 applyBondCalcSuccess/Failure/ManualEdit 纯函数,applyBondCalcResult 第三参由 manualSet 改为 driver(跳过源字段写回) - fastVue.base.js 新增 enter 事件区分回车/失焦,向后兼容旧的 input 行为 - TradeEdit.cshtml 三字段 input/enter 分离绑定 + 源/AUTO/REV 三态标签(去除旧铅笔图标) - 重写 bondCalc.test.js / 更新 swapCalc.test.js 覆盖状态机;jest 68 项全过
This commit is contained in:
@@ -293,79 +293,77 @@ describe('交叉校验:对齐 C# FrontendCalcCharacterizationTest 金标准',
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// D1 回归守卫:切换债券标的必须清空手动/源标志,避免旧债券手填状态污染新债券
|
||||
// 旧 bug:setUnderlyingCode 切债券时未重置 bondManual/bondDriverType,
|
||||
// 旧债券标记过的字段在新债券上会被错误跳过/沿用旧态。
|
||||
// D1 回归守卫:切换债券标的必须清空 源/AUTO/REV 标志,避免旧债券状态污染新债券
|
||||
// 旧 bug:setUnderlyingCode 切债券时未重置状态,旧债券标记过的字段在新债券上会被错误沿用。
|
||||
// 修复:setUnderlyingCode 调 SwapCalc.clearBondCalcFlags(item)。
|
||||
// ============================================================================
|
||||
describe('D1 切换标的清空债券互算手动/源标志', () => {
|
||||
test('clearBondCalcFlags 把 bondManual 三字段归 false、bondDriverType 归 null', () => {
|
||||
describe('D1 切换标的清空债券互算源/AUTO/REV 标志', () => {
|
||||
test('clearBondCalcFlags 把 bondDriverType/bondAuto/bondRev 全清', () => {
|
||||
const item = {
|
||||
bondManual: { CP: true, DP: false, YD: true },
|
||||
bondDriverType: 'YD'
|
||||
bondDriverType: 'YD',
|
||||
bondAuto: { CP: true, DP: true, YD: false },
|
||||
bondRev: { CP: false, DP: false, YD: true }
|
||||
};
|
||||
const out = SwapCalc.clearBondCalcFlags(item);
|
||||
expect(out.bondManual).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.bondDriverType).toBeNull();
|
||||
expect(out.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||||
});
|
||||
|
||||
test('clearBondCalcFlags 对全新未交互标的(无标志)也安全初始化', () => {
|
||||
const item = { UnderlyingCode: '200000.IB', isBond: true };
|
||||
const out = SwapCalc.clearBondCalcFlags(item);
|
||||
expect(out.bondManual).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.bondDriverType).toBeNull();
|
||||
expect(out.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.UnderlyingCode).toBe('200000.IB'); // 其它字段不受影响
|
||||
});
|
||||
|
||||
test('模拟"债券A手填→切债券B":B 不应继承 A 的手动标志', () => {
|
||||
// 债券 A:用户手填了全价,标记手动 + 设源
|
||||
test('模拟"债券A回车→切债券B":B 不应继承 A 的源/AUTO/REV', () => {
|
||||
// 债券 A:用户回车了全价,标记源 + AUTO
|
||||
const item = { UnderlyingCode: '190000.IB', isBond: true,
|
||||
bondManual: { CP: false, DP: true, YD: false }, bondDriverType: 'DP' };
|
||||
bondDriverType: 'DP',
|
||||
bondAuto: { CP: true, DP: false, YD: true },
|
||||
bondRev: { CP: false, DP: false, YD: false } };
|
||||
// 切到债券 B(setUnderlyingCode 会调 clearBondCalcFlags)
|
||||
SwapCalc.clearBondCalcFlags(item);
|
||||
item.UnderlyingCode = '200000.IB';
|
||||
// 若不清空,applyBondCalcResult 会以旧的 bondManual.DP=true 跳过 B 的全价→错误
|
||||
const manual = item.bondManual;
|
||||
expect(manual.CP || manual.DP || manual.YD).toBe(false); // B 上无任何手动标志
|
||||
expect(item.bondDriverType).toBeNull(); // B 无计算源,下一步反算不会误用 A 的源
|
||||
expect(item.bondDriverType).toBeNull(); // B 无计算源
|
||||
expect(item.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(item.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// D2 回归守卫:重开(审批重开/刷新)已保存的债券成交单时,三字段互算的手动标志
|
||||
// 随页面重置丢失 → 用户一旦编辑任一价格字段就会以它为源重新反算、覆盖当初保存的其他两格。
|
||||
// 修复:加载路径对"债券且三字段齐全(净价/全价/收益率均有值)"的标的,调 markExistingBondManual
|
||||
// 把三格一次性锁为手动(true),重开期间计算器不再自动推导;点"重算"才清除重新计算。
|
||||
// D2 回归守卫:重开(审批重开/刷新)已保存的债券成交单时,三字段互算标识随页面重置丢失 →
|
||||
// 加载路径调 clearBondCalcMarksOnLoad 把 源/AUTO/REV 全部清空,纯展示保存值、无锁定。
|
||||
// 交互约定3规定"编辑未回车不联动",故用户载入后即使改某格(未回车)也不会反算另两格;
|
||||
// 只有用户主动回车某格时,才以该格为源重新推导(符合交互约定1)。
|
||||
// ============================================================================
|
||||
describe('D2 重开已保存债券单 → 三字段锁定、编辑不联动另两格', () => {
|
||||
test('markExistingBondManual 把三字段全锁 true、不置计算源', () => {
|
||||
const item = { isBond: true, PosiNetNoFeePrice: 0.995, PosiGrossPrice: 1.0, InitYtm: 0.026 };
|
||||
const out = SwapCalc.markExistingBondManual(item);
|
||||
expect(out.bondManual).toEqual({ CP: true, DP: true, YD: true });
|
||||
expect(out.bondDriverType).toBeUndefined(); // 不置源 → calcBondForItem 早返回,重开零自动推导
|
||||
describe('D2 重开已保存债券单 → 三字段无标识、编辑不联动另两格', () => {
|
||||
test('clearBondCalcMarksOnLoad 把 源/AUTO/REV 全清、不置计算源、不动价格值', () => {
|
||||
const item = { isBond: true, PosiNetNoFeePrice: 0.995, PosiGrossPrice: 1.0, InitYtm: 0.026,
|
||||
bondDriverType: 'CP',
|
||||
bondAuto: { CP: false, DP: true, YD: true },
|
||||
bondRev: { CP: false, DP: false, YD: false } };
|
||||
const out = SwapCalc.clearBondCalcMarksOnLoad(item);
|
||||
expect(out.bondDriverType).toBeNull();
|
||||
expect(out.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(out.PosiNetNoFeePrice).toBe(0.995); // 价格值本身不动,仅清标识
|
||||
expect(out.PosiGrossPrice).toBe(1.0);
|
||||
});
|
||||
|
||||
test('重开后编辑某一格(如全价):另两格因 manual=true 被 applyBondCalcResult 跳过、不被覆盖', () => {
|
||||
test('重开后仅编辑某一格(未回车):交互约定3 使另两格不被反算覆盖', () => {
|
||||
const item = { isBond: true, PosiNetNoFeePrice: 0.995, PosiGrossPrice: 1.0, InitYtm: 0.026 };
|
||||
SwapCalc.markExistingBondManual(item);
|
||||
// 模拟用户编辑全价(DP):onBondPriceInput 会置 bondDriverType='DP' 并重算
|
||||
item.bondDriverType = 'DP';
|
||||
const proxy = { cleanPrice: 99.5, dirtyPrice: 100.0, ytm: 6.37 };
|
||||
const calc = { cleanPrice: 97.82, dirtyPrice: 98.5, ytm: 2.60 };
|
||||
SwapCalc.applyBondCalcResult(proxy, calc, item.bondManual);
|
||||
// 三格皆 manual=true → 一个都不回写,保存值(99.5/100.0/6.37)原样保留
|
||||
expect(proxy.cleanPrice).toBe(99.5);
|
||||
expect(proxy.dirtyPrice).toBe(100.0);
|
||||
expect(proxy.ytm).toBe(6.37);
|
||||
});
|
||||
|
||||
test('加载路径不会误锁"全新未填的债券"(三值不全)', () => {
|
||||
// 全新债券标的,价格字段空 → 不应被标记为手动锁定
|
||||
const item = { isBond: true, PosiNetNoFeePrice: null, PosiGrossPrice: null, InitYtm: null };
|
||||
SwapCalc.markExistingBondManual(item);
|
||||
// 全新标的本不应调用 markExistingBondManual;此处断言:即便误调也不应制造假锁定干扰后续交互
|
||||
// (实际加载逻辑用 hasV 三值齐全判定,只在已保存单上调用,此用例验证函数本身不副作用其它字段)
|
||||
expect(item.UnderlyingCode).toBeUndefined();
|
||||
SwapCalc.clearBondCalcMarksOnLoad(item);
|
||||
// 模拟用户编辑全价(DP)但未回车:onBondPriceEdit 调 applyBondManualEdit → 清源/AUTO、DP 标 REV,不调计算器
|
||||
SwapCalc.applyBondManualEdit(item, 'DP');
|
||||
expect(item.bondDriverType).toBeNull(); // 无计算源 → 不会反算
|
||||
expect(item.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||||
expect(item.bondRev).toEqual({ CP: false, DP: true, YD: false });
|
||||
expect(item.PosiNetNoFeePrice).toBe(0.995); // 另两格原值保留
|
||||
expect(item.InitYtm).toBe(0.026);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user