S8OrderDeliveryDatasetDefinitions.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers;
  2. /// <summary>
  3. /// S8-RULE02-ORDER-DELIVERY-WARNING-1:S1 销售订单行的<b>交付延期风险</b>数据集。
  4. ///
  5. /// <para><b>它衡量的是什么</b>:把「客户要求交期」与「系统按产能算出来的建议交期」放在同一行上,
  6. /// 二者之差(天)即 <c>measured_value</c>。差值为正 = 系统自己认为这一行会晚。</para>
  7. ///
  8. /// <para><b>ETA 不是本数据集算的。</b><c>sys_capacity_date</c> 由 S1 的
  9. /// <c>OrderReviewOrchestrationService</c> 在订单评审做完资源检查后写入(来源是齐套时间),
  10. /// 本 Provider 只把它取出来做差。<b>没有这个字段的订单行一律不进结果集</b> ——
  11. /// 那表示该行还没有过可用的资源检查,没有 ETA 证据,不能靠"离交期还有几天"凑一个预警出来。</para>
  12. ///
  13. /// <para><b>粒度是订单行</b>:真实交期(<c>plan_date</c>)与数量(<c>qty</c>)都挂在
  14. /// <c>crm_seorderentry</c> 上,主表 <c>crm_seorder</c> 没有交期字段。
  15. /// 一张订单可能有多行、各行交期不同,按主单聚合会把风险行淹掉。</para>
  16. ///
  17. /// <para><b>已知能力边界(勿当缺陷"修")</b>:真库 390 行订单行中只有 40 行有
  18. /// <c>sys_capacity_date</c>(10.3%)—— 只有走过评审资源检查的订单才有 ETA。
  19. /// 这是覆盖面窄,不是数据质量问题;<b>不得</b>为了提高覆盖率改用
  20. /// <c>plan_date - N 天</c>、<c>WorkOrdMaster.QtyCompleted</c>(实测 267 行全为 0)
  21. /// 或任何自造 ETA 来补 —— 那会把"临期提醒"伪装成"延期预测"。</para>
  22. /// </summary>
  23. public sealed class S8OrderDeliveryDatasetDefinitions : IS8DatasetDefinitionSource
  24. {
  25. /// <summary>数据集编码。与 <see cref="S8OrderDeliveryDataProvider.DatasetCode"/> 必须一致。</summary>
  26. public const string OrderDeliveryRiskCode = "ORDER_DELIVERY_RISK";
  27. /// <summary>
  28. /// 能力声明。
  29. /// <para><b>只开 OutOfRange</b>:本数据集的判据是「两个日期之差越界」,
  30. /// 不是「到期日已过」(那是 TIMEOUT),也不是「数量缺口」(那是 SHORTAGE)。
  31. /// 多开一项能力会让配置页把不成立的规则类型也列成可选。</para>
  32. /// <para><c>HasDueAt = true</c> 仍然为真 —— 客户要求交期会随行输出,
  33. /// 供异常展示与排序使用,但它<b>不</b>参与命中判定。</para>
  34. /// </summary>
  35. internal static readonly S8DatasetCapabilities OrderDeliveryRiskCapabilities = new()
  36. {
  37. SupportsTimeout = false,
  38. SupportsShortage = false,
  39. SupportsOutOfRange = true,
  40. HasSourceObjectId = true,
  41. HasRelatedObjectCode = true,
  42. HasDueAt = true,
  43. HasStatus = false,
  44. HasMeasuredValue = true,
  45. HasTargetQty = false,
  46. HasActualQty = false
  47. };
  48. public IEnumerable<S8DatasetDefinition> GetDefinitions() => new[]
  49. {
  50. new S8DatasetDefinition
  51. {
  52. DatasetCode = OrderDeliveryRiskCode,
  53. DisplayName = "订单交付风险(订单行级)",
  54. Kind = S8DatasetKind.Object,
  55. Status = S8DatasetStatus.Defined,
  56. Capabilities = OrderDeliveryRiskCapabilities,
  57. // mdp_std_so 是当前态镜像:upsert 覆盖、没有「发布/翻牌」这一步,
  58. // 读者(本数据集的 Provider)也从不按批次取数 ——
  59. // 表里因此不存在一个「与本轮是否完成相关、且可能被破坏」的表级状态,
  60. // 健康度完全由生产运行本身决定,不加表级不变量。
  61. // (唯一的候选「每租户单一 sync_batch_id」既非必要也非充分:
  62. // 它今天恒为真只是孤儿行被反复盖上当前批次戳的副产物,
  63. // 而最新批次过滤生效后它会合法地变成 false。)
  64. AuthoritySpec = new Health.S8AuthoritySpec
  65. {
  66. ProducerJobCode = "S1_MDP_SYNC_TRANSFORM",
  67. AuthorityKind = Health.S8AuthorityKind.CurrentStateMirror
  68. },
  69. Description =
  70. "来源 crm_seorderentry(订单行)左连 crm_seorder(主单);粒度为订单行" +
  71. "(bill_no + entry_seq,实测 390/390 唯一);" +
  72. "measured_value = DATEDIFF(sys_capacity_date, plan_date),即系统建议交期晚于客户要求交期的天数;" +
  73. "plan_date(客户要求交期)实测 100% 非空,是唯一可靠的 DueDate;" +
  74. "sys_capacity_date(系统建议交期)由订单评审资源检查写入,实测覆盖 40/390。" +
  75. "结果集含「未关闭、未删除、且已有 ETA」的全部订单行,包含已过交期的 —— " +
  76. "是否延期由规则判定,是否允许首次立案由规则的立案窗口声明,都不在取数层。"
  77. }
  78. };
  79. }