При работе с системой 1С:Предприятие, особенно в конфигурации Управление торговлей 10.3, часто возникает задача выгрузки информации о номенклатуре. Одной из специфических и достаточно сложных подзадач является необходимость получить полный список всех свойств номенклатуры, включая те, для которых на данный момент не заданы никакие значения. То есть, если у нас есть свойство "Цвет", и для какой-то позиции номенклатуры оно не заполнено, мы хотим увидеть эту позицию, свойство "Цвет" и пустое (или нулевое) значение для него.
Первоначальная мысль многих разработчиков — использовать ПОЛНОЕ СОЕДИНЕНИЕ в запросе к регистру сведений ЗначенияСвойствОбъектов. Однако, как мы убедимся, такой подход не всегда дает желаемый результат. Давайте рассмотрим подробнее, почему так происходит, и как правильно решить эту задачу в условиях УТ 10.3.
Прежде чем перейти к решению, важно понять, как устроено хранение дополнительных свойств в конфигурации 1С:Управление торговлей 10.3. В отличие от более новых конфигураций, где может использоваться справочник "Дополнительные реквизиты и сведения", в УТ 10.3 для работы с дополнительными свойствами используются следующие объекты:
СвойстваОбъектов: Здесь хранятся определения самих дополнительных свойств. Каждое свойство имеет наименование, тип значения (например, строка, число, булево, ссылка на другой справочник) и другие параметры. Это своего рода "метаданные" для наших свойств.
НазначениеСвойствОбъектов: Этот регистр связывает определенные виды объектов (в нашем случае это будет Справочник.Номенклатура) с набором свойств из ПВХ СвойстваОбъектов, которые могут быть применены к этим объектам. Именно здесь мы узнаем, какие свойства "доступны" для номенклатуры.
ЗначенияСвойствОбъектов: Этот регистр хранит фактические значения дополнительных свойств для конкретных экземпляров объектов. И вот здесь кроется ключевая особенность: записи в этом регистре существуют только для тех свойств, которые имеют какое-либо значение. Если свойство не заполнено для какого-либо объекта, записи о нем в этом регистре просто нет. Это означает, что прямое соединение только с этим регистром никогда не позволит нам увидеть незаполненные свойства.
Исходя из этой структуры, для получения полного списка свойств (включая пустые) нам понадобится комплексный подход, который учитывает информацию из всех трех упомянутых объектов.
Давайте разберем по шагам, как построить запрос, который позволит нам выгрузить все позиции номенклатуры и все доступные для них свойства, указывая пустые значения там, где они отсутствуют.
Первым делом нам необходимо получить исчерпывающий список всех дополнительных свойств, которые вообще могут быть назначены элементам справочника Номенклатура. Как мы выяснили, эту информацию мы можем получить из регистра сведений НазначениеСвойствОбъектов.
Для этого мы можем использовать следующий запрос, который помещает список уникальных свойств во временную таблицу:
ВЫБРАТЬ РАЗЛИЧНЫЕ
НазначениеСвойствОбъектов.Свойство КАК Свойство
ПОМЕСТИТЬ ВТ_ВсеСвойстваНоменклатуры
ИЗ
РегистрСведений.НазначениеСвойствОбъектов КАК НазначениеСвойствОбъектов
ГДЕ
НазначениеСвойствОбъектов.Объект = ТИП(Справочник.Номенклатура)
;
Здесь мы выбираем только те свойства, которые назначены типу объекта Справочник.Номенклатура. Результатом будет список ссылок на элементы ПВХ СвойстваОбъектов, которые представляют собой наши свойства (например, "Цвет", "Размер", "Вес").
Далее нам нужен список всех элементов номенклатуры, для которых мы хотим получить свойства. Вы можете выбрать всю номенклатуру или применить отбор, например, по определенной группе. Важно получить уникальный список ссылок на элементы справочника Номенклатура.
Предположим, что нам нужна номенклатура, которая не является группой. Отбор по группе мы можем добавить опционально.
Теперь, когда у нас есть список всех номенклатур и список всех возможных свойств для номенклатуры, нам нужно соединить их таким образом, чтобы получить каждую позицию номенклатуры со всеми потенциальными для нее свойствами. Это делается с помощью декартова произведения, или так называемого "перекрестного соединения". Результат этого шага также помещаем во временную таблицу.
Этот запрос мы можем объединить со вторым шагом:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.Наименование КАК НоменклатураНаименование,
ВТ_ВсеСвойстваНоменклатуры.Свойство КАК Свойство
ПОМЕСТИТЬ ВТ_ВсеКомбинации
ИЗ
Справочник.Номенклатура КАК Номенклатура,
ВТ_ВсеСвойстваНоменклатуры КАК ВТ_ВсеСвойстваНоменклатуры
ГДЕ
НЕ Номенклатура.ЭтоГруппа
{ГДЕ
Номенклатура.Ссылка.* КАК НоменклатураОтбор} // Возможность отбора по номенклатуре или группе
;
Обратите внимание на конструкцию {ГДЕ Номенклатура.Ссылка.* КАК НоменклатураОтбор}. Это синтаксис для использования динамического отбора в языке запросов 1С, например, при работе через систему компоновки данных (СКД) или при программной установке параметров отбора. Если вы не используете СКД, вы можете либо убрать эту строку, либо задать конкретный отбор по ссылке или группе номенклатуры (например, ГДЕ Номенклатура.Родитель В (&СписокГрупп)).
После выполнения этого запроса ВТ_ВсеКомбинации будет содержать, например: "Товар 1 - Цвет", "Товар 1 - Размер", "Товар 2 - Цвет", "Товар 2 - Размер" и так далее, для каждой комбинации номенклатуры и свойства.
Наконец, мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ нашей таблицы ВТ_ВсеКомбинации с регистром сведений ЗначенияСвойствОбъектов. ЛЕВОЕ СОЕДИНЕНИЕ гарантирует, что все записи из ВТ_ВсеКомбинации будут присутствовать в результате, а фактические значения свойств будут присоединены только там, где они существуют в регистре.
Условие соединения должно быть по Объекту (номенклатуре) и Свойству. Если для какой-то комбинации "Номенклатура - Свойство" записи в ЗначенияСвойствОбъектов нет, то поля из этого регистра будут иметь значение NULL.
Для того чтобы NULL значения отображались как пустые строки, нули или любое другое значение по умолчанию, мы используем функцию ЕСТЬNULL(). Эта функция позволяет указать, какое значение должно быть подставлено вместо NULL.
Объединяем шаги 4 и 5 в окончательном запросе:
ВЫБРАТЬ
ВТ_ВсеКомбинации.Номенклатура,
ВТ_ВсеКомбинации.НоменклатураНаименование,
ВТ_ВсеКомбинации.Свойство,
ВТ_ВсеКомбинации.Свойство.Наименование КАК НаименованиеСвойства, // Для вывода имени свойства
ЕСТЬNULL(ЗначенияСвойствОбъектов.Значение, NULL) КАК ЗначениеСвойства // Или '0', или ''
ИЗ
ВТ_ВсеКомбинации КАК ВТ_ВсеКомбинации
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ПО ВТ_ВсеКомбинации.Номенклатура = ЗначенияСвойствОбъектов.Объект
И ВТ_ВсеКомбинации.Свойство = ЗначенияСвойствОбъектов.Свойство
УПОРЯДОЧИТЬ ПО
ВТ_ВсеКомбинации.Номенклатура,
ВТ_ВсеКомбинации.Свойство
;
В этом запросе мы добавили ВТ_ВсеКомбинации.Свойство.Наименование КАК НаименованиеСвойства, чтобы в результате видеть не только ссылку на свойство, но и его читаемое наименование. Также в функции ЕСТЬNULL вместо NULL вы можете подставить 0 (если ожидаются числовые значения), пустую строку '' (если текстовые), или Неопределено в зависимости от типа свойства и ваших требований к отображению.
Давайте соберем все запросы в единый блок, который вы сможете использовать в своей обработке 1С. Этот код будет выполнять последовательно три запроса и выводить результат в табличную часть или на форму.
// Создаем объект Запрос
Запрос = Новый Запрос();
// === Шаг 1: Получаем все доступные свойства для объектов типа "Справочник.Номенклатура" ===
// Используем ПланВидовХарактеристик.СвойстваОбъектов и РегистрСведений.НазначениеСвойствОбъектов.
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| НазначениеСвойствОбъектов.Свойство КАК Свойство
|ПОМЕСТИТЬ ВТ_ВсеСвойстваНоменклатуры
|ИЗ
| РегистрСведений.НазначениеСвойствОбъектов КАК НазначениеСвойствОбъектов
|ГДЕ
| НазначениеСвойствОбъектов.Объект = ТИП(Справочник.Номенклатура)
|;
|
// === Шаг 2 и 3: Получаем все элементы номенклатуры (или выбранную группу)
// и соединяем их декартовым произведением со всеми возможными свойствами
// из ВТ_ВсеСвойстваНоменклатуры.
// Это создаст таблицу всех возможных комбинаций 'Номенклатура - Свойство'.
|ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.Наименование КАК НоменклатураНаименование,
| ВТ_ВсеСвойстваНоменклатуры.Свойство КАК Свойство
|ПОМЕСТИТЬ ВТ_ВсеКомбинации
|ИЗ
| Справочник.Номенклатура КАК Номенклатура,
| ВТ_ВсеСвойстваНоменклатуры КАК ВТ_ВсеСвойстваНоменклатуры
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
|{ГДЕ
| Номенклатура.Ссылка.* КАК НоменклатураОтбор} // Возможность отбора по номенклатуре или группе
|;
|
// === Шаг 4 и 5: Присоединяем фактические значения свойств
// и обрабатываем незаполненные значения с помощью ЕСТЬNULL.
|ВЫБРАТЬ
| ВТ_ВсеКомбинации.Номенклатура,
| ВТ_ВсеКомбинации.НоменклатураНаименование,
| ВТ_ВсеКомбинации.Свойство,
| ВТ_ВсеКомбинации.Свойство.Наименование КАК НаименованиеСвойства,
| ЕСТЬNULL(ЗначенияСвойствОбъектов.Значение, NULL) КАК ЗначениеСвойства
|ИЗ
| ВТ_ВсеКомбинации КАК ВТ_ВсеКомбинации
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
| ПО ВТ_ВсеКомбинации.Номенклатура = ЗначенияСвойствОбъектов.Объект
| И ВТ_ВсеКомбинации.Свойство = ЗначенияСвойствОбъектов.Свойство
|УПОРЯДОЧИТЬ ПО
| ВТ_ВсеКомбинации.Номенклатура,
| ВТ_ВсеКомбинации.Свойство";
// Пример установки параметра отбора, если вы используете {ГДЕ Номенклатура.Ссылка.*}
// Если у вас есть элемент справочника Номенклатура, по которому нужно отобрать:
// Запрос.УстановитьПараметр("НоменклатураОтбор", МояВыбраннаяНоменклатура);
// Если по группе:
// Запрос.УстановитьПараметр("НоменклатураОтбор", МояВыбраннаяГруппаНоменклатуры);
// Запрос.Отбор.НоменклатураОтбор.ВидСравнения = ВидСравнения.ВИерархии; // Для отбора по группе с иерархией
Попытка
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
// Теперь РезультатЗапроса содержит табличную часть с колонками:
// Номенклатура, НоменклатураНаименование, Свойство, НаименованиеСвойства, ЗначениеСвойства.
// Вы можете вывести эти данные, например, в табличную часть на форме обработки.
// Пример вывода в Сообщения
Для Каждого Строка ИЗ РезультатЗапроса Цикл
Сообщить("Номенклатура: " + Строка.НоменклатураНаименование +
", Свойство: " + Строка.НаименованиеСвойства +
", Значение: " + Строка.ЗначениеСвойства);
КонецЦикла;
Исключение
Сообщить("Ошибка при выполнении запроса: " + ОписаниеОшибки());
КонецПопытки;
В тексте темы также поднимался вопрос о выводе свойств в виде отдельных колонок в таблице на форме. Язык запросов 1С не поддерживает прямое "поворачивание" строк в колонки без явного перечисления этих колонок. Если вы хотите, чтобы каждое свойство отображалось как отдельная колонка, вам придется реализовать это программно:
Этот подход требует больше программного кода, но позволяет добиться желаемого представления данных. В более сложных случаях, когда требуется динамическое формирование отчетов с кросс-таблицами, можно использовать возможности Системы Компоновки Данных (СКД), которая предлагает более гибкие инструменты для построения таких отчетов.
Выгрузка всех свойств номенклатуры, включая незаполненные, в 1С Управление торговлей 10.3 — это задача, которая требует понимания внутренней структуры хранения данных о свойствах. Применив предложенный многошаговый подход с использованием РегистрСведений.НазначениеСвойствОбъектов для получения полного списка свойств, декартова произведения для создания всех комбинаций и ЛЕВОГО СОЕДИНЕНИЯ с РегистрСведений.ЗначенияСвойствОбъектов для присоединения фактических значений, мы можем успешно получить необходимую информацию. Использование функции ЕСТЬNULL() позволяет корректно отображать незаполненные значения, что делает отчет максимально информативным.