dabke 0.83.0 → 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +22 -27
- package/dist/client.d.ts +39 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/client.schemas.d.ts +118 -18
- package/dist/client.schemas.d.ts.map +1 -1
- package/dist/client.schemas.js +110 -6
- package/dist/client.schemas.js.map +1 -1
- package/dist/client.types.d.ts +63 -7
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/cost.d.ts +5 -2
- package/dist/cpsat/cost.d.ts.map +1 -1
- package/dist/cpsat/cost.js +13 -6
- package/dist/cpsat/cost.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +42 -61
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +451 -56
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +19 -7
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +48 -9
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rule-descriptor.d.ts +250 -0
- package/dist/cpsat/rule-descriptor.d.ts.map +1 -0
- package/dist/cpsat/rule-descriptor.js +51 -0
- package/dist/cpsat/rule-descriptor.js.map +1 -0
- package/dist/cpsat/rules/artifacts.d.ts +9 -0
- package/dist/cpsat/rules/artifacts.d.ts.map +1 -0
- package/dist/cpsat/rules/artifacts.js +31 -0
- package/dist/cpsat/rules/artifacts.js.map +1 -0
- package/dist/cpsat/rules/assign-together.d.ts +18 -20
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
- package/dist/cpsat/rules/assign-together.js +90 -54
- package/dist/cpsat/rules/assign-together.js.map +1 -1
- package/dist/cpsat/rules/assigned-day.d.ts +12 -0
- package/dist/cpsat/rules/assigned-day.d.ts.map +1 -0
- package/dist/cpsat/rules/assigned-day.js +43 -0
- package/dist/cpsat/rules/assigned-day.js.map +1 -0
- package/dist/cpsat/rules/assignment-priority.d.ts +15 -29
- package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -1
- package/dist/cpsat/rules/assignment-priority.js +47 -50
- package/dist/cpsat/rules/assignment-priority.js.map +1 -1
- package/dist/cpsat/rules/assignment-terms.d.ts +6 -0
- package/dist/cpsat/rules/assignment-terms.d.ts.map +1 -0
- package/dist/cpsat/rules/assignment-terms.js +19 -0
- package/dist/cpsat/rules/assignment-terms.js.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts +19 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.js +57 -0
- package/dist/cpsat/rules/concurrent-intervals.js.map +1 -0
- package/dist/cpsat/rules/cost-utils.d.ts +1 -3
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -1
- package/dist/cpsat/rules/cost-utils.js +0 -7
- package/dist/cpsat/rules/cost-utils.js.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +88 -77
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +76 -65
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +30 -24
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +30 -24
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/location-preference.d.ts +14 -21
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
- package/dist/cpsat/rules/location-preference.js +45 -45
- package/dist/cpsat/rules/location-preference.js.map +1 -1
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts +46 -0
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js +121 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts +18 -17
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +67 -66
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts +45 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js +95 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/max-days-week.d.ts +17 -31
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-days-week.js +69 -81
- package/dist/cpsat/rules/max-days-week.js.map +1 -1
- package/dist/cpsat/rules/max-hours-day.d.ts +14 -31
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +56 -72
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts +15 -31
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +60 -75
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts +17 -33
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +57 -73
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts +18 -18
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +123 -94
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts +35 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js +108 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/min-days-week.d.ts +13 -26
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-days-week.js +85 -74
- package/dist/cpsat/rules/min-days-week.js.map +1 -1
- package/dist/cpsat/rules/min-hours-day.d.ts +14 -17
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +72 -54
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts +15 -18
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +75 -57
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +14 -17
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +148 -138
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts +2 -18
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +151 -163
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +5 -42
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -1
- package/dist/cpsat/rules/must-assign.js +43 -75
- package/dist/cpsat/rules/must-assign.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +6 -10
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +118 -112
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +6 -11
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +104 -99
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +9 -21
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +181 -199
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +13 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +143 -143
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +7 -11
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +124 -119
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/pattern-eligibility.d.ts +6 -0
- package/dist/cpsat/rules/pattern-eligibility.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-eligibility.js +16 -0
- package/dist/cpsat/rules/pattern-eligibility.js.map +1 -0
- package/dist/cpsat/rules/pattern-time.d.ts +5 -0
- package/dist/cpsat/rules/pattern-time.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-time.js +20 -0
- package/dist/cpsat/rules/pattern-time.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts +217 -30
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +98 -34
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.d.ts +9 -7
- package/dist/cpsat/rules/resolver.d.ts.map +1 -1
- package/dist/cpsat/rules/resolver.js +19 -18
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/role-preference.d.ts +24 -0
- package/dist/cpsat/rules/role-preference.d.ts.map +1 -0
- package/dist/cpsat/rules/role-preference.js +72 -0
- package/dist/cpsat/rules/role-preference.js.map +1 -0
- package/dist/cpsat/rules/rules.types.d.ts +90 -17
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +24 -11
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +14 -19
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/target-days-week.d.ts +31 -0
- package/dist/cpsat/rules/target-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/target-days-week.js +80 -0
- package/dist/cpsat/rules/target-days-week.js.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts +33 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js +146 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/time-cost-surcharge.d.ts +15 -14
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +82 -96
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts +21 -57
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +114 -127
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/rules/variables.d.ts +4 -0
- package/dist/cpsat/rules/variables.d.ts.map +1 -0
- package/dist/cpsat/rules/variables.js +10 -0
- package/dist/cpsat/rules/variables.js.map +1 -0
- package/dist/cpsat/rules.d.ts +20 -3
- package/dist/cpsat/rules.d.ts.map +1 -1
- package/dist/cpsat/rules.js +19 -2
- package/dist/cpsat/rules.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +7 -6
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +15 -28
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +47 -4
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/types.js +24 -1
- package/dist/cpsat/types.js.map +1 -1
- package/dist/cpsat/utils.d.ts +23 -6
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +76 -15
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +6 -2
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +122 -10
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +21 -10
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/datetime.utils.d.ts +7 -13
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +29 -36
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +48 -60
- package/dist/schedule/cost.d.ts.map +1 -1
- package/dist/schedule/cost.js +15 -51
- package/dist/schedule/cost.js.map +1 -1
- package/dist/schedule/coverage.d.ts +57 -28
- package/dist/schedule/coverage.d.ts.map +1 -1
- package/dist/schedule/coverage.js +24 -1
- package/dist/schedule/coverage.js.map +1 -1
- package/dist/schedule/definition.d.ts +138 -19
- package/dist/schedule/definition.d.ts.map +1 -1
- package/dist/schedule/definition.js +99 -90
- package/dist/schedule/definition.js.map +1 -1
- package/dist/schedule/index.d.ts +4 -4
- package/dist/schedule/index.d.ts.map +1 -1
- package/dist/schedule/index.js +2 -2
- package/dist/schedule/index.js.map +1 -1
- package/dist/schedule/rules.d.ts +438 -113
- package/dist/schedule/rules.d.ts.map +1 -1
- package/dist/schedule/rules.js +310 -108
- package/dist/schedule/rules.js.map +1 -1
- package/dist/schedule/shift-patterns.d.ts +0 -17
- package/dist/schedule/shift-patterns.d.ts.map +1 -1
- package/dist/schedule/shift-patterns.js +0 -17
- package/dist/schedule/shift-patterns.js.map +1 -1
- package/dist/schedule/time-periods.d.ts +37 -27
- package/dist/schedule/time-periods.d.ts.map +1 -1
- package/dist/schedule/time-periods.js +37 -27
- package/dist/schedule/time-periods.js.map +1 -1
- package/dist/types.d.ts +67 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +64 -0
- package/dist/types.js.map +1 -1
- package/package.json +8 -9
- package/solver/src/solver/models.py +41 -4
- package/solver/src/solver/solver.py +546 -157
- package/solver/tests/test_solver.py +363 -7
- package/src/client.schemas.ts +127 -10
- package/src/client.ts +3 -0
- package/src/client.types.ts +73 -7
- package/src/cpsat/cost.ts +17 -7
- package/src/cpsat/model-builder.ts +614 -129
- package/src/cpsat/response.ts +57 -13
- package/src/cpsat/rule-descriptor.ts +311 -0
- package/src/cpsat/rules/artifacts.ts +56 -0
- package/src/cpsat/rules/assign-together.ts +104 -69
- package/src/cpsat/rules/assigned-day.ts +70 -0
- package/src/cpsat/rules/assignment-priority.ts +54 -58
- package/src/cpsat/rules/assignment-terms.ts +43 -0
- package/src/cpsat/rules/concurrent-intervals.ts +93 -0
- package/src/cpsat/rules/cost-utils.ts +1 -9
- package/src/cpsat/rules/day-cost-multiplier.ts +101 -81
- package/src/cpsat/rules/day-cost-surcharge.ts +91 -70
- package/src/cpsat/rules/index.ts +30 -24
- package/src/cpsat/rules/location-preference.ts +47 -52
- package/src/cpsat/rules/max-concurrent-assignments.ts +170 -0
- package/src/cpsat/rules/max-consecutive-days.ts +74 -91
- package/src/cpsat/rules/max-days-of-week-per-period.ts +127 -0
- package/src/cpsat/rules/max-days-week.ts +89 -115
- package/src/cpsat/rules/max-hours-day.ts +64 -84
- package/src/cpsat/rules/max-hours-week.ts +74 -90
- package/src/cpsat/rules/max-shifts-day.ts +65 -85
- package/src/cpsat/rules/min-consecutive-days.ts +161 -127
- package/src/cpsat/rules/min-days-of-week-per-period.ts +144 -0
- package/src/cpsat/rules/min-days-week.ts +93 -91
- package/src/cpsat/rules/min-hours-day.ts +79 -58
- package/src/cpsat/rules/min-hours-week.ts +87 -62
- package/src/cpsat/rules/min-rest-between-shifts.ts +191 -178
- package/src/cpsat/rules/minimize-cost.ts +175 -188
- package/src/cpsat/rules/must-assign.ts +44 -81
- package/src/cpsat/rules/overtime-daily-multiplier.ts +158 -140
- package/src/cpsat/rules/overtime-daily-surcharge.ts +141 -126
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +234 -253
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +183 -175
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +161 -150
- package/src/cpsat/rules/pattern-eligibility.ts +26 -0
- package/src/cpsat/rules/pattern-time.ts +30 -0
- package/src/cpsat/rules/registry.ts +140 -62
- package/src/cpsat/rules/resolver.ts +58 -30
- package/src/cpsat/rules/role-preference.ts +92 -0
- package/src/cpsat/rules/rules.types.ts +89 -17
- package/src/cpsat/rules/scope.types.ts +31 -26
- package/src/cpsat/rules/target-days-week.ts +105 -0
- package/src/cpsat/rules/target-peak-concurrent-assignments.ts +212 -0
- package/src/cpsat/rules/time-cost-surcharge.ts +116 -122
- package/src/cpsat/rules/time-off.ts +154 -163
- package/src/cpsat/rules/variables.ts +11 -0
- package/src/cpsat/rules.ts +43 -17
- package/src/cpsat/semantic-time.ts +29 -43
- package/src/cpsat/types.ts +53 -4
- package/src/cpsat/utils.ts +92 -20
- package/src/cpsat/validation-reporter.ts +170 -19
- package/src/cpsat/validation.types.ts +22 -11
- package/src/datetime.utils.ts +45 -44
- package/src/index.ts +64 -12
- package/src/schedule/cost.ts +40 -61
- package/src/schedule/coverage.ts +57 -28
- package/src/schedule/definition.ts +565 -76
- package/src/schedule/index.ts +23 -3
- package/src/schedule/rules.ts +631 -132
- package/src/schedule/shift-patterns.ts +0 -17
- package/src/schedule/time-periods.ts +37 -27
- package/src/types.ts +114 -2
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { defineRuleDescriptor } from "../rule-descriptor.js";
|
|
3
|
+
import { targetPeakPriorityToPenalty } from "../utils.js";
|
|
4
|
+
import {
|
|
5
|
+
boolVariable,
|
|
6
|
+
hardConstraint,
|
|
7
|
+
intVariable,
|
|
8
|
+
reportValidation,
|
|
9
|
+
skipValidation,
|
|
10
|
+
softConstraint,
|
|
11
|
+
} from "./artifacts.js";
|
|
12
|
+
import {
|
|
13
|
+
buildConcurrentAssignmentSegments,
|
|
14
|
+
collectConcurrentAssignmentIntervals,
|
|
15
|
+
resolveConcurrentWindow,
|
|
16
|
+
} from "./concurrent-intervals.js";
|
|
17
|
+
import {
|
|
18
|
+
entityScope,
|
|
19
|
+
parseEntityScope,
|
|
20
|
+
parseTimeScope,
|
|
21
|
+
resolveActiveDaysFromScope,
|
|
22
|
+
resolveMembersFromScope,
|
|
23
|
+
ruleGroup,
|
|
24
|
+
SoftPrioritySchema,
|
|
25
|
+
timeScope,
|
|
26
|
+
} from "./scope.types.js";
|
|
27
|
+
|
|
28
|
+
export const TargetPeakConcurrentAssignmentsSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
assignments: z.number().int().min(0),
|
|
31
|
+
priority: SoftPrioritySchema.default("HIGH"),
|
|
32
|
+
})
|
|
33
|
+
.and(entityScope(["members", "roles", "skills"]))
|
|
34
|
+
.and(timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]));
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Configuration for {@link targetPeakConcurrentAssignmentsRuleDescriptor}.
|
|
38
|
+
*/
|
|
39
|
+
export type TargetPeakConcurrentAssignmentsConfig = z.infer<
|
|
40
|
+
typeof TargetPeakConcurrentAssignmentsSchema
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
/** @internal */
|
|
44
|
+
export const TARGET_PEAK_CONCURRENT_ASSIGNMENTS_OBJECTIVE_STAGE_ID =
|
|
45
|
+
"__dabke_target_peak_concurrent_assignments__";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Low-level descriptor for the `target-peak-concurrent-assignments` rule.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* Softly targets the daily peak number of concurrent targeted assignments
|
|
52
|
+
* within the scoped day set. This is not a lower bound across the whole day.
|
|
53
|
+
* Use {@link import("../../schedule/coverage.js").cover} for whole-window
|
|
54
|
+
* minimum staffing, and pair this rule with
|
|
55
|
+
* {@link import("./max-concurrent-assignments.js").maxConcurrentAssignmentsRuleDescriptor}
|
|
56
|
+
* when the same value is also a hard capacity cap.
|
|
57
|
+
*
|
|
58
|
+
* @category Rules
|
|
59
|
+
*/
|
|
60
|
+
export const targetPeakConcurrentAssignmentsRuleDescriptor = defineRuleDescriptor({
|
|
61
|
+
name: "target-peak-concurrent-assignments",
|
|
62
|
+
schema: TargetPeakConcurrentAssignmentsSchema,
|
|
63
|
+
compile(config, ctx) {
|
|
64
|
+
const { assignments, priority } = config;
|
|
65
|
+
if (assignments === 0) {
|
|
66
|
+
return { rule: "target-peak-concurrent-assignments", artifacts: [] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const entityScopeValue = parseEntityScope(config);
|
|
70
|
+
const timeScopeValue = parseTimeScope(config);
|
|
71
|
+
const targetMembers = resolveMembersFromScope(entityScopeValue, [...ctx.members]);
|
|
72
|
+
const activeDays = resolveActiveDaysFromScope(timeScopeValue, [...ctx.days]);
|
|
73
|
+
|
|
74
|
+
if (activeDays.length === 0) {
|
|
75
|
+
return { rule: "target-peak-concurrent-assignments", artifacts: [] };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const group = ruleGroup(
|
|
79
|
+
`target-peak-concurrent-assignments:${assignments}`,
|
|
80
|
+
`Target peak ${assignments} concurrent assignment${assignments === 1 ? "" : "s"}`,
|
|
81
|
+
entityScopeValue,
|
|
82
|
+
timeScopeValue,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const artifacts = activeDays.flatMap((day) => {
|
|
86
|
+
const dayContext = { days: [day.iso], memberIds: targetMembers.map((member) => member.id) };
|
|
87
|
+
const intervals = collectConcurrentAssignmentIntervals(day, targetMembers, ctx.shiftPatterns);
|
|
88
|
+
const window = resolveConcurrentWindow(intervals);
|
|
89
|
+
const segments = buildConcurrentAssignmentSegments(intervals, window.start, window.end);
|
|
90
|
+
const dayKey = `${group.key}:${day.iso}`;
|
|
91
|
+
const shortfallVar = `${dayKey}:shortfall`;
|
|
92
|
+
|
|
93
|
+
if (segments.length === 0) {
|
|
94
|
+
return [
|
|
95
|
+
intVariable(shortfallVar, 0, assignments),
|
|
96
|
+
hardConstraint({
|
|
97
|
+
group,
|
|
98
|
+
description: `Fix peak-target shortfall to ${assignments} on ${day.iso} because no eligible assignments exist`,
|
|
99
|
+
context: dayContext,
|
|
100
|
+
validation: skipValidation(
|
|
101
|
+
"scaffolding",
|
|
102
|
+
"Peak-target shortfall is fixed when no eligible assignments can contribute to the daily peak.",
|
|
103
|
+
),
|
|
104
|
+
terms: [{ var: shortfallVar, coeff: 1 }],
|
|
105
|
+
comparator: "==",
|
|
106
|
+
targetValue: assignments,
|
|
107
|
+
}),
|
|
108
|
+
softConstraint({
|
|
109
|
+
stage: TARGET_PEAK_CONCURRENT_ASSIGNMENTS_OBJECTIVE_STAGE_ID,
|
|
110
|
+
group,
|
|
111
|
+
description: `Target peak ${assignments} concurrent assignment${assignments === 1 ? "" : "s"} on ${day.iso}`,
|
|
112
|
+
context: dayContext,
|
|
113
|
+
validation: reportValidation(),
|
|
114
|
+
terms: [{ var: shortfallVar, coeff: 1 }],
|
|
115
|
+
comparator: "<=",
|
|
116
|
+
targetValue: 0,
|
|
117
|
+
penalty: targetPeakPriorityToPenalty(priority),
|
|
118
|
+
constraintId: `${dayKey}:shortfall`,
|
|
119
|
+
}),
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const witnessVars = segments.map((segment) => ({
|
|
124
|
+
segmentStart: segment.start,
|
|
125
|
+
name: `${dayKey}:${segment.start}:witness`,
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
const helperArtifacts = [
|
|
129
|
+
intVariable(shortfallVar, 0, assignments),
|
|
130
|
+
...witnessVars.map(({ name }) => boolVariable(name)),
|
|
131
|
+
hardConstraint({
|
|
132
|
+
group,
|
|
133
|
+
description: `Select exactly one peak segment witness on ${day.iso}`,
|
|
134
|
+
context: dayContext,
|
|
135
|
+
validation: skipValidation(
|
|
136
|
+
"scaffolding",
|
|
137
|
+
"Peak-target witness selection is an internal helper for day-level peak targeting.",
|
|
138
|
+
),
|
|
139
|
+
terms: witnessVars.map(({ name }) => ({ var: name, coeff: 1 })),
|
|
140
|
+
comparator: "==",
|
|
141
|
+
targetValue: 1,
|
|
142
|
+
}),
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const linkageArtifacts = segments.map((segment) => {
|
|
146
|
+
const witnessVar = witnessVars.find(
|
|
147
|
+
(witness) => witness.segmentStart === segment.start,
|
|
148
|
+
)?.name;
|
|
149
|
+
if (!witnessVar) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Missing peak-target witness variable for segment ${segment.start} on ${day.iso}`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return hardConstraint({
|
|
156
|
+
group,
|
|
157
|
+
description: `Link peak-target shortfall to segment ${formatTimeRange(segment.start, segment.end)} on ${day.iso}`,
|
|
158
|
+
context: dayContext,
|
|
159
|
+
validation: skipValidation(
|
|
160
|
+
"scaffolding",
|
|
161
|
+
"Peak-target segment linkage is an internal helper that selects the best segment on a day.",
|
|
162
|
+
),
|
|
163
|
+
terms: [
|
|
164
|
+
...segment.varNames.map((varName) => ({ var: varName, coeff: 1 })),
|
|
165
|
+
{ var: witnessVar, coeff: -assignments },
|
|
166
|
+
{ var: shortfallVar, coeff: 1 },
|
|
167
|
+
],
|
|
168
|
+
comparator: ">=",
|
|
169
|
+
targetValue: 0,
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const shortfallArtifact = softConstraint({
|
|
174
|
+
stage: TARGET_PEAK_CONCURRENT_ASSIGNMENTS_OBJECTIVE_STAGE_ID,
|
|
175
|
+
group,
|
|
176
|
+
description: `Target peak ${assignments} concurrent assignment${assignments === 1 ? "" : "s"} on ${day.iso}`,
|
|
177
|
+
context: dayContext,
|
|
178
|
+
validation: reportValidation(),
|
|
179
|
+
terms: [{ var: shortfallVar, coeff: 1 }],
|
|
180
|
+
comparator: "<=",
|
|
181
|
+
targetValue: 0,
|
|
182
|
+
penalty: targetPeakPriorityToPenalty(priority),
|
|
183
|
+
constraintId: `${dayKey}:shortfall`,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const dayArtifacts: Array<
|
|
187
|
+
| (typeof helperArtifacts)[number]
|
|
188
|
+
| (typeof linkageArtifacts)[number]
|
|
189
|
+
| typeof shortfallArtifact
|
|
190
|
+
> = [];
|
|
191
|
+
dayArtifacts.push(...helperArtifacts, ...linkageArtifacts, shortfallArtifact);
|
|
192
|
+
return dayArtifacts;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
rule: "target-peak-concurrent-assignments",
|
|
197
|
+
artifacts,
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
function formatTimeRange(startMinutes: number, endMinutes: number): string {
|
|
203
|
+
return `${formatClockMinutes(startMinutes)}-${formatClockMinutes(endMinutes)}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function formatClockMinutes(totalMinutes: number): string {
|
|
207
|
+
const minutesPerDay = 24 * 60;
|
|
208
|
+
const normalized = ((totalMinutes % minutesPerDay) + minutesPerDay) % minutesPerDay;
|
|
209
|
+
const hours = Math.floor(normalized / 60);
|
|
210
|
+
const minutes = normalized % 60;
|
|
211
|
+
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
|
212
|
+
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
import type { CompilationRule, CostContribution } from "../model-builder.js";
|
|
3
|
-
import type { ShiftPattern, SchedulingMember } from "../types.js";
|
|
4
2
|
import type { TimeOfDay } from "../../types.js";
|
|
5
|
-
import
|
|
3
|
+
import { defineRuleDescriptor } from "../rule-descriptor.js";
|
|
4
|
+
import type { CostArtifact } from "../rule-descriptor.js";
|
|
6
5
|
import { COST_CATEGORY } from "../cost.js";
|
|
7
|
-
import {
|
|
6
|
+
import { MINUTES_PER_DAY, normalizeEndMinutes, timeOfDayToMinutes } from "../utils.js";
|
|
8
7
|
import {
|
|
9
8
|
entityScope,
|
|
10
|
-
timeScope,
|
|
11
9
|
parseEntityScope,
|
|
12
10
|
parseTimeScope,
|
|
13
|
-
resolveMembersFromScope,
|
|
14
11
|
resolveActiveDaysFromScope,
|
|
12
|
+
resolveMembersFromScope,
|
|
13
|
+
timeScope,
|
|
15
14
|
} from "./scope.types.js";
|
|
16
15
|
|
|
17
16
|
const TimeOfDaySchema = z.object({
|
|
@@ -19,7 +18,7 @@ const TimeOfDaySchema = z.object({
|
|
|
19
18
|
minutes: z.number().int().min(0).max(59),
|
|
20
19
|
});
|
|
21
20
|
|
|
22
|
-
const TimeCostSurchargeSchema = z
|
|
21
|
+
export const TimeCostSurchargeSchema = z
|
|
23
22
|
.object({
|
|
24
23
|
amountPerHour: z.number().min(0),
|
|
25
24
|
window: z.object({
|
|
@@ -30,14 +29,112 @@ const TimeCostSurchargeSchema = z
|
|
|
30
29
|
.and(entityScope(["members", "roles", "skills"]))
|
|
31
30
|
.and(timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]));
|
|
32
31
|
|
|
33
|
-
/** Configuration for {@link
|
|
32
|
+
/** Configuration for {@link timeCostSurchargeRuleDescriptor}. */
|
|
34
33
|
export type TimeCostSurchargeConfig = z.infer<typeof TimeCostSurchargeSchema>;
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
export const timeCostSurchargeRuleDescriptor = defineRuleDescriptor({
|
|
36
|
+
name: "time-cost-surcharge",
|
|
37
|
+
schema: TimeCostSurchargeSchema,
|
|
38
|
+
compile(config) {
|
|
39
|
+
const { amountPerHour, window } = config;
|
|
40
|
+
const entityScopeValue = parseEntityScope(config);
|
|
41
|
+
const timeScopeValue = parseTimeScope(config);
|
|
42
|
+
|
|
43
|
+
const validation = {
|
|
44
|
+
strategy: "skip" as const,
|
|
45
|
+
category: "no-meaningful-feedback" as const,
|
|
46
|
+
rationale:
|
|
47
|
+
amountPerHour <= 0
|
|
48
|
+
? "A zero time surcharge does not change optimization or cost accounting."
|
|
49
|
+
: "Time cost surcharges only affect optimization when minimize-cost is active.",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const costArtifact: CostArtifact = {
|
|
53
|
+
kind: "cost",
|
|
54
|
+
validation,
|
|
55
|
+
compileObjective(builder) {
|
|
56
|
+
if (!builder.costContext?.active || amountPerHour <= 0) return;
|
|
57
|
+
|
|
58
|
+
const targetMembers = resolveMembersFromScope(entityScopeValue, builder.members);
|
|
59
|
+
const activeDays = resolveActiveDaysFromScope(timeScopeValue, builder.days);
|
|
60
|
+
if (targetMembers.length === 0 || activeDays.length === 0) return;
|
|
61
|
+
|
|
62
|
+
const { normalizationFactor } = builder.costContext;
|
|
63
|
+
for (const member of targetMembers) {
|
|
64
|
+
for (const pattern of builder.shiftPatterns) {
|
|
65
|
+
if (!builder.canAssign(member, pattern)) continue;
|
|
66
|
+
const overlapMinutes = computeOverlapMinutes(
|
|
67
|
+
pattern.startTime,
|
|
68
|
+
pattern.endTime,
|
|
69
|
+
window.from,
|
|
70
|
+
window.until,
|
|
71
|
+
);
|
|
72
|
+
if (overlapMinutes <= 0) continue;
|
|
73
|
+
const surcharge = (amountPerHour * overlapMinutes) / 60;
|
|
74
|
+
const normalizedPenalty = surcharge / normalizationFactor;
|
|
75
|
+
|
|
76
|
+
for (const day of activeDays) {
|
|
77
|
+
if (!builder.patternAvailableOnDay(pattern, day)) continue;
|
|
78
|
+
builder.addPenalty(
|
|
79
|
+
builder.assignment(member.id, pattern.id, day),
|
|
80
|
+
Math.max(1, normalizedPenalty),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
calculateCost(assignments, costContext) {
|
|
87
|
+
if (amountPerHour <= 0) return { entries: [] };
|
|
88
|
+
|
|
89
|
+
const patternMap = new Map(
|
|
90
|
+
costContext.shiftPatterns.map((pattern) => [pattern.id, pattern]),
|
|
91
|
+
);
|
|
92
|
+
const activeDays = new Set(
|
|
93
|
+
resolveActiveDaysFromScope(timeScopeValue, [...costContext.days]).map((day) => day.iso),
|
|
94
|
+
);
|
|
95
|
+
const targetMemberIds = new Set(
|
|
96
|
+
resolveMembersFromScope(entityScopeValue, [...costContext.members]).map(
|
|
97
|
+
(member) => member.id,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const entries = [] as Array<{
|
|
102
|
+
memberId: string;
|
|
103
|
+
day: string;
|
|
104
|
+
category: string;
|
|
105
|
+
amount: number;
|
|
106
|
+
}>;
|
|
107
|
+
for (const assignment of assignments) {
|
|
108
|
+
if (!activeDays.has(assignment.day) || !targetMemberIds.has(assignment.memberId))
|
|
109
|
+
continue;
|
|
110
|
+
const pattern = patternMap.get(assignment.shiftPatternId);
|
|
111
|
+
if (!pattern) continue;
|
|
112
|
+
const overlapMinutes = computeOverlapMinutes(
|
|
113
|
+
pattern.startTime,
|
|
114
|
+
pattern.endTime,
|
|
115
|
+
window.from,
|
|
116
|
+
window.until,
|
|
117
|
+
);
|
|
118
|
+
if (overlapMinutes <= 0) continue;
|
|
119
|
+
entries.push({
|
|
120
|
+
memberId: assignment.memberId,
|
|
121
|
+
day: assignment.day,
|
|
122
|
+
category: COST_CATEGORY.PREMIUM,
|
|
123
|
+
amount: (amountPerHour * overlapMinutes) / 60,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { entries };
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
rule: "time-cost-surcharge",
|
|
133
|
+
artifacts: [costArtifact],
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
41
138
|
function computeOverlapMinutes(
|
|
42
139
|
shiftStart: TimeOfDay,
|
|
43
140
|
shiftEnd: TimeOfDay,
|
|
@@ -49,24 +146,15 @@ function computeOverlapMinutes(
|
|
|
49
146
|
const wFrom = timeOfDayToMinutes(windowFrom);
|
|
50
147
|
const wUntil = timeOfDayToMinutes(windowUntil);
|
|
51
148
|
|
|
52
|
-
// Expand window: if overnight (e.g., 22:00-06:00), wUntil < wFrom
|
|
53
|
-
// Represent as two intervals or one extended interval
|
|
54
|
-
let totalOverlap = 0;
|
|
55
|
-
|
|
56
149
|
if (wFrom < wUntil) {
|
|
57
|
-
|
|
58
|
-
totalOverlap = rangeOverlap(sStart, sEnd, wFrom, wUntil);
|
|
59
|
-
} else {
|
|
60
|
-
// Overnight window (e.g., 22:00-06:00)
|
|
61
|
-
// Split into [wFrom, MINUTES_PER_DAY) and [0, wUntil)
|
|
62
|
-
// But shift may also span overnight, so use extended ranges
|
|
63
|
-
totalOverlap += rangeOverlap(sStart, sEnd, wFrom, MINUTES_PER_DAY);
|
|
64
|
-
totalOverlap += rangeOverlap(sStart, sEnd, MINUTES_PER_DAY, MINUTES_PER_DAY + wUntil);
|
|
65
|
-
// Also check if shift overlaps [0, wUntil) in the same day
|
|
66
|
-
totalOverlap += rangeOverlap(sStart, sEnd, 0, wUntil);
|
|
150
|
+
return rangeOverlap(sStart, sEnd, wFrom, wUntil);
|
|
67
151
|
}
|
|
68
152
|
|
|
69
|
-
return
|
|
153
|
+
return (
|
|
154
|
+
rangeOverlap(sStart, sEnd, wFrom, MINUTES_PER_DAY) +
|
|
155
|
+
rangeOverlap(sStart, sEnd, MINUTES_PER_DAY, MINUTES_PER_DAY + wUntil) +
|
|
156
|
+
rangeOverlap(sStart, sEnd, 0, wUntil)
|
|
157
|
+
);
|
|
70
158
|
}
|
|
71
159
|
|
|
72
160
|
function rangeOverlap(aStart: number, aEnd: number, bStart: number, bEnd: number): number {
|
|
@@ -74,97 +162,3 @@ function rangeOverlap(aStart: number, aEnd: number, bStart: number, bEnd: number
|
|
|
74
162
|
const end = Math.min(aEnd, bEnd);
|
|
75
163
|
return Math.max(0, end - start);
|
|
76
164
|
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Creates a time-of-day surcharge rule (e.g., night differential).
|
|
80
|
-
*
|
|
81
|
-
* Adds a flat surcharge per hour for the portion of a shift that overlaps
|
|
82
|
-
* a time-of-day window. Independent of the member's base rate.
|
|
83
|
-
*
|
|
84
|
-
* When `minimizeCost()` is not present, no solver terms are emitted,
|
|
85
|
-
* but the `cost()` method still contributes to post-solve calculation.
|
|
86
|
-
*/
|
|
87
|
-
export function createTimeCostSurchargeRule(config: TimeCostSurchargeConfig): CompilationRule {
|
|
88
|
-
const parsed = TimeCostSurchargeSchema.parse(config);
|
|
89
|
-
const { amountPerHour, window } = parsed;
|
|
90
|
-
const entityScopeValue = parseEntityScope(parsed);
|
|
91
|
-
const timeScopeValue = parseTimeScope(parsed);
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
compile(b) {
|
|
95
|
-
if (!b.costContext?.active) return;
|
|
96
|
-
if (amountPerHour <= 0) return;
|
|
97
|
-
|
|
98
|
-
const targetMembers = resolveMembersFromScope(entityScopeValue, b.members);
|
|
99
|
-
const activeDays = resolveActiveDaysFromScope(timeScopeValue, b.days);
|
|
100
|
-
|
|
101
|
-
if (targetMembers.length === 0 || activeDays.length === 0) return;
|
|
102
|
-
|
|
103
|
-
const { normalizationFactor } = b.costContext;
|
|
104
|
-
|
|
105
|
-
for (const emp of targetMembers) {
|
|
106
|
-
for (const pattern of b.shiftPatterns) {
|
|
107
|
-
if (!b.canAssign(emp, pattern)) continue;
|
|
108
|
-
|
|
109
|
-
const overlapMinutes = computeOverlapMinutes(
|
|
110
|
-
pattern.startTime,
|
|
111
|
-
pattern.endTime,
|
|
112
|
-
window.from,
|
|
113
|
-
window.until,
|
|
114
|
-
);
|
|
115
|
-
if (overlapMinutes <= 0) continue;
|
|
116
|
-
|
|
117
|
-
const surcharge = (amountPerHour * overlapMinutes) / 60;
|
|
118
|
-
const normalizedPenalty = surcharge / normalizationFactor;
|
|
119
|
-
|
|
120
|
-
for (const day of activeDays) {
|
|
121
|
-
if (!b.patternAvailableOnDay(pattern, day)) continue;
|
|
122
|
-
b.addPenalty(b.assignment(emp.id, pattern.id, day), Math.max(1, normalizedPenalty));
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
cost(
|
|
129
|
-
assignments: ShiftAssignment[],
|
|
130
|
-
members: ReadonlyArray<SchedulingMember>,
|
|
131
|
-
shiftPatterns: ReadonlyArray<ShiftPattern>,
|
|
132
|
-
): CostContribution {
|
|
133
|
-
if (amountPerHour <= 0) return { entries: [] };
|
|
134
|
-
|
|
135
|
-
const patternMap = new Map(shiftPatterns.map((p) => [p.id, p]));
|
|
136
|
-
const allDays = [...new Set(assignments.map((a) => a.day))].toSorted();
|
|
137
|
-
const activeDays = new Set(resolveActiveDaysFromScope(timeScopeValue, allDays));
|
|
138
|
-
const targetEmpIds = new Set(
|
|
139
|
-
resolveMembersFromScope(entityScopeValue, [...members]).map((e) => e.id),
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const entries: CostContribution["entries"] = [];
|
|
143
|
-
|
|
144
|
-
for (const a of assignments) {
|
|
145
|
-
if (!activeDays.has(a.day)) continue;
|
|
146
|
-
if (!targetEmpIds.has(a.memberId)) continue;
|
|
147
|
-
const pattern = patternMap.get(a.shiftPatternId);
|
|
148
|
-
if (!pattern) continue;
|
|
149
|
-
|
|
150
|
-
const overlapMinutes = computeOverlapMinutes(
|
|
151
|
-
pattern.startTime,
|
|
152
|
-
pattern.endTime,
|
|
153
|
-
window.from,
|
|
154
|
-
window.until,
|
|
155
|
-
);
|
|
156
|
-
if (overlapMinutes <= 0) continue;
|
|
157
|
-
|
|
158
|
-
const amount = (amountPerHour * overlapMinutes) / 60;
|
|
159
|
-
entries.push({
|
|
160
|
-
memberId: a.memberId,
|
|
161
|
-
day: a.day,
|
|
162
|
-
category: COST_CATEGORY.PREMIUM,
|
|
163
|
-
amount,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return { entries };
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
}
|