Как в 1С 7.7 выбрать подчиненные документы и избежать ошибки "Объект - документ определенного вида!"?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

В этой статье мы подробно разберем причины возникновения этой ошибки, рассмотрим правильный классический способ работы с подчиненными документами, а также изучим альтернативные и более производительные варианты решения этой задачи.

Выясняем причину: почему возникает ошибка?

Давайте проанализируем архитектуру платформы 1С 7.7. Когда мы пишем следующий код:


Док = СоздатьОбъект("Документ.ЧекФР");
Док.ВыбратьПодчиненныеДокументы(,, ТекущийДокумент());

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

Если мы инициализируем объект конкретного вида (в нашем примере — Документ.ЧекФР), система жестко ограничивает контекст работы этого объекта только структурой и реквизитами данного метаданного. Вызывать методы сквозной выборки по таблицам связей на таком объекте запрещено на уровне ядра платформы.

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

---

Решение 1. Классический метод через универсальный объект "Документ"

Рассмотрим правильный код штатного механизма 1С 7.7. Для этого мы создаем объект общего вида Документ без указания конкретного идентификатора через точку. Затем мы открываем выборку подчиненных документов для нашего ТекущийДокумент() и в цикле отсекаем документы других видов с помощью метода Вид().

Давайте разберем этот процесс по шагам:


// 1. Создаем универсальный нетипизированный объект
Док = СоздатьОбъект("Документ");

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

Важный нюанс производительности: Связи подчиненности в 1С 7.7 отслеживаются автоматически и записываются в системный индекс (таблица _1SCRDOC в SQL-версии или файл 1CRDOC.CDX в DBF-версии). Метод ВыбратьПодчиненныеДокументы() работает напрямую с этим индексом. На очень больших базах данных, если у одного документа-родителя имеется огромное количество подчиненных документов разных видов, этот цикл может работать медленно, так как платформа сначала прочитает все ссылки, а затем будет последовательно анализировать их вид на клиенте.

---

Решение 2. Использование штатного "черного" запроса 1С 7.7

Если вы хотите избежать холостого перебора лишних подчиненных документов и переложить фильтрацию по виду документа на плечи СУБД, отличным решением станет использование стандартного механизма запросов 1С. Этот способ работает быстрее на объемных базах данных.

Для реализации этого метода у вашего подчиненного документа (в нашем примере — ЧекФР) в конфигураторе должен быть создан реквизит (например, ДокОснование) с типом «Документ».

Давайте посмотрим на пример реализации такого запроса:


Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(ВыборкаПодчиненных)
|Период с '01.01.1980' по '31.12.2050'; // Задаем максимально широкий диапазон времени
|Обрабатывать Документы;
|Чек = Документ.ЧекФР.ТекущийДокумент;
|Основание = Документ.ЧекФР.ДокОснование; // Наш реквизит связи
|Группировка Чек;
|Условие(Основание = ТекущийДокумент);
|"//}}ЗАПРОС
;

// Выполняем запрос к базе данных
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    // Обходим результат запроса
    Пока Запрос.Группировка(1) = 1 Цикл
        НайденныйЧек = Запрос.Чек;
        Сообщить("Найден чек через запрос: " + НайденныйЧек);
    КонецЦикла;
КонецЕсли;

Этот подход гарантирует, что база данных вернет только строго подходящие документы ЧекФР, у которых в реквизите ДокОснование записана наша расходная накладная, минуя чтение всей таблицы связей.

---

Решение 3. Использование Граф отбора и метода "ВыбратьПоЗначению"

Еще один штатный способ быстрого поиска — использование системного механизма Граф отбора. Метод ВыбратьПоЗначению() также выдавал ошибку «Объект - документ определенного вида!», потому что вызывался на типизированном объекте. Исправим это.

Для реализации этого подхода выполните следующие шаги:

  1. Откройте конфигурацию в режиме Конфигуратор.
  2. Найдите ветку «Графы отбора» и создайте новую графу, назвав её, например, Основание.
  3. В свойствах созданной графы отбора отметьте галочкой реквизит ДокОснование документа ЧекФР.
  4. Сохраните конфигурацию.

Теперь мы можем использовать этот индекс для быстрого поиска документов через универсальный объект:


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

Пока Док.ПолучитьДокумент() = 1 Цикл
    // Дополнительно проверяем вид, если в эту же графу включены другие документы
    Если Док.Вид() = "ЧекФР" Тогда
        Сообщить("Найден документ через графу отбора: " + Док.ТекущийДокумент());
    КонецЕсли;
КонецЦикла;

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

---

Решение 4. Прямой SQL-запрос (для продвинутых разработчиков)

Если ваша информационная база работает под управлением Microsoft SQL Server, а в системе установлена популярная внешняя компонента 1С++ (1cpp.dll), вы можете получить список документов мгновенно, отправив прямой запрос к СУБД в обход штатных механизмов.

Проанализируем пример кода с использованием класса ODBCRecordset:


// Создаем объект для работы с прямыми SQL-запросами
ЗапросSQL = СоздатьОбъект("ODBCRecordset");

ТекстЗапроса = "
|SELECT 
|   Журнал.IDDOC as [Док $Документ.ЧекФР]
|FROM 
|   _1SCRDOC as Связи (NOLOCK)
|INNER JOIN 
|   _1SJOURN as Журнал (NOLOCK) ON Связи.ChildID = Журнал.IDDOC
|WHERE 
|   Связи.ParentVal = :ВыбДокумент
|   AND Журнал.IDDOCDEF = :IDDefЧек
|";

// Передаем параметры в запрос
ЗапросSQL.УстановитьТекстовыйПараметр("ВыбДокумент", ТекущийДокумент());
// Получаем внутренний идентификатор вида документа "ЧекФР"
ЗапросSQL.УстановитьТекстовыйПараметр("IDDefЧек", "ЧекФР"); 

// Выполняем SQL-инструкцию и получаем результат в виде Таблицы Значений
ТаблицаЧеков = ЗапросSQL.ВыполнитьИнструкцию(ТекстЗапроса);

// Теперь у нас есть готовая таблица с подчиненными чеками
ТаблицаЧеков.ВыбратьСтроки();
Пока ТаблицаЧеков.ПолучитьСтроку() = 1 Цикл
    Сообщить("Найден подчиненный документ через SQL: " + ТаблицаЧеков.Док);
КонецЦикла;

Этот метод считается наиболее профессиональным для оптимизации высоконагруженных систем 1С 7.7, так как он минимизирует транзакционную нагрузку на SQL Server и возвращает результат за миллисекунды.

Резюме

Мы подробно разобрали проблему и нашли несколько путей её решения. Главный вывод: никогда не вызывайте методы сквозного поиска на типизированных объектах документов. Используйте универсальный объект СоздатьОбъект("Документ") для простых задач, стандартные запросы и графы отбора для оптимизации штатных механизмов, или прямые SQL-запросы для достижения максимальной производительности вашей системы.

← На главную