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