Доброго времени суток.
Вступление
Собственно все началось с простого задания - есть номенклатура (список номенклатуры), собираемая по спецификации. Надо вывести сколько комплектующих надо для сборки, сколько есть на складах, чтобы принять решение о закупках, сборке, передачах. (Складов много)
Отдельное спасибо за статьи СКД - наборы данных и связи между ними, создание собственной иерархии, вложенные отчеты и Агрегатные функции СКД, о которых мало кто знает.
Суть проблемы простая - пока в отчете нет отборов (список номенклатуры задается параметром) то схема из нескольких Наборов данных работает хорошо и правильно. Но стоит задать отбор по складу - можно получить пустой отчет... Итак по-порядку. (Конфигурация УПП, так что тексты запросов соответствующие)
1. Подготовка и демонстрация
НаборДанных1 (основная выборка) с полями Комплектующая, Продукция и КоличествоНа1 (среднее взял, потому что бардак в спецификации) - запрос:
НаборДанных2 (остатки, зависимый) - запрос:
Здесь немного поясню: я разделил Склад на поле "выборки" и поле "отбора", т.е. в отборах будет поле СкладОтбора, но его нельзя выбирать в Выбранные поля, хотя СкладКомплектующей тоже можно отбирать. Причина - в хотелке, но об этом ниже.
Связи наборов данных: Источник - НаборДанных1 (основной), Приемник - НаборДанных2 (зависимый), Выражения - Комплектующая.
Ресурсы пока простые: Сумма(КоличествоНа1) и Сумма(ОстатокКомлектующей).
Структура простенькая группировки: Комплектующая - Продукция - Детальные записи. Выбраны все поля.
Выполняю. Получаю ожидаемый результат - все есть, все хорошо:
Дальше была идея Склады в колонки, но тогда получается очень широко и почти не заполнено. Ну если одна комплектующая только на одном складе, то по всем остальным пусто. В общем не очень красиво выходит.
Проблема:
Но вот я задаю отбор "Склад отбора Равно Цех готовой продукции" и результат - только одна комплектующая:
Тогда я еще не знал про "замену Левого на Внутреннее, если есть отбор"...
2. Попытка выкрутиться
В статье "СКД - наборы данных и связи между ними..." есть пункт "Некоторые особенности соединения наборов данных" с подпунктом "Отбор по полю подчиненного набора", где сказано "если необходимо получить все данные из основного набора, нужно наложить отбор на уровне группировки отчета".
Но вот беда: в отборе по группировке, среди доступных полей, нет поля "СкладОтбора":
Хорошо, попробую использовать СкладКомплектующей. Добавил отборы в каждую группировку, но выключенные и стал включать по очереди. Первым включаю у группировки Комплектующая. Результат такой же, как с отбором по отчету целиком. Отключаю.
Включаю следующий - отбор у группировки Продукции. Уже лучше - все Комплектующие появились, а вот продукции нет. Да и остатки комплектующих странные - по группировке остатки показаны, а из чего получились, откуда взялись?
Отключаю у Продукции. Включаю последний отбор - у детальных записей. Почти идеальный результат, по крайней мере продукция появилась. Но с Остатками по-прежнему: по складам 46, а у группировок - 107 с хвостом. По остальным и того хуже.
Итог: либо я не так понял, либо не то делаю. Но и это не работает. Да и пользователям пояснять в какой ветке нужно поставить отбор - тяжело (прибабахи толстого клиента, где проще дать возможность редактировать настройку, чем вытащить что-то из глубины структуры на форму)
3. Хотелки и их исполнение.
Кому интересно решение проблемы - этот пункт можно пропустить.
Хотелка 1 - а почему не добавить показ остатков по Продукции?
Хотелка 2 - как бы сделать отчет более смотребильным, а то складов много и увидеть остатки сразу в строке - значит надо склады в колонки, а тогда отчет получается "разряженным".
Хотелка 3 - если уж добавил остатки продукции, то отбор по складу должен работать и на это.
Первая решается просто: еще один набор данных. Вот спрашивается, ну почему в запросах можно сделать один запрос во временную таблицу и соединить его несколько раз, а вот тут увы - надо новый набор данных. Ну и чтобы данные не пересекались - колонки переименованы. Так родился НаборДанных3.
Тут интересный эффект наблюдал, если поле Склад будет иметь одинаковое наименование в обоих подчиненных наборах, то в результате в остатках у продукции будет не правильный склад, потому что значение будет от первого набора. Поэтому - они разные. Одинаковыми могут быть только действительно одинаковые по смыслу поля, например те которые участвуют в связи. Так Номенклатура для НабораДанных2 называется Комплектующая, а в НабореДанных3 - Продукция. Ну а чтобы поле отбора было единым для обоих наборов: появился СкладОтбора - это решило хотелку 3.
Ну и добавляем еще одну связь.
Ну и в ресурсах определяем ОстатокПродукции.
Теперь вторая хотелка. Если выводить данные по складам списком - получим умножение строк остатков, например 5 складов по Комплектующей и 3 по Продукции итого 15 строк. Хорошо хоть итоги по группировкам правильные. Да и как "сложить" 2 одинаковых склада по комплектующей и по продукции? Поля называются по разному и как сделать по ним группировку - не придумал.
Тут на помощь пришла вторая статья Агрегатные функции СКД, о которых мало кто знает. Сначала использовал функцию ТаблицаЗначений. Но при простом повторении формирования отчета строки складов прыгают. Тогда добавил Упорядочить в итоге получилось так:
Для ОстаткаКомплектующих: Упорядочить(ТаблицаЗначений(Различные СкладКомплектующей Как Склад, ОстатокКомплектующей Как Остаток), "Склад Автоупорядочивание")
Для ОстаткаПродукции: Упорядочить(ТаблицаЗначений(Различные СкладПродукции Как Склад, ОстатокПродукции Как Остаток), "Склад Автоупорядочивание")
Дальше немного "причесал" структуру: выключил детальные записи, сгруппировал поля, отключил "Общие Итоги по вертикали".
В результате получил - строки группировки "пухнут" в зависимости от количества складов, но видно где, чего и сколько, просто посмотрев по строке. Поставил в ресурсах "Расчитывать по..." (как на картинке выше) и по крайне мере для Комплектующей убрались остатки по складам из колонки остатков продукции. Вот такой результат получил:
Теперь влючаю отбор по складу и вижу результат, опять исчезли строки (что в прочем уже ожидаемо):
4. Поиск решения
Сразу скажу: на мой взгляд не очень хорошее, но другого не нашел. Вопросы такого плана есть, а ответов увы не много. Потому думал сам и пришел к выводу - если связь вместо ЛЕВОЙ становится ВНУТРЕННЕЙ, значит необходимо сделать так, чтобы для каждой записи основного набора существовала запись зависимого, не взирая на отбор.
И тут у меня возникла сложность - я не очень хорошо понимаю в какой момент накладывается отбор при формировании. Потому решил пойти простым путем - эксперимента. Первое что попробовал - изменить запрос остатков: к справочнику номенклатуры Левым соединением добавил Остатки. На тот момент это выглядело так (не использовать, почему - ниже):
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Склад КАК СкладКомплектующей,
СпрНоменклатура.Ссылка КАК Комплектующая,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК ОстатокКомплектующей
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки({(&КонецПериода)}, ) КАК ТоварыНаСкладахОстатки
ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
{ГДЕ
ТоварыНаСкладахОстатки.Склад.* КАК СкладОтбора}
Дальше экспериментировал с настройками. Работало долго. На каком-то этапе вообще получил от сервера отлуп по памяти... Бросил.
Пробовал создать набор типа Объединение, где один набор - остатки, другой - опять же номенклатура. Но там надо было создавать сложные условия с ИЛИ, чтобы запись Номенклатуры выбиралась в любом случае... Не пошло.
День бодался. На следующий день пошел по второму кугу. Только все создавал с нуля.
5. Решение
И начал снова с Левого соединения в запросе. Получился такой запрос (правильный):
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Склад КАК СкладКомплектующей,
СпрНоменклатура.Ссылка КАК Комплектующая,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК ОстатокКомплектующей
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки({(&КонецПериода)}, {(Склад).* КАК СкладОтбора, (Номенклатура).* КАК Комплектующая}) КАК ТоварыНаСкладахОстатки
ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
Сформировал, посмотрел нормальный результат. Включил отбор и увидел это:
Стал сравнивать с предыдущими тестами и понял: {ГДЕ ТоварыНаСкладахОстатки.Склад.* КАК СкладОтбора} отрабатывала после соединения, а потому отсекало номенклатуру. А когда я перенес его в условия таблицы остатков, то и отбор сработал только на регистр остатков не затронув номенклатуру. Потому первый запрос - не правильный и не работает, а второй заработал (так я считаю).
Но самым удивительным для меня оказался такой результат (отбор по другому складу и еще одна продукция):
Чем удивило? А тем, что Левое соединение я делал только в одном наборе данных - во втором (по комплектующим), а в третьем - как было, так и осталось. Почему же группировка по Продукции не пропала? Почему остатки выдает правильные с учетом отбора? Вывод получился такой - а третий набор, несмотря на то что к нему был применен отбор, все-равно имеет ЛЕВОЕ соединение. Возможно свою лепту вносит тот факт, что для второго и третьего набора один и тот же отбор. Возможно третий слепили со вторым, а потом только с первым.
Правильный вывод или нет - я не знаю.
В общем дальше можно пытаться улучшить. Например, если основной запрос не сложный, то в НабореДанных2 связывать не со справочником номенклатуры, а поместить результат основного запроса во временную таблицу и связать с ним...
Конец
Прошу специалистов не смеяться над стилем изложения, я курсов не проходил. Общаюсь в большинстве своем с пользователями, которым надо объяснить понятно что, почему и как. Да и статья это первая, не потому что писать нечего, а потому, что большая часть уже написана Вами и надо только найти :).