Как программно изменить время документа при записи и проведении без появления модифицированности формы

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

В практике разработки на платформе 1С:Предприятие 8 часто возникает задача жесткой фиксации времени создания или проведения документов. Например, требуется, чтобы все документы поступления товаров регистрировались строго на начало рабочего дня (скажем, на 10:00:00), чтобы последующие продажи, оформленные тем же днем, гарантированно не приводили к отрицательным остаткам на складах.

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

Почему появляется «звездочка» модифицированности после записи?

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Дата = НачалоДня(Дата) + 10*60*60; // Фиксируем время на 10:00:00
КонецПроцедуры

При таком подходе после нажатия кнопки «Записать» или «Провести» документ успешно сохраняется в базе данных с нужным временем. Но на экране пользователя в заголовке формы появляется знак *, сигнализирующий о том, что данные формы изменены и не сохранены. При попытке закрыть форму система выдает назойливый диалог: «Данные были изменены. Сохранить изменения?».

Выясним причину этого явления. Она кроется в рассинхронизации контекстов формы и объекта:

  1. Пользователь нажимает кнопку сохранения на форме. В этот момент данные из реквизитов формы передаются в объект базы данных.
  2. В модуле объекта срабатывает событие ПередЗаписью, где мы программно меняем реквизит Дата непосредственно у объекта.
  3. Объект записывается в базу данных с обновленным временем (10:00:00).
  4. Управление возвращается в форму. Но форма «не знает» о том, что объект внутри транзакции перезаписал свою дату! На клиенте в поле ввода по-прежнему отображается старое время.
  5. Платформа обнаруживает несоответствие данных в форме и в базе данных, из-за чего взводит флаг модифицированности формы (та самая «звездочка»).

Для решения этой проблемы запомним важное правило: любые интерактивные изменения реквизитов документа, которые должен увидеть пользователь, необходимо выполнять в контексте формы, а не в модуле объекта. Для глубокого понимания порядка вызова различных обработчиков полезно использовать инструмент для отслеживания последовательности событий платформы.

Решение проблемы модифицированности в обычных формах (на примере УТ 10.3)

Поскольку автор темы работает с конфигурацией «Управление торговлей 10.3», использующей обычные формы, нам нужно перенести код установки времени в модуль формы документа. Разберем этот шаг подробно. При переходе на новую версию программы для миграции всей накопленной истории используется перенос данных УТ 10.3 — УТ 11, позволяющий корректно перенести остатки, справочники и документы в новую базу.

Откроем форму документа и найдем предопределенный обработчик события ПередЗаписью. Напишем в нем следующий код:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверим, изменилась ли дата, чтобы лишний раз не модифицировать объект
    ТребуемаяДата = НачалоДня(Ссылка.Дата) + 10*60*60; // 10:00:00
    
    Если ЭтотОбъект.Дата <> ТребуемаяДата Тогда
        ЭтотОбъект.Дата = ТребуемаяДата;
    КонецЕсли;
    
КонецПроцедуры

Обратите внимание на использование свойства ЭтотОбъект внутри модуля обычной формы. Обращаясь к ЭтотОбъект.Дата, мы изменяем реквизит основного реквизита формы. Платформа фиксирует это изменение в контексте формы, корректно обновляет отображаемое время на экране и успешно сбрасывает флаг модифицированности после завершения записи.

Решение для управляемых форм (для расширения кругозора)

Если бы мы работали в современной конфигурации на управляемых формах (например, «Управление торговлей 11» или «ERP»), архитектура работы с данными была бы иной. В управляемом интерфейсе напрямую изменять данные объекта на клиенте нельзя. Для освоения тонкостей работы с клиент-серверными вызовами пригодится руководство по управлению контекстом выполнения кода в управляемых формах.

В этом случае нам помог бы обработчик события формы ПередЗаписьюНаСервере. Посмотрим на пример реализации:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Работаем напрямую с передаваемым объектом до его записи в БД
    НовоеВремя = НачалоДня(ТекущийОбъект.Дата) + 10*60*60;
    
    Если ТекущийОбъект.Дата <> НовоеВремя Тогда
        ТекущийОбъект.Дата = НовоеВремя;
    КонецЕсли;
    
КонецПроцедуры

При использовании ПередЗаписьюНаСервере платформа автоматически синхронизирует измененные реквизиты объекта ТекущийОбъект с реквизитом формы Объект и возвращает на клиент актуальные данные без вызова конфликта модифицированности.

Вторая проблема: Почему при оперативном проведении сбрасывается время?

Перейдем ко второй проблеме, описанной в сообщениях форума. Даже если мы правильно пропишем установку времени в событии формы, при проведении документа сегодняшним числом время все равно может сбрасываться на текущее системное время компьютера.

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

Соответственно, любые попытки программно присвоить документу фиксированное время (например, 10:00:00) при включенном оперативном проведении будут проигнорированы платформой во время фактической записи движений.

Как обойти ограничение оперативного проведения?

Рассмотрим два основных способа решения этой задачи.

Способ 1. Запрет оперативного проведения в конфигураторе

Если для данного типа документа (например, для «Поступления товаров и услуг») оперативное проведение не является критически важным, мы можем отключить его полностью. Для этого:

  1. Откроем конфигуратор и найдем нужный документ.
  2. Перейдем на вкладку «Движения».
  3. В свойстве «Оперативное проведение» установим значение «Запретить».

После этого документ всегда будет проводиться в режиме неоперативного проведения, и платформа не станет переопределять назначенное нами время 10:00:00.

Способ 2. Программное переключение режима проведения

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, что выполняется именно проведение документа
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        
        // Переводим проведение в неоперативный режим, чтобы зафиксировать время
        РежимПроведения = РежимПроведенияДокумента.Неоперативный;
        
        // Устанавливаем наше фиксированное время
        ЭтотОбъект.Дата = НачалоДня(ЭтотОбъект.Дата) + 10*60*60; 
        
    КонецЕсли;
    
КонецПроцедуры

При таком подходе платформа выполнит проведение документа неоперативно, что позволит полностью сохранить заданные нами 10 часов утра без автокорректировки времени сервером.

Важная деталь: свойство «Автовремя» документа

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

Если у вашего документа свойство «АвтоВремя» установлено, например, в значение «В конец дня», то при записи неоперативным путем платформа может проигнорировать ваши «10:00:00» и автоматически перенести документ на конец суток («23:59:59»). Чтобы гарантировать запись точного времени, в коде перед сохранением рекомендуется явно отключать механизм автовремени с помощью встроенного метода:


ЭтотОбъект.УстановитьВремя(РежимАвтоВремя.НеИспользовать);
ЭтотОбъект.Дата = НачалоДня(ЭтотОбъект.Дата) + 10*60*60;

Использование метода УстановитьВремя с параметром РежимАвтоВремя.НеИспользовать сообщает платформе, что автоматическое распределение времени применять не нужно, и документ запишется строго с тем временем, которое указано в реквизите Дата.

Подведем итоги и закрепим лучшие практики

Для успешного управления временем документов в системе 1С:Предприятие 8 всегда придерживайтесь следующих архитектурных правил:

  1. Никогда не меняйте дату и ключевые реквизиты шапки документа в процедуре ОбработкаПроведения модуля объекта. На этом этапе транзакция проведения уже запущена, и изменение реквизитов самого документа приведет к блокировкам и ошибкам синхронизации данных.
  2. Если документ изменяется интерактивно пользователем, управляйте датой и временем в модуле формы (в событиях ПередЗаписью для обычных форм или ПередЗаписьюНаСервере для управляемых форм). Это исключит появление символа модифицированности (звездочки) в заголовке формы.
  3. Помните, что оперативное проведение всегда заменяет установленное время текущим временем сервера. Чтобы зафиксировать время, переводите проведение в неоперативный режим (свойством метаданных или программно через параметр РежимПроведения).
  4. Принудительно отключайте правила автоматического времени с помощью метода УстановитьВремя(РежимАвтоВремя.НеИспользовать), чтобы настройки метаданных не конфликтовали с вашим программным кодом.
← На главную