# Локальные проекты и участие сотрудников `projects` — самостоятельный Django-модуль корпоративного портала. Проект создаётся локально и не требует подключения МойСклад. Торговые модели и действующая синхронизация остаются в `commerce`. ## Структура - `models/project.py`: карточка проекта, история настроек и связанные материалы. - `models/membership.py`: участники, роли, даты участия и плановая загрузка. - `models/stage.py`: этапы и сроки. - `models/external_binding.py`: связь локального проекта с внешним объектом. - `models/time_allocation.py`: день сотрудника, распределение, версии и напоминания. - `services/time_tracking.py`: валидация распределения, снимок часов, напоминания. - `permissions.py`: доступ к проектам и управлению ими. - `api/v1/projects/`: API; `frontend/src/app/projects/`: список и карточка. ## Доступ и история Создание доступно администраторам и пользователям с `projects.add_project`. Руководитель управляет своей карточкой, командой, этапами, связями и видит отчёт команды. Участник видит проект и собственные строки времени. Администратор видит все проекты. `?mine=true` ограничивает список собственными назначениями и для администратора. Финансовый endpoint отдельно проверяет действующие права `can_access_payroll_management`; руководство проектом не открывает зарплаты. Все ответы API имеют `Cache-Control: private, no-store`. Проекты архивируются, API не удаляет их. Участие завершается датой окончания, чтобы сохранить контекст. API сохраняет события изменений карточки и связей; версии распределения времени хранят полный снимок процентов, часов, комментария и статуса. В админке дни и версии доступны только для чтения. ## Внешние связи Идентичность объекта — `(source, object_type, external_id)`. Один внешний объект может принадлежать одному локальному проекту; локальный проект может объединять несколько внешних объектов. Названия не служат ключом и автоматически не объединяются. Привязка не изменяет внешнюю запись, перенос привязки требует явного удаления прежней связи. Синхронизация `commerce` не записывает локальные проекты. Для проектов МойСклад есть поиск по импортированному справочнику, включая индикацию занятых привязок. Главная страница `/projects` отдельно показывает активные внешние проекты без связи. Из этой очереди руководитель может одной операцией создать локальную карточку вместе с привязкой либо связать внешний проект с уже существующей карточкой. Создание выполняется транзакционно и не изменяет исходную запись МойСклад. Для будущих источников можно сохранять привязки по ID и типу объекта. Это хранение соответствий, а не реализация новых адаптеров. Финансовая вкладка карточки и корпоративная аналитика выбирают проведённые, не удалённые из источника исходящие платежи по точным связанным идентичностям и одной валюте. Корпоративный отчёт использует локальную карточку как основную строку, объединяет в ней продажи, расходы и платежи всех привязанных объектов и показывает неподключённые данные в группе «Без привязки». Подтверждённое распределение сотрудников добавляется в часы, дни учёта и число участников. Движение денег, расходы продаж и трудозатраты остаются отдельными показателями. ## Ежедневное распределение `/projects`, профиль и ежедневная «Выработка» открывают один модал. В нём есть дата, проекты сотрудника, общая деятельность (работа отдела, обучение, поддержка), проценты и комментарий. Подтверждение требует ровно 100%; черновик допускает меньшую сумму. Повторяющиеся строки и сумма больше 100% отклоняются. Повторение прошлого подтверждённого дня за 90 дней фильтрует недоступные назначения и требует ручного подтверждения нового дня. Можно редактировать дни за прошедшие 366 дней. Доступные назначения учитывают даты участия. Новые записи не относятся к завершённым и архивным проектам. Ранее сохранённые строки можно исправлять с сохранением версии. Продолжительность берётся из успешного анализа посещаемости с положительным `work_hours`. Без него сохраняются проценты, часы остаются `null`. Это часы по данным посещаемости, не автоматически подтверждённый трудозатратный табель. Изменение посещаемости после сохранения отмечается при открытии дня; повторное сохранение создаёт новую версию с обновлёнными часами. Часы участия = часы конкретного дня × процент / 100. В итог входят только подтверждённые дни с известными часами; черновики и дни без часов отмечены отдельно. Средние дневные проценты не используются вместо суммы часов. Часы и баллы выработки не смешиваются, зарплата от ввода процентов не изменяется. Сохранение блокирует строку сотрудника, проверяет версию и атомарно заменяет строки дня вместе со снимком истории. Для конкурентного доступа предусмотрен PostgreSQL; SQLite в локальной разработке не реализует `SELECT FOR UPDATE`. ## Напоминания Общая оболочка портала проверяет напоминание после входа и раз в минуту, когда вкладка видима, другое окно не открыто и пользователь не вводит текст. Сервер выбирает последний незаполненный день за 14 дней, для которого есть успешная посещаемость с положительными часами и доступный проект. Используются только прошедшие даты, поэтому сегодняшняя/ночная смена не предлагается до завершения календарного дня. Выходные и отсутствия не выводятся из предположения о пятидневке. Право показа резервируется на сервере один раз за день, общим для устройств. «Напомнить через 2 часа» откладывает следующую попытку; закрытие оставляет день незаполненным, доступным из «Мой день». Нужна открытая вкладка портала: отдельных email/push-рассылок эта версия не отправляет. Отключение напоминаний конкретному сотруднику доступно в админке `AllocationReminder.enabled`. ## Связи с другими разделами и границы версии Задачи, закупки и документы присоединяются внутренними ссылками из карточки проекта. Доступ к оригиналу проверяется его модулем. Пока это явные ссылки, а не обязательное поле проекта в формах создания каждого из этих объектов. Плановые денежные бюджеты, распределение зарплаты и общих затрат, согласование трудозатрат руководителем и закрытие финансовых периодов этой версией не вводятся. Они требуют отдельных правил расчёта и будут строиться поверх сохранённого участия. Исходная книга Excel не импортируется этим модулем. ## Проверка ```sh cd backend .venv/bin/python manage.py migrate .venv/bin/python manage.py check .venv/bin/python -m pytest tests/projects tests/finance/test_projects.py -q ``` Frontend: `npm run build`, `npx tsc --noEmit`, целевой ESLint для новых компонентов.