Как автоматически изменить статус документа-основания при вводе на его основании другого документа?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

Где хранить связь между документами?

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

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

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

Шаг 1. Первоначальное заполнение в ОбработкаЗаполнения

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


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    
    Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.Заказ") Тогда
        // Заполняем наш реквизит ссылки на основание
        ЭтотОбъект.ДокументОснование = ДанныеЗаполнения;
        
        // Переносим данные из заказа в производство (товары, количество и т.д.)
        ЭтотОбъект.Организация = ДанныеЗаполнения.Организация;
        // ... заполнение табличных частей ...
    КонецЕсли;
    
КонецПроцедуры

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

Шаг 2. Изменение статуса при проведении

Наиболее правильным местом для изменения статуса является процедура ОбработкаПроведения. Проанализируем ситуацию: статус заказа должен измениться только тогда, когда факт производства официально зафиксирован в системе (документ проведен).

Рассмотрим пример кода в модуле объекта документа Производство:


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

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

В данном примере мы используем РежимЗаписиДокумента.Запись для заказа, так как изменение статуса обычно не требует движений по регистрам самого заказа. Если в вашей конфигурации изменение статуса триггерит бухгалтерские проводки, следует использовать РежимЗаписиДокумента.Проведение.

Шаг 3. Обработка отмены проведения

Проанализируем ситуацию, когда пользователь ошибся и удалил (отменил проведение) документа Производство. Логично, что Заказ должен вернуться в статус «Принято». Для этого используем процедуру ОбработкаУдаленияПроведения.

Выясним причину, по которой здесь нужна дополнительная проверка: по одному заказу может быть создано несколько документов производства (частичное производство). Мы должны возвращать статус «Принято» только в том случае, если это было последнее проведенное производство по данному заказу.


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

Использование Подписок на события — профессиональный подход

Если вы работаете с типовой конфигурацией (БП, УНФ, ERP), изменять код напрямую в модуле документа не рекомендуется, так как это усложнит дальнейшие обновления. Рассмотрим более гибкий вариант — Подписки на события.

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

Резюме и рекомендации

Подведем итог нашему разбору. Для реализации корректного изменения статуса при вводе на основании необходимо:

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

← На главную