При работе в классических конфигурациях на «Обычных формах» разработчики часто сталкиваются с задачей автоматизации рутинных процессов, таких как пакетная выгрузка документов. Основная сложность заключается не только в правильном переборе коллекции, но и в корректной обработке типов данных, чтобы избежать ошибок выполнения. Рассмотрим подробно, как преобразовать СписокЗначений в набор объектов и автоматизировать выгрузку, когда стандартные методы требуют ручного подтверждения для каждого файла.
Часто при попытке передать коллекцию (массив или список) в функцию, ожидающую одиночную ссылку, возникает критическая ошибка. Разберем ситуацию: если в коде используется метод Метаданные.НайтиПоТипу(ТипЗнч(Документ)), а в переменную Документ вместо ссылки на конкретную реализацию попал весь СписокЗначений, система не сможет определить тип метаданных. Метод НайтиПоТипу вернет Неопределено, и при попытке обратиться к свойству .Имя программа «упадет».
Проанализируем пример некорректного кода, который часто приводит к таким последствиям:
Функция ОпределитьИмяТипаДокумента(Документ) Экспорт
// Если Документ — это СписокЗначений, НайтиПоТипу вернет Неопределено
МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(Документ));
Возврат МетаданныеОбъекта.Имя; // Ошибка здесь!
КонецФункции
Чтобы этого избежать, необходимо перед вызовом логики выгрузки убедиться, что мы работаем с конкретным элементом коллекции, а не с самой коллекцией.
Если нам нужно просто последовательно вызвать существующую процедуру выгрузки для каждого документа из списка, мы можем использовать цикл Для каждого. Важно помнить, что в объекте СписокЗначений каждый элемент имеет свойство Значение, где и хранится ссылка на документ.
Рассмотрим пошагово реализацию в обычном приложении:
Посмотрим на пример кода:
ОбработкаВыгрузки = Обработки.ВыгрузкаДанныхCommerceML.Создать();
// ВыборДокументов — это наш СписокЗначений с документами
Для каждого ЭлементСписка Из ВыборДокументов Цикл
// Извлекаем ссылку на документ из элемента списка
ДокументСсылка = ЭлементСписка.Значение;
// Вызываем экспортную процедуру обработки
ОбработкаВыгрузки.ВыгрузитьИзДокумента(ДокументСсылка);
КонецЦикла;
Важный нюанс: Если в процедуре выгрузки внутри используется Предупреждение() или Вопрос(), пользователю придется нажимать «ОК» для каждого документа. Это крайне неудобно при обработке 100 и более позиций.
Для того чтобы избежать постоянного появления диалоговых окон выбора файла, нам необходимо модифицировать логику или создать «обертку», которая будет автоматически генерировать пути к файлам. Выясним, как это сделать, если у нас есть возможность изменить код обработки.
Мы создадим новую процедуру ВыгрузитьМассивДокументов, которая принимает массив ссылок и путь к каталогу. Проанализируем реализацию:
Процедура ВыгрузитьМассивДокументов(МассивДокументов, ИмяКаталога) Экспорт
МассивИмен = Новый Массив;
Для Каждого Документ Из МассивДокументов Цикл
// Проверка проведения (бизнес-логика)
ИмяТипа = ОпределитьИмяТипаДокумента(Документ);
Если НЕ Документ.Проведен Тогда
Сообщить("Документ " + Строка(Документ) + " пропущен: не проведен.");
Продолжить;
КонецЕсли;
// Формируем имя файла на основе контрагента и типа
ИмяФайла = ОпределитьИмяФайлаДляСохранения(Документ.Контрагент, ИмяТипа);
// Проверяем уникальность имени в рамках текущей выгрузки
Если МассивИмен.Найти(ИмяФайла) <> Неопределено Тогда
ИмяФайла = ИмяФайла + "_" + Формат(ТекущаяДата(), "ДФ=HHmmss");
КонецЕсли;
МассивИмен.Добавить(ИмяФайла);
// Полный путь к файлу
ПолныйПуть = ИмяКаталога + "\" + ИмяФайла + ".xml";
// Вызываем сохранение напрямую, минуя диалоги
СохранитьДокументВФайл(Документ, ПолныйПуть);
КонецЦикла;
КонецПроцедуры
Для запуска этого процесса на форме обработки достаточно один раз вызвать ДиалогВыбораФайла в режиме выбора каталога:
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Если Диалог.Выбрать() Тогда
Путь = Диалог.ПолноеИмяФайла;
МассивСсылок = СписокДокументов.ВыгрузитьЗначения();
ОбработкаОбъект.ВыгрузитьМассивДокументов(МассивСсылок, Путь);
Предупреждение("Пакетная выгрузка завершена успешно!");
КонецЕсли;
Бывают ситуации, когда .epf файл защищен или является типовым, и вносить в него правки запрещено регламентом. В этом случае мы сталкиваемся с проблемой: метод ВыгрузитьИзДокумента внутри себя жестко вызывает диалог сохранения. Рассмотрим хитрости, которые применяются в «Обычных формах» для решения таких задач:
1. Использование SendKeys (эмуляция нажатий). Это «грязный» метод, но в среде Windows он позволяет программно «нажать» Enter за пользователя. Перед вызовом процедуры выгрузки подготавливается скрипт:
WshShell = Новый COMОбъект("WScript.Shell");
// Вызываем процедуру, которая откроет диалог
ОбработкаВыгрузки.ВыгрузитьИзДокумента(Документ);
// Посылаем сигнал нажатия Enter через небольшую паузу
WshShell.SendKeys("{ENTER}");
Внимание: Этот метод нестабилен и зависит от фокуса окна, поэтому использовать его стоит только в крайнем случае.
2. Программная подмена текста модуля. Можно считать текст внешней обработки как текстовый файл, программно заменить в строке Предупреждение на Сообщить, сохранить во временный файл и запустить его. Это требует глубоких знаний работы с объектом ВнешниеОбработки, но гарантирует «тихую» работу кода.
При реализации собственных процедур обработки всегда помните о разнице между ДокументОбъект и ДокументСсылка. Большинство стандартных выгрузок требуют именно Объект, так как им нужен доступ к табличным частям и дополнительным свойствам. Если у вас в списке только ссылки, не забывайте получать объект методом .ПолучитьОбъект(), прежде чем обращаться к метаданным или данным документа, чтобы минимизировать количество неявных запросов к БД внутри цикла.
Выясним разницу на примере обращения к реквизитам:
ДокументСсылка.Контрагент — вызывает неявное обращение к базе для чтения одного поля.ДокументОбъект = ДокументСсылка.ПолучитьОбъект() — загружает весь документ в память один раз.Проанализировав ситуацию, можно сделать вывод: для эффективной работы со списками в старых системах 1С 8.x лучше всего переводить СписокЗначений в Массив и использовать централизованную процедуру обработки, вынесенную в модуль объекта, передавая путь к каталогу параметром.