В процессе разработки в системе 1С:Предприятие 8 часто возникает задача автоматизации жизненного цикла документов. Типичный пример: при создании документа Производство на основании документа Заказ, статус последнего должен автоматически измениться с «Принято» на «В производстве». На первый взгляд задача кажется простой, но она таит в себе несколько архитектурных нюансов, связанных с транзакциями, отменой проведения и пользовательским интерфейсом. Рассмотрим подробно, как реализовать этот механизм правильно и надежно.
Прежде чем менять статус, необходимо убедиться, что документ Производство «знает», на основании какого Заказа он создан. Платформа 1С не создает эту связь магическим образом в скрытых полях. Проанализируем, где может храниться эта информация:
Производство реквизит с именем ДокументОснование (или ЗаказОснование) с типом ДокументСсылка.Заказ. Именно наличие этого реквизита позволит нам в любой момент обратиться к родительскому документу.КритерииОтбора (например, СвязанныеДокументы). Это позволяет видеть структуру подчиненности, но для программного изменения статуса нам все равно потребуется ссылка на объект.Производство создается по нескольким Заказам, ссылка на основание должна храниться в строках табличной части.Если при написании кода вы получаете ошибку «Переменная не найдена ДокументОснование», это означает, что такого реквизита в метаданных вашего документа нет. Его необходимо добавить вручную в конфигураторе.
Когда пользователь нажимает кнопку «Создать на основании», вызывается процедура ОбработкаЗаполнения в модуле объекта нового документа. Разберем, что в ней должно происходить. Важно понимать: здесь мы только копируем данные, но еще не меняем статус заказа.
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.Заказ") Тогда
// Заполняем наш реквизит ссылки на основание
ЭтотОбъект.ДокументОснование = ДанныеЗаполнения;
// Переносим данные из заказа в производство (товары, количество и т.д.)
ЭтотОбъект.Организация = ДанныеЗаполнения.Организация;
// ... заполнение табличных частей ...
КонецЕсли;
КонецПроцедуры
Важный нюанс: Почему нельзя менять статус заказа прямо здесь? Если пользователь откроет форму нового документа «Производство», но решит его не записывать и просто закроет окно, статус заказа уже будет изменен. Это приведет к дезинформации в системе. Поэтому логику изменения данных в базе всегда нужно выносить в момент записи или проведения.
Наиболее правильным местом для изменения статуса является процедура ОбработкаПроведения. Проанализируем ситуацию: статус заказа должен измениться только тогда, когда факт производства официально зафиксирован в системе (документ проведен).
Рассмотрим пример кода в модуле объекта документа Производство:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Проверяем, заполнено ли основание
Если ЗначениеЗаполнено(ДокументОснование) Тогда
// Блокируем объект заказа для изменения другими пользователями
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.Заказ");
ЭлементБлокировки.УстановитьЗначение("Ссылка", ДокументОснование);
Блокировка.Заблокировать();
// Получаем объект заказа для редактирования
ОбъектЗаказа = ДокументОснование.ПолучитьОбъект();
// Проверяем текущий статус, чтобы не делать лишних действий
Если ОбъектЗаказа.Статус <> Перечисления.СтатусыЗаказов.ВПроизводстве Тогда
ОбъектЗаказа.Статус = Перечисления.СтатусыЗаказов.ВПроизводстве;
Попытка
ОбъектЗаказа.Записать(РежимЗаписиДокумента.Запись);
Исключение
Инфо = ИнформацияОбОшибке();
Сообщить("Не удалось изменить статус заказа: " + Инфо.Описание);
Отказ = Истина;
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В данном примере мы используем РежимЗаписиДокумента.Запись для заказа, так как изменение статуса обычно не требует движений по регистрам самого заказа. Если в вашей конфигурации изменение статуса триггерит бухгалтерские проводки, следует использовать РежимЗаписиДокумента.Проведение.
Проанализируем ситуацию, когда пользователь ошибся и удалил (отменил проведение) документа Производство. Логично, что Заказ должен вернуться в статус «Принято». Для этого используем процедуру ОбработкаУдаленияПроведения.
Выясним причину, по которой здесь нужна дополнительная проверка: по одному заказу может быть создано несколько документов производства (частичное производство). Мы должны возвращать статус «Принято» только в том случае, если это было последнее проведенное производство по данному заказу.
Процедура ОбработкаУдаленияПроведения(Отказ)
Если ЗначениеЗаполнено(ДокументОснование) Тогда
// Проверяем, есть ли другие ПРОВЕДЕННЫЕ документы производства по этому заказу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Производство.Ссылка
|ИЗ
| Документ.Производство КАК Производство
|ГДЕ
| Производство.ДокументОснование = &Основание
| И Производство.Проведен = ИСТИНА
| И Производство.Ссылка <> &ТекущаяСсылка";
Запрос.УстановитьПараметр("Основание", ДокументОснование);
Запрос.УстановитьПараметр("ТекущаяСсылка", Ссылка);
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
// Других производств нет, возвращаем статус заказа назад
ОбъектЗаказа = ДокументОснование.ПолучитьОбъект();
ОбъектЗаказа.Статус = Перечисления.СтатусыЗаказов.Принято;
ОбъектЗаказа.Записать();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если вы работаете с типовой конфигурацией (БП, УНФ, ERP), изменять код напрямую в модуле документа не рекомендуется, так как это усложнит дальнейшие обновления. Рассмотрим более гибкий вариант — Подписки на события.
Вы можете создать в дереве метаданных общую подписку на событие ОбработкаПроведения для документа Производство. В качестве обработчика укажите процедуру в общем модуле. Это позволит реализовать ту же логику, не затрагивая основной код документа. Это считается «правилом хорошего тона» в современной разработке на платформе 8.3.
Подведем итог нашему разбору. Для реализации корректного изменения статуса при вводе на основании необходимо:
ОбработкаЗаполнения только для визуального наполнения нового документа данными.ОбработкаПроведения).ОбработкаУдаленияПроведения.Такой подход гарантирует целостность базы данных и корректную работу пользователей в многопользовательском режиме, исключая ситуации «повисших» или неверных статусов.