Вложенные таблицы в списке источников

Язык запросов

Синтаксис и применение Вложенные таблицы в списке источников в платформе 1С:Предприятие. Описание и примеры кода.

Полное описание, параметры и ограничения

Вложенные таблицы в списке источников

В списке источников могут фигурировать и вложенные таблицы – табличные части справочников и документов.

Пример:

// В отчет необходимо вывести спецификацию товарных накладных –
// показать сам документ, номенклатуру и количество.
// В списке источников указана вложенная таблица "Состав" –
// табличная часть накладной.
// Выборка ограничена восемью записями, чтобы не перегружать пример.

ВЫБРАТЬ ПЕРВЫЕ 8
Ссылка, Номенклатура, Количество

ИЗ
Документ.РасходнаяНакладная.Состав

Результат запроса:

Ссылка

Номенклатура

Количество

Расходная накладная 00007 от 25.02.2002 21:03:21

Джинсы женские

4

Расходная накладная 00006 от 25.02.2002 0:00:00

Джинсы женские

5

Расходная накладная 00006 от 25.02.2002 0:00:00

Рубашка "Ковбойка"

5

Расходная накладная 00005 от 01.03.2002 20:58:28

Джинсы женские

1

Расходная накладная 00004 от 01.03.2002 20:50:40

Джинсы женские

1

Расходная накладная 00003 от 23.02.2002 0:00:00

Мойдодыр "Аквариум"

5

Расходная накладная 00003 от 23.02.2002 0:00:00

Раковина "Лилия"

8

Расходная накладная 00003 от 23.02.2002 0:00:00

Смеситель "Ультра"

10

Обратите внимание, что при указании вложенной таблицы в списке источников допускается обращение, как к полям самой вложенной таблицы, так и к полям таблицы верхнего уровня (той, которая содержит вложенную таблицу) через поле "Ссылка". В данном случае происходит обращение к полю "Ссылка.НаименованиеПоля" самого документа.

Практические примеры

Табчасть в ИЗ: получить плоскую ведомость и разыменовать только свои реальные ссылки

Пять строк собственных документов становятся верхними строками запроса. Ссылка.Номер обращается к своему документу, Категория.Наименование — к своему записанному листу; пустой документ не добавляет фиктивную строку.

Где и как запускать

Полный серверный общий модуль УчебныйЯзык только новой собственной пустой конфигурации УчебныеИерархииИСтроки85 на 8.5.1.1343. BSL/запросы здесь не запускались. Предоставленный код будущего ручного опыта временно записывает только свои учебные данные в своей транзакции и обязательно отменяет их.

Подготовка

  1. В отдельной учебной базе 8.5.1.1343 создайте общий модуль УчебныйЯзык: Сервер=Истина, Глобальный=Ложь, ВызовСервера=Ложь, Привилегированный=Ложь. Клиентские и внешнее соединение отключены; ПовторноеИспользованиеВозвращаемыхЗначений=НеИспользовать.

  2. Вставьте весь приведённый текст в этот общий модуль. ВыполнитьПример — экспортная точка входа; остальные процедуры и функции располагаются рядом, а не внутри неё.

  3. В своей внешней учебной обработке создайте управляемую форму, команду Выполнить и кнопку. Клиентский обработчик Выполнить(Команда) вызывает свою процедуру ВыполнитьНаСервере(); она имеет директиву &НаСервереБезКонтекста и одну строку УчебныйЯзык.ВыполнитьПример();. Это серверный вызов общего модуля, а не разрешение клиенту прямо обращаться к серверной библиотеке.

  4. HTML только показывает BSL. Код в 1С не запускался; перечисленные результаты и отрицательные опыты проверяйте самостоятельно в собственном стенде.

  5. Все данные полностью заданы этим модулем. В отличие от опытов только в памяти, этот BSL будущего ручного запуска временно записывает семь собственных категорий и три документа с пятью строками в новой учебной базе, затем обязательно откатывает всё. Чужая база, файлы, сеть и печать не используются; в рабочие модули код не вставляйте.

  6. Новый стенд запускайте единственным сеансом без фоновых/регламентных заданий и иных пользователей. Учебному пользователю нужны права читать, добавлять и изменять только свои подготовленные категории/документы и вызывать свой серверный модуль; недостаток прав — неожиданная ошибка опыта, не успешный результат и не разрешение менять права рабочей базы.

  7. Создайте НОВУЮ отдельную пустую учебную базу, не копию рабочего приложения, на точной платформе 8.5.1.1343. Конфигурация: Имя=УчебныеИерархииИСтроки85; режим совместимости отключён. Не подключайте расширения; иных данных, обработчиков, подписок, регистров, планов, перечислений, бизнес-процессов и задач нет.

  8. Справочники → создать УчебныеКатегории85. Иерархический=Да; Вид иерархии=Иерархия групп и элементов; Ограничивать количество уровней=Нет; Владельцы пусты. Тип кода=Строка; Длина кода=3; Автонумерация=Нет; Длина наименования=20. Собственные реквизиты, табличные части и предопределённые элементы отсутствуют. Стандартные поля платформы остаются.

  9. Документы → создать УчебнаяСпецификация85. Проведение=Запретить; Движения пусты; Тип номера=Строка; Длина номера=8; Автонумерация=Нет. Собственных реквизитов документа нет. Создать единственную Табличную часть Позиции: реквизит Категория с единственным типом СправочникСсылка.УчебныеКатегории85 и реквизит Количество с единственным неотрицательным Число(4,0). Проверка заполнения собственных реквизитов=Не проверять; наш ввод проверяется полным модулем до добавления.

  10. Все модули объектов/менеджеров справочника и документа, формы прикладных объектов и другие обработчики оставьте пустыми. Guard проверяет перечисленные свойства схемы и типы, но не тексты модулей/форм, расширения или предопределённые элементы; отсутствие такого кода — обязательное отдельное условие подготовки.

  11. Весь основной код этого урока целиком заменяет модуль своего УчебныйЯзык. Это полный самостоятельный серверный общий модуль с помощниками на уровне модуля. Для следующего урока замените его целиком, не склеивайте три одноимённых экспортных точки.

  12. Полный отдельный запуск: новая собственная внешняя обработка УчебныйПускПлоскихСтрок85 → управляемая Форма → команда Выполнить с одноимённым действием и кнопкой. Остальные модули обработки пусты. Модуль формы:

    &НаКлиенте
    Процедура Выполнить(Команда)
    ВыполнитьНаСервере();
    Сообщить("Собственный учебный опыт проверен, все его записи отменены");
    КонецПроцедуры

    &НаСервереБезКонтекста
    Процедура ВыполнитьНаСервере()
    УчебныйЯзык.ВыполнитьПример();
    КонецПроцедуры

  13. Читатель вручную обновляет только свою новую учебную конфигурацию и запускает опыт. Здесь никакая конфигурация/база не менялась и 1С не запускалась. До начала код проверит отсутствие активной транзакции и всех записей своих трёх источников. При любой неожиданной ошибке собственная транзакция откатывается; проверка отсутствия записей идёт и после успеха, и после ошибки.

  14. Полный fixture задан в коде: 001/R→пустой родитель;002/A→R;003/B→R;004/AX→A;005/AY→A;006/BX→B;007/ZZ→пустой родитель. R,A,B — группы. Документы OWN00001/OWN00002/OWN00003 имеют дату 06.10.2026 12:00:00 и соответственно три/две/ноль строк. Никакие учебные записи заранее руками создавать не нужно.

Процедура ВыполнитьПример() Экспорт
    ПроверитьСвоюСхемуИерархииИСтрок();
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Не допускается чужая или вложенная активная транзакция";
    КонецЕсли;
    ПроверитьСвойПустойСтенд();
    НачатьТранзакцию();
    Попытка
        Данные = СоздатьСвоиЗаписиДляЗапросов();
        ПроверитьСвоиЗаписанныеИсточники(Данные);
        Запрос = Новый Запрос;
        Запрос.Текст = "ВЫБРАТЬ Строки.Ссылка.Номер КАК Номер, Строки.НомерСтроки КАК НомерСтроки,
            |    Строки.Категория.Наименование КАК Категория, Строки.Количество КАК Количество
            |ИЗ Документ.УчебнаяСпецификация85.Позиции КАК Строки
            |ГДЕ Строки.Ссылка В (&СвоиДокументы)
            |УПОРЯДОЧИТЬ ПО Строки.Ссылка.Номер ВОЗР, Строки.НомерСтроки ВОЗР";
        Запрос.УстановитьПараметр("СвоиДокументы", Данные.Документы);
        Таблица = Запрос.Выполнить().Выгрузить();
        Ожидания = Новый Массив;
        Ожидания.Добавить(Новый Структура("Номер,НомерСтроки,Категория,Количество", "OWN00001", 1, "AY", 2));
        Ожидания.Добавить(Новый Структура("Номер,НомерСтроки,Категория,Количество", "OWN00001", 2, "AX", 5));
        Ожидания.Добавить(Новый Структура("Номер,НомерСтроки,Категория,Количество", "OWN00001", 3, "AY", 5));
        Ожидания.Добавить(Новый Структура("Номер,НомерСтроки,Категория,Количество", "OWN00002", 1, "BX", 4));
        Ожидания.Добавить(Новый Структура("Номер,НомерСтроки,Категория,Количество", "OWN00002", 2, "AX", 1));
        Поля = Новый Массив;
        Поля.Добавить("Номер");
        Поля.Добавить("НомерСтроки");
        Поля.Добавить("Категория");
        Поля.Добавить("Количество");
        ПроверитьСвоюПолнуюТаблицу(Таблица, Ожидания, Поля);
        СуммаПервого = 0;
        СуммаВторого = 0;
        Для Каждого СтрокаПозиции Из Таблица Цикл
            Если СтрокаПозиции.Номер = "OWN00001" Тогда
                СуммаПервого = СуммаПервого + СтрокаПозиции.Количество;
            ИначеЕсли СтрокаПозиции.Номер = "OWN00002" Тогда
                СуммаВторого = СуммаВторого + СтрокаПозиции.Количество;
            Иначе
                ВызватьИсключение "В плоском результате не должно быть иных номеров или строки пустого документа";
            КонецЕсли;
        КонецЦикла;
        Если СуммаПервого <> 12 Или СуммаВторого <> 5 Тогда
            ВызватьИсключение "Не совпали собственные суммы плоских строк 12 и 5";
        КонецЕсли;
        Сообщить("Проверены все пять плоских строк, два реальных пути разыменования и суммы12/5");
        ПроверитьСвоиЗаписанныеИсточники(Данные);
    Исключение
        Если ТранзакцияАктивна() Тогда
            ОтменитьТранзакцию();
        КонецЕсли;
        ПроверитьСвойПустойСтенд();
        ВызватьИсключение;
    КонецПопытки;
    ОтменитьТранзакцию();
    ПроверитьСвойПустойСтенд();
    Сообщить("Все семь категорий, три документа и пять строк отменены в собственном новом стенде");
КонецПроцедуры

Процедура ПроверитьСвоюСхемуИерархииИСтрок()
    Если Метаданные.Справочники.Количество() <> 1 Или Метаданные.Документы.Количество() <> 1 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только новый собственный минимальный стенд";
    КонецЕсли;
    МетаКатегория = Метаданные.Справочники.Найти("УчебныеКатегории85");
    МетаДокумент = Метаданные.Документы.Найти("УчебнаяСпецификация85");
    Если МетаКатегория = Неопределено Или МетаДокумент = Неопределено Тогда
        ВызватьИсключение "Не найдены свои справочник и документ";
    КонецЕсли;
    Свойства = Метаданные.СвойстваОбъектов;
    Если Не МетаКатегория.Иерархический Или МетаКатегория.ВидИерархии <> Свойства.ВидИерархии.ИерархияГруппИЭлементов
        Или МетаКатегория.ОграничиватьКоличествоУровней Или МетаКатегория.Владельцы.Количество() <> 0
        Или МетаКатегория.ТипКода <> Свойства.ТипКодаСправочника.Строка Или МетаКатегория.ДлинаКода <> 3
        Или МетаКатегория.ДлинаНаименования <> 20 Или МетаКатегория.Автонумерация
        Или МетаКатегория.Реквизиты.Количество() <> 0 Или МетаКатегория.ТабличныеЧасти.Количество() <> 0 Тогда
        ВызватьИсключение "Не совпала точная схема собственного иерархического справочника";
    КонецЕсли;
    Если МетаДокумент.Проведение <> Свойства.Проведение.Запретить
        Или МетаДокумент.ТипНомера <> Свойства.ТипНомераДокумента.Строка Или МетаДокумент.ДлинаНомера <> 8
        Или МетаДокумент.Автонумерация Или МетаДокумент.Движения.Количество() <> 0
        Или МетаДокумент.Реквизиты.Количество() <> 0 Или МетаДокумент.ТабличныеЧасти.Количество() <> 1 Тогда
        ВызватьИсключение "Не совпала точная схема собственного документа";
    КонецЕсли;
    МетаПозиции = МетаДокумент.ТабличныеЧасти.Найти("Позиции");
    Если МетаПозиции = Неопределено Тогда
        ВызватьИсключение "Не найдена своя табличная часть Позиции";
    КонецЕсли;
    Если МетаПозиции.Реквизиты.Количество() <> 2 Тогда
        ВызватьИсключение "В собственной табличной части нужны ровно два реквизита";
    КонецЕсли;
    МетаСсылка = МетаПозиции.Реквизиты.Найти("Категория");
    МетаКоличество = МетаПозиции.Реквизиты.Найти("Количество");
    Если МетаСсылка = Неопределено Или МетаКоличество = Неопределено Тогда
        ВызватьИсключение "Не найдены собственные реквизиты Категория и Количество";
    КонецЕсли;
    Если МетаСсылка.ПроверкаЗаполнения <> ПроверкаЗаполнения.НеПроверять
        Или МетаКоличество.ПроверкаЗаполнения <> ПроверкаЗаполнения.НеПроверять Тогда
        ВызватьИсключение "Автоматическая проверка двух собственных реквизитов должна быть выключена";
    КонецЕсли;
    Если МетаСсылка.Тип.Типы().Количество() <> 1
        Или Не МетаСсылка.Тип.СодержитТип(Тип("СправочникСсылка.УчебныеКатегории85")) Тогда
        ВызватьИсключение "Категория должна иметь единственный собственный ссылочный тип";
    КонецЕсли;
    Если МетаКоличество.Тип.Типы().Количество() <> 1 Или Не МетаКоличество.Тип.СодержитТип(Тип("Число")) Тогда
        ВызватьИсключение "Количество должно иметь единственный тип Число";
    КонецЕсли;
    Если МетаКоличество.Тип.КвалификаторыЧисла.Разрядность <> 4
        Или МетаКоличество.Тип.КвалификаторыЧисла.РазрядностьДробнойЧасти <> 0
        Или МетаКоличество.Тип.КвалификаторыЧисла.ДопустимыйЗнак <> ДопустимыйЗнак.Неотрицательный Тогда
        ВызватьИсключение "Нужен собственный тип Количество Число(4,0), неотрицательный";
    КонецЕсли;
КонецПроцедуры

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

Процедура ПроверитьСвойПустойСтенд()
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Проверка отсутствия своих записей должна идти вне активной транзакции";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить("Справочник.УчебныеКатегории85");
    Источники.Добавить("Документ.УчебнаяСпецификация85");
    Источники.Добавить("Документ.УчебнаяСпецификация85.Позиции");
    Для Каждого ИмяИсточника Из Источники Цикл
        ЗапросПустоты = Новый Запрос;
        ЗапросПустоты.Текст = "ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК Количество ИЗ " + ИмяИсточника;
        Таблица = ЗапросПустоты.Выполнить().Выгрузить();
        Если Таблица.Количество() <> 1 Или Таблица.Колонки.Количество() <> 1 Тогда
            ВызватьИсключение "Не совпала полная форма проверки пустоты своего источника";
        КонецЕсли;
        Если Таблица.Колонки.Найти("Количество") = Неопределено Тогда
            ВызватьИсключение "Не найден собственный счётчик пустоты";
        КонецЕсли;
        Если ТипЗнч(Таблица[0].Количество) <> Тип("Число") Тогда
            ВызватьИсключение "Счётчик своего источника должен иметь тип Число";
        КонецЕсли;
        Если Таблица[0].Количество <> 0 Тогда
            ВызватьИсключение "В новом собственном стенде не должно остаться ни одной записи: " + ИмяИсточника;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

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

Процедура ПроверитьСвойВводПозиции(Категория, Количество, ДопустимыеКатегории)
    Если ТипЗнч(Категория) <> Тип("СправочникСсылка.УчебныеКатегории85") Тогда
        ВызватьИсключение "Нужна ссылка только собственного справочника";
    КонецЕсли;
    Если ДопустимыеКатегории.Найти(Категория) = Неопределено Тогда
        ВызватьИсключение "Нужен один из трёх собственных листьев AX, AY, BX";
    КонецЕсли;
    Если ТипЗнч(Количество) <> Тип("Число") Тогда
        ВызватьИсключение "Своё количество должно иметь тип Число";
    КонецЕсли;
    Если Количество < 0 Или Количество > 9999 Или Количество <> Цел(Количество) Тогда
        ВызватьИсключение "Своё количество должно быть целым от 0 до 9999";
    КонецЕсли;
КонецПроцедуры

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

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

Функция СоздатьСвойДокумент(Номер)
    Если ТипЗнч(Номер) <> Тип("Строка") Тогда
        ВызватьИсключение "Номер собственного документа должен иметь тип Строка";
    КонецЕсли;
    Если СтрДлина(Номер) <> 8 Тогда
        ВызватьИсключение "Заданный собственный номер должен иметь длину 8";
    КонецЕсли;
    Объект = Документы.УчебнаяСпецификация85.СоздатьДокумент();
    Объект.Номер = Номер;
    Объект.Дата = Дата(2026, 10, 6, 12, 0, 0);
    Если Объект.Позиции.Количество() <> 0 Тогда
        ВызватьИсключение "Новая собственная табличная часть должна быть пустой";
    КонецЕсли;
    Возврат Объект;
КонецФункции

Функция СоздатьСвоиЗаписиДляЗапросов()
    ПустойРодитель = Справочники.УчебныеКатегории85.ПустаяСсылка();
    R = СоздатьСвоюКатегорию("001", "R", ПустойРодитель, Истина);
    A = СоздатьСвоюКатегорию("002", "A", R, Истина);
    B = СоздатьСвоюКатегорию("003", "B", R, Истина);
    AX = СоздатьСвоюКатегорию("004", "AX", A, Ложь);
    AY = СоздатьСвоюКатегорию("005", "AY", A, Ложь);
    BX = СоздатьСвоюКатегорию("006", "BX", B, Ложь);
    ZZ = СоздатьСвоюКатегорию("007", "ZZ", ПустойРодитель, Ложь);
    СвоиКатегории = Новый Массив;
    СвоиКатегории.Добавить(R);
    СвоиКатегории.Добавить(A);
    СвоиКатегории.Добавить(B);
    СвоиКатегории.Добавить(AX);
    СвоиКатегории.Добавить(AY);
    СвоиКатегории.Добавить(BX);
    СвоиКатегории.Добавить(ZZ);
    ДопустимыеКатегории = Новый Массив;
    ДопустимыеКатегории.Добавить(AX);
    ДопустимыеКатегории.Добавить(AY);
    ДопустимыеКатегории.Добавить(BX);
    ПроверитьСвойВводПозиции(AX, 0, ДопустимыеКатегории);
    ПроверитьСвойВводПозиции(BX, 9999, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(Неопределено, 2, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(7, 2, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(ПустойРодитель, 2, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(R, 2, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(AX, "2", ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(AX, -1, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(AX, 10000, ДопустимыеКатегории);
    ПроверитьОтклонениеСвоейПозиции(AX, 2.5, ДопустимыеКатегории);
    СвоиДокументы = Новый Массив;
    ОжиданияДокументов = Новый Массив;
    ОжиданияСтрок = Новый Массив;
    Объект = СоздатьСвойДокумент("OWN00001");
    ДобавитьСвоюПозицию(Объект, 1, AY, 2, ДопустимыеКатегории);
    ДобавитьСвоюПозицию(Объект, 2, AX, 5, ДопустимыеКатегории);
    ДобавитьСвоюПозицию(Объект, 3, AY, 5, ДопустимыеКатегории);
    Если Объект.Позиции.Количество() <> 3 Тогда
        ВызватьИсключение "Не совпал полный размер собственной табличной части";
    КонецЕсли;
    Объект.Записать(РежимЗаписиДокумента.Запись);
    СсылкаДокумента = Объект.Ссылка;
    Если ТипЗнч(СсылкаДокумента) <> Тип("ДокументСсылка.УчебнаяСпецификация85") Тогда
        ВызватьИсключение "После записи нужен собственный ссылочный тип документа";
    КонецЕсли;
    Если СсылкаДокумента = Документы.УчебнаяСпецификация85.ПустаяСсылка() Тогда
        ВызватьИсключение "После записи своя ссылка документа не должна быть пустой";
    КонецЕсли;
    СвоиДокументы.Добавить(СсылкаДокумента);
    ОжиданияДокументов.Добавить(Новый Структура("Ссылка,Номер,Дата", СсылкаДокумента, "OWN00001", Дата(2026, 10, 6, 12, 0, 0)));
    ОжиданияСтрок.Добавить(Новый Структура("Ссылка,Номер,НомерСтроки,Категория,Количество", СсылкаДокумента, "OWN00001", 1, AY, 2));
    ОжиданияСтрок.Добавить(Новый Структура("Ссылка,Номер,НомерСтроки,Категория,Количество", СсылкаДокумента, "OWN00001", 2, AX, 5));
    ОжиданияСтрок.Добавить(Новый Структура("Ссылка,Номер,НомерСтроки,Категория,Количество", СсылкаДокумента, "OWN00001", 3, AY, 5));
    Объект = СоздатьСвойДокумент("OWN00002");
    ДобавитьСвоюПозицию(Объект, 1, BX, 4, ДопустимыеКатегории);
    ДобавитьСвоюПозицию(Объект, 2, AX, 1, ДопустимыеКатегории);
    Если Объект.Позиции.Количество() <> 2 Тогда
        ВызватьИсключение "Не совпал полный размер собственной табличной части";
    КонецЕсли;
    Объект.Записать(РежимЗаписиДокумента.Запись);
    СсылкаДокумента = Объект.Ссылка;
    Если ТипЗнч(СсылкаДокумента) <> Тип("ДокументСсылка.УчебнаяСпецификация85") Тогда
        ВызватьИсключение "После записи нужен собственный ссылочный тип документа";
    КонецЕсли;
    Если СсылкаДокумента = Документы.УчебнаяСпецификация85.ПустаяСсылка() Тогда
        ВызватьИсключение "После записи своя ссылка документа не должна быть пустой";
    КонецЕсли;
    СвоиДокументы.Добавить(СсылкаДокумента);
    ОжиданияДокументов.Добавить(Новый Структура("Ссылка,Номер,Дата", СсылкаДокумента, "OWN00002", Дата(2026, 10, 6, 12, 0, 0)));
    ОжиданияСтрок.Добавить(Новый Структура("Ссылка,Номер,НомерСтроки,Категория,Количество", СсылкаДокумента, "OWN00002", 1, BX, 4));
    ОжиданияСтрок.Добавить(Новый Структура("Ссылка,Номер,НомерСтроки,Категория,Количество", СсылкаДокумента, "OWN00002", 2, AX, 1));
    Объект = СоздатьСвойДокумент("OWN00003");
    Если Объект.Позиции.Количество() <> 0 Тогда
        ВызватьИсключение "Не совпал полный размер собственной табличной части";
    КонецЕсли;
    Объект.Записать(РежимЗаписиДокумента.Запись);
    СсылкаДокумента = Объект.Ссылка;
    Если ТипЗнч(СсылкаДокумента) <> Тип("ДокументСсылка.УчебнаяСпецификация85") Тогда
        ВызватьИсключение "После записи нужен собственный ссылочный тип документа";
    КонецЕсли;
    Если СсылкаДокумента = Документы.УчебнаяСпецификация85.ПустаяСсылка() Тогда
        ВызватьИсключение "После записи своя ссылка документа не должна быть пустой";
    КонецЕсли;
    СвоиДокументы.Добавить(СсылкаДокумента);
    ОжиданияДокументов.Добавить(Новый Структура("Ссылка,Номер,Дата", СсылкаДокумента, "OWN00003", Дата(2026, 10, 6, 12, 0, 0)));
    ОжиданияКатегорий = Новый Массив;
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", R, "001", "R", ПустойРодитель, Истина));
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", A, "002", "A", R, Истина));
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", B, "003", "B", R, Истина));
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", AX, "004", "AX", A, Ложь));
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", AY, "005", "AY", A, Ложь));
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", BX, "006", "BX", B, Ложь));
    ОжиданияКатегорий.Добавить(Новый Структура("Ссылка,Код,Наименование,Родитель,ЭтоГруппа", ZZ, "007", "ZZ", ПустойРодитель, Ложь));
    Возврат Новый Структура("Категории,Документы,R,A,AX,ОжиданияКатегорий,ОжиданияДокументов,ОжиданияСтрок", СвоиКатегории, СвоиДокументы, R, A, AX, ОжиданияКатегорий, ОжиданияДокументов, ОжиданияСтрок);
КонецФункции

Процедура ПроверитьСвоиЗаписанныеИсточники(Данные)
    Если Данные.Категории.Количество() <> 7 Или Данные.Документы.Количество() <> 3 Тогда
        ВызватьИсключение "Нужны ровно семь своих категорий и три своих документа";
    КонецЕсли;
    Проверка = Новый Запрос;
    Проверка.Текст = "ВЫБРАТЬ Кат.Ссылка КАК Ссылка, Кат.Код КАК Код, Кат.Наименование КАК Наименование,
    |Кат.Родитель КАК Родитель, Кат.ЭтоГруппа КАК ЭтоГруппа
    |ИЗ Справочник.УчебныеКатегории85 КАК Кат ГДЕ Кат.Ссылка В (&Категории)
    |УПОРЯДОЧИТЬ ПО Кат.Код ВОЗР";
    Проверка.УстановитьПараметр("Категории", Данные.Категории);
    Поля = Новый Массив;
    Поля.Добавить("Ссылка");
    Поля.Добавить("Код");
    Поля.Добавить("Наименование");
    Поля.Добавить("Родитель");
    Поля.Добавить("ЭтоГруппа");
    ПроверитьСвоюПолнуюТаблицу(Проверка.Выполнить().Выгрузить(), Данные.ОжиданияКатегорий, Поля);
    Проверка.Текст = "ВЫБРАТЬ Док.Ссылка КАК Ссылка, Док.Номер КАК Номер, Док.Дата КАК Дата
    |ИЗ Документ.УчебнаяСпецификация85 КАК Док ГДЕ Док.Ссылка В (&Документы)
    |УПОРЯДОЧИТЬ ПО Док.Номер ВОЗР";
    Проверка.УстановитьПараметр("Документы", Данные.Документы);
    Поля = Новый Массив;
    Поля.Добавить("Ссылка");
    Поля.Добавить("Номер");
    Поля.Добавить("Дата");
    ПроверитьСвоюПолнуюТаблицу(Проверка.Выполнить().Выгрузить(), Данные.ОжиданияДокументов, Поля);
    Проверка.Текст = "ВЫБРАТЬ Поз.Ссылка КАК Ссылка, Поз.Ссылка.Номер КАК Номер, Поз.НомерСтроки КАК НомерСтроки,
    |Поз.Категория КАК Категория, Поз.Количество КАК Количество
    |ИЗ Документ.УчебнаяСпецификация85.Позиции КАК Поз ГДЕ Поз.Ссылка В (&Документы)
    |УПОРЯДОЧИТЬ ПО Поз.Ссылка.Номер ВОЗР, Поз.НомерСтроки ВОЗР";
    Поля = Новый Массив;
    Поля.Добавить("Ссылка");
    Поля.Добавить("Номер");
    Поля.Добавить("НомерСтроки");
    Поля.Добавить("Категория");
    Поля.Добавить("Количество");
    ПроверитьСвоюПолнуюТаблицу(Проверка.Выполнить().Выгрузить(), Данные.ОжиданияСтрок, Поля);
КонецПроцедуры
УчебныйПускПлоскихСтрок85.Форма

Собственная новая внешняя обработка УчебныйПускПлоскихСтрок85 → Управляемая форма Форма → Модуль

Отдельный полный модуль формы запуска. Клиент вызывает серверную процедуру без контекста, та — УчебныйЯзык.ВыполнитьПример(). Не вставлять директивы формы внутрь серверного общего модуля.

&НаКлиенте
Процедура Выполнить(Команда)
    ВыполнитьНаСервере();
    Сообщить("Собственный учебный опыт проверен, все его записи отменены");
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ВыполнитьНаСервере()
    УчебныйЯзык.ВыполнитьПример();
КонецПроцедуры

Разбор по шагам

  1. До любых действий read-only guard проверяет минимальный состав объектов и собственную схему полей, иерархии, строкового кода/номера и числового количества.

  2. ТранзакцияАктивна должна вернуть Ложь. Чужая/вложенная активная транзакция запрещена до НачатьТранзакцию: урок не имеет права откатить чужие изменения.

  3. До записи три собственных источника — справочник, документ и его табчасть — проверяются КОЛИЧЕСТВО(*)=0 с точным типом Число и полной формой результата. Непустой учебный стенд не очищается, а отклоняется.

  4. Внутри своей транзакции настоящие СоздатьГруппу/СоздатьЭлемент и Записать формируют семь записей настоящего дерева. Родители — свои ссылки уже записанных групп; для R и ZZ задана своя пустая ссылка.

  5. Ссылки берутся из readonly Ссылка после Записать и проверяются. Это настоящие записи данного учебного опыта до отката, не плановые адреса и не фиксированные строки, имитирующие дерево.

  6. Создать три собственных документа с точными номерами/датой; Добавить вызывается только у Позиции нового документа-объекта. Native запрещает Добавить у табчасти, полученной из ссылки или выборки.

  7. Свой валидатор проверяет тип/принадлежность листа AX/AY/BX и целое количество0..9999 до добавления. Две положительные границы и восемь намеренно плохих входов не добавляют строк; локальный catch охватывает только валидатор.

  8. Записать документы явно в режиме Запись, не Проведение. Fixture содержит ровно 7 категорий/3 документа/5 строк; полный состав всех записанных полей, типов и родителей независимо проверяется запросами до и после основного опыта.

  9. В ИЗ указан именно Документ.УчебнаяСпецификация85.Позиции, а не верхняя таблица документа. Native разрешает табчасти документов/справочников как самостоятельный источник.

  10. Через поле Строки.Ссылка.Номер обратиться к полю содержащего табчасть документа. Ссылка принадлежит каждому своему документу; в фильтре разрешены только три созданных документа.

  11. Через Строки.Категория.Наименование пройти по настоящему собственному ссылочному полю к справочнику. Native разрешает последовательность полей через точку, когда предыдущее поле ссылочное.

  12. Явный псевдоним Строки используется во всех исходных полях. Это реальное разыменование своих записанных значений, не неизвестный getter и не строковая имитация ссылки.

  13. УПОРЯДОЧИТЬ ПО Ссылка.Номер ВОЗР, НомерСтроки ВОЗР фиксирует порядок пяти строк. Категория AY повторяется в первом документе, поэтому нельзя заменять проверку полных строк множеством наименований.

  14. Сверить все четыре поля пяти строк, типы Строка/Число/Строка/Число и точные количества2,5,5,4,1. Пустая табчасть OWN00003 не содержит строки, следовательно отсутствует в плоском источнике.

  15. После полного сравнения вычислить собственные суммы по номерам12/5. Никакой новый агрегат/итоговый уровень запроса не создаётся; предложения ИТОГИ и AUTOORDER не используются.

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

  17. Ни ЗафиксироватьТранзакцию, ни удаление объектов, ни проведение не применяются. После отката не разыменовывать полученные раньше ссылки и не обещать существование их записей.

  18. Основной модуль и отдельный полный модуль формы предоставлены целиком. Их контексты не смешиваются; Python проверяет только модель своих фиксированных данных, не выполняет этот BSL.

Как проверить результат

  1. Полный состав: (OWN00001,1,AY,2),(OWN00001,2,AX,5),(OWN00001,3,AY,5),(OWN00002,1,BX,4),(OWN00002,2,AX,1).

  2. Четыре точных колонки и пять полных строк в заданном порядке; Номер/Категория строго Строка, НомерСтроки/Количество строго Число; строка OWN00003 отсутствует.

  3. Два настоящих пути разыменования возвращают собственные записанные значения до отката. Полная проверка деталей предшествует суммам12/5.

  4. Исходные поля/родители/даты/количества не меняются от запроса; обе ветви транзакции отменяют fixture и проверяют ноль записей в собственных трёх источниках.

Ожидаемый результат

Настоящая табчасть как источник даёт плоскую ведомость с двумя разными путями разыменования; все поля проверены и учебные записи отменяются.

На что обратить внимание

  1. Реальная иерархия/табличные части доступны запросу только после записи собственных объектов. Простая ТаблицаЗначений с названиями групп или ссылка без записи не заменяет этот fixture.

  2. Все записи существуют только в предоставленном коде будущего ручного опыта между НачатьТранзакцию и ОтменитьТранзакцию. Здесь BSL/1С не исполнялись и создание записей не утверждается.

  3. Автонумерация выключена, коды/номера заданы целиком. Пустые модули/подписки исключают внешний код из опыта, но статический guard не анализирует текст обработчиков: это обязательное условие новой подготовки.

  4. Python моделирует только фиксированные данные и полные ожидания; прямое чтение installed HBK подтверждает нативный текст, а не работу BSL, запросов, записи или отката в 1С.

  5. Не прикреплять AUTOORDER, иерархические итоги/сортировку или ПУСТАЯТАБЛИЦА: эти конструкции не используются. Поддерживающие API и ранее покрытые темы не являются новыми raw-привязками.

  6. Обращение к полям верхней таблицы идёт через Ссылка, а не через случайное имя документа. Псевдоним источника допустим вместо полного имени таблицы по native field_dereferencing.

  7. Документ с пустой табчастью остаётся в верхней таблице документа, но не обязан создавать строку табчасти. Мы не добавляем Null-строку и не выдаём вложенный результат за плоский источник.

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

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.