219 Commits

Author SHA1 Message Date
lokses fcaf8b85c8 nextcloud: публичные RO-ссылки на документы аптек; аптеки больше не на Group Folders.
Нужен внешний (без входа в ACORE) read-only доступ к документам аптеки —
для сторон вроде поставщиков/проверяющих. Для сотрудников внутренний доступ
уже был (access.can_access_pharmacy_docs через виджет портала), это отдельный,
внешний путь.

- NextcloudSharingService — клиент OCS Files Sharing API (публичные ссылки,
  идемпотентно: сначала проверяет существующую шару на путь, не плодит дубли).
- webdav_service.ensure_dir() — идемпотентный MKCOL (в отличие от mkdir(),
  который должен падать на дубликате — так и оставлен для UI).
- PharmacyPublicLink — аптека ↔ URL + share_id (для последующего отзыва/
  обновления).
- sync_service.sync_pharmacy(): больше не использует Group Folders (аптечные
  сотрудники учёток Nextcloud не имеют — ACL по AD-группам тут не нужен,
  это была подготовка под LDAP, которая пока не горит). Теперь — обычная
  WebDAV-папка под сервисным аккаунтом + публичная RO-ссылка. Попутно чинит
  сегодняшний блокер: --pharmacies-only больше не зависит от сломанного (404)
  Group Folders API — клиенты в NextcloudSyncService стали ленивыми.
- Отделы (sync_department) не тронуты — Group Folders/LDAP остаётся
  архитектурным заделом на будущее, не нужно прямо сейчас.

Тесты: SharingServiceTests, SyncPharmacyPublicLinkTests (идемпотентность,
обновление URL, обработка ошибок WebDAV, dry-run). 326 тестов в затронутых
приложениях — зелёные.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-02 23:13:58 +02:00
lokses f52526ea98 fix(check_connectors): Mattermost OK-ветка — bot.caps() вместо bot.role.
MattermostBot не имеет поля .role (есть галки can_* и метод caps()).
При успешном коннекте (users/me → 200) команда падала с AttributeError.
Теперь показывает включённые возможности бота. Тест на OK-ветку добавлен.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-02 20:24:41 +02:00
lokses f53593966e connectors: команда check_connectors — health-check Mattermost/Nextcloud/SILO.
Первый шаг подключения внешних сервисов: проверка доступности +
аутентификации, а не просто наличия настроек.
- mattermost — GET users/me ботом provisioning;
- nextcloud  — WebDAV PROPFIND корня files-хранилища;
- s3 (silo)  — list_objects_v2 на media-бакете (таймаут 5с, без ретраев).

Отключённый коннектор → SKIP; включённый недоступный → FAIL + ненулевой
код возврата (для мониторинга/CI). Переиспользует существующих клиентов
(MattermostService, NextcloudWebDAVService, django-storages S3-настройки).
Тесты: core/tests_check_connectors.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-02 19:09:40 +02:00
lokses 33c43c9c7d chore(.env.example): убрать реальные внутренние IP, заменить плейсхолдерами
Пример окружения больше не раскрывает топологию сети:
- ALLOWED_HOSTS: убран 10.10.31.3 (оставлены localhost/127.0.0.1)
- DATABASE_URL: 10.10.31.2 → 127.0.0.1 (+ комментарий про <db-host>)
- комментарий Traefik: 10.10.31.3 → <portal-lan-ip>

Секретов не было (SECRET_KEY/пароли = CHANGE_ME/пусто) — только IP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-01 20:49:21 +02:00
lokses 9fb33be6b2 security(api): rate limiting, constant-time токены, без утечки ошибок
По аудиту маршрутов (OWASP API):
- DRF throttling: глобально UserRateThrottle 120/min; attendance-register —
  ScopedRateThrottle 30/min, photo — UserRateThrottle. Анти-DoS/абьюз.
- Mattermost-вебхуки (slash + interactive): сверка токена через
  hmac.compare_digest (constant-time) вместо '!='
- /api/attendance/register: PvmsClientError больше не уходит клиентом —
  детали в лог, ответ общий ('Сервис учёта временно недоступен')
- /set-password/<token>: мягкий IP-троттл только на НЕвалидные токены
  (легитимный валидный токен не блокируется)

Тесты: throttle attendance → 429; вебхук отклоняет неверный/пустой токен;
set-password троттлит невалидные, пропускает валидный. 801 тест, 0 провалов.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-01 20:42:35 +02:00
lokses 3d8f88b61e security: CSP-middleware (Фаза 1, Report-Only, выкл. по умолчанию)
ContentSecurityPolicyMiddleware ставит заголовок CSP при CSP_ENABLED=True.
По умолчанию выключен; CSP_REPORT_ONLY=True — только отчёты, ничего не
блокирует (для сбора нарушений перед боевым режимом).

Политика (дефолт под текущий стек): default-src 'self'; object-src 'none';
base-uri 'self'; frame-ancestors 'none'; form-action 'self';
script/style-src с 'unsafe-inline' (пока есть инлайн onclick/<script> —
Фаза 2: вынести и перейти на nonce) + CDN (jsdelivr/unpkg/cdnjs) + тайлы карт.
CSP_EXTRA_HOSTS — внешние хосты (напр. домен OnlyOffice Document Server).

htmx CSP-чистый (allowEval=false, нет hx-on) — 'unsafe-eval' не нужен.

Тесты: выкл по умолчанию, Report-Only/enforce заголовки, extra-hosts. 795 ок.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-01 16:49:07 +02:00
lokses 2a350f7301 security: устранение двух Stored XSS (JSON в <script>) + харднинг
Аудит по OWASP: найдены и исправлены два Stored XSS класса «json.dumps|safe
в <script>» — вырыв из тега через </script> в пользовательских данных.

- OnlyOffice-редактор (documents-editor): имя файла (title) выводилось как
  var cfg = {{ editor_config|safe }} → перешли на |json_script
- РРО (rro.html): поля реестра (notes/model_name/...) в <script
  application/json> → |json_script; из view убран ручной json.dumps
- safe_filename: отсекает < > : " | ? * и управляющие символы (defense-in-depth)
- SECURE_CONTENT_TYPE_NOSNIFF = True

Тесты: XSS-пэйлоад </script> в имени файла и в поле РРО экранируется;
safe_filename отклоняет HTML-опасные символы. Прогон: 791 тест, 0 провалов.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-01 16:39:30 +02:00
lokses 59377b24ab it_manager/portal: импорт инвентаря аптек + складские POS-права
- it_manager: модуль импорта инвентаря аптек (inventory_import.py),
  management-команда import_pharmacy_inventory, тесты и фикстура
  pharmacy_inventory.json
- portal_connector: POS-права склада (builders, pos_permissions) + тесты
- core: seed-миграция 0027 — права WMS группе «Сотрудник склада»
- it_manager: фикстура aliases-bad.json для теста report-import

check чист; тесты инвентаря и portal_connector зелёные.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-01 16:04:04 +02:00
lokses 5fa9fede82 tests: актуализация под телефонию и форматирование телефона
- EmployeeAdminPasswordTests: форма требует internal_line_type (поле
  телефонии, у модели default=ANALOG) — добавлено в данные формы
- EmployeeDetailTests: в чужой карточке телефон отображается
  форматированным (format_ua_phone_display) — проверка обновлена

Полный прогон (.venv): 788 тестов, 0 провалов.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-01 15:47:09 +02:00
lokses 1215655569 projects: полное редактирование проекта и форма добавления шага по кнопке.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 17:58:03 +03:00
lokses 827140b5a5 projects: редактирование шаблонов, блочное добавление шагов и смена порядка.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 17:48:03 +03:00
lokses 1e5de2ed57 projects: не валидировать скрытые поля ручного режима в форме создания.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 17:36:25 +03:00
lokses 5b16d6edd4 projects: убрать отдел шага по умолчанию — отдел только у задач.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 17:29:40 +03:00
lokses 18cabe0e63 projects: деревовидный выбор отделов вместо плоского списка.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 17:18:37 +03:00
lokses 7da277b9ab projects: popup создания и шаблоны с блоками шагов на странице.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 17:09:52 +03:00
lokses 18cb33dc1d projects: отдел на уровне задачи внутри шага.
В одном шаге можно назначать задачи разным отделам (админка и «Отдел | задача»).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 16:59:33 +03:00
lokses 030bbc98fd projects: модуль проектов с шагами по отделам и Task.
Шаблоны/ручное создание, access_projects, сид «Открытие аптеки». Плюс SILO/S3 для Django media.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 16:26:45 +03:00
lokses 69ae14f65e documents: права view/edit_all_documents для группы и сотрудника.
Чтобы на портале можно было выдать доступ ко всем документам так же, как к отчётам всех отделов.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 14:04:51 +03:00
lokses 50825ce505 telephony: скрыть книгу и пропущенные, пока модуль не готов.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-15 13:41:53 +03:00
lokses 81a93bfb38 telephony: телефонная книга и история пропущенных через AMI.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 16:48:02 +03:00
lokses 6a9fbdf6a7 telephony: Originate и статус линии под диалплан MikoPBX.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:58:03 +03:00
lokses 8b6924ffd1 telephony: Originate через Local и запасной набор 380 при отсутствии 050.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:46:52 +03:00
lokses 9117140d6d telephony: показывать ошибку звонка в тосте, а не глухой 400.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:40:36 +03:00
lokses 68752231d6 telephony: клик по мобильному и статус трубки через AMI.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:29:11 +03:00
lokses 5f3660ff15 telephony: принимать AMI Pong и не путать ping с событиями.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:16:57 +03:00
lokses 839eb9aad0 telephony: ping AMI не падает 500 при таймауте и plaintext на 5038.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:11:32 +03:00
lokses 5d06d4d3ce telephony: автоформат +380, тип линии SIP/АТС и звонок через Asterisk AMI.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 14:52:09 +03:00
lokses cc62324b69 docs: не дублировать папки своего отдела в «Дополнительно доступно».
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 13:34:08 +03:00
lokses 87bb98d097 it: не дублировать списки аптек и сотрудников в каждом ряду оборудования.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 13:23:47 +03:00
lokses 4e1d5f221e ui: заменить оставшиеся боковые панели на центрированные popup.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 13:12:21 +03:00
lokses 31dd3b4ffc fix: ссылки в админку ведут на /acore-a-panel/, а не на /admin/.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:57:23 +03:00
lokses f3629d7833 docs: зафиксировать HTMX-портал и границы с PHMS и VisitManager.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:46:29 +03:00
lokses 24f9e54283 hr: различать штат и договор ЦПХ у сотрудника и в заявке на найм.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:46:17 +03:00
lokses a3356cdd90 it: каталог моделей в popup и возврат к добавлению экземпляров.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:41:22 +03:00
lokses f2e9129a77 it: формы добавления оборудования и выполненной работы в popup.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:32:32 +03:00
lokses 07b4c69d14 pharmacies: вкладки в popup редактирования и точка на карте для координат.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:16:11 +03:00
lokses 045171275e pharmacies: popup редактирования со списка и выгрузка аптек в Excel.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 11:00:10 +03:00
lokses c0b9cf12f7 tasks: форма создания задачи в popup вместо раскрывающегося блока.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 22:35:52 +03:00
lokses bc3f3e29c8 hr: в мастере найма сначала место, затем кандидат и доступы.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 22:27:07 +03:00
lokses 45d668a675 hr: форма найма в popup-мастере из трёх шагов.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 22:22:13 +03:00
lokses 7b04ca7477 hiring: спрашивать ИНН в заявке на найм для фоторегистратора.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 17:29:31 +03:00
lokses e3154f8118 ad: DnD дерева OU, очередь сопоставления переименованных учёток и simple bind без NTLM/MD4.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 17:04:50 +03:00
lokses 456af4a836 ad/mattermost: сопоставление и OU в админке, доступы к командам и каналам по отделам и группам.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 13:58:36 +03:00
lokses b4876c4efd it_manager: настраиваемое содержимое QR в шаблоне этикетки.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 17:24:05 +03:00
lokses 620f4016da it_manager: показывать текст на этикетке даже если в шаблоне только QR/EAN.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 17:09:07 +03:00
lokses 22085219c3 it_manager: автоинв. № EAN-13 и фиксация мм в печати шаблона.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 17:03:38 +03:00
lokses f381d692dd it_manager: одна кнопка печати этикетки по шаблону.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 16:57:59 +03:00
lokses c87911ee55 it_manager: исправить загрузку layout и drag в редакторе этикеток.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 16:51:16 +03:00
lokses d54f6fbe42 it_manager/locations: редактор шаблона этикеток, раздельная печать QR/EAN и карточка поставщика.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 16:40:48 +03:00
lokses 82ad90c661 it_manager/locations: этикетки QR/EAN-13 и вкладка сервисов в дислокации.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 16:18:41 +03:00