При работе с отчетами в 1С:СКД разработчики часто сталкиваются с необходимостью отображения комплексной информации, объединяющей данные из различных документов и регистров. Одна из таких типовых задач — создание отчета по оплаченным заказам, где важно видеть номенклатуру самого заказа, даже если заказ был создан за пределами текущего отчетного периода, но оплата по нему прошла в выбранный период. В этом материале мы подробно разберем, как решить эту задачу, а также устранить проблему дублирования данных и настроить вывод только итоговых значений.
Представим ситуацию: нам необходимо создать отчет "Реестр оплат заказов и счетов", который должен отображать номенклатуру и ее тип для оплаченных заказов. Ключевое условие — мы интересуемся именно оплаченными в выбранный период заказами, независимо от даты создания самого заказа. То есть, заказ мог быть создан в апреле, оплачен в июне, и при выборе периода "Июнь" в отчете должна появиться номенклатура этого апрельского заказа.
Исходная проблема заключается в том, что отчет корректно отображает оплаты в заданном периоде, но детализация по номенклатуре заказа доступна только для тех заказов, которые по каким-то причинам также попадают в выбранный период, либо не отображается вовсе, если заказ старше. Рассмотрим, почему это происходит и как это исправить.
Для решения подобных задач, как правило, используется регистр накопления, хранящий информацию об оплатах, и документ "Заказ покупателя" для получения номенклатурного состава. Частью запроса, которая может вызывать проблему, является условие соединения (ЛЕВОЕ СОЕДИНЕНИЕ) между регистром оплат и документом заказа, а также условия фильтрации (ГДЕ).
В нашем случае, исходный запрос мог выглядеть примерно так (фрагмент, касающийся соединения и фильтрации):
ВЫБРАТЬ
ЗаказПокупателя.Запасы.(
Номенклатура КАК Номенклатура,
Номенклатура.ТипНоменклатуры КАК НоменклатураТипНоменклатуры,
Сумма КАК Сумма,
Номенклатура.Наименование КАК НоменклатураНаименование
) КАК Запасы,
// ... другие поля регистра оплат
ИЗ
РегистрНакопления.ОплатаСчетовИЗаказов КАК ОплатаСчетовИЗаказов
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК ЗаказПокупателя
ПО ОплатаСчетовИЗаказов.Регистратор = ЗаказПокупателя.Ссылка
И ОплатаСчетовИЗаказов.СчетНаОплату = ЗаказПокупателя.Ссылка
ГДЕ
ОплатаСчетовИЗаказов.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
И ОплатаСчетовИЗаказов.СчетНаОплату ССЫЛКА Документ.ЗаказПокупателя
Ключевые моменты, на которые стоит обратить внимание:
ОплатаСчетовИЗаказов.Период МЕЖДУ &ДатаНачала И &ДатаОкончания является корректным, так как мы хотим видеть оплаты, совершенные в выбранный период. Это условие правильно отбирает движения регистра, соответствующие нашему требованию.ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК ЗаказПокупателя ПО ОплатаСчетовИЗаказов.Регистратор = ЗаказПокупателя.Ссылка И ОплатаСчетовИЗаказов.СчетНаОплату = ЗаказПокупателя.Ссылка. Здесь кроется основная проблема. Разберем его подробнее:
ОплатаСчетовИЗаказов.Регистратор в регистре накопления обычно ссылается на документ, который выполнил движение, т.е. на документ оплаты (например, ПоступлениеНаРасчетныйСчет, РасходныйКассовыйОрдер и т.д.).ОплатаСчетовИЗаказов.СчетНаОплату в данном регистре, как следует из названия и контекста, ссылается на документ, который был оплачен (это может быть как ЗаказПокупателя, так и СчетНаОплату).Если Регистратор является документом оплаты, а не самим ЗаказомПокупателя, то условие ОплатаСчетовИЗаказов.Регистратор = ЗаказПокупателя.Ссылка будет практически всегда ложным. Это означает, что вся строка условий для ЛЕВОГО СОЕДИНЕНИЯ (из-за оператора И) становится ложной, и соединение с Документ.ЗаказПокупателя не произойдет. В результате, мы не сможем получить данные из табличной части ЗаказПокупателя.Запасы.
Для корректного получения номенклатуры из ЗаказаПокупателя нам необходимо связать регистр оплат именно с тем документом, который был оплачен, а не с документом-регистратором движения. Для этого мы убираем ошибочное условие ОплатаСчетовИЗаказов.Регистратор = ЗаказПокупателя.Ссылка из секции ПО.
Исправленный фрагмент запроса будет выглядеть так:
ВЫБРАТЬ
ЗаказПокупателя.Запасы.(
Номенклатура КАК Номенклатура,
Номенклатура.ТипНоменклатуры КАК НоменклатураТипНоменклатуры,
Сумма КАК Сумма,
Номенклатура.Наименование КАК НоменклатураНаименование
) КАК Запасы,
ОплатаСчетовИЗаказов.СуммаОплаты КАК СуммаОплаты,
ОплатаСчетовИЗаказов.СчетНаОплату.Ответственный КАК СчетНаОплатуОтветственный,
ОплатаСчетовИЗаказов.СчетНаОплату КАК СчетНаОплату,
ОплатаСчетовИЗаказов.Период КАК Период,
ОплатаСчетовИЗаказов.СуммаАванса + ОплатаСчетовИЗаказов.СуммаОплаты КАК ОплаченоЗаПериод,
ВЫБОР КОГДА ОплатаСчетовИЗаказов.Регистратор ССЫЛКА Документ.ЗаказПокупателя
ИЛИ ОплатаСчетовИЗаказов.Регистратор ССЫЛКА Документ.СчетНаОплату
ТОГДА ВЫБОР КОГДА ОплатаСчетовИЗаказов.ДокументОплаты = НЕОПРЕДЕЛЕНО
ТОГДА &ЗачетПредоплаты
ИНАЧЕ ОплатаСчетовИЗаказов.ДокументОплаты
КОНЕЦ
ИНАЧЕ ОплатаСчетовИЗаказов.Регистратор
КОНЕЦ КАК ДокументОплаты
ИЗ
РегистрНакопления.ОплатаСчетовИЗаказов КАК ОплатаСчетовИЗаказов
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК ЗаказПокупателя
ПО ОплатаСчетовИЗаказов.СчетНаОплату = ЗаказПокупателя.Ссылка
ГДЕ
ОплатаСчетовИЗаказов.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
И ОплатаСчетовИЗаказов.СчетНаОплату ССЫЛКА Документ.ЗаказПокупателя
УПОРЯДОЧИТЬ ПО
СчетНаОплату,
СчетНаОплатуОтветственный
Таким образом, мы обеспечиваем, что ЛЕВОЕ СОЕДИНЕНИЕ будет выполняться только по ссылке на оплачиваемый документ (СчетНаОплату), которая в свою очередь может быть ссылкой на ЗаказПокупателя. Это позволяет получить данные из табличной части ЗаказПокупателя.Запасы для всех оплат, попадающих в выбранный период, независимо от даты создания самого заказа.
РегистрНакопления.ОплатаСчетовИЗаказов.Обороты) период следует указывать непосредственно в параметрах виртуальной таблицы. Это позволяет системе 1С оптимальнее фильтровать данные на уровне СУБД. Например:
ИЗ
РегистрНакопления.ОплатаСчетовИЗаказов.Обороты(&ДатаНачала, &ДатаОкончания, Регистратор, ) КАК ОплатаСчетовИЗаказовОбороты
Если вы используете таблицу движений (без оборотов), как в примере, то условие в ГДЕ остается актуальным.
НАЧАЛОПЕРИОДА() и КОНЕЦПЕРИОДА(): При работе с датами в параметрах запросов и виртуальных таблиц всегда используйте функции НАЧАЛОПЕРИОДА() и КОНЕЦПЕРИОДА(). Это гарантирует, что диапазон дат будет включать весь день начала и весь день окончания, предотвращая потерю данных, например, за последние часы конечного дня. Например:
ГДЕ ОплатаСчетовИЗаказов.Период МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ) И КОНЕЦПЕРИОДА(&ДатаОкончания, ДЕНЬ)
После исправления запроса, который позволил корректно получать номенклатуру, может возникнуть новая проблема: дублирование строк в отчете, особенно если один заказ содержит несколько позиций номенклатуры, или если одна оплата закрывает несколько позиций счета, при этом суммы оплаты повторяются для каждой строки номенклатуры. Также часто требуется, чтобы для некоторых колонок (например, "Оплачено за период") выводились только итоговые значения, а детальные строки оставались пустыми или скрытыми.
Дублирование строк в отчете СКД может быть вызвано несколькими факторами:
СУММА, КОЛИЧЕСТВО, МИНИМУМ, МАКСИМУМ) для числовых полей, СКД будет выводить значение для каждой детальной строки, что приводит к зрительному дублированию.Рассмотрим по шагам основные способы решения этих проблем в СКД.
Если ваш отчет построен на нескольких наборах данных (например, один набор для оплат, другой для номенклатуры заказов), убедитесь, что связи между ними настроены корректно и однозначно. Ошибки в связях могут приводить к перемножению записей.
Для каждого поля, по которому должна быть связь (например, ссылка на заказ), установите соответствующие связи в СКД на вкладке "Связи наборов данных". Убедитесь, что тип связи соответствует вашим требованиям (например, Левое соединение, если нам нужны все оплаты, даже если нет связанных заказов).
Это один из наиболее важных шагов для корректного отображения числовых данных и предотвращения дублирования.
Шаги:
СуммаОплаты, ОплаченоЗаПериод), перенесите их из левой части в правую.СУММА().Пример настройки ресурса:
СуммаОплатыСУММА(СуммаОплаты)ИстинаТаким образом, для каждой группировки будет рассчитываться сумма по всем детальным записям, относящимся к этой группировке.
Если вы хотите, чтобы колонка "Оплачено за период" отображала значение только в итоговой строке (например, для всего заказа или для группы заказов), а в детальных строках номенклатуры эта сумма не дублировалась, воспользуйтесь настройками группировок.
Шаги:
СчетНаОплату).ОплаченоЗаПериод), найдите его в списке.СчетНаОплату (или более высокой) оно выбрано для вывода.Отдельно и в заголовке или Отдельно и в подвале, а "Расположение группировок" в Нет или Вместе с детальными записями в зависимости от желаемого эффекта. Для поля ОплаченоЗаПериод в настройках полей детальных записей можно снять флаг вывода.Это позволит вывести сумму оплаты только один раз для всего заказа, а не для каждой номенклатурной позиции.
Этот метод позволяет "скрыть" дублирующиеся значения, делая текст пустым, оставляя при этом структуру отчета.
Шаги:
ТекстОплаченоЗаПериод).
СчетНаОплату, и вы хотите скрыть значения для каждой строки номенклатуры внутри этого заказа, но оставить в итоговой строке заказа, то условие может быть связано с проверкой, является ли текущая строка детальной записью внутри группировки. К сожалению, СКД не всегда позволяет напрямую обращаться к "уровню группировки" в условиях, поэтому часто приходится использовать косвенные методы, например, проверять, является ли значение поля уникальным для текущей детальной строки (что чаще всего не подходит для сумм, которые должны агрегироваться).
СчетНаОплату, вы можете настроить условное оформление для поля ОплаченоЗаПериод таким образом, чтобы оно не выводилось в строках "Детальные записи", но выводилось в строках "Итоги по СчетНаОплату". Для этого в настройках вывода полей для детальных записей вы можете просто отключить вывод поля ОплаченоЗаПериод, а для заголовка/подвала группировки по СчетНаОплату – включить.РАЗЛИЧНЫЕ в запросеЕсли дублирование происходит на уровне самого запроса, и вам нужны только уникальные комбинации полей, можно использовать ключевое слово РАЗЛИЧНЫЕ в операторе ВЫБРАТЬ:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ОплатаСчетовИЗаказов.СчетНаОплату КАК СчетНаОплату,
// ... другие поля
ИЗ
// ...
Однако будьте осторожны: РАЗЛИЧНЫЕ будет работать только в том случае, если вся выбранная строка полностью уникальна. Если хотя бы одно поле отличается, строка не будет считаться дубликатом. Этот метод редко подходит для агрегированных числовых данных, так как он скрывает детали, которые могут быть важны для расчета итогов.
Применяя эти подходы, вы сможете создать гибкий и информативный отчет в 1С:СКД, который будет корректно отображать необходимую информацию по оплаченным заказам из разных периодов, а также обеспечит чистоту данных без дублирования и с правильным расчетом и выводом итогов.