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