diff --git a/DEVLOG.md b/DEVLOG.md index 8aae336..e3bb531 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -2100,3 +2100,6 @@ QMT在线、对账无差异。午休任务按原时段跳过,不制造交易 页面只在确认内容确实变化时清空原理由,不把连接失败叫建议更新。 新增批次43,实际API、来源解析、筛选、快速检查和SQL事务共13例;桥逐票7例。 47批PMS禁网回归与三页面守卫通过。桥完整版本契约回归通过。部署及真机读数随后追加。 + +补充:现场原500股因组合现金变化被后台反复生成新说明,连带更换确认标识。 +原数量仍过全部闸门时保留准备结果;额度不足才重算。新增第14例,原回归补齐快速检查依赖。 diff --git a/app/services/plan_recheck.py b/app/services/plan_recheck.py index 80e2339..41d791c 100644 --- a/app/services/plan_recheck.py +++ b/app/services/plan_recheck.py @@ -212,8 +212,12 @@ def refresh_one(p, *, now=None, deadline=None): return _failed_recheck(p, {'rejected': [{'by': 'qualification', 'why': check['error']}]}) if not check.get('view'): return _pending_response(p, check.get('error') or '建议资料暂不可用', check.get('stage', 'source')) - if check.get('ok') and (hard.get('preparation') or {}).get('status') == 'ready' and not hard.get('last_accept_check'): - return {'ok': True} + if ((hard.get('preparation') or {}).get('status') == 'ready' and not hard.get('last_accept_check') + and not check.get('research_changed') and not check.get('params_changed')): + # 其他持仓/现金读数变化不等于本建议失效。原数量仍满足当前闸门就保留, + # 否则报价写进新扫描的说明会让相同500股每分钟变成另一份待确认内容。 + if check.get('ok') or not fast_checks(p, check): + return {'ok': True} # 旧的短暂取数失败不应触发扫描、重新选数和新消费;当前原数量仍过闸即可恢复。 if ((hard.get('preparation') or {}).get('stage') in ('source', 'research_pending') and hard.get('consumption_id') and hard.get('proposal_revision') diff --git a/scripts/run_tests.py b/scripts/run_tests.py index afd845e..4b6b346 100644 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -5,7 +5,7 @@ 运行: docker compose run --rm pms-web python scripts/run_tests.py 包含 (例数按 2026-10-12 隔离回归实跑校正): - test_batch43_units.py 逐票来源核验、实际API至SQL事务、故障回滚与幂等 (13 例) + test_batch43_units.py 逐票来源核验、实际API至SQL事务、故障回滚与幂等 (14 例) test_batch42_units.py 提议先准备后采纳、模型复用、并发更新与发布审计 (24 例) test_batch41_units.py 跨流程买卖互斥、清仓收尾、出口撤单与真实进展及人工清仓T+1 (24 例) test_batch40_units.py 逐笔历史修复、进度暂停、并发与事务回滚与均价失效 (22 例) diff --git a/scripts/test_batch42_units.py b/scripts/test_batch42_units.py index ae4b90e..01775bb 100644 --- a/scripts/test_batch42_units.py +++ b/scripts/test_batch42_units.py @@ -128,7 +128,7 @@ class PreparedProposalTest(unittest.TestCase): patch.object(ps,'_scan_open',return_value=[c]), \ patch.object(ps,'_route_one',return_value={'verdict':{'verdict':'PASS','reason':'原研究'}}) as routed, \ patch.object(pms_repo,'publish_prepared_proposal',return_value=1), \ - patch.object(rc,'fast_checks',return_value=None), patch.object(ps.param_store,'get_int',return_value=0): + patch.object(rc,'fast_checks',return_value='原数量不再满足资金条件'), patch.object(ps.param_store,'get_int',return_value=0): out=rc.refresh_one(p) return out,routed @@ -234,6 +234,7 @@ class PreparedProposalTest(unittest.TestCase): def test_background_failure_stays_pending_and_does_not_publish(self): with patch.object(rc,'current_basis',return_value=current(ok=False,portfolio_changed=True)), \ + patch.object(rc,'fast_checks',return_value='资金不足'), \ patch.object(pms_repo,'record_proposal_check',return_value=1), \ patch.object(ps,'_scan_open',return_value=[]), \ patch.object(pms_repo,'publish_prepared_proposal',side_effect=AssertionError('无结果不能发布')): diff --git a/scripts/test_batch43_units.py b/scripts/test_batch43_units.py index 4c1a995..8470190 100644 --- a/scripts/test_batch43_units.py +++ b/scripts/test_batch43_units.py @@ -127,6 +127,13 @@ class AcceptancePathTest(unittest.TestCase): def test_existing_quantity_still_requires_funds(self): self.view['totals']['cash_avail']=100 r=self.decide();self.assertFalse(r['ok']);self.assertIn('资金不足',r['error']);self.assertEqual(self.counts(),(0,0)) + def test_ready_portfolio_price_changes_keep_confirmation_when_quantity_fits(self): + self.view['totals']['cash_avail']=90000 + before=pms_repo.get_proposal(self.pid) + self.assertTrue(rc.refresh_one(before)['ok']) + self.assertEqual(pms_repo.get_proposal(self.pid)['hard_numbers_json'],before['hard_numbers_json']) + self.assertEqual(self.counts(),(0,0)) + self.assertTrue(self.decide()['ok']) def test_source_recovery_restores_original_content_without_scan(self): p=pms_repo.get_proposal(self.pid);h=dict(p['hard_numbers'],preparation={'status':'pending','stage':'source'}) pms_repo.record_proposal_check(p,h)