В практике разработчика на платформе 1С:Предприятие 7.7 часто возникает задача: реализовать в форме документа кнопку, которая должна изменить дату документа на текущую, снять его с проведения (если он был проведен) и провести заново. На первый взгляд задача кажется тривиальной, однако при попытке вызвать метод СделатьНеПроведенным() или изменить реквизит ДатаДок из модуля открытой формы, система выдает ошибку о блокировке объекта. Разберем подробно, почему так происходит и какие существуют надежные способы решения этой проблемы.
Проанализируем архитектуру платформы 7.7. Когда пользователь открывает форму документа, система создает интерактивный объект, который накладывает блокировку на соответствующую запись в базе данных. Любые попытки создать новый объект через СоздатьОбъект("Документ.Вид") и найти в нем этот же документ (пока форма открыта) будут натыкаться на конфликт: база данных «видит», что документ уже редактируется (заблокирован формой). Именно поэтому методы Записать() или СделатьНеПроведенным() внутри кода формы не позволяют выполнить операции, требующие монопольного доступа к записи документа в транзакции.
Рассмотрим самый популярный и надежный способ — использование глобальной процедуры и метода ОбработкаОжидания. Суть метода заключается в том, чтобы «отпустить» форму (закрыть ее), снять блокировку и лишь затем выполнить программные действия. Посмотрим на алгоритм реализации по шагам.
Шаг 1. Подготовка в Глобальном модуле. Нам необходимо создать экспортную процедуру, которая выполнит «грязную работу», и переменную для хранения ссылки на документ.
Перем глДокументДляОбработки Экспорт;
Процедура глПерепровестиДокументТекущейДатой() Экспорт
// Сразу отключаем ожидание, чтобы процедура не зациклилась
ОбработкаОжидания("глПерепровестиДокументТекущейДатой", 0);
Док = СоздатьОбъект("Документ." + глДокументДляОбработки.Вид());
Если Док.НайтиДокумент(глДокументДляОбработки) = 1 Тогда
// Снимаем проведение, если документ проведен
Если Док.Выбран() = 1 Тогда
Док.СделатьНеПроведенным();
Док.ДатаДок = РабочаяДата(); // или ТекущаяДата()
Док.Записать();
Попытка
Док.Провести();
Сообщить("Документ перепроведен текущей датой!");
Исключение
Сообщить("Ошибка при проведении: " + ОписаниеОшибки());
КонецПопытки;
// При необходимости — открываем форму обратно
// ОткрытьФорму(Док.ТекущийДокумент());
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Шаг 2. Вызов из модуля формы документа. В процедуре нажатия кнопки мы подготавливаем данные и закрываем форму.
Процедура НажатьКнопкуПерепроведения()
глДокументДляОбработки = ТекущийДокумент();
// Запускаем ожидание на 1 секунду. Этого достаточно, чтобы форма успела закрыться.
ОбработкаОжидания("глПерепровестиДокументТекущейДатой", 1);
Форма.Закрыть(0); // Закрываем без вопросов о сохранении
КонецПроцедуры
Проанализируем ситуацию, когда мы не хотим загромождать глобальный модуль. В этом случае можно использовать внешнюю обработку. Этот метод элегантен тем, что контекст передается напрямую. Разберем пример реализации.
В модуле формы документа пишем:
Процедура ВыполнитьПерепроведение()
Параметр = СоздатьОбъект("СписокЗначений");
Параметр.ДобавитьЗначение(ТекущийДокумент(), "Документ");
ОткрытьФорму("Отчет", Параметр, КаталогИБ() + "ExtForms\FixDoc.ert");
Форма.Закрыть(0);
КонецПроцедуры
В самой обработке FixDoc.ert в процедуре ПриОткрытии() реализуем следующую логику:
Процедура ПриОткрытии()
ТекДок = Форма.Параметр.Получить("Документ");
// Здесь также лучше использовать ОбработкаОжидания внутри обработки,
// чтобы дать время форме документа окончательно освободить объект.
// Либо выполнять действия, если форма гарантированно закрыта.
// ... код изменения и проведения ...
СтатусВозврата(0); // Закрываем саму обработку после выполнения
КонецПроцедуры
Выясним еще одну интересную особенность 1С 7.7. Если документ открыт в режиме просмотра, он не блокируется так жестко, как при редактировании. Посмотрим, как это можно использовать. Мы можем программно закрыть форму и тут же открыть ее программно в режиме просмотра через ОткрытьФорму(Контекст, 0). В некоторых конфигурациях это позволяет избежать использования глобальных переменных, так как объект становится доступен для изменения через СоздатьОбъект.
При реализации подобных механизмов стоит учитывать следующие моменты:
Попытка...Исключение.ОбработкаОжидания всегда первой строкой отключайте ее (вторым параметром 0), иначе процедура будет вызываться бесконечно с заданным интервалом.Таким образом, мы рассмотрели основные способы обхода ограничений платформы. Выбор конкретного метода зависит от стандартов разработки, принятых в вашей организации, и необходимости сохранения чистоты глобального модуля.