Как объединить данные из разных справочников, включая вложенные структуры, в единую табличную часть документа 1С?

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

При разработке конфигураций в 1С часто возникает задача, когда необходимо заполнить табличную часть документа данными, которые могут поступать из различных источников. Например, состав некоторого объекта (блюда, комплекта, набора) может формироваться как из отдельных элементов (продуктов), так и из других составных объектов (других блюд, полуфабрикатов), каждый из которых, в свою очередь, имеет свой состав. В таких ситуациях прямой метод ТабличнаяЧасть.Загрузить() может не сработать, так как он требует строгого соответствия структуры полей. Рассмотрим подробнее, как решить эту задачу, обрабатывая данные построчно с проверкой типов.

В чем заключается проблема при загрузке данных из разных справочников

Представим, что у нас есть документ, в котором есть реквизит, ссылающийся либо на справочник «Блюда», либо на справочник «Наборы». В документе также присутствует табличная часть «Продукты», куда должен выводиться полный состав выбранного блюда или набора.

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

  1. Для справочника «Блюда», у которого есть табличная часть «Продукты» (содержащая ссылку на справочник «Продукты»), заполнение табличной части документа работает корректно с помощью кода:
    
    Прод = Объект.Блюдо.Продукты.Выгрузить();
    Объект.Продукты.Загрузить(Прод);
    
    Это происходит потому, что структура табличной части Блюдо.Продукты совпадает со структурой табличной части Документ.Продукты, и метод Загрузить() успешно переносит данные.
  2. Однако для справочника «Наборы», у которого также есть табличная часть «Состав», но с тремя ссылочными полями (Строка, СсылкаСправочникаБлюда, СсылкаСправочникаПродукты), заполнение табличной части документа не работает. При попытке использовать аналогичный код, как в случае с блюдами, выводятся пустые поля, хотя количество строк соответствует составу набора. Проблема проявляется при использовании кода:
    
    Набор = Объект.Блюдо.Состав.Выгрузить();
    Объект.Продукты.Загрузить(Набор);
    
    Мы видим, что метод Загрузить() не справляется с задачей, поскольку структура выгружаемой таблицы (Набор.Состав) значительно отличается от структуры принимающей таблицы (Документ.Продукты). Поля в Набор.Состав — это ссылки на блюда или продукты, а не сами данные продуктов, которые должны быть в табличной части документа. Более того, если в составе набора есть ссылка на блюдо, то необходимо «распаковать» это блюдо и получить его собственные продукты.

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

Пошаговое заполнение табличной части с проверкой типов

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

  1. Очистка табличной части документа. Перед началом заполнения всегда рекомендуется очищать табличную часть документа, чтобы избежать дублирования данных или устаревшей информации. Объект.Продукты.Очистить();
  2. Определение основного объекта. В нашем случае, документ имеет реквизит Объект.Блюдо, который может хранить ссылку либо на справочник «Блюда», либо на справочник «Наборы». Нам необходимо определить, какой тип объекта выбран, чтобы применить соответствующую логику.
  3. Итерация по составу и проверка типов элементов. Мы будем перебирать каждый элемент в составе выбранного объекта (блюда или набора) и для каждого элемента определять его тип с помощью функции ТипЗнч().

Пример кода для обработки состава Блюд и Наборов

Рассмотрим полный пример кода, который позволит нам правильно заполнить табличную часть документа «Продукты», вне зависимости от того, выбрано ли в документе «Блюдо» или «Набор».

Предположим, что в табличной части документа Объект.Продукты есть следующие поля:

В справочнике «Блюда» табличная часть «Продукты» содержит поля:

В справочнике «Наборы» табличная часть «Состав» содержит поля (согласно описанию автора):

Теперь приступим к реализации:


&НаСервере
Процедура ЗаполнитьТабличнуюЧастьПродукты()
    // 1. Очищаем текущую табличную часть документа, чтобы избежать дублирования
    Объект.Продукты.Очистить();

    // 2. Определяем тип выбранного основного элемента (Блюдо или Набор)
    Если ТипЗнч(Объект.Блюдо) = Тип("СправочникСсылка.Наборы") Тогда
        // Обработка, если выбран Набор
        ТекущийНаборОбъект = Объект.Блюдо.ПолучитьОбъект();

        Если ТекущийНаборОбъект <> Неопределено Тогда
            // Итерируем по табличной части "Состав" справочника "Наборы"
            Для Каждого СтрокаСоставаНабора Из ТекущийНаборОбъект.Состав Цикл

                // Проверяем, какое из полей-ссылок заполнено в текущей строке состава набора
                Если ЗначениеЗаполнено(СтрокаСоставаНабора.СсылкаСправочникаПродукты) Тогда
                    // Если в строке указан конкретный Продукт
                    НоваяСтрокаТЧ = Объект.Продукты.Добавить();
                    НоваяСтрокаТЧ.Продукт = СтрокаСоставаНабора.СсылкаСправочникаПродукты;
                    // Предполагаем, что в ТЧ Набора есть поле "Количество" для элемента
                    НоваяСтрокаТЧ.Количество = СтрокаСоставаНабора.Количество;
                    // Дополнительные поля могут быть заполнены из самого продукта,
                    // например, НоваяСтрокаТЧ.ЕдиницаИзмерения = СтрокаСоставаНабора.СсылкаСправочникаПродукты.ЕдиницаХранения;
                    // Если поле "Строка" в Наборе.Состав содержит комментарий
                    // НоваяСтрокаТЧ.Комментарий = СтрокаСоставаНабора.Строка;

                ИначеЕсли ЗначениеЗаполнено(СтрокаСоставаНабора.СсылкаСправочникаБлюда) Тогда
                    // Если в строке указано Блюдо, нам нужно получить его собственный состав продуктов
                    БлюдоИзНабораОбъект = СтрокаСоставаНабора.СсылкаСправочникаБлюда.ПолучитьОбъект();
                    Если БлюдоИзНабораОбъект <> Неопределено Тогда
                        // Итерируем по табличной части "Продукты" справочника "Блюда"
                        Для Каждого ПродуктИзБлюда Из БлюдоИзНабораОбъект.Продукты Цикл
                            НоваяСтрокаТЧ = Объект.Продукты.Добавить();
                            НоваяСтрокаТЧ.Продукт = ПродуктИзБлюда.Продукт; // Ссылка на Продукт
                            // Учитываем количество продукта из самого блюда,
                            // умножая на количество "порций" блюда, указанных в наборе
                            НоваяСтрокаТЧ.Количество = ПродуктИзБлюда.Количество * СтрокаСоставаНабора.Количество;
                        КонецЦикла;
                    КонецЕсли;
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;

    ИначеЕсли ТипЗнч(Объект.Блюдо) = Тип("СправочникСсылка.Блюда") Тогда
        // Обработка, если выбрано простое Блюдо.
        // Хотя метод Загрузить() может работать здесь, для унификации и возможности
        // более гибкой обработки (например, добавления дополнительных полей или логики),
        // лучше использовать тот же подход с циклом.
        ТекущееБлюдоОбъект = Объект.Блюдо.ПолучитьОбъект();

        Если ТекущееБлюдоОбъект <> Неопределено Тогда
            Для Каждого ПродуктБлюда Из ТекущееБлюдоОбъект.Продукты Цикл
                НоваяСтрокаТЧ = Объект.Продукты.Добавить();
                НоваяСтрокаТЧ.Продукт = ПродуктБлюда.Продукт;
                НоваяСтрокаТЧ.Количество = ПродуктБлюда.Количество;
            КонецЦикла;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Важные замечания по коду:

  1. Мы предполагаем, что в табличной части Наборы.Состав есть поле Количество, которое указывает, сколько единиц Блюда или Продукта входит в Набор. Если этого поля нет, логику пересчета необходимо скорректировать.
  2. Для полей СсылкаСправочникаБлюда и СсылкаСправочникаПродукты в Наборы.Состав используется проверка ЗначениеЗаполнено(), так как предполагается, что в каждой строке заполняется только одно из этих полей.
  3. Если в вашей конфигурации есть стандартные реквизиты, такие как ЕдиницаИзмерения, их также следует заполнять, используя данные из справочника Продукты или других источников.
  4. Эта процедура должна быть вызвана при изменении реквизита Объект.Блюдо в документе, например, в обработчике события Объект.БлюдоПриИзменении() формы документа.

Дополнительные рекомендации и анализ причин

Для того чтобы эффективно решать подобные задачи и понимать, почему возникают трудности, рассмотрим несколько ключевых моментов:

  1. Явное указание полей при заполнении: Метод Загрузить() удобен, когда структура источника (например, табличная часть Блюда.Продукты) полностью совпадает со структурой приемника (табличная часть Документ.Продукты). В случаях, когда имена полей отличаются, или когда требуется более сложная логика (например, "распаковка" вложенных структур, как в случае с Наборами), необходимо заполнять табличную часть построчно. Это позволяет явно указать, какое поле из источника должно быть записано в каждое конкретное поле табличной части документа.
  2. Обработка вложенных табличных частей: Как мы видели в примере с Наборами, если элемент состава является другим составным объектом (Блюдом), необходимо получить его собственный объект и итерировать по его табличной части, чтобы извлечь окончательные компоненты (Продукты). Это требует дополнительного внутреннего цикла.
  3. Синхронизация полей табличных частей: Убедитесь, что имена и типы полей в табличной части документа, куда происходит загрузка (Объект.Продукты), соответствуют тем данным, которые вы пытаетесь в них записать. Если табличная часть документа ожидает СправочникСсылка.Продукты и дополнительную информацию (количество, цена), то поля из состава набора должны быть корректно отображены на эти поля.
  4. Отладка и анализ типов: При возникновении проблем с пустыми полями или некорректным отображением данных, критически важно использовать отладчик 1С. Пошаговое выполнение кода позволит нам проверить, какие значения присваиваются каждой переменной, какой тип имеет каждый элемент и строка табличной части (ТипЗнч()), и корректно ли происходит заполнение каждого поля. Это поможет выявить расхождения в типах или именах полей, которые могут быть причиной проблемы.

Таким образом, хотя решение кажется более объемным, чем простая функция Загрузить(), оно обеспечивает необходимую гибкость и контроль при работе со сложными, составными типами данных в табличных частях 1С.

← На главную