Shift
The shift module defines working hours for factory lines. It has three layers: base shifts, shift overrides, and available time — a pre-generated table that combines them for fast lookup at runtime.
Background: Work Groups
Section titled “Background: Work Groups”Lines are divided into work groups ([Planning].[WorkGroup]). Each line can have multiple work groups.
Each work group has its own shift schedule.
Overrides can target individual work groups to apply different timings for specific lines or sections of a line.
Base Shifts
Section titled “Base Shifts”Base shifts are defined in [Planning].[Shift].
Multiple shifts can exist simultaneously (e.g., Morning, General, Night).
A base shift applies to all work groups and all dates unless an override says otherwise.
Attendance Window
Section titled “Attendance Window”Each shift defines a six-point attendance window:
| Field | Description |
|---|---|
EarlyInTime | Earliest allowed clock-in |
InTime | Scheduled start time |
LateInTime | Latest allowed clock-in |
EarlyOutTime | Earliest allowed clock-out |
OutTime | Scheduled end time |
LateOutTime | Latest allowed clock-out |
The maximum possible shift span runs from EarlyInTime to LateOutTime.
The normal shift span runs from InTime to OutTime.
Duration Fields
Section titled “Duration Fields”All durations are in minutes and exclude break time.
| Field | Description |
|---|---|
ShiftMinutes | Normal shift duration (OutTime − InTime) |
MaxShiftMinutes | Maximum shift duration (LateOutTime − EarlyInTime) |
BreakMinutes | Total break duration |
Breaks
Section titled “Breaks”Breaks are stored as a JSON array in the Breaks column:
[ { "start": "12:00:00", "end": "12:30:00" }, { "start": "15:00:00", "end": "15:15:00" }]BreakMinutes is the sum of all break durations.
ShiftMinutes and MaxShiftMinutes are calculated after subtracting BreakMinutes.
Overrides
Section titled “Overrides”[Planning].[ShiftOverride] allows modifying or disabling a base shift for specific contexts.
Each override references an existing shift via ShiftID.
Overrides cannot add new shifts, only modify or disable existing ones.
Filters
Section titled “Filters”Each override narrows its scope using one or more of three optional filters.
At least one filter must be set.
| Filter | Fields | Description |
|---|---|---|
| Work Group | WorkGroupID | Applies only to this work group |
| Date Range | StartDate + EndDate | Applies only within this date range (both fields must be set together) |
| Day of Week | DayOfWeek | Applies only on this day (e.g., "Friday") |
Disabling a Shift
Section titled “Disabling a Shift”When IsDisabled = 1, the override suppresses that shift for the matching context.
An [Planning].[AvailableTime] entry is still created for that shift on those days, but with IsDisabled = 1. Other shifts on the same day are unaffected.
Precedence
Section titled “Precedence”When multiple overrides match the same shift on a given date for a given work group, the following rules determine which one applies:
Rule 1 — More filters wins. An override with more filters set takes precedence over one with fewer, regardless of which filters are used.
Rule 2 — Tie-break by filter rank. When two overrides have the same number of filters, compare their highest-ranked filter. The filter rank is: WorkGroup > DateRange > DayOfWeek. The override with the higher-ranked filter wins.
Full precedence order (highest to lowest):
| Filters set | Filter count | Score |
|---|---|---|
| WorkGroup + DateRange + DayOfWeek | 3 | 18 |
| WorkGroup + DateRange | 2 | 10 |
| WorkGroup + DayOfWeek | 2 | 8 |
| DateRange + DayOfWeek | 2 | 6 |
| WorkGroup only | 1 | 3 |
| DateRange only | 1 | 2 |
| DayOfWeek only | 1 | 1 |
Examples
Section titled “Examples”Example 1 — Shorter Friday shift for all lines
Add an override with DayOfWeek = "Friday" and the adjusted timings.
Leave WorkGroupID, StartDate, and EndDate null. Applies to every work group every Friday.
Example 2 — Ramadan timings for all lines
Add an override with StartDate = 2026-02-17 and EndDate = 2026-03-19 and the adjusted timings.
Leave WorkGroupID and DayOfWeek null. Applies to every work group throughout Ramadan.
Example 3 — Ramadan Fridays get their own schedule (coexisting with Examples 1 and 2)
Add a third override with the same StartDate/EndDate range, DayOfWeek = "Friday", and its own timings.
This 2-filter override has higher precedence than both the 1-filter Friday override (Example 1) and the 1-filter Ramadan range override (Example 2).
On Fridays during Ramadan, this override wins.
Example 4 — A specific work group has different timings year-round
Add a WorkGroup-only override (WorkGroupID set, date range and day of week null).
If the global Friday override from Example 1 also exists, the WorkGroup override wins on Fridays for this group — both have 1 filter, but WorkGroup outranks DayOfWeek.
To give this work group a different Friday schedule as well, add a second override with both WorkGroupID and DayOfWeek = "Friday" set.
That 2-filter override then takes precedence over the 1-filter WorkGroup-only override on Fridays.
Example 5 — Disable a night shift for a work group during Ramadan
Add an override with WorkGroupID and StartDate/EndDate set, and IsDisabled = 1.
The night shift’s [Planning].[AvailableTime] entries for that work group during Ramadan are created with IsDisabled = 1.
All other shifts on those days are unaffected.
Day Off Rules
Section titled “Day Off Rules”Day off rules are defined in [Planning].[DayOffRule]. Each rule is global — it applies to all work groups and all shifts on matching dates.
When a date matches any day off rule, all AvailableTime entries for that day are generated with IsDisabled = 1, regardless of what shifts or overrides exist.
Rule Types
Section titled “Rule Types”| Type | Description | StartDate / EndDate | DayOfWeek |
|---|---|---|---|
OneTime | A specific date or date range | Full date values | — |
Annual | Repeats every year on the same month/day | Sentinel year 1900; only month+day used | — |
Monthly | Repeats every month on the same day(s) | Sentinel year+month 1900-01; only day used | — |
DayOfWeek | Repeats every week on a given weekday | — | Required |
StartDate and EndDate are required for OneTime, Annual, and Monthly rules. For a single-day rule, set it equal to StartDate.
DayOfWeek rules leave both StartDate and EndDate null.
For DayOfWeek, one row per weekday. A “Weekend” rule covering Saturday and Sunday requires two rows sharing the same RuleName.
Available Time Generation
Section titled “Available Time Generation”[Planning].[AvailableTime] is a pre-generated table storing the resolved shift timings for every work group × day combination.
This is the table queried at runtime for shift information — lookups never touch the base shift or override tables directly.
What Gets Generated
Section titled “What Gets Generated”For each work group and each day in the selected year:
- All base shifts are evaluated.
- For each base shift, all applicable overrides are found (matching
ShiftID,WorkGroupID, date range, and day of week). - For each date and work group, the override with the highest precedence is selected. If none apply, the base shift is used.
- One
AvailableTimerow is written using the resolved timings. IsDisabledis set to1if the date matches any day off rule, or if the winning override hasIsDisabled = 1.
A single day can have multiple AvailableTime rows if multiple base shifts are active (e.g., both a morning and a night shift run for the same work group).
Triggering Generation
Section titled “Triggering Generation”Generation is triggered manually in the portal via a Generate button with a year picker.
Only shift times for the current and next year can be generated.
Only entries from today onward are deleted and regenerated. Past entries are left untouched.
AvailableTime Schema
Section titled “AvailableTime Schema”| Column | Description |
|---|---|
ShiftDate | The specific date |
ShiftID | Reference to the base shift |
WorkGroupID | Reference to the work group |
IsDisabled | 1 if the entry is a day off or the winning override is disabled |
EarlyInTime / InTime / LateInTime | Resolved clock-in window |
EarlyOutTime / OutTime / LateOutTime | Resolved clock-out window |
ShiftMinutes | Normal shift duration (excluding breaks) |
MaxShiftMinutes | Maximum shift duration (excluding breaks) |
BreakMinutes | Total break duration |
OvertimeMinutes | Computed: MaxShiftMinutes − ShiftMinutes |