From c51ca2e247652934b44dbde41f1878f9e4b0777b Mon Sep 17 00:00:00 2001 From: lizerk Date: Sat, 20 Jun 2026 10:47:10 +0300 Subject: [PATCH] docs: add guest system requirements --- backend/guests/тз.txt | 740 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 740 insertions(+) create mode 100644 backend/guests/тз.txt diff --git a/backend/guests/тз.txt b/backend/guests/тз.txt new file mode 100644 index 00000000..078e659d --- /dev/null +++ b/backend/guests/тз.txt @@ -0,0 +1,740 @@ +# Техническое задание: гостевая система + +Дата подготовки: 2026-06-20 +Проект: EUSRR +Область: backend Django/DRF, frontend Next.js, LDAP/Active Directory, документы, уведомления. + +## 1. Цель + +Разработать модуль гостевых визитов, который позволяет сотрудникам создавать заявки на приглашение внешних гостей, администраторам принимать и менять решения по этим заявкам, а системе автоматически создавать и деактивировать гостевые учетные записи в LDAP/Active Directory. + +Гость не является сотрудником и не должен получать доступ к веб-системе EUSRR. LDAP-учетка гостя нужна для внешних интеграций и сервисов, которые используют AD/LDAP как каталог пользователей. + +## 2. Контекст проекта + +В проекте уже есть: + +- backend на Django/DRF; +- пользовательская модель `employees.Employee`; +- LDAP-интеграция в `employees/ldap` с `LdapSyncState` и `LdapSyncQueue`; +- универсальные комментарии через `communications.comments_helpers`; +- универсальные уведомления через `notifications.signals.notify`; +- документный модуль `documents.Document` на django-filer; +- модуль заявлений `requests_app` с простыми статусами и комментариями; +- модуль закупок `procurement` с action-процессом `submit/approve/reject`; +- frontend на Next.js с доменными страницами `/requests`, `/procurement`, `/documents`. + +Гостевую систему нужно делать отдельным доменным приложением `guests`, не расширяя `requests_app.Request`, потому что у гостевых визитов другая предметная область, другой жизненный цикл, отдельная LDAP-синхронизация и запрет логина в систему. + +## 3. Термины + +- Гость: внешний человек, для которого может быть создана LDAP-учетка в отдельном OU `Guests`. +- Гостевой визит: заявка сотрудника на создание или продление доступа гостя. +- Приглашающий: сотрудник, который создал заявку и несет ответственность за гостя. +- Администратор гостевых визитов: пользователь, который может рассматривать заявки и управлять гостевыми учетками. +- Запрос дополнительной информации: действие администратора, после которого заявитель должен дать развернутый комментарий или изменить данные заявки. +- Действие доступа: фактический период, когда гостевая LDAP-учетка должна быть активна. + +## 4. Границы MVP + +В MVP нужно реализовать: + +- создание гостя и гостевого визита из одной формы; +- повторное использование уже существующего гостя при новом визите; +- черновик и отправку заявки на рассмотрение; +- одобрение, отказ, запрос дополнительной информации; +- возможность администратору менять решение после одобрения или отказа; +- комментарии по визиту через существующую систему comments; +- привязку паспортных/подтверждающих файлов через модуль документов; +- создание/обновление/деактивацию гостевой учетной записи в LDAP; +- автоматическую деактивацию доступа после окончания срока визита; +- уведомления участникам процесса; +- список и карточку визита на frontend. + +Не входит в MVP: + +- самостоятельный вход гостя в EUSRR; +- личный кабинет гостя; +- выдача пароля гостю через EUSRR; +- многоэтапное согласование, как в закупках; +- интеграция с физической СКУД, если она не работает напрямую через LDAP; +- электронная подпись документов. + +## 5. Роли и права + +### 5.1. Обычный сотрудник + +Может: + +- создать гостевой визит от своего имени; +- сохранить визит как черновик; +- отправить визит на рассмотрение; +- просматривать свои визиты; +- редактировать свои визиты в статусах `draft` и `needs_info`; +- добавлять комментарии к своим визитам; +- прикреплять документы к своим визитам; +- отменить свой визит до фактического завершения доступа. + +Не может: + +- принимать решение по визиту; +- менять LDAP-статус гостя вручную; +- редактировать чужие визиты; +- создавать гостя без визита; +- удалять аудит, решения, комментарии и LDAP-следы. + +### 5.2. Администратор гостевых визитов + +Администратор определяется одним из условий: + +- `is_staff` или `is_superuser`; +- модельное право `guests.view_all_guestvisit`; +- модельное право `guests.decide_guestvisit`; +- модельное право `guests.manage_guestaccount`. + +Может: + +- видеть все гостевые визиты; +- принимать решение: одобрить или отказать; +- запросить дополнительную информацию; +- изменить ранее принятое решение; +- редактировать данные гостя и визита при необходимости; +- вручную запустить LDAP-синхронизацию по визиту или гостю; +- вручную деактивировать гостевую учетку; +- просматривать журнал решений и LDAP-событий. + +### 5.3. Системные задачи + +Могут: + +- создать или обновить LDAP-учетку гостя после одобрения; +- деактивировать гостя по окончании срока визита; +- повторять LDAP-операции через очередь при ошибках; +- отправлять системные уведомления об ошибках LDAP и истечении доступа. + +## 6. Жизненный цикл гостевого визита + +### 6.1. Статусы визита + +Нужно завести отдельный enum `GuestVisitStatus`: + +- `draft`: черновик, виден только автору и администраторам; +- `pending`: на рассмотрении; +- `needs_info`: требуется дополнительная информация от заявителя; +- `approved`: одобрено, доступ должен быть создан или активирован; +- `rejected`: отказано, доступ не должен быть активен; +- `cancelled`: отменено заявителем или администратором; +- `expired`: срок визита истек, доступ деактивирован; +- `revoked`: доступ досрочно отозван администратором. + +Статусы `approved` и `rejected` не являются окончательно заблокированными: администратор должен иметь возможность поменять решение после получения новой информации. + +### 6.2. Переходы статусов + +Разрешенные переходы: + +- `draft -> pending`: заявитель отправил на рассмотрение; +- `pending -> approved`: администратор одобрил; +- `pending -> rejected`: администратор отказал; +- `pending -> needs_info`: администратор запросил информацию; +- `needs_info -> pending`: заявитель ответил и повторно отправил; +- `needs_info -> cancelled`: заявитель отменил; +- `approved -> rejected`: администратор изменил решение, гостевой доступ деактивируется; +- `rejected -> approved`: администратор изменил решение, гостевой доступ создается/активируется; +- `approved -> revoked`: администратор досрочно отозвал доступ; +- `approved -> expired`: системная задача закрыла доступ после срока; +- `draft|pending|needs_info -> cancelled`: заявитель или администратор отменил; +- `cancelled -> pending`: только администратор может вернуть в работу, если это нужно; +- `expired -> approved`: только администратор при продлении или исправлении срока. + +Каждый переход, кроме технического повторного сохранения без изменения статуса, должен писать запись в историю решений/событий. + +### 6.3. Запрос дополнительной информации + +Администратор нажимает "Требуется больше информации" и обязан указать комментарий. + +После этого: + +- статус становится `needs_info`; +- заявитель получает уведомление; +- заявитель видит, что именно нужно уточнить; +- заявитель может изменить поля заявки и/или добавить комментарий; +- после ответа заявитель нажимает "Отправить повторно"; +- статус возвращается в `pending`; +- администраторы получают уведомление о повторной отправке. + +## 7. Данные гостя + +Нужно завести модель `Guest`. + +Обязательные поля: + +- `last_name`: фамилия; +- `first_name`: имя; +- `patronymic`: отчество, опционально; +- `birth_date`: дата рождения, опционально, но рекомендуется; +- `phone`: телефон, опционально; +- `email`: email, опционально; +- `organization`: организация/компания гостя, опционально; +- `position`: должность гостя, опционально; +- `comment`: служебный комментарий, опционально; +- `created_by`: кто впервые создал гостя; +- `created_at`, `updated_at`. + +LDAP-поля и состояние: + +- `is_active`: активен ли гость в бизнес-смысле; +- `ldap_enabled`: должна ли LDAP-учетка быть включена сейчас; +- `ldap_username`: кэш `sAMAccountName`; +- `ldap_upn`: кэш `userPrincipalName`; +- `ldap_last_synced_at`; +- `ldap_last_error`. + +Идентификация гостя: + +- в MVP не хранить паспортные номера отдельными полями, если это не требуется внешней интеграцией; +- паспортные сканы и персональные документы хранить через `documents.Document`; +- если позднее потребуется хранить паспортные реквизиты, добавить отдельную модель с ограниченными правами и аудитом. + +Дедупликация: + +- при создании нового гостя backend должен пытаться найти существующего по `email`, `phone`, а при отсутствии контактов по ФИО и дате рождения; +- если найден возможный дубль, API должен вернуть предупреждение или список кандидатов; +- администратор может объединять или выбирать существующего гостя в форме визита. + +## 8. Данные гостевого визита + +Нужно завести модель `GuestVisit`. + +Обязательные поля: + +- `guest`: ссылка на `Guest`; +- `inviter`: сотрудник-приглашающий, `employees.Employee`; +- `purpose`: цель приглашения; +- `status`: статус визита; +- `access_starts_at`: дата/время начала доступа, nullable для бессрочного доступа; +- `access_expires_at`: дата/время окончания доступа, nullable для бессрочного доступа; +- `all_day`: флаг, что время не указано и период считается полными сутками; +- `unlimited`: флаг бессрочного доступа; +- `submitted_at`; +- `decided_by`; +- `decided_at`; +- `decision_comment`; +- `created_at`, `updated_at`. + +Дополнительные поля: + +- `visit_comment`: комментарий заявителя; +- `admin_comment`: внутренний комментарий администратора; +- `host_department`: отдел приглашающего на момент создания, nullable; +- `cancelled_at`, `cancelled_by`, `cancel_reason`; +- `revoked_at`, `revoked_by`, `revoke_reason`; +- `expired_at`. + +Связи: + +- `documents`: ManyToMany к `documents.Document` для паспорта и иных вложений; +- `history`: отдельная модель истории решений и событий; +- `ldap_events`: отдельная модель или записи в общей истории для LDAP-событий. + +Правило связи с приглашающим: + +- `inviter` должен быть `on_delete=PROTECT` или `SET_NULL` с обязательным сохранением snapshot-полей; +- нужно хранить снимок приглашающего на момент создания: ФИО, email, id отдела; +- если сотрудник уволен, деактивирован или удален, это должно отображаться в карточке визита и списке гостей; +- при увольнении приглашающего активные гостевые доступы должны попадать в список риска для администратора; +- автоматическая деактивация всех гостей уволенного приглашающего включается только если бизнес подтвердит это правило. До подтверждения требуется уведомлять администраторов и помечать визиты флагом `inviter_inactive`. + +## 9. Правила дат и времени + +### 9.1. Полные сутки + +Если пользователь указал только даты без времени, визит действует полные календарные сутки в timezone проекта `Europe/Moscow`. + +Пример: + +- дата начала: 2026-06-15; +- дата окончания: 2026-06-18; +- фактическое начало доступа: `2026-06-15 00:00:00 Europe/Moscow`; +- фактическое окончание доступа: `2026-06-19 00:00:00 Europe/Moscow`. + +То есть доступ закрывается в 00:00 следующего дня после указанной даты окончания. + +### 9.2. Даты с временем + +Если указано время, используются точные `access_starts_at` и `access_expires_at`. + +Требования: + +- окончание должно быть строго позже начала; +- нельзя создать визит с окончанием в прошлом, кроме администраторского исправления; +- timezone хранится в БД как aware datetime; +- frontend показывает даты в локальном формате пользователя, но API принимает ISO-8601. + +### 9.3. Бессрочный доступ + +Если `unlimited=true`: + +- `access_expires_at` может быть null; +- статус не переводится в `expired` автоматически; +- LDAP-учетка остается активной до ручного отзыва, отказа, отмены или деактивации гостя; +- такие записи должны быть явно выделены в списке администраторов. + +## 10. LDAP/Active Directory + +### 10.1. Основное требование + +Гостевые учетные записи должны создаваться в отдельном контейнере AD/LDAP, отличном от сотрудников. + +Нужно добавить настройку: + +- `LDAP_GUESTS_BASE`, например `OU=Guests,OU=company,DC=robotail,DC=local`. + +Нельзя создавать для гостя `employees.Employee`. Иначе гость сможет попасть в существующую логику аутентификации, прав, сотрудников и отделов. + +### 10.2. Модель синхронизации + +Использовать существующий подход: + +- `LdapSyncState(model="guest", object_pk=str(guest.pk))`; +- `LdapSyncQueue` для retry LDAP-операций; +- отдельные операции очереди: `guest_save`, `guest_disable`, `guest_delete` или `guest_sync_access`; +- отдельный сервис `guests.ldap.services.GuestLdapService`. + +### 10.3. Создание LDAP-учетки + +Создание выполняется после одобрения визита. + +Алгоритм: + +1. Проверить, что визит в статусе `approved`. +2. Проверить, что текущая дата попадает в период доступа или доступ должен быть подготовлен заранее. +3. Найти существующий LDAP DN гостя через `LdapSyncState`. +4. Если DN нет, создать LDAP `user` в `LDAP_GUESTS_BASE`. +5. Записать атрибут связи, например `employeeNumber` или отдельный настраиваемый атрибут со значением `guest:`. +6. Установить `sAMAccountName`, `userPrincipalName`, `displayName`, `givenName`, `sn`, `mail`, `telephoneNumber`, `description`. +7. Установить состояние учетной записи: + - enabled, если доступ уже активен; + - disabled, если визит одобрен заранее, но дата начала еще не наступила. +8. Обновить `LdapSyncState` и поля кэша в `Guest`. + +### 10.4. Запрет входа гостя в EUSRR + +Нужно обеспечить несколько уровней защиты: + +- не создавать `Employee` для гостя; +- `LDAP_GUESTS_BASE` не должен попадать в `LDAP_USER_BASE` для auth-поиска; +- если auth search base технически широкий, добавить фильтр, исключающий OU гостей; +- не выдавать гостю Django permissions/groups; +- не создавать JWT/session для гостя; +- не использовать гостевой DN в employee sync/import. + +### 10.5. Деактивация LDAP-учетки + +LDAP-учетка гостя должна быть disabled, если: + +- визит отклонен; +- визит отменен; +- доступ отозван; +- срок визита истек; +- гость вручную деактивирован администратором; +- все активные/будущие одобренные визиты гостя отсутствуют. + +Если у гостя несколько одобренных визитов, деактивировать LDAP-учетку можно только когда нет ни одного актуального визита, требующего активного доступа. + +### 10.6. Планировщик + +Нужны Celery-задачи: + +- `guests.tasks.activate_due_guest_visits`: включает учетки, у которых наступило начало доступа; +- `guests.tasks.expire_guest_visits`: переводит истекшие визиты в `expired` и выключает учетки; +- `guests.tasks.process_guest_ldap_queue` или расширение существующей `employees.tasks.process_ldap_queue`; +- периодичность: минимум раз в минуту, аналогично существующей LDAP retry-задаче. + +## 11. Документы и паспортные файлы + +Паспортные и подтверждающие файлы нужно хранить через существующий модуль документов. + +Требования: + +- у `GuestVisit` должна быть связь ManyToMany `documents.Document`; +- при загрузке из формы визита backend создает `Document` с `uploaded_by=inviter`; +- документ должен быть приватным; +- документ должен быть доступен автору визита и администраторам гостевой системы; +- рекомендуется использовать тег `guest-passport` или тип документа `guest_passport`; +- в карточке визита показывать список документов, ссылку на просмотр/скачивание и статус наличия обязательных документов; +- удаление документа не должно удалять визит, но должно записывать событие в аудит. + +Для MVP паспортный документ сделать опциональным или обязательным через настройку `GUESTS_REQUIRE_ID_DOCUMENT`. Если настройка включена, отправка визита на рассмотрение без документа запрещена. + +## 12. История, аудит и комментарии + +### 12.1. История событий + +Нужно завести модель `GuestVisitEvent`. + +Поля: + +- `visit`; +- `actor`: сотрудник или null для системной задачи; +- `event_type`: тип события; +- `from_status`; +- `to_status`; +- `comment`; +- `metadata`; +- `created_at`. + +Типы событий: + +- `created`; +- `submitted`; +- `needs_info_requested`; +- `info_provided`; +- `approved`; +- `rejected`; +- `decision_changed`; +- `cancelled`; +- `revoked`; +- `expired`; +- `ldap_created`; +- `ldap_updated`; +- `ldap_enabled`; +- `ldap_disabled`; +- `ldap_failed`; +- `document_attached`; +- `document_removed`; +- `inviter_inactive_detected`. + +### 12.2. Комментарии + +Комментарии реализовать через `communications.comments_helpers`. + +Правила: + +- автор визита и администраторы могут читать и писать комментарии; +- комментарий администратора при `needs_info` обязателен; +- комментарий заявителя при повторной отправке из `needs_info` обязателен; +- комментарии не заменяют историю решений, но могут быть связаны через `system_metadata`. + +## 13. Уведомления + +Использовать `notifications.signals.notify`. + +Нужны verbs: + +- `guest_visit_submitted`; +- `guest_visit_needs_info`; +- `guest_visit_info_provided`; +- `guest_visit_approved`; +- `guest_visit_rejected`; +- `guest_visit_decision_changed`; +- `guest_visit_cancelled`; +- `guest_visit_revoked`; +- `guest_visit_expired`; +- `guest_ldap_failed`; +- `guest_inviter_inactive`. + +Получатели: + +- при отправке на рассмотрение: все администраторы гостевой системы; +- при `needs_info`: заявитель; +- при ответе заявителя: администраторы; +- при одобрении/отказе/смене решения: заявитель; +- при LDAP-ошибке: администраторы; +- при истечении доступа: заявитель и администраторы, если это требуется настройкой; +- при увольнении/деактивации приглашающего: администраторы. + +`action_url` должен вести на frontend-страницу `/guests?visit=`. + +## 14. Backend API + +Добавить `api/v1/guests`. + +### 14.1. Роуты + +Через DRF router: + +- `GET /api/v1/guests/visits/`: список визитов; +- `POST /api/v1/guests/visits/`: создать визит; +- `GET /api/v1/guests/visits/{id}/`: карточка визита; +- `PATCH /api/v1/guests/visits/{id}/`: изменить визит; +- `DELETE /api/v1/guests/visits/{id}/`: удалить только черновик или выполнить soft-cancel; +- `POST /api/v1/guests/visits/{id}/submit/`; +- `POST /api/v1/guests/visits/{id}/approve/`; +- `POST /api/v1/guests/visits/{id}/reject/`; +- `POST /api/v1/guests/visits/{id}/request-info/`; +- `POST /api/v1/guests/visits/{id}/provide-info/`; +- `POST /api/v1/guests/visits/{id}/cancel/`; +- `POST /api/v1/guests/visits/{id}/revoke/`; +- `POST /api/v1/guests/visits/{id}/sync-ldap/`; +- `GET /api/v1/guests/visits/{id}/comments/`; +- `POST /api/v1/guests/visits/{id}/comments/`; +- `DELETE /api/v1/guests/visits/{id}/comments/{comment_id}/`; +- `POST /api/v1/guests/visits/{id}/documents/`; +- `DELETE /api/v1/guests/visits/{id}/documents/{document_id}/`. + +Дополнительно: + +- `GET /api/v1/guests/guests/`: список гостей; +- `GET /api/v1/guests/guests/{id}/`: карточка гостя; +- `PATCH /api/v1/guests/guests/{id}/`: редактирование гостя администратором; +- `POST /api/v1/guests/guests/{id}/deactivate/`; +- `POST /api/v1/guests/guests/{id}/sync-ldap/`; +- `GET /api/v1/guests/guests/search/?q=...`: поиск/дедупликация гостей. + +### 14.2. Фильтры списка визитов + +Поддержать query params: + +- `scope=mine|all|pending_decision|active|expired|risk`; +- `status`; +- `guest_id`; +- `inviter_id`; +- `created_from`, `created_to`; +- `access_from`, `access_to`; +- `unlimited=true|false`; +- `ldap_enabled=true|false`; +- `q`: поиск по ФИО гостя, организации, цели, email/phone; +- `ordering=-created_at|created_at|access_starts_at|-access_starts_at|access_expires_at|-access_expires_at`. + +Обычный пользователь при `scope=all` все равно видит только свои визиты. + +### 14.3. Сериализаторы + +Read serializer визита должен возвращать: + +- данные визита; +- краткие данные гостя; +- краткие данные приглашающего; +- статус и display label; +- вычисляемые поля `can_edit`, `can_submit`, `can_decide`, `can_request_info`, `can_cancel`, `can_revoke`, `can_sync_ldap`; +- `is_active_now`; +- `is_expired`; +- `ldap_state`; +- `documents`; +- `comments_count`; +- последний event. + +Write serializer должен принимать: + +- вложенные данные гостя или `guest_id`; +- `purpose`; +- дату/время или даты без времени; +- `all_day`; +- `unlimited`; +- `visit_comment`; +- `document_ids`; +- файлы для создания документов, если реализуется multipart. + +### 14.4. Валидация API + +Обязательные проверки: + +- авторизованность пользователя; +- наличие гостя или данных для создания гостя; +- цель приглашения не пустая; +- если `unlimited=false`, должен быть задан период доступа; +- дата окончания позже даты начала; +- при `all_day=true` входные даты нормализуются в границы суток; +- нельзя отправить черновик без обязательных документов, если включена настройка; +- обычный пользователь не может подменить `inviter`, `status`, `decided_by`; +- обычный пользователь не может менять одобренный визит напрямую; +- администратор при `approve/reject/request-info/revoke` должен передать комментарий, если это требуется настройкой. + +## 15. Frontend + +Добавить страницу `/guests`. + +### 15.1. Список визитов + +Список должен поддерживать: + +- вкладки или сегментированный фильтр: "Мои", "На рассмотрении", "Активные", "Истекшие", "Риски", "Все" для админов; +- поиск по гостю, организации и цели; +- фильтры по статусу, периоду доступа, бессрочности, LDAP-статусу; +- сортировку; +- пагинацию или бесконечную загрузку; +- быстрые действия для админа: одобрить, отказать, запросить информацию; +- быстрые действия для автора: редактировать черновик/needs_info, отменить. + +### 15.2. Форма создания/редактирования + +Поля: + +- гость: выбор существующего или создание нового; +- фамилия, имя, отчество; +- дата рождения; +- телефон; +- email; +- организация; +- должность; +- цель приглашения; +- режим периода: "полные сутки", "точное время", "бессрочно"; +- дата начала; +- дата окончания; +- время начала/окончания при точном режиме; +- документы паспорта/подтверждения; +- комментарий заявителя. + +Поведение: + +- при вводе ФИО/email/phone искать возможные дубли; +- показывать предупреждение, если найден похожий гость; +- кнопки: "Сохранить черновик", "Отправить на рассмотрение", "Отмена"; +- при `needs_info` показывать комментарий администратора и поле ответа. + +### 15.3. Карточка визита + +Показывать: + +- статус; +- гостя и его организацию; +- приглашающего и его текущий кадровый статус; +- период доступа; +- LDAP-состояние; +- документы; +- комментарии; +- историю событий; +- действия, доступные текущему пользователю. + +Для админа: + +- "Одобрить"; +- "Отказать"; +- "Требуется больше информации"; +- "Изменить решение"; +- "Отозвать доступ"; +- "Синхронизировать LDAP"; +- "Деактивировать гостя". + +## 16. Admin UI + +В Django admin добавить: + +- `GuestAdmin`; +- `GuestVisitAdmin`; +- `GuestVisitEventAdmin`. + +Требования: + +- фильтры по статусу, LDAP-статусу, бессрочности, дате доступа; +- поиск по ФИО гостя, email, phone, organization, inviter email; +- read-only поля аудита и LDAP state; +- inline для визитов гостя; +- inline или отдельная вкладка событий. + +## 17. Поиск + +Если используется общий search/watson: + +- индексировать `Guest` по ФИО, email, phone, organization; +- индексировать `GuestVisit` по purpose и snapshot приглашающего; +- в глобальном поиске показывать гостевые записи только пользователям с правом просмотра гостей. + +## 18. Безопасность и персональные данные + +Требования: + +- паспортные документы приватные; +- доступ к документам только автору визита и администраторам; +- не отдавать паспортные файлы в списке, только в карточке с проверкой прав; +- все решения и LDAP-операции логировать; +- не логировать содержимое паспортных документов; +- не логировать пароли LDAP; +- гостевые записи не должны попадать в Employee API; +- гостевые LDAP-учетки не должны использоваться для входа в EUSRR. + +Рекомендуется: + +- добавить настройку срока хранения истекших гостевых документов; +- добавить management command для отчета по бессрочным гостям; +- добавить флаг согласия/основания обработки персональных данных, если это требуется внутренними правилами. + +## 19. Миграции и настройки + +Нужно добавить: + +- Django app `guests`; +- модели `Guest`, `GuestVisit`, `GuestVisitEvent`; +- настройки: + - `LDAP_GUESTS_BASE`; + - `GUESTS_REQUIRE_ID_DOCUMENT`; + - `GUESTS_NOTIFY_ON_EXPIRATION`; + - `GUESTS_AUTO_DISABLE_ON_INVITER_INACTIVE`; + - `GUESTS_LDAP_LINK_ATTR`; +- регистрацию app в `INSTALLED_APPS`; +- регистрацию URL в `api/v1/urls.py`; +- периодические Celery-задачи в `CELERY_BEAT_SCHEDULE`; +- permissions в `Meta.permissions`. + +## 20. Тесты + +### 20.1. Backend tests + +Покрыть: + +- создание черновика; +- отправку на рассмотрение; +- валидацию дат для полного дня; +- валидацию точного времени; +- бессрочный доступ; +- запрет подмены автора обычным пользователем; +- права просмотра своих и чужих визитов; +- approve/reject/request-info/provide-info; +- смену решения `rejected -> approved` и `approved -> rejected`; +- создание event history; +- комментарии; +- привязку документов; +- dedup поиска гостя; +- истечение визита через Celery task; +- деактивацию LDAP при истечении; +- отсутствие создания `Employee` для гостя; +- запрет попадания гостя в employee import/auth; +- очередь LDAP retry при ошибке; +- уведомления получателям. + +### 20.2. Frontend tests + +Покрыть: + +- отображение списка; +- создание визита; +- переключение режима периода; +- обязательность документов при включенной настройке; +- отображение `needs_info`; +- действия администратора; +- отображение LDAP-ошибки; +- отсутствие кнопок без прав. + +## 21. Acceptance criteria + +Функциональность считается готовой, если: + +- сотрудник может создать гостевой визит и отправить его на рассмотрение; +- администратор видит заявку и может одобрить, отказать или запросить информацию; +- заявитель получает уведомление о запросе информации и может ответить; +- администратор может изменить отказ на одобрение и одобрение на отказ; +- после одобрения создается или активируется LDAP-учетка гостя в `LDAP_GUESTS_BASE`; +- гость не может войти в EUSRR как сотрудник; +- после окончания срока доступ автоматически деактивируется; +- бессрочный доступ не истекает автоматически; +- документы паспорта прикрепляются через модуль документов и защищены правами; +- история решений и LDAP-событий видна в карточке; +- все ключевые действия отправляют уведомления; +- тесты backend проходят; +- frontend страница `/guests` работает в desktop и mobile layout без наложений текста и элементов. + +## 22. Открытые вопросы + +Перед реализацией нужно подтвердить: + +1. Должен ли паспортный документ быть обязательным всегда или только для отдельных типов гостей? +2. Какие LDAP-атрибуты обязаны заполняться для внешних интеграций? +3. Нужно ли выдавать гостю пароль или внешние сервисы используют только сам факт наличия LDAP-учетки? +4. Нужно ли автоматически деактивировать гостей при увольнении приглашающего или только уведомлять администраторов? +5. Нужны ли разные категории гостей и разные сроки доступа по категориям? +6. Должен ли один гость иметь несколько параллельных визитов от разных сотрудников? +7. Нужна ли интеграция с физической СКУД или достаточно LDAP? +8. Какой срок хранения паспортных документов после истечения визита? +9. Нужно ли согласование руководителем отдела приглашающего или только администраторами? +10. Должен ли администратор указывать обязательную причину при отказе, отзыве и смене решения?