120 lines
11 KiB
Markdown
120 lines
11 KiB
Markdown
# Локальные проекты и участие сотрудников
|
||
|
||
`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 для новых компонентов.
|