dabke 0.81.1 → 0.83.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.
- package/CHANGELOG.md +58 -0
- package/README.md +45 -27
- package/dist/client.d.ts +20 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -1
- package/dist/client.js.map +1 -1
- package/dist/client.types.d.ts +9 -0
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js +1 -0
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +9 -0
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +36 -34
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +13 -1
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +4 -0
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rules/cost-utils.d.ts +11 -0
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -0
- package/dist/cpsat/rules/cost-utils.js +24 -0
- package/dist/cpsat/rules/cost-utils.js.map +1 -0
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +3 -14
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +3 -7
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +3 -0
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +3 -0
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +16 -2
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-week.d.ts +44 -0
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-week.js +95 -0
- package/dist/cpsat/rules/max-days-week.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +15 -2
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +16 -2
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +15 -2
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +15 -2
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-week.d.ts +34 -0
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-week.js +84 -0
- package/dist/cpsat/rules/min-days-week.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +15 -2
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +16 -2
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +72 -2
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +2 -23
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +49 -0
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
- package/dist/cpsat/rules/must-assign.js +86 -0
- package/dist/cpsat/rules/must-assign.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +1 -5
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +5 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +1 -5
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/registry.d.ts +28 -2
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +4 -1
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.js +2 -2
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/rules.types.d.ts +3 -0
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +18 -1
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +59 -16
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +2 -1
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +6 -3
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +44 -42
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +64 -46
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +37 -27
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +7 -12
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +10 -7
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +44 -72
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +54 -44
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/cpsat/validation.types.js +15 -10
- package/dist/cpsat/validation.types.js.map +1 -1
- package/dist/datetime.utils.d.ts +3 -203
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +1 -288
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +14 -83
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -83
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +204 -0
- package/dist/schedule/cost.d.ts.map +1 -0
- package/dist/schedule/cost.js +187 -0
- package/dist/schedule/cost.js.map +1 -0
- package/dist/schedule/coverage.d.ts +85 -0
- package/dist/schedule/coverage.d.ts.map +1 -0
- package/dist/schedule/coverage.js +33 -0
- package/dist/schedule/coverage.js.map +1 -0
- package/dist/schedule/definition.d.ts +227 -0
- package/dist/schedule/definition.d.ts.map +1 -0
- package/dist/schedule/definition.js +659 -0
- package/dist/schedule/definition.js.map +1 -0
- package/dist/schedule/index.d.ts +67 -0
- package/dist/schedule/index.d.ts.map +1 -0
- package/dist/schedule/index.js +69 -0
- package/dist/schedule/index.js.map +1 -0
- package/dist/schedule/rules.d.ts +353 -0
- package/dist/schedule/rules.d.ts.map +1 -0
- package/dist/schedule/rules.js +352 -0
- package/dist/schedule/rules.js.map +1 -0
- package/dist/schedule/shift-patterns.d.ts +34 -0
- package/dist/schedule/shift-patterns.d.ts.map +1 -0
- package/dist/schedule/shift-patterns.js +41 -0
- package/dist/schedule/shift-patterns.js.map +1 -0
- package/dist/schedule/time-periods.d.ts +69 -0
- package/dist/schedule/time-periods.d.ts.map +1 -0
- package/dist/schedule/time-periods.js +91 -0
- package/dist/schedule/time-periods.js.map +1 -0
- package/dist/types.d.ts +14 -78
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -9
- package/solver/src/solver/app.py +1 -1
- package/solver/src/solver/solver.py +7 -4
- package/src/client.ts +6 -8
- package/src/client.types.ts +9 -0
- package/src/cpsat/model-builder.ts +44 -35
- package/src/cpsat/response.ts +13 -1
- package/src/cpsat/rules/cost-utils.ts +25 -0
- package/src/cpsat/rules/day-cost-multiplier.ts +3 -14
- package/src/cpsat/rules/day-cost-surcharge.ts +3 -8
- package/src/cpsat/rules/index.ts +3 -0
- package/src/cpsat/rules/max-consecutive-days.ts +17 -0
- package/src/cpsat/rules/max-days-week.ts +143 -0
- package/src/cpsat/rules/max-hours-day.ts +21 -1
- package/src/cpsat/rules/max-hours-week.ts +22 -1
- package/src/cpsat/rules/max-shifts-day.ts +21 -1
- package/src/cpsat/rules/min-consecutive-days.ts +16 -1
- package/src/cpsat/rules/min-days-week.ts +120 -0
- package/src/cpsat/rules/min-hours-day.ts +16 -1
- package/src/cpsat/rules/min-hours-week.ts +17 -1
- package/src/cpsat/rules/min-rest-between-shifts.ts +92 -2
- package/src/cpsat/rules/minimize-cost.ts +2 -29
- package/src/cpsat/rules/must-assign.ts +108 -0
- package/src/cpsat/rules/overtime-daily-multiplier.ts +1 -12
- package/src/cpsat/rules/overtime-daily-surcharge.ts +1 -6
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +6 -13
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +1 -12
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +1 -6
- package/src/cpsat/rules/registry.ts +8 -2
- package/src/cpsat/rules/resolver.ts +2 -2
- package/src/cpsat/rules/rules.types.ts +3 -0
- package/src/cpsat/rules/scope.types.ts +73 -20
- package/src/cpsat/rules/time-cost-surcharge.ts +2 -1
- package/src/cpsat/rules/time-off.ts +6 -2
- package/src/cpsat/semantic-time.ts +115 -91
- package/src/cpsat/types.ts +37 -27
- package/src/cpsat/utils.ts +8 -12
- package/src/cpsat/validation-reporter.ts +51 -82
- package/src/cpsat/validation.types.ts +72 -47
- package/src/datetime.utils.ts +3 -334
- package/src/index.ts +35 -107
- package/src/schedule/cost.ts +242 -0
- package/src/schedule/coverage.ts +135 -0
- package/src/schedule/definition.ts +958 -0
- package/src/schedule/index.ts +112 -0
- package/src/schedule/rules.ts +529 -0
- package/src/schedule/shift-patterns.ts +46 -0
- package/src/schedule/time-periods.ts +110 -0
- package/src/types.ts +14 -88
- package/dist/errors.d.ts +0 -12
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -17
- package/dist/errors.js.map +0 -1
- package/dist/llms.d.ts +0 -2
- package/dist/llms.d.ts.map +0 -1
- package/dist/llms.js +0 -3
- package/dist/llms.js.map +0 -1
- package/dist/schedule.d.ts +0 -724
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -899
- package/dist/schedule.js.map +0 -1
- package/dist/validation.d.ts +0 -105
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -130
- package/dist/validation.js.map +0 -1
- package/llms.txt +0 -925
- package/src/errors.ts +0 -17
- package/src/llms.ts +0 -3
- package/src/schedule.ts +0 -1419
- package/src/validation.ts +0 -188
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
/**
|
|
8
8
|
* Day of the week identifier.
|
|
9
|
+
*
|
|
10
|
+
* @category Supporting Types
|
|
9
11
|
*/
|
|
10
12
|
export type DayOfWeek = "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday";
|
|
11
13
|
/**
|
|
@@ -14,86 +16,13 @@ export type DayOfWeek = "monday" | "tuesday" | "wednesday" | "thursday" | "frida
|
|
|
14
16
|
*/
|
|
15
17
|
export declare const DayOfWeekSchema: z.ZodUnion<readonly [z.ZodLiteral<"monday">, z.ZodLiteral<"tuesday">, z.ZodLiteral<"wednesday">, z.ZodLiteral<"thursday">, z.ZodLiteral<"friday">, z.ZodLiteral<"saturday">, z.ZodLiteral<"sunday">]>;
|
|
16
18
|
/**
|
|
17
|
-
* Time of day
|
|
18
|
-
*
|
|
19
|
-
* Used for defining shift start/end times and semantic time boundaries.
|
|
20
|
-
* Hours are in 24-hour format (0-23).
|
|
19
|
+
* Time of day (24-hour format).
|
|
21
20
|
*
|
|
22
|
-
* @
|
|
23
|
-
* ```typescript
|
|
24
|
-
* { hours: 9, minutes: 0 }
|
|
25
|
-
* { hours: 17, minutes: 30 }
|
|
26
|
-
* ```
|
|
21
|
+
* @category Supporting Types
|
|
27
22
|
*/
|
|
28
23
|
export interface TimeOfDay {
|
|
29
24
|
hours: number;
|
|
30
25
|
minutes: number;
|
|
31
|
-
seconds?: number;
|
|
32
|
-
nanos?: number;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Calendar date representation (year, month, day).
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* const christmas: CalendarDate = {
|
|
40
|
-
* year: 2025,
|
|
41
|
-
* month: 12,
|
|
42
|
-
* day: 25
|
|
43
|
-
* };
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export interface CalendarDate {
|
|
47
|
-
year: number;
|
|
48
|
-
month: number;
|
|
49
|
-
day: number;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Time horizon defining the start and end dates for scheduling.
|
|
53
|
-
*
|
|
54
|
-
* Specifies the date range over which the schedule should be generated.
|
|
55
|
-
* The range is inclusive of start date and exclusive of end date.
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```typescript
|
|
59
|
-
* // One week schedule starting Monday, March 3, 2025
|
|
60
|
-
* const horizon: TimeHorizon = {
|
|
61
|
-
* start: new Date('2025-03-03'), // Monday
|
|
62
|
-
* end: new Date('2025-03-10') // Following Monday (exclusive)
|
|
63
|
-
* };
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
export interface TimeHorizon {
|
|
67
|
-
start: Date;
|
|
68
|
-
end: Date;
|
|
69
|
-
}
|
|
70
|
-
export interface DateTimeComponents extends Partial<CalendarDate>, Partial<TimeOfDay> {
|
|
71
|
-
}
|
|
72
|
-
interface DateTimeWithUtcOffset extends DateTimeComponents {
|
|
73
|
-
utcOffset?: string;
|
|
74
|
-
timeZone?: never;
|
|
75
|
-
}
|
|
76
|
-
interface DateTimeWithTimeZone extends DateTimeComponents {
|
|
77
|
-
timeZone?: {
|
|
78
|
-
id: string;
|
|
79
|
-
version: string;
|
|
80
|
-
};
|
|
81
|
-
utcOffset?: never;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Date and time representation supporting both UTC offset and timezone-aware formats.
|
|
85
|
-
*
|
|
86
|
-
* Can be specified either with a UTC offset (e.g., "-08:00") or with a timezone ID
|
|
87
|
-
* (e.g., "America/Los_Angeles").
|
|
88
|
-
*/
|
|
89
|
-
export type DateTime = DateTimeWithUtcOffset | DateTimeWithTimeZone;
|
|
90
|
-
/**
|
|
91
|
-
* Represents a time range with start and end DateTimes.
|
|
92
|
-
* Used for checking overlaps and scheduling constraints.
|
|
93
|
-
*/
|
|
94
|
-
export interface DateTimeRange {
|
|
95
|
-
start: DateTime;
|
|
96
|
-
end: DateTime;
|
|
97
26
|
}
|
|
98
27
|
/**
|
|
99
28
|
* Defines a scheduling period as a date range with optional filters.
|
|
@@ -117,7 +46,15 @@ export interface DateTimeRange {
|
|
|
117
46
|
* };
|
|
118
47
|
* ```
|
|
119
48
|
*
|
|
120
|
-
|
|
49
|
+
* @example Only specific dates within the range
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const period: SchedulingPeriod = {
|
|
52
|
+
* dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
53
|
+
* dates: ['2025-02-05', '2025-02-07'],
|
|
54
|
+
* };
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @category Supporting Types
|
|
121
58
|
*/
|
|
122
59
|
export interface SchedulingPeriod {
|
|
123
60
|
/**
|
|
@@ -132,12 +69,11 @@ export interface SchedulingPeriod {
|
|
|
132
69
|
* Include only these days of the week.
|
|
133
70
|
* If omitted, all days of the week are included.
|
|
134
71
|
*/
|
|
135
|
-
dayOfWeek?: DayOfWeek[];
|
|
72
|
+
dayOfWeek?: readonly DayOfWeek[];
|
|
136
73
|
/**
|
|
137
74
|
* Include only these specific dates (YYYY-MM-DD) within the range.
|
|
138
75
|
* If omitted, all dates in the range are included (subject to dayOfWeek filter).
|
|
139
76
|
*/
|
|
140
77
|
dates?: string[];
|
|
141
78
|
}
|
|
142
|
-
export {};
|
|
143
79
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAMzB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAMzB;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,UAAU,GACV,QAAQ,CAAC;AAEb;;;GAGG;AACH,eAAO,MAAM,eAAe,uMAQ1B,CAAC;AAEH;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAoBzB;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC;IACrC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACnB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IACpB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;IACtB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;IACrB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACnB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;IACrB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;CACpB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dabke",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
4
4
|
"description": "Scheduling library powered by constraint programming (CP-SAT)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"constraint-programming",
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"CHANGELOG.md",
|
|
27
27
|
"dist",
|
|
28
28
|
"LICENSE",
|
|
29
|
-
"llms.txt",
|
|
30
29
|
"README.md",
|
|
31
30
|
"solver",
|
|
32
31
|
"src"
|
|
@@ -40,10 +39,6 @@
|
|
|
40
39
|
"types": "./dist/index.d.ts",
|
|
41
40
|
"default": "./dist/index.js"
|
|
42
41
|
},
|
|
43
|
-
"./llms": {
|
|
44
|
-
"types": "./dist/llms.d.ts",
|
|
45
|
-
"default": "./dist/llms.js"
|
|
46
|
-
},
|
|
47
42
|
"./testing": {
|
|
48
43
|
"types": "./dist/testing/index.d.ts",
|
|
49
44
|
"default": "./dist/testing/index.js"
|
|
@@ -59,14 +54,14 @@
|
|
|
59
54
|
"test:unit:watch": "vitest --watch --project=unit",
|
|
60
55
|
"test:integration": "[ -f .env ] && { set -a; source .env; set +a; } && vitest run --project=integration",
|
|
61
56
|
"test:integration:watch": "[ -f .env ] && { set -a; source .env; set +a; } && vitest --watch --project=integration",
|
|
62
|
-
"generate:
|
|
57
|
+
"generate:reference": "tsx scripts/generate-reference.ts"
|
|
63
58
|
},
|
|
64
59
|
"dependencies": {
|
|
65
60
|
"zod": "^4.3.6"
|
|
66
61
|
},
|
|
67
62
|
"devDependencies": {
|
|
68
|
-
"@types/node": "^
|
|
69
|
-
"commander": "^14.0.
|
|
63
|
+
"@types/node": "^25.0.0",
|
|
64
|
+
"commander": "^14.0.3",
|
|
70
65
|
"typescript": "^5.9.3",
|
|
71
66
|
"vitest": "^4.0.18"
|
|
72
67
|
},
|
package/solver/src/solver/app.py
CHANGED
|
@@ -18,7 +18,7 @@ async def health() -> dict[str, str]:
|
|
|
18
18
|
return {"status": "ok", "service": "scheduler-solver"}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
@app.post("/solve", response_model=SolverResponse)
|
|
21
|
+
@app.post("/solve", response_model=SolverResponse, response_model_exclude_unset=True)
|
|
22
22
|
async def solve(request: SolverRequest) -> SolverResponse:
|
|
23
23
|
"""Solve a scheduling model described by primitive constraints."""
|
|
24
24
|
return solve_request(request)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from typing import Iterable
|
|
6
|
+
from typing import Iterable
|
|
7
7
|
|
|
8
8
|
from ortools.sat.python import cp_model
|
|
9
9
|
|
|
@@ -74,7 +74,7 @@ def _expression_range(terms: list[Term], bounds: dict[str, _VariableBounds]) ->
|
|
|
74
74
|
return min_expr, max_expr
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
def _sum_expr(exprs: list[cp_model.LinearExpr]) ->
|
|
77
|
+
def _sum_expr(exprs: list[cp_model.LinearExpr]) -> cp_model.LinearExpr | None:
|
|
78
78
|
if not exprs:
|
|
79
79
|
return None
|
|
80
80
|
total = exprs[0]
|
|
@@ -319,9 +319,10 @@ def solve_request(request: SolverRequest) -> SolverResponse:
|
|
|
319
319
|
status_text = status_map.get(int(status), "ERROR")
|
|
320
320
|
|
|
321
321
|
if status not in (cp_model.OPTIMAL, cp_model.FEASIBLE):
|
|
322
|
+
solution_info = solver.response_proto.solution_info
|
|
322
323
|
return SolverResponse(
|
|
323
324
|
status=status_text,
|
|
324
|
-
solutionInfo
|
|
325
|
+
**({"solutionInfo": solution_info} if solution_info else {}),
|
|
325
326
|
)
|
|
326
327
|
|
|
327
328
|
statistics: dict[str, int | float] = {
|
|
@@ -349,11 +350,13 @@ def solve_request(request: SolverRequest) -> SolverResponse:
|
|
|
349
350
|
)
|
|
350
351
|
)
|
|
351
352
|
|
|
353
|
+
solution_info = solver.response_proto.solution_info
|
|
352
354
|
return SolverResponse(
|
|
353
355
|
status=status_text,
|
|
354
356
|
values={name: int(solver.value(var)) for name, var in vars_map.items()},
|
|
355
357
|
statistics=statistics,
|
|
356
|
-
|
|
358
|
+
**({"solutionInfo": solution_info} if solution_info else {}),
|
|
359
|
+
softViolations=soft_violations,
|
|
357
360
|
)
|
|
358
361
|
except Exception as exc: # pragma: no cover - surfaced in response
|
|
359
362
|
return SolverResponse(status="ERROR", error=str(exc))
|
package/src/client.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type SolverClient,
|
|
4
|
-
type SolverRequest,
|
|
5
|
-
type SolverResponse,
|
|
6
|
-
} from "./client.types.js";
|
|
1
|
+
import { type FetcherLike, type SolverClient, type SolverRequest } from "./client.types.js";
|
|
2
|
+
import { SolverResponseSchema } from "./client.schemas.js";
|
|
7
3
|
|
|
8
4
|
export type {
|
|
9
5
|
SolverClient,
|
|
@@ -25,6 +21,8 @@ const normalizeFetch = (
|
|
|
25
21
|
|
|
26
22
|
/**
|
|
27
23
|
* Generic HTTP client for the solver service.
|
|
24
|
+
*
|
|
25
|
+
* @category Solver
|
|
28
26
|
*/
|
|
29
27
|
export class HttpSolverClient implements SolverClient {
|
|
30
28
|
#fetch: (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
@@ -35,7 +33,7 @@ export class HttpSolverClient implements SolverClient {
|
|
|
35
33
|
this.#baseUrl = baseUrl.replace(/\/$/, "");
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
async solve(request: SolverRequest, options?: { signal?: AbortSignal })
|
|
36
|
+
async solve(request: SolverRequest, options?: { signal?: AbortSignal }) {
|
|
39
37
|
const res = await this.#fetch(`${this.#baseUrl}/solve`, {
|
|
40
38
|
method: "POST",
|
|
41
39
|
headers: { "Content-Type": "application/json" },
|
|
@@ -53,7 +51,7 @@ export class HttpSolverClient implements SolverClient {
|
|
|
53
51
|
throw new Error("Solver returned an empty response");
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
return JSON.parse(bodyText)
|
|
54
|
+
return SolverResponseSchema.parse(JSON.parse(bodyText));
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
async health(): Promise<void> {
|
package/src/client.types.ts
CHANGED
|
@@ -85,6 +85,8 @@ export type SolverResponse = z.infer<typeof SolverResponseSchema>;
|
|
|
85
85
|
* Solver outcome status.
|
|
86
86
|
*
|
|
87
87
|
* One of `"OPTIMAL"`, `"FEASIBLE"`, `"INFEASIBLE"`, `"TIMEOUT"`, or `"ERROR"`.
|
|
88
|
+
*
|
|
89
|
+
* @category Solver
|
|
88
90
|
*/
|
|
89
91
|
export type SolverStatus = z.infer<typeof SolverStatusSchema>;
|
|
90
92
|
|
|
@@ -100,6 +102,7 @@ export type SoftConstraintViolation = z.infer<typeof SoftConstraintViolationSche
|
|
|
100
102
|
// Status constants (for convenience)
|
|
101
103
|
// --------------------------------------------------------------------------
|
|
102
104
|
|
|
105
|
+
/** Convenience constants for {@link SolverStatus} values. */
|
|
103
106
|
export const SOLVER_STATUS = {
|
|
104
107
|
OPTIMAL: "OPTIMAL",
|
|
105
108
|
FEASIBLE: "FEASIBLE",
|
|
@@ -112,12 +115,18 @@ export const SOLVER_STATUS = {
|
|
|
112
115
|
// Client interface
|
|
113
116
|
// --------------------------------------------------------------------------
|
|
114
117
|
|
|
118
|
+
/** A `fetch` function or an object with a `fetch` method. */
|
|
115
119
|
export type FetcherLike =
|
|
116
120
|
| typeof fetch
|
|
117
121
|
| {
|
|
118
122
|
fetch: typeof fetch;
|
|
119
123
|
};
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Interface for sending solver requests and receiving responses.
|
|
127
|
+
*
|
|
128
|
+
* @category Solver
|
|
129
|
+
*/
|
|
121
130
|
export interface SolverClient {
|
|
122
131
|
solve(request: SolverRequest, options?: { signal?: AbortSignal }): Promise<SolverResponse>;
|
|
123
132
|
health?(): Promise<void>;
|
|
@@ -94,6 +94,11 @@ export interface CostContribution {
|
|
|
94
94
|
entries: CostEntry[];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Result of {@link ModelBuilder.compile}.
|
|
99
|
+
*
|
|
100
|
+
* @category Model Builder
|
|
101
|
+
*/
|
|
97
102
|
export interface CompilationResult {
|
|
98
103
|
request: SolverRequest;
|
|
99
104
|
validation: ScheduleValidation;
|
|
@@ -103,6 +108,8 @@ export interface CompilationResult {
|
|
|
103
108
|
/**
|
|
104
109
|
* Configuration for ModelBuilder.
|
|
105
110
|
*
|
|
111
|
+
* @category Model Builder
|
|
112
|
+
*
|
|
106
113
|
* @example Date range with day-of-week filtering (restaurant closed Mon/Tue)
|
|
107
114
|
* ```typescript
|
|
108
115
|
* const config: ModelBuilderConfig = {
|
|
@@ -148,6 +155,8 @@ export interface ModelBuilderConfig extends ModelBuilderOptions {
|
|
|
148
155
|
/**
|
|
149
156
|
* Compilation context that creates variables, constraints, and objectives
|
|
150
157
|
* and emits a `SolverRequest` for the Python CP-SAT solver service.
|
|
158
|
+
*
|
|
159
|
+
* @category Model Builder
|
|
151
160
|
*/
|
|
152
161
|
export class ModelBuilder {
|
|
153
162
|
readonly members: SchedulingMember[];
|
|
@@ -187,8 +196,8 @@ export class ModelBuilder {
|
|
|
187
196
|
|
|
188
197
|
// Validate coverage requirements have at least roles or skills
|
|
189
198
|
for (const cov of config.coverage) {
|
|
190
|
-
const hasRoles = cov.
|
|
191
|
-
const hasSkills = cov.
|
|
199
|
+
const hasRoles = cov.roleIds !== undefined && cov.roleIds.length > 0;
|
|
200
|
+
const hasSkills = cov.skillIds !== undefined && cov.skillIds.length > 0;
|
|
192
201
|
if (!hasRoles && !hasSkills) {
|
|
193
202
|
throw new Error(
|
|
194
203
|
`Coverage requirement for day "${cov.day}" must have at least one of roles or skills`,
|
|
@@ -332,7 +341,7 @@ export class ModelBuilder {
|
|
|
332
341
|
}
|
|
333
342
|
|
|
334
343
|
membersWithRole(roleId: string): SchedulingMember[] {
|
|
335
|
-
return this.members.filter((m) => m.
|
|
344
|
+
return this.members.filter((m) => m.roleIds.includes(roleId));
|
|
336
345
|
}
|
|
337
346
|
|
|
338
347
|
/**
|
|
@@ -345,15 +354,15 @@ export class ModelBuilder {
|
|
|
345
354
|
*/
|
|
346
355
|
membersForCoverage(cov: CoverageRequirement): SchedulingMember[] {
|
|
347
356
|
return this.members.filter((m) => {
|
|
348
|
-
if (cov.
|
|
349
|
-
const hasMatchingRole = cov.
|
|
357
|
+
if (cov.roleIds && cov.roleIds.length > 0) {
|
|
358
|
+
const hasMatchingRole = cov.roleIds.some((role) => m.roleIds.includes(role));
|
|
350
359
|
if (!hasMatchingRole) {
|
|
351
360
|
return false;
|
|
352
361
|
}
|
|
353
362
|
}
|
|
354
|
-
if (cov.
|
|
355
|
-
const memberSkills = m.
|
|
356
|
-
if (!cov.
|
|
363
|
+
if (cov.skillIds && cov.skillIds.length > 0) {
|
|
364
|
+
const memberSkills = m.skillIds ?? [];
|
|
365
|
+
if (!cov.skillIds.every((skill) => memberSkills.includes(skill))) {
|
|
357
366
|
return false;
|
|
358
367
|
}
|
|
359
368
|
}
|
|
@@ -362,10 +371,10 @@ export class ModelBuilder {
|
|
|
362
371
|
}
|
|
363
372
|
|
|
364
373
|
canAssign(member: SchedulingMember, pattern: ShiftPattern): boolean {
|
|
365
|
-
if (!pattern.
|
|
374
|
+
if (!pattern.roleIds || pattern.roleIds.length === 0) {
|
|
366
375
|
return true;
|
|
367
376
|
}
|
|
368
|
-
return pattern.
|
|
377
|
+
return pattern.roleIds.some((role) => member.roleIds.includes(role));
|
|
369
378
|
}
|
|
370
379
|
|
|
371
380
|
/**
|
|
@@ -498,13 +507,13 @@ export class ModelBuilder {
|
|
|
498
507
|
for (const cov of this.coverage) {
|
|
499
508
|
const covStart = timeOfDayToMinutes(cov.startTime);
|
|
500
509
|
const covEnd = normalizeEndMinutes(covStart, timeOfDayToMinutes(cov.endTime));
|
|
501
|
-
const covKey = cov.
|
|
510
|
+
const covKey = cov.roleIds?.join(",") ?? cov.skillIds?.join(",") ?? "unknown";
|
|
502
511
|
const coverageLabel =
|
|
503
|
-
cov.
|
|
504
|
-
? cov.
|
|
505
|
-
? `role "${cov.
|
|
506
|
-
: `role "${cov.
|
|
507
|
-
: `skills [${cov.
|
|
512
|
+
cov.roleIds && cov.roleIds.length > 0
|
|
513
|
+
? cov.roleIds.length === 1
|
|
514
|
+
? `role "${cov.roleIds[0]}"`
|
|
515
|
+
: `role "${cov.roleIds.join(" or ")}"`
|
|
516
|
+
: `skills [${cov.skillIds?.join(", ")}]`;
|
|
508
517
|
const coverageWindow = formatTimeRange(covStart, covEnd);
|
|
509
518
|
|
|
510
519
|
const eligibleMembers = this.membersForCoverage(cov);
|
|
@@ -513,16 +522,16 @@ export class ModelBuilder {
|
|
|
513
522
|
this.reporter.reportCoverageError({
|
|
514
523
|
day: cov.day,
|
|
515
524
|
timeSlots: [coverageWindow],
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
525
|
+
roleIds: cov.roleIds,
|
|
526
|
+
skillIds: cov.skillIds,
|
|
527
|
+
message: `Coverage for ${coverageLabel} on ${cov.day} (${coverageWindow}) cannot be met: no eligible team members available.`,
|
|
519
528
|
suggestions: [
|
|
520
|
-
cov.
|
|
521
|
-
? `Add team members with role "${cov.
|
|
529
|
+
cov.roleIds && cov.roleIds.length > 0
|
|
530
|
+
? `Add team members with role "${cov.roleIds.join(" or ")}"`
|
|
522
531
|
: "Add team members with the required skills",
|
|
523
532
|
"Change the coverage requirement to match available team members",
|
|
524
533
|
],
|
|
525
|
-
|
|
534
|
+
group: cov.group,
|
|
526
535
|
});
|
|
527
536
|
|
|
528
537
|
const impossibleVar = `infeasible:coverage:${covKey}:${cov.day}`;
|
|
@@ -550,7 +559,7 @@ export class ModelBuilder {
|
|
|
550
559
|
{
|
|
551
560
|
key: "no_patterns",
|
|
552
561
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
553
|
-
|
|
562
|
+
message: "no shift patterns overlap this time",
|
|
554
563
|
suggestions: [
|
|
555
564
|
"Add shift patterns that overlap this coverage window",
|
|
556
565
|
"Adjust the coverage window to match available shifts",
|
|
@@ -582,7 +591,7 @@ export class ModelBuilder {
|
|
|
582
591
|
{
|
|
583
592
|
key: "no_assignable",
|
|
584
593
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
585
|
-
|
|
594
|
+
message: "no eligible team members can work overlapping shift patterns",
|
|
586
595
|
suggestions: [
|
|
587
596
|
"Adjust shift pattern role requirements",
|
|
588
597
|
"Add shift patterns that eligible team members can work",
|
|
@@ -616,7 +625,7 @@ export class ModelBuilder {
|
|
|
616
625
|
{
|
|
617
626
|
key: "mandatory_time_off",
|
|
618
627
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
619
|
-
|
|
628
|
+
message: "all eligible team members are on mandatory time off",
|
|
620
629
|
suggestions: [
|
|
621
630
|
"Adjust mandatory time-off requests",
|
|
622
631
|
"Add more team members with the required role or skills",
|
|
@@ -630,7 +639,7 @@ export class ModelBuilder {
|
|
|
630
639
|
{
|
|
631
640
|
key: `insufficient:${availableMembers.size}`,
|
|
632
641
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
633
|
-
|
|
642
|
+
message: `only ${availableMembers.size} team members available, need ${cov.targetCount}`,
|
|
634
643
|
suggestions: [
|
|
635
644
|
"Add more team members with the required role or skills",
|
|
636
645
|
`Reduce coverage target to ${availableMembers.size}`,
|
|
@@ -679,13 +688,13 @@ export class ModelBuilder {
|
|
|
679
688
|
comparator: ">=",
|
|
680
689
|
day: cov.day,
|
|
681
690
|
timeSlot: formatMinutes(bucketStart),
|
|
682
|
-
|
|
683
|
-
|
|
691
|
+
roleIds: cov.roleIds,
|
|
692
|
+
skillIds: cov.skillIds,
|
|
684
693
|
context: {
|
|
685
694
|
days: [cov.day],
|
|
686
695
|
memberIds: eligibleMembers.map((e) => e.id),
|
|
687
696
|
},
|
|
688
|
-
|
|
697
|
+
group: cov.group,
|
|
689
698
|
});
|
|
690
699
|
}
|
|
691
700
|
|
|
@@ -695,17 +704,17 @@ export class ModelBuilder {
|
|
|
695
704
|
);
|
|
696
705
|
if (ranges.length === 0) continue;
|
|
697
706
|
|
|
698
|
-
const
|
|
707
|
+
const message = `Coverage for ${coverageLabel} on ${cov.day} (${ranges.join(", ")}) cannot be met: ${issue.message}.`;
|
|
699
708
|
|
|
700
709
|
if (issue.severity === "impossible") {
|
|
701
710
|
this.reporter.reportCoverageError({
|
|
702
711
|
day: cov.day,
|
|
703
712
|
timeSlots: ranges,
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
713
|
+
roleIds: cov.roleIds,
|
|
714
|
+
skillIds: cov.skillIds,
|
|
715
|
+
message,
|
|
707
716
|
suggestions: issue.suggestions,
|
|
708
|
-
|
|
717
|
+
group: cov.group,
|
|
709
718
|
});
|
|
710
719
|
}
|
|
711
720
|
// Note: soft coverage warnings are tracked via trackConstraint and reported post-solve
|
|
@@ -824,7 +833,7 @@ export class ModelBuilder {
|
|
|
824
833
|
type BucketIssueGroup = {
|
|
825
834
|
key: string;
|
|
826
835
|
severity: "impossible" | "warning";
|
|
827
|
-
|
|
836
|
+
message: string;
|
|
828
837
|
suggestions: string[];
|
|
829
838
|
bucketStarts: number[];
|
|
830
839
|
values?: Record<string, number>;
|
package/src/cpsat/response.ts
CHANGED
|
@@ -2,7 +2,11 @@ import type { SolverResponse } from "../client.types.js";
|
|
|
2
2
|
import type { ShiftPattern } from "./types.js";
|
|
3
3
|
import type { TimeOfDay } from "../types.js";
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* A raw assignment from the solver: which member works which shift on which day.
|
|
7
|
+
*
|
|
8
|
+
* @category Solver
|
|
9
|
+
*/
|
|
6
10
|
export interface ShiftAssignment {
|
|
7
11
|
/** The assigned member's ID. */
|
|
8
12
|
memberId: string;
|
|
@@ -14,6 +18,8 @@ export interface ShiftAssignment {
|
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* A shift assignment with resolved times.
|
|
21
|
+
*
|
|
22
|
+
* @category Solver
|
|
17
23
|
*/
|
|
18
24
|
export interface ResolvedShiftAssignment {
|
|
19
25
|
/** The assigned member's ID. */
|
|
@@ -28,6 +34,8 @@ export interface ResolvedShiftAssignment {
|
|
|
28
34
|
|
|
29
35
|
/**
|
|
30
36
|
* Parsed solver result with assignments and metadata.
|
|
37
|
+
*
|
|
38
|
+
* @category Solver
|
|
31
39
|
*/
|
|
32
40
|
export interface SolverResult {
|
|
33
41
|
/** The solver outcome: OPTIMAL, FEASIBLE, INFEASIBLE, TIMEOUT, or ERROR. */
|
|
@@ -53,6 +61,8 @@ export interface SolverResult {
|
|
|
53
61
|
* @param response - The solver response containing variable values
|
|
54
62
|
* @returns Parsed schedule result with assignments
|
|
55
63
|
*
|
|
64
|
+
* @category Solver
|
|
65
|
+
*
|
|
56
66
|
* @example
|
|
57
67
|
* ```typescript
|
|
58
68
|
* const response = await client.solve(request);
|
|
@@ -107,6 +117,8 @@ export function parseSolverResponse(response: SolverResponse): SolverResult {
|
|
|
107
117
|
* @param shiftPatterns - The shift patterns used in the model
|
|
108
118
|
* @returns Assignments with resolved start/end times
|
|
109
119
|
*
|
|
120
|
+
* @category Solver
|
|
121
|
+
*
|
|
110
122
|
* @example
|
|
111
123
|
* ```typescript
|
|
112
124
|
* const result = parseSolverResponse(response);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SchedulingMember, ShiftPattern } from "../types.js";
|
|
2
|
+
import { timeOfDayToMinutes, normalizeEndMinutes } from "../utils.js";
|
|
3
|
+
|
|
4
|
+
/** Returns the hourly rate for a member, or undefined if not hourly. */
|
|
5
|
+
export function getHourlyRate(member: SchedulingMember): number | undefined {
|
|
6
|
+
if (!member.pay) return undefined;
|
|
7
|
+
if ("hourlyRate" in member.pay) return member.pay.hourlyRate;
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Returns salaried pay info, or undefined if not salaried. */
|
|
12
|
+
export function getSalariedPay(
|
|
13
|
+
member: SchedulingMember,
|
|
14
|
+
): { annual: number; hoursPerWeek: number } | undefined {
|
|
15
|
+
if (!member.pay) return undefined;
|
|
16
|
+
if ("annual" in member.pay) return member.pay;
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Computes shift duration in minutes from a pattern. */
|
|
21
|
+
export function patternDurationMinutes(pattern: ShiftPattern): number {
|
|
22
|
+
const start = timeOfDayToMinutes(pattern.startTime);
|
|
23
|
+
const end = normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime));
|
|
24
|
+
return end - start;
|
|
25
|
+
}
|
|
@@ -2,7 +2,7 @@ import * as z from "zod";
|
|
|
2
2
|
import type { CompilationRule, CostContribution } from "../model-builder.js";
|
|
3
3
|
import type { ShiftPattern, SchedulingMember } from "../types.js";
|
|
4
4
|
import type { ShiftAssignment } from "../response.js";
|
|
5
|
-
import {
|
|
5
|
+
import { COST_CATEGORY } from "../cost.js";
|
|
6
6
|
import {
|
|
7
7
|
entityScope,
|
|
8
8
|
timeScope,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resolveMembersFromScope,
|
|
12
12
|
resolveActiveDaysFromScope,
|
|
13
13
|
} from "./scope.types.js";
|
|
14
|
+
import { getHourlyRate, patternDurationMinutes } from "./cost-utils.js";
|
|
14
15
|
|
|
15
16
|
const DayCostMultiplierSchema = z
|
|
16
17
|
.object({
|
|
@@ -22,18 +23,6 @@ const DayCostMultiplierSchema = z
|
|
|
22
23
|
/** Configuration for {@link createDayCostMultiplierRule}. */
|
|
23
24
|
export type DayCostMultiplierConfig = z.infer<typeof DayCostMultiplierSchema>;
|
|
24
25
|
|
|
25
|
-
function getHourlyRate(emp: SchedulingMember): number | undefined {
|
|
26
|
-
if (!emp.pay) return undefined;
|
|
27
|
-
if ("hourlyRate" in emp.pay) return emp.pay.hourlyRate;
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function patternDurationMinutes(pattern: ShiftPattern): number {
|
|
32
|
-
const start = timeOfDayToMinutes(pattern.startTime);
|
|
33
|
-
const end = normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime));
|
|
34
|
-
return end - start;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
26
|
/**
|
|
38
27
|
* Creates a day-based rate multiplier rule.
|
|
39
28
|
*
|
|
@@ -113,7 +102,7 @@ export function createDayCostMultiplierRule(config: DayCostMultiplierConfig): Co
|
|
|
113
102
|
entries.push({
|
|
114
103
|
memberId: a.memberId,
|
|
115
104
|
day: a.day,
|
|
116
|
-
category:
|
|
105
|
+
category: COST_CATEGORY.PREMIUM,
|
|
117
106
|
amount,
|
|
118
107
|
});
|
|
119
108
|
}
|