Перейти к основному содержимому

0037. Формат экспорта досудебной претензии: DOCX (Unicode-native), а не PDF

Копия ADR из репозитория: backend/docs/adr/0037-claim-export-format.md. Канонический текст — в репозитории; портал обновляется командой npm run sync:adr.

  • Статус: accepted (2026-07-09)
  • Дата: 2026-07-09
  • Связанные задачи: OP-137 (хранение истории + экспорт документа); соседи — OP-136 (подстановка/редактирование, ClaimEntity), OP-134/135 (модель шаблонов, генерация текста)
  • Связанные ADR: —

Контекст и проблема​

ТЗ 5.12 (AC OP-137) требует экспорт документа претензии «по умолчанию PDF/DOCX», формат «согласуется отдельно». Текст претензии — русскоязычный (кириллица). Нужен один формат для MVP: универсальный, корректно отображающий кириллицу, реализуемый без тяжёлой инфраструктуры и тестируемый детерминированно.

Ключевое техническое ограничение вскрылось на этапе реализации: PDF + кириллица требует встраивания TTF-шрифта. Стандартные PDF-шрифты (pdfkit → Helvetica и т.п.) содержат только латиницу; для кириллицы нужно поставлять и грузить в рантайме бинарный TTF-ассет (лицензия, путь загрузки в prod-сборке dist/, усложнение юнит-тестов генерации). Изначальная авто-рекомендация цикла (/op architectural-stop, авто-выбор по таймауту) называла PDF — до обнаружения этой стоимости.

Решение​

Экспорт — DOCX (OOXML), библиотека docx (pure-JS, без нативных зависимостей).

Причины предпочесть DOCX перед PDF:

  • Unicode-native: OOXML хранит текст в UTF-8, кириллица работает без встраивания шрифтов и бинарных ассетов — Word/просмотрщик применяет системный шрифт.
  • Редактируемость: досудебная претензия — черновик, который селлер дорабатывает и печатает; DOCX редактируем, в отличие от PDF.
  • Простота и тестируемость: одна pure-JS зависимость; результат — валидный ZIP-контейнер (PK-сигнатура), детерминированно проверяется в юнит-тесте без шрифтовых ассетов.
  • Соответствие AC: DOCX явно указан в критериях приёмки как допустимый формат по умолчанию.

Сборка документа выделена в чистый модуль claim-docx.builder.ts (buildClaimDocx/selectClaimText/claimDocxFilename). Итоговый текст — правка (editedText), иначе исходная генерация (generatedText). Эндпоинт GET /legal/claims/:id/export отдаёт вложение через StreamableFile (Content-Disposition: attachment).

Последствия​

  • Положительные: нулевая стоимость поддержки кириллицы; редактируемый выход; лёгкая зависимость; простые тесты; история и экспорт закрывают OP-137 без новой сущности (переиспользуется ClaimEntity из OP-136).
  • Trade-offs: PDF (неизменяемый «финальный» вид) не поставляется в MVP. Если он понадобится (печать с фиксированной вёрсткой), добавляется отдельным форматом — тогда решается вопрос встраивания кириллического шрифта (OFL TTF) отдельным решением.
  • Обратимость: формат инкапсулирован в билдере и MIME/имени файла контроллера; добавление второго формата (query-параметр format) не ломает контракт существующего эндпоинта.