При выгрузке данных из конфигураций на платформе 1С:Предприятие 8.3, использующих Библиотеку Стандартных Подсистем (БСП) — таких как 1С:Управление торговлей 11.3, 1С:ERP или 1С:Комплексная автоматизация — разработчики часто сталкиваются с трудностями при извлечении дополнительных реквизитов и сведений, а также специфических данных вроде серий, упаковок и актуальных цен. В этой статье мы подробно разберем, как устроен механизм дополнительных реквизитов изнутри, укажем на типичную ошибку соединения таблиц в запросах и покажем наиболее эффективные и быстродействующие способы выгрузки этих данных.
Проанализируем типичный запрос, который часто пытаются написать начинающие разработчики для получения значений дополнительных свойств:
ВЫБРАТЬ
Номенклатура.Наименование,
НоменклатураДополнительныеРеквизиты.Значение
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
ПО Номенклатура.Ссылка = НоменклатураДополнительныеРеквизиты.Свойство
Этот запрос не возвратит ожидаемого результата или вернет пустые значения. Выясним причину этой ошибки. В метаданных 1С табличная часть ДополнительныеРеквизиты подчинена справочнику Номенклатура. Любая табличная часть объекта в системе 1С обязательно содержит стандартный реквизит Ссылка, который указывает на сам элемент-владелец (в данном случае на конкретный товар).
Реквизит Свойство в этой табличной части имеет тип ссылки на План видов характеристик ДополнительныеРеквизитыИСведения. Он определяет, какое именно свойство записано в строке (например, «Страна происхождения», «Реестровая цена» или «Фасовка»).
Таким образом, пытаясь связать Номенклатура.Ссылка с НоменклатураДополнительныеРеквизиты.Свойство, мы совершаем логическую ошибку: пытаемся сравнить ссылку на элемент справочника Номенклатура с элементом плана видов характеристик. Связь никогда не установится.
Правильное условие связи между справочником и его табличной частью должно выглядеть следующим образом:
ПО Номенклатура.Ссылка = НоменклатураДополнительныеРеквизиты.Ссылка
Если мы просто исправим условие связи на правильное, то столкнемся со следующей проблемой: если у одного товара заполнено несколько дополнительных реквизитов (например, и «Страна», и «Фасовка»), то в результате выполнения запроса одна и та же номенклатурная позиция продублируется в результирующей таблице столько раз, сколько свойств у нее заполнено. Каждая строка будет содержать только одно из свойств.
Для выгрузки данных нам необходимо «развернуть» эти строки в отдельные именованные колонки (поля Country, Fas, Pr_ree и т.д.). Рассмотрим два эффективных метода решения этой задачи на уровне СУБД.
Этот способ интуитивно понятен. Мы присоединяем табличную часть дополнительных реквизитов столько раз, сколько свойств нам необходимо получить. Чтобы избежать дублирования строк, фильтр по конкретному свойству мы накладываем непосредственно в секции ПО (в условии связи таблиц), а не в секции ГДЕ.
Давайте посмотрим на практический пример такого запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.Наименование КАК Name,
Номенклатура.Код КАК Kod,
ЕСТЬNULL(ДопРеквизитСтрана.Значение, "Не указана") КАК Country,
ЕСТЬNULL(ДопРеквизитФас.Значение, 0) КАК Fas,
ЕСТЬNULL(ДопРеквизитРеестр.Значение, 0) КАК Pr_ree
ИЗ
Справочник.Номенклатура КАК Номенклатура
// Получаем значение свойства "Страна"
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизитСтрана
ПО Номенклатура.Ссылка = ДопРеквизитСтрана.Ссылка
И ДопРеквизитСтрана.Свойство = &СвойствоСтрана
// Получаем значение свойства "Количество в коробке (Фасовка)"
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизитФас
ПО Номенклатура.Ссылка = ДопРеквизитФас.Ссылка
И ДопРеквизитФас.Свойство = &СвойствоФас
// Получаем значение свойства "Реестровая цена"
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизитРеестр
ПО Номенклатура.Ссылка = ДопРеквизитРеестр.Ссылка
И ДопРеквизитРеестр.Свойство = &СвойствоРеестроваяЦена
ГДЕ
НЕ Номенклатура.ЭтоГруппа
Важное примечание: Если вы перенесете условие фильтрации свойств (например, ДопРеквизитСтрана.Свойство = &СвойствоСтрана) в общую секцию ГДЕ, левое соединение превратится во внутреннее. В результате из выгрузки полностью исчезнут те товары, у которых не заполнено хотя бы одно из этих дополнительных свойств.
Если свойств много (более 3-4), множественные левые соединения могут сильно снизить производительность СУБД, так как ей придется выполнять многократное сканирование одной и той же таблицы. В этом случае мы применим группировку с использованием конструкции ВЫБОР во временной таблице.
Проанализируем этот алгоритм:
ВЫБРАТЬ
ДопРеквизиты.Ссылка КАК Ссылка,
МАКСИМУМ(ВЫБОР КОГДА ДопРеквизиты.Свойство = &СвойствоСтрана ТОГДА ДопРеквизиты.Значение КОНЕЦ) КАК Country,
МАКСИМУМ(ВЫБОР КОГДА ДопРеквизиты.Свойство = &СвойствоФас ТОГДА ДопРеквизиты.Значение КОНЕЦ) КАК Fas,
МАКСИМУМ(ВЫБОР КОГДА ДопРеквизиты.Свойство = &СвойствоРеестр ТОГДА ДопРеквизиты.Значение КОНЕЦ) КАК Pr_ree
ПОМЕСТИТЬ ВТ_СвернутыеСвойства
ИЗ
Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизиты
ГДЕ
ДопРеквизиты.Свойство В (&СписокСвойств)
ГРУППИРОВАТЬ ПО
ДопРеквизиты.Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.Наименование КАК Name,
Номенклатура.Код КАК Kod,
ЕСТЬNULL(ВТ_СвернутыеСвойства.Country, "Не указана") КАК Country,
ЕСТЬNULL(ВТ_СвернутыеСвойства.Fas, 0) КАК Fas,
ЕСТЬNULL(ВТ_СвернутыеСвойства.Pr_ree, 0) КАК Pr_ree
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвернутыеСвойства КАК ВТ_СвернутыеСвойства
ПО Номенклатура.Ссылка = ВТ_СвернутыеСвойства.Ссылка
ГДЕ
НЕ Номенклатура.ЭтоГруппа
Этот запрос отрабатывает за один проход по табличной части дополнительных реквизитов. Функция МАКСИМУМ здесь используется как агрегатная функция свертки: поскольку для каждого конкретного свойства внутри одной группы по Ссылка будет существовать только одно значение (а для остальных условий ВЫБОР вернет значение Null), СУБД корректно схлопнет строки в одну запись.
Помимо дополнительных реквизитов, в исходной задаче фигурировали такие поля как минимальная партия заказа (Zakaz_min), кратность заказа (Zakaz_krat), серии (Seria) и сроки годности (Date_god). Давайте разберем особенности работы с ними в рамках архитектуры УТ 11.3.
1. Минимальная партия заказа и кратность. В старых версиях УТ 11 (включая ветку 11.3) отсутствуют типовые реквизиты номенклатуры для хранения параметров закупки у поставщиков в карточке товара. Данные механизмы появились только в поздних версиях (начиная с УТ 11.5). Для решения этой задачи у нас есть два пути:
УпаковкиНоменклатуры, где коэффициент упаковки (например, коробка из 12 штук) будет выступать физическим аналогом кратности заказа.2. Серии и сроки годности. Серия номенклатуры (справочник СерииНоменклатуры) является уникальной аналитикой. Срок годности товара (Date_god) физически хранится внутри реквизитов серии (поле ГоденДо), так как у разных партий одного и того же товара сроки годности различаются.
Если нам нужно выгрузить только те серии, которые реально находятся на остатках складов, запрос следует адресовать к регистру накопления ТоварыНаСкладах.Остатки. Рассмотрим, как это реализовать.
Проанализируем и построим комплексный запрос, который соберет воедино все необходимые требования: основные реквизиты, дополнительные реквизиты, актуальные цены, остатки на складах в разрезе серий, сроки годности и штрихкоды товаров.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.Наименование КАК Name,
Номенклатура.Код КАК Kod,
Номенклатура.Производитель КАК Maker,
Номенклатура.Файлы.Описание КАК Cnote, // Описание товара
// Получаем остатки и серии
ЕСТЬNULL(ТоварыНаСкладахОстатки.Серия, ЗНАЧЕНИЕ(Справочник.СерииНоменклатуры.ПустаяСсылка)) КАК Seria,
ЕСТЬNULL(ТоварыНаСкладахОстатки.Серия.ГоденДо, ДАТАВРЕМЯ(1, 1, 1)) КАК Date_god,
ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК Kol_vo,
// Получаем штрихкод (берём первый попавшийся для простоты структуры)
ЕСТЬNULL(Штрихкоды.Штрихкод, "") КАК Ean13,
// Получаем цены из регистра сведений
ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Price,
// Дополнительные свойства
ЕСТЬNULL(ВТ_ДопСвойства.Country, "Не указана") КАК Country,
ЕСТЬNULL(ВТ_ДопСвойства.Fas, 0) КАК Fas,
ЕСТЬNULL(ВТ_ДопСвойства.Pr_ree, 0) КАК Pr_ree
ИЗ
Справочник.Номенклатура КАК Номенклатура
// Подключаем остатки товаров на складах в разрезе серий
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыНаСкладахОстатки
ПО Номенклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
// Подключаем штрихкоды номенклатуры
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ШтрихкодыНоменклатуры КАК Штрихкоды
ПО Номенклатура.Ссылка = Штрихкоды.Номенклатура
// Получаем актуальные цены товаров
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ВидЦены = &ВидЦены) КАК ЦеныНоменклатурыСрезПоследних
ПО Номенклатура.Ссылка = ЦеныНоменклатурыСрезПоследних.Номенклатура
// Подключаем предварительно подготовленную таблицу со свойствами
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвернутыеСвойства КАК ВТ_ДопСвойства
ПО Номенклатура.Ссылка = ВТ_ДопСвойства.Ссылка
ГДЕ
НЕ Номенклатура.ЭтоГруппа
В процессе обсуждения на форуме упоминался общий модуль БСП УправлениеСвойствами и его экспортная функция ЗначенияСвойств(). Разберем детальнее этот инструмент.
Этот метод крайне полезен, когда вам нужно быстро получить значения свойств в программном коде без написания сложных запросов вручную (например, при формировании печатной формы для одного конкретного документа или при открытии карточки товара на форме). Программный вызов выглядит очень компактно:
// Пример программного получения свойств для массива объектов
МассивОбъектов = Новый Массив;
МассивОбъектов.Добавить(СсылкаНаТовар);
ТаблицаСвойств = УправлениеСвойствами.ЗначенияСвойств(МассивОбъектов, Истина, Ложь);
Однако помните о производительности! Использование подобных функций БСП внутри циклов при массовой выгрузке десятков тысяч позиций номенклатуры приведет к резкому замедлению процесса выгрузки. БСП внутри себя генерирует универсальные запросы, которые могут быть избыточными для узкоспециализированной задачи обмена данными. Для пакетной интеграции с интернет-магазинами или внешними WMS/BI-системами всегда отдавайте предпочтение прямым, оптимизированным запросам к базе данных.