-- 1.0.106.sql
-- S8-ORDER-FLOW-KPI-OVERTIME-EXCEPTION-SEED-1：派生 ORDER_FLOW KPI 超期阶段为 ado_s8_exception 事件。
-- 解决问题：order-execution 页面 yellow/red 阶段已是业务事实，但未进入 ado_s8_exception，
--           导致 S8 overview / delivery / production / supply / exception list / dept serious card 空态。
--           本脚本以幂等方式：(A) 新增 3 条阶段缺口 type；(B) 派生 16 条 exception；(C) 派生 16 条 timeline。
-- 执行入口：AutoVersionUpdate.UseAutoVersionUpdate()，在 csproj Version=1.0.106 主节点首次启动时触发。
-- 幂等性：
--   - exception_type：唯一键 (tenant_id, factory_id, type_code) + ON DUPLICATE KEY UPDATE
--   - exception：唯一键 (tenant_id, factory_id, exception_code) + ON DUPLICATE KEY UPDATE
--   - timeline：无唯一键 → INSERT ... SELECT WHERE NOT EXISTS 防重
-- 安全边界：
--   - 不改 ado_s8_order_flow_stage / 不改 order-execution UI / 不启用 watch_rule
--   - 不写 is_deleted 到 exception_type 与 timeline（实证表无该列）
--   - exception 派生的 created_at 用 LEAST(COALESCE(actual_end_at, target_at, NOW()), NOW())
--     防止 BODY_PRODUCTION / FINAL_ASSEMBLY_DELIVERY 阶段 actual_end_at 晚于 DB NOW 制造未来异常
-- 拍板依据：S8-ORDER-FLOW-KPI-OVERTIME-SYNC-DECISION-1 回执（2026-05-14）
-- 2026-05-14


-- =====================================================================
-- A) 3 条 exception_type idempotent upsert
-- =====================================================================
-- 字段映射来源：S8-ORDER-FLOW-KPI-OVERTIME-SYNC-DECISION-1 §3.2
-- 选 id：baseId(1329908100000) + 46/47/48；DB 已确认空闲，且与 PARENT_*(100..104) 不冲突
INSERT INTO ado_s8_exception_type
  (id, tenant_id, factory_id, type_code, type_name, scene_code, severity_default, sla_minutes,
   owner_role_code, escalate_role_code, stats_mode, monitoring_category_key, mobile_visible, icon,
   enabled, sort_no, remark, parent_id, is_group, created_at, updated_at)
VALUES
  (1329908100046, 0, 0, 'PRODUCT_DESIGN_DELAY',          '产品设计延期',     'S1', 'SERIOUS', 240,
   'ROLE_ORDER_PLANNER', 'ROLE_ORDER_PLANNER', 'ALL', 'PRODUCT_DESIGN',          1, NULL,
   1, 113, '[S8-ORDER-FLOW-KPI-OVERTIME-SEED-1] 产品设计阶段 KPI 超期；ORDER_FLOW 派生事件使用。',
   1329908100101, 0, '2026-05-14 00:00:00', NULL),

  (1329908100047, 0, 0, 'BODY_PRODUCTION_DELAY_WARNING', '本体生产延期预警', 'S6', 'FOLLOW',  120,
   'ROLE_PRODUCTION_PLANNER', 'ROLE_PRODUCTION_PLANNER', 'ALL', 'BODY_PRODUCTION',     1, NULL,
   1, 614, '[S8-ORDER-FLOW-KPI-OVERTIME-SEED-1] 本体生产阶段 KPI 超期预警；ORDER_FLOW 派生事件使用。',
   1329908100103, 0, '2026-05-14 00:00:00', NULL),

  (1329908100048, 0, 0, 'DELIVERY_DELAY_WARNING',        '总装发货延期预警', 'S7', 'FOLLOW',  240,
   'ROLE_WH_OUTBOUND', 'ROLE_WH_OUTBOUND', 'ALL', 'FINAL_ASSEMBLY_DELIVERY',           1, NULL,
   1, 713, '[S8-ORDER-FLOW-KPI-OVERTIME-SEED-1] 总装发货阶段 KPI 超期预警；ORDER_FLOW 派生事件使用。',
   1329908100104, 0, '2026-05-14 00:00:00', NULL)
ON DUPLICATE KEY UPDATE
  type_name               = VALUES(type_name),
  scene_code              = VALUES(scene_code),
  severity_default        = VALUES(severity_default),
  sla_minutes             = VALUES(sla_minutes),
  owner_role_code         = VALUES(owner_role_code),
  escalate_role_code      = VALUES(escalate_role_code),
  stats_mode              = VALUES(stats_mode),
  monitoring_category_key = VALUES(monitoring_category_key),
  mobile_visible          = VALUES(mobile_visible),
  enabled                 = VALUES(enabled),
  sort_no                 = VALUES(sort_no),
  remark                  = VALUES(remark),
  parent_id               = VALUES(parent_id),
  is_group                = VALUES(is_group),
  updated_at              = NOW();


-- =====================================================================
-- B) 16 条 ado_s8_exception 派生 upsert
-- =====================================================================
-- 数据来源：ado_s8_order_flow_stage（status IN 'yellow'/'red'）+ ado_s8_order_flow_order JOIN
-- 幂等键：(tenant_id=1, factory_id=1, exception_code='EX-DRV-{order_code}-{flow_short}')
INSERT INTO ado_s8_exception
  (tenant_id, factory_id, exception_code, title, description,
   scene_code, source_type, status, severity, priority_score, priority_level,
   occurrence_dept_id, responsible_dept_id, responsible_group_id, assignee_id, reporter_id,
   sla_deadline, timeout_flag, created_at, is_deleted, created_by,
   process_node_code, related_object_code, module_code,
   active_flow_instance_id, active_flow_biz_type,
   exception_type_code, source_data_source_id, source_rule_id, source_payload,
   dedup_key, last_detected_at, source_rule_code, source_object_type, source_object_id,
   consecutive_hit_count, consecutive_miss_count,
   stage_code, order_flow_code, rule_mechanism)
SELECT
  1                                                       AS tenant_id,
  1                                                       AS factory_id,
  CONCAT('EX-DRV-', s.order_code, '-',
    CASE s.order_flow_code
      WHEN 'ORDER_REVIEW_PLAN_CALC'    THEN 'OR'
      WHEN 'PRODUCT_DESIGN'            THEN 'PD'
      WHEN 'MATERIAL_PURCHASE'         THEN 'MP'
      WHEN 'BODY_PRODUCTION'           THEN 'BP'
      WHEN 'FINAL_ASSEMBLY_DELIVERY'   THEN 'FA'
    END)                                                  AS exception_code,
  CASE
    WHEN s.order_code = 'SO-2026-001' AND s.order_flow_code = 'MATERIAL_PURCHASE'
      THEN '订单 SO-2026-001 材料采购阶段累计偏差关注（节点本身未超 KPI）'
    ELSE CONCAT('订单 ', s.order_code, ' ',
      CASE s.order_flow_code
        WHEN 'ORDER_REVIEW_PLAN_CALC'  THEN '评审/排产/测算'
        WHEN 'PRODUCT_DESIGN'          THEN '产品设计'
        WHEN 'MATERIAL_PURCHASE'       THEN '材料采购'
        WHEN 'BODY_PRODUCTION'         THEN '本体生产'
        WHEN 'FINAL_ASSEMBLY_DELIVERY' THEN '总装发货'
      END,
      ' 超 KPI ', CAST(s.node_variance_days AS CHAR), ' 天')
  END                                                     AS title,
  NULL                                                    AS description,
  CASE s.order_flow_code
    WHEN 'ORDER_REVIEW_PLAN_CALC'    THEN 'S1'
    WHEN 'PRODUCT_DESIGN'            THEN 'S1'
    WHEN 'MATERIAL_PURCHASE'         THEN 'S4'
    WHEN 'BODY_PRODUCTION'           THEN 'S6'
    WHEN 'FINAL_ASSEMBLY_DELIVERY'   THEN 'S7'
  END                                                     AS scene_code,
  'AUTO_WATCH'                                            AS source_type,
  'NEW'                                                   AS status,
  CASE s.status WHEN 'red' THEN 'SERIOUS' WHEN 'yellow' THEN 'FOLLOW' END
                                                          AS severity,
  0                                                       AS priority_score,
  'P3'                                                    AS priority_level,
  CASE WHEN s.order_flow_code IN ('BODY_PRODUCTION','FINAL_ASSEMBLY_DELIVERY')
       THEN 2 ELSE 10 END                                 AS occurrence_dept_id,
  CASE WHEN s.order_flow_code IN ('BODY_PRODUCTION','FINAL_ASSEMBLY_DELIVERY')
       THEN 2 ELSE 10 END                                 AS responsible_dept_id,
  NULL                                                    AS responsible_group_id,
  NULL                                                    AS assignee_id,
  NULL                                                    AS reporter_id,
  DATE_ADD(LEAST(COALESCE(s.actual_end_at, s.target_at, NOW()), NOW()),
           INTERVAL et.sla_minutes MINUTE)                AS sla_deadline,
  0                                                       AS timeout_flag,
  LEAST(COALESCE(s.actual_end_at, s.target_at, NOW()), NOW())
                                                          AS created_at,
  0                                                       AS is_deleted,
  'system:order_flow_kpi_derive'                          AS created_by,
  NULL                                                    AS process_node_code,
  s.order_code                                            AS related_object_code,
  CASE s.order_flow_code
    WHEN 'ORDER_REVIEW_PLAN_CALC'    THEN 'S1'
    WHEN 'PRODUCT_DESIGN'            THEN 'S1'
    WHEN 'MATERIAL_PURCHASE'         THEN 'S4'
    WHEN 'BODY_PRODUCTION'           THEN 'S6'
    WHEN 'FINAL_ASSEMBLY_DELIVERY'   THEN 'S7'
  END                                                     AS module_code,
  NULL                                                    AS active_flow_instance_id,
  NULL                                                    AS active_flow_biz_type,
  et.type_code                                            AS exception_type_code,
  NULL                                                    AS source_data_source_id,
  NULL                                                    AS source_rule_id,
  JSON_OBJECT(
    'source',                 'ORDER_FLOW_KPI_OVERTIME_DERIVED',
    'orderCode',              s.order_code,
    'orderId',                o.id,
    'orderFlowCode',          s.order_flow_code,
    'status',                 s.status,
    'plannedDays',            s.planned_days,
    'actualDays',             s.actual_days,
    'nodeVarianceDays',       s.node_variance_days,
    'cumulativeVarianceDays', s.cumulative_variance_days,
    'targetAt',               DATE_FORMAT(s.target_at,      '%Y-%m-%d %H:%i:%s'),
    'actualEndAt',            DATE_FORMAT(s.actual_end_at,  '%Y-%m-%d %H:%i:%s')
  )                                                       AS source_payload,
  CONCAT('ORDER_FLOW_KPI_OVERTIME:', s.order_code, ':', s.order_flow_code)
                                                          AS dedup_key,
  LEAST(COALESCE(s.actual_end_at, s.target_at, NOW()), NOW())
                                                          AS last_detected_at,
  'ORDER_FLOW_KPI_OVERTIME_DERIVED'                       AS source_rule_code,
  'SALES_ORDER'                                           AS source_object_type,
  CAST(o.id AS CHAR)                                      AS source_object_id,
  1                                                       AS consecutive_hit_count,
  0                                                       AS consecutive_miss_count,
  CASE s.order_flow_code
    WHEN 'ORDER_REVIEW_PLAN_CALC'    THEN 'S1'
    WHEN 'PRODUCT_DESIGN'            THEN 'S1'
    WHEN 'MATERIAL_PURCHASE'         THEN 'S4'
    WHEN 'BODY_PRODUCTION'           THEN 'S6'
    WHEN 'FINAL_ASSEMBLY_DELIVERY'   THEN 'S7'
  END                                                     AS stage_code,
  s.order_flow_code                                       AS order_flow_code,
  'DATE'                                                  AS rule_mechanism
FROM ado_s8_order_flow_stage s
JOIN ado_s8_order_flow_order o
  ON o.id = s.order_id
 AND o.is_deleted = 0
 AND o.tenant_id = 1
 AND o.factory_id = 1
JOIN ado_s8_exception_type et
  ON et.tenant_id = 0
 AND et.factory_id = 0
 AND et.is_group = 0
 AND et.enabled = 1
 AND et.type_code =
   CASE s.order_flow_code
     WHEN 'ORDER_REVIEW_PLAN_CALC'    THEN 'ORDER_DUE_DATE_DELAY'
     WHEN 'PRODUCT_DESIGN'            THEN 'PRODUCT_DESIGN_DELAY'
     WHEN 'MATERIAL_PURCHASE'         THEN 'PURCHASE_EXECUTION_DELAY'
     WHEN 'BODY_PRODUCTION'           THEN 'BODY_PRODUCTION_DELAY_WARNING'
     WHEN 'FINAL_ASSEMBLY_DELIVERY'   THEN 'DELIVERY_DELAY_WARNING'
   END
WHERE s.is_deleted = 0
  AND s.tenant_id  = 1
  AND s.factory_id = 1
  AND s.status IN ('yellow','red')
ON DUPLICATE KEY UPDATE
  title               = VALUES(title),
  scene_code          = VALUES(scene_code),
  module_code         = VALUES(module_code),
  stage_code          = VALUES(stage_code),
  order_flow_code     = VALUES(order_flow_code),
  exception_type_code = VALUES(exception_type_code),
  severity            = VALUES(severity),
  source_payload      = VALUES(source_payload),
  last_detected_at    = VALUES(last_detected_at),
  source_rule_code    = VALUES(source_rule_code),
  source_object_type  = VALUES(source_object_type),
  source_object_id    = VALUES(source_object_id),
  related_object_code = VALUES(related_object_code),
  dedup_key           = VALUES(dedup_key),
  rule_mechanism      = VALUES(rule_mechanism),
  updated_at          = NOW();


-- =====================================================================
-- C) 16 条 ado_s8_exception_timeline 派生（NOT EXISTS 防重）
-- =====================================================================
-- timeline 表无唯一键，不能使用 ON DUPLICATE KEY UPDATE；使用 NOT EXISTS 子查询防重。
-- 关联键：exception_id（每个 source_rule_code='ORDER_FLOW_KPI_OVERTIME_DERIVED' 的 exception 同步 1 条 CREATE）
INSERT INTO ado_s8_exception_timeline
  (exception_id, action_code, action_label, from_status, to_status,
   operator_id, operator_name, action_remark, created_at)
SELECT
  e.id,
  'CREATE',
  '派生',
  NULL,
  'NEW',
  NULL,
  'system',
  'ORDER_FLOW KPI 超期派生',
  e.created_at
FROM ado_s8_exception e
WHERE e.tenant_id  = 1
  AND e.factory_id = 1
  AND e.is_deleted = 0
  AND e.source_rule_code = 'ORDER_FLOW_KPI_OVERTIME_DERIVED'
  AND NOT EXISTS (
    SELECT 1
    FROM ado_s8_exception_timeline t
    WHERE t.exception_id = e.id
      AND t.action_code  = 'CREATE'
      AND t.action_remark = 'ORDER_FLOW KPI 超期派生'
  );


-- =====================================================================
-- 验收预期（阶段 4 跑此 SQL 后核对）
-- =====================================================================
-- 1) ado_s8_exception_type 新增 3 条（如已存在则 ON DUPLICATE 刷新 updated_at）
-- 2) ado_s8_exception 新增 16 条（red 6 + yellow 10，含 SO-2026-001 MATERIAL_PURCHASE 累计偏差边界项）
-- 3) ado_s8_exception_timeline 新增 16 条 CREATE 行
-- 4) 重跑此 SQL：A 段 ON DUPLICATE 0 row changed；B 段 ON DUPLICATE 0 row changed；C 段 NOT EXISTS 0 row affected
