SmartOpsKpiAtomicBuildService.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. using Admin.NET.Core;
  2. using Admin.NET.Plugin.AiDOP.Infrastructure;
  3. using SqlSugar;
  4. namespace Admin.NET.Plugin.AiDOP.SmartOps;
  5. /// <summary>
  6. /// 从 DWD/STD 事实写入 KPI 原子聚合层(最小颗粒度,不做二次汇总)。
  7. /// </summary>
  8. public class SmartOpsKpiAtomicBuildService : ITransient
  9. {
  10. private readonly ISqlSugarClient _db;
  11. public SmartOpsKpiAtomicBuildService(ISqlSugarClient db)
  12. {
  13. _db = db;
  14. }
  15. public async Task<int> BuildOrderDeliveryDomainForAllDatesAsync(string? batchId, CancellationToken cancellationToken = default)
  16. {
  17. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  18. var dates = await _db.Ado.SqlQueryAsync<DateTime>(
  19. """
  20. SELECT DISTINCT DATE(order_date) AS D
  21. FROM mdp_std_so
  22. WHERE order_date IS NOT NULL
  23. ORDER BY D
  24. """);
  25. var total = 0;
  26. foreach (var date in dates)
  27. {
  28. cancellationToken.ThrowIfCancellationRequested();
  29. total += await BuildOrderDeliveryDomainAsync(date, batchId, cancellationToken);
  30. }
  31. return total;
  32. }
  33. public async Task<int> BuildWorkScheduleDomainForAllDatesAsync(string? batchId, CancellationToken cancellationToken = default)
  34. {
  35. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  36. var dates = await _db.Ado.SqlQueryAsync<DateTime>(
  37. """
  38. SELECT DISTINCT stat_date AS D
  39. FROM dwd_order_schedule_trans
  40. WHERE stat_date IS NOT NULL
  41. ORDER BY D
  42. """);
  43. var total = 0;
  44. foreach (var date in dates)
  45. {
  46. cancellationToken.ThrowIfCancellationRequested();
  47. total += await BuildWorkScheduleDomainAsync(date, batchId, cancellationToken);
  48. }
  49. return total;
  50. }
  51. public async Task<int> BuildSupplyPurchaseDomainForAllDatesAsync(string? batchId, CancellationToken cancellationToken = default)
  52. {
  53. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  54. var dates = await _db.Ado.SqlQueryAsync<DateTime>(
  55. """
  56. SELECT DISTINCT stat_date AS D
  57. FROM dwd_supplier_delivery
  58. WHERE stat_date IS NOT NULL
  59. ORDER BY D
  60. """);
  61. var total = 0;
  62. foreach (var date in dates)
  63. {
  64. cancellationToken.ThrowIfCancellationRequested();
  65. total += await BuildSupplyPurchaseDomainAsync(date, batchId, cancellationToken);
  66. }
  67. return total;
  68. }
  69. public async Task<int> BuildInventoryDomainForAllDatesAsync(string? batchId, CancellationToken cancellationToken = default)
  70. {
  71. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  72. var dates = await _db.Ado.SqlQueryAsync<DateTime>(
  73. """
  74. SELECT DISTINCT stat_date AS D
  75. FROM dwd_material_readiness
  76. WHERE stat_date IS NOT NULL
  77. ORDER BY D
  78. """);
  79. var total = 0;
  80. foreach (var date in dates)
  81. {
  82. cancellationToken.ThrowIfCancellationRequested();
  83. total += await BuildInventoryDomainAsync(date, batchId, cancellationToken);
  84. }
  85. return total;
  86. }
  87. public async Task<int> BuildOrderDeliveryDomainAsync(DateTime statDate, string? batchId, CancellationToken cancellationToken = default)
  88. {
  89. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  90. var date = statDate.Date;
  91. var now = DateTime.Now;
  92. var affected = 0;
  93. affected += await _db.Ado.ExecuteCommandAsync(
  94. """
  95. DELETE FROM ado_smart_ops_kpi_atomic_day
  96. WHERE domain_code = @Domain
  97. AND stat_date = @StatDate
  98. """,
  99. new { Domain = SmartOpsKpiAggregateRuleRegistry.DomainOrderDelivery, StatDate = date });
  100. foreach (var metricCode in new[] { "S1_L1_001", "S7_L1_001", "S9_L1_002" })
  101. {
  102. affected += await _db.Ado.ExecuteCommandAsync(
  103. """
  104. INSERT INTO ado_smart_ops_kpi_atomic_day
  105. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  106. customer_code, product_code, order_no, production_line,
  107. sum_value, sample_count, batch_id, calc_time, grain, is_deleted)
  108. SELECT
  109. so.tenant_id,
  110. COALESCE(NULLIF(so.factory_id, 0), 1),
  111. @Domain,
  112. @MetricCode,
  113. 1,
  114. DATE(so.order_date),
  115. IFNULL(so.customer_no, ''),
  116. IFNULL(so.item_code, ''),
  117. IFNULL(so.order_no, ''),
  118. '',
  119. ROUND(SUM(TIMESTAMPDIFF(HOUR, so.order_date,
  120. COALESCE(so.promised_delivery_date, so.capacity_date, so.material_ready_date, so.plan_delivery_date)) / 24), 6),
  121. COUNT(1),
  122. @BatchId,
  123. @Now,
  124. 'day',
  125. 0
  126. FROM mdp_std_so so
  127. WHERE so.order_date IS NOT NULL
  128. AND COALESCE(so.promised_delivery_date, so.capacity_date, so.material_ready_date, so.plan_delivery_date) IS NOT NULL
  129. AND COALESCE(so.promised_delivery_date, so.capacity_date, so.material_ready_date, so.plan_delivery_date) >= so.order_date
  130. AND DATE(so.order_date) = @StatDate
  131. GROUP BY so.tenant_id, COALESCE(NULLIF(so.factory_id, 0), 1),
  132. DATE(so.order_date), IFNULL(so.customer_no, ''), IFNULL(so.item_code, ''), IFNULL(so.order_no, '')
  133. """,
  134. new
  135. {
  136. Domain = SmartOpsKpiAggregateRuleRegistry.DomainOrderDelivery,
  137. MetricCode = metricCode,
  138. StatDate = date,
  139. BatchId = batchId,
  140. Now = now
  141. });
  142. }
  143. foreach (var metricCode in new[] { "S1_L1_002", "S7_L1_002", "S9_L1_003" })
  144. {
  145. affected += await _db.Ado.ExecuteCommandAsync(
  146. """
  147. INSERT INTO ado_smart_ops_kpi_atomic_day
  148. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  149. customer_code, product_code, order_no, production_line,
  150. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  151. SELECT
  152. so.tenant_id,
  153. COALESCE(NULLIF(so.factory_id, 0), 1),
  154. @Domain,
  155. @MetricCode,
  156. 1,
  157. DATE(so.order_date),
  158. IFNULL(so.customer_no, ''),
  159. IFNULL(so.item_code, ''),
  160. IFNULL(so.order_no, ''),
  161. '',
  162. SUM(CASE WHEN TIMESTAMPDIFF(HOUR, so.order_date,
  163. COALESCE(so.promised_delivery_date, so.capacity_date, so.material_ready_date, so.plan_delivery_date)) <= 72 THEN 1 ELSE 0 END),
  164. COUNT(1),
  165. @BatchId,
  166. @Now,
  167. 'day',
  168. 0
  169. FROM mdp_std_so so
  170. WHERE so.order_date IS NOT NULL
  171. AND COALESCE(so.promised_delivery_date, so.capacity_date, so.material_ready_date, so.plan_delivery_date) IS NOT NULL
  172. AND DATE(so.order_date) = @StatDate
  173. GROUP BY so.tenant_id, COALESCE(NULLIF(so.factory_id, 0), 1),
  174. DATE(so.order_date), IFNULL(so.customer_no, ''), IFNULL(so.item_code, ''), IFNULL(so.order_no, '')
  175. """,
  176. new
  177. {
  178. Domain = SmartOpsKpiAggregateRuleRegistry.DomainOrderDelivery,
  179. MetricCode = metricCode,
  180. StatDate = date,
  181. BatchId = batchId,
  182. Now = now
  183. });
  184. }
  185. foreach (var metricCode in new[] { "S1_L1_003", "S7_L1_003" })
  186. {
  187. affected += await _db.Ado.ExecuteCommandAsync(
  188. """
  189. INSERT INTO ado_smart_ops_kpi_atomic_day
  190. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  191. customer_code, product_code, order_no, production_line,
  192. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  193. SELECT
  194. so.tenant_id,
  195. COALESCE(NULLIF(so.factory_id, 0), 1),
  196. @Domain,
  197. @MetricCode,
  198. 1,
  199. DATE(so.order_date),
  200. IFNULL(so.customer_no, ''),
  201. IFNULL(so.item_code, ''),
  202. IFNULL(so.order_no, ''),
  203. '',
  204. COUNT(1),
  205. GREATEST(COUNT(DISTINCT NULLIF(so.planner_no, '')), 1),
  206. @BatchId,
  207. @Now,
  208. 'day',
  209. 0
  210. FROM mdp_std_so so
  211. WHERE so.order_date IS NOT NULL
  212. AND DATE(so.order_date) = @StatDate
  213. GROUP BY so.tenant_id, COALESCE(NULLIF(so.factory_id, 0), 1),
  214. DATE(so.order_date), IFNULL(so.customer_no, ''), IFNULL(so.item_code, ''), IFNULL(so.order_no, '')
  215. """,
  216. new
  217. {
  218. Domain = SmartOpsKpiAggregateRuleRegistry.DomainOrderDelivery,
  219. MetricCode = metricCode,
  220. StatDate = date,
  221. BatchId = batchId,
  222. Now = now
  223. });
  224. }
  225. return affected;
  226. }
  227. public async Task<int> BuildSupplyPurchaseDomainAsync(DateTime statDate, string? batchId, CancellationToken cancellationToken = default)
  228. {
  229. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  230. var date = statDate.Date;
  231. var now = DateTime.Now;
  232. var affected = 0;
  233. affected += await _db.Ado.ExecuteCommandAsync(
  234. """
  235. DELETE FROM ado_smart_ops_kpi_atomic_day
  236. WHERE domain_code = @Domain
  237. AND stat_date = @StatDate
  238. """,
  239. new { Domain = SmartOpsKpiAggregateRuleRegistry.DomainSupplyPurchase, StatDate = date });
  240. foreach (var metricCode in new[] { "S3_L1_001", "S4_L1_001" })
  241. {
  242. affected += await _db.Ado.ExecuteCommandAsync(
  243. """
  244. INSERT INTO ado_smart_ops_kpi_atomic_day
  245. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  246. supplier_code, material_code, po_no,
  247. sum_value, sample_count, batch_id, calc_time, grain, is_deleted)
  248. SELECT tenant_id, 1, @Domain, @MetricCode, 1, DATE(request_date),
  249. IFNULL(supplier_code, ''), IFNULL(item_code, ''), IFNULL(po_no, ''),
  250. ROUND(SUM(TIMESTAMPDIFF(HOUR, request_date, submit_date) / 24), 6),
  251. COUNT(1), @BatchId, @Now, 'day', 0
  252. FROM mdp_std_delivery_schedule
  253. WHERE request_date IS NOT NULL AND submit_date IS NOT NULL AND submit_date >= request_date
  254. AND DATE(request_date) = @StatDate
  255. GROUP BY tenant_id, DATE(request_date), IFNULL(supplier_code, ''), IFNULL(item_code, ''), IFNULL(po_no, '')
  256. """,
  257. new
  258. {
  259. Domain = SmartOpsKpiAggregateRuleRegistry.DomainSupplyPurchase,
  260. MetricCode = metricCode,
  261. StatDate = date,
  262. BatchId = batchId,
  263. Now = now
  264. });
  265. }
  266. foreach (var metricCode in new[] { "S4_L1_002", "S3_L1_002" })
  267. {
  268. affected += await _db.Ado.ExecuteCommandAsync(
  269. """
  270. INSERT INTO ado_smart_ops_kpi_atomic_day
  271. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  272. supplier_code, material_code, po_no,
  273. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  274. SELECT tenant_id, 1, @Domain, @MetricCode, 1, d.stat_date,
  275. IFNULL(d.supplier_code, ''), IFNULL(d.item_code, ''), IFNULL(d.po_no, ''),
  276. SUM(CASE WHEN IFNULL(d.receipt_qty, 0) >= IFNULL(d.order_qty, 0) AND IFNULL(d.order_qty, 0) > 0 THEN 1
  277. WHEN d.delivery_status = 'COMPLETED' THEN 1 ELSE 0 END),
  278. SUM(CASE WHEN IFNULL(d.order_qty, 0) > 0 THEN 1 ELSE 0 END),
  279. @BatchId, @Now, 'day', 0
  280. FROM dwd_supplier_delivery d
  281. WHERE d.stat_date = @StatDate
  282. GROUP BY d.tenant_id, d.stat_date, IFNULL(d.supplier_code, ''), IFNULL(d.item_code, ''), IFNULL(d.po_no, '')
  283. HAVING SUM(CASE WHEN IFNULL(d.order_qty, 0) > 0 THEN 1 ELSE 0 END) > 0
  284. """,
  285. new
  286. {
  287. Domain = SmartOpsKpiAggregateRuleRegistry.DomainSupplyPurchase,
  288. MetricCode = metricCode,
  289. StatDate = date,
  290. BatchId = batchId,
  291. Now = now
  292. });
  293. }
  294. foreach (var metricCode in new[] { "S4_L1_003", "S3_L1_003" })
  295. {
  296. affected += await _db.Ado.ExecuteCommandAsync(
  297. """
  298. INSERT INTO ado_smart_ops_kpi_atomic_day
  299. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  300. supplier_code, material_code, po_no,
  301. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  302. SELECT tenant_id, 1, @Domain, @MetricCode, 1, d.stat_date,
  303. IFNULL(d.supplier_code, ''), IFNULL(d.item_code, ''), IFNULL(d.po_no, ''),
  304. SUM(IFNULL(d.receipt_qty, 0)),
  305. GREATEST(COUNT(DISTINCT NULLIF(d.supplier_code, '')), 1),
  306. @BatchId, @Now, 'day', 0
  307. FROM dwd_supplier_delivery d
  308. WHERE d.stat_date = @StatDate
  309. GROUP BY d.tenant_id, d.stat_date, IFNULL(d.supplier_code, ''), IFNULL(d.item_code, ''), IFNULL(d.po_no, '')
  310. """,
  311. new
  312. {
  313. Domain = SmartOpsKpiAggregateRuleRegistry.DomainSupplyPurchase,
  314. MetricCode = metricCode,
  315. StatDate = date,
  316. BatchId = batchId,
  317. Now = now
  318. });
  319. }
  320. return affected;
  321. }
  322. public async Task<int> BuildWorkScheduleDomainAsync(DateTime statDate, string? batchId, CancellationToken cancellationToken = default)
  323. {
  324. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  325. var date = statDate.Date;
  326. var now = DateTime.Now;
  327. var affected = 0;
  328. var domain = SmartOpsKpiAggregateRuleRegistry.DomainWorkSchedule;
  329. affected += await _db.Ado.ExecuteCommandAsync(
  330. """
  331. DELETE FROM ado_smart_ops_kpi_atomic_day
  332. WHERE domain_code = @Domain AND stat_date = @StatDate
  333. """,
  334. new { Domain = domain, StatDate = date });
  335. foreach (var metricCode in new[] { "S2_L1_001", "S6_L1_001" })
  336. {
  337. affected += await _db.Ado.ExecuteCommandAsync(
  338. """
  339. INSERT INTO ado_smart_ops_kpi_atomic_day
  340. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  341. product_code, order_no, production_line, work_order_no,
  342. sum_value, sample_count, batch_id, calc_time, grain, is_deleted)
  343. SELECT tenant_id, factory_id, @Domain, @MetricCode, 1, stat_date,
  344. IFNULL(item_code, ''), IFNULL(sales_order_no, ''), IFNULL(prod_line, ''), IFNULL(work_order, ''),
  345. ROUND(SUM(schedule_cycle_days), 6), COUNT(1), @BatchId, @Now, 'day', 0
  346. FROM dwd_order_schedule_trans
  347. WHERE stat_date = @StatDate AND schedule_cycle_days IS NOT NULL AND schedule_cycle_days >= 0
  348. GROUP BY tenant_id, factory_id, stat_date, IFNULL(item_code, ''), IFNULL(sales_order_no, ''),
  349. IFNULL(prod_line, ''), IFNULL(work_order, '')
  350. """,
  351. new { Domain = domain, MetricCode = metricCode, StatDate = date, BatchId = batchId, Now = now });
  352. }
  353. foreach (var metricCode in new[] { "S2_L1_002", "S6_L1_002" })
  354. {
  355. affected += await _db.Ado.ExecuteCommandAsync(
  356. """
  357. INSERT INTO ado_smart_ops_kpi_atomic_day
  358. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  359. product_code, order_no, production_line, work_order_no,
  360. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  361. SELECT tenant_id, factory_id, @Domain, @MetricCode, 1, stat_date,
  362. IFNULL(item_code, ''), IFNULL(sales_order_no, ''), IFNULL(prod_line, ''), IFNULL(work_order, ''),
  363. SUM(IFNULL(schedule_satisfaction_flag, 0)), COUNT(1), @BatchId, @Now, 'day', 0
  364. FROM dwd_order_schedule_trans
  365. WHERE stat_date = @StatDate
  366. GROUP BY tenant_id, factory_id, stat_date, IFNULL(item_code, ''), IFNULL(sales_order_no, ''),
  367. IFNULL(prod_line, ''), IFNULL(work_order, '')
  368. """,
  369. new { Domain = domain, MetricCode = metricCode, StatDate = date, BatchId = batchId, Now = now });
  370. }
  371. foreach (var metricCode in new[] { "S2_L1_003", "S6_L1_003", "S9_L1_004" })
  372. {
  373. affected += await _db.Ado.ExecuteCommandAsync(
  374. """
  375. INSERT INTO ado_smart_ops_kpi_atomic_day
  376. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  377. product_code, order_no, production_line, work_order_no,
  378. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  379. SELECT tenant_id, factory_id, @Domain, @MetricCode, 1, stat_date,
  380. IFNULL(item_code, ''), IFNULL(sales_order_no, ''), IFNULL(prod_line, ''), IFNULL(work_order, ''),
  381. COUNT(1), GREATEST(SUM(CASE WHEN IFNULL(resource_person_count, 0) > 0 THEN resource_person_count ELSE 1 END), 1),
  382. @BatchId, @Now, 'day', 0
  383. FROM dwd_order_schedule_trans
  384. WHERE stat_date = @StatDate
  385. GROUP BY tenant_id, factory_id, stat_date, IFNULL(item_code, ''), IFNULL(sales_order_no, ''),
  386. IFNULL(prod_line, ''), IFNULL(work_order, '')
  387. """,
  388. new { Domain = domain, MetricCode = metricCode, StatDate = date, BatchId = batchId, Now = now });
  389. }
  390. return affected;
  391. }
  392. public async Task<int> BuildInventoryDomainAsync(DateTime statDate, string? batchId, CancellationToken cancellationToken = default)
  393. {
  394. AidopTenantMigration.EnsureKpiAtomicTable(_db);
  395. var date = statDate.Date;
  396. var now = DateTime.Now;
  397. var affected = 0;
  398. var domain = SmartOpsKpiAggregateRuleRegistry.DomainInventory;
  399. affected += await _db.Ado.ExecuteCommandAsync(
  400. """
  401. DELETE FROM ado_smart_ops_kpi_atomic_day
  402. WHERE domain_code = @Domain AND stat_date = @StatDate
  403. """,
  404. new { Domain = domain, StatDate = date });
  405. affected += await _db.Ado.ExecuteCommandAsync(
  406. """
  407. INSERT INTO ado_smart_ops_kpi_atomic_day
  408. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  409. warehouse_code, material_code, work_order_no,
  410. sum_value, sample_count, batch_id, calc_time, grain, is_deleted)
  411. SELECT tenant_id, 1, @Domain, 'S5_L1_001', 1, stat_date,
  412. '', IFNULL(component_item_code, ''), IFNULL(work_order, ''),
  413. ROUND(SUM(GREATEST(TIMESTAMPDIFF(DAY, stat_date, IFNULL(need_date, stat_date)), 0)), 6),
  414. COUNT(1), @BatchId, @Now, 'day', 0
  415. FROM dwd_material_readiness
  416. WHERE stat_date = @StatDate
  417. GROUP BY tenant_id, stat_date, IFNULL(component_item_code, ''), IFNULL(work_order, '')
  418. """,
  419. new { Domain = domain, StatDate = date, BatchId = batchId, Now = now });
  420. affected += await _db.Ado.ExecuteCommandAsync(
  421. """
  422. INSERT INTO ado_smart_ops_kpi_atomic_day
  423. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  424. warehouse_code, material_code, work_order_no,
  425. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  426. SELECT tenant_id, 1, @Domain, 'S5_L1_002', 1, stat_date,
  427. '', IFNULL(component_item_code, ''), IFNULL(work_order, ''),
  428. SUM(CASE WHEN IFNULL(shortage_qty, 0) <= 0 OR UPPER(IFNULL(ready_status, '')) IN ('READY', 'SUFFICIENT', 'OK') THEN 1 ELSE 0 END),
  429. COUNT(1), @BatchId, @Now, 'day', 0
  430. FROM dwd_material_readiness
  431. WHERE stat_date = @StatDate
  432. GROUP BY tenant_id, stat_date, IFNULL(component_item_code, ''), IFNULL(work_order, '')
  433. """,
  434. new { Domain = domain, StatDate = date, BatchId = batchId, Now = now });
  435. affected += await _db.Ado.ExecuteCommandAsync(
  436. """
  437. INSERT INTO ado_smart_ops_kpi_atomic_day
  438. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  439. warehouse_code, material_code, work_order_no,
  440. numerator_value, denominator_value, batch_id, calc_time, grain, is_deleted)
  441. SELECT tenant_id, 1, @Domain, 'S5_L1_003', 1, stat_date,
  442. '', IFNULL(component_item_code, ''), IFNULL(work_order, ''),
  443. COUNT(1), GREATEST(COUNT(DISTINCT NULLIF(supplier_code, '')), 1),
  444. @BatchId, @Now, 'day', 0
  445. FROM dwd_material_readiness
  446. WHERE stat_date = @StatDate
  447. GROUP BY tenant_id, stat_date, IFNULL(component_item_code, ''), IFNULL(work_order, '')
  448. """,
  449. new { Domain = domain, StatDate = date, BatchId = batchId, Now = now });
  450. affected += await _db.Ado.ExecuteCommandAsync(
  451. """
  452. INSERT INTO ado_smart_ops_kpi_atomic_day
  453. (tenant_id, factory_id, domain_code, metric_code, metric_level, stat_date,
  454. warehouse_code, material_code, work_order_no,
  455. sum_value, sample_count, batch_id, calc_time, grain, is_deleted)
  456. SELECT tenant_id, 1, @Domain, 'S9_L1_005', 1, stat_date,
  457. '', IFNULL(component_item_code, ''), IFNULL(work_order, ''),
  458. ROUND(SUM(GREATEST(IFNULL(shortage_qty, 0), 0)), 6),
  459. COUNT(1), @BatchId, @Now, 'day', 0
  460. FROM dwd_material_readiness
  461. WHERE stat_date = @StatDate
  462. GROUP BY tenant_id, stat_date, IFNULL(component_item_code, ''), IFNULL(work_order, '')
  463. """,
  464. new { Domain = domain, StatDate = date, BatchId = batchId, Now = now });
  465. return affected;
  466. }
  467. }