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