Enhance school creation and staffing management with native language support
- Updated protocol documentation to include `nativeLanguages` in `nameSets` and added `nativeLanguage` to school creation options. - Enhanced the UI for school creation to allow selection of native languages, improving user experience. - Revised API interfaces to accommodate new native language features, ensuring proper data handling. - Improved localization strings to support new native language functionalities in both English and Russian. - Updated tests to validate the new native language features and ensure robust functionality in staffing scenarios.
This commit is contained in:
@@ -139,9 +139,9 @@
|
||||
| Место урока | Кабинет класса, если у предмета нет требования к типу помещения |
|
||||
| Нет учителя | Урок не ставится; предмет числится непокрытым |
|
||||
| Карта | В дереве рядом с комнатой — что и у кого там сейчас |
|
||||
| Панель локации | «Сейчас» и «Персонажи» заполняются из расписания |
|
||||
| Панель локации | «Сейчас» и «Персонажи» заполняются из расписания — с срезом 5 из присутствия, см. [`ai.md`](ai.md) |
|
||||
| Расписание класса | В разделе «Управление», сетка «день × урок» |
|
||||
| Расписание учителя | В его карточке, там же где навыки и семья |
|
||||
| Общая сетка школы | Не делаем: её читает только тот, кто раскладывает вручную |
|
||||
| Где живёт код | Отдельная библиотека рядом с `HSchool.People` |
|
||||
| Люди и место | Находятся по расписанию; ходьбы нет |
|
||||
| Люди и место | Находятся по расписанию; ходьбы нет — отменено срезом 5, присутствие стало состоянием ([`ai.md`](ai.md)) |
|
||||
|
||||
@@ -175,3 +175,230 @@
|
||||
- Срез 1 успел сдвинуться: коммит `5cd5a6d` тронул `gameScreen.ts` и `strings.ts` (подпись
|
||||
идущего человека). По правилу журнала он больше не «проверен на `b135a9c`» — правка маленькая и
|
||||
относится к срезу 5, но следующий проход это увидит.
|
||||
|
||||
## Срез 3. Штат
|
||||
|
||||
- **Фазы:** 10–13
|
||||
- **Проверен на:** `21d79cb`, 2026-08-19
|
||||
- **Пути:** `src/HSchool.People/Staffing.cs`, `src/HSchool.People/ApplicantPool.cs`,
|
||||
`src/HSchool.Content/SubjectDef` и `defs/staffing`, `src/HSchool.Server/Api/SchoolEndpoints.cs`
|
||||
(staffing/hire/subjects), `src/HSchool.Server/mods/core/defs/{subjects,positions,rooms,staffing,traits}`,
|
||||
`src/HSchool.Client/src/ui/managementPanel.ts`, `src/HSchool.Client/src/net/api.ts`,
|
||||
`tests/HSchool.People.Tests/{StaffingTests,ApplicantPoolTests}.cs`,
|
||||
`tests/HSchool.AppHost.Tests/StaffingApiTests.cs`
|
||||
- **Итог:** дописано 3 теста (один из них — прямо требуемый фазой 12 и отсутствовавший),
|
||||
дополнен `docs/protocol.md`; расхождений кода с дизайном не найдено
|
||||
|
||||
Что подтверждено:
|
||||
|
||||
- **Фаза 10.** `PETeacher` в `core` нет, `Classroom`, `ComputerLab` и `GymHall` не открывают
|
||||
должностей — должности остались у библиотеки, приёмной, столовой, медкабинета и кабинета
|
||||
директора, ровно как обещано. Кабинет описан числом мест (`seatThing`/`defaultSeats`, пустой
|
||||
список слотов — проверено в `Catalog_LabelsDefsInTheRequestedLanguage`), редактор карты
|
||||
показывает у него одно поле «мест». Неизвестный навык в предмете роняет каталог
|
||||
(`Subject_UnknownSkill_FailsTheCatalog`), полнота локалей — `EveryVanillaLabel_ExistsInBothLocales`,
|
||||
стартовая дата 31 марта 2012 зашита в тестах API.
|
||||
- **Фаза 11.** Все шесть тестов из списка фазы на месте и проходят: детерминизм пула по сиду и
|
||||
неделе, частичное обновление с сохранением навыков и запроса, устойчивый размер за пятьдесят
|
||||
недель, «сильный просит больше», пустой штат при полной школе, соискатель вне ростера до найма.
|
||||
- **Фаза 12.** Фонд оплаты, деление часов между учителями предмета, пол одной ставки и покрытие
|
||||
предметов покрыты в `StaffingTests`. `GET /staffing` читает опубликованный снимок
|
||||
(`loop.FindPeople`), наём и назначение идут в воркер через `GameCommand` с
|
||||
`TaskCompletionSource` — инвариант 3 цел. Перезапуск с тем же штатом и назначениями —
|
||||
`AssignAndUnassign_HonourTheCap_AndReloadKeepsAssignments`.
|
||||
- **Фаза 13.** Строка денег, непокрытые предметы, список соискателей со ставкой и сильными
|
||||
навыками, кнопка найма с текстом ошибки, список штата с предметами и стоимостью, назначение и
|
||||
снятие с карточки — всё есть и всё через `t(...)`.
|
||||
- **Дизайн.** `design/staffing.md` сверен построчно. Особо проверено: `MaxWeeklyHours` не
|
||||
запрещает назначение, а делает предмет непокрытым (в коде нет такой ошибки, и замер это
|
||||
подтверждает); цена часа считается от навыков **и от черт**.
|
||||
|
||||
Дописано:
|
||||
|
||||
- `StaffingApiTests.AssigningPastTheCap_IsRejectedWithTheNumbers` — фаза 12 требует теста API на
|
||||
«отклонение по пределу», а его не было: единственное место, где он мог сработать, было написано
|
||||
через `if (over.StatusCode == Conflict) … else …` и проходило при любом исходе. Пределом
|
||||
занялся замер: при выделенных 100 000 полностью загруженная школа стоит всего 68–72 тысячи,
|
||||
то есть «нанять всех» предела не достигает. Но пик фонда приходится на середину раздачи
|
||||
предметов — пока часы предмета несут один-два учителя, а не двенадцать, — и там отказ
|
||||
случается: по сидам 1–30 отказов от 4 до 24 на прогон, ни одного сида без отказа. Тест
|
||||
раздаёт предметы штату, ловит первый `409`, сверяет `payroll-exceeded`, `allocated`,
|
||||
`attempted > allocated`, `remaining` и то, что отклонённое назначение действительно не легло.
|
||||
- `ApplicantPoolTests.ConfidentTraits_AskForMoreAndQuietOnesForLess` — дизайн фиксирует «цена
|
||||
часа: от навыков и от черт (самоуверенный просит больше)», код это делает через
|
||||
`TraitDef.WageAsk` (в `core` Leader +10, HotTempered +6, Quiet −8), а тестом закреплена была
|
||||
только половина про навыки.
|
||||
- Проверка в `AssignAndUnassign_HonourTheCap_AndReloadKeepsAssignments`, что после перезапуска
|
||||
возвращается **тот же пул** с теми же ставками — критерий готовности фазы 11, который никто не
|
||||
проверял. Тест кусается: пул на диске уже без нанятого, а сгенерированный заново вернул бы его
|
||||
обратно.
|
||||
|
||||
Исправлено:
|
||||
|
||||
- `docs/protocol.md` не упоминал `?lang=ru|en` у `POST staff/hire`, `POST …/subjects` и
|
||||
`DELETE …/subjects/{subject}`, хотя все три его принимают и клиент его шлёт. Поведение не
|
||||
трогалось — дописана документация.
|
||||
|
||||
Замечено рядом (за границами среза):
|
||||
|
||||
- Предел фонда при ванильных данных почти не ограничивает игрока: нанять всех двенадцать и
|
||||
раздать им все предметы стоит около 70 000 из 100 000. Отказ достижим только по дороге, на
|
||||
пике неравномерной нагрузки. Это игровой баланс, а не ошибка (см. коммит `207e7ec`, которым
|
||||
предел поднимали намеренно), но если предел задумывался как настоящее ограничение — числа
|
||||
сейчас говорят обратное.
|
||||
- В `AssignAndUnassign_HonourTheCap_AndReloadKeepsAssignments` осталась ветка `if/else` вокруг
|
||||
ожидаемого отказа. Теперь, когда отказ проверяется отдельным тестом, эта ветка просто не может
|
||||
упасть — её стоит однажды упростить, но трогать работающий тест ради этого ревью не стало.
|
||||
## Срез 4. Расписание
|
||||
|
||||
- **Фазы:** 14–17
|
||||
- **Проверен на:** `21d79cb`, 2026-08-19
|
||||
- **Пути:** `src/HSchool.Schedule`, `src/HSchool.Content` (каркас дня, каникулы, `SubjectDef.room`),
|
||||
`src/HSchool.Simulation/SchoolTimetables.cs`, `src/HSchool.Server/Api/TimetableEndpoints.cs`,
|
||||
`src/HSchool.Server/Game/SchoolWorker.cs` (пин, перестроение),
|
||||
`src/HSchool.Client/src/ui/timetableGrid.ts`, `src/HSchool.Client/src/ui/gameScreen.ts`,
|
||||
`tests/HSchool.Schedule.Tests`, `tests/HSchool.Content.Tests/CalendarTests.cs`,
|
||||
`tests/HSchool.AppHost.Tests/TimetableApiTests.cs`
|
||||
- **Итог:** дописан один тест на пять непокрытых кодов отказа, поправлены две устаревшие строки
|
||||
дизайна; расхождений кода с принятыми решениями нет
|
||||
|
||||
Что подтверждено:
|
||||
|
||||
- **Фаза 14.** Все пункты критерия закрыты в `CalendarTests`: суббота, каникулы и ночь дают «вне
|
||||
учебного дня», 10:20 буднего дня — третий урок, шестидневка делает субботу рабочей, зимние
|
||||
каникулы переносятся через 1 января. Требование помещения у предмета есть (физкультура —
|
||||
спортзал), неизвестная комната роняет каталог.
|
||||
- **Фаза 15.** Все шесть тестов из списка фазы на месте. Четыре запрета проверяются одним
|
||||
утверждением в `TeacherWithTwoSubjects_IsNeverInTwoRoomsAtOnce` (уникальность по учителю, классу
|
||||
и кабинету в слоте) плюс `ClassOfSixteen_DoesNotEnterALabOfTwelve`. Отсутствие сокетов, ECS и
|
||||
часов реального времени — самопроверкой сборки, как в `HSchool.People`.
|
||||
- **Фаза 16.** Расписание пересобирается только когда изменился состав: `TimetableDirty` ставится
|
||||
в `InstallPeople`/`Replace` и в годовом наборе, а воркер перестраивает таблицу под
|
||||
`if (result.PeopleChanged)` — по тику этого не происходит. `GET /timetable` читает
|
||||
опубликованный снимок, пин и снятие идут через `GameCommand` с `TaskCompletionSource`.
|
||||
Перезапуск с закреплениями — `Reload_RestoresLockedLessons`.
|
||||
- **Фаза 17.** Скобки в дереве (`Математика · 5Б`) собираются из присутствия, сетка «день × урок»
|
||||
с переносом, сменой кабинета и отметкой «закреплено» есть, отказ планировщика показывается
|
||||
текстом (`timetableErrorPin`, `timetableErrorNoTeacher`, `timetableErrorLesson`), личное
|
||||
расписание — в карточке. Всё через `t(...)`. Клиентские тесты и сборка на `21d79cb`: 33/33 и
|
||||
зелёная сборка.
|
||||
|
||||
Дописано:
|
||||
|
||||
- `TimetableApiTests.PinAndUnpinRefusals_CarryTheDocumentedCodes` — из шести кодов отказа пина в
|
||||
`docs/protocol.md` тестом был закрыт только `pin-rejected` (хвост
|
||||
`PinThenHireAnother_KeepsTheLockedSlot`, попытка провести урок в спортзале). Остальные пять —
|
||||
`no-teacher`, `unknown-class`, `unknown-subject`, `unknown-room`, `unknown-lesson` на повторном
|
||||
снятии, плюс `invalid-query` на неполном запросе — не проверялись, хотя клиент по ним
|
||||
переключает текст ошибки.
|
||||
|
||||
Исправлено:
|
||||
|
||||
- `design/schedule.md` держал в «Зафиксировано» две строки, которые срез 5 сознательно отменил:
|
||||
«Панель локации заполняется из расписания» и «Люди находятся по расписанию; ходьбы нет».
|
||||
Отмена записана в `design/ai.md` («присутствие — состояние человека, а не функция от
|
||||
расписания»), а старый документ об этом не знал. Помечены как отменённые со ссылкой; код не
|
||||
трогался — он давно живёт по новому решению.
|
||||
|
||||
Замечено рядом (за границами среза):
|
||||
|
||||
- Задача фазы 16 «снимок карты отдаёт по узлу, что там сейчас идёт и у кого» в протоколе v7
|
||||
выглядит иначе: снимок карты стал статическим, а занятость уехала в кадр `Presence`. Это
|
||||
сделано фазой 19 намеренно и описано в `docs/protocol.md`; строка фазы 16 просто историческая.
|
||||
- `GameSocketTests.OpeningASchoolDuringAMathLesson_...`, который в срезе 1 падал дважды под
|
||||
нагрузкой, за этот проход не воспроизвёлся ни разу: шесть полных прогонов подряд на чистом
|
||||
worktree и два одиночных под искусственной нагрузкой из шести жгущих процессов — зелено.
|
||||
Оставляю как «наблюдалось, не воспроизводится»; если вернётся, смотреть на выдачу кадров
|
||||
присутствия под нехваткой процессора, а не на логику.
|
||||
|
||||
Про окружение:
|
||||
|
||||
- Во время проверки параллельная сессия правила `core` и клиент (размер пула соискателей 12 → 32,
|
||||
новые навыки, диалог соискателей). Два прогона на рабочем дереве успели показать «падения»
|
||||
`Hire_IdlePool_FitsUnderTheCap` и `GetStaffing_EmptySchool_HasPoolAndNoPayroll` — это артефакт
|
||||
прогона старых бинарников против новых данных, а не находка. Поэтому весь срез проверялся в
|
||||
отдельном worktree на `21d79cb`: AppHost 63/63 (три прогона подряд), клиент 33/33 и сборка.
|
||||
|
||||
## Срез 5. Жизнь школы
|
||||
|
||||
- **Фазы:** 18–21
|
||||
- **Проверен на:** `21d79cb`, 2026-08-19. Фаза 21 закрыта за час до проверки, `core` правился
|
||||
параллельно — проверка велась в отдельном worktree на коммите, не на рабочем дереве
|
||||
- **Пути:** `src/HSchool.Ai`, `src/HSchool.Simulation/{PresenceSystem,ActivitySystem,LessonLearningSystem,School}.cs`,
|
||||
`src/HSchool.Protocol` (кадр `Presence`, пропуск в кадре часов),
|
||||
`src/HSchool.Server/Game/PresenceFrame.cs`, `src/HSchool.Client/src/ui/gameScreen.ts`,
|
||||
`src/HSchool.Server/mods/core/defs/{actions,behavior,needs}`, `tests/HSchool.Ai.Tests`,
|
||||
`tests/HSchool.Simulation.Tests/{PresenceTests,DecisionTests,ActivityTests,NeedDecayTests}.cs`
|
||||
- **Итог:** все 32 теста из списков четырёх фаз на месте и проходят; тестов не дописано, зато
|
||||
измерен прожитый учебный день — и он показал две вещи, которых списки не ловят
|
||||
|
||||
Что подтверждено:
|
||||
|
||||
- **Фаза 18.** Все 13 требуемых тестов есть: маршрут 201 → санузел через лестницу, совпадение
|
||||
суммы `travelMinutes` с реально пройденным временем, пустая школа в воскресенье и в каникулы,
|
||||
класс без уроков не приходит, ученик в кабинете к звонку, спортзал впритык к перемене,
|
||||
детерминизм по сиду, сохранение посреди перемены, четыре сценария пропуска пустого времени,
|
||||
школа без учителей, лето с первым сентября, промотанная неделя против прожитой.
|
||||
- **Фаза 19.** Все пять тестов: байтовая раскладка присутствия с обеих сторон, круговой прогон,
|
||||
отсутствие людей и урока в снимке карты, разрешение на пропуск в кадре часов, отброшенный
|
||||
пропуск в учебное время.
|
||||
- **Фаза 20.** Все шесть: декей голода за час, обед на заявленные минуты и величину, нехватка
|
||||
стульев, ночь вне школы, действие с неизвестной нуждой роняет каталог, детерминизм нужд.
|
||||
- **Фаза 21.** Все восемь, включая сквозной `ZeroToilet_LeavesClass_ReachesRestroom_AndReturns`,
|
||||
`DecisionCap_DefersTheOverflow` и `SameSeedAndActions_MatchAfterAWeek`, плюс табличный разбор
|
||||
решений в `DecisionPlannerTests` без мира.
|
||||
- **Инварианты.** `HSchool.Ai` не знает Arch, ASP.NET и часов реального времени, `HSchool.Simulation`
|
||||
не знает сокетов — обе проверки в самих тестах. Протокол v7 сходится в трёх местах.
|
||||
|
||||
Измерено (временный тест, удалён после):
|
||||
|
||||
Полностью укомплектованная ванильная школа (12 учителей, 225 уроков, 176 учеников), прожитая
|
||||
тиками с вторника по пятницу:
|
||||
|
||||
| | день 1 | день 2 | день 3 | день 4 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| побывали в столовой | 0 | 144 | 133 | 141 |
|
||||
| завершённых обедов | 0 | 75 | 132 | 111 |
|
||||
| минимальный голод | 0.32 | 0.00 | 0.00 | 0.00 |
|
||||
|
||||
Картина дня в остальном осмысленная: в 8:25 двор, вестибюль и лестницы полны, к 8:45 все 144
|
||||
пришедших в кабинетах и спортзале, на перемене коридор — самый людный узел школы (пик 112),
|
||||
после уроков остаются только те, у кого урок ещё идёт.
|
||||
|
||||
- **Столовая пустует весь первый день.** Голод стартует с 1.0 и падает на 0.1 в игровой час
|
||||
только на территории школы, так что порог 0.35 достигается лишь к концу первого дня. Критерий
|
||||
готовности фазы 21 обещает «перемены с коридором и столовой» — со второго дня так и есть, но
|
||||
прогон одного дня, как в критерии написано, столовой не покажет. Это не поломка, это то, чего
|
||||
стоит ожидать при проверке руками.
|
||||
- **Со второго дня голод упирается в ноль и оттуда не выходит.** К концу четырёх учебных дней
|
||||
27 учеников из 176 держат голод ≤0.05, 98 из 176 — ниже порога срочности, средний по школе
|
||||
ровно 0.35. Причина арифметическая: вне школы голод не восстанавливается (в отличие от сна),
|
||||
дома никто не ест, а единственный источник еды — восемь стульев в столовой на 176 человек при
|
||||
обеде в 15 минут. Обедов в день выходит 75–132, то есть до половины школы ежедневно остаётся
|
||||
без еды, и дефицит переносится на завтра. Поскольку рост навыка на уроке зависит от состояния
|
||||
нужд, больше половины школы учится со штрафом постоянно. Это баланс, а не логика: числа лежат
|
||||
в `core` (`decayPerHour`, `needGain`, число стульев на карте) и правятся данными. Молча их
|
||||
менять ревью не стало — см. «Открыто».
|
||||
|
||||
Открыто:
|
||||
|
||||
- **Чем закрывать голод.** Вариантов три, и это решение автора, а не ревью: восстанавливать голод
|
||||
вне школы, как сон (`restoredOffCampus` на `Hunger`); поставить в ванильную столовую больше
|
||||
стульев; или снизить `decayPerHour` голода. Первое честнее всего описывает «поел дома», третье
|
||||
дешевле всего, второе меняет карту. Числа выше дают точку отсчёта для любого из них.
|
||||
|
||||
Замечено рядом (за границами среза):
|
||||
|
||||
- **`GameSocketTests.OpeningASchoolDuringAMathLesson_...` подтверждённо нестабилен.** За сессию
|
||||
он упал четырежды — и все четыре раза в полном `dotnet test` по решению, когда параллельно шла
|
||||
сборка; в одиночку он проходит всегда, в том числе шесть прогонов подряд и два под нагрузкой из
|
||||
шести жгущих процессов. Симптом всегда один: последнее ожидание после снятия паузы — «нанятый
|
||||
учитель появился среди людей в кадре присутствия» — не выполняется за 40 кадров (~20 секунд).
|
||||
Механизм не пойман: инструментированный прогон, печатающий замороженное время и уроки этого
|
||||
учителя, три раза подряд прошёл зелёным. Две правдоподобные версии, обе про то, что тест
|
||||
зависит от игрового времени, набежавшего за время его собственных HTTP-вызовов (5 игровых минут
|
||||
на реальную секунду): либо замороженный момент уезжает за последний урок учителя и он по плану
|
||||
дня уже не приходит, либо за 20 секунд ожидания школьный день успевает кончиться. В обоих
|
||||
случаях чинить надо тест, а не присутствие: занятость узла берётся из расписания, а явка — из
|
||||
состояния, и совпадать они не обязаны — фаза 21 прямо обещает, что «опоздание и отсутствие
|
||||
видны».
|
||||
|
||||
+16
-8
@@ -77,8 +77,10 @@ single seats field on a homeroom and the old slot rows on everything else.
|
||||
Things carry `pupilSlots` — how many pupils that thing hosts. The snapshot's per-node pupil-slot
|
||||
total is computed on the server.
|
||||
|
||||
`nameSets` is the list of placeable name packs (`defName` + label). The create dialog picks one;
|
||||
it is independent of the UI language.
|
||||
`nameSets` is the list of placeable name packs (`defName` + label + `nativeLanguages`). Each
|
||||
`nativeLanguages` entry is a skill (`defName` + label). The create dialog picks a name set and a
|
||||
native language; the language list comes from that set (vanilla Slavic: Russian, Belarusian,
|
||||
Ukrainian). It is independent of the UI language.
|
||||
|
||||
`subjects` is the list of placeable subjects (`defName`, label, `gradeMin`/`gradeMax`,
|
||||
`hoursPerWeek`, `skills` with shares, and optional `room`). `room` is the RoomDef the lesson
|
||||
@@ -104,7 +106,8 @@ Body:
|
||||
"startDate": "2012-03-31T06:00:00Z",
|
||||
"modIds": [],
|
||||
"map": null,
|
||||
"nameSetId": "Slavic"
|
||||
"nameSetId": "Slavic",
|
||||
"nativeLanguage": null
|
||||
}
|
||||
```
|
||||
|
||||
@@ -112,6 +115,8 @@ Body:
|
||||
pack set's default layout. A supplied map is validated as a connected yard-and-rooms graph.
|
||||
`nameSetId` is a `NameSetDef`; omit it to use the first placeable set in the catalog (vanilla:
|
||||
`Slavic`). Unknown ids return `400` `unknown-name-set`.
|
||||
`nativeLanguage` is a skill from that set's `nativeLanguages`. Omit it (or send `null`) to pick
|
||||
one from the school seed. An id that is not in the set returns `400` `unknown-native-language`.
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
@@ -122,6 +127,7 @@ pack set's default layout. A supplied map is validated as a connected yard-and-r
|
||||
| `400` `unknown-mod` | An extra pack id is missing under `mods/`. |
|
||||
| `400` `invalid-catalog` | The selected packs could not be loaded. |
|
||||
| `400` `unknown-name-set` | `nameSetId` is not a placeable `NameSetDef` in those packs. |
|
||||
| `400` `unknown-native-language` | `nativeLanguage` is not in that name set's `nativeLanguages`. |
|
||||
| `409` `school-limit-reached` | `maxSchools` schools already exist. |
|
||||
|
||||
Failures are RFC 7807 problem details with an extra `code` field — that is what the UI switches on.
|
||||
@@ -334,8 +340,9 @@ ids that are in neither place stay `404` `unknown-person`.
|
||||
### `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.
|
||||
On success returns the same payload as `GET .../staffing`, so it takes the same `?lang=ru|en`.
|
||||
`Teacher` needs no room opening; other positions fill the first free `RoomDef.positions` slot
|
||||
of that kind.
|
||||
|
||||
| Status | `code` | When |
|
||||
| --- | --- | --- |
|
||||
@@ -351,7 +358,8 @@ other positions fill the first free `RoomDef.positions` slot of that kind.
|
||||
|
||||
### `POST /api/schools/{id}/staff/{personId}/subjects`
|
||||
|
||||
Body: `{ "subject": "Mathematics" }`. Teachers only. Same success payload as GET staffing.
|
||||
Body: `{ "subject": "Mathematics" }`. Teachers only. Same success payload as GET staffing,
|
||||
and the same `?lang=ru|en`.
|
||||
|
||||
| Status | `code` | When |
|
||||
| --- | --- | --- |
|
||||
@@ -363,8 +371,8 @@ Body: `{ "subject": "Mathematics" }`. Teachers only. Same success payload as GET
|
||||
|
||||
### `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`.
|
||||
Removes one assignment. Payroll drops when the subject was not the only one. Same success
|
||||
payload and `?lang=ru|en` as the other two. Unknown assignment is `404` `unknown-assignment`.
|
||||
|
||||
### `GET /api/schools/{id}/timetable`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user