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

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

При разработке цепочек связанных документов в платформе 1С разработчики часто сталкиваются с задачей автоматической смены состояний. Типичный сценарий: на основании документа Заказ покупателя создается подчиненный Счет на оплату. При создании счета статус заказа меняется на «Счет», а при ошибочном создании и последующей установке пометки на удаление счета статус заказа должен возвращаться в значение «Запрос». Однако на практике программисты нередко обнаруживают, что при пометке счета на удаление статус заказа-основания остается прежним. Разберем по шагам, почему это происходит и как правильно организовать логику работы с подчиненными объектами.

Анализ проблемы и последовательности событий записи

Проанализируем типовую ситуацию. Разработчик помещает код возврата статуса в событие ПередЗаписью модуля объекта подчиненного документа (счета), отслеживая изменение пометки удаления:


Объект = Основание.ПолучитьОбъект();
Объект.Статус = Перечисление.Статус.Запрос;
Объект.Записать(РежимЗаписиДокумента.Запись);

Казалось бы, код отрабатывает без ошибок, транзакция фиксируется, но после открытия заказа значение реквизита Статус по-прежнему равно «Счет». Выясним причину такого поведения платформы 1С.

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

  1. Вызывается процедура ПередЗаписью модуля объекта.
  2. Начинается транзакция записи документа.
  3. Вызывается процедура ПриЗаписи модуля объекта.
  4. Транзакция фиксируется в базе данных.

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

Кроме того, обратим внимание на синтаксис метода Записать() у объекта документа. Метод принимает параметры в строгом порядке: Записать(<РежимЗаписи>, <РежимПроведения>). Попытка передать первым параметром режим проведения или использование некорректных значений системных перечислений приводит к исключительным ситуациям или непредвиденному поведению при проведении.

Способ 1. Корректировка условий в обработчиках модуля объекта

Рассмотрим прямое исправление логики в модуле объекта счета. Чтобы исключить повторную перезапись статуса в ПриЗаписи, необходимо разделить сценарии обычной записи и пометки на удаление.

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


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

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

Важный момент: если форма документа-основания в этот момент открыта на рабочем столе пользователя, программная модификация через ПолучитьОбъект() изменит данные в базе, но не обновит уже открытую форму. Для актуализации открытых форм в интерфейсе следует использовать метод ОповеститьОбИзменении(Ссылка).

Способ 2. Проверка наличия других актуальных подчиненных документов

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


Процедура ОбновитьСтатусЗаказа(СсылкаНаЗаказ)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	СчетНаОплату.Ссылка КАК Ссылка
        |ИЗ
        |	Документ.СчетНаОплату КАК СчетНаОплату
        |ГДЕ
        |	СчетНаОплату.ДокументОснование = &Заказ
        |	И НЕ СчетНаОплату.ПометкаУдаления
        |	И СчетНаОплату.Ссылка <> &ТекущийСчет";
        
    Запрос.УстановитьПараметр("Заказ", СсылкаНаЗаказ);
    Запрос.УстановитьПараметр("ТекущийСчет", Ссылка);
    
    Результат = Запрос.Выполнить();
    
    ОбъектЗаказ = СсылкаНаЗаказ.ПолучитьОбъект();
    
    Если Результат.Пустой() Тогда
        ОбъектЗаказ.Статус = Перечисления.СтатусыЗаказов.Запрос;
    Иначе
        ОбъектЗаказ.Статус = Перечисления.СтатусыЗаказов.Счет;
    КонецЕсли;
    
    ОбъектЗаказ.Записать(РежимЗаписиДокумента.Запись);
    
КонецПроцедуры

Этот метод исключает логические ошибки при работе с множественными связями и гарантирует целостность состояний документов в системе.

Способ 3. Использование подписок на события или регистра сведений

Рассмотрим более устойчивые архитектурные альтернативы:

  1. Подписки на события: перенос логики обновления состояний в подписку на событие ПриЗаписи позволяет отвязать модуль счета от логики заказа и централизованно управлять статусами связок.
  2. Использование регистра сведений: изменение реквизита непосредственно в документе-основании блокирует его от редактирования другими пользователями и провоцирует взаимные блокировки (deadlocks). Хранение расчетного статуса заказа в независимом регистре сведений позволяет обновлять статус без модификации самого документа Заказ покупателя.
← На главную