Рассмотрим пару кейсов решения задач с помощью СКД. На первый взгляд, задачи простые, но для их решения придется поломать голову.
Корректный расчет остатков по нескольким регистрам
Нам требуется создать отчет, который будет отображать одновременно данные по виртуальной таблице «ОстаткиИОбороты» одного регистра и «Обороты» другого регистра с периодичностью до регистратора. Это важный момент, из-за которого все и происходит.
На скриншоте показан отчет, где происходит обращение к двум виртуальным таблицам: красным цветом выделен блок ресурсов одного регистра, зеленым – показатели другого регистра. При стандартном объединении таких таблиц конечный остаток по группировке «Товар» считается неправильно – я покажу, как это можно исправить.
Пример запроса я будут показывать на демонстрационной конфигурации «Управляемое приложение».
Запрос выглядит следующим образом: объединение двух виртуальных таблиц, «ТоварныеЗапасы.ОстаткиИОбороты» и виртуальной таблицы «Продажи.Обороты».
На слайде показаны настройки полей СКД – из-за того, что включено автозаполнение полей, система по умолчанию проставила поля измерений, поля периодов и поля расчета остатков (начальный и конечный остаток).
При таком решении «в лоб», когда мы выполнили объединение двух запросов к виртуальным таблицам «ОстаткиИОбороты» и «Обороты», получаем нехорошую картину – в детальных записях все правильно: есть начальный остаток, приход, расход.
Но поскольку СКД сама рассчитывает значение ресурсов для группировок, в группировке на уровне Товар у нас указаны неправильные остатки. Для товара Bosch1234 у нас должно получиться -1, но СКД нам посчитала 0. И для Bosch15, если вычислить 4-3, то должно быть 1, но СКД тоже посчитала 0.
Почему так происходит? На сайте ИТС описана методика расчета остатков в СКД – там этому посвящена целая страница. Нас интересует единственная ветка алгоритма: расчет остатков по полям с ролью «Измерение» – по полям группировки «Товар». Там написано, что:
Первые по хронологии записи для неиспользованных полей измерений будут использоваться в качестве записей начального остатка, а последние – в качестве конечного.
Например, в данном примере первая запись – документ «Корректировка остатка 000000001», по хронологии она первая, значение начального остатка 0, соответственно, на уровне группировки «Товар» у нас в качестве начального остатка тоже получается 0.
В качестве значения конечного остатка на уровне группировки берется значение конечного остатка из последней по хронологии записи. В данном случае у нас последний по хронологии – документ «Продажа 000000014».
Но на уровне группировки «Товар» значение ресурса «КоличествоКонечныйОстаток» здесь тоже 0. Так получается из-за того, что у нас используется объединение запросов. Остатки и обороты мы объединили с продажами, это значение и пошло в значение конечного остатка на уровне группировки по товару.
Решение этой задачи вытекает из того, как СКД рассчитывает остатки – остатки рассчитываются в разрезе уникальных значений измерений и по полям периода.
Чтобы дать СКД эти уникальные разрезы и изменения, мы в каждый запрос объединения вводим еще одно текстовое поле – я его называю «Раздел». В это текстовое поле пишем уникальную для этого запроса последовательность символов. Я обычно называю раздел по названию таблицы, по которой делается запрос. Для запроса 1 у меня в этом поле написано «ТоварныеЗапасы», а для запроса 2 – «Продажи».
В настройках СКД для нового поля «Раздел» мы обязательно указываем роль «Измерение» и ставим флаг «Обязательное».
Флаг «Обязательное» должен быть установлен, поскольку вы вряд ли будете выбирать в своем отчете поле «Раздел» – оно имеет техническое предназначение, только для решения конкретной задачи, вы его не будете показывать пользователю.
Если вы не установите флаг «Обязательное», то при компоновке компоновщик макетов СКД удалит это поле, и все, что вы сделали, сойдет на нет. Поэтому мы устанавливаем для поля СКД «Раздел» флаг «Обязательное», то при компоновке макета это поле удалено не будет.
При таком решении начальный и конечный остаток по виртуальной таблице «ОстаткиИОбороты» на уровне группировки «Товар» рассчитывается правильно.
Подведем итоги этому кейсу:
-
СКД сама рассчитывает итоги, это необходимо помнить. Для этого на вкладке «Набор данных» мы прописываем роли – указываем, какое поле является измерением, какое поле является полем периода и объединяем поля начального и конечного остатков в группы.
-
Для решения задачи объединения двух виртуальных таблиц мы добавляем в запросах поле «Раздел» – в моем случае это просто строка.
-
В настройках полей СКД для добавленного поля «Раздел» мы устанавливаем роль «Измерение» и ставим флаг «Обязательное».
Наложение отбора в объединении запросов
Давайте рассмотрим второй кейс – это проблема наложения отбора в объединении запросов. Причем этот кейс начинает воспроизводиться с 8.3.13 – до этого такой проблемы не было.
Мы имеем набор данных-запрос, в котором у нас производится объединение двух запросов – к виртуальным таблицам оборотов регистров «Движение денежных средств» и «Денежные средства». Я заранее добавил в это объединение поле «Раздел».
-
В первом запросе есть поля «Счет» и «СтатьяДДС».
-
Во втором запросе есть только «Счет» – поля «СтатьяДДС» в этом запросе нет.
Посмотрим, как поведет себя система, если мы в пользовательском режиме захотим отфильтровать отчет по полям «Счет» и «СтатьяДДС».
На слайде показано, как выглядит запрос компоновщика в платформе 8.3.12 – это не исходный запрос, а запрос, который нам скомпоновал компоновщик макета СКД.
Обратите внимание, что при компоновке у нас в параметры виртуальных таблиц накладывается отбор, причем для первого запроса объединения установлен отбор по двум полям – «Счет» и «СтатьяДДС», а для второго запроса объединения установлен отбор только по полю «Счет», поскольку поля «СтатьяДДС» во втором запросе нет.
Теперь давайте посмотрим, как этот механизм работает в версии платформы 8.3.13.
В первом запросе – все то же самое, а во втором запросе объединения все изменилось.
Поскольку поля «Статья ДДС» во втором запросе нет, 1С решили, что при наложении отбора на поле, которого нет в запросе объединения, СКД должна добавить в запрос отбор по условию NULL=значение параметра. То есть, в этом случае из второго запроса объединения никакие данные выбираться не будут.
Это изменение описано на сайте 1С.
Как же быть? Можно попробовать отключить в СКД флаг автозаполнения у набора данных «Запрос» и вручную проставить поля условий (поля, на которые возможно наложить отбор).
На слайде представлен запрос СКД с отключенным автозаполнением. Здесь я для первого запросе объединения на закладке «Компоновка данных» вручную вытаскиваю в условия поля «Счет» и «Статья ДДС», а для второго запроса объединения я вручную вытаскиваю в условия поле «Счет».
Но даже в случае отключенного автозаполнения у набора данных и вручную проставленных условий только для нужных полей, если мы компонуем такой запрос с отбором по полям «Счет» и «СтатьяДДС», у нас компоновщик макета упорно добавляет в запросы объединения, во второй запрос этот отбор равен NULL, где NULL=наш второй параметр.
Как же решить эту задачу, чтобы у меня из второго запроса данные не фильтровались, не удалялись совсем?
Я пробовал выносить второй запрос объединения во временную таблицу, выбирать из временной таблицы – результат остается тем же, данные из второй таблицы в итоговый отчет не выводятся. Действует правило, указанное на сайте 1С: если у вас объединение и удалось наложить отбор в один из запросов объединения, во втором запросе объединения отбор будет также наложен, и вы ничего не получите.
Но пришла платформа 8.3.14, и, к счастью, это поведение изменили – об этом также сказано на сайте 1С.
Пример на слайде я сделал на платформе 8.3.16 с отключенным режимом совместимости (когда поведение платформы уже изменилось).
Здесь для того же набора данных-запрос без использования флага «Автозаполнение» при ручной настройке полей условий, в случае отбора по полю «Счет» и «Статья ДДС» у нас отбор накладывается только в первом запросе объединения.
Еще раз обращаю внимание, что здесь флаг «Автозаполнение» снят и настройка полей условий указана вручную.
Но если включить флаг автозаполнения в наборе данных «Запрос», то на платформе 8.3.16 в случае отбора по полям «Счет» и «СтатьяДДС» компоновщик макета будет пытаться наложить отбор и в первом, и во втором запросе объединения. И мы опять получим ситуацию, когда из второго запроса объединения ни одной записи не будет выбрано.
На нашем YouTube-канале я показываю много интересных кейсов решения задач по СКД.
upd: Начиная с платформе 8.3.18 поведение изменилось – теперь отбор на отсутствующие поля объединения не накладывается.
Вопросы
Встречали ли вы в типовых конфигурациях отчеты с первым кейсом – объединения двух виртуальных таблиц с выводом записей по регистратору?
В типовых конфигурациях подобного отчета я не видел. Данную задачу я решал при разработке нетипового отчета. Это было в УПП – надо было сравнить остатки и обороты по двум регистрам накопления – «ТоварыНаСкладах» и «ПартииТоваровНаСкладах».
Это достаточно частая задача, когда есть остатки первого и второго регистра – и они не идут. Бухгалтера сами разобраться не могут, нужно детальное сравнение, в таких случаях такой запрос выручает.
Расскажите про ошибку с 8.3.13 – как вы с ней столкнулись?
В 2019-м году, когда была платформа 8.3.13 в ходу, коллега обратился – у него схема объединения двух запросов, накладываем отбор и у нас все записи из второго запроса объединения вообще не выбирается. Начали компоновать схему вручную – и в результате компоновщика макета мы увидели, что СКД добавляет отбор NULL=значение параметра.
Тогда было еще непонятно, откуда такое изменение в поведении платформы, еще не прочитали информацию об изменениях в релизе. Тогда мы решили такую проблему доработкой текста запроса, который возвращает компоновщик. Фактически, сделали «костыль» – компонуем схему программно, по частям, дорабатываем запрос компоновщика макета, удаляем отбор и у нас вроде как работает. Другого решения для платформы 8.3.13 я не видел и не знаю.
Можно ли вывести в пользовательском режиме на форму отчета условие отбора «В списке» для условного оформления в настройках СКД?
Честно говоря, никогда так не делал. Я знаю, что дополнительные параметры настроек СКД – заголовок, группировки заголовок отчета, макет оформления, выводить отбор – можно вывести в пользовательском режиме на форму быстрых настроек пользователя. Я про это недавно узнал, для меня это тоже было открытием.
По второму примеру – только автозаполнение отключать? Другого способа нет? Описывать вручную поля для большого запроса не очень удобно.
Если у вас платформа с версией выше 8.3.13 – решение этого кейса – отключать автозаполнение. Но я ничего страшного в этом не вижу.
Дорабатывается и настраивается все просто. Вы со включенным флагом автозаполнения в конструкторе запроса набора данных-запрос идете на вкладку «Компоновка данных», добавляете на вкладке все поля, которые вы выбрали, а дальше на вкладке «Условия» вы раскидываете условия, начиная от первого запроса пакета, чтобы как можно раньше наложить больше отборов, чтобы выбрать меньше данных, чтобы у вас быстрее работал запрос пакета.
Задача сводится к тому, чтобы все выбранные поля в конце добавить и раскидать поля условий там, где это надо. Ничего сложного нет. Потом вы флаг автозаполнения снимаете и проверяете – работает так же или лучше.
Единственное неудобство может быть, если в запросе появляются новые поля – но и то, здесь главное не забыть.
Другой вопрос – в первом кейсе задублированы вторая и третья строки данных по регистратору. Нельзя ли их объединить?
Поскольку у нас объединение двух виртуальных таблиц по регистрам «ТоварныеЗапасы», то первые две строки не содержат показатель «КоличествоПродажи» – оно берется из регистра «Продажи». А третья строка данных – это уже информация из регистра накопления «Продажи». И здесь нет остатков, здесь есть только продажи. В этом и суть.
Кстати, отчеты по план-факту именно так и строятся – объединением запросов плана и факта. В типовых отчетах, которые показывают план-факт, эта задача решается аналогичным образом.
Сколько времени вы потратили, когда столкнулись с кейсом №2?
Много времени. Первым столкнулся с этой проблемой мой коллега. Сколько он времени убил – я не знаю, но когда он меня позвал, он мне уже показал запрос, который компонует компоновщик макета. И я ему сразу сказал, что надо дорабатывать текст запроса, потому что столько времени убивать на решение задачи нельзя.
И потом я стал разбираться, нашел эту особенность в изменениях платформы. Часа три потратил на то, чтобы понять, как это работает со включенным флагом автозаполнения и с отключенным флагом автозаполнения. Но тогда я не нашел никакого решения.
Расскажите подробнее про вкладку «Компоновка данных» в конструкторе запроса и работу с ней.
Да, я покажу, как быстро и просто отключить флаг «Автозаполнение» и ничего не потерять. У нас, кстати, есть отдельные клиенты, которые требуют, чтобы все схемы были созданы без включенного флага «Автозаполнение» – соответственно, это важный навык, которому нужно научиться.
Итак, у нас запрос пакета – здесь все просто, объединение полей. На вкладке «Компоновка данных» мы все поля добавляем – все поля стали выбранными. И на вкладке «Условия» мы в данном случае для каждого запроса объединения добавляем все поля ресурсов.
Обратите внимание, что поля условий прописываются для каждого запроса объединения отдельно (на отдельных вкладках), а поля компоновки – они общие.
На вкладке «Таблицы» закладки «Компоновка данных» есть кнопка «Параметры виртуальных таблиц». Здесь нам обязательно нужно будет прописать параметры для начала и конца периода, потому что когда мы отключаем флаг «Автозаполнение» у нас параметров «НачалоПериода» и «КонецПериода» не будет совсем. А в поле «Условие» мы добавляем измерения, на которые мы планируем накладывать отбор – и делаем это для обоих запросов объединения.
В том случае, когда мы не используем флаг «Автозаполнение», у нас появляется преимущество – те поля измерений, на которые мы планируем накладывать отбор в своем запросе, мы можем не прописывать, не включать. А когда мы отключаем флаг «Автозаполнение», больше ответственности ложится на программиста и требуется больше контроля.
Но есть отдельные случаи работы компоновщика макета компоновки данных, когда флаг «Автозаполнение» нам все портит, и приводит к тому, что в консоли запросов мы видим один результат исполнения запроса, а в консоли СКД мы видим другой результат исполнения запроса.
Флаг «Автозаполнение» позволяет ускорить процесс разработки схемы. В простых случаях, когда у вас запрос по одной или по двум таблицам – я согласен, что флаг «Автозаполнение» экономит время. Особенно потом, когда мы дорабатываем запрос. Но когда у вас сложный запрос, множество пакетов и вы боретесь за производительность, то наверное, придется снять автозаполнение и уже в консоли СКД смотреть, какой запрос компонует компоновщик – туда ли он накладывает отбор и оптимально ли это. Или может быть, нужно не здесь, а в другом месте отбор накладывать.
После корректного заполнения закладки «Компоновка данных» мы можем отключить флаг «Автозаполнение» и у нас ничего не потеряется – все настройки, все поля остались.
Может быть, кто-нибудь из старичков помнит объект «Построитель отчета», где дополнительно задавались настройки построителя. Здесь точно так же для компоновки.
Такие конструкции с фигурными скобками можно назвать директивами прекомпиляции для СКД, потому что СКД перед выполнением запроса ищет такие конструкции и заменяет их на реальные конструкции языка запросов. А на закладке «Компоновка данных» как раз настраиваются данные для конструкций в фигурных скобках.
Без флага «Автозаполнение» подход к созданию схемы становится более сознательным, но он более рискованный – особенно поначалу, пока нет опыта. Потом это становится нормальным. Просто нужно разобраться с поведением – как это работает, что нужно сделать когда новое поле добавляется – куда его нужно добавить и т.д.
*************
Данная статья написана по итогам доклада (видео), прочитанного на онлайн-митапе "Практика применения СКД".