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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке цепочек документов в 1С часто возникает задача: при проведении подчиненного документа (например, Договор) зафиксировать в документе-основании (например, Заявление), что действие выполнено — взвести признак ДоговорЗаключен = Истина и сохранить ссылку на созданный договор. При попытке реализовать такую логику «в лоб» разработчики сталкиваются как с техническими ошибками времени выполнения, так и с фундаментальными архитектурными проблемами платформы.

Разберем, почему попытка вызвать Основание.ПолучитьОбъект() приводит к ошибке, почему прямое изменение сторонних документов в транзакции проведения считается антипаттерном и как решить эту задачу правильно с точки зрения стандартов разработки 1С.

Причины ошибки «Значение не является значением объектного типа»

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


// В модуле документа (например, в ОбработкеПроведения)
ДокОснования = Основание.ПолучитьОбъект();
ДокОснования.ДоговорЗаключен = Истина;
ДокОснования.Договор = ЭтотОбъект.Ссылка;
ДокОснования.Записать();

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

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

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

Даже если корректно получить объект основания через ПолучитьОбъект(), изменение стороннего документа в процедуре ОбработкаПроведения порождает серьезные проблемы в рабочей базе:

  1. Взаимные блокировки СУБД (Deadlocks): В момент проведения подчиненный документ открывает неявную транзакцию. Вызов метода Записать() у другого документа устанавливает исключительные транзакционные блокировки сразу на две таблицы. При одновременной работе пользователей с основанием и подчиненным документом транзакции заблокируют друг друга.
  2. Проблема отмены проведения и перепроведения: Если пользователь отменит проведение договора или пометит его на удаление, в заявлении останется признак того, что договор заключен. Придется вручную дублировать зеркальную логику очистки в процедуре ОбработкаУдаленияПроведения, обработчиках пометки на удаление и непосредственного удаления. Это приводит к постоянному рассинхрону данных.
  3. Конфликты прав доступа: Пользователь, создающий договор, может обладать правами только на чтение заявлений. При попытке программной записи основания он получит ошибку нарушения прав доступа.
  4. Конфликт версий данных: Если документ Заявление открыт на клиенте у другого оператора, попытка перезаписать его на сервере приведет к ошибке несоответствия версий при попытке оператора сохранить свою форму.

Правильное решение: Регистр сведений с подчинением регистратору

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

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

  1. Создадим новый регистр сведений, например, СтатусыЗаявлений.
  2. Установим режим записи: Подчинение регистратору.
  3. В состав регистраторов включим подчиненный документ — Договор.
  4. Добавим Измерение: Заявление (тип: ДокументСсылка.Заявление).
  5. Добавим Ресурсы: ДоговорЗаключен (тип: Булево), Договор (тип: ДокументСсылка.Договор).
  6. В модуле документа Договор в процедуре ОбработкаПроведения сформируем движения:
    
    Процедура ОбработкаПроведения(Отказ, Режим)
        
        Если Не ЗначениеЗаполнено(Основание) Тогда
            Возврат;
        КонецЕсли;
        
        Движение = Движения.СтатусыЗаявлений.Добавить();
        Движение.Период = Дата;
        Движение.Заявление = Основание;
        Движение.ДоговорЗаключен = Истина;
        Движение.Договор = Ссылка;
        
    КонецПроцедуры
    

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

Альтернативное решение: Динамический запрос без хранения статуса

Если в документе Договор уже есть заполненный реквизит Основание, хранить признак заключения договора отдельно вообще не обязательно. Мы можем получать актуальное состояние заявления в любой момент времени с помощью запроса:


ВЫБРАТЬ
    Договор.Ссылка КАК Договор,
    ИСТИНА КАК ДоговорЗаключен
ИЗ
    Документ.Договор КАК Договор
ГДЕ
    Договор.Основание = &Заявление
    И Договор.Проведен = ИСТИНА

Этот запрос можно вызывать в форме заявления при открытии, либо использовать стандартный механизм критериев отбора (структура подчиненности / связанные документы).

Что делать, если изменить реквизит основания обязательно

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

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

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

← На главную