Документы поставщиков
Документ поставщика — счёт, акт, УПД или чек сторонней компании, у которой мы что-то берём для проекта: субаренда оборудования, закупка, площадка, подрядчик. Это реальные расходы по ходу проекта. Смета считается до проекта, и расходы в ней номинальные: документы поставщиков смету не меняют. Раздел сметы у документа нужен, чтобы сравнить факт с планом и заметить перерасход. Этап 6 ТЗ «смета → документы → расходы → план и факт».
Путь документа
получен ──(руководитель проекта: «это наш расход», в пределах сметы раздела)──▶ согласован
│ │
├──(сверх сметы раздела или вне сметы: ждёт финансов)──▶ финансы согласуют ─────┤
│ ▼
└──▶ отклонён (не наш расход) ──▶ «вернуть в работу» оплата платежами: частично оплачен → оплачен| Статус | Что значит |
|---|---|
received — получен | Заведён, ждёт подтверждения. awaiting_finance — руководитель подтвердил, но расход сверх сметы: ждёт финансов |
approved — согласован | Можно оплачивать |
partially_paid — частично оплачен | Проведена часть оплаты |
paid — оплачен | Проведённые платежи покрыли сумму документа |
rejected — отклонён | Не наш расход (причина — reject_reason) |
Править можно только полученный документ; правка снимает подтверждение руководителя. Один и тот же документ дважды не заводится: у поставщика не бывает двух документов одного вида с тем же номером и датой, если они не отклонены (409 supplier_document_duplicate).
Кто что делает
- Финансы: затраты «все»,
accounting.manageили глобальный администратор, capabilitysupplier_documents_manage. Видят все документы на странице «Финансы → Документы поставщиков». Заводят документы, согласуют (в том числе сверх сметы), оплачивают. Удаляют документ без платежей. - Руководитель проекта (
projects.manager_id): документы своего проекта на вкладке «ТЗ и смета → Бюджет», карточка «Расходы поставщиков». Заводит документ, подтверждает «это наш расход», отклоняет. Может запланировать оплату, провести её — финансы. Удаляет полученный или отклонённый документ без платежей. - Читать документы проекта может ещё тот, у кого открыт раздел финансов проекта (бюджет клиента или затраты).
Перерасход
Руководитель нажимает «Подтвердить расход», и сервер сравнивает раздел с планом:
- факт раздела — согласованные и оплаченные документы раздела плюс этот документ;
- план раздела — плановая себестоимость раздела действующей сметы (согласованной, иначе последней).
Раздел документа — вид раздела сметы плюс его название: у каждой версии сметы свои строки разделов, поэтому id раздела не годится.
- Факт в пределах плана — документ согласован сразу.
- Факт выше плана — документ остаётся «получен» с
overrun_amount(на сколько документ выводит раздел за план) иawaiting_finance. Финансовому директору уходит уведомление «Расход сверх сметы ждёт согласования». - Документ без раздела или проект без сметы — расход вне сметы, весь он сверх плана.
Оплата
«Оплатить» создаёт исходящий платёж с supplier_document_id, контрагент — поставщик (counterparty_type=supplier). Правила:
- документ согласован или частично оплачен;
- сумма не больше
outstanding_amount— это сумма документа минус проведённые и запланированные платежи; - для проекта действуют обычные ворота «без ТЗ — нет денег».
После проведения платежа:
- статус документа пересчитывается по проведённым платежам;
- в факт проекта пишется оплаченная сумма: строка
project_cost_itemsсsource_type='supplier_document'и статьёй по разделу сметы. Виды разделов переводятся в статьи так: оборудование →tech, перевозки →logistics, командировки →travel, команда →labor, подрядчики →contractors, материалы →materials, креатив →creative, прочее и свои разделы →other.
При старте сервера оплата и факт пересчитываются по всем документам с проведёнными платежами.
Скан и распознавание
Скан (PDF, фото, DOCX, XLSX, до 15 МБ) ложится в папку «Счета» проекта, а у документа без проекта — к поставщику.
«Загрузить скан и распознать» (POST /ai/extract/supplier-document, нужна функция помощника «Документы») заполняет форму: вид, номер, дату, поставщика, суммы без НДС, НДС и итог, строки. Код проверяет результат:
- приводит числа и даты к единому виду;
- проверяет ИНН по контрольным цифрам;
- сверяет суммы: без НДС + НДС = итог, строки = итог;
- ищет поставщика в справочнике по ИНН.
Нет такого поставщика — форма предлагает «Добавить поставщика» (POST /supplier-documents/suppliers: с тем же ИНН возвращается существующий). Ничего не сохраняется, пока сотрудник не нажмёт «Добавить документ».
Справочник поставщиков по матрице прав открыт складу, а финансам и руководителям проектов нужен только выбор. Для этого — GET /supplier-documents/suppliers.
API
| Метод | Путь | Что делает |
|---|---|---|
| GET | /supplier-documents | список: финансы — все, остальные — своих проектов; фильтры project_id, status, supplier_id, q, awaiting=finance |
| POST | /supplier-documents | завести: multipart payload (JSON) + file, или JSON |
| GET/PUT/DELETE | /supplier-documents/:id | документ, правка полученного, удаление без платежей |
| POST | /supplier-documents/:id/approve | согласовать или подтвердить (см. «Перерасход») |
| POST | /supplier-documents/:id/reject | отклонить (reason) |
| POST | /supplier-documents/:id/reopen | вернуть отклонённый в работу |
| GET | /supplier-documents/:id/file | ссылка на скан |
| GET | /projects/:id/supplier-documents | документы проекта |
| GET/POST | /supplier-documents/suppliers | поставщики для выбора, поставщик из документа |
| POST | /ai/extract/supplier-document | распознать скан |
Живые обновления: действия с документами и платежи рассылают entity_changed с частью проекта expenses. Списки на странице и в карточке проекта обновляются сами.
Где в коде
internal/modules/supplierdocs— модель, правила, хранилище, HTTP;internal/app/supplier_documents.go— связки: план из сметы (estimates.SectionCosts), факт вfinance, скан вfiles, платежи, обязательства, уведомление финансовому директору;internal/modules/payments/gates.go— оплата по документу,PaymentObserver;internal/modules/assistant/supplier_extract.go— распознавание;- миграция
0169_supplier_documents.sql.