dabke 0.82.0 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +22 -24
  3. package/dist/client.d.ts +39 -3
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js.map +1 -1
  6. package/dist/client.schemas.d.ts +118 -18
  7. package/dist/client.schemas.d.ts.map +1 -1
  8. package/dist/client.schemas.js +110 -6
  9. package/dist/client.schemas.js.map +1 -1
  10. package/dist/client.types.d.ts +63 -7
  11. package/dist/client.types.d.ts.map +1 -1
  12. package/dist/client.types.js.map +1 -1
  13. package/dist/cpsat/cost.d.ts +5 -2
  14. package/dist/cpsat/cost.d.ts.map +1 -1
  15. package/dist/cpsat/cost.js +13 -6
  16. package/dist/cpsat/cost.js.map +1 -1
  17. package/dist/cpsat/model-builder.d.ts +42 -61
  18. package/dist/cpsat/model-builder.d.ts.map +1 -1
  19. package/dist/cpsat/model-builder.js +451 -56
  20. package/dist/cpsat/model-builder.js.map +1 -1
  21. package/dist/cpsat/response.d.ts +19 -7
  22. package/dist/cpsat/response.d.ts.map +1 -1
  23. package/dist/cpsat/response.js +48 -9
  24. package/dist/cpsat/response.js.map +1 -1
  25. package/dist/cpsat/rule-descriptor.d.ts +250 -0
  26. package/dist/cpsat/rule-descriptor.d.ts.map +1 -0
  27. package/dist/cpsat/rule-descriptor.js +51 -0
  28. package/dist/cpsat/rule-descriptor.js.map +1 -0
  29. package/dist/cpsat/rules/artifacts.d.ts +9 -0
  30. package/dist/cpsat/rules/artifacts.d.ts.map +1 -0
  31. package/dist/cpsat/rules/artifacts.js +31 -0
  32. package/dist/cpsat/rules/artifacts.js.map +1 -0
  33. package/dist/cpsat/rules/assign-together.d.ts +18 -20
  34. package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
  35. package/dist/cpsat/rules/assign-together.js +90 -54
  36. package/dist/cpsat/rules/assign-together.js.map +1 -1
  37. package/dist/cpsat/rules/assigned-day.d.ts +12 -0
  38. package/dist/cpsat/rules/assigned-day.d.ts.map +1 -0
  39. package/dist/cpsat/rules/assigned-day.js +43 -0
  40. package/dist/cpsat/rules/assigned-day.js.map +1 -0
  41. package/dist/cpsat/rules/assignment-priority.d.ts +15 -29
  42. package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -1
  43. package/dist/cpsat/rules/assignment-priority.js +47 -50
  44. package/dist/cpsat/rules/assignment-priority.js.map +1 -1
  45. package/dist/cpsat/rules/assignment-terms.d.ts +6 -0
  46. package/dist/cpsat/rules/assignment-terms.d.ts.map +1 -0
  47. package/dist/cpsat/rules/assignment-terms.js +19 -0
  48. package/dist/cpsat/rules/assignment-terms.js.map +1 -0
  49. package/dist/cpsat/rules/concurrent-intervals.d.ts +19 -0
  50. package/dist/cpsat/rules/concurrent-intervals.d.ts.map +1 -0
  51. package/dist/cpsat/rules/concurrent-intervals.js +57 -0
  52. package/dist/cpsat/rules/concurrent-intervals.js.map +1 -0
  53. package/dist/cpsat/rules/cost-utils.d.ts +1 -3
  54. package/dist/cpsat/rules/cost-utils.d.ts.map +1 -1
  55. package/dist/cpsat/rules/cost-utils.js +0 -7
  56. package/dist/cpsat/rules/cost-utils.js.map +1 -1
  57. package/dist/cpsat/rules/day-cost-multiplier.d.ts +10 -10
  58. package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
  59. package/dist/cpsat/rules/day-cost-multiplier.js +88 -77
  60. package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
  61. package/dist/cpsat/rules/day-cost-surcharge.d.ts +10 -10
  62. package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
  63. package/dist/cpsat/rules/day-cost-surcharge.js +76 -65
  64. package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
  65. package/dist/cpsat/rules/index.d.ts +30 -21
  66. package/dist/cpsat/rules/index.d.ts.map +1 -1
  67. package/dist/cpsat/rules/index.js +30 -21
  68. package/dist/cpsat/rules/index.js.map +1 -1
  69. package/dist/cpsat/rules/location-preference.d.ts +14 -21
  70. package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
  71. package/dist/cpsat/rules/location-preference.js +45 -45
  72. package/dist/cpsat/rules/location-preference.js.map +1 -1
  73. package/dist/cpsat/rules/max-concurrent-assignments.d.ts +46 -0
  74. package/dist/cpsat/rules/max-concurrent-assignments.d.ts.map +1 -0
  75. package/dist/cpsat/rules/max-concurrent-assignments.js +121 -0
  76. package/dist/cpsat/rules/max-concurrent-assignments.js.map +1 -0
  77. package/dist/cpsat/rules/max-consecutive-days.d.ts +18 -17
  78. package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
  79. package/dist/cpsat/rules/max-consecutive-days.js +67 -66
  80. package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
  81. package/dist/cpsat/rules/max-days-of-week-per-period.d.ts +45 -0
  82. package/dist/cpsat/rules/max-days-of-week-per-period.d.ts.map +1 -0
  83. package/dist/cpsat/rules/max-days-of-week-per-period.js +95 -0
  84. package/dist/cpsat/rules/max-days-of-week-per-period.js.map +1 -0
  85. package/dist/cpsat/rules/max-days-week.d.ts +30 -0
  86. package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
  87. package/dist/cpsat/rules/max-days-week.js +83 -0
  88. package/dist/cpsat/rules/max-days-week.js.map +1 -0
  89. package/dist/cpsat/rules/max-hours-day.d.ts +14 -31
  90. package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
  91. package/dist/cpsat/rules/max-hours-day.js +56 -72
  92. package/dist/cpsat/rules/max-hours-day.js.map +1 -1
  93. package/dist/cpsat/rules/max-hours-week.d.ts +15 -31
  94. package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
  95. package/dist/cpsat/rules/max-hours-week.js +60 -75
  96. package/dist/cpsat/rules/max-hours-week.js.map +1 -1
  97. package/dist/cpsat/rules/max-shifts-day.d.ts +17 -33
  98. package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
  99. package/dist/cpsat/rules/max-shifts-day.js +57 -73
  100. package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
  101. package/dist/cpsat/rules/min-consecutive-days.d.ts +18 -18
  102. package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
  103. package/dist/cpsat/rules/min-consecutive-days.js +123 -94
  104. package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
  105. package/dist/cpsat/rules/min-days-of-week-per-period.d.ts +35 -0
  106. package/dist/cpsat/rules/min-days-of-week-per-period.d.ts.map +1 -0
  107. package/dist/cpsat/rules/min-days-of-week-per-period.js +108 -0
  108. package/dist/cpsat/rules/min-days-of-week-per-period.js.map +1 -0
  109. package/dist/cpsat/rules/min-days-week.d.ts +21 -0
  110. package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
  111. package/dist/cpsat/rules/min-days-week.js +95 -0
  112. package/dist/cpsat/rules/min-days-week.js.map +1 -0
  113. package/dist/cpsat/rules/min-hours-day.d.ts +14 -17
  114. package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
  115. package/dist/cpsat/rules/min-hours-day.js +72 -54
  116. package/dist/cpsat/rules/min-hours-day.js.map +1 -1
  117. package/dist/cpsat/rules/min-hours-week.d.ts +15 -18
  118. package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
  119. package/dist/cpsat/rules/min-hours-week.js +75 -57
  120. package/dist/cpsat/rules/min-hours-week.js.map +1 -1
  121. package/dist/cpsat/rules/min-rest-between-shifts.d.ts +14 -17
  122. package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
  123. package/dist/cpsat/rules/min-rest-between-shifts.js +148 -138
  124. package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
  125. package/dist/cpsat/rules/minimize-cost.d.ts +2 -18
  126. package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
  127. package/dist/cpsat/rules/minimize-cost.js +151 -163
  128. package/dist/cpsat/rules/minimize-cost.js.map +1 -1
  129. package/dist/cpsat/rules/must-assign.d.ts +12 -0
  130. package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
  131. package/dist/cpsat/rules/must-assign.js +54 -0
  132. package/dist/cpsat/rules/must-assign.js.map +1 -0
  133. package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +6 -10
  134. package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
  135. package/dist/cpsat/rules/overtime-daily-multiplier.js +118 -112
  136. package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
  137. package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +6 -11
  138. package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
  139. package/dist/cpsat/rules/overtime-daily-surcharge.js +104 -99
  140. package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
  141. package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +9 -21
  142. package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
  143. package/dist/cpsat/rules/overtime-tiered-multiplier.js +181 -199
  144. package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
  145. package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +13 -12
  146. package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
  147. package/dist/cpsat/rules/overtime-weekly-multiplier.js +143 -143
  148. package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
  149. package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +7 -11
  150. package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
  151. package/dist/cpsat/rules/overtime-weekly-surcharge.js +124 -119
  152. package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
  153. package/dist/cpsat/rules/pattern-eligibility.d.ts +6 -0
  154. package/dist/cpsat/rules/pattern-eligibility.d.ts.map +1 -0
  155. package/dist/cpsat/rules/pattern-eligibility.js +16 -0
  156. package/dist/cpsat/rules/pattern-eligibility.js.map +1 -0
  157. package/dist/cpsat/rules/pattern-time.d.ts +5 -0
  158. package/dist/cpsat/rules/pattern-time.d.ts.map +1 -0
  159. package/dist/cpsat/rules/pattern-time.js +20 -0
  160. package/dist/cpsat/rules/pattern-time.js.map +1 -0
  161. package/dist/cpsat/rules/registry.d.ts +217 -27
  162. package/dist/cpsat/rules/registry.d.ts.map +1 -1
  163. package/dist/cpsat/rules/registry.js +98 -31
  164. package/dist/cpsat/rules/registry.js.map +1 -1
  165. package/dist/cpsat/rules/resolver.d.ts +9 -7
  166. package/dist/cpsat/rules/resolver.d.ts.map +1 -1
  167. package/dist/cpsat/rules/resolver.js +19 -18
  168. package/dist/cpsat/rules/resolver.js.map +1 -1
  169. package/dist/cpsat/rules/role-preference.d.ts +24 -0
  170. package/dist/cpsat/rules/role-preference.d.ts.map +1 -0
  171. package/dist/cpsat/rules/role-preference.js +72 -0
  172. package/dist/cpsat/rules/role-preference.js.map +1 -0
  173. package/dist/cpsat/rules/rules.types.d.ts +93 -17
  174. package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
  175. package/dist/cpsat/rules/scope.types.d.ts +25 -12
  176. package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
  177. package/dist/cpsat/rules/scope.types.js +14 -19
  178. package/dist/cpsat/rules/scope.types.js.map +1 -1
  179. package/dist/cpsat/rules/target-days-week.d.ts +31 -0
  180. package/dist/cpsat/rules/target-days-week.d.ts.map +1 -0
  181. package/dist/cpsat/rules/target-days-week.js +80 -0
  182. package/dist/cpsat/rules/target-days-week.js.map +1 -0
  183. package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts +33 -0
  184. package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts.map +1 -0
  185. package/dist/cpsat/rules/target-peak-concurrent-assignments.js +146 -0
  186. package/dist/cpsat/rules/target-peak-concurrent-assignments.js.map +1 -0
  187. package/dist/cpsat/rules/time-cost-surcharge.d.ts +15 -14
  188. package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
  189. package/dist/cpsat/rules/time-cost-surcharge.js +82 -96
  190. package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
  191. package/dist/cpsat/rules/time-off.d.ts +21 -57
  192. package/dist/cpsat/rules/time-off.d.ts.map +1 -1
  193. package/dist/cpsat/rules/time-off.js +114 -127
  194. package/dist/cpsat/rules/time-off.js.map +1 -1
  195. package/dist/cpsat/rules/variables.d.ts +4 -0
  196. package/dist/cpsat/rules/variables.d.ts.map +1 -0
  197. package/dist/cpsat/rules/variables.js +10 -0
  198. package/dist/cpsat/rules/variables.js.map +1 -0
  199. package/dist/cpsat/rules.d.ts +20 -3
  200. package/dist/cpsat/rules.d.ts.map +1 -1
  201. package/dist/cpsat/rules.js +19 -2
  202. package/dist/cpsat/rules.js.map +1 -1
  203. package/dist/cpsat/semantic-time.d.ts +7 -6
  204. package/dist/cpsat/semantic-time.d.ts.map +1 -1
  205. package/dist/cpsat/semantic-time.js +15 -28
  206. package/dist/cpsat/semantic-time.js.map +1 -1
  207. package/dist/cpsat/types.d.ts +47 -4
  208. package/dist/cpsat/types.d.ts.map +1 -1
  209. package/dist/cpsat/types.js +24 -1
  210. package/dist/cpsat/types.js.map +1 -1
  211. package/dist/cpsat/utils.d.ts +23 -6
  212. package/dist/cpsat/utils.d.ts.map +1 -1
  213. package/dist/cpsat/utils.js +76 -15
  214. package/dist/cpsat/utils.js.map +1 -1
  215. package/dist/cpsat/validation-reporter.d.ts +6 -2
  216. package/dist/cpsat/validation-reporter.d.ts.map +1 -1
  217. package/dist/cpsat/validation-reporter.js +122 -10
  218. package/dist/cpsat/validation-reporter.js.map +1 -1
  219. package/dist/cpsat/validation.types.d.ts +21 -10
  220. package/dist/cpsat/validation.types.d.ts.map +1 -1
  221. package/dist/datetime.utils.d.ts +7 -13
  222. package/dist/datetime.utils.d.ts.map +1 -1
  223. package/dist/datetime.utils.js +29 -36
  224. package/dist/datetime.utils.js.map +1 -1
  225. package/dist/index.d.ts +15 -9
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.js +11 -4
  228. package/dist/index.js.map +1 -1
  229. package/dist/schedule/cost.d.ts +192 -0
  230. package/dist/schedule/cost.d.ts.map +1 -0
  231. package/dist/schedule/cost.js +151 -0
  232. package/dist/schedule/cost.js.map +1 -0
  233. package/dist/schedule/coverage.d.ts +114 -0
  234. package/dist/schedule/coverage.d.ts.map +1 -0
  235. package/dist/schedule/coverage.js +56 -0
  236. package/dist/schedule/coverage.js.map +1 -0
  237. package/dist/schedule/definition.d.ts +346 -0
  238. package/dist/schedule/definition.d.ts.map +1 -0
  239. package/dist/schedule/definition.js +668 -0
  240. package/dist/schedule/definition.js.map +1 -0
  241. package/dist/schedule/index.d.ts +67 -0
  242. package/dist/schedule/index.d.ts.map +1 -0
  243. package/dist/schedule/index.js +69 -0
  244. package/dist/schedule/index.js.map +1 -0
  245. package/dist/schedule/rules.d.ts +678 -0
  246. package/dist/schedule/rules.d.ts.map +1 -0
  247. package/dist/schedule/rules.js +554 -0
  248. package/dist/schedule/rules.js.map +1 -0
  249. package/dist/schedule/shift-patterns.d.ts +17 -0
  250. package/dist/schedule/shift-patterns.d.ts.map +1 -0
  251. package/dist/schedule/shift-patterns.js +24 -0
  252. package/dist/schedule/shift-patterns.js.map +1 -0
  253. package/dist/schedule/time-periods.d.ts +79 -0
  254. package/dist/schedule/time-periods.d.ts.map +1 -0
  255. package/dist/schedule/time-periods.js +101 -0
  256. package/dist/schedule/time-periods.js.map +1 -0
  257. package/dist/types.d.ts +67 -3
  258. package/dist/types.d.ts.map +1 -1
  259. package/dist/types.js +64 -0
  260. package/dist/types.js.map +1 -1
  261. package/package.json +8 -14
  262. package/solver/src/solver/models.py +41 -4
  263. package/solver/src/solver/solver.py +546 -157
  264. package/solver/tests/test_solver.py +363 -7
  265. package/src/client.schemas.ts +127 -10
  266. package/src/client.ts +3 -0
  267. package/src/client.types.ts +73 -7
  268. package/src/cpsat/cost.ts +17 -7
  269. package/src/cpsat/model-builder.ts +614 -129
  270. package/src/cpsat/response.ts +57 -13
  271. package/src/cpsat/rule-descriptor.ts +311 -0
  272. package/src/cpsat/rules/artifacts.ts +56 -0
  273. package/src/cpsat/rules/assign-together.ts +104 -69
  274. package/src/cpsat/rules/assigned-day.ts +70 -0
  275. package/src/cpsat/rules/assignment-priority.ts +54 -58
  276. package/src/cpsat/rules/assignment-terms.ts +43 -0
  277. package/src/cpsat/rules/concurrent-intervals.ts +93 -0
  278. package/src/cpsat/rules/cost-utils.ts +1 -9
  279. package/src/cpsat/rules/day-cost-multiplier.ts +101 -81
  280. package/src/cpsat/rules/day-cost-surcharge.ts +91 -70
  281. package/src/cpsat/rules/index.ts +30 -21
  282. package/src/cpsat/rules/location-preference.ts +47 -52
  283. package/src/cpsat/rules/max-concurrent-assignments.ts +170 -0
  284. package/src/cpsat/rules/max-consecutive-days.ts +74 -91
  285. package/src/cpsat/rules/max-days-of-week-per-period.ts +127 -0
  286. package/src/cpsat/rules/max-days-week.ts +117 -0
  287. package/src/cpsat/rules/max-hours-day.ts +64 -84
  288. package/src/cpsat/rules/max-hours-week.ts +74 -90
  289. package/src/cpsat/rules/max-shifts-day.ts +65 -85
  290. package/src/cpsat/rules/min-consecutive-days.ts +161 -127
  291. package/src/cpsat/rules/min-days-of-week-per-period.ts +144 -0
  292. package/src/cpsat/rules/min-days-week.ts +122 -0
  293. package/src/cpsat/rules/min-hours-day.ts +79 -58
  294. package/src/cpsat/rules/min-hours-week.ts +87 -62
  295. package/src/cpsat/rules/min-rest-between-shifts.ts +191 -178
  296. package/src/cpsat/rules/minimize-cost.ts +175 -188
  297. package/src/cpsat/rules/must-assign.ts +71 -0
  298. package/src/cpsat/rules/overtime-daily-multiplier.ts +158 -140
  299. package/src/cpsat/rules/overtime-daily-surcharge.ts +141 -126
  300. package/src/cpsat/rules/overtime-tiered-multiplier.ts +234 -253
  301. package/src/cpsat/rules/overtime-weekly-multiplier.ts +183 -175
  302. package/src/cpsat/rules/overtime-weekly-surcharge.ts +161 -150
  303. package/src/cpsat/rules/pattern-eligibility.ts +26 -0
  304. package/src/cpsat/rules/pattern-time.ts +30 -0
  305. package/src/cpsat/rules/registry.ts +140 -56
  306. package/src/cpsat/rules/resolver.ts +58 -30
  307. package/src/cpsat/rules/role-preference.ts +92 -0
  308. package/src/cpsat/rules/rules.types.ts +92 -17
  309. package/src/cpsat/rules/scope.types.ts +32 -27
  310. package/src/cpsat/rules/target-days-week.ts +105 -0
  311. package/src/cpsat/rules/target-peak-concurrent-assignments.ts +212 -0
  312. package/src/cpsat/rules/time-cost-surcharge.ts +116 -122
  313. package/src/cpsat/rules/time-off.ts +154 -163
  314. package/src/cpsat/rules/variables.ts +11 -0
  315. package/src/cpsat/rules.ts +43 -17
  316. package/src/cpsat/semantic-time.ts +29 -43
  317. package/src/cpsat/types.ts +53 -4
  318. package/src/cpsat/utils.ts +92 -20
  319. package/src/cpsat/validation-reporter.ts +170 -19
  320. package/src/cpsat/validation.types.ts +22 -11
  321. package/src/datetime.utils.ts +45 -44
  322. package/src/index.ts +72 -15
  323. package/src/schedule/cost.ts +221 -0
  324. package/src/schedule/coverage.ts +164 -0
  325. package/src/schedule/definition.ts +1447 -0
  326. package/src/schedule/index.ts +132 -0
  327. package/src/schedule/rules.ts +1028 -0
  328. package/src/schedule/shift-patterns.ts +29 -0
  329. package/src/schedule/time-periods.ts +120 -0
  330. package/src/types.ts +114 -2
  331. package/dist/llms.d.ts +0 -2
  332. package/dist/llms.d.ts.map +0 -1
  333. package/dist/llms.js +0 -3
  334. package/dist/llms.js.map +0 -1
  335. package/dist/schedule.d.ts +0 -917
  336. package/dist/schedule.d.ts.map +0 -1
  337. package/dist/schedule.js +0 -1323
  338. package/dist/schedule.js.map +0 -1
  339. package/llms.txt +0 -758
  340. package/src/llms.ts +0 -3
  341. package/src/schedule.ts +0 -1960
@@ -1,25 +1,25 @@
1
1
  import * as z from "zod";
2
- import type { CompilationRule, CostContribution } from "../model-builder.js";
3
- import type { ShiftPattern, SchedulingMember, Term } from "../types.js";
4
- import type { ShiftAssignment } from "../response.js";
2
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
3
+ import type { CostArtifact } from "../rule-descriptor.js";
5
4
  import { COST_CATEGORY } from "../cost.js";
6
5
  import {
7
- timeOfDayToMinutes,
6
+ OBJECTIVE_WEIGHTS,
8
7
  normalizeEndMinutes,
8
+ timeOfDayToMinutes,
9
9
  unionMinutes,
10
- OBJECTIVE_WEIGHTS,
11
10
  } from "../utils.js";
12
11
  import {
13
12
  entityScope,
14
- timeScope,
15
13
  parseEntityScope,
16
14
  parseTimeScope,
17
- resolveMembersFromScope,
18
15
  resolveActiveDaysFromScope,
16
+ resolveMembersFromScope,
17
+ timeScope,
19
18
  } from "./scope.types.js";
20
- import { getHourlyRate, patternDurationMinutes } from "./cost-utils.js";
19
+ import { getHourlyRate } from "./cost-utils.js";
20
+ import { patternDurationMinutes } from "./pattern-time.js";
21
21
 
22
- const OvertimeDailyMultiplierSchema = z
22
+ export const OvertimeDailyMultiplierSchema = z
23
23
  .object({
24
24
  after: z.number().min(0),
25
25
  factor: z.number().min(1),
@@ -27,144 +27,162 @@ const OvertimeDailyMultiplierSchema = z
27
27
  .and(entityScope(["members", "roles", "skills"]))
28
28
  .and(timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]));
29
29
 
30
- /** Configuration for {@link createOvertimeDailyMultiplierRule}. */
30
+ /** Configuration for {@link overtimeDailyMultiplierRuleDescriptor}. */
31
31
  export type OvertimeDailyMultiplierConfig = z.infer<typeof OvertimeDailyMultiplierSchema>;
32
32
 
33
- /**
34
- * Creates a daily overtime rate multiplier rule.
35
- *
36
- * Same as the weekly variant but the threshold is per day instead of per week.
37
- */
38
- export function createOvertimeDailyMultiplierRule(
39
- config: OvertimeDailyMultiplierConfig,
40
- ): CompilationRule {
41
- const parsed = OvertimeDailyMultiplierSchema.parse(config);
42
- const { after, factor } = parsed;
43
- const entityScopeValue = parseEntityScope(parsed);
44
- const timeScopeValue = parseTimeScope(parsed);
45
- const thresholdMinutes = after * 60;
46
- const extraFactor = factor - 1;
47
-
48
- return {
49
- compile(b) {
50
- if (extraFactor <= 0) return;
51
-
52
- const targetMembers = resolveMembersFromScope(entityScopeValue, b.members);
53
- const activeDays = resolveActiveDaysFromScope(timeScopeValue, b.days);
54
-
55
- if (targetMembers.length === 0 || activeDays.length === 0) return;
56
-
57
- const hasCostContext = b.costContext?.active === true;
58
-
59
- for (const emp of targetMembers) {
60
- const rate = getHourlyRate(emp);
61
- if (rate === undefined) continue;
62
-
63
- for (const day of activeDays) {
64
- const dayRanges: Array<{ start: number; end: number }> = [];
65
- const terms: Term[] = [];
66
- for (const pattern of b.shiftPatterns) {
67
- if (!b.canAssign(emp, pattern)) continue;
68
- if (!b.patternAvailableOnDay(pattern, day)) continue;
69
- const start = timeOfDayToMinutes(pattern.startTime);
70
- dayRanges.push({
71
- start,
72
- end: normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime)),
73
- });
74
- terms.push({
75
- var: b.assignment(emp.id, pattern.id, day),
76
- coeff: patternDurationMinutes(pattern),
77
- });
78
- }
79
-
80
- if (terms.length === 0) continue;
81
-
82
- const maxOvertime = Math.max(0, unionMinutes(dayRanges) - thresholdMinutes);
83
- if (maxOvertime === 0) continue;
84
-
85
- const overtimeVar = b.intVar(`overtime:daily-mult:${emp.id}:${day}`, 0, maxOvertime);
86
-
87
- b.addLinear(
88
- [
89
- { var: overtimeVar, coeff: 1 },
90
- ...terms.map((t) => ({ var: t.var, coeff: -t.coeff })),
91
- ],
92
- ">=",
93
- -thresholdMinutes,
94
- );
33
+ export const overtimeDailyMultiplierRuleDescriptor = defineRuleDescriptor({
34
+ name: "overtime-daily-multiplier",
35
+ schema: OvertimeDailyMultiplierSchema,
36
+ compile(config) {
37
+ const { after, factor } = config;
38
+ const entityScopeValue = parseEntityScope(config);
39
+ const timeScopeValue = parseTimeScope(config);
40
+ const thresholdMinutes = after * 60;
41
+ const extraFactor = factor - 1;
42
+
43
+ const costArtifact: CostArtifact = {
44
+ kind: "cost",
45
+ validation: {
46
+ strategy: "skip",
47
+ category: "no-meaningful-feedback",
48
+ rationale:
49
+ "Overtime cost artifacts influence optimization and post-solve accounting, not validation feedback.",
50
+ },
51
+ compileObjective(builder) {
52
+ if (extraFactor <= 0) return;
53
+
54
+ const targetMembers = resolveMembersFromScope(entityScopeValue, builder.members);
55
+ const activeDays = resolveActiveDaysFromScope(timeScopeValue, builder.days);
56
+ if (targetMembers.length === 0 || activeDays.length === 0) return;
57
+
58
+ const hasCostContext = builder.costContext?.active === true;
59
+ for (const member of targetMembers) {
60
+ const rate = getHourlyRate(member);
61
+ if (rate === undefined) continue;
62
+
63
+ for (const day of activeDays) {
64
+ const dayRanges: Array<{ start: number; end: number }> = [];
65
+ const terms = [] as Array<{ var: string; coeff: number }>;
66
+ for (const pattern of builder.shiftPatterns) {
67
+ if (
68
+ !builder.canAssign(member, pattern) ||
69
+ !builder.patternAvailableOnDay(pattern, day)
70
+ ) {
71
+ continue;
72
+ }
73
+ const start = timeOfDayToMinutes(pattern.startTime);
74
+ dayRanges.push({
75
+ start,
76
+ end: normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime)),
77
+ });
78
+ terms.push({
79
+ var: builder.assignment(member.id, pattern.id, day),
80
+ coeff: patternDurationMinutes(pattern),
81
+ });
82
+ }
95
83
 
96
- if (rate > 0) {
97
- const totalOvertimeCost = (rate * extraFactor * maxOvertime) / 60;
98
- if (hasCostContext) {
99
- const normalizedMax = totalOvertimeCost / b.costContext!.normalizationFactor;
100
- b.addPenalty(overtimeVar, Math.max(1, Math.round(normalizedMax / maxOvertime)));
84
+ if (terms.length === 0) continue;
85
+ const maxOvertime = Math.max(0, unionMinutes(dayRanges) - thresholdMinutes);
86
+ if (maxOvertime === 0) continue;
87
+
88
+ const overtimeVar = builder.intVar(
89
+ `overtime:daily-mult:${member.id}:${day.iso}`,
90
+ 0,
91
+ maxOvertime,
92
+ );
93
+ builder.addLinear(
94
+ [
95
+ { var: overtimeVar, coeff: 1 },
96
+ ...terms.map((term) => ({ var: term.var, coeff: -term.coeff })),
97
+ ],
98
+ ">=",
99
+ -thresholdMinutes,
100
+ );
101
+
102
+ if (rate > 0) {
103
+ const totalOvertimeCost = (rate * extraFactor * maxOvertime) / 60;
104
+ if (hasCostContext) {
105
+ const normalizedMax = totalOvertimeCost / builder.costContext!.normalizationFactor;
106
+ builder.addPenalty(
107
+ overtimeVar,
108
+ Math.max(1, Math.round(normalizedMax / maxOvertime)),
109
+ );
110
+ } else {
111
+ builder.addPenalty(
112
+ overtimeVar,
113
+ Math.max(1, Math.round(OBJECTIVE_WEIGHTS.COST / maxOvertime)),
114
+ );
115
+ }
101
116
  } else {
102
- b.addPenalty(
103
- overtimeVar,
104
- Math.max(1, Math.round(OBJECTIVE_WEIGHTS.COST / maxOvertime)),
105
- );
117
+ builder.addPenalty(overtimeVar, 1);
106
118
  }
107
- } else {
108
- b.addPenalty(overtimeVar, 1);
109
119
  }
110
120
  }
111
- }
112
- },
113
-
114
- cost(
115
- assignments: ShiftAssignment[],
116
- members: ReadonlyArray<SchedulingMember>,
117
- shiftPatterns: ReadonlyArray<ShiftPattern>,
118
- ): CostContribution {
119
- if (extraFactor <= 0) return { entries: [] };
120
-
121
- const memberMap = new Map(members.map((e) => [e.id, e]));
122
- const patternMap = new Map(shiftPatterns.map((p) => [p.id, p]));
123
-
124
- const allDays = [...new Set(assignments.map((a) => a.day))].toSorted();
125
- const activeDays = new Set(resolveActiveDaysFromScope(timeScopeValue, allDays));
126
- const targetEmpIds = new Set(
127
- resolveMembersFromScope(entityScopeValue, [...memberMap.values()]).map((e) => e.id),
128
- );
129
-
130
- // Group assignments by member then day, computing daily minutes
131
- const memberDayMinutes = new Map<string, Map<string, number>>();
132
-
133
- for (const a of assignments) {
134
- if (!activeDays.has(a.day)) continue;
135
- if (!targetEmpIds.has(a.memberId)) continue;
136
- const pattern = patternMap.get(a.shiftPatternId);
137
- if (!pattern) continue;
138
- let dayMap = memberDayMinutes.get(a.memberId);
139
- if (!dayMap) {
140
- dayMap = new Map();
141
- memberDayMinutes.set(a.memberId, dayMap);
121
+ },
122
+ calculateCost(assignments, costContext) {
123
+ if (extraFactor <= 0) return { entries: [] };
124
+
125
+ const memberMap = new Map(costContext.members.map((member) => [member.id, member]));
126
+ const patternMap = new Map(
127
+ costContext.shiftPatterns.map((pattern) => [pattern.id, pattern]),
128
+ );
129
+ const activeDays = new Set(
130
+ resolveActiveDaysFromScope(timeScopeValue, [...costContext.days]).map((day) => day.iso),
131
+ );
132
+ const targetMemberIds = new Set(
133
+ resolveMembersFromScope(entityScopeValue, [...costContext.members]).map(
134
+ (member) => member.id,
135
+ ),
136
+ );
137
+ const memberDayMinutes = new Map<string, Map<string, number>>();
138
+
139
+ for (const assignment of assignments) {
140
+ if (!activeDays.has(assignment.day) || !targetMemberIds.has(assignment.memberId))
141
+ continue;
142
+ const pattern = patternMap.get(assignment.shiftPatternId);
143
+ if (!pattern) continue;
144
+ let dayMap = memberDayMinutes.get(assignment.memberId);
145
+ if (!dayMap) {
146
+ dayMap = new Map();
147
+ memberDayMinutes.set(assignment.memberId, dayMap);
148
+ }
149
+ dayMap.set(
150
+ assignment.day,
151
+ (dayMap.get(assignment.day) ?? 0) + patternDurationMinutes(pattern),
152
+ );
142
153
  }
143
- dayMap.set(a.day, (dayMap.get(a.day) ?? 0) + patternDurationMinutes(pattern));
144
- }
145
-
146
- const entries: CostContribution["entries"] = [];
147
-
148
- for (const [empId, dayMap] of memberDayMinutes) {
149
- const emp = memberMap.get(empId);
150
- if (!emp) continue;
151
- const rate = getHourlyRate(emp);
152
- if (rate === undefined) continue;
153
-
154
- for (const [day, minutes] of dayMap) {
155
- const overtimeMinutes = Math.max(0, minutes - thresholdMinutes);
156
- if (overtimeMinutes <= 0) continue;
157
-
158
- entries.push({
159
- memberId: empId,
160
- day,
161
- category: COST_CATEGORY.OVERTIME,
162
- amount: (rate * extraFactor * overtimeMinutes) / 60,
163
- });
154
+
155
+ const entries = [] as Array<{
156
+ memberId: string;
157
+ day: string;
158
+ category: string;
159
+ amount: number;
160
+ }>;
161
+ for (const [memberId, dayMap] of memberDayMinutes) {
162
+ const member = memberMap.get(memberId);
163
+ if (!member) continue;
164
+ const rate = getHourlyRate(member);
165
+ if (rate === undefined) continue;
166
+
167
+ for (const [day, minutes] of dayMap) {
168
+ const overtimeMinutes = Math.max(0, minutes - thresholdMinutes);
169
+ if (overtimeMinutes <= 0) continue;
170
+ entries.push({
171
+ memberId,
172
+ day,
173
+ category: COST_CATEGORY.OVERTIME,
174
+ amount: (rate * extraFactor * overtimeMinutes) / 60,
175
+ });
176
+ }
164
177
  }
165
- }
166
178
 
167
- return { entries };
168
- },
169
- };
170
- }
179
+ return { entries };
180
+ },
181
+ };
182
+
183
+ return {
184
+ rule: "overtime-daily-multiplier",
185
+ artifacts: [costArtifact],
186
+ };
187
+ },
188
+ });
@@ -1,25 +1,24 @@
1
1
  import * as z from "zod";
2
- import type { CompilationRule, CostContribution } from "../model-builder.js";
3
- import type { ShiftPattern, SchedulingMember, Term } from "../types.js";
4
- import type { ShiftAssignment } from "../response.js";
2
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
3
+ import type { CostArtifact } from "../rule-descriptor.js";
5
4
  import { COST_CATEGORY } from "../cost.js";
6
5
  import {
7
- timeOfDayToMinutes,
6
+ OBJECTIVE_WEIGHTS,
8
7
  normalizeEndMinutes,
8
+ timeOfDayToMinutes,
9
9
  unionMinutes,
10
- OBJECTIVE_WEIGHTS,
11
10
  } from "../utils.js";
12
11
  import {
13
12
  entityScope,
14
- timeScope,
15
13
  parseEntityScope,
16
14
  parseTimeScope,
17
- resolveMembersFromScope,
18
15
  resolveActiveDaysFromScope,
16
+ resolveMembersFromScope,
17
+ timeScope,
19
18
  } from "./scope.types.js";
20
- import { patternDurationMinutes } from "./cost-utils.js";
19
+ import { patternDurationMinutes } from "./pattern-time.js";
21
20
 
22
- const OvertimeDailySurchargeSchema = z
21
+ export const OvertimeDailySurchargeSchema = z
23
22
  .object({
24
23
  after: z.number().min(0),
25
24
  amount: z.number().min(0),
@@ -27,130 +26,146 @@ const OvertimeDailySurchargeSchema = z
27
26
  .and(entityScope(["members", "roles", "skills"]))
28
27
  .and(timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]));
29
28
 
30
- /** Configuration for {@link createOvertimeDailySurchargeRule}. */
29
+ /** Configuration for {@link overtimeDailySurchargeRuleDescriptor}. */
31
30
  export type OvertimeDailySurchargeConfig = z.infer<typeof OvertimeDailySurchargeSchema>;
32
31
 
33
- /**
34
- * Creates a daily overtime flat surcharge rule.
35
- *
36
- * Hours beyond the threshold per day get a flat surcharge per hour,
37
- * independent of the member's base rate.
38
- */
39
- export function createOvertimeDailySurchargeRule(
40
- config: OvertimeDailySurchargeConfig,
41
- ): CompilationRule {
42
- const parsed = OvertimeDailySurchargeSchema.parse(config);
43
- const { after, amount } = parsed;
44
- const entityScopeValue = parseEntityScope(parsed);
45
- const timeScopeValue = parseTimeScope(parsed);
46
- const thresholdMinutes = after * 60;
47
-
48
- return {
49
- compile(b) {
50
- if (amount <= 0) return;
51
-
52
- const targetMembers = resolveMembersFromScope(entityScopeValue, b.members);
53
- const activeDays = resolveActiveDaysFromScope(timeScopeValue, b.days);
54
-
55
- if (targetMembers.length === 0 || activeDays.length === 0) return;
56
-
57
- const hasCostContext = b.costContext?.active === true;
58
- const surchargePerMinute = amount / 60;
32
+ export const overtimeDailySurchargeRuleDescriptor = defineRuleDescriptor({
33
+ name: "overtime-daily-surcharge",
34
+ schema: OvertimeDailySurchargeSchema,
35
+ compile(config) {
36
+ const { after, amount } = config;
37
+ const entityScopeValue = parseEntityScope(config);
38
+ const timeScopeValue = parseTimeScope(config);
39
+ const thresholdMinutes = after * 60;
40
+
41
+ const costArtifact: CostArtifact = {
42
+ kind: "cost",
43
+ validation: {
44
+ strategy: "skip",
45
+ category: "no-meaningful-feedback",
46
+ rationale:
47
+ "Overtime cost artifacts influence optimization and post-solve accounting, not validation feedback.",
48
+ },
49
+ compileObjective(builder) {
50
+ if (amount <= 0) return;
51
+
52
+ const targetMembers = resolveMembersFromScope(entityScopeValue, builder.members);
53
+ const activeDays = resolveActiveDaysFromScope(timeScopeValue, builder.days);
54
+ if (targetMembers.length === 0 || activeDays.length === 0) return;
55
+
56
+ const hasCostContext = builder.costContext?.active === true;
57
+ const surchargePerMinute = amount / 60;
58
+ for (const member of targetMembers) {
59
+ for (const day of activeDays) {
60
+ const dayRanges: Array<{ start: number; end: number }> = [];
61
+ const terms = [] as Array<{ var: string; coeff: number }>;
62
+ for (const pattern of builder.shiftPatterns) {
63
+ if (
64
+ !builder.canAssign(member, pattern) ||
65
+ !builder.patternAvailableOnDay(pattern, day)
66
+ ) {
67
+ continue;
68
+ }
69
+ const start = timeOfDayToMinutes(pattern.startTime);
70
+ dayRanges.push({
71
+ start,
72
+ end: normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime)),
73
+ });
74
+ terms.push({
75
+ var: builder.assignment(member.id, pattern.id, day),
76
+ coeff: patternDurationMinutes(pattern),
77
+ });
78
+ }
79
+
80
+ if (terms.length === 0) continue;
81
+ const maxOvertime = Math.max(0, unionMinutes(dayRanges) - thresholdMinutes);
82
+ if (maxOvertime === 0) continue;
83
+
84
+ const overtimeVar = builder.intVar(
85
+ `overtime:daily-surcharge:${member.id}:${day.iso}`,
86
+ 0,
87
+ maxOvertime,
88
+ );
89
+ builder.addLinear(
90
+ [
91
+ { var: overtimeVar, coeff: 1 },
92
+ ...terms.map((term) => ({ var: term.var, coeff: -term.coeff })),
93
+ ],
94
+ ">=",
95
+ -thresholdMinutes,
96
+ );
59
97
 
60
- for (const emp of targetMembers) {
61
- for (const day of activeDays) {
62
- const dayRanges: Array<{ start: number; end: number }> = [];
63
- const terms: Term[] = [];
64
- for (const pattern of b.shiftPatterns) {
65
- if (!b.canAssign(emp, pattern)) continue;
66
- if (!b.patternAvailableOnDay(pattern, day)) continue;
67
- const start = timeOfDayToMinutes(pattern.startTime);
68
- dayRanges.push({
69
- start,
70
- end: normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime)),
71
- });
72
- terms.push({
73
- var: b.assignment(emp.id, pattern.id, day),
74
- coeff: patternDurationMinutes(pattern),
75
- });
98
+ if (hasCostContext) {
99
+ const totalOvertimeCost = surchargePerMinute * maxOvertime;
100
+ const normalizedMax = totalOvertimeCost / builder.costContext!.normalizationFactor;
101
+ builder.addPenalty(overtimeVar, Math.max(1, Math.round(normalizedMax / maxOvertime)));
102
+ } else {
103
+ builder.addPenalty(
104
+ overtimeVar,
105
+ Math.max(1, Math.round(OBJECTIVE_WEIGHTS.COST / maxOvertime)),
106
+ );
107
+ }
76
108
  }
77
-
78
- if (terms.length === 0) continue;
79
-
80
- const maxOvertime = Math.max(0, unionMinutes(dayRanges) - thresholdMinutes);
81
- if (maxOvertime === 0) continue;
82
-
83
- const overtimeVar = b.intVar(`overtime:daily-surcharge:${emp.id}:${day}`, 0, maxOvertime);
84
-
85
- b.addLinear(
86
- [
87
- { var: overtimeVar, coeff: 1 },
88
- ...terms.map((t) => ({ var: t.var, coeff: -t.coeff })),
89
- ],
90
- ">=",
91
- -thresholdMinutes,
92
- );
93
-
94
- if (hasCostContext) {
95
- const totalOvertimeCost = surchargePerMinute * maxOvertime;
96
- const normalizedMax = totalOvertimeCost / b.costContext!.normalizationFactor;
97
- b.addPenalty(overtimeVar, Math.max(1, Math.round(normalizedMax / maxOvertime)));
98
- } else {
99
- b.addPenalty(
100
- overtimeVar,
101
- Math.max(1, Math.round(OBJECTIVE_WEIGHTS.COST / maxOvertime)),
102
- );
109
+ }
110
+ },
111
+ calculateCost(assignments, costContext) {
112
+ if (amount <= 0) return { entries: [] };
113
+
114
+ const patternMap = new Map(
115
+ costContext.shiftPatterns.map((pattern) => [pattern.id, pattern]),
116
+ );
117
+ const activeDays = new Set(
118
+ resolveActiveDaysFromScope(timeScopeValue, [...costContext.days]).map((day) => day.iso),
119
+ );
120
+ const targetMemberIds = new Set(
121
+ resolveMembersFromScope(entityScopeValue, [...costContext.members]).map(
122
+ (member) => member.id,
123
+ ),
124
+ );
125
+ const memberDayMinutes = new Map<string, Map<string, number>>();
126
+
127
+ for (const assignment of assignments) {
128
+ if (!activeDays.has(assignment.day) || !targetMemberIds.has(assignment.memberId))
129
+ continue;
130
+ const pattern = patternMap.get(assignment.shiftPatternId);
131
+ if (!pattern) continue;
132
+ let dayMap = memberDayMinutes.get(assignment.memberId);
133
+ if (!dayMap) {
134
+ dayMap = new Map();
135
+ memberDayMinutes.set(assignment.memberId, dayMap);
103
136
  }
137
+ dayMap.set(
138
+ assignment.day,
139
+ (dayMap.get(assignment.day) ?? 0) + patternDurationMinutes(pattern),
140
+ );
104
141
  }
105
- }
106
- },
107
142
 
108
- cost(
109
- assignments: ShiftAssignment[],
110
- members: ReadonlyArray<SchedulingMember>,
111
- shiftPatterns: ReadonlyArray<ShiftPattern>,
112
- ): CostContribution {
113
- if (amount <= 0) return { entries: [] };
114
-
115
- const patternMap = new Map(shiftPatterns.map((p) => [p.id, p]));
116
- const allDays = [...new Set(assignments.map((a) => a.day))].toSorted();
117
- const activeDays = new Set(resolveActiveDaysFromScope(timeScopeValue, allDays));
118
- const targetEmpIds = new Set(
119
- resolveMembersFromScope(entityScopeValue, [...members]).map((e) => e.id),
120
- );
121
-
122
- const memberDayMinutes = new Map<string, Map<string, number>>();
123
-
124
- for (const a of assignments) {
125
- if (!activeDays.has(a.day)) continue;
126
- if (!targetEmpIds.has(a.memberId)) continue;
127
- const pattern = patternMap.get(a.shiftPatternId);
128
- if (!pattern) continue;
129
- let dayMap = memberDayMinutes.get(a.memberId);
130
- if (!dayMap) {
131
- dayMap = new Map();
132
- memberDayMinutes.set(a.memberId, dayMap);
143
+ const entries = [] as Array<{
144
+ memberId: string;
145
+ day: string;
146
+ category: string;
147
+ amount: number;
148
+ }>;
149
+ for (const [memberId, dayMap] of memberDayMinutes) {
150
+ for (const [day, minutes] of dayMap) {
151
+ const overtimeMinutes = Math.max(0, minutes - thresholdMinutes);
152
+ if (overtimeMinutes <= 0) continue;
153
+ entries.push({
154
+ memberId,
155
+ day,
156
+ category: COST_CATEGORY.OVERTIME,
157
+ amount: (amount * overtimeMinutes) / 60,
158
+ });
159
+ }
133
160
  }
134
- dayMap.set(a.day, (dayMap.get(a.day) ?? 0) + patternDurationMinutes(pattern));
135
- }
136
-
137
- const entries: CostContribution["entries"] = [];
138
161
 
139
- for (const [empId, dayMap] of memberDayMinutes) {
140
- for (const [day, minutes] of dayMap) {
141
- const overtimeMinutes = Math.max(0, minutes - thresholdMinutes);
142
- if (overtimeMinutes <= 0) continue;
143
-
144
- entries.push({
145
- memberId: empId,
146
- day,
147
- category: COST_CATEGORY.OVERTIME,
148
- amount: (amount * overtimeMinutes) / 60,
149
- });
150
- }
151
- }
162
+ return { entries };
163
+ },
164
+ };
152
165
 
153
- return { entries };
154
- },
155
- };
156
- }
166
+ return {
167
+ rule: "overtime-daily-surcharge",
168
+ artifacts: [costArtifact],
169
+ };
170
+ },
171
+ });