Пометка удаления элемента справочника из кода 1С 8.3: метод, транзакции, формы

· 9 мин

Содержание

Чтобы программно пометить на удаление элемент справочника в 1С 8.3, получите объект по ссылке и вызовите у него метод УстановитьПометкуУдаления(Истина): Ссылка.ПолучитьОбъект().УстановитьПометкуУдаления(Истина). Метод сам записывает объект в базу, отдельный Записать() не нужен. Работает только на сервере или во внешнем соединении — на клиенте объекта справочника нет.

Есть и второй способ: присвоить Объект.ПометкаУдаления = Истина и вызвать Записать(). Он годится, когда в той же записи вы меняете ещё и реквизиты. Для чистой пометки удобнее метод: он умеет помечать подчинённые элементы иерархии и читается однозначно. Всё ниже относится к платформе, а не к конкретной конфигурации, — код одинаков в Бухгалтерии, ЗУП, УТ и самописных базах.

Как платформа хранит пометку удаления справочника

Пометка удаления — это стандартный реквизит ПометкаУдаления типа Булево. Он есть у каждого справочника, документа, плана видов характеристик и плана счетов. Пометка ничего не удаляет физически: элемент остаётся в таблице, на него по-прежнему ведут ссылки, его можно выбрать в запросе.

Физически элемент исчезает только при удалении помеченных объектов. В типовых конфигурациях на БСП это обработка «Удаление помеченных объектов» в разделе Администрирование → Обслуживание. Она проверяет ссылки и удаляет только то, на что никто не ссылается.

Чем отличаются три операции:

ОперацияЧто делаетСсылочная целостностьОткат
УстановитьПометкуУдаления(Истина)Ставит флаг и записывает объектНе проверяетсяСнять пометку: УстановитьПометкуУдаления(Ложь)
Удаление помеченных (обработка)Удаляет помеченные объекты без ссылокПроверяетсяТолько из резервной копии
Объект.Удалить()Удаляет запись сразуНе проверяетсяТолько из резервной копии

Удалить() у объекта оставляет в базе «битые» ссылки: документы будут показывать <Объект не найден>. В рабочем коде используйте пометку, а удаление доверьте штатной обработке.

Чтобы прочитать пометку без получения объекта, обратитесь к ссылке: Ссылка.ПометкаУдаления. Платформа выполнит запрос к базе. В цикле по тысячам ссылок это медленно — лучше достать поле запросом сразу.

Порядок действий при программной пометке на удаление

Последовательность одна и та же, откуда бы ни вызывался код:

  1. Получите ссылку на элемент: из запроса, НайтиПоКоду(), НайтиПоНаименованию() или из формы.
  2. Проверьте, что ссылка не пустая и элемент ещё не помечен.
  3. Получите объект методом ПолучитьОбъект().
  4. Вызовите УстановитьПометкуУдаления(Истина) внутри Попытка … Исключение.
  5. Запишите результат в журнал или сообщите пользователю.

Права. Для программной пометки пользователю нужно право Изменение на справочник. Право ИнтерактивнаяПометкаУдаления касается только кнопки в интерфейсе и на код не влияет. Если права дать нельзя, а пометить нужно (например, в регламентном задании), выполните код в привилегированном режиме: УстановитьПривилегированныйРежим(Истина) в серверном модуле.

События. Пометка удаления — это запись объекта. Поэтому срабатывают обработчики ПередЗаписью и ПриЗаписи модуля объекта и подписки на события. Если там стоит проверка, которая отменяет запись, пометка не установится и вы получите исключение.

💡 Проверить, что вызов пришёл именно ради пометки, можно в ПередЗаписью: сравните ПометкаУдаления объекта со значением в базе по ссылке. Так отсекают тяжёлые проверки, которые при пометке не нужны.

Пример кода: пометить элемент справочника на удаление

Серверная функция, которая помечает один элемент и возвращает результат. Её можно положить в общий модуль с флагами «Сервер» и «Вызов сервера».

// Помечает элемент справочника на удаление.
// Возвращает Истина, если пометка установлена.
Функция ПометитьЭлементНаУдаление(Ссылка, ВключаяПодчиненные = Истина) Экспорт

    Если Не ЗначениеЗаполнено(Ссылка) Тогда
        Возврат Ложь;
    КонецЕсли;

    Если Ссылка.ПометкаУдаления Тогда
        Возврат Истина; // уже помечен, повторно писать не нужно
    КонецЕсли;

    СпрОбъект = Ссылка.ПолучитьОбъект();
    Если СпрОбъект = Неопределено Тогда
        Возврат Ложь; // ссылка битая, объекта в базе нет
    КонецЕсли;

    Попытка
        СпрОбъект.Заблокировать();
        СпрОбъект.УстановитьПометкуУдаления(Истина, ВключаяПодчиненные);
    Исключение
        ЗаписьЖурналаРегистрации("ПометкаУдаления",
            УровеньЖурналаРегистрации.Ошибка,
            Ссылка.Метаданные(), Ссылка,
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат Ложь;
    КонецПопытки;

    Возврат Истина;

КонецФункции

Разбор синтаксиса:

  • 🔹 УстановитьПометкуУдаления(Пометка, ВключаяПодчиненные) — первый параметр Булево. Второй необязательный, по умолчанию Истина. Он нужен иерархическим справочникам: при пометке группы помечаются и вложенные элементы.
  • 🔹 ПолучитьОбъект() возвращает Неопределено, если записи по ссылке в базе уже нет. Без проверки получите ошибку вызова метода у неопределённого значения.
  • 🔹 Заблокировать() — объектная блокировка. Если элемент сейчас открыт и изменён в форме у другого пользователя, исключение возникнет здесь, а не позже.
  • 🔹 ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()) даёт текст с модулем и строкой. В журнале регистрации его удобно фильтровать по событию «ПометкаУдаления».

Найти элемент по коду и пометить его — две строки:

Ссылка = Справочники.Номенклатура.НайтиПоКоду("00-00000125");
ПометитьЭлементНаУдаление(Ссылка);

НайтиПоКоду() при неудаче возвращает пустую ссылку. Функция выше это отсечёт.

Снять пометку — тот же метод со значением Ложь: СпрОбъект.УстановитьПометкуУдаления(Ложь). Для группы с параметром Истина пометка снимется и с вложенных элементов.

Ошибки и блокировки при пометке удаления

Код пометки падает по нескольким понятным причинам. Удобно разбирать их по симптому:

Диагностика: выберите симптом

Про транзакции отдельно. Если вы помечаете несколько связанных объектов и нужно «всё или ничего», оборачивайте их в явную транзакцию:

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

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

Групповая пометка на удаление элементов справочника

Массовую пометку делают через запрос: он сразу отбирает нужные элементы и не тянет в память объекты, которые трогать не надо. Пример — пометить номенклатуру, которая не использовалась, по признаку из реквизита или списку кодов:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Спр.Ссылка КАК Ссылка
|ИЗ
|    Справочник.Номенклатура КАК Спр
|ГДЕ
|    НЕ Спр.ПометкаУдаления
|    И НЕ Спр.Предопределенный
|    И НЕ Спр.ЭтоГруппа
|    И Спр.Код В (&Коды)";
Запрос.УстановитьПараметр("Коды", МассивКодов);

Выборка = Запрос.Выполнить().Выбрать();
Помечено = 0;
Ошибок = 0;

Пока Выборка.Следующий() Цикл
    Если ПометитьЭлементНаУдаление(Выборка.Ссылка, Ложь) Тогда
        Помечено = Помечено + 1;
    Иначе
        Ошибок = Ошибок + 1;
    КонецЕсли;
КонецЦикла;

Сообщить(СтрШаблон("Помечено: %1, ошибок: %2",
    Формат(Помечено, "ЧГ=0"), Формат(Ошибок, "ЧГ=0")));

Что здесь сделано осознанно:

  • ✅ НЕ Спр.ПометкаУдаления — уже помеченные не перезаписываются, повторный запуск безопасен.
  • ✅ НЕ Спр.Предопределенный — предопределённые элементы используются в коде конфигурации, помечать их нельзя.
  • ✅ НЕ Спр.ЭтоГруппа — группы не попадают в выборку, а второй параметр Ложь не задевает соседние элементы.
  • ✅ Каждый элемент пишется отдельно: ошибка на одном не останавливает остальные.

Если в справочнике нет поля ЭтоГруппа (справочник не иерархический или иерархия только элементов), уберите это условие, иначе запрос не выполнится.

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

Массовая пометка в рабочей базе — сначала резервная копия. Пометку можно снять тем же кодом, но обработчики ПриЗаписи могли изменить связанные данные, и откатить их кодом уже сложнее.

Если обработчики записи при массовой операции не нужны, иногда ставят СпрОбъект.ОбменДанными.Загрузка = Истина перед записью. Тогда платформа пропускает проверки и большую часть обработчиков. Делайте так только когда точно знаете, что пропускаете: типовые подписки на события тоже не выполнятся.

Пометка элементов, на которые есть ссылки

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

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

МассивСсылок = Новый Массив;
МассивСсылок.Добавить(Ссылка);

НайденныеСсылки = НайтиПоСсылкам(МассивСсылок);

Если НайденныеСсылки.Количество() = 0 Тогда
    ПометитьЭлементНаУдаление(Ссылка);
Иначе
    Для Каждого СтрокаТаблицы Из НайденныеСсылки Цикл
        Сообщить(СтрШаблон("Используется в: %1 (%2)",
            Строка(СтрокаТаблицы.Данные), СтрокаТаблицы.Метаданные.Представление()));
    КонецЦикла;
КонецЕсли;

Три практических момента:

  1. НайтиПоСсылкам() сканирует все таблицы, где может храниться ссылка этого типа. На большой базе вызывайте его сразу для массива ссылок, а не по одной в цикле.
  2. Элемент может ссылаться сам на себя: например, через реквизит «Родитель» или служебные регистры сведений. Такие строки в результате ещё не значат, что элемент реально используется. Отфильтруйте их по колонке Данные.
  3. Если элемент используется и его всё же нужно убрать, правильный путь — заменить ссылки на другой элемент и только потом пометить. В типовых конфигурациях для этого есть обработка «Поиск и удаление дублей»; в коде ссылки меняют, переписывая найденные объекты.

Пометка удаления из управляемой формы

В управляемой форме объект справочника на клиенте недоступен. Поэтому команда формы разбивается на две процедуры: клиентскую, которая собирает ссылки, и серверную, которая помечает.

Пример для формы списка — пометить все выделенные строки:

&НаКлиенте
Процедура ПометитьВыделенные(Команда)

    Выделенные = Элементы.Список.ВыделенныеСтроки;
    Если Выделенные.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    МассивСсылок = Новый Массив;
    Для Каждого ВыделеннаяСсылка Из Выделенные Цикл
        МассивСсылок.Добавить(ВыделеннаяСсылка);
    КонецЦикла;

    ПометитьНаСервере(МассивСсылок);
    Элементы.Список.Обновить();

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

&НаСервереБезКонтекста
Процедура ПометитьНаСервере(МассивСсылок)

    Для Каждого Ссылка Из МассивСсылок Цикл
        СпрОбъект = Ссылка.ПолучитьОбъект();
        СпрОбъект.УстановитьПометкуУдаления(Истина);
    КонецЦикла;

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

В динамическом списке справочника ВыделенныеСтроки содержит ссылки, поэтому их можно передавать на сервер напрямую. &НаСервереБезКонтекста не передаёт на сервер данные формы — вызов получается лёгким.

Если пометку ставят из формы элемента, задача другая: объект уже лежит в реквизите формы Объект. Удобный вариант — серверная процедура в форме:

&НаСервере
Процедура ПометитьТекущийНаСервере()
    СпрОбъект = РеквизитФормыВЗначение("Объект");
    СпрОбъект.УстановитьПометкуУдаления(Истина);
    ЗначениеВРеквизитФормы(СпрОбъект, "Объект");
КонецПроцедуры

После этого вызовите на клиенте ОповеститьОбИзменении(Объект.Ссылка), чтобы открытые списки перерисовали строку с пометкой.

💡 Если цель — просто дать пользователю пометку, ничего писать не нужно. В списках интерфейса «Такси» стандартная команда пометки удаления уже есть в меню «Ещё» и вызывается клавишей Del. Свой код нужен, когда пометка — часть сценария: закрыть договор и пометить его, пометить товары по отбору, обработать выделенное с проверками.

Перед выпуском своей команды полезно пройтись по списку:

Перед выпуском команды пометки
Выполнено: 0 / 6

Частые вопросы

Чем пометка удаления справочника отличается от документа?
У документа метод принимает только саму пометку: подчинённых элементов у документов нет. Пометка проведённого документа отменяет его проведение и удаляет движения.
Можно ли пометить на удаление элемент через внешнее соединение или COM?
Да. Внешнее соединение работает в серверном контексте, там доступны ПолучитьОбъект() и УстановитьПометкуУдаления(). Нужны права пользователя, под которым идёт подключение.
Как найти всё, что помечено на удаление, в одном справочнике?
Запросом с условием ГДЕ Спр.ПометкаУдаления. В форме списка — установите отбор по колонке «Пометка удаления» или включите показ помеченных в настройках списка.
Попадёт ли пометка удаления в обмен с другой базой?
Да, если справочник входит в план обмена. Пометка — это запись объекта, она регистрируется к выгрузке как любое изменение.
Можно ли запретить пометку удаления только для части элементов?
Да. В обработчике ПередЗаписью модуля объекта проверьте, что пометка ставится, и выставьте Отказ = Истина по своему условию. Это сработает и для кнопки, и для кода.