При разработке конфигураций в 1С часто возникает задача, когда необходимо заполнить табличную часть документа данными, которые могут поступать из различных источников. Например, состав некоторого объекта (блюда, комплекта, набора) может формироваться как из отдельных элементов (продуктов), так и из других составных объектов (других блюд, полуфабрикатов), каждый из которых, в свою очередь, имеет свой состав. В таких ситуациях прямой метод ТабличнаяЧасть.Загрузить() может не сработать, так как он требует строгого соответствия структуры полей. Рассмотрим подробнее, как решить эту задачу, обрабатывая данные построчно с проверкой типов.
Представим, что у нас есть документ, в котором есть реквизит, ссылающийся либо на справочник «Блюда», либо на справочник «Наборы». В документе также присутствует табличная часть «Продукты», куда должен выводиться полный состав выбранного блюда или набора.
Исходная проблема, с которой мы столкнулись, заключается в следующем:
Прод = Объект.Блюдо.Продукты.Выгрузить();
Объект.Продукты.Загрузить(Прод);
Блюдо.Продукты совпадает со структурой табличной части Документ.Продукты, и метод Загрузить() успешно переносит данные.Строка, СсылкаСправочникаБлюда, СсылкаСправочникаПродукты), заполнение табличной части документа не работает. При попытке использовать аналогичный код, как в случае с блюдами, выводятся пустые поля, хотя количество строк соответствует составу набора. Проблема проявляется при использовании кода:
Набор = Объект.Блюдо.Состав.Выгрузить();
Объект.Продукты.Загрузить(Набор);
Загрузить() не справляется с задачей, поскольку структура выгружаемой таблицы (Набор.Состав) значительно отличается от структуры принимающей таблицы (Документ.Продукты). Поля в Набор.Состав — это ссылки на блюда или продукты, а не сами данные продуктов, которые должны быть в табличной части документа. Более того, если в составе набора есть ссылка на блюдо, то необходимо «распаковать» это блюдо и получить его собственные продукты.Для решения этой проблемы нам потребуется использовать подход с пошаговым заполнением табличной части документа, явно проверяя тип каждого элемента состава и извлекая необходимые данные.
Чтобы корректно собрать данные из разных справочников и их вложенных структур в единую табличную часть документа, мы будем использовать цикл и проверку типа каждого элемента состава. Это позволит нам применить специфическую логику для каждого типа элемента.
Объект.Продукты.Очистить();
Объект.Блюдо, который может хранить ссылку либо на справочник «Блюда», либо на справочник «Наборы». Нам необходимо определить, какой тип объекта выбран, чтобы применить соответствующую логику.ТипЗнч().Рассмотрим полный пример кода, который позволит нам правильно заполнить табличную часть документа «Продукты», вне зависимости от того, выбрано ли в документе «Блюдо» или «Набор».
Предположим, что в табличной части документа Объект.Продукты есть следующие поля:
Продукт (тип: СправочникСсылка.Продукты)Количество (тип: Число)ЕдиницаИзмерения (тип: СправочникСсылка.ЕдиницыИзмерения, если есть)В справочнике «Блюда» табличная часть «Продукты» содержит поля:
Продукт (тип: СправочникСсылка.Продукты)Количество (тип: Число)В справочнике «Наборы» табличная часть «Состав» содержит поля (согласно описанию автора):
Строка (возможно, текстовое описание или номер)СсылкаСправочникаБлюда (тип: СправочникСсылка.Блюда)СсылкаСправочникаПродукты (тип: СправочникСсылка.Продукты)Количество (тип: Число) - для указания, сколько единиц этого элемента входит в набор.Теперь приступим к реализации:
&НаСервере
Процедура ЗаполнитьТабличнуюЧастьПродукты()
// 1. Очищаем текущую табличную часть документа, чтобы избежать дублирования
Объект.Продукты.Очистить();
// 2. Определяем тип выбранного основного элемента (Блюдо или Набор)
Если ТипЗнч(Объект.Блюдо) = Тип("СправочникСсылка.Наборы") Тогда
// Обработка, если выбран Набор
ТекущийНаборОбъект = Объект.Блюдо.ПолучитьОбъект();
Если ТекущийНаборОбъект <> Неопределено Тогда
// Итерируем по табличной части "Состав" справочника "Наборы"
Для Каждого СтрокаСоставаНабора Из ТекущийНаборОбъект.Состав Цикл
// Проверяем, какое из полей-ссылок заполнено в текущей строке состава набора
Если ЗначениеЗаполнено(СтрокаСоставаНабора.СсылкаСправочникаПродукты) Тогда
// Если в строке указан конкретный Продукт
НоваяСтрокаТЧ = Объект.Продукты.Добавить();
НоваяСтрокаТЧ.Продукт = СтрокаСоставаНабора.СсылкаСправочникаПродукты;
// Предполагаем, что в ТЧ Набора есть поле "Количество" для элемента
НоваяСтрокаТЧ.Количество = СтрокаСоставаНабора.Количество;
// Дополнительные поля могут быть заполнены из самого продукта,
// например, НоваяСтрокаТЧ.ЕдиницаИзмерения = СтрокаСоставаНабора.СсылкаСправочникаПродукты.ЕдиницаХранения;
// Если поле "Строка" в Наборе.Состав содержит комментарий
// НоваяСтрокаТЧ.Комментарий = СтрокаСоставаНабора.Строка;
ИначеЕсли ЗначениеЗаполнено(СтрокаСоставаНабора.СсылкаСправочникаБлюда) Тогда
// Если в строке указано Блюдо, нам нужно получить его собственный состав продуктов
БлюдоИзНабораОбъект = СтрокаСоставаНабора.СсылкаСправочникаБлюда.ПолучитьОбъект();
Если БлюдоИзНабораОбъект <> Неопределено Тогда
// Итерируем по табличной части "Продукты" справочника "Блюда"
Для Каждого ПродуктИзБлюда Из БлюдоИзНабораОбъект.Продукты Цикл
НоваяСтрокаТЧ = Объект.Продукты.Добавить();
НоваяСтрокаТЧ.Продукт = ПродуктИзБлюда.Продукт; // Ссылка на Продукт
// Учитываем количество продукта из самого блюда,
// умножая на количество "порций" блюда, указанных в наборе
НоваяСтрокаТЧ.Количество = ПродуктИзБлюда.Количество * СтрокаСоставаНабора.Количество;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ИначеЕсли ТипЗнч(Объект.Блюдо) = Тип("СправочникСсылка.Блюда") Тогда
// Обработка, если выбрано простое Блюдо.
// Хотя метод Загрузить() может работать здесь, для унификации и возможности
// более гибкой обработки (например, добавления дополнительных полей или логики),
// лучше использовать тот же подход с циклом.
ТекущееБлюдоОбъект = Объект.Блюдо.ПолучитьОбъект();
Если ТекущееБлюдоОбъект <> Неопределено Тогда
Для Каждого ПродуктБлюда Из ТекущееБлюдоОбъект.Продукты Цикл
НоваяСтрокаТЧ = Объект.Продукты.Добавить();
НоваяСтрокаТЧ.Продукт = ПродуктБлюда.Продукт;
НоваяСтрокаТЧ.Количество = ПродуктБлюда.Количество;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важные замечания по коду:
Наборы.Состав есть поле Количество, которое указывает, сколько единиц Блюда или Продукта входит в Набор. Если этого поля нет, логику пересчета необходимо скорректировать.СсылкаСправочникаБлюда и СсылкаСправочникаПродукты в Наборы.Состав используется проверка ЗначениеЗаполнено(), так как предполагается, что в каждой строке заполняется только одно из этих полей.ЕдиницаИзмерения, их также следует заполнять, используя данные из справочника Продукты или других источников.Объект.Блюдо в документе, например, в обработчике события Объект.БлюдоПриИзменении() формы документа.Для того чтобы эффективно решать подобные задачи и понимать, почему возникают трудности, рассмотрим несколько ключевых моментов:
Загрузить() удобен, когда структура источника (например, табличная часть Блюда.Продукты) полностью совпадает со структурой приемника (табличная часть Документ.Продукты). В случаях, когда имена полей отличаются, или когда требуется более сложная логика (например, "распаковка" вложенных структур, как в случае с Наборами), необходимо заполнять табличную часть построчно. Это позволяет явно указать, какое поле из источника должно быть записано в каждое конкретное поле табличной части документа.Наборами, если элемент состава является другим составным объектом (Блюдом), необходимо получить его собственный объект и итерировать по его табличной части, чтобы извлечь окончательные компоненты (Продукты). Это требует дополнительного внутреннего цикла.Объект.Продукты), соответствуют тем данным, которые вы пытаетесь в них записать. Если табличная часть документа ожидает СправочникСсылка.Продукты и дополнительную информацию (количество, цена), то поля из состава набора должны быть корректно отображены на эти поля.ТипЗнч()), и корректно ли происходит заполнение каждого поля. Это поможет выявить расхождения в типах или именах полей, которые могут быть причиной проблемы.Таким образом, хотя решение кажется более объемным, чем простая функция Загрузить(), оно обеспечивает необходимую гибкость и контроль при работе со сложными, составными типами данных в табличных частях 1С.