Как найти родительский документ (Заказ покупателя) при создании цепочки документов в 1С

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

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

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

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

Критически важно понимать направление связи. Критерий отбора в типовых конфигурациях (таких как УПП, УТ 10.3 или более современные версии) настроен на поиск подчиненных объектов. Это означает, что если мы передаем в критерий «Заказ поставщику», система ищет все документы, в которых этот заказ указан как «ДокументОснование». Родительский документ («Заказ покупателя») сам содержит в себе ссылку на «Заказ поставщику» (или наоборот, в зависимости от логики конфигурации), но он стоит выше в иерархии.

Проанализируем структуру: если «Заказ поставщику» был создан на основании «Заказа покупателя», то ссылка на «Заказ покупателя» хранится внутри реквизитов поставщика. Критерий отбора же ищет «вниз» — тех, кто ссылается на текущий объект, а не тех, на кого ссылается он сам.

Способ 1: Получение родителя через реквизиты документа

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

Посмотрим на пример кода, который можно разместить в модуле объекта приходной накладной в процедуре ОбработкаЗаполнения. На этом этапе накладная еще не записана (не имеет ссылки), но у нас есть доступ к данным заполнения.


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

Способ 2: Рекурсивный поиск по цепочке оснований

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

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


Функция НайтиКорневойЗаказПокупателя(ТекущийДокумент)
    
    Основание = ТекущийДокумент;
    
    // Цикл будет работать, пока не найдем нужный тип или пока не закончатся основания
    Пока ЗначениеЗаполнено(Основание) Цикл
        
        Если ТипЗнч(Основание) = Тип("ДокументСсылка.ЗаказПокупателя") Тогда
            Возврат Основание;
        КонецЕсли;
        
        // Пытаемся получить следующее основание. 
        // В разных документах реквизит может называться по-разному.
        Попытка
            Основание = Основание.ДокументОснование;
        Исключение
            // Если реквизита нет, прерываем поиск
            Основание = Неопределено;
        КонецПопытки;
        
    КонецЦикла;
    
    Возврат Неопределено;
    
КонецФункции

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

Как справедливо отмечено в сообщении №8 обсуждения, в современных конфигурациях (например, УТ 11, ERP, КА 2) прямая связь через реквизит «ДокументОснование» может отсутствовать. В таких системах используется механизм обеспечения потребностей, и связь между заказом поставщику и заказом покупателя фиксируется в регистрах.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |    Регистр.ЗаказПокупателя КАК Заказ
    |ИЗ
    |    РегистрНакопления.ЗаказыНаПоступление КАК Регистр
    |ГДЕ
    |    Регистр.ЗаказПоставщику = &ЗаказПоставщику";

Запрос.УстановитьПараметр("ЗаказПоставщику", Основание);
РезультатЗапроса = Запрос.Выполнить();
// Далее анализируем полученные заказы покупателей

Нюансы прав доступа (RLS) и привилегированный режим

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

Выясним причину: механизм RLS (Record Level Security) ограничивает выборку данных на уровне СУБД. Чтобы проверка сработала корректно вне зависимости от прав пользователя, рекомендуется выполнять запрос или обращение к данным в привилегированном режиме.

Рассмотрим правильное оформление кода:


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

Подведем итоги

Для решения задачи проверки «дедушки» (Заказа покупателя) при создании «внука» (Приходной накладной) следует придерживаться следующих правил:

  1. Не использовать КритерииОтбора для поиска вверх по иерархии — они предназначены для поиска подчиненных объектов (вниз).
  2. Использовать реквизиты документа (Основание, Сделка) для быстрого перехода к родителю.
  3. Применять циклы, если глубина вложенности документов может быть произвольной.
  4. Обращаться к регистрам в комплексных конфигурациях (ERP/УТ11), где связь между заказами реализуется через механизмы распределения запасов.
  5. Помнить о процедуре ОбработкаЗаполнения — это лучшее место для предотвращения создания документа, если условия не соблюдены. Установка Отказ = Истина в этой процедуре не даст пользователю даже открыть форму новой накладной.

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

← На главную