docs: add guest system requirements
This commit is contained in:
parent
32500e3037
commit
c51ca2e247
740
backend/guests/тз.txt
Normal file
740
backend/guests/тз.txt
Normal file
@ -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:<id>`.
|
||||
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=<id>`.
|
||||
|
||||
## 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. Должен ли администратор указывать обязательную причину при отказе, отзыве и смене решения?
|
||||
Loading…
x
Reference in New Issue
Block a user