Как отфильтровать документы одного вида: ССЫЛКА, ТИПЗНАЧЕНИЯ, вид операции и СКД

· 9 мин

Содержание

Чтобы сделать отбор по типу документа в запросе 1С, используйте оператор ССЫЛКА: условие ГДЕ Т.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг оставит только строки, созданные реализациями. Во встроенном языке то же самое делает сравнение ТипЗнч(Значение) = Тип("ДокументСсылка.РеализацияТоваровУслуг"). Если нужен не тип, а разновидность внутри одного документа, фильтруйте по реквизиту ВидОперации через ЗНАЧЕНИЕ(Перечисление...).

Приёмы общие для платформы 8.3 и не зависят от конфигурации. Примеры ниже написаны на объектах Бухгалтерии 3.0, но в УТ 11 или ЗУП 3.1 меняются только имена документов и регистров.

Отбор документов по типу в языке запросов

Задача обычно возникает при работе с полями составного типа. Самое частое из них — Регистратор в регистрах: туда пишут движения десятки видов документов. Второе — журналы документов и реквизиты вида «Документ-основание».

Оператор ССЫЛКА

Самый читаемый способ. Он проверяет, ссылается ли значение на объект указанного вида:

ВЫБРАТЬ
    Х.Регистратор КАК Документ,
    Х.СчетДт,
    Х.СчетКт,
    Х.Сумма
ИЗ
    РегистрБухгалтерии.Хозрасчетный КАК Х
ГДЕ
    Х.Период МЕЖДУ &НачалоПериода И &КонецПериода
    И Х.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

Несколько видов объединяйте через ИЛИ, исключение делайте через НЕ. Так можно, например, убрать из выборки ручные операции:

ГДЕ
    (Х.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        ИЛИ Х.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг)
    И НЕ Х.Регистратор ССЫЛКА Документ.ОперацияБух

Функция ТИПЗНАЧЕНИЯ

ТИПЗНАЧЕНИЯ() возвращает тип значения, и его сравнивают с ТИП():

ГДЕ
    ТИПЗНАЧЕНИЯ(Х.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)

Главный плюс — тип можно передать параметром. Список видов документов тогда не зашит в текст запроса, а собирается кодом:

ГДЕ
    ТИПЗНАЧЕНИЯ(Х.Регистратор) В (&ТипыДокументов)
ТипыДокументов = Новый Массив;
Для Каждого ИмяДокумента Из СтрРазделить("РеализацияТоваровУслуг,ПоступлениеТоваровУслуг", ",") Цикл
    ТипыДокументов.Добавить(Тип("ДокументСсылка." + ИмяДокумента));
КонецЦикла;
Запрос.УстановитьПараметр("ТипыДокументов", ТипыДокументов);

Как получить реквизиты отобранного документа

После отбора часто нужен реквизит самого документа, например контрагент. Обращаться через точку к составному полю (Х.Регистратор.Контрагент) не стоит: платформа соединит регистр со всеми таблицами документов, которые могут быть регистраторами. Приведите тип явно:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Х.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент,
    СУММА(Х.Сумма) КАК Сумма
ИЗ
    РегистрБухгалтерии.Хозрасчетный КАК Х
ГДЕ
    Х.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
СГРУППИРОВАТЬ ПО
    ВЫРАЗИТЬ(Х.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент

Какие виды документов вообще пишут в регистр

Перед отбором полезно узнать, из каких документов состоит регистр за период. Это быстрый способ найти точное имя вида и заодно заметить «чужие» документы, о которых никто не помнил:

ВЫБРАТЬ
    ТИПЗНАЧЕНИЯ(Х.Регистратор) КАК ТипДокумента,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Х.Регистратор) КАК Документов,
    СУММА(Х.Сумма) КАК Сумма
ИЗ
    РегистрБухгалтерии.Хозрасчетный КАК Х
ГДЕ
    Х.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    ТИПЗНАЧЕНИЯ(Х.Регистратор)
УПОРЯДОЧИТЬ ПО
    Документов УБЫВ

В консоли запросов колонка ТипДокумента покажет виды документов, а рядом — сколько их провелось за период. Дальше берите нужный вид и ставьте по нему отбор ССЫЛКА.

Условие из примеров можно не писать руками. В конструкторе запроса Конфигуратора откройте вкладку «Условия», включите флаг «Произвольное» и вставьте строку с ССЫЛКА. Конструктор сохранит её как есть.

Как сделать отбор по виду операции внутри документа

Тип документа и вид операции — разные вещи. «Списание с расчётного счёта» одним типом покрывает оплату поставщику, перечисление налога, выдачу подотчётному лицу. Различает их реквизит ВидОперации, и обычно он имеет тип перечисления.

Сравнение с элементом перечисления в запросе

Значение перечисления в тексте запроса задают функцией ЗНАЧЕНИЕ(). Строки и синонимы не подходят:

ВЫБРАТЬ
    Спис.Ссылка,
    Спис.Дата,
    Спис.СуммаДокумента
ИЗ
    Документ.СписаниеСРасчетногоСчета КАК Спис
ГДЕ
    Спис.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И Спис.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийСписаниеДенежныхСредств.ОплатаПоставщику)
    И НЕ Спис.ПометкаУдаления

Для нескольких видов операций удобнее параметр-массив: Спис.ВидОперации В (&ВидыОпераций).

Как узнать точное имя перечисления

Имена перечислений различаются между конфигурациями и иногда между редакциями. Проверьте их сами, это занимает минуту:

  1. Откройте Конфигуратор и дерево метаданных (Конфигурация → Открыть конфигурацию).
  2. Разверните ветку Документы, найдите нужный документ и его реквизит ВидОперации.
  3. В свойствах реквизита посмотрите поле «Тип» — там будет ПеречислениеСсылка.<Имя>.
  4. Откройте это перечисление в ветке Перечисления и скопируйте имя значения, а не синоним.

Вид операции у регистратора

Комбинация двух отборов: сначала тип регистратора, потом вид операции через приведение типа.

ГДЕ
    Х.Регистратор ССЫЛКА Документ.СписаниеСРасчетногоСчета
    И ВЫРАЗИТЬ(Х.Регистратор КАК Документ.СписаниеСРасчетногоСчета).ВидОперации
        = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийСписаниеДенежныхСредств.ОплатаПоставщику)

Проверка типа документа во встроенном языке

В коде модуля тип проверяют функцией ТипЗнч(). Сравнивать нужно со ссылочным типом, а не с типом объекта:

Если ТипЗнч(Основание) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
    // заполнение по реализации
ИначеЕсли ТипЗнч(Основание) = Тип("ДокументСсылка.ПоступлениеТоваровУслуг") Тогда
    // заполнение по поступлению
КонецЕсли;

Внутри модуля объекта (ОбработкаПроведения, ПередЗаписью) переменная ЭтотОбъект имеет тип ДокументОбъект.<Имя>. Ссылка на него — ЭтотОбъект.Ссылка.

Проверка «это вообще документ?»

Когда неважно, какой именно вид, а важно отличить документ от справочника или числа:

Если Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
    ИмяВида = Значение.Метаданные().Имя;      // РеализацияТоваровУслуг
    Синоним = Значение.Метаданные().Синоним;  // название для пользователя
КонецЕсли;

Метаданные() у ссылки дают объект описания документа. Имя пригодится для логики, синоним — для сообщений пользователю.

Отбор строк коллекции по типу

Если данные уже в таблице значений, а переписать запрос нельзя, удаляйте строки обратным циклом. Прямой цикл с удалением пропускает строки, потому что индексы сдвигаются:

ТипОтбора = Тип("ДокументСсылка.РеализацияТоваровУслуг");
Для Сч = 1 По Таблица.Количество() Цикл
    Строка = Таблица[Таблица.Количество() - Сч];
    Если ТипЗнч(Строка.Регистратор) <> ТипОтбора Тогда
        Таблица.Удалить(Строка);
    КонецЕсли;
КонецЦикла;

Отбор по типу в коде после выборки — запасной вариант. На тысячах строк он медленнее, чем то же условие в тексте запроса.

Настройка отбора по типу документа в СКД

В системе компоновки данных пользователь может поставить отбор по полю Регистратор. Но вид сравнения «В списке» просит выбрать конкретные документы, а не их тип. Поэтому отбор по виду документа в отчёте на СКД готовят на уровне схемы.

Способ 1. Поле с названием вида

Добавьте в запрос набора данных вычисляемую колонку:

ВЫБОР
    КОГДА Х.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        ТОГДА "Реализация"
    КОГДА Х.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
        ТОГДА "Поступление"
    ИНАЧЕ "Прочие"
КОНЕЦ КАК ВидДокумента

Поле ВидДокумента появится в настройках отчёта. Пользователь ставит по нему отбор, группировку или выводит колонкой. Минус один: новые виды документов нужно дописывать в ВЫБОР руками.

Способ 2. Параметр со списком типов

В запрос набора данных добавьте условие ТИПЗНАЧЕНИЯ(Х.Регистратор) В (&ТипыДокументов). Значение параметра задайте из кода формы или модуля отчёта:

Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.УстановитьЗначениеПараметра(
    "ТипыДокументов", ТипыДокументов);

Массив ТипыДокументов собирается так же, как в разделе про запросы. Этот вариант лучше, когда виды документов выбирает программа, а не пользователь.

Способ 3. Условие прямо в схеме

Если отчёт всегда строится по одному виду документа, впишите ССЫЛКА в ГДЕ запроса набора данных. В настройках пользователя этот отбор не виден и случайно не снимается.

СпособКогда выбиратьОграничение
Поле ВидДокумента через ВЫБОРПользователь сам выбирает виды в отчётеНовые виды дописывают вручную
Параметр &ТипыДокументовСписок видов задаёт кодНужен модуль отчёта или формы
ССЫЛКА в тексте схемыОтчёт только по одному видуОтбор нельзя изменить без правки схемы

Отбор в формах списков, журналах и динамических списках

Без программирования

В режиме «Предприятие» отбор по типу доступен там, где в одном списке собраны разные документы, то есть в журналах. У журнала документов есть стандартное поле Тип. Откройте меню «Ещё» над списком, перейдите к настройке списка и на закладке «Отбор» добавьте условие по полю типа документа.

Если журнала в интерфейсе нет, его можно открыть через Все функции → Журналы документов. Пункт «Все функции» включается в параметрах программы (Главное меню → Сервис → Параметры, флаг «Отображать команду “Все функции”»).

В списке документов одного вида отбор ставят по реквизиту «Вид операции». Он доступен в той же настройке списка, если реквизит есть у документа.

В запросе к журналу

Стандартное поле Тип у журнала работает и в запросе:

ВЫБРАТЬ
    Ж.Ссылка,
    Ж.Дата,
    Ж.Номер
ИЗ
    ЖурналДокументов.<ИмяЖурнала> КАК Ж
ГДЕ
    Ж.Тип = ТИП(Документ.РеализацияТоваровУслуг)

Программно в динамическом списке

Отбор на форме списка ставят через свойство Отбор динамического списка:

&НаСервере
Процедура УстановитьОтборПоВидуОперации()
    Список.Отбор.Элементы.Очистить();
    ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидОперации");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементОтбора.ПравоеЗначение = Перечисления.ВидыОперацийСписаниеДенежныхСредств.ОплатаПоставщику;
    ЭлементОтбора.Использование = Истина;
КонецПроцедуры

Очистить() убирает прежние условия, иначе при повторном вызове они копятся. В конфигурациях на БСП то же делает одна строка: ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "ВидОперации", Значение). Она находит существующий элемент и меняет его, а не добавляет новый.

Если у динамического списка свой запрос (флаг «Произвольный запрос» в свойствах реквизита формы), отбор по типу проще вписать в текст: ГДЕ Т.Документ ССЫЛКА Документ.<Имя>.

Где нужен отбор по типу документа?

Ошибки при отборе по типу и скорость запроса

Что ломается чаще всего

СимптомПричинаЧто сделать
Тип не определен (ДокументСсылка.Реализация товаров)В Тип() указан синоним, а не имя объектаВозьмите имя из дерева метаданных, без пробелов
Условие с ТипЗнч никогда не срабатываетСравнение с ДокументОбъект.<Имя> вместо ДокументСсылка.<Имя>В значении лежит ссылка, сравнивайте со ссылочным типом
Запрос не выбирает ни одной строки по виду операцииВид операции сравнивается со строкойИспользуйте ЗНАЧЕНИЕ(Перечисление.<Имя>.<Значение>)
В колонке ВидОперации много NULLПоле берётся через точку от составного Регистратор без отбора по типуДобавьте ССЫЛКА и ВЫРАЗИТЬ к нужному виду
Отчёт стал медленным после добавления реквизита документаРазыменование составного поля соединило все таблицы регистраторовЗамените Регистратор.Реквизит на ВЫРАЗИТЬ(...)
В выборку попадают удалённые документыОтбор по типу не учитывает пометку удаленияДобавьте НЕ Т.ПометкаУдаления или условие Проведен

Как не потерять в скорости

  • 🔹 Проверка ССЫЛКА и ТИПЗНАЧЕНИЯ у составного поля сводится к сравнению кода типа. Сама по себе она дешёвая.
  • 🔹 Сначала сужайте выборку по периоду (Период МЕЖДУ или Дата МЕЖДУ), потом по типу. На регистре за несколько лет разница заметна.
  • 🔹 Реквизит ВидОперации в типовых документах обычно не индексирован. Без условия на дату запрос просматривает всю таблицу документа.
  • 🔹 Не тяните реквизиты через точку от Регистратор: каждое такое обращение без ВЫРАЗИТЬ добавляет соединения по всем видам регистраторов.
  • 🔹 Если отчёт тормозит, включите в Конфигураторе замер производительности при отладке. Он покажет, сколько времени уходит на строку Запрос.Выполнить(), и станет ясно, виноват ли запрос.

Для запросов с отбором через ВЫРАЗИТЬ по большому регистру удобно проверять результат в консоли запросов на копии базы. Так видно и количество строк, и время выполнения.

Частые вопросы

Как получить список всех видов документов конфигурации?
Обойдите коллекцию Метаданные.Документы циклом Для Каждого. У каждого элемента есть Имя и Синоним, а тип ссылки получается так: Тип("ДокументСсылка." + МД.Имя).
Чем ССЫЛКА отличается от ТИПЗНАЧЕНИЯ?
Результат одинаковый. ССЫЛКА короче и читается проще, а ТИПЗНАЧЕНИЯ позволяет передать тип или массив типов параметром.
Работает ли оператор ССЫЛКА для справочников?
Да. Условие Т.Субконто1 ССЫЛКА Справочник.Контрагенты отбирает строки, где в субконто стоит контрагент. Так же проверяются планы видов характеристик и другие ссылочные объекты.
Можно ли отобрать документы по типу без Конфигуратора?
Да, в консоли запросов, запущенной в режиме «Предприятие». Условие с ССЫЛКА пишется в текст запроса так же, как в коде, править конфигурацию не нужно.
Почему пустая ссылка проходит отбор по типу?
У пустой ссылки тип тот же, что у заполненной. Если пустые значения не нужны, добавьте условие Т.Поле <> ЗНАЧЕНИЕ(Документ.<Имя>.ПустаяСсылка).