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