Как программно обработать список документов для массовой выгрузки в XML в 1С (Обычные формы)

Программист 1С v8.3 (Обычные формы)
← На главную

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

Анализируем причину ошибки «Значение не является значением объектного типа (Имя)»

Часто при попытке передать коллекцию (массив или список) в функцию, ожидающую одиночную ссылку, возникает критическая ошибка. Разберем ситуацию: если в коде используется метод Метаданные.НайтиПоТипу(ТипЗнч(Документ)), а в переменную Документ вместо ссылки на конкретную реализацию попал весь СписокЗначений, система не сможет определить тип метаданных. Метод НайтиПоТипу вернет Неопределено, и при попытке обратиться к свойству .Имя программа «упадет».

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


Функция ОпределитьИмяТипаДокумента(Документ) Экспорт
    // Если Документ — это СписокЗначений, НайтиПоТипу вернет Неопределено
    МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(Документ)); 
    Возврат МетаданныеОбъекта.Имя; // Ошибка здесь!
КонецФункции

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

Решение 1: Базовый цикл обхода списка значений

Если нам нужно просто последовательно вызвать существующую процедуру выгрузки для каждого документа из списка, мы можем использовать цикл Для каждого. Важно помнить, что в объекте СписокЗначений каждый элемент имеет свойство Значение, где и хранится ссылка на документ.

Рассмотрим пошагово реализацию в обычном приложении:

  1. Создаем экземпляр обработки выгрузки.
  2. Организуем цикл по элементам списка.
  3. Передаем в процедуру именно значение элемента.

Посмотрим на пример кода:


ОбработкаВыгрузки = Обработки.ВыгрузкаДанныхCommerceML.Создать();
// ВыборДокументов — это наш СписокЗначений с документами
Для каждого ЭлементСписка Из ВыборДокументов Цикл
    // Извлекаем ссылку на документ из элемента списка
    ДокументСсылка = ЭлементСписка.Значение;
    
    // Вызываем экспортную процедуру обработки
    ОбработкаВыгрузки.ВыгрузитьИзДокумента(ДокументСсылка);
КонецЦикла;

Важный нюанс: Если в процедуре выгрузки внутри используется Предупреждение() или Вопрос(), пользователю придется нажимать «ОК» для каждого документа. Это крайне неудобно при обработке 100 и более позиций.

Решение 2: Пакетная выгрузка в один каталог (без модальных окон)

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

Мы создадим новую процедуру ВыгрузитьМассивДокументов, которая принимает массив ссылок и путь к каталогу. Проанализируем реализацию:


Процедура ВыгрузитьМассивДокументов(МассивДокументов, ИмяКаталога) Экспорт
    МассивИмен = Новый Массив;
    
    Для Каждого Документ Из МассивДокументов Цикл
        // Проверка проведения (бизнес-логика)
        ИмяТипа = ОпределитьИмяТипаДокумента(Документ);
        Если НЕ Документ.Проведен Тогда
            Сообщить("Документ " + Строка(Документ) + " пропущен: не проведен.");
            Продолжить;
        КонецЕсли;

        // Формируем имя файла на основе контрагента и типа
        ИмяФайла = ОпределитьИмяФайлаДляСохранения(Документ.Контрагент, ИмяТипа);
        
        // Проверяем уникальность имени в рамках текущей выгрузки
        Если МассивИмен.Найти(ИмяФайла) <> Неопределено Тогда
            ИмяФайла = ИмяФайла + "_" + Формат(ТекущаяДата(), "ДФ=HHmmss");
        КонецЕсли;
        МассивИмен.Добавить(ИмяФайла);

        // Полный путь к файлу
        ПолныйПуть = ИмяКаталога + "\" + ИмяФайла + ".xml";
        
        // Вызываем сохранение напрямую, минуя диалоги
        СохранитьДокументВФайл(Документ, ПолныйПуть);
    КонецЦикла;
КонецПроцедуры

Для запуска этого процесса на форме обработки достаточно один раз вызвать ДиалогВыбораФайла в режиме выбора каталога:


Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Если Диалог.Выбрать() Тогда
    Путь = Диалог.ПолноеИмяФайла;
    МассивСсылок = СписокДокументов.ВыгрузитьЗначения();
    ОбработкаОбъект.ВыгрузитьМассивДокументов(МассивСсылок, Путь);
    Предупреждение("Пакетная выгрузка завершена успешно!");
КонецЕсли;

Решение 3: Что делать, если внешнюю обработку нельзя изменять?

Бывают ситуации, когда .epf файл защищен или является типовым, и вносить в него правки запрещено регламентом. В этом случае мы сталкиваемся с проблемой: метод ВыгрузитьИзДокумента внутри себя жестко вызывает диалог сохранения. Рассмотрим хитрости, которые применяются в «Обычных формах» для решения таких задач:

1. Использование SendKeys (эмуляция нажатий). Это «грязный» метод, но в среде Windows он позволяет программно «нажать» Enter за пользователя. Перед вызовом процедуры выгрузки подготавливается скрипт:


WshShell = Новый COMОбъект("WScript.Shell");
// Вызываем процедуру, которая откроет диалог
ОбработкаВыгрузки.ВыгрузитьИзДокумента(Документ);
// Посылаем сигнал нажатия Enter через небольшую паузу
WshShell.SendKeys("{ENTER}");

Внимание: Этот метод нестабилен и зависит от фокуса окна, поэтому использовать его стоит только в крайнем случае.

2. Программная подмена текста модуля. Можно считать текст внешней обработки как текстовый файл, программно заменить в строке Предупреждение на Сообщить, сохранить во временный файл и запустить его. Это требует глубоких знаний работы с объектом ВнешниеОбработки, но гарантирует «тихую» работу кода.

Особенности работы с типами метаданных в обычном приложении

При реализации собственных процедур обработки всегда помните о разнице между ДокументОбъект и ДокументСсылка. Большинство стандартных выгрузок требуют именно Объект, так как им нужен доступ к табличным частям и дополнительным свойствам. Если у вас в списке только ссылки, не забывайте получать объект методом .ПолучитьОбъект(), прежде чем обращаться к метаданным или данным документа, чтобы минимизировать количество неявных запросов к БД внутри цикла.

Выясним разницу на примере обращения к реквизитам:

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

← На главную