Вынесены can_* для кассы и посещаемости, синхронизация в builders/signals, seed групп в core.0023–0024.
Co-authored-by: Cursor <cursoragent@cursor.com>
Убрана пустая «графік»-вкладка; Mattermost/AD сгруппированы с подсказкой
про ручную привязку; группы через filter_horizontal.
Co-authored-by: Cursor <cursoragent@cursor.com>
Management-команда convert_usernames приводит логины существующих
сотрудников к новому формату <буква имени>.<фамилия> (та же логика
_username_base, что при найме).
- dry-run по умолчанию, запись только с --apply
- суперпользователи пропускаются (не переименовать свой админ-аккаунт),
переопределяется через --include-superusers
- --exclude a.user,mail@dom — исключить по логину/email
- уникальность: коллизии получают суффикс; исключённые и уже корректные
логины резервируются первыми
- tests: 8 (dry-run, apply, коллизии, skip/ include суперов, exclude, резерв)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Админ-страница «Ссылки на установку пароля» (Сотрудники → действия):
таблица всех активных сотрудников со статусом (пароль установлен / MM
привязан / активная ссылка), кнопки «создать всем без пароля», создать/
обновить/отозвать по одному, экспорт CSV. Ссылки одноразовые, TTL 30 дней.
При активации по ссылке, помимо пароля портала, теперь синхронно ставится
тот же пароль в Mattermost: аккаунт связывается по email/ID или создаётся
(первичная связь). Ошибка MM не отменяет установку пароля портала.
- services/password_invite: bulk_create_invites_for_pending,
active_invites_by_employee, revoke_active_invites (BULK_TTL_DAYS=30)
- mattermost sync_service: link_and_set_password (ok|disabled|error)
- views_password: MM-синк после commit (вне транзакции, best-effort)
- EmployeeAdmin: action password-invites + шаблон
- tests: 6 новых (bulk, revoke, MM-линк ok/disabled, активация, admin+CSV)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Тесты больше не хардкодят /admin/ — путь берётся из reverse (admin:index /
именованный admin-URL), поэтому переименование префикса админки их не ломает.
Плюс подчинённому в EmployeeDetailTests задан email, не содержащий логин —
иначе проверка «логин скрыт от руководителя» ложно срабатывала на подстроку.
- apps/employees/tests: reverse('admin:employees_employee_change')
- core/tests_user_groups: matrix_url через reverse('admin:index')
- core/tests: distinct email у подчинённого
Полный прогон: 560 тестов, 0 провалов.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
На карточке Employee — вкладка «Доступ (сводка)»: по каждому
ограниченному разделу портала показывает итог (есть/нет) и источник
(какая группа дала право, individual grant/revoke override, личное право).
Считается теми же правилами, что Employee.has_perm (override имеет
приоритет над группами), поэтому сводка 1-в-1 совпадает с реальной
проверкой доступа. Править по-прежнему: поле «Группы» + инлайн
«Доп. права на разделы портала» (grant/revoke).
- core/access_summary.py: employee_access_summary()
- EmployeeAdmin: readonly effective_access_summary + вкладка
- core/tests_access_summary.py: 6 тестов (в т.ч. совпадение с has_perm)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
При утверждении найма сотрудник получает роли (группы) по умолчанию для
своего типа локации (Аптека/Офис/Склад). Наборы настраиваются в
EmployeeDefaults (админка «Настройки сотрудников по умолчанию»).
- EmployeeDefaults: 3 M2M default_groups_* + apply_default_groups();
- approve_hiring_request вызывает apply_default_groups после создания Employee;
- админка: блок «Роли по умолчанию при найме» (filter_horizontal);
- миграция 0023; тесты найма.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Иван Иванов -> i.ivanov (транслит pytils, нижний регистр, коллизия +счётчик).
Раньше slugify('Фамилия.Имя') терял точку и давал ivanovivan. Единый формат
в найме (_generate_username) и в импорте (transliterate_username).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Кнопка «Матрица доступа» на списке групп (unfold actions_list) открывает
сетку: строки — разделы портала, колонки — все группы, галочки просмотра.
Сохранение через apply_full_access_matrix (CUD не трогает — они на карточке
группы). Логика переиспользована из user_groups_portal. Смоук-тесты рендера
и сохранения.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Две сущности (auth.Group + PermissionGroup-профиль) слиты в одну страницу
админки Group: имя, разделы портала (curated), прочие права, описание,
активна, ссылка на матрицу доступа. Отдельный пункт «Группы разрешений»
убран из меню; модель PermissionGroup остаётся профилем под капотом.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Form Word draft for OnlyOffice, publish the file to unlock the month; roster is head-first then by name with Employee responsibility zones.
Co-authored-by: Cursor <cursoragent@cursor.com>
- ConnectorConfig(onlyoffice) + миграция, подсказка по extra_config в админке.
- /documents/onlyoffice/ в whitelist 2FA — Document Server забирает файл
без сессии портала (доступ по подписанному file-token + JWT DS).
- Страница редактора и кнопки «открыть в OnlyOffice» в браузере документов.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Модель/сервисы HR-заявок с заменами, приглашения на установку пароля
(инвайты), обновлённая страница профиля сотрудника и правки 2FA.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 2FA: убраны /api/ и /media/ из ALLOWED_PREFIXES — staff-сессия без TOTP
больше не дотягивается до API/медиа (устройства в /api/ идут без staff-сессии)
- Nextcloud «Обмен»: writable только для staff (не для любого logged-in)
- Nextcloud путь отдела/адрес аптеки: safe_component() санитайзит сегмент
(сепараторы/управляющие/ведущие точки) в провижининге и в браузере
- Mattermost: shared secret кнопки «Виконано» перенесён из query string в
context (тело POST) — без утечки в логи/Referer
- Login: throttle на кэше (5 попыток / 5 мин, 429); logout только POST (CSRF),
logout-ссылки в шаблонах → POST-формы
Тесты обновлены под enforcement 2FA (login_2fa helper) и новые правила.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- FIELD_ENCRYPTION_KEY: production требует явный ключ (ImproperlyConfigured
при пустом), local — fallback на SECRET_KEY с предупреждением (crypto.py)
- MATTERMOST_INTERACTIVE_TOKEN: fail-closed токен для интерактивных кнопок
Mattermost (bot_service task-done URL + проверка в mattermost views)
- Ужесточение прав API (employees/visit_manager) + тесты
(employees, mattermost_connector, visit_manager)
- .env.example с описанием переменных; правки шаблонов (index/dislocation/pharmacies)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- core/views.py: fix open redirect in login — validate next_url with
url_has_allowed_host_and_scheme before redirecting
- visit_manager/api/views.py: add explicit permission_classes/
authentication_classes on all 3 API views; add MIME magic-byte
validation + Pillow verify for photo uploads; validate employee_id/
location_id/date query params; restrict photo access to is_staff,
own record, or direct manager
- employees/serializers.py: split into EmployeeSerializer (public
fields only) and EmployeeDetailSerializer (PII fields, for HR/admin)
- mattermost_service.py: sanitize sensitive fields (password, token)
before logging; log only error message not full response body
- settings/base.py: add SESSION/CSRF cookie HttpOnly + SameSite flags;
set file upload size limits (15MB); remove BasicAuthentication from
DRF (sends credentials in plaintext)
- Renamed is_staff label to 'Доступ к панели администратора' with
clearer help text explaining it's for IT admins only
- Removed is_staff from the standard Authentication fieldset
- Added get_fieldsets() override: superusers get an extra collapsed
'Системный доступ' tab with is_staff and user_permissions
- Remove Employee.role CharField — was a display-only label with no
actual permission logic
- 3 migrations: create groups, migrate data, remove field
- Admin: list/filter/fieldset now use groups; added groups_display method
- Serializer: role → groups (SlugRelatedField returning group names)
- Template: get_role_display → groups.first.name
- EmployeePermissionOverride and PermissionGroup remain for fine-grained
per-employee and per-group permission management