Вложенные позиции заказов: настоящий макет СКД, поля и проверенный вывод

Учебные задачи

На собственной модели O1/O2 получаем скомпилированный вложенный набор, исследуем его настоящие свойства и проверяем принадлежность трёх позиций заказам, а не только сумму.

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

Полный серверный общий модуль УчебныеВложенныеМакеты85 собственной новой базы; отдельный полный модуль управляемой формы ниже.

Подготовка

  1. Только НОВАЯ пустая учебная база 8.5.1.1343: корневое Имя=УчебныеВложенныеМакеты85, совместимость НеИспользовать, управляемое приложение. Не копия рабочей КА2/УНФ/УТ/БП. Без бизнес-метаданных, расширений, подписок, заданий и внешних действий. Здесь база не создаётся и пример не выполняется: runtimeVerifiedScenarios=0.

  2. Создайте общий модуль УчебныеВложенныеМакеты85: Сервер=Да, Клиент=Нет, Вызов сервера=Нет, Глобальный=Нет, Привилегированный=Нет. Вставьте целиком основной код ОДНОГО урока. Он включает создание всех данных, защиту стенда и проверку результата; неизвестных входных объектов нет.

  3. В своей новой конфигурации создайте обработку УчебныйРазборПозиций85 → управляемая форма Пульт → команда Выполнить с действием Выполнить и кнопкой. Отдельный полный модуль формы ниже. Выполните F7 и обновление только собственной учебной базы перед будущим отдельно разрешённым тестом.

  4. Стенд создаёт в памяти таблицу Заказы85: O1 содержит свою таблицу A01/3 и B02/5, O2 — другую таблицу C03/9. Никаких документов заказов создавать не нужно. Колонка Позиции85 без ограничения типа; её фактические значения и все вложенные примитивные колонки полностью проверяются.

  5. В исходной схеме четыре описания в одном каталоге: Номер85, вложенное поле Позиции85, Позиции85.Код, Позиции85.Количество. У поля исходной схемы нет собственного каталога Поля. Настройки выбирают только номер заказа и два вложенных значения, структура — детальные записи без таблиц и диаграмм.

  6. Макет компилируется генератором для коллекции значений. Настоящие внешние данные передаются ВТОРЫМ аргументом Процессор.Инициализировать, не третьим аргументом расшифровки. Таблицы и макеты не возвращаются в тонкий клиент; весь опыт остаётся серверным.

  7. В редакторском уроке XDTO переносит весь собственный макет в строку памяти и читает её обратно. Файлы, архивы и резервные копии не создаются. Неполный черновой каталог с именами отсутствующих внешних полей НИКОГДА не подаётся в процессор; исполняются только исходный и полностью восстановленный макеты.

  8. Для будущего разрешённого теста поставьте серверные точки останова в ПроверитьСвоиВложенныеОпределения и ПроверитьСвойВыводПозиций. Статические проверки не подтверждают реальную компиляцию макета, его XDTO round-trip или фактическое разворачивание вложенных строк на платформе.

Процедура ВыполнитьПример() Экспорт
    ПроверитьСвойПустойСтенд();
    Если ПривилегированныйРежим() Тогда
        ВызватьИсключение "Привилегированный режим не нужен";
    КонецЕсли;
    Стенд = СоздатьСвойСтендПозиций();
    ПроверитьСвойСтендПозиций(Стенд, Истина, "Позиции заказа", 5);
    Схема = Стенд.Схема;
    Настройки = Схема.НастройкиПоУмолчанию;
    Для Каждого Путь Из СтрРазделить("Номер85,Позиции85.Код,Позиции85.Количество", ",") Цикл
        Выбранное = Настройки.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
        Выбранное.Поле = Новый ПолеКомпоновкиДанных(Путь);
        Выбранное.Использование = Истина;
    КонецЦикла;
    Детали = Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
    Детали.Использование = Истина;
    ВыборДеталей = Детали.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
    ВыборДеталей.Использование = Истина;
    Компоновщик = Новый КомпоновщикМакетаКомпоновкиДанных;
    Макет = Компоновщик.Выполнить(Схема, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
    Вложенный = ПроверитьСвоиВложенныеОпределения(Макет);
    ПроверитьСвойВыводПозиций(Макет, Стенд.ВнешниеДанные);
КонецПроцедуры

Функция СвойТипКоличества()
    Возврат Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0, ДопустимыйЗнак.Неотрицательный));
КонецФункции

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

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

Функция СоздатьСвойСтендПозиций()
    Заказы = Новый ТаблицаЗначений;
    Заказы.Колонки.Добавить("Номер85", Новый ОписаниеТипов("Строка"));
    Заказы.Колонки.Добавить("Позиции85");
    Для ИндексЗаказа = 0 По 1 Цикл
        Позиции = Новый ТаблицаЗначений;
        Позиции.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка"));
        Позиции.Колонки.Добавить("Количество", СвойТипКоличества());
        СтрокаПозиции = Позиции.Добавить();
        Если ИндексЗаказа = 0 Тогда
            СтрокаПозиции.Код = "A01";
            СтрокаПозиции.Количество = 3;
            СтрокаПозиции = Позиции.Добавить();
            СтрокаПозиции.Код = "B02";
            СтрокаПозиции.Количество = 5;
        Иначе
            СтрокаПозиции.Код = "C03";
            СтрокаПозиции.Количество = 9;
        КонецЕсли;
        СтрокаЗаказа = Заказы.Добавить();
        Если ИндексЗаказа = 0 Тогда
            СтрокаЗаказа.Номер85 = "O1";
        Иначе
            СтрокаЗаказа.Номер85 = "O2";
        КонецЕсли;
        СтрокаЗаказа.Позиции85 = Позиции;
    КонецЦикла;
    Схема = Новый СхемаКомпоновкиДанных;
    Источник = Схема.ИсточникиДанных.Добавить();
    Источник.Имя = "СвоиЗаказы85";
    Источник.ТипИсточникаДанных = "Local";
    Набор = Схема.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
    Если ТипЗнч(Набор) <> Тип("НаборДанныхОбъектСхемыКомпоновкиДанных") Тогда
        ВызватьИсключение "Нужен собственный объектный набор";
    КонецЕсли;
    Набор.Имя = "Заказы85";
    Набор.ИмяОбъекта = "Заказы85";
    Набор.ИсточникДанных = "СвоиЗаказы85";
    ЗаполнитьСвоиОписанияПолей(Набор);
    ВнешниеДанные = Новый Структура;
    ВнешниеДанные.Вставить("Заказы85", Заказы);
    Стенд = Новый Структура;
    Стенд.Вставить("Схема", Схема);
    Стенд.Вставить("ВнешниеДанные", ВнешниеДанные);
    Возврат Стенд;
КонецФункции

Процедура ПроверитьСвойСтендПозиций(Стенд, ЕстьПоля, Заголовок, КоличествоB02)
    Если ТипЗнч(Стенд) <> Тип("Структура") Или Стенд.Количество() <> 2 Тогда
        ВызватьИсключение "Нужна собственная структура схемы и внешних данных";
    КонецЕсли;
    Схема = Неопределено;
    ВнешниеДанные = Неопределено;
    Если Не Стенд.Свойство("Схема", Схема) Или Не Стенд.Свойство("ВнешниеДанные", ВнешниеДанные) Тогда
        ВызватьИсключение "Не совпали оба ключа собственного стенда";
    КонецЕсли;
    Если ТипЗнч(Схема) <> Тип("СхемаКомпоновкиДанных") Или Схема.ИсточникиДанных.Количество() <> 1
        Или Схема.НаборыДанных.Количество() <> 1 Тогда
        ВызватьИсключение "Нужен единственный собственный источник и объектный набор";
    КонецЕсли;
    Если ТипЗнч(ВнешниеДанные) <> Тип("Структура") Или ВнешниеДанные.Количество() <> 1 Тогда
        ВызватьИсключение "Нужен единственный собственный внешний объект";
    КонецЕсли;
    Заказы = Неопределено;
    Если Не ВнешниеДанные.Свойство("Заказы85", Заказы) Тогда
        ВызватьИсключение "Не найдена настоящая собственная таблица заказов";
    КонецЕсли;
    Если ТипЗнч(Заказы) <> Тип("ТаблицаЗначений") Или Заказы.Количество() <> 2 Или Заказы.Колонки.Количество() <> 2 Тогда
        ВызватьИсключение "Нужно ровно два заказа и две колонки";
    КонецЕсли;
    Колонка = Заказы.Колонки.Получить(0);
    Если ТипЗнч(Колонка) <> Тип("КолонкаТаблицыЗначений") Или Колонка.Имя <> "Номер85" Тогда
        ВызватьИсключение "Не совпала первая колонка заказа";
    КонецЕсли;
    ПроверитьСвойТип(Колонка.ТипЗначения, "Строка");
    Колонка = Заказы.Колонки.Получить(1);
    Если ТипЗнч(Колонка) <> Тип("КолонкаТаблицыЗначений") Или Колонка.Имя <> "Позиции85"
        Или ТипЗнч(Колонка.ТипЗначения) <> Тип("ОписаниеТипов") Тогда
        ВызватьИсключение "Не совпала собственная колонка вложенных значений";
    КонецЕсли;
    Для ИндексЗаказа = 0 По 1 Цикл
        СтрокаЗаказа = Заказы.Получить(ИндексЗаказа);
        Если ТипЗнч(СтрокаЗаказа) <> Тип("СтрокаТаблицыЗначений") Или ТипЗнч(СтрокаЗаказа.Номер85) <> Тип("Строка") Тогда
            ВызватьИсключение "Не совпал тип строки заказа или номера";
        КонецЕсли;
        Если ИндексЗаказа = 0 Тогда
            Номер = "O1";
            ЧислоПозиций = 2;
        Иначе
            Номер = "O2";
            ЧислоПозиций = 1;
        КонецЕсли;
        Если СтрокаЗаказа.Номер85 <> Номер Тогда
            ВызватьИсключение "Не совпал полный номер заказа";
        КонецЕсли;
        Позиции = СтрокаЗаказа.Позиции85;
        Если ТипЗнч(Позиции) <> Тип("ТаблицаЗначений") Или Позиции.Количество() <> ЧислоПозиций
            Или Позиции.Колонки.Количество() <> 2 Тогда
            ВызватьИсключение "Ячейка должна содержать настоящую собственную таблицу позиций";
        КонецЕсли;
        Имена = СтрРазделить("Код,Количество", ",");
        Типы = СтрРазделить("Строка,Число", ",");
        Для Индекс = 0 По 1 Цикл
            Колонка = Позиции.Колонки.Получить(Индекс);
            Если ТипЗнч(Колонка) <> Тип("КолонкаТаблицыЗначений") Или Колонка.Имя <> Имена[Индекс] Тогда
                ВызватьИсключение "Не совпали порядок и имена обеих вложенных колонок";
            КонецЕсли;
            ПроверитьСвойТип(Колонка.ТипЗначения, Типы[Индекс]);
        КонецЦикла;
        Для Индекс = 0 По ЧислоПозиций - 1 Цикл
            СтрокаПозиции = Позиции.Получить(Индекс);
            Если ИндексЗаказа = 1 Тогда
                Код = "C03";
                Количество = 9;
            ИначеЕсли Индекс = 0 Тогда
                Код = "A01";
                Количество = 3;
            Иначе
                Код = "B02";
                Количество = КоличествоB02;
            КонецЕсли;
            Если ТипЗнч(СтрокаПозиции) <> Тип("СтрокаТаблицыЗначений")
                Или ТипЗнч(СтрокаПозиции.Код) <> Тип("Строка") Или СтрокаПозиции.Код <> Код
                Или ТипЗнч(СтрокаПозиции.Количество) <> Тип("Число") Или СтрокаПозиции.Количество <> Количество Тогда
                ВызватьИсключение "Не совпали все вложенные ячейки, коды, количества и их типы";
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    Источник = Схема.ИсточникиДанных.Получить(0);
    Если ТипЗнч(Источник) <> Тип("ИсточникДанныхСхемыКомпоновкиДанных")
        Или Источник.Имя <> "СвоиЗаказы85" Или Источник.ТипИсточникаДанных <> "Local" Тогда
        ВызватьИсключение "Не совпал собственный логический источник";
    КонецЕсли;
    Набор = Схема.НаборыДанных.Получить(0);
    Если ТипЗнч(Набор) <> Тип("НаборДанныхОбъектСхемыКомпоновкиДанных") Или Набор.Имя <> "Заказы85"
        Или Набор.ИмяОбъекта <> "Заказы85" Или Набор.ИсточникДанных <> "СвоиЗаказы85"
        Или Схема.НаборыДанных.Найти("Заказы85") <> Набор
        Или ТипЗнч(Набор.Поля) <> Тип("ПоляНабораДанныхСхемыКомпоновкиДанных") Тогда
        ВызватьИсключение "Не совпала связь своего набора с реальным внешним объектом";
    КонецЕсли;
    ЧислоПолей = 0;
    Если ЕстьПоля Тогда
        ЧислоПолей = 4;
    КонецЕсли;
    Если Набор.Поля.Количество() <> ЧислоПолей Тогда
        ВызватьИсключение "Не совпал полный размер своего каталога описаний";
    КонецЕсли;
    Если Не ЕстьПоля Тогда
        Возврат;
    КонецЕсли;
    ВложенноеПоле = Набор.Поля.Получить(1);
    Если ТипЗнч(ВложенноеПоле) <> Тип("ВложенныйНаборДанныхСхемыКомпоновкиДанных")
        Или ТипЗнч(ВложенноеПоле.Поле) <> Тип("Строка") Или ВложенноеПоле.Поле <> "Позиции85"
        Или ТипЗнч(ВложенноеПоле.ПутьКДанным) <> Тип("Строка") Или ВложенноеПоле.ПутьКДанным <> "Позиции85"
        Или ТипЗнч(ВложенноеПоле.Заголовок) <> Тип("Строка") Или ВложенноеПоле.Заголовок <> Заголовок Тогда
        ВызватьИсключение "Не совпали тип, реальное исходное поле, полный путь и заголовок вложенных данных";
    КонецЕсли;
    Пути = СтрРазделить("Номер85,Позиции85.Код,Позиции85.Количество", ",");
    Типы = СтрРазделить("Строка,Строка,Число", ",");
    Для Индекс = 0 По 2 Цикл
        ИндексПоля = Индекс;
        Если Индекс > 0 Тогда
            ИндексПоля = Индекс + 1;
        КонецЕсли;
        Поле = Набор.Поля.Получить(ИндексПоля);
        Если ТипЗнч(Поле) <> Тип("ПолеНабораДанныхСхемыКомпоновкиДанных")
            Или Поле.Поле <> Пути[Индекс] Или Поле.ПутьКДанным <> Пути[Индекс] Тогда
            ВызватьИсключение "Не совпали все простые исходные поля и полные составные пути";
        КонецЕсли;
        ПроверитьСвойТип(Поле.ТипЗначения, Типы[Индекс]);
    КонецЦикла;
КонецПроцедуры

Функция ПроверитьСвоиВложенныеОпределения(Макет)
    Если ТипЗнч(Макет) <> Тип("МакетКомпоновкиДанных") Или Макет.НаборыДанных.Количество() <> 1 Тогда
        ВызватьИсключение "В своей модели нужен один полный настоящий макет и один корневой набор";
    КонецЕсли;
    Родитель = Макет.НаборыДанных.Получить(0);
    Если ТипЗнч(Родитель) <> Тип("НаборДанныхОбъектМакетаКомпоновкиДанных") Или Родитель.ИмяОбъекта <> "Заказы85" Тогда
        ВызватьИсключение "Нужен настоящий объектный набор для своего внешнего ключа Заказы85";
    КонецЕсли;
    Каталог = Родитель.ВложенныеНаборыДанных;
    Если ТипЗнч(Каталог) <> Тип("ВложенныеНаборыДанныхМакетаКомпоновкиДанных") Или Каталог.Количество() <> 1 Тогда
        ВызватьИсключение "Нужен один настоящий вложенный набор, не вложенная схема или поле исходной схемы";
    КонецЕсли;
    Вложенный = Каталог.Получить(0);
    Если ТипЗнч(Вложенный) <> Тип("ВложенныйНаборДанныхМакетаКомпоновкиДанных") Тогда
        ВызватьИсключение "Нужен настоящий скомпилированный вложенный набор";
    КонецЕсли;
    Найденный = Каталог.Найти("Позиции85");
    Если Найденный = Неопределено Тогда
        ВызватьИсключение "Вложенный набор должен извлекаться из своего поля Позиции85";
    КонецЕсли;
    Если Найденный <> Вложенный Или Вложенный.Имя <> "Позиции85" Или Вложенный.ПутьКДанным = "" Тогда
        ВызватьИсключение "Имя, фактический путь и найденный элемент должны соответствовать своему вложенному набору";
    КонецЕсли;
    Если Каталог.Найти("НетТакогоВложенного85") <> Неопределено Тогда
        ВызватьИсключение "Отсутствующее имя должно дать Неопределено, не первый элемент";
    КонецЕсли;
    Сообщить("Вложенное поле=" + Вложенный.Имя + "; путь=" + Вложенный.ПутьКДанным + "; отбор=" + Вложенный.Отбор);
    Поля = Вложенный.Поля;
    Если ТипЗнч(Поля) <> Тип("ПоляНабораДанныхМакетаКомпоновкиДанных") Или Поля.Количество() < 2 Тогда
        ВызватьИсключение "Нужен настоящий readonly-каталог как минимум двух выбранных вложенных полей";
    КонецЕсли;
    Для Номер = 0 По Поля.Количество() - 1 Цикл
        Поле = Поля.Получить(Номер);
        Если ТипЗнч(Поле) <> Тип("ПолеНабораДанныхМакетаКомпоновкиДанных") Или Поле.Имя = "" Или Поле.ПутьКДанным = "" Тогда
            ВызватьИсключение "Внутри каталога нужны реальные описания полей с именем и путём";
        КонецЕсли;
        Если Поля.Найти(Поле.Имя) <> Поле Тогда
            ВызватьИсключение "Поиск должен возвращать то же настоящее поле";
        КонецЕсли;
        Сообщить("Поле вложенного набора=" + Поле.Имя + "; путь=" + Поле.ПутьКДанным);
    КонецЦикла;
    Возврат Вложенный;
КонецФункции

Функция НайтиСвоюКолонкуПоЗначениям(Таблица, Вариант)
    Если Вариант <> "Заказ" И Вариант <> "Код" И Вариант <> "Количество" Тогда
        ВызватьИсключение "У своей узкой модели только три непересекающихся области значений";
    КонецЕсли;
    Найденная = Неопределено;
    Для Каждого Колонка Из Таблица.Колонки Цикл
        Подходит = Истина;
        Для Каждого СтрокаРезультата Из Таблица Цикл
            Значение = СтрокаРезультата[Колонка.Имя];
            Если Вариант = "Заказ" Тогда
                Подходит = Подходит И ТипЗнч(Значение) = Тип("Строка") И (Значение = "O1" Или Значение = "O2");
            ИначеЕсли Вариант = "Код" Тогда
                Подходит = Подходит И ТипЗнч(Значение) = Тип("Строка") И (Значение = "A01" Или Значение = "B02" Или Значение = "C03");
            Иначе
                Подходит = Подходит И ТипЗнч(Значение) = Тип("Число") И (Значение = 3 Или Значение = 5 Или Значение = 9);
            КонецЕсли;
        КонецЦикла;
        Если Подходит Тогда
            Если Найденная <> Неопределено Тогда
                ВызватьИсключение "Роль колонки не должна определяться неоднозначно";
            КонецЕсли;
            Найденная = Колонка;
        КонецЕсли;
    КонецЦикла;
    Если Найденная = Неопределено Тогда
        ВызватьИсключение "В настоящем выводе не найдена колонка своей роли " + Вариант;
    КонецЕсли;
    Возврат Найденная;
КонецФункции

Процедура ПроверитьСвойВыводПозиций(Макет, ВнешниеДанные)
    Процессор = Новый ПроцессорКомпоновкиДанных;
    Процессор.Инициализировать(Макет, ВнешниеДанные);
    Вывод = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
    Приемник = Новый ТаблицаЗначений;
    Вывод.ОтображатьПроцентВывода = Ложь;
    Вывод.УстановитьОбъект(Приемник);
    Результат = Вывод.Вывести(Процессор, Ложь);
    Если ТипЗнч(Результат) <> Тип("ТаблицаЗначений") Или Результат.Количество() <> 3 Или Результат.Колонки.Количество() <> 3 Тогда
        ВызватьИсключение "Настоящий детальный вывод должен дать три позиции и три колонки";
    КонецЕсли;
    КолонкаЗаказа = НайтиСвоюКолонкуПоЗначениям(Результат, "Заказ");
    КолонкаКода = НайтиСвоюКолонкуПоЗначениям(Результат, "Код");
    КолонкаКоличества = НайтиСвоюКолонкуПоЗначениям(Результат, "Количество");
    Если КолонкаЗаказа = КолонкаКода Или КолонкаЗаказа = КолонкаКоличества Или КолонкаКода = КолонкаКоличества Тогда
        ВызватьИсключение "Три разные роли должны иметь три разные настоящие колонки";
    КонецЕсли;
    ЕстьA01 = Ложь;
    ЕстьB02 = Ложь;
    ЕстьC03 = Ложь;
    Итог = 0;
    Для Каждого СтрокаРезультата Из Результат Цикл
        Заказ = СтрокаРезультата[КолонкаЗаказа.Имя];
        Код = СтрокаРезультата[КолонкаКода.Имя];
        Количество = СтрокаРезультата[КолонкаКоличества.Имя];
        Если Код = "A01" Тогда
            Если ЕстьA01 Или Заказ <> "O1" Или Количество <> 3 Тогда
                ВызватьИсключение "Нужна ровно одна позиция O1/A01/3";
            КонецЕсли;
            ЕстьA01 = Истина;
        ИначеЕсли Код = "B02" Тогда
            Если ЕстьB02 Или Заказ <> "O1" Или Количество <> 5 Тогда
                ВызватьИсключение "Нужна ровно одна позиция O1/B02/5";
            КонецЕсли;
            ЕстьB02 = Истина;
        ИначеЕсли Код = "C03" Тогда
            Если ЕстьC03 Или Заказ <> "O2" Или Количество <> 9 Тогда
                ВызватьИсключение "Нужна ровно одна позиция O2/C03/9";
            КонецЕсли;
            ЕстьC03 = Истина;
        Иначе
            ВызватьИсключение "Лишняя позиция в настоящем выводе";
        КонецЕсли;
        Итог = Итог + Количество;
        Сообщить(Заказ + "/" + Код + "/" + Строка(Количество));
    КонецЦикла;
    Если Не ЕстьA01 Или Не ЕстьB02 Или Не ЕстьC03 Или Итог <> 17 Или Результат.Итог(КолонкаКоличества.Имя) <> 17 Тогда
        ВызватьИсключение "Все три собственные позиции и итог количества 17 должны сохраниться";
    КонецЕсли;
    Сообщить("Реальные три позиции связаны со своими заказами, количество=17.");
КонецПроцедуры

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

УчебныеВложенныеМакеты85 → Обработки → УчебныйРазборПозиций85 → Формы → Пульт → Модуль

Полный модуль собственной управляемой формы Пульт; не выполнялся.

&НаКлиенте
Процедура Выполнить(Команда)
    ВыполнитьНаСервере();
КонецПроцедуры

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

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

  1. СоздатьСвойСтендПозиций формирует два отдельных вложенных объекта ТаблицаЗначений, явные типы Код/Количество и числовые квалификаторы 10/0/Неотрицательный. ПроверитьСвойСтендПозиций сверяет каждую исходную ячейку, тип, имя и путь, а не только размер таблиц.

  2. Компоновщик строит настоящий макет по своей схеме и трём выбранным путям. НаборДанныхОбъектМакетаКомпоновкиДанных — уже описание исполнения, не исходный набор схемы; его ВложенныеНаборыДанных содержит настоящие ВложенныйНаборДанныхМакетаКомпоновкиДанных.

  3. Каталог.Получить(0) даёт элемент; Каталог.Найти("Позиции85") обязан вернуть тот же элемент. Отсутствующее имя проверяется на Неопределено. Имя означает исходное поле со вложенной таблицей; фактические ПутьКДанным и Отбор печатаются из макета и не подменяются выдуманными значениями.

  4. Поля у скомпилированного вложения — readonly-ссылка на настоящий каталог описаний. Код читает элементы, их имена/пути и сверяет поиск по фактическому имени. Нельзя писать Вложенный.Поля=Новый... или переносить это свойство на вложенное поле исходной схемы.

  5. Вывод.Вывести создаёт настоящий результат компоновки в ТаблицаЗначений. Проверка требует три строки и три колонки. Имя колонки составного пути может задаваться генератором: вместо догадки о точках/подчёркиваниях проверка просматривает все фактические колонки по всем строкам.

  6. Только для данного малого стенда роли колонок однозначны: O1/O2, A01/B02/C03 и числа 3/5/9 не пересекаются. Затем проверяются три точные пары заказ/позиция/количество, отсутствие повторов и лишних строк, независимая сумма и ТаблицаЗначений.Итог по фактическому имени колонки. Одна сумма 17 не обнаружила бы перепривязку позиции к чужому заказу.

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

  1. Будущий ожидаемый вывод: O1/A01/3, O1/B02/5, O2/C03/9 в любом порядке; ровно три настоящие строки, три колонки и количество 17. Флаги ЕстьA01/ЕстьB02/ЕстьC03 запрещают повторные позиции даже при сохранённом общем размере.

  2. Отрицательный ручной контроль в отдельно разрешённом опыте: передать в проверку полученный реальный результат с C03, перепривязанным к O1. Сумма может остаться 17, но точная проверка O2/C03/9 должна отказать. Здесь этот контроль не выполнялся.

  3. Если компоновка вернула таблицу с вложенными объектами вместо ожидаемых трёх плоских детальных строк, урок должен остановиться на проверке. Не создавайте сами три строки эталона вместо настоящего вывода и не объявляйте такой обход успешной работой СКД.

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

Ожидается настоящий вывод O1/A01/3, O1/B02/5, O2/C03/9; ровно три позиции, итог количества 17. Здесь в 1С не запускалось.

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

  1. Это подробный учебный проект ожидаемого опыта, не результат запуска в 1С: runtimeVerifiedScenarios=0. Нужна будущая проверка в собственной пустой базе; не исправляйте рабочие конфигурации ради примера.

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

  3. Не путайте вложенный набор макета с объединением наборов, вложенной схемой или группировкой настроек. В корневой native-странице вложенного элемента XDTO-имя названо DataSetUnion; из этой неоднозначной строки не выводится XML-тип. Здесь сериализуется весь настоящий МакетКомпоновкиДанных.

  4. Назначение ролей колонок по непересекающимся буквальным значениям применимо только к этому маленькому стенду. Это не универсальный способ разбора производственных отчётов: повторяющиеся значения и дополнительные колонки требуют отдельного явно согласованного контракта.

  5. Добавить() и Вставить(Индекс) вложенного каталога не принимают параметр Тип. Сдвинуть и Удалить получают собственный элемент этой же коллекции. Поля можно редактировать через его каталог, но readonly-свойство Поля нельзя заменять; неполные черновики не являются готовыми отчётами.

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

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

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

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

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