提议采纳:原数量可执行时保持确认内容(台账 078)

一、现场原500股因组合现金变化而反复改写说明,确认标识随报价漂移。研究与参数未变且原数量仍过闸时保留准备结果,不扩大数量。

二、额度不足仍按原流程重算数量并复用研判。新增真实API反例至14项,47批隔离禁网回归重新通过。
This commit is contained in:
baobao 2026-10-21 10:14:20 +08:00
parent 2d3982f945
commit 888a20897d
5 changed files with 19 additions and 4 deletions

View File

@ -2100,3 +2100,6 @@ QMT在线、对账无差异。午休任务按原时段跳过,不制造交易
页面只在确认内容确实变化时清空原理由,不把连接失败叫建议更新。 页面只在确认内容确实变化时清空原理由,不把连接失败叫建议更新。
新增批次43,实际API、来源解析、筛选、快速检查和SQL事务共13例;桥逐票7例。 新增批次43,实际API、来源解析、筛选、快速检查和SQL事务共13例;桥逐票7例。
47批PMS禁网回归与三页面守卫通过。桥完整版本契约回归通过。部署及真机读数随后追加。 47批PMS禁网回归与三页面守卫通过。桥完整版本契约回归通过。部署及真机读数随后追加。
补充:现场原500股因组合现金变化被后台反复生成新说明,连带更换确认标识。
原数量仍过全部闸门时保留准备结果;额度不足才重算。新增第14例,原回归补齐快速检查依赖。

View File

@ -212,8 +212,12 @@ def refresh_one(p, *, now=None, deadline=None):
return _failed_recheck(p, {'rejected': [{'by': 'qualification', 'why': check['error']}]}) return _failed_recheck(p, {'rejected': [{'by': 'qualification', 'why': check['error']}]})
if not check.get('view'): if not check.get('view'):
return _pending_response(p, check.get('error') or '建议资料暂不可用', check.get('stage', 'source')) 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'): if ((hard.get('preparation') or {}).get('status') == 'ready' and not hard.get('last_accept_check')
return {'ok': True} 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') if ((hard.get('preparation') or {}).get('stage') in ('source', 'research_pending')
and hard.get('consumption_id') and hard.get('proposal_revision') and hard.get('consumption_id') and hard.get('proposal_revision')

View File

@ -5,7 +5,7 @@
运行: docker compose run --rm pms-web python scripts/run_tests.py 运行: docker compose run --rm pms-web python scripts/run_tests.py
包含 (例数按 2026-10-12 隔离回归实跑校正): 包含 (例数按 2026-10-12 隔离回归实跑校正):
test_batch43_units.py 逐票来源核验、实际API至SQL事务、故障回滚与幂等 (13 例) test_batch43_units.py 逐票来源核验、实际API至SQL事务、故障回滚与幂等 (14 例)
test_batch42_units.py 提议先准备后采纳、模型复用、并发更新与发布审计 (24 例) test_batch42_units.py 提议先准备后采纳、模型复用、并发更新与发布审计 (24 例)
test_batch41_units.py 跨流程买卖互斥、清仓收尾、出口撤单与真实进展及人工清仓T+1 (24 例) test_batch41_units.py 跨流程买卖互斥、清仓收尾、出口撤单与真实进展及人工清仓T+1 (24 例)
test_batch40_units.py 逐笔历史修复、进度暂停、并发与事务回滚与均价失效 (22 例) test_batch40_units.py 逐笔历史修复、进度暂停、并发与事务回滚与均价失效 (22 例)

View File

@ -128,7 +128,7 @@ class PreparedProposalTest(unittest.TestCase):
patch.object(ps,'_scan_open',return_value=[c]), \ patch.object(ps,'_scan_open',return_value=[c]), \
patch.object(ps,'_route_one',return_value={'verdict':{'verdict':'PASS','reason':'原研究'}}) as routed, \ 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(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) out=rc.refresh_one(p)
return out,routed return out,routed
@ -234,6 +234,7 @@ class PreparedProposalTest(unittest.TestCase):
def test_background_failure_stays_pending_and_does_not_publish(self): 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)), \ 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(pms_repo,'record_proposal_check',return_value=1), \
patch.object(ps,'_scan_open',return_value=[]), \ patch.object(ps,'_scan_open',return_value=[]), \
patch.object(pms_repo,'publish_prepared_proposal',side_effect=AssertionError('无结果不能发布')): patch.object(pms_repo,'publish_prepared_proposal',side_effect=AssertionError('无结果不能发布')):

View File

@ -127,6 +127,13 @@ class AcceptancePathTest(unittest.TestCase):
def test_existing_quantity_still_requires_funds(self): def test_existing_quantity_still_requires_funds(self):
self.view['totals']['cash_avail']=100 self.view['totals']['cash_avail']=100
r=self.decide();self.assertFalse(r['ok']);self.assertIn('资金不足',r['error']);self.assertEqual(self.counts(),(0,0)) 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): 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'}) p=pms_repo.get_proposal(self.pid);h=dict(p['hard_numbers'],preparation={'status':'pending','stage':'source'})
pms_repo.record_proposal_check(p,h) pms_repo.record_proposal_check(p,h)