Как правильно реализовать изменение данных сотрудника при проведении документа в 1С

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

В процессе разработки часто возникает задача: при проведении документа (например, приказа о присвоении звания) необходимо обновить данные в карточке сотрудника. На первый взгляд кажется логичным просто перезаписать реквизит в справочнике Сотрудники. Однако, проанализируем ситуацию глубже: такой подход ломает историю изменений и создает технические проблемы. Разберем по шагам, почему прямая запись в справочник — это ошибка, и как реализовать задачу правильно с точки зрения архитектуры 1С.

Проблема прямой записи в справочник

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


// ПРИМЕР НЕПРАВИЛЬНОГО КОДА
Для Каждого Строка Из Объект.ТабличнаяЧасть1 Цикл
    СотрудникОбъект = Строка.Сотрудник.ПолучитьОбъект();
    СотрудникОбъект.Звание = Строка.НовоеЗвание;
    СотрудникОбъект.Записать();
КонецЦикла;

Выясним причины, по которым этот код считается «плохим тоном» и опасен для системы:

  1. Потеря истории: Если вы запишете новое звание поверх старого, вы никогда не узнаете, какое звание было у сотрудника месяц назад. Это сделает невозможным построение отчетов «на дату».
  2. Проблема отмены проведения: Если пользователь решит отменить документ, звание в справочнике останется новым. Придется писать сложную логику «возврата» старого значения, что крайне ненадежно.
  3. Блокировки данных: Метод ПолучитьОбъект().Записать() внутри транзакции проведения документа блокирует запись справочника. В многопользовательской среде это приведет к тому, что другие пользователи не смогут работать с этим сотрудником, пока документ не проведется.
  4. Транзакционные ошибки: Если после записи справочника сам документ не сможет записаться (например, из-за нехватки прав или ошибки в другом модуле), данные в справочнике уже будут изменены, а документ останется непроведенным. Произойдет рассинхронизация.

Правильное решение: Использование периодического регистра сведений

Проанализируем стандартные механизмы 1С. Для хранения данных, которые меняются во времени (звание, оклад, должность), идеально подходит Регистр сведений. Рассмотрим алгоритм реализации:

Шаг 1. Создание регистра

Создадим новый регистр сведений, назовем его, например, ИсторияЗванийСотрудников. Настроим его свойства:

Шаг 2. Настройка движений документа

В документе, который присваивает звания, перейдем на вкладку «Движения» и отметим наш новый регистр. В модуле проведения (ОбработкаПроведения) напишем код формирования записей:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // Очищаем старые движения
    Движения.ИсторияЗванийСотрудников.Записывать = Истина;
    Движения.ИсторияЗванийСотрудников.Очистить();

    Для Каждого ТекСтрока Из ТабличнаяЧасть1 Цикл
        Движение = Движения.ИсторияЗванийСотрудников.Добавить();
        Движение.Период = Дата; // Дата приказа
        Движение.Регистратор = Ссылка;
        Движение.Сотрудник = ТекСтрока.Сотрудник;
        Движение.Звание = ТекСтрока.НовоеЗвание;
    КонецЦикла;
КонецПроцедуры

Как отображать актуальное звание в справочнике

Теперь, когда данные хранятся в регистре, нам не нужно хранить реквизит Звание в самом справочнике Сотрудники. Но как сделать так, чтобы пользователь видел его в списке или в форме? Разберем два способа:

Способ 1. Виртуальная таблица «СрезПоследних»

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

Способ 2. Динамическое чтение в форме справочника

В форме элемента справочника Сотрудники мы можем создать реквизит формы (не путать с реквизитом объекта!) и заполнять его при открытии или программно:


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

Преимущества подхода с регистром

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

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

← На главную