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,223 +1,210 @@
1
1
  import type * as z from "zod";
2
- import type { DayOfWeek } from "../../types.js";
3
- import type { CompilationRule, CostContribution } from "../model-builder.js";
4
- import type { ShiftPattern, SchedulingMember } from "../types.js";
5
- import type { ShiftAssignment } from "../response.js";
2
+ import type { DateString, DayOfWeek } from "../../types.js";
3
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
4
+ import type { CostArtifact } from "../rule-descriptor.js";
6
5
  import { COST_CATEGORY } from "../cost.js";
7
6
  import { OBJECTIVE_WEIGHTS, splitIntoWeeks } from "../utils.js";
8
7
  import {
9
8
  entityScope,
10
- timeScope,
11
9
  parseEntityScope,
12
10
  parseTimeScope,
13
- resolveMembersFromScope,
14
11
  resolveActiveDaysFromScope,
12
+ resolveMembersFromScope,
13
+ timeScope,
15
14
  } from "./scope.types.js";
16
- import { getHourlyRate, getSalariedPay, patternDurationMinutes } from "./cost-utils.js";
15
+ import { getHourlyRate, getSalariedPay } from "./cost-utils.js";
16
+ import { patternDurationMinutes } from "./pattern-time.js";
17
17
 
18
18
  const MinimizeCostSchema = entityScope(["members", "roles", "skills"]).and(
19
19
  timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]),
20
20
  );
21
21
 
22
- /** Configuration for {@link createMinimizeCostRule}. */
22
+ /** Configuration for {@link minimizeCostRuleDescriptor}. */
23
23
  export type MinimizeCostConfig = z.infer<typeof MinimizeCostSchema>;
24
24
 
25
- /**
26
- * Creates the minimize-cost rule.
27
- *
28
- * **Hourly members:** adds a penalty proportional to
29
- * `hourlyRate * shiftDurationMinutes / 60` for each assignment.
30
- *
31
- * **Salaried members:** zero marginal cost up to their contracted hours.
32
- * A boolean variable tracks whether the member has any assignment in a
33
- * given week. When active, their full weekly salary (`annual / 52`) is
34
- * added as a penalty. This causes the solver to load work onto salaried
35
- * members (who are "free" once activated) before hourly workers.
36
- *
37
- * Computes a normalization factor and stores it on the model builder's
38
- * `costContext` so modifier rules can use the same scale.
39
- */
40
- export function createMinimizeCostRule(config: MinimizeCostConfig): CompilationRule {
41
- const parsed = MinimizeCostSchema.parse(config);
42
- const entityScopeValue = parseEntityScope(parsed);
43
- const timeScopeValue = parseTimeScope(parsed);
44
-
45
- let resolvedWeekStartsOn: DayOfWeek = "monday";
46
-
47
- return {
48
- compile(b) {
49
- resolvedWeekStartsOn = b.weekStartsOn;
50
-
51
- const targetMembers = resolveMembersFromScope(entityScopeValue, b.members);
52
- const activeDays = resolveActiveDaysFromScope(timeScopeValue, b.days);
53
-
54
- if (targetMembers.length === 0 || activeDays.length === 0) return;
55
-
56
- // Compute normalization factor: max raw cost of any single assignment
57
- // (hourly) or weekly salary (salaried)
58
- let maxRawCost = 0;
59
- for (const member of targetMembers) {
60
- const rate = getHourlyRate(member);
61
- if (rate !== undefined) {
62
- for (const pattern of b.shiftPatterns) {
63
- if (!b.canAssign(member, pattern)) continue;
64
- const duration = patternDurationMinutes(pattern);
65
- const rawCost = (rate * duration) / 60;
66
- if (rawCost > maxRawCost) maxRawCost = rawCost;
67
- }
68
- }
69
- const salaried = getSalariedPay(member);
70
- if (salaried !== undefined) {
71
- const weeklyCost = salaried.annual / 52;
72
- if (weeklyCost > maxRawCost) maxRawCost = weeklyCost;
73
- }
74
- }
75
-
76
- if (maxRawCost === 0) return;
77
-
78
- const normalizationFactor = maxRawCost / OBJECTIVE_WEIGHTS.COST;
79
-
80
- b.costContext = {
81
- normalizationFactor,
82
- active: true,
83
- };
84
-
85
- // Add penalty terms for hourly members
86
- for (const member of targetMembers) {
87
- const rate = getHourlyRate(member);
88
- if (rate !== undefined) {
89
- for (const pattern of b.shiftPatterns) {
90
- if (!b.canAssign(member, pattern)) continue;
91
- for (const day of activeDays) {
92
- if (!b.patternAvailableOnDay(pattern, day)) continue;
25
+ export const minimizeCostRuleDescriptor = defineRuleDescriptor({
26
+ name: "minimize-cost",
27
+ schema: MinimizeCostSchema,
28
+ compile(config) {
29
+ const entityScopeValue = parseEntityScope(config);
30
+ const timeScopeValue = parseTimeScope(config);
31
+ let resolvedWeekStartsOn: DayOfWeek = "monday";
32
+
33
+ const costArtifact: CostArtifact = {
34
+ kind: "cost",
35
+ validation: {
36
+ strategy: "skip",
37
+ category: "no-meaningful-feedback",
38
+ rationale:
39
+ "Cost optimization influences the objective and contributes post-solve accounting, not validation feedback.",
40
+ },
41
+ compileObjective(builder) {
42
+ resolvedWeekStartsOn = builder.weekStartsOn;
43
+
44
+ const targetMembers = resolveMembersFromScope(entityScopeValue, builder.members);
45
+ const activeDays = resolveActiveDaysFromScope(timeScopeValue, builder.days);
46
+ if (targetMembers.length === 0 || activeDays.length === 0) return;
47
+
48
+ let maxRawCost = 0;
49
+ for (const member of targetMembers) {
50
+ const rate = getHourlyRate(member);
51
+ if (rate !== undefined) {
52
+ for (const pattern of builder.shiftPatterns) {
53
+ if (!builder.canAssign(member, pattern)) continue;
93
54
  const duration = patternDurationMinutes(pattern);
94
55
  const rawCost = (rate * duration) / 60;
95
- const normalizedPenalty = rawCost / normalizationFactor;
96
- b.addPenalty(
97
- b.assignment(member.id, pattern.id, day),
98
- Math.max(1, normalizedPenalty),
99
- );
56
+ if (rawCost > maxRawCost) maxRawCost = rawCost;
100
57
  }
101
58
  }
102
- continue;
59
+ const salaried = getSalariedPay(member);
60
+ if (salaried !== undefined) {
61
+ const weeklyCost = salaried.annual / 52;
62
+ if (weeklyCost > maxRawCost) maxRawCost = weeklyCost;
63
+ }
103
64
  }
104
65
 
105
- // Salaried members: weekly fixed cost when active
106
- const salaried = getSalariedPay(member);
107
- if (salaried === undefined) continue;
108
-
109
- const weeklyCost = salaried.annual / 52;
110
- const normalizedWeeklyCost = weeklyCost / normalizationFactor;
111
-
112
- const weeks = splitIntoWeeks(activeDays, b.weekStartsOn);
113
-
114
- for (const [weekIdx, weekDays] of weeks.entries()) {
115
- // Collect all assignment vars for this member this week
116
- const weekAssignmentVars: string[] = [];
117
- for (const day of weekDays) {
118
- for (const pattern of b.shiftPatterns) {
119
- if (!b.canAssign(member, pattern)) continue;
120
- if (!b.patternAvailableOnDay(pattern, day)) continue;
121
- weekAssignmentVars.push(b.assignment(member.id, pattern.id, day));
66
+ if (maxRawCost === 0) return;
67
+
68
+ const normalizationFactor = maxRawCost / OBJECTIVE_WEIGHTS.COST;
69
+ builder.costContext = {
70
+ normalizationFactor,
71
+ active: true,
72
+ };
73
+
74
+ for (const member of targetMembers) {
75
+ const rate = getHourlyRate(member);
76
+ if (rate !== undefined) {
77
+ for (const pattern of builder.shiftPatterns) {
78
+ if (!builder.canAssign(member, pattern)) continue;
79
+ for (const day of activeDays) {
80
+ if (!builder.patternAvailableOnDay(pattern, day)) continue;
81
+ const duration = patternDurationMinutes(pattern);
82
+ const rawCost = (rate * duration) / 60;
83
+ const normalizedPenalty = rawCost / normalizationFactor;
84
+ builder.addPenalty(
85
+ builder.assignment(member.id, pattern.id, day),
86
+ Math.max(1, normalizedPenalty),
87
+ );
88
+ }
122
89
  }
90
+ continue;
123
91
  }
124
92
 
125
- if (weekAssignmentVars.length === 0) continue;
126
-
127
- // Bool var: true if this salaried member works any shift this week
128
- const activeVar = b.boolVar(`active:cost:${member.id}:w${weekIdx}`);
129
-
130
- // If any assignment is true, activeVar must be true
131
- // activeVar OR NOT(assignment_1) OR NOT(assignment_2) OR ...
132
- // This is equivalent to: for each assignment, assignment => activeVar
133
- for (const assignVar of weekAssignmentVars) {
134
- b.addImplication(assignVar, activeVar);
135
- }
93
+ const salaried = getSalariedPay(member);
94
+ if (salaried === undefined) continue;
136
95
 
137
- b.addPenalty(activeVar, Math.max(1, Math.round(normalizedWeeklyCost)));
138
- }
139
- }
140
- },
141
-
142
- cost(
143
- assignments: ShiftAssignment[],
144
- members: ReadonlyArray<SchedulingMember>,
145
- shiftPatterns: ReadonlyArray<ShiftPattern>,
146
- ): CostContribution {
147
- const memberMap = new Map(members.map((m) => [m.id, m]));
148
- const patternMap = new Map(shiftPatterns.map((p) => [p.id, p]));
149
-
150
- const allDays = [...new Set(assignments.map((a) => a.day))].toSorted();
151
- const activeDays = new Set(resolveActiveDaysFromScope(timeScopeValue, allDays));
152
- const targetMemberIds = new Set(
153
- resolveMembersFromScope(entityScopeValue, [...members]).map((m) => m.id),
154
- );
155
-
156
- const entries: CostContribution["entries"] = [];
157
-
158
- // Track which salaried members are active per week for weekly cost
159
- const salariedWeekActive = new Map<string, Set<string>>();
160
-
161
- for (const a of assignments) {
162
- if (!activeDays.has(a.day)) continue;
163
- if (!targetMemberIds.has(a.memberId)) continue;
164
- const member = memberMap.get(a.memberId);
165
- if (!member) continue;
166
-
167
- const pattern = patternMap.get(a.shiftPatternId);
168
- if (!pattern) continue;
169
- const duration = patternDurationMinutes(pattern);
170
-
171
- const rate = getHourlyRate(member);
172
- if (rate !== undefined) {
173
- entries.push({
174
- memberId: a.memberId,
175
- day: a.day,
176
- category: COST_CATEGORY.BASE,
177
- amount: (rate * duration) / 60,
178
- });
179
- continue;
180
- }
96
+ const weeklyCost = salaried.annual / 52;
97
+ const normalizedWeeklyCost = weeklyCost / normalizationFactor;
98
+ const weeks = splitIntoWeeks(activeDays, builder.weekStartsOn);
99
+
100
+ for (const [weekIndex, weekDays] of weeks.entries()) {
101
+ const weekAssignmentVars: string[] = [];
102
+ for (const day of weekDays) {
103
+ for (const pattern of builder.shiftPatterns) {
104
+ if (!builder.canAssign(member, pattern)) continue;
105
+ if (!builder.patternAvailableOnDay(pattern, day)) continue;
106
+ weekAssignmentVars.push(builder.assignment(member.id, pattern.id, day));
107
+ }
108
+ }
181
109
 
182
- // Salaried: track weeks with assignments
183
- const salaried = getSalariedPay(member);
184
- if (salaried === undefined) continue;
110
+ if (weekAssignmentVars.length === 0) continue;
111
+
112
+ const activeVar = builder.boolVar(`active:cost:${member.id}:w${weekIndex}`);
113
+ for (const assignVar of weekAssignmentVars) {
114
+ builder.addLinear(
115
+ [
116
+ { var: activeVar, coeff: 1 },
117
+ { var: assignVar, coeff: -1 },
118
+ ],
119
+ ">=",
120
+ 0,
121
+ );
122
+ }
185
123
 
186
- let days = salariedWeekActive.get(a.memberId);
187
- if (!days) {
188
- days = new Set();
189
- salariedWeekActive.set(a.memberId, days);
124
+ builder.addPenalty(activeVar, Math.max(1, Math.round(normalizedWeeklyCost)));
125
+ }
190
126
  }
191
- days.add(a.day);
192
- }
193
-
194
- // Compute salaried costs: weekly salary distributed across active days
195
- for (const [memberId, activeDaysSet] of salariedWeekActive) {
196
- const member = memberMap.get(memberId);
197
- if (!member) continue;
198
- const salaried = getSalariedPay(member);
199
- if (!salaried) continue;
200
-
201
- const activeList = [...activeDaysSet].toSorted();
202
- const weeks = splitIntoWeeks(activeList, resolvedWeekStartsOn);
203
-
204
- for (const weekDays of weeks) {
205
- if (weekDays.length === 0) continue;
206
- const weeklyCost = salaried.annual / 52;
207
- // Distribute evenly across days worked that week
208
- const perDay = weeklyCost / weekDays.length;
209
- for (const day of weekDays) {
127
+ },
128
+ calculateCost(assignments, costContext) {
129
+ const memberMap = new Map(costContext.members.map((member) => [member.id, member]));
130
+ const patternMap = new Map(
131
+ costContext.shiftPatterns.map((pattern) => [pattern.id, pattern]),
132
+ );
133
+ const activeDays = new Set(
134
+ resolveActiveDaysFromScope(timeScopeValue, [...costContext.days]).map((day) => day.iso),
135
+ );
136
+ const targetMemberIds = new Set(
137
+ resolveMembersFromScope(entityScopeValue, [...costContext.members]).map(
138
+ (member) => member.id,
139
+ ),
140
+ );
141
+
142
+ const entries: Array<{ memberId: string; day: string; category: string; amount: number }> =
143
+ [];
144
+ const salariedWeekActive = new Map<string, Set<DateString>>();
145
+
146
+ for (const assignment of assignments) {
147
+ if (!activeDays.has(assignment.day) || !targetMemberIds.has(assignment.memberId))
148
+ continue;
149
+ const member = memberMap.get(assignment.memberId);
150
+ if (!member) continue;
151
+ const pattern = patternMap.get(assignment.shiftPatternId);
152
+ if (!pattern) continue;
153
+ const duration = patternDurationMinutes(pattern);
154
+
155
+ const rate = getHourlyRate(member);
156
+ if (rate !== undefined) {
210
157
  entries.push({
211
- memberId,
212
- day,
158
+ memberId: assignment.memberId,
159
+ day: assignment.day,
213
160
  category: COST_CATEGORY.BASE,
214
- amount: perDay,
161
+ amount: (rate * duration) / 60,
215
162
  });
163
+ continue;
216
164
  }
165
+
166
+ const salaried = getSalariedPay(member);
167
+ if (salaried === undefined) continue;
168
+ let memberDays = salariedWeekActive.get(assignment.memberId);
169
+ if (!memberDays) {
170
+ memberDays = new Set();
171
+ salariedWeekActive.set(assignment.memberId, memberDays);
172
+ }
173
+ memberDays.add(assignment.day);
217
174
  }
218
- }
219
175
 
220
- return { entries };
221
- },
222
- };
223
- }
176
+ for (const [memberId, activeDaysSet] of salariedWeekActive) {
177
+ const member = memberMap.get(memberId);
178
+ if (!member) continue;
179
+ const salaried = getSalariedPay(member);
180
+ if (!salaried) continue;
181
+
182
+ const activeDateSet = new Set(activeDaysSet);
183
+ const activeList = costContext.days.filter((day) => activeDateSet.has(day.iso));
184
+ const weeks = splitIntoWeeks(activeList, resolvedWeekStartsOn);
185
+
186
+ for (const weekDays of weeks) {
187
+ if (weekDays.length === 0) continue;
188
+ const weeklyCost = salaried.annual / 52;
189
+ const perDay = weeklyCost / weekDays.length;
190
+ for (const day of weekDays) {
191
+ entries.push({
192
+ memberId,
193
+ day: day.iso,
194
+ category: COST_CATEGORY.BASE,
195
+ amount: perDay,
196
+ });
197
+ }
198
+ }
199
+ }
200
+
201
+ return { entries };
202
+ },
203
+ };
204
+
205
+ return {
206
+ rule: "minimize-cost",
207
+ artifacts: [costArtifact],
208
+ };
209
+ },
210
+ });
@@ -0,0 +1,71 @@
1
+ import * as z from "zod";
2
+ import { DayOfWeekSchema } from "../../types.js";
3
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
4
+ import { priorityToPenalty, splitIntoWeeks } from "../utils.js";
5
+ import {
6
+ entityScope,
7
+ parseEntityScope,
8
+ resolveMembersFromScope,
9
+ ruleGroup,
10
+ } from "./scope.types.js";
11
+ import { assignmentTermsForDays } from "./assignment-terms.js";
12
+ import { reportValidation, softConstraint } from "./artifacts.js";
13
+
14
+ /** Internally always HIGH. Not user-configurable. */
15
+ const MUST_ASSIGN_PENALTY = priorityToPenalty("HIGH");
16
+
17
+ export const MustAssignSchema = z
18
+ .object({
19
+ weekStartsOn: DayOfWeekSchema.optional(),
20
+ })
21
+ .and(entityScope(["members", "roles", "skills"]));
22
+
23
+ /**
24
+ * Configuration for {@link mustAssignRuleDescriptor}.
25
+ */
26
+ export type MustAssignConfig = z.infer<typeof MustAssignSchema>;
27
+
28
+ export const mustAssignRuleDescriptor = defineRuleDescriptor({
29
+ name: "must-assign",
30
+ schema: MustAssignSchema,
31
+ compile(config, ctx) {
32
+ const scope = parseEntityScope(config);
33
+ const { weekStartsOn } = config;
34
+ const group = ruleGroup("must-assign", "Must assign", scope);
35
+ const members = resolveMembersFromScope(scope, [...ctx.members]);
36
+ const weeks = splitIntoWeeks([...ctx.days], weekStartsOn ?? ctx.weekStartsOn);
37
+
38
+ const artifacts = members.flatMap((member) =>
39
+ weeks.flatMap((weekDays) => {
40
+ const weekStart = weekDays[0];
41
+ if (!weekStart) return [];
42
+
43
+ const terms = assignmentTermsForDays(member, weekDays, ctx.shiftPatterns, () => 1);
44
+ if (terms.length === 0) return [];
45
+
46
+ const description = `${member.id} not assigned in week starting ${weekStart.iso} (staffing obligation)`;
47
+ const context = { memberIds: [member.id], days: weekDays.map((day) => day.iso) };
48
+ const constraintId = `must-assign:${member.id}:${weekStart.iso}`;
49
+
50
+ return [
51
+ softConstraint({
52
+ group,
53
+ description,
54
+ context,
55
+ validation: reportValidation(),
56
+ terms,
57
+ comparator: ">=",
58
+ targetValue: 1,
59
+ penalty: MUST_ASSIGN_PENALTY,
60
+ constraintId,
61
+ }),
62
+ ];
63
+ }),
64
+ );
65
+
66
+ return {
67
+ rule: "must-assign",
68
+ artifacts,
69
+ };
70
+ },
71
+ });