Почему изменения во внешней печатной форме 1С не отображаются при печати через интеграцию с CRM

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработчики систем на платформе 1С часто сталкиваются с ситуацией, когда доработанная печатная форма отлично выводит новые данные при печати непосредственно из интерфейса программы, но при автоматической отправке через API или обмене с CRM-системой клиенту уходит старый вариант документа. Кажется, что платформа игнорирует внесенные изменения, берет макет «из воздуха» или обращается к какому-то скрытому кэшу.

В этой статье мы подробно разберем, почему возникает такое поведение, проанализируем архитектуру вызова внешних обработок печати, выясним, почему советы «почистить кэш» в данном случае абсолютно бесполезны, и рассмотрим правильный алгоритм поиска и устранения подобных ошибок.

Анализ механизма интеграции и выгрузки печатной формы

Для начала детально рассмотрим программный код, который отвечает за формирование печатной формы в формате PDF для последующей передачи в CRM-систему. Проанализируем ключевую функцию обмена:


Функция СформироватьИСохранитьПечатнуюФорму(ДокументСсылка, СтруктураДанных, НастройкиОбмена)
        
        ПечатнаяФорма = Неопределено;
        ПутьКФайлу = "";
        
        Если ТипЗнч(ДокументСсылка) = Тип("ДокументСсылка.СчетНаОплатуПокупателю") Тогда
            
            НайденаяОбработка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию("Счет на бланке");
            
            Если Не НайденаяОбработка.Пустая() Тогда
                
                МассивОбъектов = Новый Массив;
                МассивОбъектов.Добавить(ДокументСсылка);
                
                ОбъектыПечати = Новый СписокЗначений;
                
                ИмяФайла = ПолучитьИмяВременногоФайла();
                ДвоичныеДанные = НайденаяОбработка.ХранилищеОбработки.Получить();
                ДвоичныеДанные.Записать(ИмяФайла); 
                
                ОбработкаОбъект = ВнешниеОбработки.Создать(ИмяФайла, Истина);
                ПечатнаяФорма = ОбработкаОбъект.PechatDokumenta(МассивОбъектов, ОбъектыПечати, (Число(СтруктураДанных.sign) = 1));
                ОбработкаОбъект = Неопределено;
                
                УдалитьФайлы(ИмяФайла);
                
            КонецЕсли;            
            
        // ... (код для обработки других типов документов)
        
        Если ПечатнаяФорма <> Неопределено Тогда
            
            Если Число(СтруктураДанных.format) = 0 Тогда
                ПутьКФайлу = СтруктураДанных.ПутьКаталога + "\" + СтруктураДанных.Представление + ".xlsx";
                ПечатнаяФорма.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
            ИначеЕсли Число(СтруктураДанных.format) = 1 Тогда
                ПутьКФайлу = СтруктураДанных.ПутьКаталога + "\" + СтруктураДанных.Представление + ".pdf";
                ПечатнаяФорма.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.PDF);
            КонецЕсли;    
            
        Иначе
            ПутьКФайлу = "";
        КонецЕсли;    
        
        Возврат ПутьКФайлу;    
        
КонецФункции

Разберем этот алгоритм по шагам, чтобы понять логику его работы:

  1. Программа проверяет тип переданной ссылки. Если это ДокументСсылка.СчетНаОплатуПокупателю, управление передается ветке обработки счета.
  2. Выполняется поиск внешней обработки в справочнике ДополнительныеОтчетыИОбработки по наименованию «Счет на бланке».
  3. Если обработка найдена, платформа извлекает бинарные данные файла обработки из реквизита ХранилищеОбработки, который имеет тип ХранилищеЗначения.
  4. Метод ПолучитьИмяВременногоФайла() генерирует уникальный путь во временной папке операционной системы (например, C:\Users\Username\AppData\Local\Temp\v8_A1B2_34.tmp).
  5. Двоичные данные записываются на жесткий диск по этому пути.
  6. Метод ВнешниеОбработки.Создать(ИмяФайла, Истина) инициализирует объект внешней обработки в безопасном режиме.
  7. Вызывается экспортная функция PechatDokumenta() модуля внешней обработки, которая возвращает сформированный объект ТабличныйДокумент.
  8. Полученный документ сохраняется на диск в формате PDF или XLSX (в зависимости от параметров структуры) и передается в CRM-систему. Временный файл обработки незамедлительно удаляется с диска методом УдалитьФайлы().

Почему советы по очистке кэша здесь бесполезны

Когда разработчик сталкивается с ситуацией, при которой изменения в печатной форме не «применяются» при программном вызове, первая реакция большинства коллег на форумах — посоветовать очистить локальный кэш конфигурации 1С. Однако, если мы внимательно проанализируем приведенный выше код, станет очевидно, что кэш здесь абсолютно ни при чем.

Кэширование платформы 1С работает для метаданных конфигурации, а также для некоторых повторно используемых модулей. В данном коде внешняя обработка извлекается непосредственно из базы данных (из таблицы справочника ДополнительныеОтчетыИОбработки) в виде потока байт при каждом (!) запуске обмена. Платформа каждый раз записывает этот поток в файл с новым уникальным именем во временном каталоге ОС, открывает его как абсолютно новую внешнюю обработку и сразу после генерации макета физически удаляет файл с диска.

В такой схеме старой версии обработки физически неоткуда взяться в локальном кэше платформы. Проблема кроется глубже — во внутренней логике самого макета и в параметрах, которые передаются при вызове функции печати.

Истинная причина: Скрытые области макета и параметризация вызова

Обратим внимание на строку вызова функции печати из модуля обмена:


ПечатнаяФорма = ОбработкаОбъект.PechatDokumenta(МассивОбъектов, ОбъектыПечати, (Число(СтруктураДанных.sign) = 1));

Третьим параметром в функцию PechatDokumenta() передается логическое выражение (Число(СтруктураДанных.sign) = 1). Этот параметр определяет, нужно ли выводить в печатную форму факсимильные подписи и печать организации:

Внешние печатные формы часто проектируются таким образом, что области макета для вывода с факсимиле и без него расположены в разных частях одной таблицы MXL. Разработчик, открыв макет в конфигураторе, визуально видит только первую область (подвал без подписи), вносит туда правки и сохраняет обработку. Область для факсимильной печати может находиться ниже, отделенная пустыми строками, или вообще располагаться на отдельном листе макета.

Поскольку в области ПодвалСчетаСПодписьюИПечатью текст не менялся, CRM-система продолжала получать PDF-файлы, сформированные по старому шаблону. В этом и заключалась «мистика» поведения платформы.

Критика архитектуры кода и антипаттерны

Представленный фрагмент программного кода обмена содержит несколько критических уязвимостей и нарушений стандартов разработки 1С, которые усложняют поддержку и приводят к подобным скрытым ошибкам.

1. Использование метода НайтиПоНаименованию() без точного соответствия
Поиск элемента справочника по строковому представлению НайтиПоНаименованию("Счет на бланке") крайне опасен. Во-первых, если в справочнике появится элемент с похожим именем (например, «Счет на бланке (старый)» или «Счет на бланке (копия)»), метод может вернуть ссылку на неактуальный объект. Во-вторых, любое случайное изменение названия элемента пользователем в режиме «1С:Предприятие» (например, добавление лишней точки или пробела) полностью заблокирует работу обмена с CRM, так как обработка не будет найдена.

2. Игнорирование механизмов БСП (Библиотеки стандартных подсистем)
В современных конфигурациях 1С ручное извлечение обработок из хранилища и запись их во временные файлы на диске считается устаревшим подходом. Для работы с дополнительными отчетами и обработками в БСП предусмотрен специальный программный интерфейс (API), который автоматизирует безопасное извлечение, инициализацию и выполнение кода во внешних объектах. Ручной обход этих механизмов лишает систему контроля прав доступа и централизованного логирования ошибок.

3. Жесткое кодирование (Hardcode) типов документов
Конструкция Если ТипЗнч(ДокументСсылка) = Тип("ДокументСсылка.СчетНаОплатуПокупателю") делает модуль интеграции статичным. При добавлении новых типов документов для отправки в CRM разработчику придется каждый раз вручную дописывать новые ветки ИначеЕсли в общий модуль обмена.

Пошаговый алгоритм отладки внешних печатных форм

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

  1. Временно заблокируйте удаление временного файла. В коде обмена найдите строку УдалитьФайлы(ИмяФайла); и закомментируйте её на время проведения расследования.
  2. Запустите процесс интеграции под отладчиком. Установите точку останова непосредственно перед вызовом метода PechatDokumenta().
  3. Получите точный путь к сформированному файлу. В момент остановки программы скопируйте значение переменной ИмяФайла (это будет реальный путь к временному файлу .tmp или .epf на вашем компьютере).
  4. Откройте временную обработку в Конфигураторе. Не прерывая сеанс отладки, откройте скопированный файл через меню «Файл — Открыть» в конфигураторе. Теперь вы работаете именно с тем экземпляром обработки, который база данных отдала для интеграции.
  5. Настройте точки останова внутри самой обработки. Перейдите в модуль объекта открытой временной обработки, найдите экспортную функцию печати и установите внутри нее точки останова. Откройте макет и внимательно изучите все его области, прокрутив страницу до самого конца вниз и вправо.
  6. Запустите выполнение кода далее. Платформа остановится на точках останова внутри внешней обработки. Шаг за шагом проследите, какие именно области макета выбираются методом ПолучитьОбласть() в зависимости от переданных из CRM параметров и флага подписи.

Этот простой метод гарантирует 100% результат при поиске несоответствий в макетах и избавляет от необходимости тратить часы на безрезультатную очистку кэша или гадание на форумах.

← На главную