Как избежать ошибки «Попытка использования закрытого менеджера временных таблиц» при отмене проведения документа в 1С?

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

При разработке или доработке функционала в 1С, особенно при работе с проведением и отменой проведения документов, мы можем столкнуться с различными сложностями. Одна из таких проблем – ошибка «Попытка использования закрытого менеджера временных таблиц», которая возникает в процедуре обработки удаления проведения документа. Рассмотрим подробнее, почему возникает эта ошибка и как ее эффективно исправить.

Автор темы столкнулся с этой проблемой при отмене проведения документа, несмотря на то, что порядок действий, по его словам, был реализован аналогично типовым решениям 1С:Управление Торговлей (УТ). Суть ошибки заключается в том, что в какой-то момент система пытается обратиться к менеджеру временных таблиц, который уже был закрыт или недоступен.

Что такое Менеджер временных таблиц и его жизненный цикл?

Прежде чем углубляться в решение проблемы, давайте выясним, что представляет собой МенеджерВременныхТаблиц в 1С и каков его жизненный цикл.

Менеджер временных таблиц (МВТ) – это специальный объект платформы 1С, который позволяет создавать и управлять временными таблицами в контексте выполнения запросов. Временные таблицы используются для хранения промежуточных результатов запросов, что значительно повышает производительность и стабильность сложных запросов, особенно при работе с большими объемами данных.

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

Причины возникновения ошибки "Попытка использования закрытого менеджера временных таблиц"

Ошибка "Попытка использования закрытого менеджера временных таблиц" возникает, когда наш код пытается выполнить операцию с временными таблицами, но МВТ, к которому идет обращение, уже закрыт или не инициализирован в текущем контексте. Разберем основные причины:

  1. Преждевременное закрытие: Наиболее частая причина – это преждевременное закрытие МВТ. В процессе отмены проведения документа, если вызов записи документа с режимом ОтменаПроведения происходит вне контекста активного менеджера временных таблиц (например, в обработчике ПередЗаписью или в других местах, не предназначенных для таких операций в рамках процедуры проведения), то возникает данная ошибка.
  2. Конфликт с автоматическим удалением движений: Ошибка часто связана с тем, что в настройках документа установлено "удалять движ автоматически". В этом случае платформа берет на себя управление удалением движений. Если же при этом в нашем коде мы пытаемся выполнять операции с временными таблицами в процедурах, которые вызываются платформой (например, ОбработкаУдаленияПроведения), а МВТ уже был неявно закрыт платформой, возникает конфликт. Платформа могла уже закрыть свой МВТ после выполнения своих операций, а наш код все еще ожидает его наличия.
  3. Рекурсивные вызовы: Рекурсивные вызовы записи документа в обработчике ПередЗаписью или других обработчиках могут также привести к тому, что менеджер временных таблиц будет создан и закрыт несколько раз, что нарушит его жизненный цикл.
  4. Сложные взаимодействия с регистрами: В некоторых случаях, например, если в настройках склада-отправителя установлена галочка "Контролировать оперативные остатки", это может указывать на более сложное взаимодействие с регистрами, которое может включать в себя преждевременное закрытие МВТ.

Анализ исходного кода и временное решение

Давайте рассмотрим код, представленный автором темы:


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

В этом коде мы видим, что отмена проведения осуществляется через серверные процедуры, которые, по всей видимости, используют временные таблицы для формирования движений. Ошибка возникает в момент, когда одна из этих процедур (вероятно, внутри нПроведениеСерверУТ.ЗаписатьНаборыЗаписей) пытается обратиться к менеджеру временных таблиц.

В качестве быстрого, но не всегда корректного решения, на форуме было предложено пересоздавать менеджер временных таблиц перед использованием:


ДополнительныеСвойства.ДляПроведения.СтруктураВременныеТаблицы.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц

Мы помещаем эту строку кода перед вызовом процедуры, которая вызывает ошибку. Например, это может быть перед нПроведениеСерверУТ.ЗаписатьНаборыЗаписей(ЭтотОбъект);

Почему это "костыль"? Мы должны понимать, что это решение является "костылем", потому что оно не устраняет первопричину проблемы. Если менеджер временных таблиц был закрыт, это означает, что система уже завершила свою работу с ним или ожидает другого менеджера. Принудительное создание нового менеджера может привести к нарушению логики работы, некорректному поведению в других частях системы или утечкам памяти, если старый менеджер не был корректно очищен. Оно "затыкает" проблему в одном месте, но не решает системный конфликт.

Комплексное решение: Правильное управление отменой проведения и менеджером временных таблиц

Ключевой момент в решении этой проблемы – это настройка документа "удалять движ автоматически". Автор темы сам пришел к этому выводу в конце обсуждения. Давайте разберем этот аспект подробнее.

  1. Если настройка "удалять движ автоматически" включена:

    Если для нашего документа установлена настройка "удалять движ автоматически", это означает, что платформа 1С самостоятельно управляет удалением движений регистров при отмене проведения. В этом случае, при вызове метода ОтменитьПроведение(), платформа сама создает и управляет своим менеджером временных таблиц, выполняет все необходимые операции по удалению движений и затем закрывает МВТ. Наш код в ОбработкаУдаленияПроведения в этом сценарии не должен пытаться выполнять операции, которые требуют активного МВТ, так как он уже мог быть закрыт платформой. Если нам необходимо выполнить какую-то логику, связанную с временными таблицами, то она должна быть в контексте, где МВТ гарантированно активен, или же мы должны использовать свой собственный МВТ, явно передавая его. Однако, при автоматическом удалении движений, ОбработкаУдаленияПроведения часто не должна содержать сложную логику, связанную с записями регистров.

  2. Если настройка "удалять движ автоматически" отключена:

    Это наиболее вероятный сценарий для возникновения описываемой проблемы и путь к ее корректному решению. Если эта настройка отключена, мы берем на себя ответственность за удаление движений документа в процедуре ОбработкаУдаленияПроведения. Именно так поступают в типовых конфигурациях 1С для сложных документов. В этом случае мы должны явно управлять менеджером временных таблиц.

    Примерный подход для ручного управления удалением движений:

    1. Создание менеджера временных таблиц: Мы явно создаем Новый МенеджерВременныхТаблиц() в начале нашей процедуры обработки.
    2. Использование и передача: Этот менеджер передается во все подчиненные процедуры и функции, которые используют запросы с временными таблицами для удаления движений.
    3. Удаление движений: Внутри ОбработкаУдаленияПроведения или в вызываемых из нее серверных процедурах мы реализуем логику по очистке движений. Это может быть либо полный откат движений по регистрам, либо формирование обратных движений.
    4. Передача дополнительных свойств: Как и в коде автора, мы передаем структуру дополнительных свойств, которая должна содержать ссылку на наш активный менеджер временных таблиц.

    Рассмотрим, как могла бы выглядеть процедура ОбработкаУдаленияПроведения с учетом явной передачи менеджера временных таблиц:

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

    Важно: убедитесь, что все функции и процедуры, такие как нПроведениеСерверУТ.ИнициализироватьДополнительныеСвойстваДляПроведения, нПроведениеСерверУТ.ПодготовитьНаборыЗаписейКРегистрацииДвижений и нПроведениеСерверУТ.ЗаписатьНаборыЗаписей, корректно принимают и используют переданный МенеджерВременныхТаблиц из структуры ДополнительныеСвойства.

Общие рекомендации по эффективному использованию временных таблиц

Чтобы избежать подобных ошибок и повысить производительность, мы рекомендуем следовать следующим правилам при работе с временными таблицами:

  1. Контролируйте объем данных: Не создавайте временные таблицы с очень большим объемом данных (сотни тысяч записей) без крайней необходимости, так как это может привести к снижению производительности и исчерпанию свободного места на диске.
  2. Ограничивайте поля: Максимально ограничивайте количество данных и полей, помещаемых во временные таблицы, только до того, что действительно требуется последующим запросам.
  3. Избегайте циклического создания/удаления: Не рекомендуется создавать и удалять временные таблицы в цикле, если можно создать одну временную таблицу до выполнения цикла и использовать ее повторно.
  4. Индексируйте временные таблицы: Для повышения производительности временные таблицы всегда следует индексировать, особенно если они участвуют в соединениях или используются в подзапросах.
  5. Отладка: Всегда используйте отладчик 1С для пошагового анализа выполнения кода, особенно в сложных процедурах проведения/отмены проведения. Это поможет нам точно определить момент создания и закрытия менеджера временных таблиц и выявить источник ошибки.

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

← На главную