При разработке внешних печатных форм в среде 1С часто возникает задача вывода данных, которые не хранятся в основных реквизитах объектов. Типичный пример из торговой практики — вывод Ячейки хранения для товаров в печатной форме документа Заказ клиента. Поскольку в типовых конфигурациях на базе БСП (Библиотеки стандартных подсистем), таких как 1С:Управление торговлей 11.4, ячейка хранения часто создается как дополнительный реквизит номенклатуры, стандартное обращение через точку в запросе здесь не сработает.
В этой статье мы подробно разберем, как устроен механизм хранения дополнительных реквизитов, проанализируем исходный код печатной формы и реализуем два альтернативных и надежных способа вывода ячейки хранения в печатную форму: с помощью оптимизированного запроса и с помощью стандартных функций БСП.
Прежде чем переходить к написанию кода, выясним причину, почему нельзя просто написать ЗаказКлиентаТовары.Номенклатура.ЯчейкаХранения. В платформе 1С дополнительные реквизиты не создаются как физические колонки в таблице справочника БД. Они хранятся в специальной подчиненной табличной части объекта. Для справочника номенклатуры это табличная часть ДополнительныеРеквизиты (системное имя — Справочник.Номенклатура.ДополнительныеРеквизиты).
Данная табличная часть состоит из двух ключевых реквизитов:
ДополнительныеРеквизитыИСведения, определяющая, какой именно доп. реквизит записан в этой строке.Таким образом, чтобы получить значение нужного нам реквизита, нам необходимо найти строку в этой табличной части, где поле Свойство соответствует нашей «Ячейке хранения», и прочитать значение из поля Значение этой же строки.
Рассмотрим подробнее два способа решения этой задачи для нашей внешней печатной формы.
Этот способ является наиболее правильным с точки зрения производительности 1С, так как все данные выбираются за одно обращение к базе данных. Нам нужно модифицировать существующий в обработке запрос, связав таблицу товаров заказа с табличной частью дополнительных реквизитов номенклатуры с помощью левого соединения.
Разберем по шагам, как изменить код функции СформироватьПечатнуюФорму, представленной в исходном коде обработки.
Шаг 1. Модернизируем текст основного запроса. Мы добавим левое соединение с табличной частью Справочник.Номенклатура.ДополнительныеРеквизиты. Обратите внимание, что имя свойства в базе данных может содержать суффикс владельца, например, Ячейка хранения (Номенклатура). Чтобы избежать жесткой привязки к имени, используем оператор ПОДОБНО с маской поиска.
Посмотрим на пример измененного текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
| ЗаказКлиентаТовары.Номенклатура.Артикул КАК НоменклатураАртикул,
| ЗаказКлиентаТовары.Номенклатура.ЕдиницаИзмерения КАК НоменклатураЕдиницаИзмерения,
| ВЫБОР
| КОГДА ЗаказКлиентаТовары.Номенклатура.ВесЗнаменатель <> 0
| ТОГДА ЗаказКлиентаТовары.Номенклатура.ВесЧислитель / ЗаказКлиентаТовары.Номенклатура.ВесЗнаменатель * ЗаказКлиентаТовары.Количество
| ИНАЧЕ &Ничего
| КОНЕЦ КАК Вес,
| ВЫБОР
| КОГДА ЗаказКлиентаТовары.Номенклатура.ВесЗнаменатель <> 0
| ТОГДА ЗаказКлиентаТовары.Номенклатура.ВесЕдиницаИзмерения
| ИНАЧЕ &Ничего
| КОНЕЦ КАК ВесЕдИзм,
| ЗаказКлиентаТовары.Цена КАК Цена,
| ЗаказКлиентаТовары.Количество КАК Количество,
| ЗаказКлиентаТовары.Сумма КАК Сумма,
| ЗаказКлиентаТовары.НомерСтроки КАК НомерСтроки,
| ЕСТЬNULL(НоменклатураДопРеквизиты.Значение, """") КАК ЯчейкаХранения
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
|
| // Левое соединение для извлечения дополнительного реквизита
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДопРеквизиты
| ПО ЗаказКлиентаТовары.Номенклатура = НоменклатураДопРеквизиты.Ссылка
| И НоменклатураДопРеквизиты.Свойство.Наименование ПОДОБНО ""%Ячейка хранения%""
|ГДЕ
| ЗаказКлиентаТовары.Ссылка = &Ссылка";
В этом запросе функция ЕСТЬNULL(НоменклатураДопРеквизиты.Значение, "") проверяет, заполнена ли ячейка у номенклатуры. Если запись о дополнительном реквизите отсутствует, запрос вернет пустую строку вместо значения NULL, что предотвратит ошибки вывода.
Шаг 2. Заполняем параметр области макета в цикле обхода выборки. В исходном коде была пустая строка ОбластьСтрокаТаблицы.Параметры.ЯчейкаХранения = ;, которая вызывала синтаксическую ошибку при компиляции. Изменим ее на присвоение полученного из запроса значения:
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ОбластьСтрокаТаблицы.Параметры.НомерСтроки = ВыборкаДетальныеЗаписи.НомерСтроки;
ОбластьСтрокаТаблицы.Параметры.Товар = ВыборкаДетальныеЗаписи.Номенклатура;
ОбластьСтрокаТаблицы.Параметры.Количество = ВыборкаДетальныеЗаписи.Количество;
ОбластьСтрокаТаблицы.Параметры.Артикул = ВыборкаДетальныеЗаписи.НоменклатураАртикул;
ОбластьСтрокаТаблицы.Параметры.ЕдИзм = ВыборкаДетальныеЗаписи.НоменклатураЕдиницаИзмерения;
// Присваиваем значение параметра из нашей выборки запроса
ОбластьСтрокаТаблицы.Параметры.ЯчейкаХранения = ВыборкаДетальныеЗаписи.ЯчейкаХранения;
// ... дальнейшая обработка веса и вывод области ...
ТабличныйДокумент.Вывести(ОбластьСтрокаТаблицы);
КонецЦикла;
Данный подход максимально быстрый и рекомендуется для промышленного использования на реальных базах данных 1С с большим объемом документов.
Если вы только начинаете изучать программирование 1С и написание сложных SQL-запросов с соединениями вызывает у вас трудности, можно воспользоваться готовыми механизмами БСП. В конфигурации 1С:Управление торговлей интегрирована подсистема управления свойствами объектов, в составе которой есть общий модуль УправлениеСвойствами.
Проанализируем, как получить значение реквизита с помощью этого модуля.
Шаг 1. Перед началом формирования табличного документа (или непосредственно перед циклом вывода строк) найдем ссылку на сам дополнительный реквизит «Ячейка хранения» в плане видов характеристик:
СвойствоЯчейка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Ячейка хранения (Номенклатура)");
Если Не ЗначениеЗаполнено(СвойствоЯчейка) Тогда
// Если точное наименование с постфиксом не найдено, пробуем найти по краткому наименованию
СвойствоЯчейка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Ячейка хранения");
КонецЕсли;
Шаг 2. Внутри цикла обхода товаров Пока ВыборкаДетальныеЗаписи.Следующий() Цикл вызовем экспортную функцию общего модуля БСП для получения значения этого свойства у текущей номенклатуры:
// Внутри цикла обхода строк таблицы товаров
Если ЗначениеЗаполнено(СвойствоЯчейка) Тогда
ЗначениеРеквизита = УправлениеСвойствами.ЗначениеСвойства(ВыборкаДетальныеЗаписи.Номенклатура, СвойствоЯчейка);
ОбластьСтрокаТаблицы.Параметры.ЯчейкаХранения = ЗначениеРеквизита;
Иначе
ОбластьСтрокаТаблицы.Параметры.ЯчейкаХранения = "";
КонецЕсли;
Важное предупреждение: Использование метода УправлениеСвойствами.ЗначениеСвойства() внутри цикла приводит к тому, что на каждой итерации (для каждой строки товара в заказе) будет выполняться отдельный запрос к базе данных. Если в заказе содержится 100-200 строк, формирование печатной формы может заметно замедлиться. Используйте этот метод только для небольших документов или в учебных целях.
Каким бы способом мы ни получили данные в коде, платформа 1С не сможет вывести их на экран, если в макете печатной формы не настроена соответствующая ячейка.
Давайте выполним необходимые настройки в макете:
ПФ_MXL_ЗаказКлиента_Количественный).СтрокаТаблицы.ЯчейкаХранения. Имя должно полностью совпадать с именем свойства структуры параметров области, которое мы заполняем в коде: ОбластьСтрокаТаблицы.Параметры.ЯчейкаХранения.После этого сохраните макет и заново зарегистрируйте внешнюю печатную форму в справочнике Дополнительные отчеты и обработки вашей информационной базы 1С.
Для закрепления материала объединим все доработки в единый рабочий модуль. Ниже представлен готовый программный код функции СформироватьПечатнуюФорму с использованием оптимального первого способа (запрос с левым соединением):
Функция СформироватьПечатнуюФорму(СсылкаНаДокумент, ОбъектыПечати)
ТабличныйДокумент = Новый ТабличныйДокумент;
ТабличныйДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_ЗаказКлиента_ЗаказКлиентаКоличественный";
МакетОбработки = ПолучитьМакет("ПФ_MXL_ЗаказКлиента_Количественный");
// Вывод заголовка
ОбластьЗаголовок = МакетОбработки.ПолучитьОбласть("Заголовок");
ОбластьЗаголовок.Параметры.ТекстЗаголовка = "Заказ клиента № " + СсылкаНаДокумент.Номер + " от " + СсылкаНаДокумент.Дата;
ТабличныйДокумент.Вывести(ОбластьЗаголовок);
// Вывод информации о поставщике
ОбластьПоставщик = МакетОбработки.ПолучитьОбласть("Поставщик");
ОбластьПоставщик.Параметры.ПредставлениеОрганизации = СсылкаНаДокумент.Организация;
ТабличныйДокумент.Вывести(ОбластьПоставщик);
// Вывод информации о покупателе
ОбластьПокупатель = МакетОбработки.ПолучитьОбласть("Покупатель");
ОбластьПокупатель.Параметры.ПредставлениеПартнера = СсылкаНаДокумент.Партнер;
// Получение адреса партнера через запрос
ЗапросАдрес = Новый Запрос;
ЗапросАдрес.Текст =
"ВЫБРАТЬ
| ПартнерыКонтактнаяИнформация.Представление КАК Представление
|ИЗ
| Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
|ГДЕ
| ПартнерыКонтактнаяИнформация.Ссылка = &Ссылка";
ЗапросАдрес.УстановитьПараметр("Ссылка", СсылкаНаДокумент.Партнер);
РезультатАдрес = ЗапросАдрес.Выполнить();
ВыборкаАдрес = РезультатАдрес.Выбрать();
Если ВыборкаАдрес.Следующий() Тогда
ОбластьПокупатель.Параметры.Адрес = ВыборкаАдрес.Представление;
КонецЕсли;
ТабличныйДокумент.Вывести(ОбластьПокупатель);
// Вывод шапки таблицы товаров
ОбластьШапкаТаблицы = МакетОбработки.ПолучитьОбласть("ШапкаТаблицы");
ТабличныйДокумент.Вывести(ОбластьШапкаТаблицы);
ОбластьСтрокаТаблицы = МакетОбработки.ПолучитьОбласть("СтрокаТаблицы");
// Основной запрос для получения товаров и их дополнительных реквизитов (Ячейка хранения)
ЗапросТовары = Новый Запрос;
ЗапросТовары.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
| ЗаказКлиентаТовары.Номенклатура.Артикул КАК НоменклатураАртикул,
| ЗаказКлиентаТовары.Номенклатура.ЕдиницаИзмерения КАК НоменклатураЕдиницаИзмерения,
| ВЫБОР
| КОГДА ЗаказКлиентаТовары.Номенклатура.ВесЗнаменатель <> 0
| ТОГДА ЗаказКлиентаТовары.Номенклатура.ВесЧислитель / ЗаказКлиентаТовары.Номенклатура.ВесЗнаменатель * ЗаказКлиентаТовары.Количество
| ИНАЧЕ &Ничего
| КОНЕЦ КАК Вес,
| ВЫБОР
| КОГДА ЗаказКлиентаТовары.Номенклатура.ВесЗнаменатель <> 0
| ТОГДА ЗаказКлиентаТовары.Номенклатура.ВесЕдиницаИзмерения
| ИНАЧЕ &Ничего
| КОНЕЦ КАК ВесЕдИзм,
| ЗаказКлиентаТовары.Цена КАК Цена,
| ЗаказКлиентаТовары.Количество КАК Количество,
| ЗаказКлиентаТовары.Сумма КАК Сумма,
| ЗаказКлиентаТовары.НомерСтроки КАК НомерСтроки,
| ЕСТЬNULL(НоменклатураДопРеквизиты.Значение, """") КАК ЯчейкаХранения
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДопРеквизиты
| ПО ЗаказКлиентаТовары.Номенклатура = НоменклатураДопРеквизиты.Ссылка
| И НоменклатураДопРеквизиты.Свойство.Наименование ПОДОБНО ""%Ячейка хранения%""
|ГДЕ
| ЗаказКлиентаТовары.Ссылка = &Ссылка";
ЗапросТовары.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
ЗапросТовары.УстановитьПараметр("Ничего", "---");
РезультатТовары = ЗапросТовары.Выполнить();
ВыборкаТовары = РезультатТовары.Выбрать();
КоличествоСтрок = ВыборкаТовары.Количество();
СуммаКоличество = 0;
СуммаВес = 0;
Пока ВыборкаТовары.Следующий() Цикл
ОбластьСтрокаТаблицы.Параметры.НомерСтроки = ВыборкаТовары.НомерСтроки;
ОбластьСтрокаТаблицы.Параметры.Товар = ВыборкаТовары.Номенклатура;
ОбластьСтрокаТаблицы.Параметры.Количество = ВыборкаТовары.Количество;
ОбластьСтрокаТаблицы.Параметры.Артикул = ВыборкаТовары.НоменклатураАртикул;
ОбластьСтрокаТаблицы.Параметры.ЕдИзм = ВыборкаТовары.НоменклатураЕдиницаИзмерения;
// Заполняем наш новый параметр "ЯчейкаХранения"
ОбластьСтрокаТаблицы.Параметры.ЯчейкаХранения = ВыборкаТовары.ЯчейкаХранения;
// Форматирование вывода веса
Если ВыборкаТовары.Вес = "---" Тогда
ОбластьСтрокаТаблицы.Параметры.Вес = ВыборкаТовары.Вес;
Иначе
ОбластьСтрокаТаблицы.Параметры.Вес = Формат(Окр(ВыборкаТовары.Вес, 1), "ЧДЦ=1");
КонецЕсли;
ОбластьСтрокаТаблицы.Параметры.ВесЕдИзм = ВыборкаТовары.ВесЕдИзм;
ТабличныйДокумент.Вывести(ОбластьСтрокаТаблицы);
// Подсчет итогов по весу
ТекущВес = ВыборкаТовары.Вес;
Если ТекущВес = "---" Тогда
ТекущВес = 0;
КонецЕсли;
СуммаКоличество = СуммаКоличество + ВыборкаТовары.Количество;
СуммаВес = СуммаВес + ТекущВес;
КонецЦикла;
// Вывод подвала таблицы с итогами
ОбластьПодвалТаблицы = МакетОбработки.ПолучитьОбласть("ПодвалТаблицы");
ОбластьПодвалТаблицы.Параметры.ИтогКоличество = СуммаКоличество;
ОбластьПодвалТаблицы.Параметры.ИтогВес = Формат(Окр(СуммаВес, 1), "ЧДЦ=1");
ТабличныйДокумент.Вывести(ОбластьПодвалТаблицы);
// Вывод подписей и итоговой суммы прописью
ОбластьПодписи = МакетОбработки.ПолучитьОбласть("Подписи");
ТекстИтоговойСтроки = НСтр("ru = 'Всего наименований %ВсегоНаименований% на сумму %Сумма% руб.'");
ТекстИтоговойСтроки = СтрЗаменить(ТекстИтоговойСтроки, "%ВсегоНаименований%", КоличествоСтрок);
ТекстИтоговойСтроки = СтрЗаменить(ТекстИтоговойСтроки, "%Сумма%", СсылкаНаДокумент.СуммаДокумента);
ОбластьПодписи.Параметры.ИтоговаяСумма = ТекстИтоговойСтроки;
ОбластьПодписи.Параметры.СуммаПрописью = ЧислоПрописью(СсылкаНаДокумент.СуммаДокумента, "Л = ru_RU; ДП = Истина", "рубль, рубля, рублей, м, копейка, копейки, копеек, ж, 2");
ОбластьПодписи.Параметры.Менеджер = СсылкаНаДокумент.Менеджер;
ТабличныйДокумент.Вывести(ОбластьПодписи);
ТабличныйДокумент.АвтоМасштаб = Истина;
Возврат ТабличныйДокумент;
КонецФункции
Мы успешно модифицировали код внешней обработки и решили поставленную задачу. Описанные подходы подходят для извлечения любых дополнительных реквизитов как для справочников (Номенклатура, Контрагенты, договоры), так и для документов в конфигурациях 1С, построенных на БСП.