Как сделать автоматическую запись в регистр сведений из справочника Контрагенты в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде 1С часто возникает задача: организовать хранение контактной информации (например, телефонов или адресов) в независимом регистре сведений, но редактировать эти данные непосредственно в форме элемента справочника (например, Контрагенты). При этом пользователь ожидает, что всё должно происходить в едином цикле работы — при нажатии кнопки «Записать и закрыть» в справочнике данные должны автоматически сохраняться и в регистре сведений.

На форумах часто возникают споры о том, как реализовать подобную логику. Кто-то предлагает делать это через документы, кто-то путает механизмы обычных и управляемых форм. Мы разберем эту задачу подробно, рассмотрим три наиболее эффективных и современных способа реализации и научимся писать правильный, отказоустойчивый код.

Вариант 1. Использование подсистемы «Контактная информация» БСП (Стандартный путь)

Если вы работаете в типовой конфигурации (например, 1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей или 1С:Бухгалтерия), либо в вашей самописной конфигурации внедрена Библиотека стандартных подсистем (БСП), изобретать велосипед не нужно. В БСП уже присутствует мощный, готовый механизм для хранения контактной информации.

Давайте проанализируем, как устроен этот механизм:

  1. Все виды контактной информации (мобильный телефон, рабочий, домашний, юридический адрес и т.д.) регистрируются в справочнике ВидыКонтактнойИнформации.
  2. Для хранения самих значений используется табличная часть КонтактнаяИнформация у объектов-владельцев (например, у справочника Контрагенты) или связанный регистр сведений.
  3. На управляемой форме размещается специальный программный интерфейс БСП, который автоматически выводит удобные поля для ввода номеров телефонов с масками, выбором стран и кодов городов.
  4. Вся логика записи, валидации номеров и сохранения изменений выполняется БСП автоматически при стандартной записи элемента справочника. Разработчику не приходится писать код вручную.

Если же перед вами стоит задача реализовать собственную структуру хранения данных в кастомном (самописном) регистре сведений, перейдем к разбору классических программных способов разработки.

Вариант 2. Использование Набора записей на управляемой форме (Табличный ввод)

Этот способ идеально подходит, если на форме контрагента нам нужно вывести полноценную таблицу со списком его телефонов. Для этого мы будем использовать тип данных управляемой формы РегистрСведенийНаборЗаписей.[ИмяРегистра].

Давайте разберем по шагам, как настроить форму и написать правильный код.

Шаг 1. Настройка реквизитов формы

Откроем редактор управляемой формы справочника Контрагенты и выполним следующие действия:

  1. На вкладке «Реквизиты» добавим новый реквизит формы и назовем его, например, ТелефоныКонтрагента.
  2. В поле «Тип» выберем значение РегистрСведенийНаборЗаписей.ТелефоныКонтрагентов (где ТелефоныКонтрагентов — ваш независимый регистр сведений).
  3. В свойствах созданного реквизита обязательно установим флажок «Сохраняемые данные». Это критически важно: благодаря этому свойству форма будет отслеживать изменения в таблице и при закрытии предупредит пользователя о необходимости сохранения данных (появится звездочка модифицированности в заголовке формы).
  4. Перетащим реквизит ТелефоныКонтрагента в левую часть редактора — в дерево элементов формы. Платформа автоматически создаст таблицу формы, связанную с этим набором записей.

Шаг 2. Чтение существующих данных при открытии

Поскольку наш регистр сведений является независимым, платформа автоматически не свяжет данные регистра с открываемым элементом справочника. Нам нужно прочитать существующие номера телефонов из базы данных. Сделаем это в обработчике события формы ПриЧтенииНаСервере:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    
    // Устанавливаем жесткий отбор набора записей по текущему контрагенту
    ТелефоныКонтрагента.Отбор.Контрагент.Установить(ТекущийОбъект.Ссылка);
    
    // Считываем записи из физической таблицы базы данных в реквизит формы
    ТелефоныКонтрагента.Прочитать();
    
КонецПроцедуры

Шаг 3. Сохранение данных при записи справочника

Теперь нам нужно организовать запись измененных данных. Писать данные в базу при каждом изменении строки таблицы пользователем — плохая практика, нарушающая транзакционность. Пользователь должен иметь возможность нажать кнопку «Отмена», и тогда изменения не должны попасть в базу. Поэтому запись набора мы будем производить строго в момент записи самого элемента справочника.

Здесь есть один важный нюанс: если мы создаем абсолютно нового контрагента, в момент открытия формы его ссылка еще не существует в базе (она пустая). Ссылка сформируется только при непосредственной записи объекта. Поэтому перед записью набора нам нужно обойти все строки таблицы и присвоить им созданную ссылку контрагента. Реализуем это в обработчиках событий формы:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Заполняем измерение связи для каждой строки набора записей.
    // Если это новый контрагент, то ТекущийОбъект.Ссылка уже сгенерирована платформой
    Для Каждого СтрокаЗаписи Из ТелефоныКонтрагента Цикл
        СтрокаЗаписи.Контрагент = ТекущийОбъект.Ссылка;
    КонецЦикла;
    
КонецПроцедуры

&НаСервере
Процедура ПриЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    
    // Записываем набор записей регистра сведений.
    // Запись происходит в единой транзакции с записью самого контрагента.
    // В случае ошибки в регистре, транзакция записи контрагента также откатится.
    Попытка
        ТелефоныКонтрагента.Записать();
    Исключение
        ВызватьИсключение "Не удалось сохранить телефоны контрагента: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
    
КонецПроцедуры

Вариант 3. Использование простых полей ввода на форме (Без таблиц)

Рассмотрим другую ситуацию: вам не нужна таблица на форме. Вместо этого вы хотите вывести три отдельных красивых поля ввода: «Телефон рабочий», «Телефон мобильный» и «Телефон домашний». При этом данные по-прежнему должны сохраняться в независимом регистре сведений с соответствующим измерением (или ресурсом) ВидТелефона.

Разберем по шагам, как реализовать такую схему.

Шаг 1. Создание реквизитов формы

Создадим на управляемой форме три реквизита с типом Строка:

Выведем их на форму как обычные поля ввода текста.

Шаг 2. Заполнение полей при открытии формы

В обработчике события ПриЧтенииНаСервере мы напишем запрос, который выберет все сохраненные телефоны данного контрагента из регистра сведений и распределит их по нашим реквизитам формы:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    
    // Очищаем поля на случай, если открывается новый объект
    ПолеТелефонРабочий = "";
    ПолеТелефонМобильный = "";
    ПолеТелефонДомашний = "";
    
    Если Не ТекущийОбъект.Ссылка.Пустая() Тогда
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   Телефоны.ВидТелефона КАК ВидТелефона,
            |   Телефоны.НомерТелефона КАК НомерТелефона
            |ИЗ
            |   РегистрСведений.ТелефоныКонтрагентов КАК Телефоны
            |ГДЕ
            |   Телефоны.Контрагент = &Контрагент";
        
        Запрос.УстановитьПараметр("Контрагент", ТекущийОбъект.Ссылка);
        
        РезультатЗапроса = Запрос.Выполнить();
        ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
        
        Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
            
            Если ВыборкаДетальныеЗаписи.ВидТелефона = ПредопределенноеЗначение("Перечисление.ВидыТелефонов.Рабочий") Тогда
                ПолеТелефонРабочий = ВыборкаДетальныеЗаписи.НомерТелефона;
            ИначеЕсли ВыборкаДетальныеЗаписи.ВидТелефона = ПредопределенноеЗначение("Перечисление.ВидыТелефонов.Мобильный") Тогда
                ПолеТелефонМобильный = ВыборкаДетальныеЗаписи.НомерТелефона;
            ИначеЕсли ВыборкаДетальныеЗаписи.ВидТелефона = ПредопределенноеЗначение("Перечисление.ВидыТелефонов.Домашний") Тогда
                ПолеТелефонДомашний = ВыборкаДетальныеЗаписи.НомерТелефона;
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 3. Запись данных при сохранении справочника

Для записи данных по отдельным полям удобнее всего использовать объект МенеджерЗаписи нашего регистра сведений. Мы вызовем процедуру записи для каждого поля отдельно в обработчике ПриЗаписиНаСервере:


&НаСервере
Процедура ПриЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    
    // Записываем каждое поле в регистр сведений
    ЗаписатьТелефонВРегистр(ТекущийОбъект.Ссылка, ПредопределенноеЗначение("Перечисление.ВидыТелефонов.Рабочий"), ПолеТелефонРабочий);
    ЗаписатьТелефонВРегистр(ТекущийОбъект.Ссылка, ПредопределенноеЗначение("Перечисление.ВидыТелефонов.Мобильный"), ПолеТелефонМобильный);
    ЗаписатьТелефонВРегистр(ТекущийОбъект.Ссылка, ПредопределенноеЗначение("Перечисление.ВидыТелефонов.Домашний"), ПолеТелефонДомашний);
    
КонецПроцедуры

&НаСервере
Процедура ЗаписатьТелефонВРегистр(СсылкаКонтрагента, ВидТелефона, ЗначениеНомера)
    
    // Создаем менеджер записи для точечной работы с одной строкой регистра
    МенеджерЗаписи = РегистрыСведений.ТелефоныКонтрагентов.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Контрагент = СсылкаКонтрагента;
    МенеджерЗаписи.ВидТелефона = ВидТелефона;
    
    // Пытаемся прочитать существующую запись в базе данных
    МенеджерЗаписи.Прочитать();
    
    Если ПустаяСтрока(ЗначениеНомера) Тогда
        // Если пользователь стер номер телефона на форме, мы должны удалить запись из регистра
        Если МенеджерЗаписи.Выбран() Тогда
            МенеджерЗаписи.Удалить();
        КонецЕсли;
    Иначе
        // Если номер заполнен, присваиваем ресурс и записываем (создаем новую или обновляем старую)
        МенеджерЗаписи.НомерТелефона = ЗначениеНомера;
        МенеджерЗаписи.Записать();
    КонецЕсли;
    
КонецПроцедуры

Важные нюансы разработки, о которых нельзя забывать

При реализации автоматической записи сведений из справочников разработчики часто совершают типичные ошибки. Проанализируем ключевые правила, которые помогут избежать сбоев в работе системы:

Мы подробно разобрали, как связать независимый регистр сведений с формой элемента справочника в среде 1С. Выберите оптимальный для вашей задачи вариант — будь то использование типовой подсистемы БСП, вывод полноценной таблицы через РегистрСведенийНаборЗаписей или разработка точечных полей с помощью МенеджерЗаписи. Это позволит сделать интерфейс вашей программы удобным для пользователей, а код — чистым и надежным.

← На главную