dabke 0.78.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 +120 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +42 -0
- package/dist/client.js.map +1 -0
- package/dist/client.schemas.d.ts +250 -0
- package/dist/client.schemas.d.ts.map +1 -0
- package/dist/client.schemas.js +137 -0
- package/dist/client.schemas.js.map +1 -0
- package/dist/client.types.d.ts +34 -0
- package/dist/client.types.d.ts.map +1 -0
- package/dist/client.types.js +18 -0
- package/dist/client.types.js.map +1 -0
- package/dist/cpsat/model-builder.d.ts +128 -0
- package/dist/cpsat/model-builder.d.ts.map +1 -0
- package/dist/cpsat/model-builder.js +640 -0
- package/dist/cpsat/model-builder.js.map +1 -0
- package/dist/cpsat/response.d.ts +74 -0
- package/dist/cpsat/response.d.ts.map +1 -0
- package/dist/cpsat/response.js +92 -0
- package/dist/cpsat/response.js.map +1 -0
- package/dist/cpsat/rules/assign-together.d.ts +23 -0
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -0
- package/dist/cpsat/rules/assign-together.js +78 -0
- package/dist/cpsat/rules/assign-together.js.map +1 -0
- package/dist/cpsat/rules/employee-assignment-priority.d.ts +64 -0
- package/dist/cpsat/rules/employee-assignment-priority.d.ts.map +1 -0
- package/dist/cpsat/rules/employee-assignment-priority.js +151 -0
- package/dist/cpsat/rules/employee-assignment-priority.js.map +1 -0
- package/dist/cpsat/rules/index.d.ts +13 -0
- package/dist/cpsat/rules/index.d.ts.map +1 -0
- package/dist/cpsat/rules/index.js +13 -0
- package/dist/cpsat/rules/index.js.map +1 -0
- package/dist/cpsat/rules/location-preference.d.ts +29 -0
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -0
- package/dist/cpsat/rules/location-preference.js +59 -0
- package/dist/cpsat/rules/location-preference.js.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts +28 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.js +70 -0
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts +57 -0
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -0
- package/dist/cpsat/rules/max-hours-day.js +159 -0
- package/dist/cpsat/rules/max-hours-day.js.map +1 -0
- package/dist/cpsat/rules/max-hours-week.d.ts +62 -0
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-hours-week.js +169 -0
- package/dist/cpsat/rules/max-hours-week.js.map +1 -0
- package/dist/cpsat/rules/max-shifts-day.d.ts +69 -0
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -0
- package/dist/cpsat/rules/max-shifts-day.js +170 -0
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -0
- package/dist/cpsat/rules/min-consecutive-days.d.ts +29 -0
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -0
- package/dist/cpsat/rules/min-consecutive-days.js +104 -0
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts +28 -0
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -0
- package/dist/cpsat/rules/min-hours-day.js +61 -0
- package/dist/cpsat/rules/min-hours-day.js.map +1 -0
- package/dist/cpsat/rules/min-hours-week.d.ts +29 -0
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-hours-week.js +68 -0
- package/dist/cpsat/rules/min-hours-week.js.map +1 -0
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +28 -0
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -0
- package/dist/cpsat/rules/min-rest-between-shifts.js +95 -0
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts +7 -0
- package/dist/cpsat/rules/registry.d.ts.map +1 -0
- package/dist/cpsat/rules/registry.js +28 -0
- package/dist/cpsat/rules/registry.js.map +1 -0
- package/dist/cpsat/rules/resolver.d.ts +31 -0
- package/dist/cpsat/rules/resolver.d.ts.map +1 -0
- package/dist/cpsat/rules/resolver.js +124 -0
- package/dist/cpsat/rules/resolver.js.map +1 -0
- package/dist/cpsat/rules/rules.types.d.ts +32 -0
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -0
- package/dist/cpsat/rules/rules.types.js +2 -0
- package/dist/cpsat/rules/rules.types.js.map +1 -0
- package/dist/cpsat/rules/scoping.d.ts +129 -0
- package/dist/cpsat/rules/scoping.d.ts.map +1 -0
- package/dist/cpsat/rules/scoping.js +190 -0
- package/dist/cpsat/rules/scoping.js.map +1 -0
- package/dist/cpsat/rules/time-off.d.ts +78 -0
- package/dist/cpsat/rules/time-off.d.ts.map +1 -0
- package/dist/cpsat/rules/time-off.js +261 -0
- package/dist/cpsat/rules/time-off.js.map +1 -0
- package/dist/cpsat/rules.d.ts +5 -0
- package/dist/cpsat/rules.d.ts.map +1 -0
- package/dist/cpsat/rules.js +4 -0
- package/dist/cpsat/rules.js.map +1 -0
- package/dist/cpsat/semantic-time.d.ts +198 -0
- package/dist/cpsat/semantic-time.d.ts.map +1 -0
- package/dist/cpsat/semantic-time.js +222 -0
- package/dist/cpsat/semantic-time.js.map +1 -0
- package/dist/cpsat/types.d.ts +180 -0
- package/dist/cpsat/types.d.ts.map +1 -0
- package/dist/cpsat/types.js +2 -0
- package/dist/cpsat/types.js.map +1 -0
- package/dist/cpsat/utils.d.ts +47 -0
- package/dist/cpsat/utils.d.ts.map +1 -0
- package/dist/cpsat/utils.js +92 -0
- package/dist/cpsat/utils.js.map +1 -0
- package/dist/cpsat/validation-reporter.d.ts +54 -0
- package/dist/cpsat/validation-reporter.d.ts.map +1 -0
- package/dist/cpsat/validation-reporter.js +261 -0
- package/dist/cpsat/validation-reporter.js.map +1 -0
- package/dist/cpsat/validation.types.d.ts +141 -0
- package/dist/cpsat/validation.types.d.ts.map +1 -0
- package/dist/cpsat/validation.types.js +14 -0
- package/dist/cpsat/validation.types.js.map +1 -0
- package/dist/datetime.utils.d.ts +245 -0
- package/dist/datetime.utils.d.ts.map +1 -0
- package/dist/datetime.utils.js +372 -0
- package/dist/datetime.utils.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +17 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/llms.d.ts +5 -0
- package/dist/llms.d.ts.map +1 -0
- package/dist/llms.js +8 -0
- package/dist/llms.js.map +1 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +11 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/solver-container.d.ts +49 -0
- package/dist/testing/solver-container.d.ts.map +1 -0
- package/dist/testing/solver-container.js +127 -0
- package/dist/testing/solver-container.js.map +1 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +105 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +130 -0
- package/dist/validation.js.map +1 -0
- package/llms.txt +2188 -0
- package/package.json +76 -0
- package/solver/Dockerfile +31 -0
- package/solver/README.md +23 -0
- package/solver/pyproject.toml +28 -0
- package/solver/src/solver/__init__.py +1 -0
- package/solver/src/solver/app.py +24 -0
- package/solver/src/solver/models.py +120 -0
- package/solver/src/solver/solver.py +359 -0
- package/solver/tests/test_solver.py +156 -0
- package/solver/uv.lock +661 -0
- package/src/client.schemas.ts +163 -0
- package/src/client.ts +67 -0
- package/src/client.types.ts +66 -0
- package/src/cpsat/model-builder.ts +858 -0
- package/src/cpsat/response.ts +130 -0
- package/src/cpsat/rules/assign-together.ts +96 -0
- package/src/cpsat/rules/employee-assignment-priority.ts +182 -0
- package/src/cpsat/rules/index.ts +12 -0
- package/src/cpsat/rules/location-preference.ts +68 -0
- package/src/cpsat/rules/max-consecutive-days.ts +98 -0
- package/src/cpsat/rules/max-hours-day.ts +187 -0
- package/src/cpsat/rules/max-hours-week.ts +197 -0
- package/src/cpsat/rules/max-shifts-day.ts +198 -0
- package/src/cpsat/rules/min-consecutive-days.ts +140 -0
- package/src/cpsat/rules/min-hours-day.ts +69 -0
- package/src/cpsat/rules/min-hours-week.ts +77 -0
- package/src/cpsat/rules/min-rest-between-shifts.ts +121 -0
- package/src/cpsat/rules/registry.ts +49 -0
- package/src/cpsat/rules/resolver.ts +181 -0
- package/src/cpsat/rules/rules.types.ts +41 -0
- package/src/cpsat/rules/scoping.ts +340 -0
- package/src/cpsat/rules/time-off.ts +336 -0
- package/src/cpsat/rules.ts +27 -0
- package/src/cpsat/semantic-time.ts +463 -0
- package/src/cpsat/types.ts +194 -0
- package/src/cpsat/utils.ts +105 -0
- package/src/cpsat/validation-reporter.ts +366 -0
- package/src/cpsat/validation.types.ts +185 -0
- package/src/datetime.utils.ts +426 -0
- package/src/errors.ts +17 -0
- package/src/index.ts +289 -0
- package/src/llms.ts +9 -0
- package/src/testing/index.ts +12 -0
- package/src/testing/solver-container.ts +172 -0
- package/src/types.ts +191 -0
- package/src/validation.ts +188 -0
package/llms.txt
ADDED
|
@@ -0,0 +1,2188 @@
|
|
|
1
|
+
# dabke
|
|
2
|
+
|
|
3
|
+
> Scheduling library powered by constraint programming (CP-SAT)
|
|
4
|
+
|
|
5
|
+
Scheduling library powered by constraint programming (CP-SAT).
|
|
6
|
+
|
|
7
|
+
Define teams, shifts, coverage, and rules — dabke turns them
|
|
8
|
+
into an optimized schedule.
|
|
9
|
+
|
|
10
|
+
## Core Concepts
|
|
11
|
+
|
|
12
|
+
**ModelBuilder**: Creates the constraint programming model from your team,
|
|
13
|
+
shift patterns, coverage requirements, and rules.
|
|
14
|
+
|
|
15
|
+
**Semantic Time**: Flexible time period definitions that can vary by day or date.
|
|
16
|
+
- `{ name: "morning", startTime: { hours: 8 }, endTime: { hours: 12 } }`
|
|
17
|
+
- The same semantic name can map to different times based on context
|
|
18
|
+
- Enables business-friendly scheduling: "Need 3 waiters during lunch_rush"
|
|
19
|
+
|
|
20
|
+
**Rules System**: Translate business requirements into scheduling constraints.
|
|
21
|
+
- 12 built-in rules: hours limits, time-off, rest periods, prioritization, etc.
|
|
22
|
+
- Scoping: Apply rules globally, per person, per role, or per time period
|
|
23
|
+
- Priority levels: MANDATORY (hard constraint) vs LOW/MEDIUM/HIGH (soft preferences)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# CP-SAT Scheduling API
|
|
28
|
+
|
|
29
|
+
Core scheduling types for the CP-SAT solver.
|
|
30
|
+
|
|
31
|
+
## Interfaces
|
|
32
|
+
|
|
33
|
+
### TimeOfDay
|
|
34
|
+
Time of day representation (hours and minutes, with optional seconds/nanos).
|
|
35
|
+
|
|
36
|
+
Used for defining shift start/end times and semantic time boundaries.
|
|
37
|
+
Hours are in 24-hour format (0-23).
|
|
38
|
+
|
|
39
|
+
**Example:**
|
|
40
|
+
```typescript
|
|
41
|
+
const morningStart: TimeOfDay = {
|
|
42
|
+
hours: 9,
|
|
43
|
+
minutes: 0
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const afternoonEnd: TimeOfDay = {
|
|
47
|
+
hours: 17,
|
|
48
|
+
minutes: 30
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Properties:**
|
|
53
|
+
- `hours: number`
|
|
54
|
+
- `minutes: number`
|
|
55
|
+
- `seconds?: number | undefined`
|
|
56
|
+
- `nanos?: number | undefined`
|
|
57
|
+
|
|
58
|
+
### CalendarDate
|
|
59
|
+
Calendar date representation (year, month, day).
|
|
60
|
+
|
|
61
|
+
**Example:**
|
|
62
|
+
```typescript
|
|
63
|
+
const christmas: CalendarDate = {
|
|
64
|
+
year: 2025,
|
|
65
|
+
month: 12,
|
|
66
|
+
day: 25
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Properties:**
|
|
71
|
+
- `year: number`
|
|
72
|
+
- `month: number`
|
|
73
|
+
- `day: number`
|
|
74
|
+
|
|
75
|
+
### TimeHorizon
|
|
76
|
+
Time horizon defining the start and end dates for scheduling.
|
|
77
|
+
|
|
78
|
+
Specifies the date range over which the schedule should be generated.
|
|
79
|
+
The range is inclusive of start date and exclusive of end date.
|
|
80
|
+
|
|
81
|
+
**Example:**
|
|
82
|
+
```typescript
|
|
83
|
+
// One week schedule starting Monday, March 3, 2025
|
|
84
|
+
const horizon: TimeHorizon = {
|
|
85
|
+
start: new Date('2025-03-03'), // Monday
|
|
86
|
+
end: new Date('2025-03-10') // Following Monday (exclusive)
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Properties:**
|
|
91
|
+
- `start: Date`
|
|
92
|
+
- `end: Date`
|
|
93
|
+
|
|
94
|
+
### DateTimeComponents
|
|
95
|
+
**Properties:**
|
|
96
|
+
- `year?: number | undefined`
|
|
97
|
+
- `month?: number | undefined`
|
|
98
|
+
- `day?: number | undefined`
|
|
99
|
+
- `hours?: number | undefined`
|
|
100
|
+
- `minutes?: number | undefined`
|
|
101
|
+
- `seconds?: number | undefined`
|
|
102
|
+
- `nanos?: number | undefined`
|
|
103
|
+
|
|
104
|
+
### DateTimeRange
|
|
105
|
+
Represents a time range with start and end DateTimes.
|
|
106
|
+
Used for checking overlaps and scheduling constraints.
|
|
107
|
+
|
|
108
|
+
**Properties:**
|
|
109
|
+
- `start: DateTimeWithUtcOffset | DateTimeWithTimeZone`
|
|
110
|
+
- `end: DateTimeWithUtcOffset | DateTimeWithTimeZone`
|
|
111
|
+
|
|
112
|
+
### SolverClient
|
|
113
|
+
**Properties:**
|
|
114
|
+
- `solve: (request: SolverRequest, options?: { signal?: AbortSignal; }) => Promise<SolverResponse>`
|
|
115
|
+
- `health?: (() => Promise<void>) | undefined`
|
|
116
|
+
|
|
117
|
+
### ModelBuilderConfig
|
|
118
|
+
Configuration for ModelBuilder.
|
|
119
|
+
|
|
120
|
+
**Example:**
|
|
121
|
+
Date range with day-of-week filtering (restaurant closed Mon/Tue)
|
|
122
|
+
```typescript
|
|
123
|
+
const config: ModelBuilderConfig = {
|
|
124
|
+
employees: [...],
|
|
125
|
+
shiftPatterns: [...],
|
|
126
|
+
coverage: [...],
|
|
127
|
+
schedulingPeriod: {
|
|
128
|
+
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
129
|
+
daysOfWeek: ['wednesday', 'thursday', 'friday', 'saturday', 'sunday'],
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Properties:**
|
|
135
|
+
- `employees: SchedulingEmployee[]`
|
|
136
|
+
- `shiftPatterns: ShiftPattern[]`
|
|
137
|
+
- `schedulingPeriod: { dateRange: { start: string; end: string; }; daysOfWeek?: DayOfWeek[]; specificDates?: never; } | { specificDates: string[]; dateRange?: never; daysOfWeek?: never; }` - Defines when scheduling should occur. Can specify either a date range
|
|
138
|
+
(with optional day-of-week filtering) or a list of specific dates.
|
|
139
|
+
- `coverage: CoverageRequirement[]`
|
|
140
|
+
- `rules?: CompilationRule[] | undefined` - Pre-compiled rules; use this for custom rules that are not part of the registry.
|
|
141
|
+
- `ruleConfigs?: CpsatRuleConfigEntry[] | undefined` - Named rule configurations that will be compiled using the provided rule factories.
|
|
142
|
+
- `ruleFactories?: CpsatRuleFactories | undefined` - Rule factories to use when compiling ruleConfigs. Defaults to built-in CP-SAT rules.
|
|
143
|
+
- `reporter?: ValidationReporter | undefined` - Optional validation reporter for diagnostics.
|
|
144
|
+
|
|
145
|
+
### CompilationResult
|
|
146
|
+
**Properties:**
|
|
147
|
+
- `request: { variables: ({ type: "bool"; name: string; } | { type: "int"; name: string; min: number; max: number; } | { type: "interval"; name: string; start: number; end: number; size: number; presenceVar?: string | undefined; })[]; constraints: ({ type: "linear"; terms: { var: string; coeff: number; }[]; op: "<=" | ">=" | "=="; rhs: number; } | { type: "soft_linear"; terms: { var: string; coeff: number; }[]; op: "<=" | ">="; rhs: number; penalty: number; id?: string | undefined; } | { type: "exactly_one"; vars: string[]; } | { type: "at_most_one"; vars: string[]; } | { type: "implication"; if: string; then: string; } | { type: "bool_or"; vars: string[]; } | { type: "bool_and"; vars: string[]; } | { type: "no_overlap"; intervals: string[]; })[]; objective?: { sense: "minimize" | "maximize"; terms: { var: string; coeff: number; }[]; } | undefined; options?: { timeLimitSeconds?: number | undefined; solutionLimit?: number | undefined; } | undefined; }`
|
|
148
|
+
- `validation: ScheduleValidation`
|
|
149
|
+
- `canSolve: boolean`
|
|
150
|
+
|
|
151
|
+
### CompilationRule
|
|
152
|
+
**Properties:**
|
|
153
|
+
- `compile: (builder: ModelBuilder) => void`
|
|
154
|
+
- `validate?: ((assignments: ResolvedShiftAssignment[], reporter: ValidationReporter, context: RuleValidationContext) => void) | undefined`
|
|
155
|
+
|
|
156
|
+
### RuleValidationContext
|
|
157
|
+
Context provided to rules during post-solve validation.
|
|
158
|
+
|
|
159
|
+
**Properties:**
|
|
160
|
+
- `employees: SchedulingEmployee[]`
|
|
161
|
+
- `days: string[]`
|
|
162
|
+
- `shiftPatterns: ShiftPattern[]`
|
|
163
|
+
|
|
164
|
+
### ShiftAssignment
|
|
165
|
+
A shift assignment extracted from the solver response.
|
|
166
|
+
|
|
167
|
+
**Properties:**
|
|
168
|
+
- `employeeId: string`
|
|
169
|
+
- `shiftPatternId: string`
|
|
170
|
+
- `day: string`
|
|
171
|
+
|
|
172
|
+
### ResolvedShiftAssignment
|
|
173
|
+
A shift assignment with resolved times.
|
|
174
|
+
|
|
175
|
+
**Properties:**
|
|
176
|
+
- `employeeId: string`
|
|
177
|
+
- `day: string`
|
|
178
|
+
- `startTime: TimeOfDay`
|
|
179
|
+
- `endTime: TimeOfDay`
|
|
180
|
+
|
|
181
|
+
### SolverResult
|
|
182
|
+
Parsed solver result with assignments and metadata.
|
|
183
|
+
|
|
184
|
+
**Properties:**
|
|
185
|
+
- `status: "OPTIMAL" | "FEASIBLE" | "INFEASIBLE" | "TIMEOUT" | "ERROR"`
|
|
186
|
+
- `assignments: ShiftAssignment[]`
|
|
187
|
+
- `statistics?: { solveTimeMs?: number | undefined; conflicts?: number | undefined; branches?: number | undefined; } | undefined`
|
|
188
|
+
- `error?: string | undefined`
|
|
189
|
+
|
|
190
|
+
### CpsatRuleRegistry
|
|
191
|
+
**Properties:**
|
|
192
|
+
- `"assign-together": { groupEmployeeIds: [string, string, ...string[]]; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; }`
|
|
193
|
+
- `"employee-assignment-priority": { preference: "high" | "low"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
194
|
+
- `"location-preference": { locationId: string; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
195
|
+
- `"max-consecutive-days": { days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
196
|
+
- `"max-hours-day": { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
197
|
+
- `"max-hours-week": { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
198
|
+
- `"max-shifts-day": { shifts: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
199
|
+
- `"min-consecutive-days": { days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
200
|
+
- `"min-hours-day": { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
201
|
+
- `"min-hours-week": { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
202
|
+
- `"min-rest-between-shifts": { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
203
|
+
- `"time-off": { priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
204
|
+
|
|
205
|
+
### SemanticTimeDef
|
|
206
|
+
Base definition for a semantic time period.
|
|
207
|
+
|
|
208
|
+
**Properties:**
|
|
209
|
+
- `startTime: TimeOfDay`
|
|
210
|
+
- `endTime: TimeOfDay`
|
|
211
|
+
|
|
212
|
+
### SemanticTimeVariant
|
|
213
|
+
Variant of a semantic time that applies to specific days or dates.
|
|
214
|
+
|
|
215
|
+
**Properties:**
|
|
216
|
+
- `days?: DayOfWeek[] | undefined` - Apply this variant only on these days of the week
|
|
217
|
+
- `dates?: string[] | undefined` - Apply this variant only on these specific dates (YYYY-MM-DD)
|
|
218
|
+
|
|
219
|
+
### SemanticTimeContext
|
|
220
|
+
Result of defineSemanticTimes - provides type-safe coverage function.
|
|
221
|
+
|
|
222
|
+
**Properties:**
|
|
223
|
+
- `defs: { [P in S]: SemanticTimeEntry; }` - The semantic time definitions
|
|
224
|
+
- `coverage: (reqs: MixedCoverageRequirement<S>[]) => MixedCoverageRequirement<S>[]` - Create coverage requirements with type-safe semantic time names.
|
|
225
|
+
Accepts both semantic references and concrete one-off requirements.
|
|
226
|
+
- `resolve: (reqs: MixedCoverageRequirement<S>[], days: string[]) => CoverageRequirement[]` - Resolve all coverage requirements to concrete CoverageRequirement[]
|
|
227
|
+
for the given days in the scheduling horizon.
|
|
228
|
+
|
|
229
|
+
### SchedulingEmployee
|
|
230
|
+
**Properties:**
|
|
231
|
+
- `id: string`
|
|
232
|
+
- `roleIds: string[]`
|
|
233
|
+
- `skillIds?: string[] | undefined`
|
|
234
|
+
|
|
235
|
+
### ShiftPattern
|
|
236
|
+
A shift pattern defines WHEN people can work — the time slots available for assignment.
|
|
237
|
+
|
|
238
|
+
Shift patterns are templates that repeat across all scheduling days. The solver assigns
|
|
239
|
+
team members to these patterns based on coverage requirements and constraints.
|
|
240
|
+
|
|
241
|
+
**Example:**
|
|
242
|
+
// Simple venue: one shift type, anyone can work it
|
|
243
|
+
const patterns: ShiftPattern[] = [
|
|
244
|
+
{ id: "day", startTime: { hours: 9 }, endTime: { hours: 17 } }
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
**Example:**
|
|
248
|
+
// Restaurant: different shifts for different roles
|
|
249
|
+
const patterns: ShiftPattern[] = [
|
|
250
|
+
{ id: "kitchen_morning", startTime: { hours: 6 }, endTime: { hours: 14 }, roleIds: ["chef", "prep_cook"] },
|
|
251
|
+
{ id: "floor_lunch", startTime: { hours: 11 }, endTime: { hours: 15 }, roleIds: ["waiter", "host"] },
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
**Properties:**
|
|
255
|
+
- `id: string` - Unique identifier for this shift pattern.
|
|
256
|
+
Used in assignments and rule configurations.
|
|
257
|
+
- `roleIds?: [string, ...string[]] | undefined` - Restricts who can be assigned to this shift based on their roles.
|
|
258
|
+
|
|
259
|
+
- If omitted: anyone can work this shift
|
|
260
|
+
- If provided: only team members whose roleIds overlap with this list can be assigned
|
|
261
|
+
|
|
262
|
+
Most venues have the same shifts for everyone and don't need this.
|
|
263
|
+
Use it when different roles have different schedules (e.g., kitchen staff starts
|
|
264
|
+
earlier than floor staff).
|
|
265
|
+
- `daysOfWeek?: DayOfWeek[] | undefined` - Restricts which days of the week this shift pattern can be used.
|
|
266
|
+
|
|
267
|
+
- If omitted: shift can be used on any day
|
|
268
|
+
- If provided: shift can only be assigned on the specified days
|
|
269
|
+
|
|
270
|
+
**Example:**
|
|
271
|
+
```typescript
|
|
272
|
+
// Saturday-only short shift
|
|
273
|
+
{ id: "saturday_shift", startTime: t(9), endTime: t(14), daysOfWeek: ["saturday"] }
|
|
274
|
+
|
|
275
|
+
// Weekday-only full shift
|
|
276
|
+
{ id: "full_shift", startTime: t(9), endTime: t(18), daysOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"] }
|
|
277
|
+
```
|
|
278
|
+
- `locationId?: string | undefined` - Physical location where this shift takes place.
|
|
279
|
+
Used for multi-location scheduling and location-based constraints.
|
|
280
|
+
- `startTime: TimeOfDay` - When the shift starts (e.g., { hours: 9, minutes: 0 } for 9:00 AM)
|
|
281
|
+
- `endTime: TimeOfDay` - When the shift ends (e.g., { hours: 17, minutes: 30 } for 5:30 PM)
|
|
282
|
+
|
|
283
|
+
### TimeInterval
|
|
284
|
+
**Properties:**
|
|
285
|
+
- `day: string`
|
|
286
|
+
- `startTime: TimeOfDay`
|
|
287
|
+
- `endTime: TimeOfDay`
|
|
288
|
+
|
|
289
|
+
### ModelBuilderOptions
|
|
290
|
+
**Properties:**
|
|
291
|
+
- `weekStartsOn?: DayOfWeek | undefined`
|
|
292
|
+
- `solverOptions?: { timeLimitSeconds?: number | undefined; solutionLimit?: number | undefined; } | undefined`
|
|
293
|
+
- `coverageBucketMinutes?: number | undefined` - Bucket size used when translating coverage requirements into time-indexed constraints.
|
|
294
|
+
Smaller buckets are more accurate but increase the number of constraints.
|
|
295
|
+
- `fairDistribution?: boolean | undefined` - Whether to enable fair distribution of shifts across team members.
|
|
296
|
+
|
|
297
|
+
When enabled (default), the solver minimizes the maximum number of shifts
|
|
298
|
+
any single person works, ensuring work is distributed evenly. Each person
|
|
299
|
+
works between floor(total/n) and ceil(total/n) shifts.
|
|
300
|
+
|
|
301
|
+
Disable this if you want other rules (like employee-assignment-priority)
|
|
302
|
+
to have full control over shift distribution.
|
|
303
|
+
|
|
304
|
+
### ValidationReporter
|
|
305
|
+
**Properties:**
|
|
306
|
+
- `excludeFromCoverage: (exclusion: CoverageExclusion) => void`
|
|
307
|
+
- `reportCoverageError: (error: Omit<CoverageError, "type" | "id">) => void`
|
|
308
|
+
- `reportRuleError: (error: Omit<RuleError, "type" | "id">) => void`
|
|
309
|
+
- `reportSolverError: (reason: string) => void`
|
|
310
|
+
- `reportCoverageViolation: (violation: Omit<CoverageViolation, "type" | "id">) => void`
|
|
311
|
+
- `reportRuleViolation: (violation: Omit<RuleViolation, "type" | "id">) => void`
|
|
312
|
+
- `reportCoveragePassed: (passed: Omit<CoveragePassed, "type" | "id">) => void`
|
|
313
|
+
- `reportRulePassed: (passed: Omit<RulePassed, "type" | "id">) => void`
|
|
314
|
+
- `trackConstraint: (constraint: TrackedConstraint) => void`
|
|
315
|
+
- `hasErrors: () => boolean`
|
|
316
|
+
- `getValidation: () => ScheduleValidation`
|
|
317
|
+
- `getExclusions: () => CoverageExclusion[]`
|
|
318
|
+
- `analyzeSolution: (response: SolverResponse) => void`
|
|
319
|
+
|
|
320
|
+
### TrackedConstraint
|
|
321
|
+
**Properties:**
|
|
322
|
+
- `id: string`
|
|
323
|
+
- `type: "coverage" | "rule"`
|
|
324
|
+
- `rule?: string | undefined`
|
|
325
|
+
- `description: string`
|
|
326
|
+
- `targetValue: number`
|
|
327
|
+
- `comparator: "<=" | ">="`
|
|
328
|
+
- `day?: string | undefined`
|
|
329
|
+
- `timeSlot?: string | undefined`
|
|
330
|
+
- `roleIds?: string[] | undefined`
|
|
331
|
+
- `skillIds?: readonly string[] | undefined`
|
|
332
|
+
- `context: ValidationContext`
|
|
333
|
+
- `groupKey?: GroupKey | undefined`
|
|
334
|
+
|
|
335
|
+
### CoverageExclusion
|
|
336
|
+
Coverage exclusion - indicates a team member is unavailable for coverage during a time period.
|
|
337
|
+
Used during compile-time to determine coverage feasibility.
|
|
338
|
+
|
|
339
|
+
**Properties:**
|
|
340
|
+
- `employeeId: string`
|
|
341
|
+
- `day: string`
|
|
342
|
+
- `startTime?: TimeOfDay | undefined`
|
|
343
|
+
- `endTime?: TimeOfDay | undefined`
|
|
344
|
+
|
|
345
|
+
### ScheduleValidation
|
|
346
|
+
**Properties:**
|
|
347
|
+
- `errors: readonly ScheduleError[]`
|
|
348
|
+
- `violations: readonly ScheduleViolation[]`
|
|
349
|
+
- `passed: readonly SchedulePassed[]`
|
|
350
|
+
|
|
351
|
+
### CoverageError
|
|
352
|
+
**Properties:**
|
|
353
|
+
- `id: string`
|
|
354
|
+
- `type: "coverage"`
|
|
355
|
+
- `day: string`
|
|
356
|
+
- `timeSlots: readonly string[]`
|
|
357
|
+
- `roleIds?: string[] | undefined`
|
|
358
|
+
- `skillIds?: readonly string[] | undefined`
|
|
359
|
+
- `reason: string`
|
|
360
|
+
- `suggestions?: readonly string[] | undefined`
|
|
361
|
+
- `groupKey?: GroupKey | undefined`
|
|
362
|
+
|
|
363
|
+
### CoverageViolation
|
|
364
|
+
**Properties:**
|
|
365
|
+
- `id: string`
|
|
366
|
+
- `type: "coverage"`
|
|
367
|
+
- `day: string`
|
|
368
|
+
- `timeSlots: readonly string[]`
|
|
369
|
+
- `roleIds?: string[] | undefined`
|
|
370
|
+
- `skillIds?: readonly string[] | undefined`
|
|
371
|
+
- `targetCount: number`
|
|
372
|
+
- `actualCount: number`
|
|
373
|
+
- `shortfall: number`
|
|
374
|
+
- `groupKey?: GroupKey | undefined`
|
|
375
|
+
|
|
376
|
+
### CoveragePassed
|
|
377
|
+
**Properties:**
|
|
378
|
+
- `id: string`
|
|
379
|
+
- `type: "coverage"`
|
|
380
|
+
- `day: string`
|
|
381
|
+
- `timeSlots: readonly string[]`
|
|
382
|
+
- `roleIds?: string[] | undefined`
|
|
383
|
+
- `skillIds?: readonly string[] | undefined`
|
|
384
|
+
- `description: string`
|
|
385
|
+
- `groupKey?: GroupKey | undefined`
|
|
386
|
+
|
|
387
|
+
### RuleError
|
|
388
|
+
**Properties:**
|
|
389
|
+
- `id: string`
|
|
390
|
+
- `type: "rule"`
|
|
391
|
+
- `rule: string`
|
|
392
|
+
- `reason: string`
|
|
393
|
+
- `context: ValidationContext`
|
|
394
|
+
- `suggestions?: readonly string[] | undefined`
|
|
395
|
+
- `groupKey?: GroupKey | undefined`
|
|
396
|
+
|
|
397
|
+
### RuleViolation
|
|
398
|
+
**Properties:**
|
|
399
|
+
- `id: string`
|
|
400
|
+
- `type: "rule"`
|
|
401
|
+
- `rule: string`
|
|
402
|
+
- `reason: string`
|
|
403
|
+
- `context: ValidationContext`
|
|
404
|
+
- `shortfall?: number | undefined`
|
|
405
|
+
- `overflow?: number | undefined`
|
|
406
|
+
- `groupKey?: GroupKey | undefined`
|
|
407
|
+
|
|
408
|
+
### RulePassed
|
|
409
|
+
**Properties:**
|
|
410
|
+
- `id: string`
|
|
411
|
+
- `type: "rule"`
|
|
412
|
+
- `rule: string`
|
|
413
|
+
- `description: string`
|
|
414
|
+
- `context: ValidationContext`
|
|
415
|
+
- `groupKey?: GroupKey | undefined`
|
|
416
|
+
|
|
417
|
+
### SolverError
|
|
418
|
+
**Properties:**
|
|
419
|
+
- `id: string`
|
|
420
|
+
- `type: "solver"`
|
|
421
|
+
- `reason: string`
|
|
422
|
+
|
|
423
|
+
### ValidationContext
|
|
424
|
+
Context shared across validation results for grouping/display.
|
|
425
|
+
|
|
426
|
+
**Properties:**
|
|
427
|
+
- `days?: string[] | undefined`
|
|
428
|
+
- `timeSlots?: string[] | undefined`
|
|
429
|
+
- `employeeIds?: string[] | undefined`
|
|
430
|
+
|
|
431
|
+
### ValidationSummary
|
|
432
|
+
Summary of validation items grouped by their source instruction.
|
|
433
|
+
Use `summarizeValidation()` to create these from a ScheduleValidation.
|
|
434
|
+
|
|
435
|
+
**Properties:**
|
|
436
|
+
- `groupKey: string & { readonly [GroupKeyBrand]: never; }`
|
|
437
|
+
- `type: "coverage" | "rule"`
|
|
438
|
+
- `description: string`
|
|
439
|
+
- `days: readonly string[]`
|
|
440
|
+
- `status: "passed" | "partial" | "failed"`
|
|
441
|
+
- `passedCount: number`
|
|
442
|
+
- `violatedCount: number`
|
|
443
|
+
- `errorCount: number`
|
|
444
|
+
|
|
445
|
+
### CoverageValidationResult
|
|
446
|
+
Result of coverage role validation.
|
|
447
|
+
|
|
448
|
+
**Properties:**
|
|
449
|
+
- `valid: boolean`
|
|
450
|
+
- `unknownRoles: string[]` - Role IDs used in coverage that don't match any team member
|
|
451
|
+
- `knownRoles: string[]` - Role IDs used in coverage that match team members
|
|
452
|
+
|
|
453
|
+
### SkillValidationResult
|
|
454
|
+
Result of coverage skill validation.
|
|
455
|
+
|
|
456
|
+
**Properties:**
|
|
457
|
+
- `valid: boolean`
|
|
458
|
+
- `unknownSkills: string[]` - Skill IDs used in coverage that don't match any team member
|
|
459
|
+
- `knownSkills: string[]` - Skill IDs used in coverage that match team members
|
|
460
|
+
|
|
461
|
+
### CoverageConfigValidationResult
|
|
462
|
+
Combined validation result for coverage requirements.
|
|
463
|
+
|
|
464
|
+
**Properties:**
|
|
465
|
+
- `valid: boolean`
|
|
466
|
+
- `roles: CoverageValidationResult`
|
|
467
|
+
- `skills: SkillValidationResult`
|
|
468
|
+
- `errors: string[]` - Human-readable error messages
|
|
469
|
+
|
|
470
|
+
## Type Aliases
|
|
471
|
+
|
|
472
|
+
### DayOfWeek
|
|
473
|
+
```typescript
|
|
474
|
+
"monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
### DateTime
|
|
479
|
+
Date and time representation supporting both UTC offset and timezone-aware formats.
|
|
480
|
+
|
|
481
|
+
Can be specified either with a UTC offset (e.g., "-08:00") or with a timezone ID
|
|
482
|
+
(e.g., "America/Los_Angeles").
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
DateTimeWithUtcOffset | DateTimeWithTimeZone
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
### SchedulingPeriod
|
|
490
|
+
Defines a scheduling period either as a date range or specific dates.
|
|
491
|
+
|
|
492
|
+
Use this to specify when scheduling should occur. This is more expressive
|
|
493
|
+
than a simple list of days because it can filter by day-of-week.
|
|
494
|
+
|
|
495
|
+
**Example:**
|
|
496
|
+
Date range with day-of-week filtering (restaurant closed Mon/Tue)
|
|
497
|
+
```typescript
|
|
498
|
+
const period: SchedulingPeriod = {
|
|
499
|
+
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
500
|
+
daysOfWeek: ['wednesday', 'thursday', 'friday', 'saturday', 'sunday'],
|
|
501
|
+
};
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Example:**
|
|
505
|
+
Date range for all days
|
|
506
|
+
```typescript
|
|
507
|
+
const period: SchedulingPeriod = {
|
|
508
|
+
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
509
|
+
};
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Example:**
|
|
513
|
+
Specific dates (non-contiguous or custom selection)
|
|
514
|
+
```typescript
|
|
515
|
+
const period: SchedulingPeriod = {
|
|
516
|
+
specificDates: ['2025-02-05', '2025-02-07', '2025-02-10'],
|
|
517
|
+
};
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
{ dateRange: { start: string; end: string; }; daysOfWeek?: DayOfWeek[]; specificDates?: never; } | { specificDates: string[]; dateRange?: never; daysOfWeek?: never; }
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
### SolverRequest
|
|
526
|
+
```typescript
|
|
527
|
+
{ variables: ({ type: "bool"; name: string; } | { type: "int"; name: string; min: number; max: number; } | { type: "interval"; name: string; start: number; end: number; size: number; presenceVar?: string | undefined; })[]; constraints: ({ type: "linear"; terms: { var: string; coeff: number; }[]; op: "<=" | ">=" | "=="; rhs: number; } | { type: "soft_linear"; terms: { var: string; coeff: number; }[]; op: "<=" | ">="; rhs: number; penalty: number; id?: string | undefined; } | { type: "exactly_one"; vars: string[]; } | { type: "at_most_one"; vars: string[]; } | { type: "implication"; if: string; then: string; } | { type: "bool_or"; vars: string[]; } | { type: "bool_and"; vars: string[]; } | { type: "no_overlap"; intervals: string[]; })[]; objective?: { sense: "minimize" | "maximize"; terms: { var: string; coeff: number; }[]; } | undefined; options?: { timeLimitSeconds?: number | undefined; solutionLimit?: number | undefined; } | undefined; }
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
### SolverResponse
|
|
532
|
+
```typescript
|
|
533
|
+
{ status: "OPTIMAL" | "FEASIBLE" | "INFEASIBLE" | "TIMEOUT" | "ERROR"; values?: Record<string, number> | undefined; statistics?: { solveTimeMs?: number | undefined; conflicts?: number | undefined; branches?: number | undefined; } | undefined; error?: string | undefined; solutionInfo?: string | undefined; softViolations?: { constraintId: string; violationAmount: number; targetValue: number; actualValue: number; }[] | undefined; }
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
### SolverVariable
|
|
538
|
+
```typescript
|
|
539
|
+
{ type: "bool"; name: string; } | { type: "int"; name: string; min: number; max: number; } | { type: "interval"; name: string; start: number; end: number; size: number; presenceVar?: string | undefined; }
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
### SolverConstraint
|
|
544
|
+
```typescript
|
|
545
|
+
{ type: "linear"; terms: { var: string; coeff: number; }[]; op: "<=" | ">=" | "=="; rhs: number; } | { type: "soft_linear"; terms: { var: string; coeff: number; }[]; op: "<=" | ">="; rhs: number; penalty: number; id?: string | undefined; } | { type: "exactly_one"; vars: string[]; } | { type: "at_most_one"; vars: string[]; } | { type: "implication"; if: string; then: string; } | { type: "bool_or"; vars: string[]; } | { type: "bool_and"; vars: string[]; } | { type: "no_overlap"; intervals: string[]; }
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
### SolverTerm
|
|
550
|
+
```typescript
|
|
551
|
+
{ var: string; coeff: number; }
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
### SolverObjective
|
|
556
|
+
```typescript
|
|
557
|
+
{ sense: "minimize" | "maximize"; terms: { var: string; coeff: number; }[]; }
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
### SolverStatus
|
|
562
|
+
```typescript
|
|
563
|
+
"OPTIMAL" | "FEASIBLE" | "INFEASIBLE" | "TIMEOUT" | "ERROR"
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
### SoftConstraintViolation
|
|
568
|
+
```typescript
|
|
569
|
+
{ constraintId: string; violationAmount: number; targetValue: number; actualValue: number; }
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
### FetcherLike
|
|
574
|
+
```typescript
|
|
575
|
+
((input: string | URL | Request, init?: RequestInit) => Promise<Response>) | { fetch: typeof fetch; }
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
### AssignTogetherConfig
|
|
580
|
+
```typescript
|
|
581
|
+
{ groupEmployeeIds: [string, string, ...string[]]; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; }
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
### EmployeeAssignmentPriorityConfig
|
|
586
|
+
```typescript
|
|
587
|
+
{ preference: "high" | "low"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
### LocationPreferenceConfig
|
|
592
|
+
```typescript
|
|
593
|
+
{ locationId: string; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
### MaxConsecutiveDaysConfig
|
|
598
|
+
```typescript
|
|
599
|
+
{ days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
### MaxHoursDayConfig
|
|
604
|
+
```typescript
|
|
605
|
+
{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
### MaxHoursWeekConfig
|
|
610
|
+
```typescript
|
|
611
|
+
{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
### MaxShiftsDayConfig
|
|
616
|
+
```typescript
|
|
617
|
+
{ shifts: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
### MinConsecutiveDaysConfig
|
|
622
|
+
```typescript
|
|
623
|
+
{ days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
### MinHoursDayConfig
|
|
628
|
+
```typescript
|
|
629
|
+
{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
### MinHoursWeekConfig
|
|
634
|
+
```typescript
|
|
635
|
+
{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
### MinRestBetweenShiftsConfig
|
|
640
|
+
```typescript
|
|
641
|
+
{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
### TimeOffConfig
|
|
646
|
+
```typescript
|
|
647
|
+
{ priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
### CpsatRuleName
|
|
652
|
+
```typescript
|
|
653
|
+
keyof CpsatRuleRegistry
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
### CpsatRuleConfigEntry
|
|
658
|
+
```typescript
|
|
659
|
+
{ name: K; config: CpsatRuleRegistry[K]; }
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
### CpsatRuleFactories
|
|
664
|
+
```typescript
|
|
665
|
+
{ [ruleName: string]: CreateCpsatRuleFunction<any>; }
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
### BuiltInCpsatRuleFactories
|
|
670
|
+
```typescript
|
|
671
|
+
[Complex type: __type]
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
### CreateCpsatRuleFunction
|
|
676
|
+
```typescript
|
|
677
|
+
(config: TConfig) => CompilationRule
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
### CpsatRuleRegistryFromFactories
|
|
682
|
+
```typescript
|
|
683
|
+
{ [K in keyof F]: InferCpsatRuleConfig<F[K]>; }
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
### SemanticTimeEntry
|
|
688
|
+
A semantic time can be a simple definition (applies every day)
|
|
689
|
+
or an array of variants with different times for different days/dates.
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
SemanticTimeDef | SemanticTimeVariant[]
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
### SemanticCoverageRequirement
|
|
697
|
+
Coverage requirement that references a semantic time by name.
|
|
698
|
+
Type-safe: S is constrained to known semantic time names.
|
|
699
|
+
|
|
700
|
+
This is a discriminated union enforcing at compile time that at least
|
|
701
|
+
one of `roleIds` or `skillIds` must be provided.
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
RoleBasedSemanticCoverageRequirement<S> | SkillBasedSemanticCoverageRequirement<S>
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
### ConcreteCoverageRequirement
|
|
709
|
+
Concrete coverage requirement with explicit day and times.
|
|
710
|
+
Used for one-off requirements that don't fit a semantic time.
|
|
711
|
+
|
|
712
|
+
This is a discriminated union enforcing at compile time that at least
|
|
713
|
+
one of `roleIds` or `skillIds` must be provided.
|
|
714
|
+
|
|
715
|
+
```typescript
|
|
716
|
+
RoleBasedConcreteCoverageRequirement | SkillBasedConcreteCoverageRequirement
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
### MixedCoverageRequirement
|
|
721
|
+
Union type for coverage - either semantic (type-safe) or concrete.
|
|
722
|
+
|
|
723
|
+
```typescript
|
|
724
|
+
ConcreteCoverageRequirement | SemanticCoverageRequirement<S>
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
### Employee
|
|
729
|
+
```typescript
|
|
730
|
+
SchedulingEmployee
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
### CoverageRequirement
|
|
735
|
+
Defines staffing needs for a specific time period.
|
|
736
|
+
|
|
737
|
+
This is a discriminated union that enforces at compile time that at least
|
|
738
|
+
one of `roleIds` or `skillIds` must be provided:
|
|
739
|
+
|
|
740
|
+
- Role-based: `{ roleIds: ["waiter"], ... }` - anyone with ANY of these roles (OR logic)
|
|
741
|
+
- Role + skill: `{ roleIds: ["waiter"], skillIds: ["senior"], ... }` - role AND skills
|
|
742
|
+
- Skill-only: `{ skillIds: ["keyholder"], ... }` - any role with ALL skills (AND logic)
|
|
743
|
+
|
|
744
|
+
**Example:**
|
|
745
|
+
// Need 2 waiters during lunch (role-based)
|
|
746
|
+
{ day: "2024-01-01", startTime: { hours: 11 }, endTime: { hours: 14 }, roleIds: ["waiter"], targetCount: 2, priority: "MANDATORY" }
|
|
747
|
+
|
|
748
|
+
**Example:**
|
|
749
|
+
// Need 1 manager OR supervisor during service (OR logic on roles)
|
|
750
|
+
{ day: "2024-01-01", startTime: { hours: 11 }, endTime: { hours: 22 }, roleIds: ["manager", "supervisor"], targetCount: 1, priority: "MANDATORY" }
|
|
751
|
+
|
|
752
|
+
**Example:**
|
|
753
|
+
// Need 1 keyholder for opening (skill-only, any role)
|
|
754
|
+
{ day: "2024-01-01", startTime: { hours: 6 }, endTime: { hours: 8 }, skillIds: ["keyholder"], targetCount: 1, priority: "MANDATORY" }
|
|
755
|
+
|
|
756
|
+
**Example:**
|
|
757
|
+
// Need 1 senior waiter for training shift (role + skill filter)
|
|
758
|
+
{ day: "2024-01-01", startTime: { hours: 9 }, endTime: { hours: 17 }, roleIds: ["waiter"], skillIds: ["senior"], targetCount: 1, priority: "HIGH" }
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
RoleBasedCoverageRequirement | SkillBasedCoverageRequirement
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
### Priority
|
|
766
|
+
```typescript
|
|
767
|
+
"LOW" | "MEDIUM" | "HIGH" | "MANDATORY"
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
### ScheduleError
|
|
772
|
+
```typescript
|
|
773
|
+
CoverageError | RuleError | SolverError
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
### ScheduleViolation
|
|
778
|
+
```typescript
|
|
779
|
+
CoverageViolation | RuleViolation
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
### SchedulePassed
|
|
784
|
+
```typescript
|
|
785
|
+
CoveragePassed | RulePassed
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
### GroupKey
|
|
790
|
+
Branded type for validation group keys.
|
|
791
|
+
Groups related validation items that originated from the same instruction.
|
|
792
|
+
|
|
793
|
+
```typescript
|
|
794
|
+
string & { readonly [GroupKeyBrand]: never; }
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
### InferCpsatRuleConfig
|
|
799
|
+
```typescript
|
|
800
|
+
T extends CreateCpsatRuleFunction<infer Config> ? Config : never
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
### Term
|
|
805
|
+
```typescript
|
|
806
|
+
{ var: string; coeff: number; }
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
## Functions
|
|
811
|
+
|
|
812
|
+
### dateToCalendarDate
|
|
813
|
+
Converts a JavaScript Date to a CalendarDate
|
|
814
|
+
|
|
815
|
+
**Parameters:**
|
|
816
|
+
- `date: Date`
|
|
817
|
+
|
|
818
|
+
**Returns:** `CalendarDate`
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
### dateTimeToDate
|
|
822
|
+
Converts a DateTime to a JavaScript Date
|
|
823
|
+
Internal helper function
|
|
824
|
+
|
|
825
|
+
**Parameters:**
|
|
826
|
+
- `dateTime: DateTimeWithUtcOffset | DateTimeWithTimeZone`
|
|
827
|
+
|
|
828
|
+
**Returns:** `Date`
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
### compareDateTimes
|
|
832
|
+
Compares two DateTimes
|
|
833
|
+
Returns:
|
|
834
|
+
-1 if dateTime1 < dateTime2
|
|
835
|
+
0 if dateTime1 = dateTime2
|
|
836
|
+
1 if dateTime1 > dateTime2
|
|
837
|
+
|
|
838
|
+
**Parameters:**
|
|
839
|
+
- `dateTime1: DateTimeWithUtcOffset | DateTimeWithTimeZone`
|
|
840
|
+
- `dateTime2: DateTimeWithUtcOffset | DateTimeWithTimeZone`
|
|
841
|
+
|
|
842
|
+
**Returns:** `number`
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
### toDayOfWeek
|
|
846
|
+
Helper to get the day of week name from a Date (local time)
|
|
847
|
+
|
|
848
|
+
**Parameters:**
|
|
849
|
+
- `date: Date`
|
|
850
|
+
|
|
851
|
+
**Returns:** `"monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"`
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
### toDayOfWeekUTC
|
|
855
|
+
Helper to get the day of week name from a Date (UTC)
|
|
856
|
+
Use this when working with date strings like "2026-01-10" that are timezone-agnostic.
|
|
857
|
+
|
|
858
|
+
**Parameters:**
|
|
859
|
+
- `date: Date`
|
|
860
|
+
|
|
861
|
+
**Returns:** `"monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"`
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
### formatDateString
|
|
865
|
+
Formats a date as YYYY-MM-DD string
|
|
866
|
+
|
|
867
|
+
**Parameters:**
|
|
868
|
+
- `date: Date`
|
|
869
|
+
|
|
870
|
+
**Returns:** `string`
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
### generateDays
|
|
874
|
+
Generates an array of day strings (YYYY-MM-DD) from a time horizon.
|
|
875
|
+
|
|
876
|
+
**Example:**
|
|
877
|
+
```typescript
|
|
878
|
+
const days = generateDays({
|
|
879
|
+
start: new Date('2025-01-01'),
|
|
880
|
+
end: new Date('2025-01-04')
|
|
881
|
+
});
|
|
882
|
+
// Returns: ["2025-01-01", "2025-01-02", "2025-01-03", "2025-01-04"]
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
**Parameters:**
|
|
886
|
+
- `horizon: { start: Date; end: Date; }`
|
|
887
|
+
|
|
888
|
+
**Returns:** `string[]`
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
### splitPeriodIntoDays
|
|
892
|
+
Splits a time period into consecutive day ranges.
|
|
893
|
+
|
|
894
|
+
Each range represents a single calendar day within the period from start to end.
|
|
895
|
+
This is useful for rules that need to apply constraints on a per-day basis,
|
|
896
|
+
such as maximum or minimum hours per day.
|
|
897
|
+
|
|
898
|
+
**Example:**
|
|
899
|
+
```typescript
|
|
900
|
+
const ranges = splitPeriodIntoDays({
|
|
901
|
+
start: new Date('2025-01-01'),
|
|
902
|
+
end: new Date('2025-01-03')
|
|
903
|
+
});
|
|
904
|
+
// Returns:
|
|
905
|
+
// [
|
|
906
|
+
// [Date('2025-01-01'), Date('2025-01-02')],
|
|
907
|
+
// [Date('2025-01-02'), Date('2025-01-03')]
|
|
908
|
+
// ]
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**Parameters:**
|
|
912
|
+
- `{ start, end }: { start: Date; end: Date; }`
|
|
913
|
+
|
|
914
|
+
**Returns:** `[Date, Date][]`
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
### splitPeriodIntoWeeks
|
|
918
|
+
Splits a time period into consecutive week ranges.
|
|
919
|
+
|
|
920
|
+
Each range represents a week period starting on the specified day of the week.
|
|
921
|
+
This is useful for rules that need to apply constraints on a per-week basis,
|
|
922
|
+
such as maximum or minimum hours per week.
|
|
923
|
+
|
|
924
|
+
The first range starts at the provided start date (not necessarily on weekStartsOn).
|
|
925
|
+
Subsequent ranges align to the weekStartsOn day. The last range's end date will be
|
|
926
|
+
the provided end date.
|
|
927
|
+
|
|
928
|
+
**Example:**
|
|
929
|
+
```typescript
|
|
930
|
+
const ranges = splitPeriodIntoWeeks({
|
|
931
|
+
start: new Date('2025-01-01'), // Wednesday
|
|
932
|
+
end: new Date('2025-01-15'),
|
|
933
|
+
weekStartsOn: 'monday'
|
|
934
|
+
});
|
|
935
|
+
// Returns ranges starting from Jan 1 (Wed), then aligning to Mondays:
|
|
936
|
+
// [
|
|
937
|
+
// [Date('2025-01-01 Wed'), Date('2025-01-06 Mon')],
|
|
938
|
+
// [Date('2025-01-06 Mon'), Date('2025-01-13 Mon')],
|
|
939
|
+
// [Date('2025-01-13 Mon'), Date('2025-01-15 Wed')]
|
|
940
|
+
// ]
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**Parameters:**
|
|
944
|
+
- `{
|
|
945
|
+
start,
|
|
946
|
+
end,
|
|
947
|
+
weekStartsOn,
|
|
948
|
+
}: { start: Date; end: Date; weekStartsOn: DayOfWeek; }`
|
|
949
|
+
|
|
950
|
+
**Returns:** `[Date, Date][]`
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
### dateTimeRangesOverlap
|
|
954
|
+
Checks if two DateTime ranges overlap in both date and time.
|
|
955
|
+
Ranges overlap if they share any moment in time.
|
|
956
|
+
|
|
957
|
+
Two ranges overlap if: range1.start < range2.end AND range2.start < range1.end
|
|
958
|
+
|
|
959
|
+
**Example:**
|
|
960
|
+
```typescript
|
|
961
|
+
// Same day, overlapping times (9-17 overlaps with 12-20)
|
|
962
|
+
dateTimeRangesOverlap(
|
|
963
|
+
{
|
|
964
|
+
start: { year: 2025, month: 6, day: 1, hours: 9, minutes: 0 },
|
|
965
|
+
end: { year: 2025, month: 6, day: 1, hours: 17, minutes: 0 }
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
start: { year: 2025, month: 6, day: 1, hours: 12, minutes: 0 },
|
|
969
|
+
end: { year: 2025, month: 6, day: 1, hours: 20, minutes: 0 }
|
|
970
|
+
}
|
|
971
|
+
); // true
|
|
972
|
+
|
|
973
|
+
// Different days - no overlap
|
|
974
|
+
dateTimeRangesOverlap(
|
|
975
|
+
{
|
|
976
|
+
start: { year: 2025, month: 6, day: 1, hours: 9, minutes: 0 },
|
|
977
|
+
end: { year: 2025, month: 6, day: 1, hours: 17, minutes: 0 }
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
start: { year: 2025, month: 6, day: 2, hours: 9, minutes: 0 },
|
|
981
|
+
end: { year: 2025, month: 6, day: 2, hours: 17, minutes: 0 }
|
|
982
|
+
}
|
|
983
|
+
); // false
|
|
984
|
+
|
|
985
|
+
// Works naturally with Shift objects
|
|
986
|
+
dateTimeRangesOverlap(
|
|
987
|
+
{ start: shift1.startDateTime, end: shift1.endDateTime },
|
|
988
|
+
{ start: shift2.startDateTime, end: shift2.endDateTime }
|
|
989
|
+
);
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
**Parameters:**
|
|
993
|
+
- `range1: DateTimeRange`
|
|
994
|
+
- `range2: DateTimeRange`
|
|
995
|
+
|
|
996
|
+
**Returns:** `boolean`
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
### daysBetween
|
|
1000
|
+
Calculates the number of complete days between two dates
|
|
1001
|
+
|
|
1002
|
+
**Example:**
|
|
1003
|
+
```typescript
|
|
1004
|
+
daysBetween(new Date('2025-01-01'), new Date('2025-01-05')); // 4
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
**Parameters:**
|
|
1008
|
+
- `start: Date`
|
|
1009
|
+
- `end: Date`
|
|
1010
|
+
|
|
1011
|
+
**Returns:** `number`
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
### resolveDaysFromPeriod
|
|
1015
|
+
Computes the list of day strings (YYYY-MM-DD) from a SchedulingPeriod.
|
|
1016
|
+
|
|
1017
|
+
For date ranges, generates all days between start and end (inclusive),
|
|
1018
|
+
optionally filtering to specific days of the week.
|
|
1019
|
+
|
|
1020
|
+
For specific dates, returns the dates as-is (sorted).
|
|
1021
|
+
|
|
1022
|
+
**Example:**
|
|
1023
|
+
Date range with day-of-week filter
|
|
1024
|
+
```typescript
|
|
1025
|
+
const days = resolveDaysFromPeriod({
|
|
1026
|
+
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
1027
|
+
daysOfWeek: ['wednesday', 'friday'],
|
|
1028
|
+
});
|
|
1029
|
+
// Returns: ['2025-02-05', '2025-02-07'] (Wed and Fri only)
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
**Example:**
|
|
1033
|
+
Date range without filter
|
|
1034
|
+
```typescript
|
|
1035
|
+
const days = resolveDaysFromPeriod({
|
|
1036
|
+
dateRange: { start: '2025-02-03', end: '2025-02-05' },
|
|
1037
|
+
});
|
|
1038
|
+
// Returns: ['2025-02-03', '2025-02-04', '2025-02-05']
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
**Example:**
|
|
1042
|
+
Specific dates
|
|
1043
|
+
```typescript
|
|
1044
|
+
const days = resolveDaysFromPeriod({
|
|
1045
|
+
specificDates: ['2025-02-07', '2025-02-03', '2025-02-10'],
|
|
1046
|
+
});
|
|
1047
|
+
// Returns: ['2025-02-03', '2025-02-07', '2025-02-10'] (sorted)
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
**Parameters:**
|
|
1051
|
+
- `period: { dateRange: { start: string; end: string; }; daysOfWeek?: DayOfWeek[]; specificDates?: never; } | { specificDates: string[]; dateRange?: never; daysOfWeek?: never; }`
|
|
1052
|
+
|
|
1053
|
+
**Returns:** `string[]`
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
### parseSolverResponse
|
|
1057
|
+
Extracts shift assignments from solver response.
|
|
1058
|
+
|
|
1059
|
+
Parses variable names matching the pattern `assign:${employeeId}:${patternId}:${day}`
|
|
1060
|
+
and returns assignments where the variable value is 1 (true).
|
|
1061
|
+
|
|
1062
|
+
IDs are validated by ModelBuilder to not contain colons,
|
|
1063
|
+
ensuring unambiguous parsing.
|
|
1064
|
+
|
|
1065
|
+
**Example:**
|
|
1066
|
+
```typescript
|
|
1067
|
+
const response = await client.solve(request);
|
|
1068
|
+
const result = parseSolverResponse(response);
|
|
1069
|
+
|
|
1070
|
+
if (result.status === "OPTIMAL" || result.status === "FEASIBLE") {
|
|
1071
|
+
for (const assignment of result.assignments) {
|
|
1072
|
+
console.log(`${assignment.employeeId} works ${assignment.shiftPatternId} on ${assignment.day}`);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
**Parameters:**
|
|
1078
|
+
- `response: { status: "OPTIMAL" | "FEASIBLE" | "INFEASIBLE" | "TIMEOUT" | "ERROR"; values?: Record<string, number> | undefined; statistics?: { solveTimeMs?: number | undefined; conflicts?: number | undefined; branches?: number | undefined; } | undefined; error?: string | undefined; solutionInfo?: string | undefined; softViolations?: { constraintId: string; violationAmount: number; targetValue: number; actualValue: number; }[] | undefined; }`
|
|
1079
|
+
|
|
1080
|
+
**Returns:** `SolverResult`
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
### resolveAssignments
|
|
1084
|
+
Resolves shift assignments to concrete times using shift patterns.
|
|
1085
|
+
|
|
1086
|
+
**Example:**
|
|
1087
|
+
```typescript
|
|
1088
|
+
const result = parseScheduleResult(response);
|
|
1089
|
+
const resolved = resolveAssignments(result.assignments, shiftPatterns);
|
|
1090
|
+
|
|
1091
|
+
for (const shift of resolved) {
|
|
1092
|
+
console.log(`${shift.employeeId} works ${shift.day} from ${shift.startTime.hours}:${shift.startTime.minutes}`);
|
|
1093
|
+
}
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
**Parameters:**
|
|
1097
|
+
- `assignments: ShiftAssignment[]`
|
|
1098
|
+
- `shiftPatterns: ShiftPattern[]`
|
|
1099
|
+
|
|
1100
|
+
**Returns:** `ResolvedShiftAssignment[]`
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
### createAssignTogetherRule
|
|
1104
|
+
Encourages or enforces that team members in the group work the same shift patterns on a day.
|
|
1105
|
+
For each pair of team members in the group, ensures they are assigned to the same shifts.
|
|
1106
|
+
|
|
1107
|
+
**Example:**
|
|
1108
|
+
```ts
|
|
1109
|
+
const rule = createAssignTogetherRule({
|
|
1110
|
+
groupEmployeeIds: ["alice", "bob", "charlie"],
|
|
1111
|
+
priority: "HIGH",
|
|
1112
|
+
});
|
|
1113
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
**Parameters:**
|
|
1117
|
+
- `config: { groupEmployeeIds: [string, string, ...string[]]; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; }`
|
|
1118
|
+
|
|
1119
|
+
**Returns:** `CompilationRule`
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
### createEmployeeAssignmentPriorityRule
|
|
1123
|
+
Adds objective weight to prefer or avoid assigning team members.
|
|
1124
|
+
|
|
1125
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1126
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1127
|
+
|
|
1128
|
+
**Example:**
|
|
1129
|
+
Prefer specific team members
|
|
1130
|
+
```ts
|
|
1131
|
+
createEmployeeAssignmentPriorityRule({
|
|
1132
|
+
employeeIds: ["alice", "bob"],
|
|
1133
|
+
preference: "high",
|
|
1134
|
+
});
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
**Example:**
|
|
1138
|
+
Avoid assigning students on weekdays
|
|
1139
|
+
```ts
|
|
1140
|
+
createEmployeeAssignmentPriorityRule({
|
|
1141
|
+
roleIds: ["student"],
|
|
1142
|
+
dayOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"],
|
|
1143
|
+
preference: "low",
|
|
1144
|
+
});
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
**Example:**
|
|
1148
|
+
Prefer experienced staff on weekends
|
|
1149
|
+
```ts
|
|
1150
|
+
createEmployeeAssignmentPriorityRule({
|
|
1151
|
+
skillIds: ["senior"],
|
|
1152
|
+
dayOfWeek: ["saturday", "sunday"],
|
|
1153
|
+
preference: "high",
|
|
1154
|
+
});
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
**Parameters:**
|
|
1158
|
+
- `config: { preference: "high" | "low"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1159
|
+
|
|
1160
|
+
**Returns:** `CompilationRule`
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
### createLocationPreferenceRule
|
|
1164
|
+
Prefers assigning a person to shift patterns matching a specific location.
|
|
1165
|
+
|
|
1166
|
+
**Example:**
|
|
1167
|
+
```ts
|
|
1168
|
+
const rule = createLocationPreferenceRule({
|
|
1169
|
+
locationId: "terrace",
|
|
1170
|
+
priority: "HIGH",
|
|
1171
|
+
employeeIds: ["alice"],
|
|
1172
|
+
});
|
|
1173
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
**Parameters:**
|
|
1177
|
+
- `config: { locationId: string; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1178
|
+
|
|
1179
|
+
**Returns:** `CompilationRule`
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
### createMaxConsecutiveDaysRule
|
|
1183
|
+
Limits how many consecutive days a person can be assigned.
|
|
1184
|
+
|
|
1185
|
+
**Example:**
|
|
1186
|
+
```ts
|
|
1187
|
+
const rule = createMaxConsecutiveDaysRule({
|
|
1188
|
+
days: 5,
|
|
1189
|
+
priority: "MANDATORY",
|
|
1190
|
+
});
|
|
1191
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
**Parameters:**
|
|
1195
|
+
- `config: { days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1196
|
+
|
|
1197
|
+
**Returns:** `CompilationRule`
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
### createMaxHoursDayRule
|
|
1201
|
+
Limits how many hours a person can work in a single day.
|
|
1202
|
+
|
|
1203
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1204
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1205
|
+
|
|
1206
|
+
**Example:**
|
|
1207
|
+
Limit everyone to 8 hours per day
|
|
1208
|
+
```ts
|
|
1209
|
+
createMaxHoursDayRule({
|
|
1210
|
+
hours: 8,
|
|
1211
|
+
priority: "MANDATORY",
|
|
1212
|
+
});
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
**Example:**
|
|
1216
|
+
Students limited to 4 hours on weekdays during term
|
|
1217
|
+
```ts
|
|
1218
|
+
createMaxHoursDayRule({
|
|
1219
|
+
roleIds: ["student"],
|
|
1220
|
+
hours: 4,
|
|
1221
|
+
dayOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"],
|
|
1222
|
+
priority: "MANDATORY",
|
|
1223
|
+
});
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
**Parameters:**
|
|
1227
|
+
- `config: { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1228
|
+
|
|
1229
|
+
**Returns:** `CompilationRule`
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
### createMaxHoursWeekRule
|
|
1233
|
+
Caps total hours a person can work within each scheduling week.
|
|
1234
|
+
|
|
1235
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1236
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1237
|
+
Time scoping filters which days within each week count toward the limit.
|
|
1238
|
+
|
|
1239
|
+
**Example:**
|
|
1240
|
+
Limit everyone to 40 hours per week
|
|
1241
|
+
```ts
|
|
1242
|
+
createMaxHoursWeekRule({
|
|
1243
|
+
hours: 40,
|
|
1244
|
+
priority: "HIGH",
|
|
1245
|
+
});
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
**Example:**
|
|
1249
|
+
Students limited to 20 hours during term time
|
|
1250
|
+
```ts
|
|
1251
|
+
createMaxHoursWeekRule({
|
|
1252
|
+
roleIds: ["student"],
|
|
1253
|
+
hours: 20,
|
|
1254
|
+
recurringPeriods: [
|
|
1255
|
+
{ name: "fall-term", startMonth: 9, startDay: 1, endMonth: 12, endDay: 15 },
|
|
1256
|
+
{ name: "spring-term", startMonth: 1, startDay: 15, endMonth: 5, endDay: 31 },
|
|
1257
|
+
],
|
|
1258
|
+
priority: "MANDATORY",
|
|
1259
|
+
});
|
|
1260
|
+
```
|
|
1261
|
+
|
|
1262
|
+
**Parameters:**
|
|
1263
|
+
- `config: { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1264
|
+
|
|
1265
|
+
**Returns:** `CompilationRule`
|
|
1266
|
+
|
|
1267
|
+
|
|
1268
|
+
### createMaxShiftsDayRule
|
|
1269
|
+
Limits how many shifts a person can work in a single day.
|
|
1270
|
+
|
|
1271
|
+
This rule controls the maximum number of distinct shift assignments per day,
|
|
1272
|
+
regardless of shift duration. For limiting total hours worked, use `max-hours-day`.
|
|
1273
|
+
|
|
1274
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1275
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1276
|
+
|
|
1277
|
+
**Example:**
|
|
1278
|
+
Limit to one shift per day (common for most schedules)
|
|
1279
|
+
```ts
|
|
1280
|
+
createMaxShiftsDayRule({
|
|
1281
|
+
shifts: 1,
|
|
1282
|
+
priority: "MANDATORY",
|
|
1283
|
+
});
|
|
1284
|
+
```
|
|
1285
|
+
|
|
1286
|
+
**Example:**
|
|
1287
|
+
Allow up to two shifts per day for part-time workers
|
|
1288
|
+
```ts
|
|
1289
|
+
createMaxShiftsDayRule({
|
|
1290
|
+
roleIds: ["part-time"],
|
|
1291
|
+
shifts: 2,
|
|
1292
|
+
priority: "HIGH",
|
|
1293
|
+
});
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
**Example:**
|
|
1297
|
+
Students can work 2 shifts on weekends only
|
|
1298
|
+
```ts
|
|
1299
|
+
createMaxShiftsDayRule({
|
|
1300
|
+
roleIds: ["student"],
|
|
1301
|
+
shifts: 2,
|
|
1302
|
+
dayOfWeek: ["saturday", "sunday"],
|
|
1303
|
+
priority: "MANDATORY",
|
|
1304
|
+
});
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
**Parameters:**
|
|
1308
|
+
- `config: { shifts: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1309
|
+
|
|
1310
|
+
**Returns:** `CompilationRule`
|
|
1311
|
+
|
|
1312
|
+
|
|
1313
|
+
### createMinConsecutiveDaysRule
|
|
1314
|
+
Requires that once a person starts working, they continue for a minimum
|
|
1315
|
+
number of consecutive days.
|
|
1316
|
+
|
|
1317
|
+
**Example:**
|
|
1318
|
+
```ts
|
|
1319
|
+
const rule = createMinConsecutiveDaysRule({
|
|
1320
|
+
days: 3,
|
|
1321
|
+
priority: "MANDATORY",
|
|
1322
|
+
});
|
|
1323
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
**Parameters:**
|
|
1327
|
+
- `config: { days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1328
|
+
|
|
1329
|
+
**Returns:** `CompilationRule`
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
### createMinHoursDayRule
|
|
1333
|
+
Ensures a person works at least a minimum number of hours per day.
|
|
1334
|
+
|
|
1335
|
+
**Example:**
|
|
1336
|
+
```ts
|
|
1337
|
+
const rule = createMinHoursDayRule({
|
|
1338
|
+
hours: 6,
|
|
1339
|
+
priority: "MANDATORY",
|
|
1340
|
+
});
|
|
1341
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
**Parameters:**
|
|
1345
|
+
- `config: { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1346
|
+
|
|
1347
|
+
**Returns:** `CompilationRule`
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
### createMinHoursWeekRule
|
|
1351
|
+
Enforces a minimum total number of hours per scheduling week.
|
|
1352
|
+
|
|
1353
|
+
**Example:**
|
|
1354
|
+
```ts
|
|
1355
|
+
const rule = createMinHoursWeekRule({
|
|
1356
|
+
hours: 30,
|
|
1357
|
+
priority: "HIGH",
|
|
1358
|
+
});
|
|
1359
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
**Parameters:**
|
|
1363
|
+
- `config: { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1364
|
+
|
|
1365
|
+
**Returns:** `CompilationRule`
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
### createMinRestBetweenShiftsRule
|
|
1369
|
+
Enforces a minimum rest period between any two shifts a person works.
|
|
1370
|
+
|
|
1371
|
+
**Example:**
|
|
1372
|
+
```ts
|
|
1373
|
+
const rule = createMinRestBetweenShiftsRule({
|
|
1374
|
+
hours: 10,
|
|
1375
|
+
priority: "MANDATORY",
|
|
1376
|
+
});
|
|
1377
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1378
|
+
```
|
|
1379
|
+
|
|
1380
|
+
**Parameters:**
|
|
1381
|
+
- `config: { hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1382
|
+
|
|
1383
|
+
**Returns:** `CompilationRule`
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
### createTimeOffRule
|
|
1387
|
+
Blocks or penalizes assignments during specified time periods.
|
|
1388
|
+
|
|
1389
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1390
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1391
|
+
Optionally supports partial-day time-off with startTime/endTime.
|
|
1392
|
+
|
|
1393
|
+
**Example:**
|
|
1394
|
+
Full day vacation
|
|
1395
|
+
```ts
|
|
1396
|
+
createTimeOffRule({
|
|
1397
|
+
employeeIds: ["alice"],
|
|
1398
|
+
dateRange: { start: "2024-02-01", end: "2024-02-05" },
|
|
1399
|
+
priority: "MANDATORY",
|
|
1400
|
+
});
|
|
1401
|
+
```
|
|
1402
|
+
|
|
1403
|
+
**Example:**
|
|
1404
|
+
Every Wednesday afternoon off for students
|
|
1405
|
+
```ts
|
|
1406
|
+
createTimeOffRule({
|
|
1407
|
+
roleIds: ["student"],
|
|
1408
|
+
dayOfWeek: ["wednesday"],
|
|
1409
|
+
startTime: { hours: 14, minutes: 0 },
|
|
1410
|
+
endTime: { hours: 23, minutes: 59 },
|
|
1411
|
+
priority: "MANDATORY",
|
|
1412
|
+
});
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
**Example:**
|
|
1416
|
+
Specific date, partial day
|
|
1417
|
+
```ts
|
|
1418
|
+
createTimeOffRule({
|
|
1419
|
+
employeeIds: ["bob"],
|
|
1420
|
+
specificDates: ["2024-03-15"],
|
|
1421
|
+
startTime: { hours: 16, minutes: 0 },
|
|
1422
|
+
endTime: { hours: 23, minutes: 59 },
|
|
1423
|
+
priority: "MANDATORY",
|
|
1424
|
+
});
|
|
1425
|
+
```
|
|
1426
|
+
|
|
1427
|
+
**Parameters:**
|
|
1428
|
+
- `config: { priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1429
|
+
|
|
1430
|
+
**Returns:** `CompilationRule`
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
### createCpsatRuleFactory
|
|
1434
|
+
Creates a rule factory map, preventing overriding built-in rules.
|
|
1435
|
+
|
|
1436
|
+
**Parameters:**
|
|
1437
|
+
- `factories: F`
|
|
1438
|
+
|
|
1439
|
+
**Returns:** `F`
|
|
1440
|
+
|
|
1441
|
+
|
|
1442
|
+
### defineSemanticTimes
|
|
1443
|
+
Define semantic times with type-safe names.
|
|
1444
|
+
|
|
1445
|
+
Returns a context object that provides:
|
|
1446
|
+
- Type-safe coverage() function that only accepts defined semantic time names
|
|
1447
|
+
- resolve() function to expand semantic times to concrete requirements
|
|
1448
|
+
|
|
1449
|
+
**Example:**
|
|
1450
|
+
Basic usage
|
|
1451
|
+
```typescript
|
|
1452
|
+
const times = defineSemanticTimes({
|
|
1453
|
+
opening: { startTime: { hours: 6 }, endTime: { hours: 8 } },
|
|
1454
|
+
lunch: { startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 } },
|
|
1455
|
+
closing: { startTime: { hours: 21 }, endTime: { hours: 23 } },
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
const coverage = times.coverage([
|
|
1459
|
+
{ semanticTime: "lunch", roleId: "server", targetCount: 3 },
|
|
1460
|
+
{ semanticTime: "opening", roleId: "keyholder", targetCount: 1, priority: "MANDATORY" },
|
|
1461
|
+
// Type error: "dinner" is not a defined semantic time
|
|
1462
|
+
// { semanticTime: "dinner", roleId: "server", targetCount: 2 },
|
|
1463
|
+
]);
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
**Example:**
|
|
1467
|
+
Variants for different days
|
|
1468
|
+
```typescript
|
|
1469
|
+
const times = defineSemanticTimes({
|
|
1470
|
+
lunch: [
|
|
1471
|
+
{ startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 }, days: ["monday", "tuesday", "wednesday", "thursday", "friday"] },
|
|
1472
|
+
{ startTime: { hours: 12 }, endTime: { hours: 15 }, days: ["saturday", "sunday"] },
|
|
1473
|
+
],
|
|
1474
|
+
});
|
|
1475
|
+
```
|
|
1476
|
+
|
|
1477
|
+
**Example:**
|
|
1478
|
+
Mixed semantic and concrete coverage
|
|
1479
|
+
```typescript
|
|
1480
|
+
const coverage = times.coverage([
|
|
1481
|
+
{ semanticTime: "lunch", roleId: "server", targetCount: 3 },
|
|
1482
|
+
// One-off party - concrete time
|
|
1483
|
+
{ day: "2026-01-14", startTime: { hours: 15 }, endTime: { hours: 20 }, roleId: "server", targetCount: 5 },
|
|
1484
|
+
]);
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
**Parameters:**
|
|
1488
|
+
- `defs: T`
|
|
1489
|
+
|
|
1490
|
+
**Returns:** `SemanticTimeContext<keyof T & string>`
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
### isConcreteCoverage
|
|
1494
|
+
Type guard to check if a requirement is concrete (has explicit day/times).
|
|
1495
|
+
|
|
1496
|
+
**Parameters:**
|
|
1497
|
+
- `req: ConcreteCoverageRequirement | SemanticCoverageRequirement<S>`
|
|
1498
|
+
|
|
1499
|
+
**Returns:** `boolean`
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
### isSemanticCoverage
|
|
1503
|
+
Type guard to check if a requirement is semantic (references a named time).
|
|
1504
|
+
|
|
1505
|
+
**Parameters:**
|
|
1506
|
+
- `req: ConcreteCoverageRequirement | SemanticCoverageRequirement<S>`
|
|
1507
|
+
|
|
1508
|
+
**Returns:** `boolean`
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
### groupKey
|
|
1512
|
+
Creates a GroupKey from a description string.
|
|
1513
|
+
Use this to create keys that group related validation items together.
|
|
1514
|
+
|
|
1515
|
+
**Example:**
|
|
1516
|
+
```typescript
|
|
1517
|
+
const key = groupKey("2x waiter during lunch");
|
|
1518
|
+
coverage.groupKey = key;
|
|
1519
|
+
```
|
|
1520
|
+
|
|
1521
|
+
**Parameters:**
|
|
1522
|
+
- `description: string`
|
|
1523
|
+
|
|
1524
|
+
**Returns:** `string & { readonly [GroupKeyBrand]: never; }`
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
### summarizeValidation
|
|
1528
|
+
Aggregates validation items by their groupKey into summaries.
|
|
1529
|
+
This is a pure function that doesn't modify the input.
|
|
1530
|
+
|
|
1531
|
+
Items without a groupKey are grouped by their ID (ungrouped).
|
|
1532
|
+
|
|
1533
|
+
**Example:**
|
|
1534
|
+
```typescript
|
|
1535
|
+
const validation = reporter.getValidation();
|
|
1536
|
+
const summaries = summarizeValidation(validation);
|
|
1537
|
+
// summaries[0] = {
|
|
1538
|
+
// groupKey: "2x waiter during lunch",
|
|
1539
|
+
// status: "passed",
|
|
1540
|
+
// passedCount: 180,
|
|
1541
|
+
// days: ["2026-02-02", "2026-02-03", ...]
|
|
1542
|
+
// }
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
**Parameters:**
|
|
1546
|
+
- `validation: ScheduleValidation`
|
|
1547
|
+
|
|
1548
|
+
**Returns:** `readonly ValidationSummary[]`
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
### validateCoverageRoles
|
|
1552
|
+
Validates that all roleIds used in coverage requirements match the team.
|
|
1553
|
+
|
|
1554
|
+
This catches a common LLM error where the model generates coverage requirements
|
|
1555
|
+
using role names that don't match any team member's roleIds. Without this validation,
|
|
1556
|
+
such mismatches would result in valid but semantically wrong schedules (e.g.,
|
|
1557
|
+
coverage requirements that no one can satisfy).
|
|
1558
|
+
|
|
1559
|
+
**Example:**
|
|
1560
|
+
```typescript
|
|
1561
|
+
const employees = [
|
|
1562
|
+
{ id: "alice", roleIds: ["cashier"] },
|
|
1563
|
+
{ id: "bob", roleIds: ["stocker"] },
|
|
1564
|
+
];
|
|
1565
|
+
|
|
1566
|
+
const coverage = [
|
|
1567
|
+
{ roleId: "cashier", targetCount: 1, ... }, // OK
|
|
1568
|
+
{ roleId: "worker", targetCount: 1, ... }, // Unknown role!
|
|
1569
|
+
];
|
|
1570
|
+
|
|
1571
|
+
const result = validateCoverageRoles(coverage, employees);
|
|
1572
|
+
// result.valid = false
|
|
1573
|
+
// result.unknownRoles = ["worker"]
|
|
1574
|
+
// result.knownRoles = ["cashier"]
|
|
1575
|
+
```
|
|
1576
|
+
|
|
1577
|
+
**Parameters:**
|
|
1578
|
+
- `coverage: CoverageRequirement[]`
|
|
1579
|
+
- `employees: SchedulingEmployee[]`
|
|
1580
|
+
|
|
1581
|
+
**Returns:** `CoverageValidationResult`
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
### validateCoverageSkills
|
|
1585
|
+
Validates that all skillIds used in coverage requirements match the team.
|
|
1586
|
+
|
|
1587
|
+
Similar to role validation, this catches LLM hallucinations where skill names
|
|
1588
|
+
in coverage don't match any team member's skillIds.
|
|
1589
|
+
|
|
1590
|
+
**Example:**
|
|
1591
|
+
```typescript
|
|
1592
|
+
const employees = [
|
|
1593
|
+
{ id: "alice", roleIds: ["server"], skillIds: ["keyholder"] },
|
|
1594
|
+
{ id: "bob", roleIds: ["server"] },
|
|
1595
|
+
];
|
|
1596
|
+
|
|
1597
|
+
const coverage = [
|
|
1598
|
+
{ skillIds: ["keyholder"], targetCount: 1, ... }, // OK
|
|
1599
|
+
{ skillIds: ["manager"], targetCount: 1, ... }, // Unknown skill!
|
|
1600
|
+
];
|
|
1601
|
+
|
|
1602
|
+
const result = validateCoverageSkills(coverage, employees);
|
|
1603
|
+
// result.valid = false
|
|
1604
|
+
// result.unknownSkills = ["manager"]
|
|
1605
|
+
```
|
|
1606
|
+
|
|
1607
|
+
**Parameters:**
|
|
1608
|
+
- `coverage: CoverageRequirement[]`
|
|
1609
|
+
- `employees: SchedulingEmployee[]`
|
|
1610
|
+
|
|
1611
|
+
**Returns:** `SkillValidationResult`
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
### validateCoverageConfig
|
|
1615
|
+
Validates coverage requirements against team roles and skills.
|
|
1616
|
+
|
|
1617
|
+
This is the primary validation function to call before building a scheduling model.
|
|
1618
|
+
It checks both roles and skills, returning a combined result with error messages.
|
|
1619
|
+
|
|
1620
|
+
**Example:**
|
|
1621
|
+
```typescript
|
|
1622
|
+
const result = validateCoverageConfig(coverage, employees);
|
|
1623
|
+
if (!result.valid) {
|
|
1624
|
+
throw new Error(result.errors.join("; "));
|
|
1625
|
+
}
|
|
1626
|
+
```
|
|
1627
|
+
|
|
1628
|
+
**Parameters:**
|
|
1629
|
+
- `coverage: CoverageRequirement[]`
|
|
1630
|
+
- `employees: SchedulingEmployee[]`
|
|
1631
|
+
|
|
1632
|
+
**Returns:** `CoverageConfigValidationResult`
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
### addMinutesToDate
|
|
1636
|
+
Adds a number of minutes to a base date and returns a DateTime.
|
|
1637
|
+
Treats the base date as a reference point (typically midnight of horizon start),
|
|
1638
|
+
and the minutes parameter as absolute minutes from that point.
|
|
1639
|
+
|
|
1640
|
+
**Example:**
|
|
1641
|
+
```typescript
|
|
1642
|
+
// Add 90 minutes from midnight
|
|
1643
|
+
addMinutesToDate(new Date('2025-01-01'), 90);
|
|
1644
|
+
// Returns: { year: 2025, month: 1, day: 1, hours: 1, minutes: 30 }
|
|
1645
|
+
|
|
1646
|
+
// Add 1500 minutes (spans to next day)
|
|
1647
|
+
addMinutesToDate(new Date('2025-01-01'), 1500);
|
|
1648
|
+
// Returns: { year: 2025, month: 1, day: 2, hours: 1, minutes: 0 }
|
|
1649
|
+
```
|
|
1650
|
+
|
|
1651
|
+
**Parameters:**
|
|
1652
|
+
- `baseDate: Date`
|
|
1653
|
+
- `minutes: number`
|
|
1654
|
+
|
|
1655
|
+
**Returns:** `DateTimeWithUtcOffset | DateTimeWithTimeZone`
|
|
1656
|
+
|
|
1657
|
+
|
|
1658
|
+
### splitPoints
|
|
1659
|
+
Returns the points where a range should be split, filtered to within [start, end).
|
|
1660
|
+
Always includes range start. Sorted ascending.
|
|
1661
|
+
|
|
1662
|
+
**Parameters:**
|
|
1663
|
+
- `[start, end]: [number, number]`
|
|
1664
|
+
- `splitAt: number[]`
|
|
1665
|
+
|
|
1666
|
+
**Returns:** `number[]`
|
|
1667
|
+
|
|
1668
|
+
|
|
1669
|
+
### parseDayString
|
|
1670
|
+
Parse a day string (YYYY-MM-DD) to a UTC Date.
|
|
1671
|
+
Used internally for day-of-week calculations and date comparisons.
|
|
1672
|
+
|
|
1673
|
+
**Parameters:**
|
|
1674
|
+
- `day: string`
|
|
1675
|
+
|
|
1676
|
+
**Returns:** `Date`
|
|
1677
|
+
|
|
1678
|
+
|
|
1679
|
+
### timeOfDayToMinutes
|
|
1680
|
+
**Parameters:**
|
|
1681
|
+
- `time: TimeOfDay`
|
|
1682
|
+
|
|
1683
|
+
**Returns:** `number`
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
### normalizeEndMinutes
|
|
1687
|
+
**Parameters:**
|
|
1688
|
+
- `startMinutes: number`
|
|
1689
|
+
- `endMinutes: number`
|
|
1690
|
+
|
|
1691
|
+
**Returns:** `number`
|
|
1692
|
+
|
|
1693
|
+
|
|
1694
|
+
### priorityToPenalty
|
|
1695
|
+
**Parameters:**
|
|
1696
|
+
- `priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"`
|
|
1697
|
+
|
|
1698
|
+
**Returns:** `number`
|
|
1699
|
+
|
|
1700
|
+
|
|
1701
|
+
### splitIntoWeeks
|
|
1702
|
+
**Parameters:**
|
|
1703
|
+
- `days: string[]`
|
|
1704
|
+
- `weekStartsOn: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"`
|
|
1705
|
+
|
|
1706
|
+
**Returns:** `string[][]`
|
|
1707
|
+
|
|
1708
|
+
|
|
1709
|
+
## Classes
|
|
1710
|
+
|
|
1711
|
+
### ORSchedulingError
|
|
1712
|
+
Error thrown when Google OR Tools scheduling API requests fail.
|
|
1713
|
+
|
|
1714
|
+
Contains the HTTP status code and raw response data from the API for debugging.
|
|
1715
|
+
Common causes include infeasible constraints, invalid requests, or API unavailability.
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
### HttpSolverClient
|
|
1719
|
+
Generic HTTP client for the solver service.
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
### ModelBuilder
|
|
1723
|
+
Compilation context that creates variables, constraints, and objectives
|
|
1724
|
+
and emits a `SolverRequest` for the Python CP-SAT solver service.
|
|
1725
|
+
|
|
1726
|
+
|
|
1727
|
+
### ValidationReporterImpl
|
|
1728
|
+
|
|
1729
|
+
## Constants
|
|
1730
|
+
|
|
1731
|
+
### DayOfWeekSchema
|
|
1732
|
+
Zod schema for {@link DayOfWeek}.
|
|
1733
|
+
Useful for rule configs that need to accept a day-of-week string.
|
|
1734
|
+
|
|
1735
|
+
**Type:** `z.ZodUnion<readonly [z.ZodLiteral<"monday">, z.ZodLiteral<"tuesday">, z.ZodLiteral<"wednesday">, z.ZodLiteral<"thursday">, z.ZodLiteral<"friday">, z.ZodLiteral<"saturday">, z.ZodLiteral<"sunday">]>`
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
### SOLVER_STATUS
|
|
1739
|
+
**Type:** `{ readonly OPTIMAL: "OPTIMAL"; readonly FEASIBLE: "FEASIBLE"; readonly INFEASIBLE: "INFEASIBLE"; readonly TIMEOUT: "TIMEOUT"; readonly ERROR: "ERROR"; }`
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
### SolverRequestSchema
|
|
1743
|
+
**Type:** `[Complex type: ZodObject]`
|
|
1744
|
+
|
|
1745
|
+
|
|
1746
|
+
### SolverResponseSchema
|
|
1747
|
+
**Type:** `z.ZodObject<{ status: z.ZodEnum<{ OPTIMAL: "OPTIMAL"; FEASIBLE: "FEASIBLE"; INFEASIBLE: "INFEASIBLE"; TIMEOUT: "TIMEOUT"; ERROR: "ERROR"; }>; values: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>; statistics: z.ZodOptional<z.ZodObject<{ solveTimeMs: z.ZodOptional<z.ZodNumber>; conflicts: z.ZodOptional<z.ZodNumber>; branches: z.ZodOptional<z.ZodNumber>; }, z.core.$strip>>; error: z.ZodOptional<z.ZodString>; solutionInfo: z.ZodOptional<z.ZodString>; softViolations: z.ZodOptional<z.ZodArray<z.ZodObject<{ constraintId: z.ZodString; violationAmount: z.ZodNumber; targetValue: z.ZodNumber; actualValue: z.ZodNumber; }, z.core.$strip>>>; }, z.core.$strip>`
|
|
1748
|
+
|
|
1749
|
+
|
|
1750
|
+
### builtInCpsatRuleFactories
|
|
1751
|
+
**Type:** `[Complex type: __type]`
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
### OBJECTIVE_WEIGHTS
|
|
1755
|
+
Standard objective weights for the scheduling solver.
|
|
1756
|
+
|
|
1757
|
+
These weights define the relative importance of different objectives.
|
|
1758
|
+
Higher weights mean stronger preference. Rules can use these as reference
|
|
1759
|
+
points when adding their own penalties.
|
|
1760
|
+
|
|
1761
|
+
Weight hierarchy (highest to lowest priority):
|
|
1762
|
+
- SHIFT_ACTIVE (1000): Minimize number of active shift patterns
|
|
1763
|
+
- ASSIGNMENT_PREFERENCE (10): Per-assignment preference (e.g., prefer permanent staff)
|
|
1764
|
+
- FAIRNESS (5): Fair distribution of shifts across team members
|
|
1765
|
+
- ASSIGNMENT_BASE (1): Tiebreaker - minimize total assignments
|
|
1766
|
+
|
|
1767
|
+
**Example:**
|
|
1768
|
+
Using weights in a custom rule
|
|
1769
|
+
```ts
|
|
1770
|
+
import { OBJECTIVE_WEIGHTS } from "feasible";
|
|
1771
|
+
|
|
1772
|
+
// Prefer senior staff with same weight as employee-assignment-priority
|
|
1773
|
+
b.addPenalty(assignment, -OBJECTIVE_WEIGHTS.ASSIGNMENT_PREFERENCE);
|
|
1774
|
+
|
|
1775
|
+
// Strong preference (2x normal)
|
|
1776
|
+
b.addPenalty(assignment, -2 * OBJECTIVE_WEIGHTS.ASSIGNMENT_PREFERENCE);
|
|
1777
|
+
```
|
|
1778
|
+
|
|
1779
|
+
**Type:** `{ readonly SHIFT_ACTIVE: 1000; readonly ASSIGNMENT_PREFERENCE: 10; readonly FAIRNESS: 5; readonly ASSIGNMENT_BASE: 1; }`
|
|
1780
|
+
|
|
1781
|
+
|
|
1782
|
+
### DAY_OF_WEEK_MAP
|
|
1783
|
+
**Type:** `{ sunday: number; monday: number; tuesday: number; wednesday: number; thursday: number; friday: number; saturday: number; }`
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
### SolverTermSchema
|
|
1787
|
+
**Type:** `z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>`
|
|
1788
|
+
|
|
1789
|
+
|
|
1790
|
+
### BoolVariableSchema
|
|
1791
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"bool">; name: z.ZodString; }, z.core.$strip>`
|
|
1792
|
+
|
|
1793
|
+
|
|
1794
|
+
### IntVariableSchema
|
|
1795
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"int">; name: z.ZodString; min: z.ZodNumber; max: z.ZodNumber; }, z.core.$strip>`
|
|
1796
|
+
|
|
1797
|
+
|
|
1798
|
+
### IntervalVariableSchema
|
|
1799
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"interval">; name: z.ZodString; start: z.ZodNumber; end: z.ZodNumber; size: z.ZodNumber; presenceVar: z.ZodOptional<z.ZodString>; }, z.core.$strip>`
|
|
1800
|
+
|
|
1801
|
+
|
|
1802
|
+
### SolverVariableSchema
|
|
1803
|
+
**Type:** `z.ZodUnion<readonly [z.ZodObject<{ type: z.ZodLiteral<"bool">; name: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"int">; name: z.ZodString; min: z.ZodNumber; max: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"interval">; name: z.ZodString; start: z.ZodNumber; end: z.ZodNumber; size: z.ZodNumber; presenceVar: z.ZodOptional<z.ZodString>; }, z.core.$strip>]>`
|
|
1804
|
+
|
|
1805
|
+
|
|
1806
|
+
### LinearConstraintSchema
|
|
1807
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"linear">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<"<=">, z.ZodLiteral<">=">, z.ZodLiteral<"==">]>; rhs: z.ZodNumber; }, z.core.$strip>`
|
|
1808
|
+
|
|
1809
|
+
|
|
1810
|
+
### SoftLinearConstraintSchema
|
|
1811
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"soft_linear">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<"<=">, z.ZodLiteral<">=">]>; rhs: z.ZodNumber; penalty: z.ZodNumber; id: z.ZodOptional<z.ZodString>; }, z.core.$strip>`
|
|
1812
|
+
|
|
1813
|
+
|
|
1814
|
+
### ExactlyOneConstraintSchema
|
|
1815
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"exactly_one">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`
|
|
1816
|
+
|
|
1817
|
+
|
|
1818
|
+
### AtMostOneConstraintSchema
|
|
1819
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"at_most_one">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
### ImplicationConstraintSchema
|
|
1823
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"implication">; if: z.ZodString; then: z.ZodString; }, z.core.$strip>`
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
### BoolOrConstraintSchema
|
|
1827
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"bool_or">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`
|
|
1828
|
+
|
|
1829
|
+
|
|
1830
|
+
### BoolAndConstraintSchema
|
|
1831
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"bool_and">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`
|
|
1832
|
+
|
|
1833
|
+
|
|
1834
|
+
### NoOverlapConstraintSchema
|
|
1835
|
+
**Type:** `z.ZodObject<{ type: z.ZodLiteral<"no_overlap">; intervals: z.ZodArray<z.ZodString>; }, z.core.$strip>`
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
### SolverConstraintSchema
|
|
1839
|
+
**Type:** `z.ZodUnion<readonly [z.ZodObject<{ type: z.ZodLiteral<"linear">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<"<=">, z.ZodLiteral<">=">, z.ZodLiteral<"==">]>; rhs: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"soft_linear">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<"<=">, z.ZodLiteral<">=">]>; rhs: z.ZodNumber; penalty: z.ZodNumber; id: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"exactly_one">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"at_most_one">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"implication">; if: z.ZodString; then: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"bool_or">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"bool_and">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"no_overlap">; intervals: z.ZodArray<z.ZodString>; }, z.core.$strip>]>`
|
|
1840
|
+
|
|
1841
|
+
|
|
1842
|
+
### SolverObjectiveSchema
|
|
1843
|
+
**Type:** `z.ZodObject<{ sense: z.ZodUnion<readonly [z.ZodLiteral<"minimize">, z.ZodLiteral<"maximize">]>; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; }, z.core.$strip>`
|
|
1844
|
+
|
|
1845
|
+
|
|
1846
|
+
### SolverOptionsSchema
|
|
1847
|
+
**Type:** `z.ZodObject<{ timeLimitSeconds: z.ZodOptional<z.ZodNumber>; solutionLimit: z.ZodOptional<z.ZodNumber>; }, z.core.$strip>`
|
|
1848
|
+
|
|
1849
|
+
|
|
1850
|
+
### SolverStatusSchema
|
|
1851
|
+
**Type:** `z.ZodEnum<{ OPTIMAL: "OPTIMAL"; FEASIBLE: "FEASIBLE"; INFEASIBLE: "INFEASIBLE"; TIMEOUT: "TIMEOUT"; ERROR: "ERROR"; }>`
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
### SolverStatisticsSchema
|
|
1855
|
+
**Type:** `z.ZodObject<{ solveTimeMs: z.ZodOptional<z.ZodNumber>; conflicts: z.ZodOptional<z.ZodNumber>; branches: z.ZodOptional<z.ZodNumber>; }, z.core.$strip>`
|
|
1856
|
+
|
|
1857
|
+
|
|
1858
|
+
### SoftConstraintViolationSchema
|
|
1859
|
+
**Type:** `z.ZodObject<{ constraintId: z.ZodString; violationAmount: z.ZodNumber; targetValue: z.ZodNumber; actualValue: z.ZodNumber; }, z.core.$strip>`
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
### MINUTES_PER_DAY
|
|
1863
|
+
**Type:** `number`
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
|
|
1867
|
+
## Built-In Rules Reference
|
|
1868
|
+
|
|
1869
|
+
Rules are constraints and preferences you can apply to schedules using `ctx.addRule(ruleName, config)`.
|
|
1870
|
+
|
|
1871
|
+
### assign-together
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
|
|
1875
|
+
Encourages or enforces that team members in the group work the same shift patterns on a day.
|
|
1876
|
+
For each pair of team members in the group, ensures they are assigned to the same shifts.
|
|
1877
|
+
|
|
1878
|
+
**Example:**
|
|
1879
|
+
```ts
|
|
1880
|
+
const rule = createAssignTogetherRule({
|
|
1881
|
+
groupEmployeeIds: ["alice", "bob", "charlie"],
|
|
1882
|
+
priority: "HIGH",
|
|
1883
|
+
});
|
|
1884
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1885
|
+
```
|
|
1886
|
+
|
|
1887
|
+
**Config:** `{ groupEmployeeIds: [string, string, ...string[]]; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; }`
|
|
1888
|
+
|
|
1889
|
+
### employee-assignment-priority
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
|
|
1893
|
+
Adds objective weight to prefer or avoid assigning team members.
|
|
1894
|
+
|
|
1895
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1896
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1897
|
+
|
|
1898
|
+
**Example:**
|
|
1899
|
+
Prefer specific team members
|
|
1900
|
+
```ts
|
|
1901
|
+
createEmployeeAssignmentPriorityRule({
|
|
1902
|
+
employeeIds: ["alice", "bob"],
|
|
1903
|
+
preference: "high",
|
|
1904
|
+
});
|
|
1905
|
+
```
|
|
1906
|
+
|
|
1907
|
+
**Example:**
|
|
1908
|
+
Avoid assigning students on weekdays
|
|
1909
|
+
```ts
|
|
1910
|
+
createEmployeeAssignmentPriorityRule({
|
|
1911
|
+
roleIds: ["student"],
|
|
1912
|
+
dayOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"],
|
|
1913
|
+
preference: "low",
|
|
1914
|
+
});
|
|
1915
|
+
```
|
|
1916
|
+
|
|
1917
|
+
**Example:**
|
|
1918
|
+
Prefer experienced staff on weekends
|
|
1919
|
+
```ts
|
|
1920
|
+
createEmployeeAssignmentPriorityRule({
|
|
1921
|
+
skillIds: ["senior"],
|
|
1922
|
+
dayOfWeek: ["saturday", "sunday"],
|
|
1923
|
+
preference: "high",
|
|
1924
|
+
});
|
|
1925
|
+
```
|
|
1926
|
+
|
|
1927
|
+
**Config:** `{ preference: "high" | "low"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1928
|
+
|
|
1929
|
+
### location-preference
|
|
1930
|
+
|
|
1931
|
+
|
|
1932
|
+
|
|
1933
|
+
Prefers assigning a person to shift patterns matching a specific location.
|
|
1934
|
+
|
|
1935
|
+
**Example:**
|
|
1936
|
+
```ts
|
|
1937
|
+
const rule = createLocationPreferenceRule({
|
|
1938
|
+
locationId: "terrace",
|
|
1939
|
+
priority: "HIGH",
|
|
1940
|
+
employeeIds: ["alice"],
|
|
1941
|
+
});
|
|
1942
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1943
|
+
```
|
|
1944
|
+
|
|
1945
|
+
**Config:** `{ locationId: string; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1946
|
+
|
|
1947
|
+
### max-consecutive-days
|
|
1948
|
+
|
|
1949
|
+
|
|
1950
|
+
|
|
1951
|
+
Limits how many consecutive days a person can be assigned.
|
|
1952
|
+
|
|
1953
|
+
**Example:**
|
|
1954
|
+
```ts
|
|
1955
|
+
const rule = createMaxConsecutiveDaysRule({
|
|
1956
|
+
days: 5,
|
|
1957
|
+
priority: "MANDATORY",
|
|
1958
|
+
});
|
|
1959
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
1960
|
+
```
|
|
1961
|
+
|
|
1962
|
+
**Config:** `{ days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
1963
|
+
|
|
1964
|
+
### max-hours-day
|
|
1965
|
+
|
|
1966
|
+
|
|
1967
|
+
|
|
1968
|
+
Limits how many hours a person can work in a single day.
|
|
1969
|
+
|
|
1970
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
1971
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
1972
|
+
|
|
1973
|
+
**Example:**
|
|
1974
|
+
Limit everyone to 8 hours per day
|
|
1975
|
+
```ts
|
|
1976
|
+
createMaxHoursDayRule({
|
|
1977
|
+
hours: 8,
|
|
1978
|
+
priority: "MANDATORY",
|
|
1979
|
+
});
|
|
1980
|
+
```
|
|
1981
|
+
|
|
1982
|
+
**Example:**
|
|
1983
|
+
Students limited to 4 hours on weekdays during term
|
|
1984
|
+
```ts
|
|
1985
|
+
createMaxHoursDayRule({
|
|
1986
|
+
roleIds: ["student"],
|
|
1987
|
+
hours: 4,
|
|
1988
|
+
dayOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"],
|
|
1989
|
+
priority: "MANDATORY",
|
|
1990
|
+
});
|
|
1991
|
+
```
|
|
1992
|
+
|
|
1993
|
+
**Config:** `{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
1994
|
+
|
|
1995
|
+
### max-hours-week
|
|
1996
|
+
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
Caps total hours a person can work within each scheduling week.
|
|
2000
|
+
|
|
2001
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
2002
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
2003
|
+
Time scoping filters which days within each week count toward the limit.
|
|
2004
|
+
|
|
2005
|
+
**Example:**
|
|
2006
|
+
Limit everyone to 40 hours per week
|
|
2007
|
+
```ts
|
|
2008
|
+
createMaxHoursWeekRule({
|
|
2009
|
+
hours: 40,
|
|
2010
|
+
priority: "HIGH",
|
|
2011
|
+
});
|
|
2012
|
+
```
|
|
2013
|
+
|
|
2014
|
+
**Example:**
|
|
2015
|
+
Students limited to 20 hours during term time
|
|
2016
|
+
```ts
|
|
2017
|
+
createMaxHoursWeekRule({
|
|
2018
|
+
roleIds: ["student"],
|
|
2019
|
+
hours: 20,
|
|
2020
|
+
recurringPeriods: [
|
|
2021
|
+
{ name: "fall-term", startMonth: 9, startDay: 1, endMonth: 12, endDay: 15 },
|
|
2022
|
+
{ name: "spring-term", startMonth: 1, startDay: 15, endMonth: 5, endDay: 31 },
|
|
2023
|
+
],
|
|
2024
|
+
priority: "MANDATORY",
|
|
2025
|
+
});
|
|
2026
|
+
```
|
|
2027
|
+
|
|
2028
|
+
**Config:** `{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
2029
|
+
|
|
2030
|
+
### max-shifts-day
|
|
2031
|
+
|
|
2032
|
+
|
|
2033
|
+
|
|
2034
|
+
Limits how many shifts a person can work in a single day.
|
|
2035
|
+
|
|
2036
|
+
This rule controls the maximum number of distinct shift assignments per day,
|
|
2037
|
+
regardless of shift duration. For limiting total hours worked, use `max-hours-day`.
|
|
2038
|
+
|
|
2039
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
2040
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
2041
|
+
|
|
2042
|
+
**Example:**
|
|
2043
|
+
Limit to one shift per day (common for most schedules)
|
|
2044
|
+
```ts
|
|
2045
|
+
createMaxShiftsDayRule({
|
|
2046
|
+
shifts: 1,
|
|
2047
|
+
priority: "MANDATORY",
|
|
2048
|
+
});
|
|
2049
|
+
```
|
|
2050
|
+
|
|
2051
|
+
**Example:**
|
|
2052
|
+
Allow up to two shifts per day for part-time workers
|
|
2053
|
+
```ts
|
|
2054
|
+
createMaxShiftsDayRule({
|
|
2055
|
+
roleIds: ["part-time"],
|
|
2056
|
+
shifts: 2,
|
|
2057
|
+
priority: "HIGH",
|
|
2058
|
+
});
|
|
2059
|
+
```
|
|
2060
|
+
|
|
2061
|
+
**Example:**
|
|
2062
|
+
Students can work 2 shifts on weekends only
|
|
2063
|
+
```ts
|
|
2064
|
+
createMaxShiftsDayRule({
|
|
2065
|
+
roleIds: ["student"],
|
|
2066
|
+
shifts: 2,
|
|
2067
|
+
dayOfWeek: ["saturday", "sunday"],
|
|
2068
|
+
priority: "MANDATORY",
|
|
2069
|
+
});
|
|
2070
|
+
```
|
|
2071
|
+
|
|
2072
|
+
**Config:** `{ shifts: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
2073
|
+
|
|
2074
|
+
### min-consecutive-days
|
|
2075
|
+
|
|
2076
|
+
|
|
2077
|
+
|
|
2078
|
+
Requires that once a person starts working, they continue for a minimum
|
|
2079
|
+
number of consecutive days.
|
|
2080
|
+
|
|
2081
|
+
**Example:**
|
|
2082
|
+
```ts
|
|
2083
|
+
const rule = createMinConsecutiveDaysRule({
|
|
2084
|
+
days: 3,
|
|
2085
|
+
priority: "MANDATORY",
|
|
2086
|
+
});
|
|
2087
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
2088
|
+
```
|
|
2089
|
+
|
|
2090
|
+
**Config:** `{ days: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
2091
|
+
|
|
2092
|
+
### min-hours-day
|
|
2093
|
+
|
|
2094
|
+
|
|
2095
|
+
|
|
2096
|
+
Ensures a person works at least a minimum number of hours per day.
|
|
2097
|
+
|
|
2098
|
+
**Example:**
|
|
2099
|
+
```ts
|
|
2100
|
+
const rule = createMinHoursDayRule({
|
|
2101
|
+
hours: 6,
|
|
2102
|
+
priority: "MANDATORY",
|
|
2103
|
+
});
|
|
2104
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
2105
|
+
```
|
|
2106
|
+
|
|
2107
|
+
**Config:** `{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
2108
|
+
|
|
2109
|
+
### min-hours-week
|
|
2110
|
+
|
|
2111
|
+
|
|
2112
|
+
|
|
2113
|
+
Enforces a minimum total number of hours per scheduling week.
|
|
2114
|
+
|
|
2115
|
+
**Example:**
|
|
2116
|
+
```ts
|
|
2117
|
+
const rule = createMinHoursWeekRule({
|
|
2118
|
+
hours: 30,
|
|
2119
|
+
priority: "HIGH",
|
|
2120
|
+
});
|
|
2121
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
2122
|
+
```
|
|
2123
|
+
|
|
2124
|
+
**Config:** `{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; weekStartsOn?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
2125
|
+
|
|
2126
|
+
### min-rest-between-shifts
|
|
2127
|
+
|
|
2128
|
+
|
|
2129
|
+
|
|
2130
|
+
Enforces a minimum rest period between any two shifts a person works.
|
|
2131
|
+
|
|
2132
|
+
**Example:**
|
|
2133
|
+
```ts
|
|
2134
|
+
const rule = createMinRestBetweenShiftsRule({
|
|
2135
|
+
hours: 10,
|
|
2136
|
+
priority: "MANDATORY",
|
|
2137
|
+
});
|
|
2138
|
+
builder = new ModelBuilder({ ...config, rules: [rule] });
|
|
2139
|
+
```
|
|
2140
|
+
|
|
2141
|
+
**Config:** `{ hours: number; priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`
|
|
2142
|
+
|
|
2143
|
+
### time-off
|
|
2144
|
+
|
|
2145
|
+
|
|
2146
|
+
|
|
2147
|
+
Blocks or penalizes assignments during specified time periods.
|
|
2148
|
+
|
|
2149
|
+
Supports entity scoping (people, roles, skills) and time scoping
|
|
2150
|
+
(date ranges, specific dates, days of week, recurring periods).
|
|
2151
|
+
Optionally supports partial-day time-off with startTime/endTime.
|
|
2152
|
+
|
|
2153
|
+
**Example:**
|
|
2154
|
+
Full day vacation
|
|
2155
|
+
```ts
|
|
2156
|
+
createTimeOffRule({
|
|
2157
|
+
employeeIds: ["alice"],
|
|
2158
|
+
dateRange: { start: "2024-02-01", end: "2024-02-05" },
|
|
2159
|
+
priority: "MANDATORY",
|
|
2160
|
+
});
|
|
2161
|
+
```
|
|
2162
|
+
|
|
2163
|
+
**Example:**
|
|
2164
|
+
Every Wednesday afternoon off for students
|
|
2165
|
+
```ts
|
|
2166
|
+
createTimeOffRule({
|
|
2167
|
+
roleIds: ["student"],
|
|
2168
|
+
dayOfWeek: ["wednesday"],
|
|
2169
|
+
startTime: { hours: 14, minutes: 0 },
|
|
2170
|
+
endTime: { hours: 23, minutes: 59 },
|
|
2171
|
+
priority: "MANDATORY",
|
|
2172
|
+
});
|
|
2173
|
+
```
|
|
2174
|
+
|
|
2175
|
+
**Example:**
|
|
2176
|
+
Specific date, partial day
|
|
2177
|
+
```ts
|
|
2178
|
+
createTimeOffRule({
|
|
2179
|
+
employeeIds: ["bob"],
|
|
2180
|
+
specificDates: ["2024-03-15"],
|
|
2181
|
+
startTime: { hours: 16, minutes: 0 },
|
|
2182
|
+
endTime: { hours: 23, minutes: 59 },
|
|
2183
|
+
priority: "MANDATORY",
|
|
2184
|
+
});
|
|
2185
|
+
```
|
|
2186
|
+
|
|
2187
|
+
**Config:** `{ priority: "LOW" | "MEDIUM" | "HIGH" | "MANDATORY"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: ("monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`
|
|
2188
|
+
|