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