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