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:
@@ -11,20 +11,20 @@
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Выделяемая на месяц сумма — в `SimulationOptions`, с умолчанием
|
||||
- [ ] Фонд оплаты школы: сумма базовых ставок плюс надбавки за предметы сверх первого
|
||||
- [ ] Наём: соискатель уходит из пула, становится работником, попадает в ростер. Родитель
|
||||
- [x] Выделяемая на месяц сумма — в `SimulationOptions`, с умолчанием
|
||||
- [x] Фонд оплаты школы: сумма базовых ставок плюс надбавки за предметы сверх первого
|
||||
- [x] Наём: соискатель уходит из пула, становится работником, попадает в ростер. Родитель
|
||||
остаётся родителем — новой сущности не заводится
|
||||
- [ ] Назначение предмета нанятому и снятие предмета
|
||||
- [ ] Предел проверяется **в момент действия**: наём или назначение, выводящее фонд за
|
||||
- [x] Назначение предмета нанятому и снятие предмета
|
||||
- [x] Предел проверяется **в момент действия**: наём или назначение, выводящее фонд за
|
||||
выделенную сумму, отклоняется с внятным кодом ошибки, а не откладывается до конца месяца
|
||||
- [ ] Покрытие предметов: какие предметы преподаются в существующих параллелях и не имеют ни
|
||||
- [x] Покрытие предметов: какие предметы преподаются в существующих параллелях и не имеют ни
|
||||
одного учителя
|
||||
- [ ] `GET /api/schools/{id}/staffing` — деньги, покрытие, пул и штат; читает опубликованный
|
||||
- [x] `GET /api/schools/{id}/staffing` — деньги, покрытие, пул и штат; читает опубликованный
|
||||
снимок, воркер не трогает
|
||||
- [ ] Наём и назначение идут в воркер через мейлбокс с `TaskCompletionSource`, как create и delete
|
||||
- [ ] Состав пишется на диск при изменении, снимок публикуется заново
|
||||
- [ ] `docs/protocol.md` пополняется в том же коммите, что и обработчики
|
||||
- [x] Наём и назначение идут в воркер через мейлбокс с `TaskCompletionSource`, как create и delete
|
||||
- [x] Состав пишется на диск при изменении, снимок публикуется заново
|
||||
- [x] `docs/protocol.md` пополняется в том же коммите, что и обработчики
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
| --- | --- | --- |
|
||||
| [10. Предметы и мебель](10-subjects.md) | ✅ | `SubjectDef`, одна учительская должность, кабинет как число мест |
|
||||
| [11. Пустая школа и пул](11-applicants.md) | ✅ | Школа без сотрудников, соискатели с запросом по зарплате |
|
||||
| [12. Наём и бюджет](12-hiring-budget.md) | ⬜ | Наём, назначение предметов, предел фонда оплаты |
|
||||
| [12. Наём и бюджет](12-hiring-budget.md) | ✅ | Наём, назначение предметов, предел фонда оплаты |
|
||||
| [13. Раздел «Управление»](13-management-tab.md) | ⬜ | Деньги, соискатели, штат и назначения на экране |
|
||||
|
||||
## Срез 4. Расписание
|
||||
|
||||
@@ -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 `0x00–0x7F`, server-to-client ids in `0x80–0xFF`, so a misrouted
|
||||
|
||||
Reference in New Issue
Block a user