Пометка удаления 1С — стандартный реквизит ПометкаУдаления (Булево) у справочников, документов, планов счетов и других ссылочных объектов. Помеченный объект остаётся в базе, ссылки на него работают. Физически его удаляет отдельная операция — удаление помеченных с контролем ссылочной целостности: объект, на который ссылаются другие данные, она не тронет.
Ниже — код для типовых задач: установить и снять пометку программно, вернуть документу проведение, запретить пометку по условию, массово снять пометку, найти ссылки и удалить помеченные объекты. Имена справочников, документов и регистров в примерах условные (Номенклатура, ЗаказКлиента, ТоварыНаСкладах), платформа — 8.3, управляемые формы.
| Операция | Что происходит | Ссылки на объект |
|---|---|---|
Пометка: Del в списке, УстановитьПометкуУдаления(Истина) | Проведённый документ теряет проведение, у справочника помечаются подчинённые элементы и элементы подчинённых справочников | Работают |
| Снятие пометки | Флаг сбрасывается, проведение документа не восстанавливается | Работают |
| Удаление помеченных | Удаляется только объект, на который никто не ссылается | Проверяются |
Непосредственное удаление: Shift+Del, Удалить() | Объект удаляется сразу, пометка не нужна | Становятся битыми |
Пометку можно снять без потерь. Непосредственное удаление отменяется только восстановлением из резервной копии.
Метод УстановитьПометкуУдаления(Пометка) есть у объекта, а не у ссылки, и сам записывает объект — Записать() после него не нужен. У справочника есть второй параметр ВключаяПодчиненные (по умолчанию Истина). Объект по ссылке получают только на сервере, поэтому команда формы делится на клиентскую и серверную части.
// Модуль формы списка документов. Команда ПереключитьПометкуУдаления:
// у помеченного объекта снимает пометку, у непомеченного — ставит.
&НаКлиенте
Процедура ПереключитьПометкуУдаления(Команда)
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ВыбраннаяСсылка = ТекущиеДанные.Ссылка;
ТекстОшибки = ПереключитьПометкуНаСервере(ВыбраннаяСсылка);
Если ПустаяСтрока(ТекстОшибки) Тогда
ОповеститьОбИзменении(ВыбраннаяСсылка);
Иначе
ПоказатьПредупреждение(, ТекстОшибки);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПереключитьПометкуНаСервере(Знач Ссылка)
ОбъектДанных = Ссылка.ПолучитьОбъект();
Если ОбъектДанных = Неопределено Тогда
Возврат "Объект не найден: возможно, он уже удалён.";
КонецЕсли;
Попытка
// Метод сам записывает объект: сработают ПередЗаписью и ПриЗаписи
ОбъектДанных.УстановитьПометкуУдаления(Не ОбъектДанных.ПометкаУдаления);
Исключение
Возврат КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
Возврат "";
КонецФункции
ПолучитьОбъект() возвращает Неопределено, если объект уже удалён, — проверяйте до вызова метода.ПередЗаписью и ПриЗаписи. Исключение возникнет, если обработчик установил Отказ, не хватает прав или объект сейчас редактирует другой пользователь. Текст ошибки возвращается на клиент строкой.ОповеститьОбИзменении обновляет открытые списки с этим объектом.У проведённого документа УстановитьПометкуУдаления(Истина) сначала отменяет проведение: флаг Проведен сбрасывается, движения очищаются, если в свойстве документа «Удаление движений» не выбрано «Не удалять автоматически». Провести помеченный документ нельзя, а снятие пометки проведение не возвращает.
Присваивание ПометкаУдаления = Истина с последующим Записать() дополнительных действий не выполняет: документ не распроведётся, подчинённые элементы справочника не пометятся. Функция ниже снимает пометку и сразу проводит документ.
// Общий модуль с флажками «Сервер» и «Вызов сервера».
// Снимает пометку удаления и, если нужно, проводит документ.
Функция СнятьПометкуУдаленияДокумента(ДокументСсылка, Провести = Ложь) Экспорт
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
Если ДокументОбъект = Неопределено Тогда
Возврат "Документ не найден.";
КонецЕсли;
Если Не ДокументОбъект.ПометкаУдаления Тогда
Возврат "Документ не помечен на удаление.";
КонецЕсли;
НачатьТранзакцию();
Попытка
ДокументОбъект.УстановитьПометкуУдаления(Ложь);
Если Провести Тогда
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЕсли;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Возврат КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
Возврат "";
КонецФункции
Снятие пометки и проведение идут в одной транзакции: если проведение не прошло (не хватило остатков, закрыт период), пометка вернётся. НачатьТранзакцию() стоит перед Попытка, ОтменитьТранзакцию() — первой строкой в Исключение. Параметр Провести по умолчанию выключен: перепроведение старых документов меняет остатки задним числом.
Проще всего прочитать пометку через ссылку: Ссылка.ПометкаУдаления. Но так платформа читает из базы объект целиком. В конфигурациях на БСП для одного поля есть ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ПометкаУдаления"), а строки табличной части проверяют одним запросом. Пример — нельзя провести заказ с помеченным контрагентом или товаром.
// Модуль объекта документа ЗаказКлиента.
// Нельзя провести заказ с помеченным контрагентом или товаром.
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Реквизит шапки: функция БСП читает из базы только одно поле.
// Без БСП: Контрагент.ПометкаУдаления
Если ЗначениеЗаполнено(Контрагент)
И ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Контрагент, "ПометкаУдаления") Тогда
СообщитьОбОшибке("Контрагент помечен на удаление.", "Контрагент", Отказ);
КонецЕсли;
// Табличная часть: все строки одним запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&СписокНоменклатуры)
| И Номенклатура.ПометкаУдаления";
Запрос.УстановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"));
Помеченные = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Для Каждого СтрокаТовара Из Товары Цикл
Если Помеченные.Найти(СтрокаТовара.Номенклатура) = Неопределено Тогда
Продолжить;
КонецЕсли;
Поле = "Товары[" + XMLСтрока(СтрокаТовара.НомерСтроки - 1) + "].Номенклатура";
Текст = "Строка " + XMLСтрока(СтрокаТовара.НомерСтроки) + ": номенклатура помечена на удаление.";
СообщитьОбОшибке(Текст, Поле, Отказ);
КонецЦикла;
КонецПроцедуры
Процедура СообщитьОбОшибке(Текст, Поле, Отказ)
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = Текст;
Сообщение.Поле = Поле;
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецПроцедуры
ОбработкаПроверкиЗаполнения срабатывает при интерактивном проведении и при вызове ПроверитьЗаполнение(), но не при установке пометки, поэтому запрещать пометку здесь бесполезно.Сообщение.Поле и УстановитьДанные(ЭтотОбъект) привязывают сообщение к ячейке формы. Индекс строки в пути начинается с нуля, XMLСтрока выводит число без пробелов между разрядами.Отбор непомеченных и проведённых документов в запросе разобран в статье «Запрос к документам 1С».
Отдельного события «при пометке на удаление» нет: пометка — это запись объекта, её ловят в ПередЗаписью и ПриЗаписи модуля объекта или подписки на событие. Чтобы отличить установку пометки от перезаписи уже помеченного объекта, сравните текущее значение со значением в базе. Событие табличного поля ПередУстановкойПометкиУдаления в обычных формах ловит только стандартную команду конкретной формы, для запрета оно не годится.
// Модуль объекта справочника Номенклатура.
// Запрет пометки при ненулевом остатке и запись в журнал регистрации.
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
ПометкаВБазе = ?(ЭтоНовый(), Ложь, ПометкаУдаленияВБазе());
ДополнительныеСвойства.Вставить("ПометкаИзменена", ПометкаУдаления <> ПометкаВБазе);
// Пометку именно устанавливают: в базе её ещё нет
Если ПометкаУдаления И Не ПометкаВБазе И ЕстьОстатки() Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Нельзя пометить на удаление ""%1"": по позиции есть остатки.", Наименование);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Процедура ПриЗаписи(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если ДополнительныеСвойства.Свойство("ПометкаИзменена")
И ДополнительныеСвойства.ПометкаИзменена Тогда
ЗаписьЖурналаРегистрации("Номенклатура.ПометкаУдаления",
УровеньЖурналаРегистрации.Информация, ЭтотОбъект.Метаданные(), Ссылка,
?(ПометкаУдаления, "Пометка установлена", "Пометка снята"));
КонецЕсли;
КонецПроцедуры
Функция ПометкаУдаленияВБазе()
// В конфигурации на БСП то же самое делает
// ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ПометкаУдаления")
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Т.ПометкаУдаления КАК ПометкаУдаления
|ИЗ
| Справочник.Номенклатура КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка");
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.ПометкаУдаления;
КонецЕсли;
Возврат Ложь;
КонецФункции
Функция ЕстьОстатки()
Запрос = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1
| Остатки.Номенклатура КАК Номенклатура
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК Остатки
|ГДЕ
| Остатки.КоличествоОстаток <> 0");
Запрос.УстановитьПараметр("Номенклатура", Ссылка);
Возврат Не Запрос.Выполнить().Пустой();
КонецФункции
ОбменДанными.Загрузка: при загрузке по обмену проверки пропускают.Если ПометкаУдаления Тогда Отказ = Истина запретит любую запись уже помеченного элемента.Отказ = Истина при пометке из списка выводит сообщение, а при программном вызове метода вызывает исключение.ДополнительныеСвойства передают признак из ПередЗаписью в ПриЗаписи.ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения), логика та же. Физическое удаление перехватывают в ПередУдалением(Отказ).Типичная ситуация: обмен или групповая обработка пометили сотни документов. Пометку снимают внешней обработкой: на форме реквизиты НачалоПериода и КонецПериода (тип Дата) и команда СнятьПометку.
// Модуль формы внешней обработки.
// Реквизиты формы: НачалоПериода, КонецПериода (Дата); команда СнятьПометку.
&НаКлиенте
Процедура СнятьПометку(Команда)
Если Не ЗначениеЗаполнено(НачалоПериода) Или Не ЗначениеЗаполнено(КонецПериода) Тогда
ПоказатьПредупреждение(, "Укажите период.");
Возврат;
КонецЕсли;
Результат = СнятьПометкуНаСервере(НачалоПериода, КонецПериода);
Для Каждого ТекстОшибки Из Результат.Ошибки Цикл
Сообщить(ТекстОшибки);
КонецЦикла;
ПоказатьПредупреждение(, СтрШаблон("Пометка снята: %1. Ошибок: %2.",
Результат.Обработано, Результат.Ошибки.Количество()));
КонецПроцедуры
&НаСервереБезКонтекста
Функция СнятьПометкуНаСервере(Знач НачалоПериода, Знач КонецПериода)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ.ЗаказКлиента КАК Док
|ГДЕ
| Док.ПометкаУдаления
| И Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|
|УПОРЯДОЧИТЬ ПО
| Док.Дата";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
Выборка = Запрос.Выполнить().Выбрать();
Результат = Новый Структура("Обработано, Ошибки", 0, Новый Массив);
Пока Выборка.Следующий() Цикл
ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если ДокументОбъект = Неопределено Тогда
Продолжить;
КонецЕсли;
Попытка
ДокументОбъект.УстановитьПометкуУдаления(Ложь);
Результат.Обработано = Результат.Обработано + 1;
Исключение
Результат.Ошибки.Добавить(Строка(Выборка.Ссылка) + ": "
+ КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЦикла;
Возврат Результат;
КонецФункции
НЕ Док.ПометкаУдаления И НЕ Док.Проведен и передайте в метод Истина.В типовых конфигурациях на БСП помеченные объекты удаляет обработка «Удаление помеченных объектов» (раздел «Администрирование» → «Обслуживание», в БП 3.0 — группа «Корректировка данных»). Она ищет ссылки на помеченные объекты по всей базе, удаляет те, на которые никто не ссылается, и показывает места использования остальных. Ссылки между самими удаляемыми объектами не мешают: помеченный документ и номенклатура, которая встречается только в нём, удалятся вместе. В платформе есть аналог: «Все функции» → «Стандартные» → «Удаление помеченных объектов». Перед запуском сделайте резервную копию; на большой базе поиск ссылок идёт долго, поэтому удаление запускают в нерабочее время.
Объект остаётся, если на него ссылается хотя бы одна запись, которая сама не удаляется: непомеченный документ, элемент справочника, запись регистра сведений, константа. Решения: снять пометку, если объект нужен; заменить ссылки на правильный объект (в БСП — обработка «Поиск и удаление дублей»); удалить ссылающиеся объекты. Места использования из кода находит НайтиПоСсылкам.
// Общий модуль с флажком «Сервер».
// Возвращает список мест, где используется ссылка.
Функция МестаИспользованияОбъекта(Ссылка) Экспорт
СписокСсылок = Новый Массив;
СписокСсылок.Добавить(Ссылка);
// Колонки результата: Ссылка (что искали), Данные (где нашли), Метаданные
НайденныеСсылки = НайтиПоСсылкам(СписокСсылок);
Результат = Новый Массив;
Для Каждого СтрокаТаблицы Из НайденныеСсылки Цикл
Если СтрокаТаблицы.Данные = Ссылка Тогда
Продолжить; // объект ссылается сам на себя
КонецЕсли;
Результат.Добавить(СтрШаблон("%1: %2",
СтрокаТаблицы.Метаданные.Представление(), СтрокаТаблицы.Данные));
КонецЦикла;
Возврат Результат;
КонецФункции
Функция принимает массив, поэтому для списка объектов её вызывают один раз: каждый вызов просматривает таблицы базы. В колонке Данные — ссылка на объект или ключ записи регистра, в Метаданные — его тип.
Объект.Удалить() — непосредственное удаление: пометка не нужна, ссылки не проверяются, и все ссылки на объект превращаются в «<Объект не найден>». Платформа это ошибкой не считает, но чинить данные придётся вам. Метод допустим для временных объектов, созданных вашим же кодом, для очистки тестовой базы и для объектов, проверенных через НайтиПоСсылкам прямо перед удалением. Для массива объектов есть и глобальный метод УдалитьОбъекты() с флагом контроля ссылок, но в примере ниже видно, что и почему не удалилось.
// Общий модуль с флажком «Сервер».
// Удаляет помеченные элементы справочника Номенклатура, на которые нет ссылок.
Функция УдалитьПомеченнуюНоменклатуру() Экспорт
Если Не ПравоДоступа("Удаление", Метаданные.Справочники.Номенклатура) Тогда
ВызватьИсключение "Недостаточно прав на удаление номенклатуры.";
КонецЕсли;
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ПометкаУдаления");
Помеченные = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Результат = Новый Структура("Удалено, НеУдалено", 0, Новый Соответствие);
Если Помеченные.Количество() = 0 Тогда
Возврат Результат;
КонецЕсли;
// Один проход по базе для всего списка
НайденныеСсылки = НайтиПоСсылкам(Помеченные);
Для Каждого СтрокаТаблицы Из НайденныеСсылки Цикл
Если СтрокаТаблицы.Данные = СтрокаТаблицы.Ссылка Тогда
Продолжить;
КонецЕсли;
Результат.НеУдалено.Вставить(СтрокаТаблицы.Ссылка,
"Используется: " + Строка(СтрокаТаблицы.Данные));
КонецЦикла;
Для Каждого ПомеченнаяСсылка Из Помеченные Цикл
Если Результат.НеУдалено[ПомеченнаяСсылка] <> Неопределено Тогда
Продолжить;
КонецЕсли;
ОбъектНоменклатуры = ПомеченнаяСсылка.ПолучитьОбъект();
Если ОбъектНоменклатуры = Неопределено Или Не ОбъектНоменклатуры.ПометкаУдаления Тогда
Продолжить; // уже удалён или пометку успели снять
КонецЕсли;
Попытка
ОбъектНоменклатуры.Удалить();
Результат.Удалено = Результат.Удалено + 1;
Исключение
Результат.НеУдалено.Вставить(ПомеченнаяСсылка,
КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЦикла;
Возврат Результат;
КонецФункции
Удалить() упадёт с ошибкой «Нарушение прав доступа!», а явная проверка остановит обработку до долгого поиска ссылок.| Право в роли | Что разрешает |
|---|---|
| Интерактивная пометка удаления | Ставить пометку командой формы |
| Интерактивное снятие пометки удаления | Снимать пометку командой формы |
| Интерактивное удаление помеченных | Удалять помеченные штатной командой |
| Интерактивное удаление | Удалять непосредственно (Shift+Del) |
| Изменение | Ставить и снимать пометку из кода |
| Удаление | Удалять из кода: Удалить(), обработки удаления |
Интерактивные права влияют только на стандартные команды интерфейса. В своей обработке проверяйте право явно функцией ПравоДоступа(). «Интерактивное удаление» в типовых конфигурациях обычно никому не выдают. Настройка ролей — в статье «Права доступа 1С».
Ссылка.ПолучитьОбъект().ПолучитьОбъект() вернул Неопределено: объект уже удалён.Записать(), проведение при этом не отменяется. Используйте УстановитьПометкуУдаления().В списке выделите объект и снова нажмите Del: у помеченного объекта команда снимает пометку. Программно — Объект.УстановитьПометкуУдаления(Ложь) на сервере. Документ после этого остаётся непроведённым.
Нет. Пометка — обычная запись объекта, её ловят в ПередЗаписью и ПриЗаписи модуля объекта или подписки на событие, сравнивая значение со значением в базе.
Проведение отменится, документ выпадет из остатков и оборотов. После снятия пометки он сам не проведётся — проведите заново, проверив, не закрыт ли период.
Только непосредственным удалением, и ссылки станут битыми. Правильнее заменить ссылки на актуальный объект (в БСП это делает «Поиск и удаление дублей»), а затем удалить помеченный объект штатной обработкой.
Если нужно разобрать тысячи неудаляемых объектов, заменить ссылки после ошибочной загрузки или встроить запрет пометки в типовую конфигурацию через расширение, быстрее поручить это программисту, который знает вашу конфигурацию. Разместите задание на 1c-only.ru с примером объекта и нужным результатом или выберите исполнителя в разделе услуг по доработке 1С.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.