Skip to content

Генерация документов: что попадает в PDF и что дорабатывать ​

Самая критичная для клиента часть системы. Разобрана по результатам прогона 16.07.2026 на проекте «[TEST] Полный флоу-тест 2026» (клиент — ПАО Сбербанк, «Моя компания» — ООО «РМС ГРУПП», НДС включён 22%, статьи затрат: техника 400 000, работы 250 000, логистика 120 000).

Какие документы существуют ​

ТипЭндпоинтРендерерСтатус после создания
СметаPOST /api/v1/projects/:id/documents/estimatefpdf (структурный)issued
СчётPOST /api/v1/projects/:id/documents/invoiceHTML-шаблон → SimplePDFGeneratorissued
АктPOST /api/v1/projects/:id/documents/actfpdfdraft
ДоговорPOST /api/v1/projects/:id/documents/contractfpdfdraft
Счёт без проектаPOST /api/v1/companies/:id/invoices (произвольные позиции)HTML-шаблонissued
Карточка компанииGET /api/v1/companies/:id/card.pdffpdf—

Скачивание: GET /api/v1/documents/:id/download → {url} (presigned S3, 5 минут). Статусы: PATCH /api/v1/documents/:id/status. Нумерация — сквозная по типу в рамках проекта (смета № 1, № 2, …) — ✅ работает.

Доп. соглашения нет

Типа «дополнительное соглашение» не существует ни на бэкенде (только invoice/estimate/act/contract), ни на фронте. Для клиента это отдельная доработка: новый тип документа + шаблон (номер и дата основного договора, изменяемые пункты, новая стоимость).

Откуда берутся данные (все типы) ​

  • Заказчик — компания проекта (companies): юр. название, ИНН, КПП, ОГРН, юр. адрес, ген. директор.
  • Исполнитель/Поставщик — «Моя компания» (/settings/my-company/requisites, основной набор): юр. название, ИНН, КПП, ОГРН, адрес, банк (наименование, БИК, р/с, к/с), режим и ставка НДС.
  • Позиции — статьи затрат проекта (project_cost_items), агрегированные по категориям: в документ попадают строки «Техника и оборудование», «Логистика», «Трудозатраты» с плановыми суммами. Названия конкретных статей (notes: «Аренда звукового оборудования» и т.п.) в PDF не попадают.
  • Суммы — плановые суммы статей; количество всегда «1 усл.» по цене = сумме категории.
  • Проект — название в шапке («Объект / проект»).

Смета (estimate) — ✅ рендерится хорошо ​

Содержимое PDF (смета № 2 от 16.07.2026):

  • Шапка: «СМЕТА № 2 от 16.07.2026 г.», Заказчик (только название), Объект/проект.
  • Таблица: №, Наименование (категория), Плановая сумма, Фактическая сумма.
  • Итоги: «Сумма без наценки: 770 000,00», «В том числе НДС (22.00%): 138 852,46» — расчёт для включённого НДС корректен, «ИТОГО: 770 000,00».
  • «Сумма прописью: Семьсот семьдесят тысяч рублей 00 коп.» — ✅ корректно.
  • Подписи «Составил / Согласовал».

Доработки:

  1. Позиции = категории. Клиенту нужна детализация по статьям (или переключатель «по категориям / по статьям»).
  2. Нет реквизитов сторон (ИНН заказчика, данных исполнителя) — смета обезличена.
  3. Наценка из настроек сметы не попадает (блок настроек вообще не сохраняется — см. флоу-тест, этап 5).
  4. Колонка «Фактическая сумма» пустая и для клиентской сметы не нужна — печатать её стоит только во внутренней версии.

Счёт (invoice) — 🔴 полностью сломан ​

Единственный тип, который идёт через HTML-шаблон (documents.templates, template_id=1) и заглушку SimplePDFGenerator (internal/modules/documents/app/render/pdf_simple.go):

  • генератор пишет весь текст одной строкой в PDF-оператор Tj со шрифтом Helvetica (Type1) — кириллица в кодировке UTF-8 превращается в мусор: в PDF-вьюере документ выглядит как строка из тире и знаков препинания;
  • HTML-теги вырезаются примитивно, HTML-энтити не декодируются — в тексте остаются " вместо кавычек;
  • нет вёрстки: ни таблицы, ни переносов строк;
  • дата в ISO-формате 2026-07-16 вместо 16.07.2026;
  • НДС в счёте не выделен вообще (для режима «НДС включён» обязана быть строка «В том числе НДС»);
  • нет суммы прописью, нет полей подписи/печати, нет назначения платежа и срока оплаты.

Содержательно в счёт попадают: Поставщик (юр. название, ИНН/КПП, банк, БИК, р/с, к/с), Покупатель (юр. название, ИНН/КПП, адрес), позиции по категориям, «Итого: 770000 руб.» (без форматирования разрядов).

Доработки (по приоритету):

  1. Заменить SimplePDFGenerator на реальный HTML→PDF движок (wkhtmltopdf/chromedp/gotenberg) или перевести счёт на тот же fpdf-рендерер, что смета/акт/договор (быстрее всего).
  2. Декодировать HTML-энтити; форматировать даты (16.07.2026) и суммы (770 000,00).
  3. Добавить блок НДС, сумму прописью, назначение платежа, подписи (руководитель/бухгалтер), опционально — QR-код для оплаты.

Акт (act) — ✅ рендерится, есть замечания ​

Содержимое: шапка «АКТ ВЫПОЛНЕННЫХ РАБОТ № 1», Заказчик с полными реквизитами (ИНН/КПП/ОГРН/адрес), Исполнитель (только название), проект, таблица работ по категориям («1 усл.» × цена), «ИТОГО», формула «работы выполнены в полном объёме… претензий не имеет», блок подписей с М.П.

Доработки:

  1. «ИТОГО: 770 000,00» печатается дважды подряд (строка таблицы + итог) — убрать дубль.
  2. У Исполнителя в шапке нет ИНН/адреса (у Заказчика есть) — асимметрия.
  3. В блоке подписей поля «Организация: ___, ИНН: ___» пустые, хотя обе стороны известны системе — заполнять автоматически.
  4. НДС не выделен (в акте обычно «В том числе НДС …»).

Договор (contract) — ✅ рендерится, есть замечания ​

Содержимое: «ДОГОВОР № 1 от 16.07.2026», преамбула с юр. названиями и ИНН сторон («в лице Греф Герман Оскарович» — ФИО директора заказчика из DaData), предмет (название проекта), объём и стоимость (таблица по категориям + ИТОГО), порядок оплаты (фикс. «5 рабочих дней с момента подписания акта»), ответственность, реквизиты и подписи сторон (у исполнителя — полный банк, у заказчика — банка нет).

Доработки:

  1. Опять двойное «ИТОГО».
  2. Адреса сторон в блоке реквизитов слипаются в одну строку без разделителя: «…помещ 205117312, г Москва…».
  3. Банковские реквизиты заказчика не выводятся, хотя счёт компании заведён в CRM (вкладка «Реквизиты» компании) — надо подтягивать основной счёт клиента.
  4. Преамбула: «в лице …» указано только у заказчика; у исполнителя не указан подписант (директор «Моей компании» в реквизитах не хранится — нужно поле).
  5. Шаблон не использует данные проекта: сроки (даты проекта), этапность оплат, предоплата — кандидаты на параметры шаблона.
  6. Грамматика: «именуемый» для ООО/ПАО → «именуемое».

Сквозные проблемы документов ​

  1. Р/с = К/с у исполнителя. Во всех документах расчётный счёт «Моей компании» равен корсчёту (30101810200000000593) — это мусор в данных /settings/my-company/requisites (у второго набора счёт вообще 044525593044525593, 18 цифр = дважды БИК). Нужна валидация: длина 20 цифр, к/с начинается с 301, р/с — с 405/406/407/408, контроль ключа счёта по БИК.
  2. Ответ создания документа отдаёт Go-поля ID/ProjectID/TotalAmount/FileID вместо snake_case — фронт не может прочитать id, теряется ссылка на созданный документ. Привести DTO к API-конвенции.
  3. Нет API списка документов (GET /documents?project_id= не существует) — поэтому вкладка «Документы» проекта живёт на моках (ProjectDocumentsTab.vue, hardcoded «Счет #661»). Нужны: эндпоинт списка + реальная вкладка с фильтрами (тип/статус/дата/сумма уже свёрстаны) + действия «скачать»/«сменить статус».
  4. UI генерации документов отсутствует: из интерфейса можно сгенерировать только смету (кнопка на вкладке Бюджет/Смета). Счёт/акт/договор создаются только через API. Нужны кнопки в вкладке «Документы».
  5. Payload документа хранит слепок данных компании/реквизитов на момент генерации — это правильно (документ не должен меняться задним числом), зафиксировать как контракт.
  6. Права: генерировать документы могут admin и обладатели documents.manage. Мёртвая проверка несуществующей системной роли project_manager удалена; permission из миграции 0057 выдан admin, manager и head_of_department. Живой прогон под logist@seed.rms (employee) подтверждает HTTP 403 на генерацию.

Печать карточки компании ​

GET /companies/:id/card.pdf — ✅ работает, содержит: тип организации, руководителя, полное наименование, ИНН/КПП/ОГРН, юр./факт. адрес, email/телефон и все банковские счета с пометкой основного.

Доработки: нормализация регистра («Пао» → «ПАО», юр. название капсом как в ЕГРЮЛ), убрать пустую вторую страницу, починить переносы в узких лейблах.

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