Skip to content

Reject unsupported RequiredStaff values instead of silently storing 1 - #4

Merged
kris7011 merged 1 commit into
mainfrom
fix/required-staff-single-employee
Oct 16, 2026
Merged

kris7011 merged 1 commit into
mainfrom
fix/required-staff-single-employee

Conversation

@kris7011

Copy link
Copy Markdown
Owner

Stacked on #3 (it uses the integration test project). Merge order: #1 → #2 → #3 → this PR.

Problem

ShiftRepository does not persist RequiredStaff (ShiftEntity has no such column), and every shift is loaded with RequiredStaff = 1. POST /api/shifts accepted requiredStaff: 3, echoed 3 back, and stored 1. The simulation UI had a free "Krævet bemanding" field: asking for 3 people still returned one suggested employee and "can be covered".

Analysis of the domain model

The system only supports one employee per shift:

Layer Behaviour
Persistence ShiftEntity.EmployeeId is a single value; there is no RequiredStaff column
Schedule generation ScheduleGeneratorService picks one matching employee per open shift (AssignToEmployee(id))
Coverage ScheduleOverviewService counts a shift as assigned when EmployeeId != null
Simulation ScheduleSimulationService returns one SuggestedEmployeeId
Analysis Assignment analysis, load and overview services evaluate one employee per shift

Shift.RequiredStaff, IsFullyStaffed() and MissingStaffCount() only work on the in-memory AssignedEmployees list, which is never persisted.

Supporting several employees per shift would need a new join table and migration, multi-assignment in the generator, partial coverage in the overview, multi-candidate simulation, and frontend changes across every view. That is a product decision, not a bug fix. The smallest correct solution is to make the API honest about the actual model.

Solution

  • Domain: Shift.SupportedRequiredStaff = 1 and Shift.IsSupportedRequiredStaff(int). These are the single source of truth, documented on the class. ShiftRepository uses the constant instead of a magic 1.
  • API: POST /api/shifts, POST /api/schedule/simulate and POST /api/load-analysis return 400 { "error": "RequiredStaff must be 1. Each shift is staffed by one employee; create one shift per required employee." } for any value other than 1. An omitted requiredStaff defaults to 1. Before, the shift and load-analysis endpoints read an omitted value as 0.
  • Frontend: the simulation's staffing input is replaced by a fixed note ("Bemanding: 1 medarbejder pr. vagt…") and the request always sends requiredStaff: 1.
  • README: a new Staffing Model: One Employee per Shift section.
  • CI: the pull_request trigger, permissions and concurrency block are copied verbatim from Add frontend lint and build to CI and fix lint errors #1. That lets this stacked PR get CI, and it merges cleanly with Add frontend lint and build to CI and fix lint errors #1 (verified).

The scheduling logic is unchanged. The in-memory IsFullyStaffed/MissingStaffCount helpers and their domain tests are kept.

Test results

ShiftPlanner.Tests                 → 63 passed (+4: IsSupportedRequiredStaff theory)
ShiftPlanner.Api.IntegrationTests  → 29 passed (+9)

New integration tests:

  • POST /api/shifts with 0/2/3 → 400 and nothing persisted
  • requiredStaff: 1 round-trips through SQLite
  • an omitted value defaults to 1
  • a created open shift is covered by one employee after generation, and the overview shows 1 assigned / 0 unassigned
  • simulate with 0 or 2 → 400
  • load analysis with 2 → 400; omitted → 200

Full local integration of #1 + #2 + #3 + this PR: dotnet test 63 + 29 passed, frontend lint clean, npm test 8 passed, build OK.

Limitations

  • This is a breaking API change for clients that sent requiredStaff > 1. Those values were never honoured, so no stored data changes.
  • Real multi-staff shifts remain a possible future feature (see the analysis above).

🤖 Generated with Claude Code

The model staffs each shift with exactly one employee: ShiftEntity has
a single EmployeeId and no RequiredStaff column, schedule generation
assigns one employee per open shift, coverage counts shifts with an
employee, and simulation suggests one employee. POST /api/shifts still
accepted any RequiredStaff and silently persisted 1, and the simulation
UI let users request more staff than could ever be assigned.

- Add Shift.SupportedRequiredStaff (1) and IsSupportedRequiredStaff as
  the single source of truth; use it in ShiftRepository
- Return 400 for RequiredStaff != 1 on POST /api/shifts,
  /api/schedule/simulate and /api/load-analysis; omitted values
  default to 1
- Replace the simulation staffing input with a fixed note
- Document the staffing model and add unit and integration tests
- Run CI for pull requests to any base branch (same trigger,
  permissions and concurrency block as the CI PR)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kris7011
kris7011 changed the base branch from test/api-integration-tests to main October 16, 2026 13:30
@kris7011
kris7011 merged commit 1b84da7 into main Oct 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant