Почему программно созданный документ не сохраняется в базе при создании из модуля объекта?

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация: программист пишет код для автоматического создания связанного документа (например, «Установка цен номенклатуры» при записи «Поступления ТМЦ»), отладчик показывает, что документ успешно создан, записан и даже проведен, но после завершения работы алгоритма новый объект просто исчезает из базы данных. Рассмотрим подробно, почему это происходит, разберем механизмы транзакций и выясним, как правильно реализовать подобную задачу.

Анализируем причину: ловушка неявной транзакции

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

Проанализируем ситуацию автора темы по шагам:

  1. Пользователь нажимает кнопку «Провести и закрыть» в документе «Поступление ТМЦ».
  2. Система открывает транзакцию.
  3. Выполняется процедура ПередЗаписью модуля объекта «Поступление ТМЦ».
  4. Внутри этой процедуры программно создается и записывается документ «Установка цен номенклатуры». В этот момент 1С видит, что транзакция уже открыта, и «присоединяет» запись нового документа к ней.
  5. Документ «Поступление ТМЦ» пытается провестись. Но для проведения ему нужны актуальные розничные цены.
  6. Так как транзакция еще не завершена (не зафиксирована командой COMMIT), изменения в регистре цен, внесенные «Установкой цен», физически еще не видны для других процессов и проверок самого проведения «Поступления».
  7. Проведение «Поступления» завершается с ошибкой «Не указана розничная цена».
  8. Система понимает, что основное действие не удалось, и выполняет откат транзакции (ROLLBACK).
  9. Все действия, выполненные внутри этой транзакции, аннулируются. Включая запись документа «Установка цен».

Именно поэтому в отладчике мы видим успешную запись, но в списке документов объект не появляется — он был стерт СУБД в момент отмены общей транзакции.

Разберем проблему с номерами документов

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

Решение №1: Перенос логики в модуль формы

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

Разберем пример кода для формы документа:


&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    
    // Проверяем, нужно ли создавать установку цен
    // Например, если документ только что был проведен
    Если ТекущийОбъект.Проведен Тогда
        СоздатьУстановкуЦенПрограммно(ТекущийОбъект.Ссылка);
    КонецЕсли;

КонецПроцедуры

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

Решение №2: Использование ДополнительныеСвойства

Иногда логику нужно оставить именно в модуле объекта (например, если документ может записываться программно из разных мест, а не только из формы). Чтобы избежать конфликтов транзакций, мы можем использовать коллекцию ДополнительныеСвойства. Это «почтовый ящик» внутри объекта, который живет только в памяти во время его работы.

Проанализируем алгоритм:

  1. В модуле объекта в ПередЗаписью мы только помечаем, что нужно создать цены.
  2. В процедуре ПриЗаписи (когда объект уже имеет ссылку) мы проверяем этот флаг.
  3. Однако помним: ПриЗаписи — все еще внутри транзакции. Чтобы гарантированно создать объект отдельно, в типовых конфигурациях (как БП 3.0) часто используют механизм отложенного проведения или фоновые задания.

Пример передачи флага:


// В модуле объекта
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если НЕ ЭтотОбъект.ОбменДанными.Загрузка Тогда
        ЭтотОбъект.ДополнительныеСвойства.Вставить("НужноСоздатьЦены", Истина);
    КонецЕсли;
КонецПроцедуры

Решение №3: Разрыв транзакции через фоновое задание

Если специфика бизнес-логики требует, чтобы цены были созданы именно в момент записи «Поступления», но транзакция «Поступления» мешает, мы можем запустить фоновое задание. Фоновое задание всегда выполняется в отдельном сеансе и в своей транзакции.

Рассмотрим схему реализации:

  1. В модуле объекта в процедуре ПриЗаписи вызываем процедуру, которая запускает фоновое задание.
  2. В фоновое задание передаем ссылку на наше «Поступление».
  3. Фоновое задание дожидается завершения транзакции в основном сеансе (или делает несколько попыток чтения) и создает документ «Установка цен».

Пример кода запуска:


Процедура ПриЗаписи(Отказ)
    Если ДополнительныеСвойства.Свойство("НужноСоздатьЦены") Тогда
        НаименованиеЗадания = "Создание цен для " + Строка(ЭтотОбъект.Ссылка);
        ПараметрыЗадания = Новый Массив;
        ПараметрыЗадания.Добавить(ЭтотОбъект.Ссылка);
        
        ФоновыеЗадания.Выполнить("МойОбщийМодуль.СоздатьЦеныФоновымЗаданием", ПараметрыЗадания, , НаименованиеЗадания);
    КонецЕсли;
КонецПроцедуры

Важные рекомендации для разработчиков БП 3.0

При работе в конфигурации «Бухгалтерия предприятия 3.0» важно учитывать следующее:

Подведем итог: если ваш документ «исчезает», значит, транзакция, в которой он создавался, была откачена. Решением будет либо обеспечение безошибочного завершения основной транзакции, либо вынос создания зависимого объекта за пределы этой транзакции (в форму или фоновое задание).

← На главную