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