Довольно часто при разработке внешних печатных форм (ВПФ) для конфигурации 1С:ERP возникает необходимость вывести дополнительную технологическую информацию. Разберем классическую ситуацию: нам нужно распечатать документ ПлановаяКалькуляция2_2 (например, технологическую карту приготовления блюда или сборки изделия), и в этой форме требуется отобразить текстовое описание процесса. Само описание хранится не в документе и не в спецификации, а в подчиненном справочнике ЭтапыПроизводства.
Давайте проанализируем архитектурную связь этих объектов. В 1С:ERP справочник этапов является подчиненным. Его реквизит Владелец ссылается на элемент справочника РесурсныеСпецификации. Сложность заключается в том, что в самом документе калькуляции табличная часть содержит реквизит Объект составного типа. Там может лежать как спецификация, так и номенклатура. Из-за этого мы не можем просто обратиться к данным через точку, и типовые запросы начинают выдавать ошибки.
Рассмотрим ошибки, которые чаще всего допускают разработчики при попытке получить эти данные напрямую.
1. Ошибка операторов сравнения при работе с массивами
Когда мы передаем в запрос коллекцию значений (например, массив ссылок на распечатываемые документы), мы не можем использовать стандартные математические операторы.
Если мы напишем код так:
И РесурсныеСпецификации.Ссылка >= &МассивОбъектов
// ИЛИ
И РесурсныеСпецификации.Ссылка = &МассивОбъектов
Платформа выдаст ошибку: Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов. Чтобы проверить вхождение ссылки в массив, всегда используем оператор В:
ГДЕ ДокументКалькуляции.Ссылка В (&МассивОбъектов)
2. Обращение через точку к составному типу (Неявные соединения)
Попытка написать путь вида ПлановаяКалькуляция.Объект.Этап.Описание приведет либо к синтаксической ошибке, либо к возврату значения NULL. Поскольку реквизит Объект имеет составной тип, платформа не знает наверняка, есть ли у этого объекта реквизит "Этап". Для решения этой проблемы разберем использование функции ВЫРАЗИТЬ.
Чтобы явно указать платформе, с каким типом мы работаем, применим конструкцию:
ВЫРАЗИТЬ(ПлановаяКалькуляция.Объект КАК Справочник.РесурсныеСпецификации)
Если в реквизите окажется другой тип данных (например, номенклатура), функция безопасно вернет NULL, и запрос не завершится с ошибкой.
Проанализируем физический смысл данных. Ресурсная спецификация (например, приготовление "Азу из говядины") редко состоит из одного шага. Обычно это многоэтапный производственный процесс. Если мы просто присоединим таблицу этапов к основному печатному запросу левым или внутренним соединением, строки калькуляции в результате запроса задвоятся или затроятся (по количеству этапов).
Нам необходимо собрать все описания этапов для конкретной спецификации и склеить их в один текст, либо выводить в макет отдельным списком.
Посмотрим на классический способ интеграции нашего запроса с помощью пакета запросов и временных таблиц. Этот подход хорош, если мы хотим сделать все вычисления на стороне СУБД.
Рассмотрим пример кода:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВЫРАЗИТЬ(ПлановаяКалькуляция.Объект КАК Справочник.РесурсныеСпецификации) КАК Спецификация
ПОМЕСТИТЬ ВТ_Спецификации
ИЗ
Документ.ПлановаяКалькуляция2_2.ОбъектыКалькуляции КАК ПлановаяКалькуляция
ГДЕ
ПлановаяКалькуляция.Ссылка В(&МассивОбъектов)
И ПлановаяКалькуляция.Объект ССЫЛКА Справочник.РесурсныеСпецификации
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ЭтапыПроизводства.Владелец КАК Спецификация,
ЭтапыПроизводства.Описание КАК ОписаниеЭтапа
ИЗ
Справочник.ЭтапыПроизводства КАК ЭтапыПроизводства
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Спецификации КАК ВТ_Спецификации
ПО ЭтапыПроизводства.Владелец = ВТ_Спецификации.Спецификация
ГДЕ
НЕ ЭтапыПроизводства.ПометкаУдаления
Этот запрос вернет нам плоскую таблицу, где каждой спецификации будут соответствовать строки с описаниями её этапов. Главный минус: если этапов много, нам всё равно придется обрабатывать их программно при выводе макета.
Типовой запрос для плановой калькуляции в 1С:ERP — это огромная структура (часто более 500 строк кода), которая собирает материалы, трудозатраты и доли стоимости. Пытаться внедрить туда еще и этапы производства через левые соединения — верный путь к существенному падению производительности и ошибкам при будущих обновлениях конфигурации.
Разберем архитектурно правильный (модульный) подход к разработке:
МассивСпецификаций.Справочник.ЭтапыПроизводства, передавая туда наш массив.Давайте посмотрим, как программно обработать склеивание нескольких этапов в один блок текста при выводе макета:
// Предположим, ВыборкаЭтапов - это результат нашего второго запроса,
// отсортированный по Владельцу (Спецификации) и номеру этапа.
ОписаниеОбщее = Новый Массив;
Пока ВыборкаЭтапов.Следующий() Цикл
// Собираем описания всех этапов для текущего блюда/изделия
Если ЗначениеЗаполнено(ВыборкаЭтапов.Описание) Тогда
ОписаниеОбщее.Добавить(ВыборкаЭтапов.Описание);
КонецЕсли;
КонецЦикла;
// Получаем область макета
ОбластьОписание = Макет.ПолучитьОбласть("ОписаниеТехнологии");
// Склеиваем массив в одну строку с переносом строк
Если ОписаниеОбщее.Количество() > 0 Тогда
ОбластьОписание.Параметры.Описание = СтрСоединить(ОписаниеОбщее, Символы.ПС);
Иначе
ОбластьОписание.Параметры.Описание = "Технологическое описание отсутствует.";
КонецЕсли;
ТабличныйДокумент.Вывести(ОбластьОписание);
Мы выяснили причину, по которой прямое обращение к описанию этапа из плановой калькуляции не работает. Реквизиты составного типа требуют особого обращения в запросах (использование проверки ССЫЛКА и приведения типов ВЫРАЗИТЬ).
Кроме того, мы рассмотрели, почему не стоит перегружать типовые монструозные запросы дополнительными соединениями. Разделение логики на получение основных данных документа и пост-получение дополнительных справочных данных (таких как этапы производства) делает код вашей внешней печатной формы читаемым, масштабируемым и устойчивым к обновлениям платформы и конфигурации 1С:ERP.