Как избежать блокировки объекта при использовании контекста формы документа во внешней обработке 1С 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При автоматизации рутинных операций в 1С:Предприятии 7.7 разработчики часто сталкиваются с необходимостью программного создания большого количества документов. Типичный сценарий: внешняя обработка в цикле создает документы (например, «Счета»), заполняет их данными из внешнего источника (Excel или текстовый файл) и записывает. Однако часто возникают ситуации, когда часть важной логики — расчет скидок, налогов или заполнение специфических переменных — заложена исключительно в модуле формы документа. Чтобы не дублировать этот сложный код в обработке, программисты пытаются использовать метод ОткрытьФорму() для получения контекста, выполнения процедур формы и последующего закрытия. Именно здесь возникает критическая ошибка: "Объект заблокирован". Проанализируем, почему это происходит и как правильно организовать работу.

Почему возникает ошибка «Объект заблокирован»?

Разберем ситуацию с точки зрения механизмов управления данными в 1С 7.7. Когда мы работаем с документом в коде обработки, мы используем объектную переменную, созданную через СоздатьОбъект("Документ.Счет"). В этот момент мы имеем программный экземпляр объекта.

Как только вызывается функция ОткрытьФорму(Конт), платформа создает второй экземпляр этого же объекта — визуальный. В памяти 1С появляются две независимые ссылки на одну и ту же запись в базе данных. Платформа 7.7 жестко блокирует возможность изменения объекта через программную переменную, если его форма открыта для редактирования пользователем (или программно). Когда обработка пытается выполнить Док.Записать() сразу после Конт.Форма.Закрыть(0), возникает конфликт блокировок, так как форма еще не успела физически завершить процесс освобождения объекта в памяти.

Решение 1: Перенос логики из формы в Глобальный модуль

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

  1. Найдем нужную процедуру в модуле формы документа и переместим её в Глобальный модуль.
  2. Добавим к объявлению процедуры ключевое слово Экспорт.
  3. В качестве параметра процедуры будем передавать контекст или сам объект.

Проанализируем пример кода в глобальном модуле:


Процедура глРассчитатьСкидкуСчета(КонтОбъекта) Экспорт
    // Здесь мы работаем с документом через переданный параметр
    Если КонтОбъекта.Наценка > 0 Тогда
        КонтОбъекта.ВыбратьСтроки();
        Пока КонтОбъекта.ПолучитьСтроку() = 1 Цикл
            КонтОбъекта.Сумма = КонтОбъекта.Цена * КонтОбъекта.Количество * (1 + КонтОбъекта.Наценка/100);
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

Теперь из внешней обработки мы можем вызвать этот расчет напрямую, не открывая форму:


Док = СоздатьОбъект("Документ.Счет");
Док.Новый();
// Заполнение реквизитов...
глРассчитатьСкидкуСчета(Док);
Док.Записать();

Решение 2: Трюк с «тихим» открытием формы через параметр

Если изменять конфигурацию (глобальный модуль) запрещено, воспользуемся методом выполнения кода в контексте формы без её визуального отображения. Рассмотрим, как это работает через передачу параметра в ОткрытьФорму().

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


// В обработке
ОткрытьФорму("Документ.Счет", "Автозаполнение", Док.ТекущийДокумент());

Теперь самое важное — внесем изменения в модуль формы документа Счет в предопределенную процедуру ПриОткрытии():


Процедура ПриОткрытии()
    Парам = Форма.Параметр;
    Если Парам = "Автозаполнение" Тогда
        // Вызываем все внутренние процедуры расчета формы
        РассчитатьСкидки(); 
        ПриИзмененииНаценки();
        // Записываем документ прямо из контекста формы
        Записать();
        // Отменяем визуальное открытие формы!
        СтатусВозврата(0); 
        Возврат;
    КонецЕсли;
КонецПроцедуры

Почему это эффективно? Метод СтатусВозврата(0) внутри ПриОткрытии сообщает платформе, что окно открывать не нужно. При этом весь код, предшествующий этой команде, выполняется полноценно в контексте формы, имея доступ ко всем её переменным и процедурам. Блокировка объекта снимается мгновенно по завершении работы процедуры.

Решение 3: Использование внешней компоненты FormEx

Если необходимо именно закрыть уже открытое окно и гарантированно избежать ошибки блокировки, рассмотрим использование возможностей компоненты FormEx. Она предоставляет прямой доступ к управлению окнами 1С.

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


// Создаем объект FormEx
Сервис = СоздатьОбъект("Сервис");
// Закрываем окно по контексту формы
Сервис.ЗакрытьОкно(Конт);
// Теперь блокировка снята, и мы можем записывать объект через переменную
Док.НайтиДокумент(Конт.ТекущийДокумент());
Док.Записать();

Решение 4: Правильная последовательность действий в цикле

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

Выясним правильный алгоритм действий без лишних блокировок:

  1. Сначала создаем и записываем документ в обработке (пустой или с базовыми реквизитами), чтобы получить ссылку ТекущийДокумент().
  2. Вызываем ОткрытьФорму(Док.ТекущийДокумент(), Конт).
  3. Через полученный Конт меняем реквизиты формы.
  4. Вместо Док.Записать() в коде обработки, вызываем Конт.Записать() (метод записи самой формы) или программно нажимаем кнопку "ОК" через Конт.Форма.ВыполнитьВыбор().
  5. Если форму нужно оставить открытой, просто не вызываем Форма.Закрыть().

Пример корректного обращения к процедурам формы через контекст:


// В обработке
Конт = "";
ОткрытьФорму(Док.ТекущийДокумент(), Конт);
// Заполняем переменную внутри модуля формы
Конт.ПеременнаяСкидки = 15; 
// Вызываем экспортную процедуру модуля формы
Конт.ПересчитатьВсе(); 
// Записываем изменения самой формой
Конт.Записать(); 

Важный нюанс: Чтобы процедура модуля формы была доступна через Конт.ИмяПроцедуры(), она ОБЯЗАТЕЛЬНО должна быть объявлена с ключевым словом Экспорт. В противном случае 1С выдаст ошибку "Поле агрегатного объекта не обнаружено".

Проанализируем выводы

Работа с контекстом формы в 1С 7.7 требует осторожности. Если ваша задача — массовое создание документов, старайтесь избегать интерактивных методов (открытия окон). Копирование логики в Глобальный модуль или использование "тихого" открытия через СтатусВозврата(0) — это профессиональные стандарты, которые обеспечивают высокую скорость работы и отсутствие ошибок блокировки базы данных. Помните, что каждое открытое окно потребляет ресурсы памяти, и при создании 1000 счетов в цикле через ОткрытьФорму без их закрытия система может просто аварийно завершиться.

← На главную