Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions docs/application-team-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
- `Team` — команда, собранная только для этой заявки;
- `TeamMember` — членство пользователя в команде заявки.

Публичного Team API пока нет. Транзакционный Application/Team service создает
командный draft вместе с Team и accepted-капитаном и проверяет полный invariant
перед submit. Состав команды через публичный API пока не редактируется.
Транзакционный Application/Team service создает командный draft вместе с Team и
accepted-капитаном и проверяет полный invariant перед submit. Публичный Team API
дает ролевой read-доступ, rename/leave/remove/transfer, но намеренно не добавляет
нового участника без будущего TeamInvite.

## Participation mode

Expand All @@ -22,8 +23,8 @@
| `individual` | Индивидуальная заявка |
| `team` | Командная заявка |

Runtime default временно равен `individual`: существующие API и frontend не
передают новое поле, поэтому все текущие и исторические Application продолжают
Runtime default временно равен `individual`: API принимает поле, но старые
клиенты его не передают, поэтому текущие и исторические Application продолжают
работать как индивидуальные. `undecided` должен стать default одновременно с
wizard и запретом submit заявки без выбранного формата.

Expand All @@ -46,8 +47,8 @@ Team относится к Application, потому что состав ком
переиспользуется как TeamMember.

В первом MVP `Team.captain` обязан совпадать с `Application.user`.
`Team.status`, invite code, token и настройки размера команды не добавлены:
редактируемость будущего Team flow должна выводиться из `Application.status`.
`Team.status`, invite code и token не добавлены. Размеры хранятся в Program
policy, а редактируемость Team выводится из `Application.status`.

## TeamMember

Expand Down Expand Up @@ -118,15 +119,14 @@ Domain service выполняет последовательность атом

Клиенты используют существующий Application create endpoint с
`participation_mode=team` и необязательным `team_name`. При ошибке service
откатывает все три шага. Публичных Team serializers/views/URLs и управления
участниками по-прежнему нет.
откатывает все три шага. После создания детали доступны через
`GET/PATCH /applications/<id>/team/`; остальные операции описаны в
`docs/team-permissions-api.md`.

## Вне текущего MVP

Следующие PR должны добавить:

- Team permissions и публичный Team API;
- блокировку состава по Application status;
- TeamInvite, accept/decline/revoke/expire;
- email и внутренние уведомления;
- frontend wizard и вкладку команды.
Expand Down
28 changes: 18 additions & 10 deletions docs/application-team-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Service предоставляет четыре публичные операц
- `validate_team_invariants()`;
- `submit_application()`.

Управление уже созданной Team вынесено в соседний
`partner_programs.services.team_management`: `rename_team()`, `leave_team()`,
`remove_team_member()` и `transfer_team_captain()`. Оба service используют одну
проверку cross-table конфликта под блокировкой Program.

## Доменные ошибки

Все ожидаемые отказы наследуются от `ApplicationTeamServiceError` и содержат
Expand Down Expand Up @@ -48,8 +53,8 @@ frontend сохраняет прежний формат.

## Смена формата

`change_application_participation_mode()` доступен только владельцу draft до
application deadline:
`change_application_participation_mode()` доступен владельцу или staff для
draft до application deadline:

- `undecided → individual` меняет только поле;
- `undecided/individual → team` создает Team и капитана;
Expand Down Expand Up @@ -80,9 +85,9 @@ PATCH Application вызывает эту операцию до сохранен
partial unique constraint дополнительно защищает две собственные активные
Application.

Cross-table invariant нельзя выразить обычным UniqueConstraint. Прямые записи
через admin/model и будущий Team API должны использовать тот же service или
отдельную транзакционную операцию принятия участника.
Cross-table invariant нельзя выразить обычным UniqueConstraint. Team transfer
использует ту же проверку под общей блокировкой Program; прямые записи через
admin/model и будущий accept TeamInvite также обязаны проходить domain service.

## Полный Team invariant

Expand Down Expand Up @@ -112,16 +117,18 @@ Cross-table invariant нельзя выразить обычным UniqueConstra

## API contract

Существующие routes и throttle scope не изменены:
Application routes сохраняют прежние поля и действия:

- `POST /programs/<id>/applications/` принимает необязательные
`participation_mode` и write-only `team_name`;
- отсутствие mode означает `individual`;
- response дополнен `participation_mode`, но не раскрывает TeamMember;
- response содержит `participation_mode` и компактный read-only `team` summary,
но не раскрывает полный список TeamMember;
- `PATCH /applications/<id>/` проводит mode/team name через service;
- `POST /applications/<id>/submit/` вызывает транзакционный submit service.

Публичного Team CRUD, управления участниками и TeamInvite в этом изменении нет.
Публичный Team API отдельно описан в `docs/team-permissions-api.md`. Прямого
добавления участника и TeamInvite по-прежнему нет.

## Ограничения concurrency и MVP

Expand All @@ -130,5 +137,6 @@ SQLite не реализует полноценный `select_for_update`, по
тест проверяет устойчивую последовательную конфликтную операцию без threading.

Service пока не управляет приглашениями/принятием участников и не блокирует
прямое редактирование моделей через admin. Нет Team permissions для обычных
members, передачи капитанства, returned Application и organizer review.
прямое редактирование моделей через admin. Team permissions, read access и
передача капитанства реализованы отдельным service/API; returned Application и
organizer review отсутствуют.
Loading
Loading