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