Как организовать ввод причины изменения документа и запись в регистр сведений при проведении

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

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

В этой статье мы подробно разберем, как правильно реализовать этот механизм, избегая блокировок базы данных и соблюдая стандарты разработки на платформе 1С:Предприятие 8.3.

Анализ проблемы: Транзакции и блокировки

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

Почему это плохо: пока транзакция открыта, таблицы базы данных заблокированы. Другие пользователи не смогут записывать документы этого типа или проводить по ним обороты. Если пользователь уйдет на обед с открытым окном «Введите причину», работа всей компании может быть парализована. Именно поэтому современные стандарты 1С требуют выносить все диалоги с пользователем до начала записи на сторону клиента.

Решение №1: Реализация в обычном приложении (толстый клиент)

Если ваша конфигурация использует обычные формы (как в примере автора темы), наиболее простым решением будет использование модального окна. Рассмотрим алгоритм действий:

  1. В модуле формы документа перехватываем событие ПередЗаписью.
  2. Проверяем, нужно ли запрашивать причину (например, если документ проводится).
  3. Открываем форму ввода причины модально.
  4. Если форма закрыта с подтверждением — передаем данные в документ и продолжаем запись.
  5. Если пользователь нажал «Отмена» — отменяем запись документа.

Рассмотрим пример кода в модуле формы документа:


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

Решение №2: Асинхронный подход в управляемом приложении (8.3)

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

  1. В обработчике ПередЗаписью на клиенте проверяем наличие причины в переменной модуля формы.
  2. Если переменной нет — ставим Отказ = Истина, запоминаем параметры записи и открываем форму ввода.
  3. После ввода причины вызываем запись документа повторно, уже с заполненным параметром.

Пример реализации в модуле управляемой формы:


&НаКлиенте
Переменная ПричинаЗаполнена; // Флаг для предотвращения зацикливания

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

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

Как правильно записывать данные в Регистр Сведений

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

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

Рассмотрим пример кода в модуле объекта:


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

Использование «Дополнительных свойств»

Обратите внимание на объект ДополнительныеСвойства. Это коллекция (структура), которая существует «внутри» объекта документа, пока он находится в памяти. Рассмотрим подробнее её преимущества:

Рекомендации по проектированию

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


Если Модифицированность() И НЕ ЗначениеЗаполнено(Объект.ПричинаИзменения) Тогда
    Сообщить("Заполните причину!");
    Отказ = Истина;
КонецЕсли;

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

Резюме

Мы рассмотрели несколько способов реализации контроля ввода причины. Выбор конкретного метода зависит от типа вашего приложения:

  1. Для обычного приложения допустимо использование ОткрытьМодально() в модуле формы.
  2. Для управляемого приложения следует использовать асинхронный вызов через ОписаниеОповещения.
  3. Саму запись в регистр всегда лучше выполнять в модуле объекта внутри транзакции через ДополнительныеСвойства, чтобы обеспечить целостность данных.

Помните, что архитектурная правильность кода в 1С напрямую влияет на производительность системы и отсутствие блокировок при многопользовательской работе.

← На главную