В практике разработки на платформе 1С:Предприятие часто возникает задача: изменить логику формирования движений в зависимости от того, как изменились реквизиты документа. Типичный пример — перенос документа из закрытого периода в открытый. Программисту необходимо в модуле набора записей регистра (например, хозрасчетного) понять, какая дата была у документа до момента записи и проведения. Однако стандартное обращение к Ссылка.Дата внутри модуля набора записей возвращает уже новую дату. Разберем подробно, почему так происходит и как правильно решить эту задачу.
Проанализируем последовательность событий при проведении документа. Когда пользователь нажимает «Провести», платформа выполняет следующие действия:
ПередЗаписью объекта ДокументОбъект.ОбработкаПроведения.ОбработкаПроведения формируются наборы записей регистров.ПередЗаписью).Таким образом, когда мы оказываемся в модуле набора записей, в базе данных по ссылке регистратора уже лежит обновленная информация. Обращение к ЭтотОбъект.Отбор.Регистратор.Значение.Дата (или через ссылку) вернет дату, актуальную на текущий момент, а не ту, что была до начала записи.
Рассмотрим наиболее надежный и лаконичный способ — передачу значения через коллекцию ДополнительныеСвойства. Важно понимать, что у документа и у набора записей регистра — это разные коллекции. Нам нужно организовать «эстафету» передачи данных.
Шаг 1: Фиксация старой даты в документе
Для начала создадим подписку на событие ПередЗаписью для нужных типов документов. В обработчике проверим, является ли объект новым и изменилась ли дата.
Процедура ДокументПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Если документ новый, старой даты нет
Если Источник.ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// Получаем текущее значение даты из базы данных через ссылку
СтараяДата = Источник.Ссылка.Дата;
// Сохраняем старую дату в свойствах документа, чтобы она была доступна в ОбработкеПроведения
Источник.ДополнительныеСвойства.Вставить("СтараяДатаДокумента", СтараяДата);
КонецПроцедуры
Шаг 2: Передача даты из документа в набор записей
Теперь в процедуре ОбработкаПроведения (или в подписке на это событие) необходимо явно передать это свойство в набор записей регистра.
Процедура ОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт
// Проверяем, есть ли у нас сохраненная старая дата
СтараяДата = Неопределено;
Если Источник.ДополнительныеСвойства.Свойство("СтараяДатаДокумента", СтараяДата) Тогда
// Передаем это значение в ДополнительныеСвойства конкретного регистра
// Предположим, нас интересует Хозрасчетный регистр
ДвиженияХозрасчетный = Источник.Движения.Хозрасчетный;
ДвиженияХозрасчетный.ДополнительныеСвойства.Вставить("СтараяДатаДокумента", СтараяДата);
КонецЕсли;
КонецПроцедуры
Шаг 3: Получение данных в модуле регистра
Наконец, в модуле набора записей регистра Хозрасчетный мы можем прочитать это значение.
Процедура ПередЗаписью(Отказ, Замещение)
Если ДополнительныеСвойства.Свойство("СтараяДатаДокумента") Тогда
СтараяДата = ДополнительныеСвойства.СтараяДатаДокумента;
// Теперь мы можем использовать СтараяДата для реализации логики закрытого периода
// Например, сравнить ее с текущей датой документа
НоваяДата = ЭтотОбъект.Отбор.Регистратор.Значение.Дата;
Если СтараяДата < ДатаГраницыЗапрета И НоваяДата >= ДатаГраницыЗапрета Тогда
// Логика при переходе из закрытого периода в открытый
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если в вашей конфигурации используется Библиотека Стандартных Подсистем (БСП) и для документов включено версионирование, мы можем попытаться получить данные из регистра сведений ВерсииОбъектов. Разберем, как это работает.
Выясним причину использования этого метода: он позволяет не модифицировать модули документов и не создавать цепочку подписок. Проанализируем пример получения версии:
Функция ПолучитьСтаруюДатуИзВерсий(СсылкаНаДокумент)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ВерсииОбъектов.ОбъектВерсии КАК ДанныеВерсии
|ИЗ
| РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
|ГДЕ
| ВерсииОбъектов.Объект = &Ссылка
|УПОРЯДОЧИТЬ ПО
| ВерсииОбъектов.НомерВерсии УБЫВ_";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
// ... далее десериализация версии и получение реквизита "Дата"
КонецФункции
Важный нюанс: Этот метод довольно ресурсоемкий, так как требует обращения к таблице версий и десериализации объекта (обычно это XML или Binary Data). Использовать его внутри записи регистра стоит только в крайнем случае, если другие методы недоступны.
Если используется платформа 8.3.11 и выше, можно задействовать механизм «История данных». Он работает на уровне платформы и более эффективен, чем версионирование БСП. Рассмотрим алгоритм:
Дата.Это позволяет получить состояние объекта «на вчера» без необходимости пробрасывать параметры через свойства. Однако это требует включенной настройки истории данных для конкретных объектов метаданных.
Выясним, какой метод выбрать в зависимости от ситуации:
Рассмотрим подробнее ситуацию, когда документов очень много (ПТУ, РТУ, Акты и т.д.). В этом случае создание индивидуальных расширений для каждого документа — плохая практика. Рекомендуется использовать Подписки на события, где в качестве источника указан тип ДокументОбъект (или составной тип из всех нужных документов). Это позволит реализовать логику один раз в общем модуле, обеспечив единообразную работу всей системы.
Посмотрим на важный момент: При отмене проведения или простом сохранении документа (без проведения) движения регистра могут не записываться или очищаться. Обязательно проверяйте условие РежимЗаписи = РежимЗаписиДокумента.Проведение в ваших подписках, чтобы не выполнять лишних вычислений при обычном сохранении карточки документа.