Commit Graph

156 Commits

Author SHA1 Message Date
lokses 7606592ed8 Чат: метка «бот» у сообщений от ботов
Захватываем is_bot при резолве пользователей (/users/ids), рядом с именем
отправителя показываем компактную подпись «бот». Также распознаём вебхуки
(props.from_bot / from_webhook). Свои сообщения и обычных юзеров не помечаем.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 21:15:29 +03:00
lokses 85bd89f762 Чат: сортировка по активности, непрочитанные, кнопка «Написати»
- Порядок каналов/ЛС — по реальному времени последнего поста (тянем per-channel,
  т.к. last_post_at из списка каналов ненадёжен), архивные (delete_at>0) — вниз
- Непрочитанные: счётчик по websocket, бейдж на строке + жирное имя, префикс
  «(N)» в тайтле, браузерное уведомление; сброс при открытии канала
- Кнопка «Написати в чат» на карточке сотрудника → /chat/?dm=<mm_id>,
  чат открывает/создаёт ЛС с этим человеком (идемпотентный /channels/direct)
- Тесты кнопки чата в EmployeeDetailTests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 16:08:53 +03:00
lokses ea2cc9daca Чат: аккуратный сайдбар списков ЛС и каналов
- Единый скролл вместо жёсткого max-height:35% на ЛС и flex:1 на каналах
  (убран пустой провал под каналами)
- Секции-заголовки — компактные uppercase-подписи в стиле портала
- Строки ЛС и каналов выровнены по единому левому краю (0.75rem)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 15:55:26 +03:00
lokses caa728158b Чат: автооткрытие свежего диалога и поиск сообщений
- Списки каналов и ЛС сортируются по last_post_at (свежие сверху)
- При открытии чата автоматически выбирается самая свежая розмова — сразу
  видны последние сообщения, а не пустой экран
- Поиск сообщений через Mattermost /teams/{id}/posts/search: строка поиска
  над лентой, результаты списком (кто/канал/время/текст), клик открывает канал
- Выход из поиска при открытии канала и смене команды

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 15:48:28 +03:00
lokses 04b1625fde Безопасность: закрытие 5 открытых находок ревью 25.07
- 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>
2026-07-28 15:38:44 +03:00
lokses ed7dcaae3b Nextcloud connector: страница «Документы» подключена к хранилищу
Заменяет статичный макет documents.html реальным браузером Group Folders:
- access.py: accessible_sections (разделы по правам: Компания/Обучение/Обмен,
  отдел сотрудника, Кадровые дела для суперюзера) + safe_relpath (посегментная
  валидация подпути от path traversal); раздел резолвится только из whitelist
- views: documents_browse/download/upload с проверкой раздела и пути на каждом
  эндпоинте, запись — только в writable-разделы
- documents.html: вкладки разделов + ленивый HTMX-браузер с хлебными крошками,
  навигацией по папкам, скачиванием и загрузкой
- documents_view отдаёт доступные разделы; тесты разделов, путей и авторизации

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 15:02:30 +03:00
lokses b6ad270971 Nextcloud connector: виджет «Документы» аптеки (Фаза 3)
- WebDAV-клиент (PROPFIND/GET/PUT/DELETE) под сервисным аккаунтом
- access.py: can_access_pharmacy_docs (аптека видит только свою) + safe_filename
  (защита от path traversal); путь папки выводится только из объекта аптеки
- Вьюхи documents/download/upload/delete с проверкой доступа на каждом эндпоинте
- Вкладка «Документы» на pharmacy-detail с ленивой HTMX-загрузкой
- Тесты авторизации: свой/чужой скоуп, 403, traversal, аноним

Аптечные сотрудники учёток Nextcloud не имеют — доступ идёт через портал под
сервисным аккаунтом, вся авторизация на стороне ACORE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 11:22:19 +03:00
lokses 19bc2874aa Nextcloud connector: скелет провижининга Group Folders (Фаза 2)
- ConnectorConfig: тип 'nextcloud' (host/username/token, шифруется)
- NextcloudService: OCS-клиент Group Folders (create/ACL/quota, идемпотентный ensure_folder)
- NextcloudSyncService: отделы → 03_Отделы/* ↔ nc-dept-<код>; аптеки → 02_Аптеки/* ↔ nc-svc
- Сигналы Directory/Pharmacy с гейтом на включённый коннектор (по умолч. no-op)
- Celery-задачи + команда nextcloud_sync (dry-run по умолчанию, --apply)
- NextcloudSyncLog + admin; тесты (пути, гейт, идемпотентность ensure_folder)

Членство юзеров идёт через AD/LDAP, коннектор ведёт только папки+ACL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 11:09:49 +03:00
lokses 4e803c53e0 2FA: обязательная двухфакторная аутентификация (TOTP) для staff
- Модель Employee: totp_secret (шифруется), totp_enabled, totp_backup_codes
- Сервис twofa: генерация секрета/QR, проверка TOTP, резервные коды (sha256)
- TwoFactorMiddleware: enforcement для is_staff на портале и /admin/
- Страницы setup/verify/backup-codes; сброс 2fa_verified при каждом входе
- Админка: колонка статуса 2FA, фильтр, действие «Сбросить 2FA»
- Тесты: enforcement, setup, verify, резервные коды, disable

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 10:41:11 +03:00
lokses 5c078c6cb5 i18n: перевод страницы it-manager (ru/uk/en)
Самая большая страница (1131 строка, ~155 строк): вкладки (оборудование/
заявки/провайдеры/расходники/работы), каталог моделей, формы выдачи/создания,
таблицы, статусы, панели, интернет-договоры, показания — помечены {% trans %}
и переведены на uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 10:22:57 +03:00
lokses aa5a1bd421 i18n: перевод страницы timetrack (ru/uk/en)
Учёт рабочего времени: заголовок, навигация по месяцам, фильтры,
столбцы таблицы, пустое состояние — {% trans %}; метрики (из вью)
переведены через gettext. uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 10:06:22 +03:00
lokses 655ba8c31a i18n: перевод ленты новостей (news/news-detail/news-edit) ru/uk/en
Список публикаций, форма создания/редактирования, детальная (комментарии,
публикация в Mattermost, важное/закреплённое), JS-строки (загрузка картинок,
свернуть/развернуть) помечены {% trans %} и переведены на uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 21:59:01 +03:00
lokses e34c45c20b i18n: перевод страницы hr-portal (ru/uk/en)
HR-портал: метрики, вкладки (сотрудники/заявки/тренинги/вакансии/стажировка),
форма найма, отпуска/больничные, документы — помечены {% trans %} и
переведены на uk/en (включая строчные/смешанные строки, кнопки, hx-confirm).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 16:20:55 +03:00
lokses 611f0e1fdd i18n: перевод страниц pharmacies/pharmacy-detail (ru/uk/en)
Список аптек (фильтры, метрики, таблица, пагинация) и карточка аптеки
(инфо, лицензия, персонал, оборудование, интернет-договоры, показания
счётчиков) помечены {% trans %} и переведены на uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 12:29:15 +03:00
lokses 38c8e35a51 Security hardening: обязательный ключ шифрования, fail-closed токены, права API
- 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>
2026-07-27 12:23:28 +03:00
lokses 9e31b9d620 i18n: перевод страниц settings/profile/structure (ru/uk/en)
Настройки портала, профиль пользователя и структура офиса помечены
{% trans %} и переведены на uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 22:04:43 +03:00
lokses 92527cc52a i18n: перевод страницы dislocation (ru/uk/en)
Вкладки аптеки/офис, метрики, фильтры, таблицы, пагинация, карта и её
popup/JS-строки помечены {% trans %} (в т.ч. внутри <script>, с обходом
кавычек) и переведены на uk/en. 51 строка.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:54:20 +03:00
lokses 87cb9de005 i18n: перевод модуля задач (tasks/task-detail/task-edit) ru/uk/en
- Task.STATUS_CHOICES/PRIORITY_CHOICES переведены на русские msgid
  (консистентно с ru-базой); миграция choices
- task_detail_view: метки ролей через gettext
- Шаблоны tasks/task-detail/task-edit размечены {% trans %}; статусы,
  приоритеты, чек-лист, комментарии, участники, кнопки действий — uk/en

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:55:48 +03:00
lokses 443135fe33 i18n: перевод страницы employee-detail (ru/uk/en)
Личные данные, контакты, работа, интеграции, руководство, статусы, задачи —
помечены {% trans %} и переведены на uk/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:03:51 +03:00
lokses 888ff9b9aa i18n: перевод страницы employees (ru/uk/en)
Заголовки, фильтры, столбцы таблицы, статусы, пустое состояние помечены
{% trans %}; переведены uk/en (часть msgid переиспользуется). fuzzy снят.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:02:08 +03:00
lokses 76f3bf3584 Фикс: контент не грузился после i18n (проверка плейсхолдера по тексту)
JS определял плейсхолдер сравнением с захардкоженным «Завантаження...», а
текст загрузчика стал переводиться ({% trans %}) — из-за чего в ru/en
контент не подгружался (и при обычной загрузке, и после смены языка).
Теперь плейсхолдер определяется по #page-loader (язык-независимо).
Переключатель языка возвращает на текущую страницу (next=pathname).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:58:03 +03:00
lokses 31f597e84a i18n: перевод страницы dashboard (ru/uk/en)
Все видимые строки главной страницы помечены {% trans %} (русский msgid),
переведены на uk/en; часть переиспользует msgid из меню. Сняты fuzzy-метки,
неверные авто-переводы перезаписаны корректными. .mo пересобраны.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:54:11 +03:00
lokses 2bc9615ce5 i18n: инфраструктура + переключатель языков + перевод меню (ru/uk/en)
Мультиязычность: русский — базовый (msgid), украинский и английский — переводы.

- Настройки: LocaleMiddleware, LANGUAGES=[ru,uk,en], LOCALE_PATHS, i18n
  context_processor; urls: django.conf.urls.i18n (set_language)
- Шелл index.html: {% load i18n %}, все пункты навигации/меню/пользовательского
  меню/нижней навигации помечены {% trans %}; динамический <html lang>;
  переключатель языков в хедере (select -> set_language)
- Каталоги locale/uk и locale/en: переведены строки меню/шелла (34), собраны .mo
- .gitignore: разрешено трекать locale/**/*.mo (нужны на проде, CI нет)

Остальные страницы пока на украинском (хардкод) — переводятся дальше поэтапно.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:46:40 +03:00
lokses fcdd5cb662 Админка: кнопка применения для дропдаун-фильтров справочников
Дропдаун-фильтры unfold рендерятся как форма и не применяются сами по
выбору — добавлен list_filter_submit=True (кнопка «Застосувати»), иначе
фильтр «не отрабатывал».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:21:15 +03:00
lokses a8faa206f6 Админка: горизонтальные фильтры в справочниках
Фильтры справочников (Directory, Системи доступу) вынесены из боковой
панели наверх в тулбар (unfold list_filter_sheet=False), кастомные фильтры
переведены на unfold DropdownFilter — выпадающие списки вместо списка ссылок.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 01:25:11 +03:00
lokses 242f0c95e0 Админка: отдельный раздел «Системи доступу» для баз/систем доступа
Системы доступа (1С, почта, CRM…) — записи справочника Directory
(access_system), которые выбираются в заявке на найм. Раньше их можно было
редактировать только внутри общего «Довідники» с фильтром — неочевидно.

- AccessSystem: proxy над Directory (фильтр по access_system), свой раздел
  админки со списком/добавлением; directory_type проставляется автоматически
- SILENCED_SYSTEM_CHECKS=['models.E016'] — гасит ложноположительный E016
  (proxy наследует treenode-индекс по _path; на реальной таблице всё корректно)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 01:12:07 +03:00
lokses 5a274f7f90 Найм: авто-задача IT на доступи з чек-листом + сповіщення постановника
При створенні HR-заявки з обраними базами/доступами тепер одразу ставиться
задача IT-відділу (замість тікета на затвердженні):

- TaskChecklistItem: чек-лист задачі, виконавець відмічає зроблене
  (task-detail: чекбокси, лічильник виконаного, хто/коли відмітив)
- create_hiring_request → _create_access_task: задача з чек-листом по базах,
  creator = HR (постановник), co_executors = IT-група (тригерить сповіщення
  ботом Ричард), priority=high, due_date рахується за робочим часом
- add_working_hours (visit_manager/services): дедлайн за робочим вікном
  09:00–18:00 з урахуванням вихідних і свят (Holiday)
- Постановнику (HR) бот пише, що заявку взято в роботу, строк — 24 год
  робочого часу (до розрахованої дати)
- approve більше не створює дублюючий IT-тікет (доступи вже задачею)
- HiringRequest.access_task; чек-лист в адмінці задач (inline)
- Тести: hiring (задача+чек-лист, approve без тікета), робочі години
  (вихідні/свята), відмітка чек-листа через в'ю

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 01:01:30 +03:00
lokses a17e982d8c HR: «Посада» у формі найму залежить від типу локації
Активовано закладений механізм Directory.location_types (використовується в
Employee.clean(), але справочник був порожній):

- Міграція core 0005: сид довідника «Тип локации» (office/pharmacy/warehouse) —
  тепер посадам можна призначати типи локацій в адмінці (M2M location_types)
- _hr_portal_context віддає карту hr_post_location_types {post_id: [коди]}
- Форма найму: селект «Посада» фільтрується за обраним типом локації
  (hrHiringFilterPosts). Порожній список типів у посади = доступна скрізь
  (та сама семантика, що в clean()); невідповідну обрану посаду скидає

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 18:20:31 +03:00
lokses 4f784582d5 HR: форма найму — з окремої вкладки на кнопку «Створити»
Вкладку «Стажування» прибрано з панелі вкладок; біла форма заявки на найм
(панель hr-panel-hiring: форма + список заявок) тепер відкривається кнопкою
«Створити» (hrTabSwitch('hiring')). Кнопка й панель — лише для staff.
Автовідкриття панелі після створення заявки (hr_active_tab) працює як раніше.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 18:12:46 +03:00
lokses 8a4577f165 Чат: аватар собеседника в списке ЛС + переключатель комнат
- Аватар співрозмовника напроти особистого повідомлення (лівий список):
  chatBuildChannelRow отримав опцію avatar; id співрозмовника ЛС резолвиться
  в chatResolveDmLabel і підвантажує фото (chatApplyAvatar). Групові ЛС —
  без фото, з ініціалами.
- Верхній перемикач «кімнат» — команди Mattermost (Офіс / Аптеки / Склад):
  chatRenderTeamSwitcher/chatSelectTeam; chatRenderChannelList фільтрує канали
  за обраною командою; за замовчуванням — перша команда. ЛС показуються завжди.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 18:03:42 +03:00
lokses 479fe3e0d7 Чат: аватары-фото сотрудников и ботов
- chatApplyAvatar(el, userId): подгружает фото из Mattermost
  /users/{id}/image (работает и для сотрудников, и для ботов) и накладывает
  на кружок-аватар; до загрузки/при ошибке остаются инициалы. Кэш по userId.
- Применено в сообщениях (chatBuildMessageEl) и в пикере коллег
  (chatRenderDmPickerList)
- Переиспользует существующий chatApiFetchBlob (Bearer-токен)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:10:09 +03:00
lokses 53c852160c Страница учёта рабочего времени на реальных данных
Было: статичный макет timetrack.html (хардкод «10 співробітників»,
пустой tbody, JS не наполнял). Стало — реальные данные из WorkShift:

- timetrack_view агрегирует смены за выбранный месяц по сотрудникам:
  часы (Sum duration), дни (distinct date), незакрытые смены (без ухода),
  среднее/день; сводные метрики; счётчики по типам локаций
- Фильтр по типу локации (всі/офіс/аптеки) и помесячная навигация —
  через HTMX, с сохранением параметров в URL
- Шаблон перестроен в стиле портала (метрики, таблица, пагинация);
  строки ведут на детальную сотрудника; пустое состояние
- Невалидный месяц откатывается на текущий

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 15:04:44 +03:00
lokses 5cc73ac924 Тесты visit_manager (19): праздники, компенсация, смены
Покрыт модуль, у которого было 0 тестов (аудит):
- Holiday: расчёт православной Пасхи (сверка 2023-2026), генерация
  украинских праздников (12 шт, идемпотентно), is_holiday/is_working_holiday,
  for_year/for_period/get_next_holiday
- Holiday-логика: calculate_work_compensation (рабочий/выходной праздник),
  clean() (overtime_rate>0), days_until/is_today/year, get_next_occurrence
- WorkShift: work_hours (из duration), is_complete

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:52:07 +03:00
lokses ab29cd5db8 Хвосты аудита: тесты tasks/employees + чистка зависимостей
- apps/tasks/tests.py (10): TaskQuerySet.for_user (видимость по ролям),
  is_participant (кто меняет статус: автор/відповідальний/співвиконавець,
  но не спостерігач), is_overdue, set_status
- apps/employees/tests.py (10): clean() (разделение фармацевт/офис, даты,
  самоуправление), has_perm с override (grant/revoke/неактивные/
  просроченные/будущие), is_currently_active
- requirements.txt: убран дубль import-export==0.3.1 (используется
  django-import-export==4.4.0)
- удалён мёртвый пустой db.sqlite3 (в .gitignore, не трекался)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:45:16 +03:00
lokses 01f0c6f002 Carbonio CE: хранение пароля ящика аптеки (шифрованно)
Вариант А — пароль ящика хранится обратимо-зашифрованным, чтобы показать
заведующему для входа:

- Pharmacy.carbonio_password (EncryptedCharField) — шифруется в БД
- sync_service при создании ящика генерирует пароль, отдаёт его в Carbonio
  и сохраняет свою зашифрованную копию; на update-пути пароль не трогается
- Пароль виден в админке аптеки (readonly, расшифрованный) — вкладка Carbonio
- 2 теста (мок CarbonioService): пароль сохранён, совпадает с отданным в
  Carbonio, в БД лежит шифртекстом; update не перетирает пароль

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:29:40 +03:00
lokses 3be444b05e Carbonio CE: API-слой синхронизации ящиков аптек
Серверная часть интеграции по эталону AD-коннектора (драйвер — Pharmacy):

- carbonio_service.py: клиент admin API Carbonio/Zimbra (JSON-SOAP на 6071):
  auth + get/create/modify(status)/delete account, ping для read-only проверки
- services/sync_service.py: sync_pharmacy_to_carbonio (идемпотентно),
  deactivate_pharmacy_carbonio; логирование в CarbonioSyncLog
- signals.py: post_save/pre_save на Pharmacy — создание ящика при включённой
  синхронизации, закрытие/открытие при смене статуса аптеки; защита от рекурсии
  (account_id гасит повторный вызов), sync_enabled=False по умолчанию не трогает прод
- tasks.py: Celery-обёртки; CarbonioSyncLog + read-only админка
- команда sync_pharmacies_to_carbonio (dry-run по умолчанию, --apply)
- Carbonio-поля в админке Pharmacy; убран конфликт models.py vs models/

Без живого сервера деградирует мягко: CarbonioConfigError не роняет сохранение.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:02:35 +03:00
lokses 3cbc019268 Carbonio CE: почтовый ящик аптеки — поля, генерация адреса, уникальность
Фундамент интеграции Carbonio на уровне модели Pharmacy (без обращений к серверу):

- Поля Pharmacy: carbonio_email, carbonio_account_id, carbonio_sync_enabled
  (default False), carbonio_sync_status, carbonio_last_sync, carbonio_sync_error
- Генерация адреса <mail_prefix организации><номер>@домен (a1@, t1@, p1@...);
  префикс из Directory.extra_data['mail_prefix'], домен из ConnectorConfig(carbonio)
  с дефолтом aks-apteka.com.ua
- Ручной override адреса не перетирается автогенерацией
- Уникальность carbonio_email: clean() + частичный unique-констрейнт в БД
  (пустые не конфликтуют); автоген при коллизии оставляет поле пустым
- 10 тестов (apps.locations.tests) — у приложения было 0

API-слой (клиент/sync/сигналы/задачи) — следующим шагом.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 19:55:15 +03:00
lokses 4f3fccb68d Mattermost: мульти-бот по ролям (provisioning/channels/tasks)
Заменяет единый cfg.token + extra_config['bot_token'] на модель MattermostBot
(роль, username, user_id, is_active, token как EncryptedCharField — токены
шифруются в БД).

- MattermostService(role='provisioning') — синхронизация сотрудников (system-bot)
- pharmacy_channel_service -> role='channels' (neo) — операции с каналами
- bot_service + feed -> role='tasks' (link) — уведомления задач и посты ленты
- Админка MattermostBot; команда create_mattermost_bot пишет в MattermostBot по --role

Токены засеваются в БД (не в git). Смена темы Матрица -> Кремниевая долина
в display_name (Гилфойл/Динеш/Ричард), код от имён не зависит.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:31:51 +03:00
lokses b788bc2a7c Шифрование секретов ConnectorConfig (password/token) в БД
- EncryptedCharField (core/models/fields.py): прозрачное Fernet-шифрование
  на границе с БД, читатели и админка не меняются
- ConnectorConfig.password/token переведены на EncryptedCharField,
  колонки расширены под шифртекст
- Миграция 0004: расширение колонок -> шифрование существующих значений -> смена типа
- FIELD_ENCRYPTION_KEY в settings (default = SECRET_KEY); на проде задавать отдельно ДО migrate

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:31:37 +03:00
lokses 4b61e78c16 Обновить тесты it_manager под каталог моделей оборудования
Тесты создавали Equipment со старыми полями type/model и били по
удалённому /equipment/create/. Переведены на EquipmentModel + массовый
ввод/выдачу (bulk-create/bulk-assign), добавлен тест массовой выдачи.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 20:55:51 +03:00
lokses c42b1b533e docs: полный аудит проекта (AUDIT.md)
Статический аудит: архитектура, приложения/модели, сильные стороны,
покрытие тестами, безопасность, конфигурация, техдолг по приоритетам,
план действий.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:33:48 +03:00
lokses 5e4528b23f Портал: страницы на реальных данных, детальные страницы, утилиты
- core/views: dashboard/databases/it-support/structure на живых данных;
  детальная сотрудника (/employees/<pk>/) с PII-гейтингом (staff/сам/
  руководитель), детальная аптеки, дислокация, профиль, показания счётчиков
- core/utils: crypto (Fernet-обёртка), pagination; партиалы пагинации и
  AD-вкладок
- шаблоны страниц обновлены под реальные контексты
- тесты (21): смоук всех страниц, PII-доступ сотрудника, дерево справочников

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:33:48 +03:00
lokses 6ff01c4119 Доменные модули: сотрудники, локации, найм, счётчики, посещения
- employees: last_seen, internal_number, default_page_size; управление-команды
- locations: категория/куратор/ответственный аптеки, seed должностей;
  Location.__str__ показывает номер аптеки (не только адрес)
- hiring: HiringRequest (заявки на найм) + тесты (7)
- utilities: UtilityReading (показания счётчиков по аптекам)
- visit_manager: правки

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:33:16 +03:00
lokses 81dee5cba2 Интеграции AD/Mattermost: OU-управление, импорт, шифрование токенов
- ad_connector: страницы управления OU и импорта из AD, поле ad_ou_dn
  в Directory, ADSyncLog; расширение sync-сервиса и клиента
- mattermost_connector: personal access token шифруется (Fernet,
  token_service), бот-сервис, команды создания бота и синхронизации;
  публикация постов в каналы. Тесты (19)
- core: ConnectorConfig, Directory (ad_ou_dn), обновления админки

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:32:26 +03:00
lokses 38747a93fc IT-менеджер: каталог моделей оборудования + перевод choices на справочники
- EquipmentModel (каталог: тип+бренд+модель заводятся раз) отделён от
  Equipment (экземпляр: серийник+локация); миграция сворачивает данные.
  Массовый ввод серийников, массовая выдача на локации/сотрудников
- Enum-choices (тип/статус/приоритет заявок, вид работ, каналы, расходники,
  договоры) переведены на core.Directory с quick-add из формы
- Фронт-страница /it-manager/: вкладки оборудование/заявки/работы/
  расходники/провайдеры/обращения; searchable-select локаций
- Тесты (10)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:31:52 +03:00
lokses f342d67f97 Модуль задач (tasks): роли Bitrix-стиля, дерево-пикер людей
- Task: ответственный/соисполнители/наблюдатели (M2M), статусы, приоритет,
  дедлайн. Видимость for_user(), наблюдатели не меняют статус
- Список с табами-статусами (один aggregate вместо 4 count), детальная
  со сменой статуса и комментариями, edit/delete с правами
- Выбор людей — дерево отделов с фото/должностями и поиском (people-picker),
  вместо select multiple; назначение задачи со страницы сотрудника
- ROLE_PREFETCH с select_related('post') против N+1; сигналы

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:31:33 +03:00
lokses 35f2091e0b Модуль ленты (feed): посты, комментарии, TinyMCE, санитизация
- Post (категории, важное/закреплено/опубликовано, автор) + PostComment;
  тело — HTML из TinyMCE, санитизация nh3 в save() (XSS), загрузка картинок
  отдельным endpoint с проверкой содержимого (data:-URI вырезаются)
- Лента с полным выводом постов, фильтры, сворачивание длинных;
  детальная страница с комментариями; edit — автор/staff, delete — автор
- Публикация постов в каналы Mattermost (mattermost_targets), сигналы
- Тесты (40): публикация/черновики, права, санитизация, картинки,
  выравнивание, комментарии, лента/дашборд

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:31:20 +03:00
lokses 78475b58e9 Инфраструктура: Celery, общие утилиты, виджеты, шелл портала
- acore: Celery-приложение (брокер из env, eager без брокера), настройки
  CELERY_*, регистрация всех новых приложений в INSTALLED_APPS, маршруты
- core/utils: render_page (HTMX-фрагмент/полный layout), is_owner/
  is_owner_or_staff; templatetags common_tags (initials, clean_excerpt,
  is_it_staff); UpdateLastSeenMiddleware
- static: people-picker (дерево отделов с аватарками), searchable-select
  (одиночный выбор с поиском), directory-quick-add, it-manager, pagination;
  тема Tailwind v4 пересобрана, лого
- templates: новый шелл index.html (мобильный сайдбар, темы, фон,
  user-dropdown) и login с брендингом

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:31:01 +03:00
lokses c3f56f8a3e Portal redesign from template, real-data pages, test suite, Celery config
- templates: new shell from ACORE-template (mobile sidebar + bottom nav,
  themes, wallpaper, user dropdown), Django-ized nav with push-url;
  login rebranding; ACORE-template/ gitignored
- new pages wired to real data: databases (Directory types/counts),
  it-support (Ticket create form + my tickets), dashboard (live counters),
  structure (departments with head heuristic + pharmacy staffing status);
  static mockups for news/tasks/email/chat/purchases/hr-portal/help
- tests: 57 tests — ad_connector (ldap3 MOCK_SYNC client, sync scenarios,
  signals, mapping page), mattermost_connector (sync on mocks, bare-except
  regression), core (pages smoke, tickets, directories, tree rebuild)
- celery: add missing acore/celery.py app + CELERY_* settings (broker from
  env, eager mode without broker locally); wrap signal .delay calls so an
  unreachable broker no longer breaks employee saves

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 22:41:46 +03:00
lokses bfd67241c4 AD office-only scope, mapping page, auto-rebuild of directory tree
- ad_connector: restrict provisioning to office employees
  (user_location_type='office') in sync service, signals and command;
  disable AD account on transfer out of office, re-enable on return
- ad_connector: /ad-mapping/ page (staff only) — match office employees
  to AD accounts with suggestions by email/login/full name, link/unlink/
  create actions, list of unmatched domain accounts; sidebar nav item
- core: auto-rebuild treenode Directory tree (_path/_depth) after
  import-export imports and post_migrate; "rebuild tree" button in
  DirectoryAdmin and rebuild_directory_tree management command

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 16:48:38 +03:00