Уважаемые коллеги, хотелось бы донести до Вашего сведения небольшой опыт работы по заполнению дополнительных реквизитов Справочников и Документов. Уже много статей посвящено теме этих самых реквизитов, добавлю и свои пять копеек.
В первую очередь хотелось бы заметить, что в самой БСП уже есть ряд процедур и функций которые помогают успешно работать с заполнением дополнительных реквизитов. Главное правильно их использовать. Поэтому и начну я с самого простого варианта.
- Заполнение доп.ревизитов в модуле на сервере.
Предыстория: в одной организации потребовался перенос данных из одной базы в другую одного из справочников. Да вот беда в базе источнике у справочника было два нужных реквизита объекта, а в базе приемнике их не было. Дабы не ломать типовую конфигурацию было принято решение в новой базе создать такое же количество реквизитов, но в качестве дополнительных. Дело оставалось за малым – поместить в эти реквизиты необходимые данные. Благо коды элементов в базе источнике и базе приёмнике были синхронизированы. Самым простым показалось выгрузить связку из трёх реквизитов (Код – Реквизит1 – Реквизит2) в обычный файл формата Excel на стороне источника и загрузить этот файл на стороне приёмника. Опуская момент выгрузки и последующей загрузки файла сразу перейду к моменту, когда файл уже загружен в базе приёмнике в таблицу значений и предстоит все эти данные разложить по нужным элементам справочника, для которого уже заведены два доп.реквизита с именами (для разработчика) «Реквизит1» и «Реквизит2». В общем-то в данном случае всё довольно таки просто. Будем использовать стандартную процедуру БСП.
Для Каждого ТекСтрока Из ТаблицаРеквизитовИсточника Цикл
//Находим ССЫЛКУ на нужный нам элемент справочника
СсылкаНаЭлемент = Справочник.НужныйНамСправочник.НайтиПоКоду(ТекСтрока.Код);
//Находим те доп.реквизиты, которые требуются для заполнения
СвойствоРеквизит1 = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","Реквизит1");
СвойствоРеквизит2 = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","Реквизит2");
//создаём массив доп.реквизитов для помещения их в нужный элемент
МассивСтруктур = Новый Массив;
МассивСтруктур.Добавить(Новый Структура("Свойство, Значение",СвойствоРеквизит1,ТекСтрока.Реквизит1));
МассивСтруктур.Добавить(Новый Структура("Свойство, Значение",СвойствоРеквизит2,ТекСтрока.Реквизит2));
//Используя стандартную процедуру БСП присваиваем значения доп.реквизитов элементу справочника
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СсылкаНаЭлемент, МассивСтруктур);
КонецЦикла;
Обратите внимание, что никаких получений объекта элемента справочника делать не нужно в силу того, что процедура «ЗаписатьСвойстваУОбъекта» сама получает объект и записывает его на сервере. Это удобно, но это и подводный камень о котором будет упомянуто далее.
- Заполнение доп.реквизитов при переносе данных с помощью КД 2.0.
Предыстория: ситуация аналогичная предыдущей. Только на этот раз для переноса данных было решено использовать не файл Excel, а правила обмена, созданные в КД 2.0. Что для этого потребовалось: два реквизита, которые должны были быть перенесены из источника в приёмник в правилах прописывались как переменные. А уже на стороне приёмника в обработчике события «ПослеЗагрузки» из данных переменных заполнялись доп.реквизиты. И тут мы и натыкаемся на подводный камень процедуры БСП. Если использовать предыдущий алгоритм, то при загрузке данных система выдаст сообщение об ошибке «Ошибка при вызове метода контекста (Записать): Данные были изменены или удалены другим пользователем». Остаётся использовать другой вариант, либо делать разные ухищрения типа правил «повторная загрузка» и передавать данные в несколько этапов. Остановимся на первом варианте.
Пропишем в параметры правил конвертации свойств «Реквизит1» и «Ревизит2» нужные нам значения из источника.
А в обработчике «После загрузки» выполним следующий алгоритм
//запишем объект перед тем как устанавливать доп.реквизиты
Если НЕ ОбъектНайден Тогда
Объект.Записать(режимЗаписиДокумента.Запись);
ОбъектМодифицирован = ЛОжь;
КонецЕсли;
//Находим те доп.реквизиты, которые требуются для заполнения
СвойствоРеквизит1 = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","Реквизит1");
СвойствоРеквизит2 = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","Реквизит2");
//На всякий случай очистим табличную часть объекта – предполагается, что мы делаем первичную загрузку
Объект.ДополнительныеРеквизиты.Очистить();
//Добавим строки в табличную часть с заполнением необходимых доп.реквизитов и их значений
НовСтр = Объект.ДополнительныеРеквизиты.Добавить();
НовСтр.Свойство = СвойствоРеквизит1;
НовСтр.Значение = ПараметрыОбъекта["Реквизит1"];
НовСтр = Объект.ДополнительныеРеквизиты.Добавить();
НовСтр.Свойство = СвойствоРеквизит2;
НовСтр.Значение = ПараметрыОбъекта["Реквизит2"];
Далее при записи объекта будет записана и его табличная часть и при открытии элемента справочника в базе Приёмник доп.реквизиты будут на своих местах.
- Заполнение доп.реквизитов в модуле внешней обработки.
Переходим к ситуации, когда для заполнения дополнительных реквизитов будет использоваться внешняя вставляемая обработка с вариантом использования «Заполнения формы». В данном случае имеется форма документа, из которой и вызывает внешняя обработка заполнения. По своему алгоритму обработка должна получить значения доп.реквизитов Реквизит1 и Реквизит2 и поместить в табличную часть документа «Дополнительные реквизиты». В данном случае при использовании типовой процедуры «УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(…)» создаст те же сложности с различными версиями одного и того же объекта как реквизита формы и находящегося на сервере: после выполнения внешней обработки записать какие-либо ещё изменения реквизитов объекта на форме не представляется возможным. В этом случае потребуется не просто заполнить доп.реквизиты, но и поместить их в тот объект, что находится на сервере. Но это ещё не вся сложность. Дело в том, что дополнительные реквизиты хранятся в объекте как табличная часть, но форме они отображаются именно как реквизиты типа «Поле ввода». По сути дела разработчики заложили в БСП возможность программно создавать набор реквизитов формы по количеству строк заполненных в табличной части «Дополнительные реквизиты» объекта и отображать в них данные из табличной части (те кто занимался этим вопросом наверняка вспомнили реквизиты с длиннющими названиями). Можно конечно пытаться удалить все эти реквизиты и команды с ними связанные (что вполне реально ведь они созданы программно) и вызывать процедуру создания их из процедуры ПриСозданииНаСервере, но есть и более простой вариант. Можно воспользоваться типовой процедурой заполняющей значения реквизитов формы из табличной части объекта УправлениеСвойствами.ЗаполнитьДополнительныеРеквизитыВФорме(…) где в первом параметре передаётся сама форма, а во втором реквизит Объект этой самой формы. Важно отметить, что заполнение на форме этих реквизитов обязательно, так как если они останутся пустыми, что при закрытии формы с записью в доп.реквизиты запишутся эти самые пустые значения.
В процедуре внешней обработки заполнения требуется прописать следующий алгоритм:
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения, ПараметрыКоманды) Экспорт
//получим контекст формы для заполнения
КонтекстФормыВызова = ПараметрыКоманды.ЭтаФорма;
//для внесения изменения получим объект, которому принадлежит форма
ДанныеОбъекта = КонтекстФормыВызова.Объект;
//поместим в переменный значения, которые должны оказаться в доп.реквизитах
Реквизит1 = «А»;
Реквизит2 = «Б»;
//Находим те доп.реквизиты, которые требуются для заполнения
СвойствоРеквизит1 = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","Реквизит1");
СвойствоРеквизит2 = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","Реквизит2");
//далее перед заполнением нового значения доп.реквизита необходимо проверить, что его нет в табличной части
//а если он есть заполнить новым значением
СвойствоЕсть = Ложь;
Для Каждого ТекСрока Из ДанныеОбъекта.ДополнительныеРеквизиты Цикл
Если ТекСтрока.Свойство = СвойствоРеквизит1 Тогда
ТекСтрока.Значение = Реквизит1;
СвойствоЕсть = Истина;
КонецЕсли;
КонецЦикла;
Если Не СвойствоЕсть Тогда
НовСтр = Объект.ДополнительныеРеквизиты.Добавить();
НовСтр.Свойство = СвойствоРеквизит1;
НовСтр.Значение = Реквизит1;
КонецЕсли;
//те же действия повторит и для остальных доп.реквизитов (в данном случае для Реквизит2)
//вполне возможно при большем значении устанавливаемых значениях доп.реквизитов код оптимизировать – в данном случае такой задачи не стоит
//теперь необходимо поместить заполненную табличную часть в объект на сервере
//получим объект с сервера
ДокОбъектССервера = ДанныеОбъекта.Ссылка.ПолучитьОбъект();
//передадим в него все доп.реквизиты с объекта на форме
ТабДопРекв = ДанныеОбъекта.ДополнительныеРеквизиты.Выгрузить();
ДокОбъектССервера.ДополнительныеРеквизиты.Загрузить(ТабДопРекв);
//Запишем объект и поместим его на форму для того чтобы можно было вносить следующие изменения
ДокОбъектССервера.Записать();
КонтекстФормыВызова.ЗначениеВРеквизитФОрмы(ДокОбъектССервера,"Объект");
//Обновим отображение значений доп.реквизитов на форме
УправлениеСвойствами.ЗаполнитьДополнительныеРеквизитыВФорме(КонтекстФормыВызова, КонтекстФормыВызова.РеквизитФормыВЗначение("Объект"));
КонецПроцедуры
Примечательно, что процедура «ЗаполнитьДополнительныеРеквизитыВФорме(…)» можно так же использовать и в том случае если доп.реквизиты заполняются по кнопочке из формы.
На этом завершаю рассмотрение программного заполнения доп.реквизитов и надеюсь, это поможет кому-то ускорить процесс разработки.