Пометка удаления из кода: объект, форма, запрос и COM-скрипт
Содержание
Чтобы программно установить пометку удаления в 1С, получите объект по ссылке и вызовите у него метод УстановитьПометкуУдаления(Истина): Ссылка.ПолучитьОбъект().УстановитьПометкуУдаления(Истина). Метод сам записывает объект, отдельный Записать() не нужен. Код должен выполняться на сервере: у ссылки этого метода нет, а на клиенте объект недоступен.
Частое заблуждение — что достаточно присвоить Объект.ПометкаУдаления = Истина и записать. Для справочника это иногда срабатывает, но для проведённого документа это неверный путь: метод УстановитьПометкуУдаления одновременно отменяет проведение и очищает движения, а ручное присваивание этого не гарантирует. Всё, что описано ниже, относится к платформе 1С:Предприятие 8.3 и не зависит от конфигурации: БП, ЗУП, УТ или своя разработка.
Как работает метод УстановитьПометкуУдаления
Метод есть у объектов всех ссылочных типов: справочников, документов, планов видов характеристик, планов счетов, планов видов расчёта, бизнес-процессов, задач и планов обмена. У регистров и констант пометки удаления нет вообще.
Сигнатура отличается по типам:
| Тип объекта | Вызов | Что делает дополнительно |
|---|---|---|
| Справочник, ПВХ, план счетов | УстановитьПометкуУдаления(Пометка, ВключаяПодчиненные) | Для группы помечает вложенные элементы; второй параметр по умолчанию Истина |
| Документ | УстановитьПометкуУдаления(Пометка) | Проведённый документ распроводится, движения удаляются |
| Прочие ссылочные | УстановитьПометкуУдаления(Пометка) | Записывает объект с новым значением пометки |
Что происходит при вызове:
- 📝 объект записывается, срабатывают
ПередЗаписьюиПриЗаписимодуля объекта и подписки на события; - 🔄 у проведённого документа выполняется отмена проведения, поэтому вызывается и
ОбработкаУдаленияПроведения; - 🔒 перед записью платформа берёт блокировку объекта; если его держит открытая форма другого пользователя, запись упадёт;
- ✅ проверяется право на изменение объекта. Права «Интерактивная пометка удаления» относятся к действиям пользователя в интерфейсе, на код они не влияют.
Простейший пример в серверном модуле:
Объект = Ссылка.ПолучитьОбъект();
Если Объект <> Неопределено И Не Объект.ПометкаУдаления Тогда
Объект.УстановитьПометкуУдаления(Истина);
КонецЕсли;
ПолучитьОбъект() возвращает Неопределено, если по ссылке объекта уже нет (битая ссылка). Проверка на текущее значение пометки спасает от лишней записи и лишней строки в журнале регистрации.
Чтобы отследить момент пометки в модуле объекта, сравните текущее значение с сохранённым: Если ПометкаУдаления И Не Ссылка.Пустая() И Не Ссылка.ПометкаУдаления Тогда. Обращение Ссылка.ПометкаУдаления читает значение из базы, то есть до записи.
Пометка удаления из управляемой формы
В управляемом приложении код формы разделён на клиент и сервер. Кнопка нажимается на клиенте, а пометку ставят на сервере. Есть два типовых сценария: форма списка и форма самого объекта.
Форма списка: пометить выделенные строки
Добавьте в форму команду, привяжите её к кнопке в командной панели списка и напишите два обработчика:
&НаКлиенте
Процедура ПометитьВыделенные(Команда)
Выделенные = Элементы.Список.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ПометитьНаСервере(Выделенные);
Элементы.Список.Обновить();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПометитьНаСервере(Ссылки)
Для Каждого Ссылка Из Ссылки Цикл
Объект = Ссылка.ПолучитьОбъект();
Если Объект = Неопределено Или Объект.ПометкаУдаления Тогда
Продолжить;
КонецЕсли;
Попытка
Объект.Заблокировать();
Объект.УстановитьПометкуУдаления(Истина);
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Не удалось пометить %1: %2",
Ссылка, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Сообщение.Сообщить();
КонецПопытки;
КонецЦикла;
КонецПроцедуры
НаСервереБезКонтекста выбран намеренно: форму на сервер передавать не нужно, вызов получается быстрее. Заблокировать() заранее проверяет, не открыт ли объект у коллеги, и даёт понятную ошибку по конкретной строке, а не обрывает весь цикл.
Если пометку ставят из другой формы, а список открыт отдельно, после записи вызовите на клиенте ОповеститьОбИзменении(Ссылка): открытые списки этого типа перечитают данные.
Форма объекта: пометить открытый документ
В форме объекта данные живут в реквизите Объект. Его переводят в прикладной объект, ставят пометку и возвращают обратно в форму:
&НаСервере
Процедура ПереключитьПометкуНаСервере()
ДокОбъект = РеквизитФормыВЗначение("Объект");
ДокОбъект.УстановитьПометкуУдаления(Не ДокОбъект.ПометкаУдаления);
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры
Две оговорки по этому способу:
- Если в форме есть несохранённые правки, они запишутся вместе с пометкой. Перед вызовом проверьте
Модифицированностьи спросите пользователя. - У нового, ещё не записанного объекта (
Объект.Ссылка.Пустая()) вызов метода приведёт к его первой записи. Обычно такую команду для нового объекта просто блокируют.
Стандартная команда «Пометить на удаление» в формах уже есть. Свою команду имеет смысл писать, когда нужна дополнительная логика: проверка статуса, запись причины в комментарий, запрет для отдельных ролей.
Массовая пометка на удаление запросом и выборкой
Типовая задача — пометить сотни объектов по условию: старые непроведённые черновики, номенклатуру из закрытой группы, договоры без движения. Схема одна: запросом получить ссылки, затем по каждой вызвать метод.
Отбор ссылок запросом
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК Док
|ГДЕ
| НЕ Док.Проведен
| И НЕ Док.ПометкаУдаления
| И Док.Дата < &Граница");
Запрос.УстановитьПараметр("Граница", НачалоГода(ТекущаяДатаСеанса()));
Ссылки = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Условие НЕ ПометкаУдаления в отборе обязательно: без него повторный запуск будет заново записывать уже помеченные объекты.
Запись порциями в транзакциях
Одна транзакция на 50 000 объектов — плохая идея: она долго держит блокировки, раздувает журнал транзакций СУБД и при ошибке на последнем объекте откатывает всё. Разбейте массив на порции по 200–500 штук:
Процедура ПометитьПорцию(Порция)
НачатьТранзакцию();
Попытка
Для Каждого Ссылка Из Порция Цикл
Объект = Ссылка.ПолучитьОбъект();
Объект.Заблокировать();
Объект.УстановитьПометкуУдаления(Истина);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
РазмерПорции = 500;
Порция = Новый Массив;
Для Каждого Ссылка Из Ссылки Цикл
Порция.Добавить(Ссылка);
Если Порция.Количество() = РазмерПорции Тогда
ПометитьПорцию(Порция);
Порция = Новый Массив;
КонецЕсли;
КонецЦикла;
Если Порция.Количество() > 0 Тогда
ПометитьПорцию(Порция);
КонецЕсли;
Если порция упала, уже зафиксированные порции остаются помеченными. Повторный запуск с тем же запросом подберёт только оставшиеся объекты благодаря условию НЕ ПометкаУдаления.
Выборка через менеджер
Без запроса можно обойтись выборкой менеджера: Справочники.Номенклатура.Выбрать() или Документы.ПоступлениеТоваровУслуг.Выбрать(ДатаНачала, ДатаОкончания). У элемента выборки есть ПолучитьОбъект(). Удобно для простого перебора, но сложное условие (по реквизитам табличной части, по связанным данным) проще и быстрее описать запросом.
Выбор способа
| Способ | Когда подходит | Риски |
|---|---|---|
| Цикл без транзакции | До нескольких сотен объектов, ошибки по отдельным объектам допустимы | Часть объектов останется непомеченной, нужен лог |
| Порции в транзакциях | Тысячи объектов, нужна целостность внутри порции | Падение порции откатывает 500 объектов, а не один |
| Одна транзакция | Набор связанных объектов, которые должны пометиться вместе | Долгие блокировки, мешает работе пользователей |
| Фоновое задание | Долгая обработка в рабочее время | Ошибки видны только в журнале или в сообщениях задания |
Перед массовой пометкой в рабочей базе сделайте резервную копию и сначала прогоните код на копии. Помеченные документы теряют проведение, а значит, меняются остатки и отчёты за прошлые периоды.
Если в базе настроена дата запрета изменения (в типовых конфигурациях на БСП), документы закрытого периода пометить не получится: проверка сработает при записи. Это ожидаемое поведение, а не ошибка кода.
Пометка через COM-соединение из внешнего скрипта
Внешнее соединение позволяет поставить пометку без запуска клиента: из планировщика задач, из скрипта интеграции, из другой программы. Объект соединения — V83.COMConnector.
Пример на PowerShell для файловой базы:
$connector = New-Object -ComObject "V83.COMConnector"
$db = $connector.Connect('File="D:\Bases\Trade";Usr="Robot";Pwd="secret";')
$ref = $db.Catalogs.Номенклатура.FindByCode("00-00000123")
if (-not $ref.IsEmpty()) {
$obj = $ref.GetObject()
if (-not $obj.DeletionMark) {
$obj.SetDeletionMark($true)
}
}
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($db) | Out-Null
Для клиент-серверной базы строка соединения выглядит так: Srvr="server1c";Ref="trade";Usr="Robot";Pwd="secret";.
Что проверить, чтобы скрипт заработал:
- 🧩 компонента
comcntr.dllзарегистрирована той же разрядности, что и процесс скрипта: 64-битный PowerShell работает только с 64-битной платформой. Регистрация от имени администратора:regsvr32 "C:\Program Files\1cv8\<версия>\bin\comcntr.dll"; - 👤 у пользователя
Robotесть право внешнего соединения и права на изменение нужных объектов; - 🎫 соединение — полноценный сеанс, он занимает клиентскую лицензию;
- 🔑 пароль не храните в тексте скрипта: берите его из защищённого хранилища или переменной окружения.
Во внешнем соединении доступны и русские, и английские имена методов. Имена объектов метаданных (Номенклатура) остаются такими, как в конфигурации. Массовую логику лучше держать в общем модуле конфигурации с флагом «Внешнее соединение», а из скрипта вызывать одну экспортную процедуру. Так код хранится в базе, а не в разрозненных скриптах.
Альтернатива COM без лицензионных и разрядных сложностей на стороне скрипта — HTTP-сервис в конфигурации или стандартный интерфейс OData: там пометку меняют запросом на изменение поля DeletionMark.
Снятие пометки, удаление помеченных и возврат данных
Пометка — это ещё не удаление. Объект остаётся в базе, на него работают все ссылки, его можно вернуть.
Снять пометку программно
Тот же метод с параметром Ложь: Объект.УстановитьПометкуУдаления(Ложь). У справочника при ВключаяПодчиненные = Истина пометка снимется и с вложенных элементов группы.
Документ после снятия пометки сам не проводится. Его нужно провести заново: Объект.Записать(РежимЗаписиДокумента.Проведение). Если документов много, проводите их в хронологическом порядке, иначе остатки на промежуточные даты разойдутся.
Физическое удаление помеченных
Окончательно помеченные объекты удаляет обработка «Удаление помеченных объектов». В типовых конфигурациях на БСП она находится в разделе Администрирование → Обслуживание. Обработка проверяет ссылки: объект, на который ссылаются другие данные, не удаляется, пока ссылку не уберут.
Программно эту же работу делают функции глобального контекста:
Помеченные = НайтиПомеченныеНаУдаление();
Ссылающиеся = НайтиПоСсылкам(Помеченные);
// Ссылающиеся — таблица значений: Ссылка, Данные, Метаданные
НайтиПоСсылкам показывает, кто ссылается на помеченные объекты. Это удобно, чтобы до удаления понять, почему, например, контрагент «не удаляется».
У объекта есть метод Удалить(), который стирает запись сразу, без пометки и без контроля ссылок. После него в документах остаются битые ссылки вида «<Объект не найден>». Для рабочих данных используйте пометку и обработку удаления, а Удалить() оставьте для служебных объектов, на которые точно никто не ссылается.
Если удалили лишнее
После физического удаления объект средствами 1С не восстановить. Единственный путь — резервная копия: разверните её рядом, найдите объект и перенесите его (выгрузкой в XML, обработкой переноса данных или вручную), а не загружайте копию поверх рабочей базы с потерей свежих данных.
Ошибки при программной пометке и как их разобрать
Про обход проверок отдельно. Установка ОбменДанными.Загрузка = Истина перед записью отключает выполнение прикладной логики в обработчиках модуля объекта. Этот режим придуман для обменов данными. Использовать его, чтобы «пробить» пометку через проверки конфигурации, опасно: вы обходите и ту логику, которая поддерживает целостность учёта.