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