1.0.541.verify.sql 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. -- 1.0.541.verify.sql — HARD ASSERTIONS,每条 SELECT 都必须返回 1
  2. --
  3. -- ⚠️ AutoVersionUpdate 按分号切分本文件,并把【每一个切片】都当断言、要求返回 1。因此:
  4. -- ① 不得放"仅供排查"的 informational 查询
  5. -- ② 文件必须以最后一条断言的分号结尾,末尾不能留纯注释块
  6. -- ③ 注释里不得出现任何字面分号
  7. --
  8. -- 口径:只断言本批 Writer 独占产出的 mdp_std_iqc_inspection。
  9. -- 全局历史卫生(如 fixture 残留、PR.work_order 为空)一律不写成 HARD,只在报告里登记 WARNING。
  10. -- 结构断言迁移后即可全过;数据断言在表为空时也成立,投影跑过之后才真正有判别力。
  11. -- ── ① 表与唯一键就位,且 Grain = (tenant_id, source_row_id) ────────────────────────
  12. SELECT (SELECT COUNT(*) FROM `information_schema`.`TABLES`
  13. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_std_iqc_inspection') = 1;
  14. SELECT (SELECT GROUP_CONCAT(`COLUMN_NAME` ORDER BY `SEQ_IN_INDEX`)
  15. FROM `information_schema`.`STATISTICS`
  16. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_std_iqc_inspection'
  17. AND `INDEX_NAME` = 'uk_mdp_std_iqc_insp') = 'tenant_id,source_row_id';
  18. -- ── ② 两条正交轴必须都在:不得只留一个 is_completed 布尔 ───────────────────────────
  19. SELECT (SELECT COUNT(*) FROM `information_schema`.`COLUMNS`
  20. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_std_iqc_inspection'
  21. AND `COLUMN_NAME` IN ('raw_pd','quality_result','flow_status','completion_time')) = 4;
  22. -- ── ③ 本批绝不得引入任何行级身份列 ─────────────────────────────────────────────────
  23. -- 上游 FSRCBILLID / FSRCBILLENTRYSEQ 实测全空,行级归因属 SOURCE CONTRACT GAP
  24. SELECT (SELECT COUNT(*) FROM `information_schema`.`COLUMNS`
  25. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_std_iqc_inspection'
  26. AND `COLUMN_NAME` IN ('po_line','purchase_order_line','receipt_line',
  27. 'source_entry_seq','source_bill_entry_seq')) = 0;
  28. -- ── ④ 租户合法:本批 Writer 只投 tenant_id > 0 的检验单 ────────────────────────────
  29. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  30. WHERE `tenant_id` IS NULL OR `tenant_id` <= 0) = 0;
  31. -- ── ⑤ 质量映射必须严格来自 raw_pd ──────────────────────────────────────────────────
  32. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  33. WHERE `raw_pd` = 0 AND `quality_result` <> 'PASS') = 0;
  34. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  35. WHERE `raw_pd` = 1 AND `quality_result` <> 'FAIL') = 0;
  36. -- ⑤c pd 为 NULL 时绝不能是 PASS —— 这是「未判定不得滑成合格」的硬底线
  37. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  38. WHERE `raw_pd` IS NULL AND `quality_result` <> 'PENDING') = 0;
  39. -- ⑤d 取值域封闭
  40. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  41. WHERE `quality_result` NOT IN ('PASS','FAIL','PENDING')) = 0;
  42. -- ── ⑥ 流程与质量是正交轴:流程完成不得被当成质量合格 ───────────────────────────────
  43. -- 判据:存在 flow_status=2(Approved)却 quality_result='FAIL' 的行是【合法】的,
  44. -- 所以这里不能断言二者一致。真正要守的是反向:不得因为流程完成就把 PASS 写上去。
  45. -- 表述为:quality_result 必须能由 raw_pd 单独推出(⑤ 已保证),此处补断流程列不污染质量列。
  46. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  47. WHERE `quality_result` = 'PASS' AND (`raw_pd` IS NULL OR `raw_pd` <> 0)) = 0;
  48. -- ── ⑦ 完成时间只能来自终态审批流,Running 状态不得伪造 ─────────────────────────────
  49. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  50. WHERE `completion_time` IS NOT NULL AND IFNULL(`flow_status`, -1) <> 2) = 0;
  51. -- ⑦b 完成时间必须逐行等于被采纳实例的 EndTime,不得自造
  52. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection` s
  53. JOIN `ApprovalFlowInstance` i ON i.`Id` = s.`flow_instance_id`
  54. WHERE s.`completion_time` IS NOT NULL
  55. AND s.`completion_time` <> i.`EndTime`) = 0;
  56. -- ── ⑧ 租户 Authority 单向:投出来的租户必须等于源检验单的租户 ───────────────────────
  57. -- 审批流表没有 TenantId,绝不允许它反向决定租户
  58. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection` s
  59. JOIN `qms_qcp_inspbill` b ON b.`id` = s.`iqc_bill_id`
  60. WHERE b.`tenant_id` <> s.`tenant_id`) = 0;
  61. -- ── ⑨ 收货单号与采购单号互不覆盖 ───────────────────────────────────────────────────
  62. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  63. WHERE `receipt_no` IS NOT NULL AND `purchase_order_no` IS NOT NULL
  64. AND `receipt_no` = `purchase_order_no`) = 0;
  65. -- ⑨b 采购单号必须逐行等于分录上的权威值(1.0.540 所写),不得另行推导
  66. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection` s
  67. JOIN `qms_qcp_insappnentry` e
  68. ON e.`id` = s.`application_entry_id` AND e.`tenant_id` = s.`tenant_id`
  69. WHERE IFNULL(s.`purchase_order_no`, '#') <> IFNULL(NULLIF(e.`purchase_order_no`, ''), '#')) = 0;
  70. -- ⑨c 收货单号同理
  71. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection` s
  72. JOIN `qms_qcp_insappnentry` e
  73. ON e.`id` = s.`application_entry_id` AND e.`tenant_id` = s.`tenant_id`
  74. WHERE IFNULL(s.`receipt_no`, '#') <> IFNULL(NULLIF(e.`FSRCORDERNUM`, ''), '#')) = 0;
  75. -- ── ⑩ 分录只能经 hid 单值外键解析,不得"从申请单下挑一条" ──────────────────────────
  76. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection` s
  77. JOIN `qms_qcp_inspbill` b ON b.`id` = s.`iqc_bill_id`
  78. WHERE IFNULL(s.`application_entry_id`, -1) <> IFNULL(b.`hid`, -1)) = 0;
  79. -- ── ⑪ 无采购身份的行必须如实为 NULL,禁止猜值 ──────────────────────────────────────
  80. -- 判据:分录本身没有采购单号时,标准层也必须是 NULL
  81. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection` s
  82. LEFT JOIN `qms_qcp_insappnentry` e
  83. ON e.`id` = s.`application_entry_id` AND e.`tenant_id` = s.`tenant_id`
  84. WHERE s.`purchase_order_no` IS NOT NULL
  85. AND (e.`id` IS NULL OR IFNULL(e.`purchase_order_no`, '') = '')) = 0;
  86. -- ── ⑫ Canonical 无重复:一张检验单只能一行 ─────────────────────────────────────────
  87. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`) =
  88. (SELECT COUNT(DISTINCT `tenant_id`, `iqc_bill_id`) FROM `mdp_std_iqc_inspection`);
  89. -- ── ⑬ 不得落字面量 'null' / 空串 ───────────────────────────────────────────────────
  90. SELECT (SELECT COUNT(*) FROM `mdp_std_iqc_inspection`
  91. WHERE `purchase_order_no` IN ('null','') OR `receipt_no` IN ('null','')
  92. OR `iqc_bill_no` IN ('null','') OR `material_code` IN ('null','')) = 0;
  93. -- ── ⑭ 本批不得建 Stage-3 聚合事实 / 完成事件 ───────────────────────────────────────
  94. SELECT (SELECT COUNT(*) FROM `information_schema`.`TABLES`
  95. WHERE `TABLE_SCHEMA` = DATABASE()
  96. AND `TABLE_NAME` IN ('dwd_iqc_completion','dwd_procurement_kpi','mdp_std_stage3_completion')) = 0;
  97. SELECT (SELECT COUNT(*) FROM `aidop_action_run_log`
  98. WHERE `action_code` = 'S3_PROCUREMENT_COMPLETE') = 0;
  99. -- ── ⑮ 上游批次的既有资产不得回归 ───────────────────────────────────────────────────
  100. -- 1.0.540:分录上的采购单号列仍在且仍可空
  101. SELECT (SELECT COUNT(*) FROM `information_schema`.`COLUMNS`
  102. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'qms_qcp_insappnentry'
  103. AND `COLUMN_NAME` = 'purchase_order_no' AND `IS_NULLABLE` = 'YES') = 1;
  104. -- 1.0.536:采购身份桥的订单行 Id 若有值仍须同租户命中
  105. SELECT (SELECT COUNT(*) FROM `mdp_std_purchase_request` p
  106. WHERE p.`sales_order_entry_id` IS NOT NULL
  107. AND NOT EXISTS (SELECT 1 FROM `mdp_std_so` s
  108. WHERE s.`tenant_id` = p.`tenant_id`
  109. AND s.`order_entry_id` = p.`sales_order_entry_id`)) = 0;