Enhance staffing management in school simulation by introducing API endpoints for hiring staff and assigning subjects. Implement payroll cap validation to ensure hiring and subject assignments do not exceed the allocated budget. Update the simulation options to include a monthly payroll cap and revise related classes to support new staffing functionalities. Enhance documentation to reflect these changes and update tests to validate the new features.

This commit is contained in:
Leonid Pershin
2026-08-19 00:19:09 +03:00
parent c189578680
commit 94842ab192
24 changed files with 1444 additions and 27 deletions
+92
View File
@@ -212,6 +212,98 @@ cards.
}
```
### `GET /api/schools/{id}/staffing`
Money, uncovered subjects, the applicant pool and current staff. Reads the **published**
roster, applicant snapshot and catalog — it does not post to the worker. Unknown `{id}` is
`404` `unknown-school`. `?lang=ru|en` labels subjects and positions.
`allocated` is `Simulation:MonthlyPayrollCap`. `payroll` is the sum of each staff member's
monthly base (`hourlyWageAsk × baseWeeklyHours × weeksPerMonth`) plus
`extraSubjectSurcharge` of that base for every subject after the first. The cap is checked
when hiring or assigning, not at month end; money itself does not move.
```json
{
"allocated": 10000,
"payroll": 5000,
"remaining": 5000,
"uncovered": [
{
"defName": "Mathematics",
"label": "Математика",
"gradeMin": 5,
"gradeMax": 11,
"hoursPerWeek": 5
}
],
"applicants": [
{
"id": "a0.p0",
"fullName": "Соколов Иван Петрович",
"female": false,
"age": 34,
"isParent": false,
"hourlyWageAsk": 50,
"monthlyBase": 4000
}
],
"staff": [
{
"id": "f3.p1",
"fullName": "Иванова Ольга Михайловна",
"female": true,
"age": 41,
"isParent": true,
"position": "Teacher",
"positionLabel": "Учитель",
"hourlyWageAsk": 50,
"monthlyPay": 5000,
"subjects": [{ "defName": "Mathematics", "label": "Математика" }]
}
]
}
```
Applicants here are the same people as in `saves/{id}.people.json`. A parent keeps the same
id on the roster; hiring them sets `isStaff` on that person and does not create a second
entity. Generated candidates (`aN.p0`) join the roster only when hired.
### `POST /api/schools/{id}/staff/hire`
Body: `{ "personId": "a0.p0", "position": "Teacher" }`. Goes through the school's mailbox.
On success returns the same payload as `GET .../staffing`. `Teacher` needs no room opening;
other positions fill the first free `RoomDef.positions` slot of that kind.
| Status | `code` | When |
| --- | --- | --- |
| `404` | `unknown-school` | No school with that id. |
| `404` | `unknown-applicant` | `personId` is not in the pool. |
| `409` | `already-hired` | That person is already staff. |
| `400` | `unknown-position` | Not a concrete `PositionDef`. |
| `409` | `no-opening` | Every opening of that position is filled. |
| `409` | `payroll-exceeded` | Hire would take `payroll` past `allocated`. |
`payroll-exceeded` includes `allocated`, `payroll` (current), `remaining` and `attempted`
(what payroll would become). Same RFC 7807 `code` field as the other errors.
### `POST /api/schools/{id}/staff/{personId}/subjects`
Body: `{ "subject": "Mathematics" }`. Teachers only. Same success payload as GET staffing.
| Status | `code` | When |
| --- | --- | --- |
| `400` | `not-staff` | Person is not staff. |
| `400` | `not-teacher` | Position is not `Teacher`. |
| `400` | `unknown-subject` | Not a concrete `SubjectDef`. |
| `409` | `already-assigned` | Already on this person. |
| `409` | `payroll-exceeded` | Extra subject would exceed the cap. |
### `DELETE /api/schools/{id}/staff/{personId}/subjects/{subject}`
Removes one assignment. Payroll drops when the subject was not the only one. Unknown
assignment is `404` `unknown-assignment`.
## WebSocket message ids
Client-to-server ids live in `0x000x7F`, server-to-client ids in `0x800xFF`, so a misrouted