Кейс

Автоматизация печатных форм: от Google Docs к шаблонам RetailCRM

От ручных Google Docs к автоматическим пиксель‑перфект формам, которые генерируются за секунды

Одна централизованная система шаблонов убрала ручной труд менеджеров и обеспечила одинаковый вид всего пакета документов.

Исходная ситуация

Документы формировались вручную в Google Docs. Каждый менеджер открывал шаблон, копировал, дописывал данные, выравнивал таблицы и линии. Высокая визуальная планка клиента делала процесс ещё затратнее: любое расхождение в межстрочном интервале или ширине столбца отправляло форму «на доработку».

  • Время на пакет документов: до 25–30 минут.
  • Риск опечаток и разного формата чисел.
  • Повторный ввод данных, которые уже есть в заказе.
  • Несогласованность оформления при правках разными людьми.

Болевые точки

  • Ручное форматирование таблиц.
  • Скрытие/показ условных секций.
  • Скидки: процент + фикс внутри одной строки.
  • Сборка «пакета» из нескольких форм.
  • Сохранение эстетики исходных Google Docs.

Что требовалось бизнесу

Пакет форм

Заявка, Наряд-заказ, Разрешение на выдачу, Накладная доставки, Акт, Счёт.

Пиксель‑перфект

Повтор ширин, толщин линий, интервалов из Google Docs.

Автоматизация

Генерация всей логики за 1–2 клика без ручного редактирования.

Условные блоки

Показывать только релевантные секции: детали клиента, товары, услуги.

Единый стиль

Идентичный брендовый заголовок и шапка во всех формах.

Скрытие пустого

Ни одной «пустой» строки — раздел просто не рендерится.

Подход к решению

Серия адаптированных HTML/Twig шаблонов с минимальной логикой на этапе рендеринга и жёстко контролируемой сеткой colgroup для точного соответствия исходным макетам.

  • Единый базовый микро-набор классов (text-center, bg-gray, пр.).
  • Оптимизация: один проход для определения флагов секций до вывода содержимого.
  • Комбинированная скидка: проценты + фикс → расчёт новой цены + зачёркивание старой.
  • Авто-формула итоговой фразы (товары, услуги или их сочетание).
  • Стили без внешних зависимостей — стабильность при печати PDF.
Сроки: одна форма ~2 дня с учётом согласований. Весь пакет — сжатый цикл поставки за счёт повторного использования паттернов.

Динамическая логика & условия

Предварительный анализ

Собираем флаги наличия блоков до рендера таблиц.

Сложные скидки

Процент + фикс в одной строке без потери округлений.

Гибкие статусы

Отбор позиций по статусу (в заявку, в выдачу, в доставку).

Чистый вывод

Отсутствие данных = полное скрытие блока.

Единый формат чисел

Пробелы как разделители тысяч, управляемые десятичные.

Изоляция расчётов

Расчёты внутри минимальных Twig выражений.

Условные колонки

Колонки скидок появляются только при фактической экономии.

Адаптация акта

Акт и счёт — собственная компоновка обязательных реквизитов.

Pixel‑perfect выполнение

Сохранили структуру визуального языка клиента: плотность сетки, визуальный ритм, контраст заголовков и столбцов без «плавающих» ширин.

Точность сетки

  • Использованы фиксированные проценты в colgroup (сумма 100%).
  • Все бордеры 0.5pt: соответствие печати.
  • Контроль межстрочных интервалов за счёт выборки шрифта и размера.

Причина успеха

  • Повторное переиспользование шаблонного каркаса.
  • Минимум «магии» — простые, проверяемые условия.
  • Сокращение точек расхождения для разных форм.

Риск «дрейфа»

Чтобы при будущих правках не «поплыл» дизайн, все значения (ширины, padding, размер шрифта) задокументированы прямо в шаблоне и вынесены в повторяющиеся блоки.

Ключевые фрагменты кода

1. Предварительное определение флагов секций

{# Определяем что выводить #}
{% set hasCustomerItems = false %}
{% for f in [
  order.customFields.cf_part1, order.customFields.cf_part2, order.customFields.cf_part3,
  order.customFields.cf_part4, order.customFields.cf_part5, order.customFields.cf_part6,
  order.customFields.cf_part7, order.customFields.cf_part8, order.customFields.cf_part9,
  order.customFields.cf_part10
] %}
  {% if f %}{% set hasCustomerItems = true %}{% break %}{% endif %}
{% endfor %}

{% set hasProducts = false %}
{% set hasServices = false %}
{% for line in order.availableOrderProducts %}
  {% if line.status == 'В заявку на работы' %}
    {% if line.offer.product.typeCode == 'product' %}{% set hasProducts = true %}{% endif %}
    {% if line.offer.product.typeCode == 'service' %}{% set hasServices = true %}{% endif %}
  {% endif %}
{% endfor %}

2. Условный вывод колонки скидок

{% set anyDiscount = false %}
{% for item in order.availableOrderProducts %}
  {% if item.offer.product.typeCode == 'service' and (order.discountPercent > 0 or item.discountTotal > 0) %}
    {% set anyDiscount = true %}
    {% break %}
  {% endif %}
{% endfor %}


  
    №
    Услуга
    Свойства
    Цена
    {% if anyDiscount %}
      Скидка
      После скидки
    {% endif %}
    Кол-во
    Сумма
  

3. Расчёт комбинированной скидки (процент + фикс)

{# price_after = initial - (percent_part + flat_part) #}
{% set percent_part = item.initialPrice * (order.discountPercent / 100) %}
{% set flat_part = item.discountTotal %}
{% set total_disc_per_unit = percent_part + flat_part %}
{% set discounted = item.initialPrice - total_disc_per_unit %}

  {% if total_disc_per_unit > 0 %}{{ item.initialPrice|number_format(0, ',', ' ') }}
  {% else %}{{ item.initialPrice|number_format(0, ',', ' ') }}{% endif %}

{% if anyDiscount %}
  
    {% if percent_part > 0 %}{{ order.discountPercent|round }}% ({{ percent_part|number_format(0, ',', ' ') }}){% elseif flat_part > 0 %}{{ flat_part|number_format(0, ',', ' ') }}{% endif %}
  
  {{ discounted|number_format(0, ',', ' ') }}
{% endif %}

4. Итоговая формулировка

{% if hasProducts and hasServices %}
  {% set totalLabel = 'ИТОГО ТОВАРОВ И УСЛУГ НА СУММУ:' %}
{% elseif hasServices %}
  {% set totalLabel = 'ИТОГО УСЛУГ НА СУММУ:' %}
{% elseif hasProducts %}
  {% set totalLabel = 'ИТОГО ТОВАРОВ НА СУММУ:' %}
{% else %}
  {% set totalLabel = 'ИТОГО К ОПЛАТЕ:' %}
{% endif %}

{{ totalLabel }} {{ order.totalSumm|number_format(2, ',', ' ') }} руб.

5. Скрытие пустых блоков без заглушек

{% if hasCustomerItems %}

  {% set row = 1 %}
  {% for ref in customerParts %}
    {% if ref.value %}
      
      {% set row = row + 1 %}
    {% endif %}
  {% endfor %}
  
{{ row }} {{ ref.value }} {{ ref.qty }}
{% endif %}

6. Унификация форматирования цены (псевдо-хелпер)

{# Twig не всегда имеет фильтр: создаём macro #}
{% macro money(v, decimals) %}
  {{- v|number_format(decimals, ',', ' ') -}}
{% endmacro %}

{# Использование: #}
{{ _self.money(item.initialPrice, 0) }}

Как обеспечили единый стиль

  • Одинаковая шапка: логотип / сайт / телефон.
  • Стандартизованный набор классов шрифта и выравнивания.
  • Синхронные названия секций между формами.
  • Унификация расчёта скидок (один паттерн — разный контекст).
Результат: менеджеры берут любую форму — визуально она «из одного комплекта», сокращая когнитивную нагрузку у клиента.

Первые результаты

Цифры ориентировочные, отражают порядок эффекта после перехода на автоматическую генерацию.

-85%
времени на пакет документов
~4 мин → 35 c
формирование одной формы
0
ручных правок форматирования
-60%
риск ошибок в итогах/скидках
100%
визуальное единообразие
2 дн
цикл разработки одной формы

Дальнейшее развитие

QR-коды

Автогенерация ссылок на оплату / отслеживание.

Подписи

Встраивание цифровой подписи без ручной вставки.

Мультиязык

RU/EN версии по флагу клиента.

Штрих-коды

Для складского быстрого сканирования.

FAQ

Да. Каркас легко расширить: добавляется шапка + секции условия.

Меняем централизованный блок расчёта — во всех формах обновление едино.

Обычно 5–10 минут: поле + условие + вывод.

Да. Абстрагированы строки — подстановка локалей через словарь.

Через генерацию изображений/встраивание SVG на этапе рендера.

Незначительно: шаблоны лёгкие, вычисления локальные без внешних запросов.

Ускорим и ваш документооборот

Покажу как превратить ручные Google / Excel шаблоны в единый автоматический пакет.