При разработке механизмов интеграции часто возникает задача сбора данных из регистров по большому количеству аналитик (например, складов) с последующей выгрузкой в XML. Если складов два или три, можно обойтись простыми переменными, но когда их становится 17 и более, код превращается в громоздкое полотно. Рассмотрим, как оптимизировать этот процесс, сделать код гибким и избежать "ручного" перечисления каждого склада.
Первый вопрос, который часто возникает: как программно обратиться к переменной или реквизиту, имя которого формируется на лету? В языке 1С существует несколько способов сделать это. Рассмотрим их подробнее.
Если данные хранятся в реквизитах формы или объекта, мы можем использовать синтаксис квадратных скобок. Это гораздо быстрее и безопаснее, чем использование функции Вычислить(), так как не требует запуска интерпретатора кода для каждой строки. Проанализируем пример кода:
Для Индекс = 1 По КоличествоСкладов Цикл
// Обращаемся к реквизиту ЭтотОбъект.КоличествоСклад1, ЭтотОбъект.КоличествоСклад2 и т.д.
ЭтотОбъект["КоличествоСклад" + Индекс] = 0;
КонецЦикла;
Однако стоит помнить, что такой подход требует наличия заранее созданных реквизитов. В задачах выгрузки данных гораздо эффективнее использовать объект Структура или Соответствие. Это позволяет динамически создавать "переменные" во время выполнения программы.
Когда нам нужно связать конкретный склад (ссылку) с числовым значением (количеством), идеально подходит объект Соответствие. Ключом будет являться ссылка на склад, а значением — сумма остатка. Разберем по шагам, как это реализовать:
Соответствие, заполнив его начальными значениями (нулями) для каждого склада.
СоответствиеСкладов = Новый Соответствие;
Для Каждого ТекСклад Из СписокНужныхСкладов Цикл
СоответствиеСкладов.Вставить(ТекСклад.Значение, 0);
КонецЦикла;
Выборка = ЗапросПоКоличеству.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Прямое обращение по ссылке на склад
ТекущийОстаток = СоответствиеСкладов[Выборка.Склад];
Если ТекущийОстаток <> Неопределено Тогда
СоответствиеСкладов.Вставить(Выборка.Склад, ТекущийОстаток + Выборка.КоличествоОстаток);
КонецЕсли;
КонецЦикла;
Преимущество этого метода в том, что нам не важно, сколько складов в списке — код остается компактным и производительным. При выгрузке в XML мы просто проитерируем Соответствие.
Для формирования XML-файла часто требуется не только количество, но и специфическое имя узла (тега). Рассмотрим ситуацию, когда имя узла должно зависеть от наименования склада. Проанализируем подход с использованием ТаблицыЗначений, которая будет выступать в роли "карты" соответствия склада и структуры XML.
ТЗ_Склады = Новый ТаблицаЗначений;
ТЗ_Склады.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
ТЗ_Склады.Колонки.Добавить("ИмяУзлаXML", Новый ОписаниеТипов("Строка"));
ТЗ_Склады.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
// Заполняем таблицу и создаем индексы для быстрого поиска
ТЗ_Склады.Индексы.Добавить("Склад");
// В цикле по выборке запроса обновляем данные
Пока Выборка.Следующий() Цикл
СтрокаТЗ = ТЗ_Склады.Найти(Выборка.Склад, "Склад");
Если СтрокаТЗ <> Неопределено Тогда
СтрокаТЗ.Количество = Выборка.КоличествоОстаток;
КонецЕсли;
КонецЦикла;
Важный момент: при формировании ИмяУзлаXML из наименования склада необходимо следить за правилами именования узлов XML. Имя тега не может содержать пробелы и начинаться с цифры. Рассмотрим функцию для безопасного формирования имени:
// Пример преобразования "Склад №19" в "Склад_19"
ИмяУзла = СтрЗаменить(Склад.Наименование, " ", "_");
ИмяУзла = СтрЗаменить(ИмяУзла, "№", "");
Для получения корректных данных по всем складам (даже по тем, где остаток равен нулю), рекомендуется использовать ЛЕВОЕ СОЕДИНЕНИЕ справочника складов с виртуальной таблицей остатков. Выясним, почему это важно:
Если использовать только таблицу РегистрНакопления.ТоварыНаСкладах.Остатки, то склады с нулевым остатком просто не попадут в результат запроса. Если логика XML требует обязательного наличия узла склада (пусть и с нулем), запрос должен выглядеть так:
ВЫБРАТЬ
СпрСклады.Ссылка КАК Склад,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК КоличествоОстаток
ИЗ
Справочник.Склады КАК СпрСклады
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаСреза, Номенклатура = &Номенклатура) КАК Остатки
ПО
СпрСклады.Ссылка = Остатки.Склад
ГДЕ
СпрСклады.Ссылка В (&СписокСкладов)
Проанализируем также использование параметра &ДатаСреза. Если нам нужны актуальные текущие остатки, платформе 1С проще получить их, если параметр даты вообще не указан. В этом случае система обращается напрямую к таблице итогов, не выполняя расчет на конкретную секунду, что значительно ускоряет выполнение запроса.
Если же дата необходима, помните, что для получения остатков на текущий момент (включая движения последнего документа) лучше использовать объект Граница с типом ГраницаТипа.Включая.
После того как данные собраны в Соответствие или ТаблицуЗначений, мы переходим к финальному этапу. Вместо того чтобы писать 17 строк ДобавитьУзелCML(), используем цикл по подготовленной коллекции:
Для Каждого СтрокаДанных Из ТЗ_Склады Цикл
// Вызываем функцию добавления узла, передавая имя и значение динамически
ДобавитьУзелCML(СписокЗначенийCML, СтрокаДанных.ИмяУзлаXML, СтрокаДанных.Количество);
КонецЦикла;
Подведем итог: для эффективного решения задачи динамического присвоения и выгрузки данных следует отказаться от именованных переменных в пользу коллекций (Структура, Соответствие, ТаблицаЗначений). Это позволит вашему коду работать с любым количеством складов без изменения логики программы. Использование индексированных таблиц и оптимизированных запросов обеспечит высокую скорость работы даже при больших объемах номенклатуры.