Генерация документов: что попадает в PDF и что дорабатывать
Самая критичная для клиента часть системы. Разобрана по результатам прогона 16.07.2026 на проекте «[TEST] Полный флоу-тест 2026» (клиент — ПАО Сбербанк, «Моя компания» — ООО «РМС ГРУПП», НДС включён 22%, статьи затрат: техника 400 000, работы 250 000, логистика 120 000).
Какие документы существуют
| Тип | Эндпоинт | Рендерер | Статус после создания |
|---|---|---|---|
| Смета | POST /api/v1/projects/:id/documents/estimate | fpdf (структурный) | issued |
| Счёт | POST /api/v1/projects/:id/documents/invoice | HTML-шаблон → SimplePDFGenerator | issued |
| Акт | POST /api/v1/projects/:id/documents/act | fpdf | draft |
| Договор | POST /api/v1/projects/:id/documents/contract | fpdf | draft |
| Счёт без проекта | POST /api/v1/companies/:id/invoices (произвольные позиции) | HTML-шаблон | issued |
| Карточка компании | GET /api/v1/companies/:id/card.pdf | fpdf | — |
Скачивание: 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 коп.» — ✅ корректно.
- Подписи «Составил / Согласовал».
Доработки:
- Позиции = категории. Клиенту нужна детализация по статьям (или переключатель «по категориям / по статьям»).
- Нет реквизитов сторон (ИНН заказчика, данных исполнителя) — смета обезличена.
- Наценка из настроек сметы не попадает (блок настроек вообще не сохраняется — см. флоу-тест, этап 5).
- Колонка «Фактическая сумма» пустая и для клиентской сметы не нужна — печатать её стоит только во внутренней версии.
Счёт (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 руб.» (без форматирования разрядов).
Доработки (по приоритету):
- Заменить
SimplePDFGeneratorна реальный HTML→PDF движок (wkhtmltopdf/chromedp/gotenberg) или перевести счёт на тот же fpdf-рендерер, что смета/акт/договор (быстрее всего). - Декодировать HTML-энтити; форматировать даты (
16.07.2026) и суммы (770 000,00). - Добавить блок НДС, сумму прописью, назначение платежа, подписи (руководитель/бухгалтер), опционально — QR-код для оплаты.
Акт (act) — ✅ рендерится, есть замечания
Содержимое: шапка «АКТ ВЫПОЛНЕННЫХ РАБОТ № 1», Заказчик с полными реквизитами (ИНН/КПП/ОГРН/адрес), Исполнитель (только название), проект, таблица работ по категориям («1 усл.» × цена), «ИТОГО», формула «работы выполнены в полном объёме… претензий не имеет», блок подписей с М.П.
Доработки:
- «ИТОГО: 770 000,00» печатается дважды подряд (строка таблицы + итог) — убрать дубль.
- У Исполнителя в шапке нет ИНН/адреса (у Заказчика есть) — асимметрия.
- В блоке подписей поля «Организация: ___, ИНН: ___» пустые, хотя обе стороны известны системе — заполнять автоматически.
- НДС не выделен (в акте обычно «В том числе НДС …»).
Договор (contract) — ✅ рендерится, есть замечания
Содержимое: «ДОГОВОР № 1 от 16.07.2026», преамбула с юр. названиями и ИНН сторон («в лице Греф Герман Оскарович» — ФИО директора заказчика из DaData), предмет (название проекта), объём и стоимость (таблица по категориям + ИТОГО), порядок оплаты (фикс. «5 рабочих дней с момента подписания акта»), ответственность, реквизиты и подписи сторон (у исполнителя — полный банк, у заказчика — банка нет).
Доработки:
- Опять двойное «ИТОГО».
- Адреса сторон в блоке реквизитов слипаются в одну строку без разделителя: «…помещ 205117312, г Москва…».
- Банковские реквизиты заказчика не выводятся, хотя счёт компании заведён в CRM (вкладка «Реквизиты» компании) — надо подтягивать основной счёт клиента.
- Преамбула: «в лице …» указано только у заказчика; у исполнителя не указан подписант (директор «Моей компании» в реквизитах не хранится — нужно поле).
- Шаблон не использует данные проекта: сроки (даты проекта), этапность оплат, предоплата — кандидаты на параметры шаблона.
- Грамматика: «именуемый» для ООО/ПАО → «именуемое».
Сквозные проблемы документов
- Р/с = К/с у исполнителя. Во всех документах расчётный счёт «Моей компании» равен корсчёту (
30101810200000000593) — это мусор в данных/settings/my-company/requisites(у второго набора счёт вообще044525593044525593, 18 цифр = дважды БИК). Нужна валидация: длина 20 цифр, к/с начинается с 301, р/с — с 405/406/407/408, контроль ключа счёта по БИК. - Ответ создания документа отдаёт Go-поля
ID/ProjectID/TotalAmount/FileIDвместо snake_case — фронт не может прочитатьid, теряется ссылка на созданный документ. Привести DTO к API-конвенции. - Нет API списка документов (
GET /documents?project_id=не существует) — поэтому вкладка «Документы» проекта живёт на моках (ProjectDocumentsTab.vue, hardcoded «Счет #661»). Нужны: эндпоинт списка + реальная вкладка с фильтрами (тип/статус/дата/сумма уже свёрстаны) + действия «скачать»/«сменить статус». - UI генерации документов отсутствует: из интерфейса можно сгенерировать только смету (кнопка на вкладке Бюджет/Смета). Счёт/акт/договор создаются только через API. Нужны кнопки в вкладке «Документы».
- Payload документа хранит слепок данных компании/реквизитов на момент генерации — это правильно (документ не должен меняться задним числом), зафиксировать как контракт.
- Права: генерировать документы могут
adminи обладателиdocuments.manage. Мёртвая проверка несуществующей системной ролиproject_managerудалена; permission из миграции0057выданadmin,managerиhead_of_department. Живой прогон подlogist@seed.rms(employee) подтверждает HTTP 403 на генерацию.
Печать карточки компании
GET /companies/:id/card.pdf — ✅ работает, содержит: тип организации, руководителя, полное наименование, ИНН/КПП/ОГРН, юр./факт. адрес, email/телефон и все банковские счета с пометкой основного.
Доработки: нормализация регистра («Пао» → «ПАО», юр. название капсом как в ЕГРЮЛ), убрать пустую вторую страницу, починить переносы в узких лейблах.