Как программно снять пометку удаления и провести документ в 1С:Предприятие 8

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

В этой статье мы подробно разберем, почему может не срабатывать снятие пометки удаления, как правильно организовать запись объекта и какие типичные ошибки допускают разработчики при работе с метаданными 1С.

Анализ проблемы: почему «не снимается» пометка удаления?

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


мТовары = Данные.Скопировать(Отбор); // Получаем данные для ТЧ
Документ_2 = ДокументСсылка.ПолучитьОбъект(); // Получаем объект для редактирования

Если мТовары.Количество() = 0 Тогда
    Документ_2.УстановитьПометкуУдаления(Истина);
Иначе
    // ОШИБКА: Использование неверной переменной
    Если ПеремещениеОбъект.ПометкаУдаления Тогда         
         Документ_2.УстановитьПометкуУдаления(Ложь);
    КонецЕсли;
    
    Документ_2.Товары.Очистить();
    Документ_2.Товары.Загрузить(мТовары);
    Документ_2.Записать(РежимЗаписиДокумента.Проведение);
КонецЕсли;

В данном примере мы видим классическую опечатку: проверка условия идет по переменной ПеремещениеОбъект, в то время как действия совершаются над Документ_2. Если ПеремещениеОбъект не определен или его состояние не соответствует логике, блок снятия пометки просто не выполнится. Однако даже после исправления этой ошибки код может работать нестабильно. Выясним, почему.

Разбор методов: УстановитьПометкуУдаления vs ПометкаУдаления = Ложь

Метод УстановитьПометкуУдаления(Значение) — это не простое присваивание значения флагу. Вызов этого метода инициирует немедленную запись объекта в базу данных. Посмотрим на последовательность событий в коде автора:

  1. Вызывается УстановитьПометкуУдаления(Ложь) — первая транзакция записи в БД.
  2. Выполняется Загрузить(мТовары) — изменение данных в оперативной памяти.
  3. Вызывается Записать(РежимЗаписиДокумента.Проведение) — вторая транзакция записи и проведение.

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


Документ_2.ПометкаУдаления = Ложь; // Просто меняем свойство объекта
Документ_2.Товары.Загрузить(мТовары);
// Одна запись решит обе задачи: снимет пометку и проведет
Документ_2.Записать(РежимЗаписиДокумента.Проведение); 

Связь между количеством товара и пометкой удаления

Часто разработчики замечают «колдунство»: если количество товара в документах совпадает, всё работает, а если нет — пометка не снимается. На самом деле никакой магии нет. Разберем цепочку событий:

Когда вы вызываете Записать(РежимЗаписиДокумента.Проведение), система выполняет проверку остатков, заполнение обязательных реквизитов и логику в процедуре ОбработкаПроведения. Если при новом количестве товара документ не может быть проведен (например, из-за нехватки товара на складе), метод Записать() вызовет исключение или вернет ошибку. В результате вся транзакция откатывается, включая и изменение флага ПометкаУдаления. Визуально пользователю кажется, что код «не сработал», хотя на самом деле он просто не смог зафиксировать изменения из-за ошибки проведения.

Оптимизация работы с табличными частями

Посмотрим на работу с табличной частью. Часто можно встретить конструкцию Товары.Очистить() перед Товары.Загрузить(). Это лишнее действие. Метод Загрузить() полностью замещает содержимое табличной части данными из таблицы значений, автоматически очищая старые строки. Единственное условие — имена колонок в таблице-источнике должны совпадать с именами колонок в табличной части документа.

Рекомендации по безопасному программному проведению

Чтобы ваш код был надежным и понятным, рекомендуем придерживаться следующего алгоритма:

  1. Всегда используйте отладчик. Проверьте значение мТовары.Количество() и убедитесь, что вы попадаете в нужную ветку условия Если ... Тогда.
  2. Проверяйте результат записи. Если вы используете программную запись, желательно оборачивать её в блок Попытка...Исключение, чтобы понимать причину отказа в проведении.
  3. Избегайте лишних транзакций. Устанавливайте свойство ПометкаУдаления напрямую, если за этим следует вызов метода Записать().
  4. Контролируйте блокировки. Помните, что ПолучитьОбъект() может не сработать, если документ заблокирован другим пользователем.

Рассмотрим финальный вариант правильного кода, который минимизирует ошибки:


ДокументОбъект = ДокументСсылка.ПолучитьОбъект();

Если мТовары.Количество() > 0 Тогда
    // Снимаем пометку в памяти
    ДокументОбъект.ПометкаУдаления = Ложь;
    // Загружаем актуальные данные
    ДокументОбъект.Товары.Загрузить(мТовары);
    
    Попытка
        // Проводим документ
        ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
        Сообщить("Документ " + ДокументОбъект + " успешно обновлен и проведен.");
    Исключение
        Сообщить("Не удалось провести документ: " + ОписаниеОшибки());
    КонецПопытки;
Иначе
    // Если товаров нет, помечаем на удаление
    Если Не ДокументОбъект.ПометкаУдаления Тогда
        ДокументОбъект.УстановитьПометкуУдаления(Истина);
    КонецЕсли;
КонецЕсли;

Использование такого подхода гарантирует, что все изменения будут записаны за один раз, а в случае возникновения проблем (например, ошибки контроля остатков) вы получите внятное сообщение в окне сообщений, а не «загадочное поведение» системы.

← На главную