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