Сервис подготовки документов рассрочки
Локальное веб-приложение для:
- ввода и нормализации сведений о должнике;
- добавления одного или нескольких договоров;
- настройки отдельной рассрочки по каждому договору;
- расчёта индивидуальных и общего графиков платежей;
- экспорта графиков в CSV;
- формирования соглашения и приложений по редактируемому Word-шаблону.
Проект намеренно сделан компактным: без фреймворка на клиенте, без базы данных и без внешнего облачного сервиса. Интерфейс, расчётный модуль и генератор документов находятся в одном репозитории.
Важно: юридические редакции Word-шаблонов хранятся непосредственно в DOCX. После каждого изменения проверяйте обе стадии на тестовых данных.
Как создать документ
Этот раздел предназначен для сотрудника, который работает с сервисом и хочет подготовить документ без изучения технической части проекта.
Шаг 1. Выберите стадию работы
В блоке 00 Стадия работы выберите нужный вариант:
- Досудебная стадия — сервис сформирует обязательство должника;
- Судебная стадия — сервис сформирует медиативное соглашение.
Стадия определяет используемый Word-шаблон. Если изменить стадию, проверьте введённые данные и параметры перед формированием нового документа.
Шаг 2. Добавьте сведения о должнике
- В разделе «Сведения о должнике» нажмите «Добавить».
- Заполните фамилию, имя и ИИН.
- При необходимости заполните отчество, адрес и номер телефона.
- Нажмите «Добавить» внутри открытой формы.
После сохранения появится карточка должника. Используйте «Изменить», если нужно исправить данные, или «Удалить», чтобы заполнить карточку заново.
Шаг 3. Заполните сведения о судебном деле
Этот раздел появляется только на судебной стадии. Все три поля необязательны:
- наименование суда;
- фамилия и инициалы судьи;
- представитель истца.
Если оставить их пустыми, в русскую часть документа будут подставлены редакционные подсказки «Указать наименование суда», «Указать Фамилию и Инициалы судьи» и «Указать представителя истца». Для казахской части используются отдельные подсказки на казахском языке.
При попытке сформировать судебный документ в PDF, скачать архив с PDF или отправить такой архив сервис предупредит о незаполненном разделе 02. В Word заглушки можно исправить после скачивания, а в готовом PDF — нельзя. Текст предупреждения начинается так: «Внимание! Раздел 02 „Сведения о судебном деле“ заполнен не полностью».
Шаг 4. Добавьте договор банковского займа
- Нажмите «Добавить» в разделе «Сведения о договоре банковского займа».
- Укажите номер договора, дату договора и сумму долга.
- Нажмите «Добавить» внутри формы договора.
Можно добавить несколько договоров. Активный договор выделяется в списке, а раздел «Параметры рассрочки» показывает настройки именно выбранного договора.
Шаг 5. Настройте рассрочку
В верхней части раздела выберите один из трёх самостоятельных способов расчёта.
При необходимости заполните блок «Авансовый платёж»: укажите сумму и дату, которая должна быть раньше даты платежа по графику. Аванс уменьшает долг, а срок рассрочки относится только к последующим регулярным платежам. В графиках, CSV и Word аванс отображается отдельной строкой по своей дате. Сумма аванса должна быть меньше суммы долга. В общем расчёте аванс распределяется между договорами пропорционально их задолженности.
Вариант 1. Равные платежи
Этот вариант подходит, когда платежи должны быть одинаковыми каждый месяц.
Расчёт по сроку рассрочки:
- Нажмите «Равные платежи».
- Выберите «По сроку рассрочки».
- Проверьте сумму долга.
- Укажите дату платежа по графику.
- Укажите срок рассрочки в месяцах.
Сервис автоматически рассчитает ежемесячный платёж. Последний платёж может отличаться из-за остатка при делении суммы долга.
Расчёт по ежемесячному платежу:
- Нажмите «Равные платежи».
- Выберите «По ежемесячному платежу».
- Проверьте сумму долга.
- Укажите дату платежа по графику.
- Укажите желаемый ежемесячный платёж.
Сервис автоматически рассчитает необходимое количество месяцев. Если долг не делится на платёж без остатка, последний платёж будет меньше обычного.
Вариант 2. Особые платежи
Этот вариант нужен, если в отдельные месяцы или периоды должны применяться суммы, отличающиеся от обычного платежа.
- Выберите «Особые платежи».
- Проверьте сумму долга.
- Укажите дату платежа по графику и общий срок рассрочки.
- Выберите способ распределения остатка:
- Ежемесячно — после учёта особых условий остаток долга распределяется поровну между остальными свободными месяцами;
- Только особые платежи — платежи создаются только по добавленным
условиям, а в остальных месяцах указывается
0.
- Нажмите «Добавить условие».
- Выберите тип условия:
- Конкретный месяц — особая сумма применяется в одном выбранном месяце;
- Период — одинаковая особая сумма применяется с начального по конечный месяц включительно.
- Выберите месяц или период и укажите сумму погашения.
- При необходимости добавьте ещё несколько условий. Ненужное условие можно удалить кнопкой с изображением корзины.
Не задавайте пересекающиеся условия для одного и того же месяца. Перед подтверждением проверьте, что особые суммы не превышают остаток долга и что выбранный способ распределения соответствует договорённости с должником.
Вариант 3. Общий расчёт
Этот вариант становится доступен после добавления двух или более договоров и создаёт единый график по всей задолженности.
- Добавьте все договоры и проверьте сумму долга в каждой карточке.
- Выберите «Общий расчёт».
- Проверьте поле «Общий долг» — оно рассчитывается автоматически как сумма долгов всех договоров.
- Укажите общий срок рассрочки.
- Укажите дату платежа по графику.
- Укажите общий ежемесячный платёж.
- Нажмите «Подтвердить».
Сервис распределит каждый общий платёж между договорами пропорционально их долгу и округлит части до целых тенге. Сумма частей всегда будет совпадать с общим платежом. В последний месяц сервис погасит весь оставшийся долг, поэтому итоговый платёж может отличаться от обычного.
Общий расчёт одновременно подтверждает параметры всех договоров. Если после этого добавить, изменить или удалить договор, общий расчёт потребуется проверить и подтвердить повторно.
Шаг 6. Подтвердите параметры
После заполнения параметров нажмите «Подтвердить». Договор и его настройки должны перейти в подтверждённое состояние. Для нескольких индивидуальных договоров повторите настройку и подтверждение для каждой карточки. При выборе «Общего расчёта» подтверждаются параметры всех включённых договоров.
Если после подтверждения изменить сумму договора или условия рассрочки, подтверждение сбросится — параметры нужно проверить и подтвердить повторно.
Шаг 7. Проверьте график
Нажмите «Показать график» и проверьте:
- дату платежа по графику;
- срок рассрочки;
- суммы ежемесячных и особых платежей;
- полное погашение остатка в последней строке;
- индивидуальные части и общий итог при нескольких договорах.
Шаг 8. Скачайте результат
В нижней панели выберите формат документа:
- Word — редактируемый файл DOCX;
- PDF — готовая PDF-копия; на сервере должен быть установлен LibreOffice.
Затем используйте нужное действие:
- Сформировать документ — скачать документ выбранной стадии;
- Скачать архив — получить одним ZIP-архивом документ и все графики;
- Отправить письмо — сформировать тот же архив и передать его в почтовую программу;
- Показать график — ещё раз проверить расчёт на странице.
Кнопка «Очистить» удаляет все введённые данные после дополнительного подтверждения. Используйте её только после скачивания нужных файлов.
Отправка графика на согласование
Кнопка «Отправить письмо» формирует ZIP-архив и открывает системное меню отправки с готовым текстом письма: «Направляем график в отношении Ф.И.О. на согласование». Если браузер и почтовая программа поддерживают передачу файлов через системное меню, архив будет добавлен к письму автоматически.
Если такая возможность недоступна, сервис скачает ZIP и откроет новое письмо
с заполненными темой и текстом. В этом случае приложите скачанный архив к
письму вручную — браузеры не разрешают добавлять вложения через ссылку
mailto:.
Какой разделитель CSV выбрать
Переключатель «Разделитель CSV» влияет только на файлы графиков. На Word, PDF и расчёт сумм он не влияет.
Для защиты от случайного изменения при выборе другого разделителя появляется предупреждение. Новый вариант применяется только после подтверждения пользователем.
В каждом CSV после столбца «ИИН» расположен столбец «Договор». В
индивидуальном графике в каждой строке указывается номер соответствующего
договора банковского займа. В общем графике перечисляются номера всех
включённых договоров через /.
;— точка с запятой — рекомендуемый вариант для русской или казахской локализации Microsoft Excel. Столбцы разделяются точкой с запятой, а суммы записываются с десятичной запятой:40000,00.,— запятая — используйте для программ, интеграций и настроек импорта, которые ожидают международный CSV с запятой между столбцами. Суммы в таком файле записываются с десятичной точкой:40000.00.
Если после открытия CSV все значения оказались в одном столбце, выберите при
импорте тот же разделитель, который был установлен в сервисе, либо сформируйте
график повторно с другим вариантом. Для обычного открытия в русской версии
Excel сначала выбирайте ;.
При одном договоре архив содержит CSV этого договора. При двух или более договорах в архив добавляются отдельный CSV для каждого договора и общий сводный график. Выбранный разделитель применяется ко всем CSV внутри архива.
Перед использованием документа обязательно откройте скачанный файл и проверьте Ф.И.О., ИИН, реквизиты договоров, судебные сведения, суммы, даты и все приложения с графиками.
Важные замечания для пользователя
- Данные существуют только в открытой странице и исчезнут после её перезагрузки.
- Сервис не сохраняет должника, договоры и сформированные документы в базе данных.
- Не обновляйте страницу до завершения работы и скачивания файлов.
- Если кнопки формирования недоступны, проверьте, добавлен ли должник, есть ли договор и подтверждены ли параметры рассрочки.
- PDF требует LibreOffice на сервере; Word, CSV, ZIP с Word и просмотр графика работают без него.
- Страница «Документация проекта» пока отображается только на русском языке;
переключатель
RU / ҚАЗдействует на основной интерфейс сервиса.
1. Основные возможности
Интерфейс построен как последовательный рабочий сценарий:
В правом верхнем углу расположен переключатель RU / ҚАЗ. Он переводит
интерфейс между русским и казахским без перезагрузки и потери введённых данных.
Выбранный язык сохраняется в браузере для следующего открытия сервиса.
Рядом расположен переключатель светлой и тёмной темы. При первом открытии используется системная тема устройства, после ручного выбора настройка сохраняется в браузере. Такой же переключатель доступен на странице «Документация проекта»; обе страницы используют общую настройку темы.
- выбрать стадию и соответствующий ей шаблон документа;
- добавить и подтвердить должника;
- добавить один или несколько договоров;
- выбрать индивидуальные параметры договора либо общий расчёт для нескольких договоров;
- нажать «Подтвердить» для индивидуальных или общих параметров;
- проверить индивидуальные или общий график;
- сформировать Word или PDF, экспортировать графики в CSV либо скачать общий архив.
Стадии и шаблоны
Перед разделом 01 расположен взаимоисключающий выбор стадии:
pretrial— «Досудебная стадия / Обязательство должника», активна по умолчанию и использует рабочий шаблонinstallment-agreement-v4;judicial— «Судебная стадия / Медиативное соглашение», использует рабочий шаблонjudicial-installment-agreement-v3.
Конфигурация стадий находится в объекте documentStages файла src/app.js.
В запрос дополнительно передаётся поле stage, а templateId выбирается из
конфигурации активной стадии. Сервер также проверяет соответствие стадии и
шаблона. Для обеих стадий доступны Word, PDF и архив с графиками.
Блок выбора стадии имеет номер 00. На досудебной стадии рабочие разделы
нумеруются 01–03. На судебной стадии между должником и договором появляется
раздел 02 «Сведения о судебном деле», поэтому последующие разделы получают
номера 03 и 04.
Кнопки добавления сведений о должнике, судебном деле и договоре одинаково называются «Добавить». Формы по умолчанию свёрнуты. После сохранения данные превращаются в компактные карточки. У карточек и настроек есть отдельные действия «Изменить» и «Удалить»:
- изменение раскрывает редактор внутри той же карточки;
- удаление должника удаляет его карточку;
- удаление договора удаляет договор вместе с его настройками;
- удаление параметров рассрочки очищает только график выбранного договора.
Состояния показаны не только текстом, но и цветом:
- зелёная карточка с галочкой — данные подтверждены;
- янтарная карточка с восклицательным знаком — договор добавлен, но параметры рассрочки ещё не подтверждены.
Все подтверждённые карточки используют одинаковую высоту, ширину, типографику и оформление без отдельной цветной полосы слева. В карточке параметров статус «✓ Выбрано» расположен справа рядом с действиями, как статус договора.
Сведения о должнике
- фамилия, имя и необязательное отчество;
- ИИН;
- адрес;
- номер телефона;
- компактная карточка после подтверждения данных;
- редактирование и удаление карточки должника.
Правила нормализации:
- в Ф.И.О. разрешены только буквы и пробелы;
- цифры и специальные символы удаляются;
- повторяющиеся пробелы объединяются;
- пробелы в начале и конце удаляются;
- первая буква каждого слова становится заглавной, остальные — строчными;
- ИИН должен содержать ровно 12 цифр;
- пробелы по краям адреса удаляются, повторяющиеся пробелы объединяются;
- поле телефона принимает до 11 цифр, допускает явный префикс
+7и для удобства отображает номер со скобками и дефисами, например+7 (700) 123-45-67,(700) 123-45-67или8 (700) 123-45-67; - буквы и посторонние специальные символы удаляются; для сохранения допустим номер из 10 или 11 цифр, а в документ значение передаётся без маски.
Сведения о судебном деле
Раздел показывается только на судебной стадии и по умолчанию свёрнут. В нём можно указать:
- наименование суда;
- фамилию и инициалы судьи;
- представителя истца.
Все поля необязательны. Если значение не задано, в русские теги Word-контекста
передаются редакционные подсказки Указать наименование суда,
Указать Фамилию и Инициалы судьи и Указать представителя истца. Для
казахской части предусмотрены отдельные теги с окончанием _kz и казахскими
заглушками. После добавления сведения отображаются компактной карточкой,
которую можно изменить или удалить.
Если выбран PDF и хотя бы одно из трёх полей не заполнено, браузер просит подтвердить формирование. Проверка применяется также к итоговому архиву и отправке письма, когда внутри формируется PDF.
Сведения о договоре банковского займа
- можно добавить несколько договоров;
- у договора есть номер, дата и сумма долга;
- договор можно изменить или удалить из компактной карточки;
- дата договора вводится текстом по маске
ДД.ММ.ГГГГ, проверяется как календарная дата и хранится в состоянии в ISO-форматеГГГГ-ММ-ДД; - дата договора не может быть позднее текущей даты;
- при редактировании компактная карточка раскрывается на своём месте, а не создаёт отдельную форму ниже списка;
- неподтверждённый договор отображается жёлтым, подтверждённый — зелёным;
- каждый договор хранит собственную конфигурацию рассрочки;
- выбранный договор открывает свои параметры в разделе расчёта;
- параметры договора необходимо отдельно подтвердить;
- после подтверждения редактор сворачивается в компактную карточку.
- подтверждённые параметры можно изменить или удалить; удаление сбрасывает только график выбранного договора и не удаляет сам договор.
Варианты рассрочки
Дата платежа по графику во всех режимах вводится как текст по маске
ДД.ММ.ГГГГ. Некорректная календарная дата не используется в расчёте.
Число дня проверяется по выбранному месяцу, включая високосные годы.
В состоянии договора дата хранится в ISO-формате ГГГГ-ММ-ДД.
Равными платежами по сроку рассрочки
- указывается сумма долга;
- указывается срок;
- рассчитывается ежемесячный платёж.
Равными платежами по ежемесячному платежу
- указывается сумма долга;
- указывается размер ежемесячного платежа;
- срок рассчитывается автоматически.
Сложный расчёт
- указывается сумма долга;
- дата платежа по графику;
- срок рассрочки;
- способ распределения остатка;
- особые платежи по конкретному месяцу или периоду месяцев.
Общий расчёт по нескольким договорам
- режим становится доступен после добавления минимум двух договоров;
- общий долг определяется автоматически как сумма долгов договоров;
- пользователь указывает единый ежемесячный платёж, общий срок и дату платежа по графику;
- общий платёж распределяется между договорами пропорционально долгу;
- общий платёж и его части округляются до целых тенге;
- остаток округления распределяется методом наибольших остатков, поэтому сумма отдельных платежей всегда точно совпадает с общим платежом;
- срок является главным ограничением: в последний месяц погашается весь оставшийся долг, даже если последний платёж больше обычного;
- подтверждение общего расчёта одновременно подтверждает параметры всех договоров;
- изменение суммы или состава договоров снимает общее подтверждение.
Результаты
- график по каждому отдельному договору;
- общий график по всем договорам;
- помесячные строки, включая строки с нулевым платежом;
- досудебный документ Word или PDF с индивидуальными графиками договоров и сводным графиком при нескольких договорах;
- судебный документ Word или PDF с одним графиком: индивидуальным при одном договоре или общим при нескольких договорах;
- итоговый ZIP с документом выбранной стадии и всеми необходимыми CSV-графиками.
В нижней панели нет отдельного поля «Ежемесячный платёж». Она содержит только
действия «Показать график», «Сформировать документ», «Отправить письмо» и
«Скачать архив». Переключатель Word / PDF определяет формат документа
при отдельном скачивании и внутри итогового ZIP. Настройка разделителя CSV
работает независимо и не меняется при выборе формата документа.
В футере находится ссылка «Документация проекта». Маршрут /about при каждом
запросе читает актуальный README.md, преобразует Markdown в оформленный HTML
и поэтому не требует отдельной сборки. После изменения README достаточно
обновить страницу документации.
2. Текущие ограничения
Перед развитием проекта важно учитывать текущие границы реализации.
- Данные хранятся только в памяти открытой страницы.
- После перезагрузки страницы должник, договоры и настройки исчезают.
- Нет базы данных, учётных записей, авторизации и истории документов.
- По умолчанию сервер слушает
127.0.0.1и доступен только локально. Для доступа из локальной сети адрес прослушивания можно явно изменить переменной окруженияHOST. - Одновременно в форме предусмотрен один должник.
- Максимальный срок расчёта — 120 месяцев.
- Процентная ставка не применяется: расчёты выполняются без начисления процентов.
- При двух и более договорах CSV-экспорт скачивается ZIP-архивом. Для просмотра его содержимого требуется стандартная функция распаковки операционной системы.
- Клиентская проверка
confirmedне является серверным механизмом безопасности. Сервер повторно проверяет обязательные данные и рассчитанный график, но не использует полеconfirmed. - Строгая проверка маски, календарного числа и запрет будущей даты договора выполняются в браузере. При развитии проекта до сетевого сервиса эти правила необходимо продублировать серверной схемой валидации.
- Для формирования PDF, в том числе внутри ZIP, на сервере должен быть
установлен LibreOffice. Если исполняемый файл
sofficeотсутствует вPATH, его путь задаётся переменной окруженияSOFFICE_PATH.
Сумма долга договора и сумма рассрочки
Источником истины является contract.debtAmount — сумма долга, указанная при
добавлении договора. Она автоматически используется:
- в карточке договора;
- в режимах «По сроку рассрочки» и «По ежемесячному платежу»;
- в сложном расчёте;
- при построении индивидуального и общего графиков;
- в CSV-экспорте;
- в Word-документе и его приложениях.
Техническое поле installment.debt сохраняется в отправляемой модели для
удобства расчётных функций, но перед расчётом всегда синхронизируется с
contract.debtAmount. Изменение суммы в любом из экранов параметров обновляет
сумму договора и остальные режимы, а подтверждение параметров договора
сбрасывается.
Сервер повторяет это правило независимо от браузера: если в запросе
contract.debtAmount и installment.debt расходятся, расчёт и документ
используют contract.debtAmount. Для совместимости со старыми запросами, в
которых debtAmount отсутствует, сервер использует installment.debt.
3. Требования
Для запуска приложения
- Node.js 20 или новее;
- npm;
- современный браузер.
Проверьте установленную версию:
node -v
npm -v
Если стандартный репозиторий Ubuntu устанавливает Node.js ниже версии 20,
установите Node.js 20 для пользователя, от имени которого запускается сервис,
через nvm:
sudo apt-get update
sudo apt-get install -y curl ca-certificates
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.6/install.sh | bash
source ~/.bashrc
command -v nvm
nvm install 20
nvm alias default 20
nvm use 20
node -v
npm -v
which node
После обновления Node.js переустановите зафиксированные зависимости проекта и проверьте приложение:
cd ~/apps/calc-rassrochka
npm ci
npm run check
HOST=0.0.0.0 PORT=8080 npm start
Версия из node -v должна начинаться с v20. Если приложение запускается
через systemd, учтите, что служба не загружает nvm из ~/.bashrc
автоматически: в ExecStart следует использовать абсолютный путь, который
показывает команда which node.
Для формирования и скачивания документа любой стадии в PDF LibreOffice
необходимо установить на сервере, где запущено приложение. Графическая
оболочка сервера не требуется: конвертация выполняется командой soffice в
режиме --headless.
# Ubuntu / Debian
sudo apt-get update
sudo apt-get install -y libreoffice-writer
# проверка установки
which soffice
soffice --version
# если soffice не находится через PATH
SOFFICE_PATH=/usr/bin/libreoffice HOST=0.0.0.0 PORT=8080 npm start
После установки нужно перезапустить Node.js-сервис. Word, CSV, ZIP с Word и просмотр графика работают без LibreOffice; зависимость нужна для отдельного PDF и ZIP-архива, если внутри выбран PDF. Шрифты, используемые в DOCX-шаблонах, также должны быть установлены на сервере, иначе LibreOffice может заменить их при конвертации.
Рекомендуется использовать актуальную LTS-версию Node.js.
Для восстановления технического DOCX-шаблона
Дополнительно требуются:
- Python 3;
- пакет
python-docx.
Установка:
python3 -m pip install python-docx
Python не нужен для обычного запуска и генерации документов. Он используется только скриптом восстановления исходного технического шаблона.
4. Быстрый запуск
Установить зависимости:
npm install
Запустить приложение:
npm start
По умолчанию приложение доступно только на текущем компьютере:
http://127.0.0.1:8080
Если порт 8080 занят, можно указать другой:
PORT=8081 npm start
Чтобы открыть доступ по IP сервера или компьютера в локальной сети, запустить:
HOST=0.0.0.0 PORT=8080 npm start
После запуска открыть с другого компьютера:
http://IP_СЕРВЕРА:8080
Например:
http://192.168.1.50:8080
0.0.0.0 означает прослушивание всех сетевых интерфейсов. В адресной строке
браузера вместо него всегда указывается настоящий IP-адрес сервера. Порт
8080 должен быть разрешён локальным или облачным сетевым экраном.
В приложении пока нет авторизации. Не публикуйте порт напрямую в интернете: сервис обрабатывает ИИН и другие персональные данные. Для внешнего доступа требуется закрытый контур либо обратный прокси с HTTPS и аутентификацией.
Запустить все автоматические проверки:
npm run check
Создать тестовый Word-документ:
npm run sample
Результат для нескольких договоров будет записан в:
.tmp/document-qa/sample-obligation-multiple.docx
Для проверки документа с одним договором:
node scripts/generate-sample-document.js --single
Результат: .tmp/document-qa/sample-obligation-single.docx.
5. Команды проекта
| Команда | Назначение |
|---|---|
npm start |
Запускает локальный HTTP-сервер и веб-интерфейс |
HOST=0.0.0.0 PORT=8080 npm start |
Запускает сервер с доступом по IP компьютера |
npm run check |
Запускает все тесты через встроенный node:test |
npm run sample |
Формирует тестовый Word-документ |
python3 scripts/create_default_template.py |
Создаёт отдельный технический DOCX, не затрагивая рабочий шаблон |
create_default_template.py по умолчанию пишет только в
server/templates/installment-agreement-generated.docx. Этот файл не
регистрируется в manifest и исключён из Git. Существующий файл скрипт не
перезаписывает. Намеренная перезапись возможна только с флагом --force.
Рабочие юридические шаблоны v1, v2 этим скриптом не затрагиваются.
6. Архитектура
Проект разделён на пять логических слоёв:
- интерфейс и состояние страницы;
- доменная логика форматирования и расчётов;
- локальный HTTP-сервер;
- генерация документов из DOCX-шаблона;
- серверная генерация CSV и ZIP-архивов.
flowchart LR
U["Пользователь"] --> UI["index.html + src/app.js"]
UI --> F["src/domain/format.js"]
UI --> S["src/domain/schedule.js"]
UI -->|POST /api/documents| H["server/index.js"]
UI -->|POST /api/exports/csv| H
UI -->|POST /api/exports/archive| H
H --> G["server/generate-document.js"]
H --> E["server/generate-csv-export.js"]
H --> A["server/generate-archive.js"]
G --> S
E --> S
G --> C["server/document-context.js"]
C --> T["DOCX-контекст"]
T --> D["Docxtemplater + PizZip"]
M["manifest.json"] --> G
W["Word-шаблон .docx"] --> D
D --> O["Готовый .docx"]
O --> U
E --> X["CSV или ZIP с CSV"]
X --> U
G --> A
E --> A
A --> Z["Итоговый ZIP"]
Z --> U
Принципы
- Один источник расчётной логики. Клиент и генератор Word используют
функции из
src/domain/schedule.js. - Шаблон отделён от данных. Юридический текст хранится в
.docx, данные подготавливаются вdocument-context.js. - Нет постоянного хранения. Все введённые данные существуют только в памяти вкладки и в теле текущего запроса.
- Версионирование шаблонов через manifest. Файл шаблона выбирается по
templateId. - Контролируемое денежное округление. Равный и сложный индивидуальные графики рассчитываются в сотых. Общий пропорциональный график рассчитывается в целых тенге, чтобы в его платежах и остатках не появлялась дробная часть.
7. Структура репозитория
.
├── index.html
├── package.json
├── package-lock.json
├── README.md
├── src
│ ├── app.js
│ ├── about.css
│ ├── i18n.js
│ ├── styles.css
│ └── domain
│ ├── format.js
│ └── schedule.js
├── server
│ ├── index.js
│ ├── generate-document.js
│ ├── generate-document-export.js
│ ├── convert-document-to-pdf.js
│ ├── generate-csv-export.js
│ ├── generate-archive.js
│ ├── file-names.js
│ ├── document-context.js
│ ├── number-to-words.js
│ ├── render-readme.js
│ └── templates
│ ├── manifest.json
│ ├── installment-agreement-v1.docx
│ ├── installment-agreement-v2.docx
│ ├── installment-agreement-v3.docx
│ ├── installment-agreement-v4.docx
│ ├── judicial-installment-agreement-v1.docx
│ ├── judicial-installment-agreement-v2.docx
│ └── judicial-installment-agreement-v3.docx
├── scripts
│ ├── create_default_template.py
│ ├── create_judicial_template.py
│ ├── patch_template_conditionals.mjs
│ └── generate-sample-document.js
└── test
├── archive-export.test.js
├── csv-export.test.js
├── document-context.test.js
├── document-export.test.js
├── document-generation.test.js
├── format.test.js
├── number-to-words.test.js
├── proportional-contracts.test.js
├── readme-page.test.js
└── schedule.test.js
Назначение файлов
index.html
Содержит всю семантическую разметку:
- переключатель досудебной и судебной стадии;
- раздел должника;
- судебный раздел с реквизитами суда, судьи и представителя истца;
- раздел договоров банковского займа;
- параметры рассрочки;
- равный и сложный расчёт;
- панель действий с формированием Word/PDF, ZIP-архива, отправкой письма, выбором CSV-разделителя и полной очисткой данных;
- футер со ссылкой на документацию;
- диалог просмотра графика.
В проекте нет шаблонизатора интерфейса и компонентного фреймворка. Динамические
карточки и строки особых платежей создаются средствами DOM в src/app.js.
src/styles.css
Содержит:
- цветовую систему и фон;
- общую компоновку страницы;
- карточки разделов;
- стили полей, кнопок и переключателей;
- компактные карточки должника и договоров;
- единое оформление подтверждённых карточек разделов
01–04; - янтарное оформление договора, ожидающего подтверждения параметров;
- адаптивные правила для ширины до
760px; - стили диалога графика и таблиц.
Основные визуальные уровни:
- фон страницы;
- белые секции
form-section; - светлые поля
field; - подтверждённые карточки зелёного оттенка без акцентной полосы слева;
- неподтверждённые договоры янтарного оттенка без акцентной полосы слева.
src/app.js
Оркестрирует работу интерфейса:
- находит DOM-элементы;
- хранит состояние должника и договоров;
- открывает и закрывает формы;
- нормализует ввод;
- переключает режимы расчёта;
- синхронизирует настройки выбранного договора;
- строит карточки договоров;
- показывает графики;
- собирает JSON для формирования файлов;
- запрашивает документы и архивы у сервера;
- открывает системную отправку письма или резервный сценарий через
mailto:; - переключает русский и казахский интерфейс, а также светлую и тёмную темы.
src/about.css
Оформляет страницу документации: типографику README, таблицы, блоки кода, цитаты, закреплённую навигацию и мобильное представление.
src/i18n.js
Содержит словарь русского и казахского интерфейса, переключение языка без
перезагрузки и локализацию динамически создаваемых элементов. Страница
документации /about намеренно остаётся русскоязычной.
src/domain/format.js
Чистые функции форматирования:
parseAmount— преобразует текстовую сумму в число;formatInputValue— форматирует денежное поле;normalizePersonName— окончательно нормализует Ф.И.О.;normalizeAddress— очищает пробелы в адресе;sanitizePersonNameInput— удаляет недопустимые символы;capitalizePersonNameInput— нормализует регистр во время ввода;maskBirthDateInput— формирует маскуДД.ММ.ГГГГ;parseMaskedBirthDate— проверяет дату и возвращает ISOГГГГ-ММ-ДД;parseMaskedPastOrTodayDate— дополнительно запрещает будущую дату договора;formatMaskedDate— переводит ISO-дату обратно вДД.ММ.ГГГГдля поля;monthLabel— возвращает компактное обозначение срокамес.;addMonths— добавляет месяцы с корректировкой последнего дня;toDateValue— переводитDateвГГГГ-ММ-ДД.
src/domain/schedule.js
Чистая расчётная логика:
calculateEqualSchedule;calculateComplexSchedule;calculateProportionalSchedules;combineContractSchedules.
Файл не зависит от DOM и используется как браузером, так и серверным генератором документов.
server/index.js
Минимальный HTTP-сервер на стандартном модуле node:http.
Обязанности:
- отдаёт статические файлы;
- запрещает выход за корень проекта;
- устанавливает MIME-типы;
- отключает кэширование через
Cache-Control: no-store; - принимает
POST /api/documents; - принимает
POST /api/exports/csv; - принимает
POST /api/exports/archive; - ограничивает JSON-запрос размером 1 МБ;
- возвращает DOCX, PDF, CSV, ZIP или JSON с ошибкой;
- по умолчанию слушает
127.0.0.1, а переменныеHOSTиPORTпозволяют изменить адрес и порт прослушивания.
server/generate-document.js
Конвейер Word-генерации:
- читает
manifest.json; - выбирает шаблон;
- пересчитывает график каждого договора;
- строит общий график;
- получает плоский контекст из
document-context.js; - открывает DOCX как ZIP через PizZip;
- передаёт контекст в Docxtemplater;
- возвращает готовый DOCX как
Buffer.
server/generate-document-export.js
Выбирает формат итогового документа. Для docx возвращает результат
generate-document.js без преобразований. Для pdf передаёт тот же буфер в
convert-document-to-pdf.js и возвращает PDF с соответствующим MIME-типом.
server/convert-document-to-pdf.js
Создаёт изолированный временный каталог и отдельный профиль LibreOffice,
записывает туда сгенерированный DOCX, запускает headless-конвертацию и читает
готовый PDF. Временные файлы удаляются в finally. Исполняемый файл выбирается
из SOFFICE_PATH, иначе используется команда soffice из PATH.
server/calculate-contracts.js
Единый серверный адаптер расчёта. Он:
- синхронизирует сумму долга договора и параметры рассрочки;
- повторно рассчитывает каждый индивидуальный график;
- повторно рассчитывает общий пропорциональный график, если выбран общий режим;
- рассчитывает общий график;
- используется как Word-генератором, так и CSV-экспортом.
server/generate-csv-export.js
Генерирует CSV-файлы без временных файлов:
- использует UTF-8 с BOM для корректного открытия кириллицы в Excel;
- поддерживает переключатель разделителя
;/,; по умолчанию выбран;; - для
;использует десятичную запятую, для,— десятичную точку; - записывает ИИН как безопасное текстовое значение, чтобы Excel не переводил 12 цифр в научную запись;
- после ИИН записывает номер договора банковского займа; в общем графике —
номера всех включённых договоров через
/; - выводит даты в формате
ДД.ММ.ГГГГ; - выводит платёж и остаток с двумя десятичными знаками;
- при одном договоре возвращает один CSV;
- при двух и более договорах создаёт отдельный CSV для каждого договора и
упаковывает их в
{ИИН}-Schedules.zip; - общий CSV добавляется при двух и более договорах;
- формирует безопасные имена файлов.
server/generate-archive.js
Собирает итоговый пакет по кнопке «Скачать архив»:
- формирует DOCX или PDF из шаблона активной стадии;
- формирует CSV-графики повторным серверным расчётом;
- при одном договоре кладёт в архив один индивидуальный график;
- при двух и более договорах кладёт все индивидуальные графики и общий;
- возвращает единый
{ИИН}-{ДД-ММ-ГГГГ}.zipбез промежуточных файлов на диске.
server/document-context.js
Адаптер между внутренней моделью и Word-шаблоном.
Именно здесь:
- повторно нормализуются Ф.И.О.;
- валидируется ИИН;
- форматируются даты;
- форматируются суммы;
- формируется текст о договорах;
- назначаются номера приложений;
- создаются строки индивидуальных и общего графиков;
- формируется полный набор полей для DOCX.
Если в Word нужно добавить новое вычисляемое поле, обычно сначала требуется добавить его именно здесь.
server/templates/manifest.json
Реестр доступных Word-шаблонов.
server/number-to-words.js
Преобразует сумму в русскую и казахскую запись прописью без названия валюты.
Используется для {total_debt_amount_words}, {debt_amount_words} и их
казахских аналогов с окончанием _kz.
server/render-readme.js
Читает README.md, скрывает встроенный блок содержания, преобразует Markdown
в HTML и добавляет идентификаторы заголовков, закреплённое оглавление, поиск с
переходом к найденному тексту и переключатель темы для страницы /about.
scripts/create_default_template.py
Создаёт технический DOCX с помощью python-docx. Фиксирует:
- размеры страницы;
- поля;
- стили заголовков;
- шрифты и цвета;
- таблицы;
- служебные метки Docxtemplater;
- структуру приложений.
Скрипт полезен как воспроизводимый пример, но не должен бездумно запускаться поверх юридически отредактированного шаблона.
scripts/generate-sample-document.js
Формирует документ без браузера. Используется для ручной проверки шаблона.
test
Набор автоматических тестов:
- контекст документа;
- фактическая подстановка меток в DOCX;
- нормализация данных;
- равные и сложные графики;
- объединение нескольких договоров.
- пропорциональное распределение в целых тенге;
- преобразование сумм прописью;
- формирование HTML-страницы из README.
8. Зависимости
Runtime-зависимости
| Пакет | Зафиксированная версия | Назначение |
|---|---|---|
docxtemplater |
3.69.3 |
Подстановка данных и обработка циклов внутри DOCX |
marked |
16.2.1 |
Преобразование актуального README в HTML для /about |
pizzip |
3.2.0 |
Чтение и сборка DOCX как ZIP-архива |
Версии в package.json заданы через ^, а конкретные установленные версии
фиксируются package-lock.json.
Что не используется
- React/Vue/Svelte;
- Express/Fastify;
- база данных;
- ORM;
- серверный шаблонизатор HTML;
- библиотека CSV;
- внешние API;
- облачное хранилище.
9. Модель данных в браузере
Должник
После нажатия «Добавить» формируется debtorRecord:
{
lastName: "Иванов",
firstName: "Иван",
middleName: "Иванович",
iin: "900512300123",
address: "г. Алматы, ул. Абая, д. 10",
phone: "87001234567"
}
Судебное дело
После нажатия «Добавить» в судебном разделе формируется
judicialCaseRecord:
{
courtName: "Районный суд № 2",
judgeNameInitials: "Петров П.П."
}
Представитель истца хранится отдельно для совместимости с Word-контекстом:
plaintiffInfoRecord = {
name: "",
representative: "Сидоров С.С."
}
Все три видимых значения могут быть пустыми. В запрос они передаются объектами
judicialCase и plaintiffInfo, а сервер заменяет пустые строки понятными
русскими и казахскими редакционными подсказками. Поле name сейчас не
заполняется через интерфейс, но сохраняется в модели для совместимости с
шаблонными тегами {plaintiff_name} и {plaintiff_name_kz}.
Договор
Каждый договор находится в массиве contracts:
{
id: 1,
number: "01-2026",
date: "2026-08-03",
debtAmount: 250000,
confirmed: false,
settings: {
calculationType: "equal",
calculationMode: "amount",
equal: {
amount: 250000,
months: 0,
paymentAmount: 0,
paymentDebt: 250000,
startDate: "",
advanceAmount: 0,
advanceDate: ""
},
complex: {
debt: 250000,
months: 0,
startDate: "",
advanceAmount: 0,
advanceDate: "",
remainderMode: "all",
conditions: []
}
}
}
Почему в договоре хранятся оба режима
В settings.equal сохраняются значения обоих подрежимов:
- расчёт по полной сумме;
- расчёт по размеру платежа.
При переключении пользователь не теряет уже введённые значения.
Выбор договора
selectedContractId определяет, настройки какого договора открыты в разделе
«Параметры рассрочки».
Перед переключением договора вызывается persistSelectedContractSettings().
После выбора нового договора вызывается applyContractSettings().
Подтверждение
Поле confirmed означает, что пользователь завершил настройку договора.
Перед подтверждением проверяются:
- положительная сумма;
- дата платежа по графику;
- срок или размер платежа;
- наличие необходимых параметров режима.
После редактирования подтверждённого договора флаг снова становится false.
До добавления договора раздел параметров не показывает лишнее пояснение.
После добавления в его заголовке отображается номер и дата выбранного
договора.
Общие параметры нескольких договоров
Общий режим хранится отдельно от индивидуальных настроек договоров:
jointSettings = {
months: 12,
payment: 100000,
startDate: "2026-08-03",
advanceAmount: 100000,
advanceDate: "2026-08-02",
confirmed: true
}
При подтверждении каждый участвующий договор получает признак joint: true.
В запрос к серверу дополнительно передаётся jointInstallment:
{
enabled: true,
months: 12,
payment: 100000,
startDate: "2026-08-03",
advanceAmount: 100000,
advanceDate: "2026-08-02"
}
Сервер не доверяет готовым строкам интерфейса и повторно формирует общий и
индивидуальные графики через calculateProportionalSchedules.
10. Расчёт графиков
Равные платежи по сроку
Вход:
{
amount: 250000,
months: 12,
startDate: "2026-08-03",
mode: "amount"
}
Алгоритм:
- сумма переводится в целые сотые;
- обычный платёж вычисляется целочисленным делением;
- остаток округления добавляется в последний платёж;
- последний баланс всегда должен стать равным нулю.
Равные платежи по размеру платежа
Вход:
{
amount: 300000,
payment: 25000,
startDate: "2026-08-03",
mode: "payment"
}
Срок:
ceil(сумма долга / размер платежа)
Последний платёж уменьшается до фактического остатка.
Даты платежей
addMonths старается сохранить число первого платежа.
Пример:
- первый платёж — 31 января;
- следующий месяц не содержит 31-го числа;
- дата будет ограничена последним днём февраля.
Общий пропорциональный расчёт
Вход:
{
contracts: [
{ id: 1, debt: 800000 },
{ id: 2, debt: 200000 }
],
months: 3,
payment: 100000,
startDate: "2026-08-03"
}
Для долгов 800 000 и 200 000 обычный общий платёж 100 000 делится как
80 000 и 20 000. В последний месяц функция направляет на погашение весь
оставшийся долг. Поэтому при сроке три месяца общий ряд платежей будет
100 000, 100 000, 800 000.
Алгоритм работает в целых тенге:
- округляет долг каждого договора и общий регулярный платёж до целого тенге;
- определяет долю каждого договора в общем долге;
- распределяет платёж пропорционально этим долям;
- распределяет остаток методом наибольших остатков так, чтобы сумма частей точно совпала с общим платежом;
- не позволяет платежу по договору превысить его остаток;
- при необходимости перераспределяет свободную часть между другими договорами;
- в последнем месяце обнуляет все остатки.
Сложный расчёт
Условие конкретного месяца:
{
kind: "date",
startDate: "2026-08-03",
endDate: "",
amount: 25000
}
Условие периода:
{
kind: "period",
startDate: "2026-08-03",
endDate: "2026-11-03",
amount: 25000
}
Для периода одна и та же сумма назначается каждому месяцу диапазона.
remainderMode: "all"
Остаток долга распределяется равными частями между месяцами, не занятыми особыми условиями.
remainderMode: "manual"
По обычным месяцам создаются строки с платежом 0. Нераспределённая сумма
остаётся в балансе.
Конфликты особых условий
Интерфейс ограничивает доступные месяцы:
- месяц, уже занятый конкретным платежом, не предлагается повторно;
- месяцы выбранного периода исключаются из других условий;
- конечный месяц периода не может быть раньше начального;
- список месяцев строится от даты платежа по графику и срока.
Общий график
combineContractSchedules:
- собирает все уникальные даты;
- суммирует платежи одной даты;
- вычисляет суммарный остаток по всем договорам;
- учитывает долг договора до наступления его первого платежа.
11. CSV-экспорт
Браузер отправляет текущую модель на POST /api/exports/csv. Сервер заново
рассчитывает графики и формирует CSV-файлы.
Столбцы:
Номер;ИИН;Договор;Дата;Платеж;Остаток.
Особенности:
- кодировка — UTF-8 с BOM;
- разделитель столбцов выбирается пользователем:
;по умолчанию либо,; - дата записывается в формате
ДД.ММ.ГГГГ; - платёж и остаток записываются с двумя десятичными знаками: с десятичной
запятой для разделителя
;и с десятичной точкой для разделителя,; - сервер не принимает готовый график от браузера, а повторно рассчитывает его общей доменной функцией.
Если добавлен один договор, скачивается один файл:
{ИИН}-{PRE|COURT}-Schedule-{НОМЕР ДОГОВОРА}.csv
Префикс PRE используется на досудебной стадии, COURT — на судебной.
Если добавлено два или более договоров, скачивается ZIP-архив:
CSV-архив называется
{ИИН}-Schedules.zip. В него входят:
- по одному файлу
{ИИН}-{PRE|COURT}-Schedule-{НОМЕР ДОГОВОРА}.csvдля каждого договора.
При двух и более договорах в архив дополнительно включается
{ИИН}-COMBINED-Schedule.csv.
Итоговый ZIP
Кнопка «Скачать архив» формирует единый пакет документов. Имя архива:
{ИИН}-{ДД-ММ-ГГГГ}.zip
Например:
900512300123-05-08-2026.zip
На досудебной стадии документ называется {ИИН}-PRE-Obligation, на судебной —
{ИИН}-COURT-Mediation. Расширение .docx или .pdf зависит от выбранного
формата.
Если договор один, архив содержит:
- документ выбранной стадии в выбранном формате;
{ИИН}-{PRE|COURT}-Schedule-{НОМЕР ДОГОВОРА}.csv.
Если договоров два или более, архив содержит:
- документ выбранной стадии в выбранном формате;
- отдельный
{ИИН}-{PRE|COURT}-Schedule-{НОМЕР ДОГОВОРА}.csvпо каждому договору; {ИИН}-COMBINED-Schedule.csv.
12. HTTP API
GET /about
Возвращает оформленную HTML-страницу «Документация проекта». Сервер при каждом
запросе читает текущий README.md и преобразует его через marked, поэтому
страница обновляется после обычного обновления браузера и не требует сборки.
POST /api/documents
Создаёт Word- или PDF-документ. Сначала сервер всегда заполняет DOCX-шаблон. Если выбран PDF, готовый DOCX конвертируется через LibreOffice, поэтому текст, условные разделы и приложения в обоих форматах идентичны.
Заголовок:
Content-Type: application/json
Пример запроса:
{
"stage": "judicial",
"templateId": "judicial-installment-agreement-v3",
"documentFormat": "pdf",
"csvDelimiter": ";",
"judicialCase": {
"courtName": "Районный суд № 2",
"judgeNameInitials": "Петров П.П."
},
"plaintiffInfo": {
"name": "",
"representative": "Сидоров С.С."
},
"debtor": {
"lastName": "Иванов",
"firstName": "Иван",
"middleName": "Иванович",
"iin": "900512300123",
"address": "г. Алматы, ул. Абая, д. 10",
"phone": "87001234567"
},
"contracts": [
{
"number": "01-2026",
"date": "2026-08-03",
"debtAmount": 250000,
"confirmed": true,
"installment": {
"type": "equal",
"mode": "amount",
"debt": 250000,
"months": 12,
"payment": 0,
"startDate": "2026-08-03",
"advanceAmount": 0,
"advanceDate": ""
}
}
]
}
Допустимые значения documentFormat: docx и pdf. Если поле отсутствует,
используется docx. Значение csvDelimiter не влияет на документ и
применяется только к CSV-файлам.
Допустимые стадии: pretrial и judicial. Сервер связывает стадию с нужным
шаблоном и отклоняет несовместимую пару stage/templateId. Объект
judicialCase и plaintiffInfo необязательны; вместо пустых реквизитов сервер
передаёт в русские и казахские теги шаблона соответствующие редакционные
подсказки. Предупреждение перед формированием неполного судебного PDF является
клиентской защитой интерфейса; прямой API-запрос не показывает диалог.
Успешный ответ:
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Content-Disposition: attachment; filename*=UTF-8''900512300123-PRE-Obligation.docx
Для PDF:
Content-Type: application/pdf
Content-Disposition: attachment; filename*=UTF-8''900512300123-COURT-Mediation.pdf
Ошибка:
{
"error": "Описание ошибки"
}
HTTP-статус ошибок генерации — 400.
Перед отправкой браузер требует:
- подтверждённого должника с корректным ИИН;
- хотя бы одного договора;
- номера и даты у каждого договора;
- подтверждённых параметров рассрочки у всех договоров.
Сервер не доверяет переданному графику: он заново рассчитывает каждый
индивидуальный график общей функцией из src/domain/schedule.js, затем строит
сводный график и только после этого формирует Word. Имя скачиваемого файла
зависит от стадии:
{ИИН}-PRE-Obligation.docx
{ИИН}-COURT-Mediation.docx
При выборе PDF расширение в соответствующем имени меняется на .pdf.
POST /api/exports/csv
Принимает ту же структуру должника, договоров и параметров рассрочки, что и
POST /api/documents.
Для одного договора успешный ответ:
Content-Type: text/csv; charset=utf-8
Content-Disposition: attachment; filename*=UTF-8''...
Для нескольких договоров успешный ответ:
Content-Type: application/zip
Content-Disposition: attachment; filename*=UTF-8''...
Имя ZIP с графиками — {ИИН}-Schedules.zip. Внутри находятся отдельные
графики договоров и {ИИН}-COMBINED-Schedule.csv.
Графики всегда повторно рассчитываются на сервере. Ошибка валидации или
расчёта возвращается с HTTP-статусом 400:
{
"error": "Описание ошибки"
}
POST /api/exports/archive
Принимает ту же JSON-модель, что и остальные операции формирования файлов. Успешный ответ:
Content-Type: application/zip
Content-Disposition: attachment; filename*=UTF-8''...
Имя итогового архива — {ИИН}-{ДД-ММ-ГГГГ}.zip.
Сервер независимо формирует Word и CSV, затем помещает результаты в один
архив. Ошибки валидации или генерации возвращаются с HTTP-статусом 400.
13. Конвейер формирования Word
sequenceDiagram
participant B as Браузер
participant S as HTTP-сервер
participant G as generate-document.js
participant C as document-context.js
participant W as DOCX-шаблон
B->>S: POST /api/documents
S->>G: payload
G->>G: расчёт каждого графика
G->>G: объединение графиков
G->>C: данные + рассчитанные графики
C-->>G: контекст Word
G->>W: открыть через PizZip
G->>G: Docxtemplater.render(context)
G-->>S: Buffer DOCX
S-->>B: файл .docx
Параметры Docxtemplater:
{
paragraphLoop: true,
linebreaks: true,
nullGetter: () => ""
}
paragraphLoopкорректно обрабатывает циклы, вынесенные в отдельные абзацы;linebreaksпревращает переносы строк в Word-переносы;nullGetterзаменяет отсутствующие значения пустой строкой.
14. Система Word-шаблонов
Расположение
Активные шаблоны:
server/templates/installment-agreement-v4.docx
server/templates/judicial-installment-agreement-v3.docx
Предыдущие версии сохранены рядом как история юридических редакций и точки восстановления.
Реестр:
server/templates/manifest.json
Текущий manifest:
{
"defaultTemplateId": "installment-agreement-v4",
"templates": [
{
"id": "installment-agreement-v1",
"version": 1,
"name": "Обязательство — сохранённая редакция пользователя",
"file": "installment-agreement-v1.docx"
},
{
"id": "installment-agreement-v2",
"version": 2,
"name": "Обязательство — рабочая версия с условными разделами",
"file": "installment-agreement-v2.docx"
},
{
"id": "installment-agreement-v3",
"version": 3,
"name": "Обязательство — предыдущая двуязычная версия",
"file": "installment-agreement-v3.docx"
},
{
"id": "installment-agreement-v4",
"version": 4,
"name": "Обязательство — вычитанная и проверенная версия",
"file": "installment-agreement-v4.docx"
},
{
"id": "judicial-installment-agreement-v1",
"version": 1,
"name": "Медиативное соглашение — предыдущая версия судебной стадии",
"file": "judicial-installment-agreement-v1.docx"
},
{
"id": "judicial-installment-agreement-v2",
"version": 2,
"name": "Медиативное соглашение — предыдущая версия судебной стадии",
"file": "judicial-installment-agreement-v2.docx"
},
{
"id": "judicial-installment-agreement-v3",
"version": 3,
"name": "Медиативное соглашение — рабочая версия судебной стадии",
"file": "judicial-installment-agreement-v3.docx"
}
]
}
Как выбирается шаблон
- Клиент передаёт
templateId. generate-document.jsчитает manifest.- Если
templateIdне передан, используетсяdefaultTemplateId. - Сервер ищет запись с соответствующим
id. - Путь к файлу разрешается только внутри
server/templates.
Сейчас клиент выбирает шаблон из конфигурации стадии:
const documentStages = {
pretrial: {
templateId: "installment-agreement-v4",
},
judicial: {
templateId: "judicial-installment-agreement-v3",
},
};
Рабочий шаблон судебного документа зарегистрирован по пути:
server/templates/judicial-installment-agreement-v3.docx
Это отдельный документ «Медиативное соглашение». Он не заменяет
досудебное обязательство и выбирается только для стадии judicial.
Откройте указанный DOCX непосредственно в Microsoft Word или LibreOffice Writer. В нём используются два вида обозначений:
{full_name},{iin},{contracts_text}и другие метки в фигурных скобках — рабочие переменные приложения; их нельзя переименовывать без синхронного измененияserver/document-context.js;- для русской части доступны
{court_name},{judge_name_initials}и{plaintiff_representative}, а для казахской — одноимённые теги с окончанием_kz; - для суммы прописью доступны
{total_debt_amount_words}и вложенная в цикл договоров{debt_amount_words}; их казахские аналоги —{total_debt_amount_words_kz}и{debt_amount_words_kz}; - для перечисления договоров доступны русская
{contracts_text}и казахская{contracts_text_kz}версии; - дата документа доступна в русском
{document_date}и казахском{document_date_kz}форматах; - обозначение валюты не входит в денежные переменные и задаётся непосредственно текстом шаблона.
Исходный скрипт, которым создана болванка, находится в
scripts/create_judicial_template.py. Повторная команда
python3 scripts/create_judicial_template.py --force
перезапишет DOCX и уничтожит ручные изменения, сделанные в Word. Обычно
редактировать нужно сам файл .docx, а скрипт использовать только для
осознанного восстановления технической версии или изменения её структуры.
Простого изменения defaultTemplateId недостаточно: клиент всегда передаёт
явный templateId, выбранный по стадии.
15. Синтаксис меток в DOCX
Метки Docxtemplater — это обычный текст Word. Их нужно напечатать с клавиатуры или вставить как обычный текст. Это не:
- поле слияния Word;
- элемент управления содержимым;
- закладка;
- формула;
- поле, создаваемое через
Ctrl+F9.
Например, в документе должно буквально находиться {full_name}.
Обычное поле
{full_name}
Цикл
{#contracts}
...
{/contracts}
Условный раздел
{#has_multiple_contracts}
Этот текст и расположенные здесь таблицы появятся только при двух и более
договорах.
{/has_multiple_contracts}
В рабочем installment-agreement-v4.docx таким условием закрыты:
- пункт 3.2 о сводном графике;
- разрыв страницы перед сводным приложением;
- заголовок, пояснение и таблица сводного приложения.
При одном договоре эти элементы полностью отсутствуют в итоговом DOCX.
Вложенный цикл
{#contracts}
Договор № {contract_number}
{#schedule}
{number} | {date} | {payment} | {balance}
{/schedule}
{/contracts}
Внутри schedule имена number, date, payment, balance относятся к
строке текущего графика. После закрытия {/schedule} контекст возвращается
к текущему договору.
Область видимости важна:
глобальный документ
├── contracts[]
│ └── schedule[]
└── combined_schedule[]
- глобальные поля разрешены в любом месте документа;
- поля договора доступны только внутри
{#contracts}...{/contracts}; - поля индивидуального графика доступны внутри вложенного
{#schedule}...{/schedule}; - поля общего графика доступны внутри
{#combined_schedule}...{/combined_schedule}.
16. Поля, передаваемые в Word
Ниже перечислен фактический контекст, создаваемый
server/document-context.js.
Глобальные поля
| Метка | Тип | Пример | Описание |
|---|---|---|---|
{last_name} |
строка | Иванов |
Фамилия |
{first_name} |
строка | Иван |
Имя |
{middle_name} |
строка | Иванович |
Необязательное отчество; может быть пустым |
{full_name} |
строка | Иванов Иван Иванович |
Собранное Ф.И.О. |
{full_name_initials} |
строка | Иванов И.И. |
Фамилия и инициалы; при отсутствии отчества — Иванов И. |
{full_name_upper} |
строка | ИВАНОВ ИВАН ИВАНОВИЧ |
Ф.И.О. прописными буквами для юридического текста |
{iin} |
строка | 900512300123 |
ИИН из 12 цифр |
{address} |
строка | г. Алматы, ул. Абая, д. 10 |
Необязательный нормализованный адрес; может быть пустым |
{phone} |
строка | 87001234567 |
Необязательный телефон из 10 или 11 цифр без форматирования |
{court_name} |
строка | Районный суд № 2 |
Необязательное наименование суда; если поле пустое — Указать наименование суда |
{court_name_kz} |
строка | Районный суд № 2 |
Наименование суда для казахской части; если поле пустое — Соттың атауын көрсету |
{judge_name_initials} |
строка | Петров П.П. |
Необязательные фамилия и инициалы судьи; если поле пустое — Указать Фамилию и Инициалы судьи |
{judge_name_initials_kz} |
строка | Петров П.П. |
Судья для казахской части; если поле пустое — Судьяның тегі мен аты-жөнін көрсету |
{plaintiff_name} |
строка | ТОО «Истец» |
Необязательное наименование истца; если поле пустое — Указать наименование истца |
{plaintiff_name_kz} |
строка | ТОО «Истец» |
Наименование истца для казахской части; если поле пустое — Талапкердің атауын көрсету |
{plaintiff_representative} |
строка | Сидоров С.С. |
Необязательный представитель истца; если поле пустое — Указать представителя истца |
{plaintiff_representative_kz} |
строка | Сидоров С.С. |
Представитель истца для казахской части; если поле пустое — Талапкердің өкілін көрсету |
{document_city} |
строка | г. Караганда |
Город подписания, заданный в серверном контексте |
{document_date} |
строка | 05 августа 2026 |
Текущая дата формирования документа |
{document_date_kz} |
строка | 2026 жылғы 05 тамыз |
Текущая дата формирования документа для казахской части |
{contracts_text} |
строка | № 01-2026 от 03.08.2026; № 02-2026 от 03.09.2026 |
Только номера и даты; слово «договор» должно находиться в тексте шаблона |
{contracts_text_kz} |
строка | 03.08.2026 күнгі № 01-2026; 03.09.2026 күнгі № 02-2026 |
Номера и даты договоров в казахском порядке |
{contracts_count} |
число | 2 |
Количество договоров |
{has_multiple_contracts} |
логическое | true |
true только при двух и более договорах; используется как условная секция |
{total_debt_amount} |
строка | 550 000,00 |
Сумма синхронизированного contract.debtAmount всех договоров, без обозначения валюты |
{total_debt_amount_words} |
строка | пятьсот пятьдесят тысяч |
Общая сумма прописью, без обозначения валюты |
{total_debt_amount_words_kz} |
строка | бес жүз елу мың |
Общая сумма прописью на казахском, без обозначения валюты |
{combined_appendix_number} |
число | 3 |
Номер сводного приложения |
{contracts} |
массив | — | Договоры для цикла |
{schedule} |
массив | — | График для судебного документа: индивидуальный при одном договоре, общий при нескольких |
{combined_schedule} |
массив | — | Строки общего графика; при одном договоре всегда пустой массив |
Поля внутри {#contracts}
| Метка | Тип | Пример | Описание |
|---|---|---|---|
{appendix_number} |
число | 1 |
Номер приложения договора |
{contract_number} |
строка | 01-2026 |
Номер договора |
{contract_date} |
строка | 03.08.2026 |
Дата договора |
{calculation_type} |
строка | Равными платежами |
Вид расчёта |
{debt_amount} |
строка | 250 000,00 |
Отформатированная сумма рассрочки, без обозначения валюты |
{debt_amount_words} |
строка | двести пятьдесят тысяч |
Сумма договора прописью, без обозначения валюты |
{debt_amount_words_kz} |
строка | екі жүз елу мың |
Сумма договора прописью на казахском, без обозначения валюты |
{debt_value} |
число | 250000 |
Та же сумма без форматирования |
{term_months} |
строка | 12 |
Срок |
{first_payment_date} |
строка | 03.08.2026 |
Дата первой строки графика |
{advance_payment_amount} |
строка | 100 000,00 |
Сумма авансового платежа без обозначения валюты; 0,00, если аванса нет |
{advance_payment_date} |
строка | 13.08.2026 |
Дата авансового платежа; пустая строка, если аванса нет |
{has_advance_payment} |
логическое | true |
Признак наличия авансового платежа для условного блока шаблона |
{schedule} |
массив | — | График текущего договора |
Поля внутри {#schedule}
| Метка | Тип | Пример | Описание |
|---|---|---|---|
{number} |
число | 1 |
Порядковый номер строки |
{date} |
строка | 03.08.2026 |
Дата платежа |
{payment} |
строка | 20 833,33 |
Платёж без символа тенге |
{balance} |
строка | 229 166,67 |
Остаток без символа тенге |
{is_advance} |
логическое | true |
true только для отдельной строки авансового платежа |
Поля внутри {#combined_schedule}
| Метка | Тип | Пример | Описание |
|---|---|---|---|
{number} |
число | 1 |
Порядковый номер строки |
{date} |
строка | 03.08.2026 |
Дата общего платежа |
{payment} |
строка | 45 833,33 |
Суммарный платёж |
{balance} |
строка | 529 166,67 |
Суммарный остаток |
Метки, используемые текущим шаблоном
В активном досудебном шаблоне installment-agreement-v4.docx используются:
{#combined_schedule}
{#contracts}
{#has_multiple_contracts}
{#schedule}
{/combined_schedule}
{/contracts}
{/has_multiple_contracts}
{/schedule}
{appendix_number}
{address}
{balance}
{combined_appendix_number}
{contract_date}
{contract_number}
{contracts_count}
{contracts_text_kz}
{contracts_text}
{date}
{debt_amount}
{document_date_kz}
{document_date}
{full_name_initials}
{full_name}
{iin}
{number}
{payment}
{phone}
{total_debt_amount_words_kz}
{total_debt_amount_words}
{total_debt_amount}
В активном судебном шаблоне judicial-installment-agreement-v3.docx
используются верхнеуровневый цикл {#schedule} и реквизиты суда, судьи,
представителя истца, даты, перечня договоров и суммы на двух языках.
Остальные поля из таблиц выше уже передаются в контекст и могут быть добавлены в Word без изменения JavaScript, если соблюдена область видимости тегов.
17. Текущая компоновка Word-шаблонов
Досудебное обязательство
Шаблон installment-agreement-v4 содержит:
- заголовок «Обязательство о добровольном погашении суммы долга»;
- город и автоматически сформированную дату документа;
- юридический текст с Ф.И.О., ИИН, перечнем договоров и общей суммой долга;
- нумерованные обязательства должника;
- адрес, телефон и строку подписи должника;
- цикл приложений по договорам;
- таблицу индивидуального графика внутри каждого приложения;
- последнее приложение со сводным графиком по всем договорам.
Технические параметры, задаваемые Python-скриптом:
- размер страницы — A4,
210 × 297мм; - поля —
16–20мм; - основной шрифт — Times New Roman;
- основной текст — чёрный, с выравниванием по ширине;
- юридические положения оформлены настоящей многоуровневой нумерацией Word;
- таблицы с фиксированной шириной;
- повторение заголовка таблицы на новых страницах;
- автоматические номера приложений.
Судебное медиативное соглашение
Шаблон judicial-installment-agreement-v3 содержит:
- соглашение об урегулировании спора в порядке медиации;
- сведения об истце, ответчике, суде и судье;
- перечень договоров и общую сумму долга, в том числе сумму прописью;
- один график платежей: индивидуальный при одном договоре или общий при нескольких договорах;
- заявление об утверждении соглашения и прекращении производства по делу.
Суд, судья и представитель истца заполняются через {court_name},
{judge_name_initials} и {plaintiff_representative}. Если поля интерфейса
оставлены пустыми, выводятся соответствующие редакционные подсказки. Для
казахской части используются теги {court_name_kz},
{judge_name_initials_kz} и {plaintiff_representative_kz}. Дата документа
разделена на {document_date} и {document_date_kz}.
18. Как правильно изменить существующий Word-шаблон
Что можно менять без изменения JavaScript
В Word можно свободно менять:
- юридический текст;
- заголовки и нумерацию;
- шрифты, интервалы, отступы и выравнивание;
- колонтитулы;
- поля и формат страницы;
- ширину столбцов, границы и заливку таблиц;
- расположение уже существующих меток;
- подписи к данным, например «Сумма задолженности» вместо «Сумма»;
- порядок глобальных полей;
- разрывы страниц между приложениями.
JavaScript не нужно менять, пока используются поля из раздела 16 и сохраняется правильная вложенность циклов.
Что требует изменения кода
Изменение кода потребуется, если нужно:
- передать новое поле, которого нет в разделе 16;
- изменить формат суммы или даты;
- изменить готовый
{contracts_text}; - поменять правила нумерации приложений;
- исключить общий график или изменить его расчёт;
- поддержать выбор шаблона пользователем в интерфейсе.
Порядок добавления нового поля описан в разделе 21.
Самый безопасный сценарий: изменить только текст
Зафиксировать рабочее состояние:
npm run check npm run sampleСкопировать
server/templates/installment-agreement-v2.docxв новый файлv3и редактировать только новую версию.Открыть рабочий
.docxв Microsoft Word.Отключить «Исправления»/Track Changes. Если исправления уже есть, принять или отклонить их до итогового сохранения.
Включить отображение непечатаемых знаков
¶. Так легче увидеть пустые абзацы, разрывы страниц и границы циклов.Изменить юридические формулировки, не затрагивая метки и строки циклов.
Сохранить файл именно как Документ Word (
.docx). Не использовать.doc,.docm,.dotx, PDF или формат Google Docs.Сформировать новый образец:
npm run sampleОткрыть
.tmp/document-qa/sample-obligation-multiple.docxв Word и проверить визуально.Сформировать и открыть однодоговорный вариант:
node scripts/generate-sample-document.js --singleВ нём не должно быть пункта 3.2, сводного приложения и лишней пустой страницы.
Запустить:
npm run checkЗафиксировать изменение шаблона отдельным Git-коммитом.
Останавливать сервер для редактирования необязательно. Каждый новый запрос читает DOCX с диска заново.
Правила работы с метками
Сохраняйте обе обычные фигурные скобки.
Не переводите имя метки и не меняйте регистр.
Не добавляйте пробелы внутрь:
правильно: {full_name} неправильно: { full_name } неправильно: {Full_Name}Вставляйте метку целиком как обычный текст.
После вставки выделяйте и форматируйте метку целиком. Не делайте одну её часть жирной, а другую обычной.
Не вставляйте внутрь метки перенос строки, табуляцию, комментарий, сноску или гиперссылку.
Не используйте визуально похожие скобки
{ }.Не размещайте открывающий и закрывающий тег цикла в колонтитуле и основной части одновременно.
Не удаляйте тег только потому, что его не должно быть видно: после генерации все корректные служебные теги исчезают автоматически.
Word хранит текст частями — runs. Визуально цельная метка иногда оказывается
разделена на несколько таких частей из-за форматирования, режима исправлений
или вставки из другого документа. Docxtemplater умеет работать со многими
такими случаями, но самый надёжный способ исправления — удалить проблемную
метку и напечатать её заново одним фрагментом.
Как перемещать поле
- Выделить всю метку вместе со скобками.
- Вырезать её.
- Вставить в новый абзац или ячейку как обычный текст.
- Применить стиль ко всей метке.
- Убедиться, что она осталась внутри нужного цикла.
Например, {contract_number} нельзя вынести за пределы {#contracts}, иначе
у поля не будет текущего договора.
Как менять таблицу графика
Можно менять оформление заголовка, ширину столбцов и порядок столбцов. Нельзя разрывать повторяемую строку:
{#schedule}{number} | {date} | {payment} | {balance}{/schedule}
Открывающий тег должен находиться перед данными повторяемой строки, а закрывающий — после них. Заголовок таблицы не должен попадать внутрь цикла, иначе он повторится для каждого платежа.
Как не потерять ручные изменения
- Не заменяйте активный файл новой редакцией под тем же именем.
- Скопируйте последнюю рабочую версию в следующий номер:
v2→v3. - Редактируйте только новую копию в Word.
- Закройте Word перед тестированием, чтобы рядом не оставался служебный файл
вида
~$...docx. - Добавьте новую версию в
server/templates/manifest.json. - Переключите
templateIdвsrc/app.js. - Выполните
npm run check, затем сформируйте образцы с одним и несколькими договорами. - Только после визуальной проверки добавьте DOCX и код в Git и создайте коммит. Git хранит предыдущую бинарную версию целиком, поэтому её можно восстановить.
Текущее состояние:
installment-agreement-v1.docx— сохранённая ручная редакция пользователя;installment-agreement-v2.docx— предыдущая рабочая версия;installment-agreement-v3.docx— предыдущая двуязычная версия;installment-agreement-v4.docx— активная вычитанная и проверенная версия;manifest.jsonхранит все версии;- служебные файлы Word
~$*.docxисключены через.gitignore.
Что нельзя делать с рабочим шаблоном
- не указывать рабочий юридический DOCX в
--outputвместе с--force; - не распаковывать DOCX и не править XML вручную без необходимости;
- не открывать один и тот же файл одновременно в нескольких редакторах;
- не заменять DOCX файлом, который Word предлагает «восстановить»;
- не сохранять только PDF вместо исходного DOCX;
- не менять имя файла без одновременного изменения
manifest.json.
DOCX является бинарным файлом: Git может сохранить и восстановить его версию,
но не покажет удобный построчный diff. Поэтому юридические редакции лучше
хранить отдельными файлами v1, v2, v3, v4, а не постоянно перезаписывать
единственный шаблон.
19. Как готовить новый шаблон допсоглашения
Вариант A. Создать новую версию на основе текущей
Рекомендуемый вариант.
Скопировать:
server/templates/installment-agreement-v2.docxНазвать новый файл, например:
installment-agreement-v3.docxНе удаляя
v2, отредактироватьv3в Word.Добавить
v3новой записью в массивtemplates.{ "defaultTemplateId": "installment-agreement-v3", "templates": [ { "id": "installment-agreement-v1", "version": 1, "name": "Сохранённая пользовательская редакция", "file": "installment-agreement-v1.docx" }, { "id": "installment-agreement-v2", "version": 2, "name": "Рабочая версия с условными разделами", "file": "installment-agreement-v2.docx" }, { "id": "installment-agreement-v3", "version": 3, "name": "Новая юридическая редакция", "file": "installment-agreement-v3.docx" } ] }Переключить явный
templateIdвgetDocumentPayload()файлаsrc/app.js:templateId: "installment-agreement-v3"Если для нового шаблона добавлены поля, обновить
server/document-context.jsи тесты.Выполнить
npm run sampleиnpm run check.Сформировать документ через веб-интерфейс и проверить имя, текст и все приложения.
Изменение только defaultTemplateId не переключит текущий интерфейс, пока
клиент явно отправляет конкретный templateId.
Вариант B. Создать чистый DOCX вручную
- Создать новый документ в Word.
- Настроить формат страницы, поля, шрифты и стили.
- Написать юридический текст.
- Вставить глобальные метки.
- Создать таблицу договоров.
- Создать цикл приложений
{#contracts}. - Внутри него создать таблицу
{#schedule}. - После закрытия цикла договоров создать сводный график и целиком обернуть
его в
{#has_multiple_contracts}...{/has_multiple_contracts}. - Зарегистрировать файл в manifest.
- Переключить
templateId. - Выполнить полную проверку.
Перед созданием чистого шаблона рекомендуется выписать структуру:
- основной юридический текст;
- сведения о должнике;
- список договоров;
- подписи;
- индивидуальное приложение, повторяемое для каждого договора;
- общее приложение.
Это помогает не вложить общий график внутрь цикла договоров и не получить его копию после каждого договора.
Минимальный текстовый каркас
СОГЛАШЕНИЕ О РАССРОЧКЕ
Должник: {full_name}
ИИН: {iin}
Договоры: {contracts_text}
Общая сумма: {total_debt_amount}
{#contracts}
Приложение № {appendix_number}
Договор № {contract_number} от {contract_date}
Сумма: {debt_amount}
Срок: {term_months} месяцев
Первый платёж: {first_payment_date}
{#schedule}
{number} | {date} | {payment} | {balance}
{/schedule}
{/contracts}
Приложение № {combined_appendix_number}
Сводный график
{#combined_schedule}
{number} | {date} | {payment} | {balance}
{/combined_schedule}
В реальном Word-документе строки графика следует располагать в строках таблицы,
а не в виде текста с символом |.
Контроль перед регистрацией нового шаблона
- имя файла содержит только ожидаемые символы и оканчивается на
.docx; idв manifest уникален;fileв manifest точно совпадает с именем файла;- все циклы закрыты;
- индивидуальный график вложен в цикл договоров;
- общий график находится после
{/contracts}; - в документе нет включённых исправлений;
- нет демонстрационных Ф.И.О., ИИН или сумм, написанных обычным текстом;
- шаблон проходит
npm run sampleиnpm run check.
20. Циклы в таблицах Word
Таблица договоров
Чтобы строка таблицы повторялась для каждого договора:
- в первой ячейке строки перед первым значением разместить
{#contracts}; - в последней ячейке после последнего значения разместить
{/contracts}.
Пример содержимого строки:
| Ячейка 1 | Ячейка 2 | Ячейка 3 | Ячейка 4 |
|---|---|---|---|
{#contracts}{contract_number} |
{contract_date} |
{debt_amount} |
{term_months}{/contracts} |
Docxtemplater повторит всю строку.
Таблица индивидуального графика
| № | Дата | Платёж | Остаток |
|---|---|---|---|
{#schedule}{number} |
{date} |
{payment} |
{balance}{/schedule} |
Эта таблица должна находиться внутри {#contracts}, иначе у неё не будет
контекста текущего договора.
Таблица общего графика
| № | Дата | Платёж | Остаток |
|---|---|---|---|
{#combined_schedule}{number} |
{date} |
{payment} |
{balance}{/combined_schedule} |
Общий график располагается вне цикла {#contracts}.
Целое приложение на каждый договор
Если нужно повторять не одну строку, а несколько абзацев и таблицу:
- поместить
{#contracts}в отдельный абзац перед приложением; - разместить текст и таблицу приложения;
- поместить
{/contracts}в отдельный абзац после приложения; - при необходимости добавить разрыв страницы внутрь повторяемого блока.
В текущем техническом шаблоне разрыв страницы находится перед циклом и ещё один — внутри повторяемого блока после индивидуальной таблицы. Поэтому каждое приложение начинается с новой страницы, а сводный график идёт после всех договоров.
Если появляется пустая страница:
- включить знаки
¶; - проверить, нет ли двух соседних разрывов;
- проверить пустой абзац после таблицы;
- убедиться, что разрыв находится с правильной стороны
{/contracts}.
Практический способ собрать повторяемую строку
- Создать таблицу с заголовком и одной строкой данных.
- Внести в строку все поля без циклов.
- В начало текста первой ячейки добавить
{#schedule}. - В конец текста последней ячейки добавить
{/schedule}. - Сформировать образец.
- Убедиться, что повторяется строка данных, а не вся таблица.
Для таблицы договоров те же действия выполняются с {#contracts} и
{/contracts}.
21. Как добавить новое поле в шаблон
Допустим, нужно добавить {organization_name}.
Шаг 1. Определить источник
Решить, откуда приходит значение:
- новое поле интерфейса;
- вычисляемое значение;
- константа;
- поле договора;
- поле строки графика.
Шаг 2. Добавить в браузерную модель
Если значение вводит пользователь:
- добавить поле в
index.html; - получить DOM-элемент в
src/app.js; - добавить нормализацию и валидацию;
- сохранить значение в состояние;
- включить его в
getDocumentPayload().
Шаг 3. Добавить в серверный контекст
В server/document-context.js:
return {
// существующие поля
organization_name: requireText(
payload.organizationName,
"Наименование организации",
),
};
Если поле относится к договору, добавить его в объект, возвращаемый внутри
contracts.map(...).
Если поле относится к строке графика, добавить его внутри schedule.map(...).
Шаг 4. Вставить метку в DOCX
{organization_name}
Шаг 5. Добавить тест
Проверить:
- наличие поля в
document-context.test.js; - фактическую подстановку в
document-generation.test.js; - отсутствие необработанной метки в XML результата.
Шаг 6. Выполнить QA
npm run check
npm run sample
22. Версионирование шаблонов
Не рекомендуется постоянно заменять один файл новой юридической редакцией.
Предпочтительная схема:
installment-agreement-v1.docx
installment-agreement-v2.docx
installment-agreement-v3.docx
installment-agreement-v4.docx
Manifest:
{
"defaultTemplateId": "installment-agreement-v4",
"templates": [
{
"id": "installment-agreement-v1",
"version": 1,
"name": "Редакция 1",
"file": "installment-agreement-v1.docx"
},
{
"id": "installment-agreement-v2",
"version": 2,
"name": "Редакция 2",
"file": "installment-agreement-v2.docx"
},
{
"id": "installment-agreement-v3",
"version": 3,
"name": "Редакция 3",
"file": "installment-agreement-v3.docx"
},
{
"id": "installment-agreement-v4",
"version": 4,
"name": "Редакция 4",
"file": "installment-agreement-v4.docx"
}
]
}
Рекомендации:
- не удалять старую редакцию, если по ней уже создавались документы;
- фиксировать изменения шаблона отдельным Git-коммитом;
- указывать номер юридической редакции в
nameиversion; - не переиспользовать один
idдля несовместимых редакций; - хранить утверждённый оригинал отдельно от технической копии;
- перед включением новой версии формировать тестовый документ.
23. Проверка шаблона
Автоматическая
npm run check
Тест генерации:
- создаёт документ по нескольким договорам;
- проверяет размер результата;
- читает
word/document.xml; - проверяет наличие Ф.И.О., ИИН и номеров договоров;
- проверяет, что служебные метки были обработаны.
Создание образца
npm run sample
Команда использует данные из scripts/generate-sample-document.js: один
договор с расчётом по сроку и второй с расчётом по размеру платежа. Готовый
файл создаётся в .tmp/document-qa/sample-obligation-multiple.docx.
Команда node scripts/generate-sample-document.js --single создаёт
.tmp/document-qa/sample-obligation-single.docx.
Если нужно проверить другой крайний случай, измените только тестовые данные в этом скрипте или создайте рядом отдельный QA-скрипт. Не подменяйте шаблон демонстрационными значениями.
Ручной чек-лист
- документ открывается без восстановления Word;
- нет текста
{full_name}или других необработанных меток; - данные должника отображаются корректно;
- все договоры присутствуют;
- суммы совпадают с интерфейсом;
- количество приложений корректно;
- каждое приложение относится к нужному договору;
- в досудебном документе сводное приложение находится последним, если договоров несколько;
- в судебном документе присутствует ровно один график: индивидуальный или общий в зависимости от количества договоров;
- заголовки таблиц повторяются на новых страницах;
- строки не обрезаются;
- длинные значения не ломают колонки;
- нулевые платежи отображаются там, где они нужны;
- последняя строка обычного графика имеет нулевой остаток;
- подписи и юридический текст находятся на нужных страницах.
Рекомендуемая матрица визуальной проверки
| Сценарий | Что проверять |
|---|---|
| Один договор, 1–2 платежа | основной текст и один индивидуальный график; пункт 3.2 и сводный график отсутствуют |
| Два договора с разными датами | нумерацию приложений и объединение дат |
| Длинное Ф.И.О. и номер договора | переносы и ширину ячеек |
| Пустое отчество | отсутствие лишних двойных пробелов |
| Большая сумма | формат 1 234 567,89 и ширину столбца; валюту задаёт шаблон |
| График на несколько страниц | повторение заголовка и отсутствие разрыва строки |
| Сложный график с нулевыми платежами | наличие помесячных нулевых строк |
| Будущая дата платежа по графику | правильную дату первой строки и все последующие месяцы |
Минимально новая юридическая редакция должна быть проверена на одном и на нескольких договорах. Успешного открытия одного короткого образца недостаточно.
Просмотр меток текущего DOCX
DOCX является ZIP-архивом. Для диагностического просмотра:
unzip -p server/templates/installment-agreement-v4.docx \
word/document.xml
Для обычного редактирования распаковывать DOCX не нужно.
24. Типовые ошибки шаблона
«Выбранный шаблон не найден»
Причины:
- неверный
templateId; - запись отсутствует в manifest;
- опечатка в
id.
«Файл не найден» или ошибка чтения DOCX
Проверить:
- значение
fileв manifest; - наличие файла в
server/templates; - расширение
.docx; - файл не был переименован Word.
Ошибка парсинга Docxtemplater
Частые причины:
- отсутствует закрывающая скобка;
- перепутаны
{#loop}и{/loop}; - закрывается другое имя цикла;
- метка разорвана сложным форматированием;
- скопированы нестандартные фигурные скобки;
- вложенные циклы закрыты в неправильном порядке.
Правильный порядок:
{#contracts}
{#schedule}
{/schedule}
{/contracts}
Пустое поле в документе
nullGetter заменяет отсутствующие значения пустой строкой. Проверить:
- существует ли поле в
document-context.js; - находится ли метка в правильном контексте;
- совпадает ли имя;
- передаётся ли исходное значение с клиента.
Если отчество отсутствует, пустое {middle_name} является штатным поведением.
Для готового Ф.И.О. предпочтительно использовать {full_name}: сервер сам
убирает лишний пробел.
В готовом документе осталась метка
Если виден текст вроде {contract_number}:
- метка может находиться вне своей области видимости;
- имя метки может отсутствовать в контексте;
- часть метки могла попасть в исправления Word;
- могли использоваться нестандартные скобки;
- был открыт старый ранее сформированный файл.
Сначала удалить и заново напечатать метку целиком, затем повторно выполнить
npm run sample.
В таблице появляется только одна строка
Проверить расположение {#schedule} и {/schedule}. Они должны охватывать
повторяемую строку.
Приложение создаётся только для одного договора
Проверить {#contracts} и {/contracts} вокруг всего блока приложения.
Word сообщает о повреждении файла
Возможные причины:
- шаблон был сохранён не как DOCX;
- файл повреждён сторонним редактором;
- вручную изменялась внутренняя XML-структура;
- цикл пересекает несовместимые элементы Word.
Вернуться к последней рабочей версии из Git и вносить изменения небольшими шагами.
25. Тестирование
Проект использует встроенный тестовый раннер Node.js:
npm run check
Актуальное количество проверок всегда видно в итоговой строке node --test;
README намеренно не фиксирует это число, поскольку набор тестов развивается.
test/format.test.js
Проверяет:
- разрешённые символы Ф.И.О.;
- регистр;
- пробелы;
- маску даты;
- проверку календарной даты;
- запрет будущей даты договора;
- нормализацию адреса и телефона.
test/schedule.test.js
Проверяет:
- полное погашение равного графика;
- расчёт срока по платежу;
- нулевые строки ручного сложного графика;
- распределение остатка;
- объединение нескольких договоров.
test/document-context.test.js
Проверяет:
- сборку Ф.И.О.;
- договоры;
- строки графиков;
- общую сумму;
- текст договоров;
- отклонение некорректного ИИН.
test/document-generation.test.js
Проверяет:
- создание DOCX по нескольким договорам;
- фактическую подстановку данных;
- отсутствие незакрытых циклов и меток;
- ошибку неизвестного шаблона.
Дополнительные файлы проверяют PDF-конвертацию, CSV и ZIP-архивы, серверный пересчёт пропорциональных графиков, русскую и казахскую запись сумм прописью, а также HTML-страницу документации.
26. Конфиденциальность и безопасность
Что происходит с персональными данными
- данные вводятся в браузере;
- хранятся в памяти JavaScript;
- при формировании Word отправляются на локальный сервер;
- сервер использует их только в рамках текущего запроса;
- сформированный документ возвращается браузеру;
- база данных отсутствует.
Что не сохраняется
- Ф.И.О.;
- ИИН;
- адрес;
- номер телефона;
- договоры;
- графики;
- сформированные документы.
Исключения:
- пользователь сам сохраняет скачанный файл;
- тестовый скрипт сохраняет файл в
.tmp/document-qa; - данные могут оставаться в памяти процесса до завершения запроса;
- браузер и операционная система могут вести собственные журналы загрузок.
Защита HTTP-сервера
- по умолчанию сервер привязан к
127.0.0.1; - сетевой доступ включается только явным запуском с
HOST=0.0.0.0; - статический обработчик проверяет выход за корень проекта;
- размер JSON ограничен 1 МБ;
- неизвестные HTTP-методы отклоняются;
- путь шаблона проверяется на нахождение внутри
server/templates.
Для публичного размещения этого недостаточно. Потребуются:
- HTTPS;
- аутентификация;
- авторизация;
- CSRF-защита;
- журналирование без утечки ИИН;
- политика хранения данных;
- ограничение частоты запросов;
- серверная схема валидации;
- безопасное хранение созданных документов;
- антивирусная и контентная проверка загружаемых шаблонов.
27. Рекомендуемый рабочий процесс
Изменение интерфейса
- Изменить
index.html. - Изменить
src/styles.css. - Обновить поведение в
src/app.js. - Проверить мобильную компоновку.
- Запустить тесты.
Изменение расчёта
- Изменить
src/domain/schedule.js. - Добавить тест в
test/schedule.test.js. - Проверить интерфейс.
- Проверить Word, потому что сервер использует тот же модуль.
Изменение нормализации
- Изменить
src/domain/format.js. - Добавить тест в
test/format.test.js. - Убедиться, что серверная нормализация не расходится с клиентской.
Изменение Word-контекста
- Изменить
server/document-context.js. - Добавить или обновить тест контекста.
- Изменить DOCX.
- Запустить
npm run sample. - Открыть результат в Word.
Изменение юридического текста
- Создать новую версию DOCX.
- Сохранить старую версию.
- Обновить manifest.
- Переключить
templateId. - Сформировать образец.
- Получить юридическое согласование.
- Зафиксировать отдельным Git-коммитом.
28. Рекомендации для дальнейшего развития
При переходе от локального прототипа к рабочей системе разумно выделить:
Схему данных
- должник;
- договор;
- конфигурация рассрочки;
- версия шаблона;
- сформированный документ.
Серверную валидацию
- единая схема payload;
- формальное закрепление
contract.debtAmountкак единственного поля суммы долга в следующей версии схемы; - проверка дат и ИИН;
- проверка полного погашения.
Хранилище
- черновики;
- история;
- аудит изменений;
- версии документов.
Управление шаблонами
- выбор шаблона в интерфейсе;
- статусы «черновик/утверждён»;
- дата вступления редакции в силу;
- запрет удаления использованной версии.
Генерацию
- очередь задач;
- PDF-копию;
- электронную подпись;
- контрольные суммы документов.
Табличный экспорт
- настраиваемый выбор CSV/XLSX;
- объединение графиков в одну книгу с листами;
- стили и форматы ячеек для XLSX.
Конфиденциальность
- роли;
- шифрование;
- сроки хранения;
- маскирование ИИН в журналах;
- журнал доступа.
29. Краткая памятка по шаблону
- Шаблон — обычный
.docx. - Текст можно менять в Microsoft Word.
- Метки — обычный текст, а не поля слияния Word.
- Метки пишутся как
{field_name}без пробелов. - Договоры повторяются через
{#contracts}. - Их графики повторяются через
{#schedule}внутри договора. - Общий график повторяется через
{#combined_schedule}вне договоров и показывается только внутри{#has_multiple_contracts}. - Новое поле сначала добавляется в
document-context.js. - Новая версия регистрируется в
manifest.json. - Клиент должен отправлять нужный
templateId. - Перед редактированием нужно сохранить предыдущую версию.
create_default_template.pyсоздаёт отдельный технический файл; флаг--forceнельзя направлять на рабочий юридический шаблон.- После каждого изменения:
npm run check
npm run sample
node scripts/generate-sample-document.js --single
- Итоговый файл обязательно открыть и визуально проверить в Microsoft Word.
- Проверять нужно как минимум один и несколько договоров.