Skip to content

Заявки на оборудование: роли и разделение обязанностей ​

Это описание ролей в equipment request workflow: кто создает заявку, кто согласует деньги, кто планирует обеспечение и почему backend не запрещает одному человеку совмещать несколько ролей.


Общая схема ​

Equipment Request — фасад над логистическими таблицами для маршрута PM -> финансы -> закупки/логистика.

Основные endpoint-ы:

  • POST /api/v1/projects/:id/equipment-requests — проектная заявка от менеджера;
  • POST /api/v1/equipment-requests — standalone-заявка;
  • POST /api/v1/equipment-requests/:id/submit — отправить на финансы;
  • POST /api/v1/equipment-requests/:id/finance/approve|reject — финансовое решение;
  • POST /api/v1/equipment-requests/:id/procurement/start|reject — решение закупок/логистики;
  • GET /api/v1/equipment-requests/:id/source-options — варианты источников;
  • POST /api/v1/equipment-requests/:id/operation-plan/preview|confirm — план и создание дочерних операций.

Фасад возвращает агрегированный status, но внутри также хранит logistics_status, finance_status и procurement_status.

Одна позиция оборудования — одна строка заявки. Повтор equipment_id в items[] не ошибка: количество складывается в первую строку. Проектная заявка, созданная при открытой черновой заявке проекта (request_created), доливается в неё: для того же оборудования requested_qty прибавляется к строке, project_requested_qty, дни аренды, цена и комментарий берутся из нового запроса. Потребность проекта (PUT /projects/:id/equipment-requirements/:equipment_id) по-прежнему задаёт количество целиком — строка на оборудование в проекте одна (уникальность по проекту и оборудованию).


Роли в процессе ​

РольЧто делаетОсновные поля
ИнициаторСоздает заявку и позиции оборудованияrequested_by, items[]
Финансовый согласующийПроверяет сумму и принимает решениеfinance_status, finance_decided_by, finance_decided_at
Закупки/логистикаБерет заявку в работу, выбирает источники, строит план операцийprocurement_status, responsible_id, source-options
Логист маршрутаВедет дочерние logistic_operations до исполнения и закрытияconfirmed_by, confirmed_at, carrier/driver/date fields

Это процессные роли, а не обязательно разные штатные должности.

Кто что может (решение «каждый отвечает за своё») ​

ДействиеКтоОтказ
Создать, править (позиции, потребности проекта), отправить, отменить заявку проекта; импорт спискаУчастники команды проекта и те, кто его ведёт (projects.Service.CanManageProject: запись карточки «все», «свой» в своём дивизионе, руководитель проекта), плюс склад и логистика (transport.RunsLogistics: logistics.manage или запись equipment_stock) и глобальный администратор403 equipment_requests_write_forbidden — например, руководитель дивизиона или менеджер в чужом проекте
Архивировать заявку проектаТе, кто ведёт проект, склад и логистика — на любом этапе; участник команды — только черновик (created, до отправки)403 equipment_requests_archive_submitted_forbidden
Отклонить заявку (/reject)Те, кто ведёт проект, склад и логистика, глобальный администратор403 equipment_requests_reject_forbidden
Читать заявки проекта, карточку и итоги заявкиРаздел логистики проекта, склад и логистика, финансы (transport.IsFinance) — им согласовывать суммы403
Завести при импорте новую позицию каталогаСклад (запись equipment_stock), инженеры (ENGINEER, TECH_DIR), equipment.manage, глобальный администратор (transport.ManagesEquipmentCatalog)Строка уходит в errors[] импорта: «Оборудования „…“ нет в каталоге — попросите склад добавить позицию»
finance/approve, finance/rejectТолько финансы: запись затрат costs «все» (OWNER, OPS, FIN_DIR) или глобальный администратор (transport.IsFinance). Менеджер с затратами «свой» свою заявку не согласует403 equipment_requests_finance_decision_forbidden
Закупка (procurement/*), план операций, автопланТолько склад и логистика (transport.RunsLogistics); раздела логистики проекта для этого недостаточно403 equipment_requests_procurement_forbidden
Очередь заявок (GET /equipment-requests)Склад и логистика, финансы403 equipment_requests_logistics_desk_required
Заявки без проекта (складские)Склад и логистика403 equipment_requests_logistics_desk_required

Правило записи собрано в internal/app/project_supply_access.go и отдаётся карточке проекта в access.equipment_requests; интерфейс по нему показывает кнопки заявки, по capability finance_approve — кнопки согласования суммы, по equipment_catalog_manage — подсказку про новые позиции при импорте, по logistics_manage (= logistics_operate) — очередь, закупку и план операций.


Маршрут заявки ​

text
created
  submit
    ↓
under_review
  finance approve
    ↓
under_review / awaiting_supply
  procurement start
    ↓
in_progress
  operation-plan confirm
    ↓
in_transit / completed

Основные переходы:

ДействиеПроверкаРезультат
submitstatus=created, finance_status=draftlogistics_status=under_review, finance_status=pending
finance/approvefinance_status=pending, logistics_status=under_reviewfinance_status=approved, logistics_status=awaiting_supply
finance/rejectзаявка ожидает финрешениеfinance_status=rejected, logistics_status=cancelled
procurement/startфинансы approvedprocurement_status=in_progress, logistics_status=sourced
procurement/rejectфинансы approvedprocurement_status=rejected, logistics_status=cancelled
operation-plan/confirmдля проектной заявки финансы approvedсоздаются дочерние logistic_operations

API-статусы фасада:

  • created;
  • under_review;
  • in_progress;
  • in_transit;
  • completed;
  • rejected;
  • cancelled.

Почему один человек может делать несколько шагов ​

Backend намеренно не зашивает жесткое правило "инициатор не может согласовать свою заявку".

Причина практическая: в маленькой команде один сотрудник может быть и менеджером, и финансистом, и логистом. Жесткое segregation of duties сделало бы такой сценарий невозможным.

Вместо запрета система фиксирует подотчетность:

  1. Финансовое решение записывает finance_decided_by, finance_decided_at и комментарий.
  2. Procurement-решение записывает procurement_decided_by, procurement_decided_at и комментарий.
  3. Подтверждение маршрута записывает confirmed_by и confirmed_at.
  4. Переходы выполняются через ожидаемые текущие статусы; повторный клик или гонка операторов не должны применить решение дважды.

Если организации нужна строгая раздельность, она настраивается правами: не выдавайте одному пользователю одновременно права инициирования, финансового согласования и procurement/logistics-действий.


Деньги в заявке ​

Стоимость позиций считается по equipment.unit_cost * requested_qty * day_multiplier.

rental_days задается на позиции заявки. Если клиент его не передал, используется 1.

Множитель по дням:

  • 1-й день — 100%;
  • 2-5 дни — 50% за день;
  • 6-10 дни — 30% за день;
  • 11-15 дни — 15% за день;
  • 16+ дни — 10% за день.

Денежная арифметика должна выполняться точно, через правила из Денежных сумм.


Поведение фронта ​

  1. Показывайте пользователю агрегированный status, но для диагностики сохраняйте raw поля logistics_status, finance_status, procurement_status.
  2. Кнопку submit показывайте только для created.
  3. Финансовые кнопки показывайте только для finance_status=pending.
  4. Procurement-действия показывайте после finance_status=approved.
  5. Планирование маршрутов для проектной заявки запускайте только после finance approve.
  6. После operation-plan/confirm открывайте список дочерних операций через GET /api/v1/equipment-requests?parent_request_id=<id>&include_items=true.

Подробный сценарий source options и дочерних логистических операций описан в Логистическом контуре.

Загружаем документацию…