Как исключить определенный вид документа из запроса к регистру накопления в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

В процессе разработки и кастомизации типовых конфигураций 1С (например, УПП, Комплексная автоматизация или Управление торговлей) часто возникает задача исключить определенные хозяйственные операции из общесистемных алгоритмов. Типичный пример — необходимость исключить конкретный вид документа (например, ПлатежноеПоручениеВходящее) из механизма переоценки валютных остатков по взаиморасчетам.

На первый взгляд задача кажется простой: достаточно добавить условие отрицания в секцию ГДЕ текста запроса. Однако на практике разработчики сталкиваются с неочевидным поведением СУБД, синтаксическими ошибками и ситуацией, когда из результата запроса полностью исчезают нужные данные. В этой статье мы подробно разберем, почему простые решения не всегда работают, проанализируем внутреннюю логику работы платформы 1С с составными типами данных и предложим надежные варианты решения этой задачи.

Исходная ситуация и анализ запроса

Рассмотрим стандартный запрос, который выбирает остатки по регистру накопления ВзаиморасчетыСКонтрагентами для последующей переоценки:


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

Наша задача — исключить из этого запроса строки, у которых в поле Сделка указан документ типа Документ.ПлатежноеПоручениеВходящее. Поле Сделка имеет составной тип данных, что накладывает существенные ограничения на логику построения условий в запросе.

---

Разбор типичных ошибок при исключении документов

Давайте пошагово разберем ошибки, которые часто совершают разработчики при решении этой задачи, и выясним их причины.

Ошибка 1: Синтаксические ошибки и опечатки

При написании условий отрицания легко допустить ошибку в ключевых словах или логических операторах. Например:

Ошибка 2: Ошибки псевдонимов при копировании кода (Поле не найдено)

Зачастую в модулях проведения документов переоценки используется не один запрос, а несколько. Например, один запрос подготавливает таблицу по регистру ВзаиморасчетыСКонтрагентами, а второй — по регистру РасчетыСКонтрагентами.

Если вы скопируете условие:

И НЕ ВзаиморасчетысКонтрагентамиОстатки.Сделка Ссылка Документ.ПлатежноеПоручениеВходящее

в запрос по регистру РасчетыСКонтрагентами, где виртуальная таблица имеет псевдоним РасчетысКонтрагентамиОстатки, система выдаст ошибку: "Поле не найдено ВзаиморасчетысКонтрагентамиОстатки.Сделка". Всегда внимательно проверяйте псевдонимы таблиц в целевом запросе.

Ошибка 3: Пропажа всех данных (Коварство значений NULL и Неопределено)

Это самая частая и опасная логическая ошибка. Разработчик пишет синтаксически верное условие:

ГДЕ НЕ ВзаиморасчетысКонтрагентамиОстатки.Сделка ССЫЛКА Документ.ПлатежноеПоручениеВходящее

После этого документ проводится без ошибок, но переоценка вообще перестает рассчитываться для любых документов. Почему так происходит?

Поле Сделка является составным. Если в базе данных учет ведется "По договору в целом", то поле Сделка в регистре остатков не заполнено и содержит значение Неопределено (или NULL при левом соединении tables).

Когда СУБД выполняет проверку Сделка ССЫЛКА Документ.ПлатежноеПоручениеВходящее для пустой сделки:

  1. Проверка на соответствие типу для пустого значения или значения другого типа возвращает ЛОЖЬ.
  2. Однако в SQL-логике проверка NULL ССЫЛКА ... возвращает специальное значение NULL (неизвестность).
  3. Отрицание НЕ NULL по правилам троичной логики SQL также возвращает NULL.
  4. Секция ГДЕ пропускает в результат запроса только те строки, для которых итоговое условие вернуло строго ИСТИНА. Строки со значением NULL или ЛОЖЬ отбрасываются.

В результате запрос отфильтровал не только платежные поручения, но и вообще все записи, у которых сделка не была заполнена или имела пустую ссылку. Поскольку у большинства контрагентов взаиморасчеты ведутся без указания сделок, из запроса исчезли практически все данные.

---

Правильные способы решения задачи

Для корректного исключения конкретного типа документа из поля составного типа в запросах 1С существует несколько надежных подходов. Проанализируем каждый из них.

Способ 1: Использование конструкции ВЫРАЗИТЬ ... ЕСТЬ NULL (Рекомендуемый)

Это наиболее изящный и быстродействующий способ с точки зрения СУБД. Мы принудительно приводим поле Сделка к целевому типу документа. Если в поле находится другой документ или пустое значение, система вернет NULL. Нам остается лишь отобрать эти NULL-значения.


ГДЕ
    КурсыВалютСрезПоследних.Курс ЕСТЬ НЕ NULL
    И ВЫРАЗИТЬ(ВзаиморасчетысКонтрагентамиОстатки.Сделка КАК Документ.ПлатежноеПоручениеВходящее) ЕСТЬ NULL

Как это работает:

Способ 2: Использование операторов ТИПЗНАЧЕНИЯ и ТИП

Этот способ более нагляден для чтения кода, но может работать чуть медленнее на огромных базах данных из-за особенностей трансляции в SQL-запрос платформой 1С. Тем не менее, он абсолютно корректно обрабатывает пустые значения.


ГДЕ
    КурсыВалютСрезПоследних.Курс ЕСТЬ НЕ NULL
    И ТИПЗНАЧЕНИЯ(ВзаиморасчетысКонтрагентамиОстатки.Сделка) <> ТИП(Документ.ПлатежноеПоручениеВходящее)

В данном случае, если сделка не заполнена, ее тип (например, Неопределено) гарантированно не равен типу Документ.ПлатежноеПоручениеВходящее. Строка успешно пройдет проверку и попадет в выборку.

Способ 3: Классическая проверка ССЫЛКА с обработкой пустых значений

Если вам по какой-то причине необходимо использовать именно оператор ССЫЛКА, вы должны явно обработать ситуацию, когда поле Сделка не заполнено (равно пустой ссылке или не имеет типа):


ГДЕ
    КурсыВалютСрезПоследних.Курс ЕСТЬ НЕ NULL
    И (НЕ ВзаиморасчетысКонтрагентамиОстатки.Сделка ССЫЛКА Документ.ПлатежноеПоручениеВходящее 
        ИЛИ ВзаиморасчетысКонтрагентамиОстатки.Сделка = НЕОПРЕДЕЛЕНО)

Этот вариант более громоздкий, поэтому на практике чаще отдают предпочтение Способу 1.

---

Комплексный пример реализации в модуле документа

Давайте посмотрим, как должны выглядеть исправленные функции в модуле вашего документа переоценки валюты. Мы применим изменения к обоим запросам (по взаиморасчетам и по расчетам с контрагентами), учитывая корректные псевдонимы таблиц.


Функция ПодготовитьТаблицуПоВзаиморасчетам(СтруктураШапкиДокумента)
    
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ДатаДокумента", Новый Граница(КонецДня(Дата), ВидГраницы.Включая));
    Запрос.УстановитьПараметр("Организация", Организация);
    Запрос.УстановитьПараметр("ПоВсемОрганизациям", ПоВсемОрганизациям);
    
    Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	ВзаиморасчетысКонтрагентамиОстатки.Организация,
    |	ВзаиморасчетысКонтрагентамиОстатки.Контрагент,
    |	ВзаиморасчетысКонтрагентамиОстатки.ДоговорКонтрагента,
    |	ВзаиморасчетысКонтрагентамиОстатки.Сделка,
    |	ВзаиморасчетысКонтрагентамиОстатки.СуммаВзаиморасчетовОстаток КАК СуммаВзаиморасчетовОстаток,
    |	ВзаиморасчетысКонтрагентамиОстатки.СуммаУпрОстаток КАК СуммаУпрОстаток,
    |	ВзаиморасчетысКонтрагентамиОстатки.ДоговорКонтрагента.ВалютаВзаиморасчетов КАК Валюта,
    |	КурсыВалютСрезПоследних.Курс КАК КурсВалютыДенежныхСредств,
    |	КурсыВалютСрезПоследних.Кратность КАК КратностьВалютыДенежныхСредств
    |ИЗ
    |	РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(
    |		&ДатаДокумента,
    |		Организация = &Организация ИЛИ &ПоВсемОрганизациям
    |	) КАК ВзаиморасчетысКонтрагентамиОстатки
    |	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&ДатаДокумента, ) КАК КурсыВалютСрезПоследних
    |		ПО КурсыВалютСрезПоследних.Валюта = ВзаиморасчетысКонтрагентамиОстатки.ДоговорКонтрагента.ВалютаВзаиморасчетов
    |ГДЕ
    |	КурсыВалютСрезПоследних.Курс ЕСТЬ НЕ NULL
    |	И ВЫРАЗИТЬ(ВзаиморасчетысКонтрагентамиОстатки.Сделка КАК Документ.ПлатежноеПоручениеВходящее) ЕСТЬ NULL
    |
    |ДЛЯ ИЗМЕНЕНИЯ
    |	РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки";
    
    ТаблицаРезультата = Запрос.Выполнить.Выгрузить();
    ТаблицаРезультата.Колонки.Добавить("СуммаУпр");
    РассчитатьРазницуДляСуммыУпр(ТаблицаРезультата, СтруктураШапкиДокумента);
    
    Возврат ТаблицаРезультата;
    
КонецФункции

Функция ПодготовитьТаблицуПоРасчетам(СтруктураШапкиДокумента)
    
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ДатаДокумента", Новый Граница(КонецДня(Дата), ВидГраницы.Включая));
    Запрос.УстановитьПараметр("Организация", Организация);
    Запрос.УстановитьПараметр("ПоВсемОрганизациям", ПоВсемОрганизациям);
    
    Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	РасчетысКонтрагентамиОстатки.Организация,
    |	РасчетысКонтрагентамиОстатки.Контрагент,
    |	РасчетысКонтрагентамиОстатки.ДоговорКонтрагента,
    |	РасчетысКонтрагентамиОстатки.Сделка,
    |	РасчетысКонтрагентамиОстатки.РасчетыВозврат,
    |	РасчетысКонтрагентамиОстатки.СуммаВзаиморасчетовОстаток КАК СуммаВзаиморасчетовОстаток,
    |	РасчетысКонтрагентамиОстатки.СуммаУпрОстаток КАК СуммаУпрОстаток,
    |	РасчетысКонтрагентамиОстатки.ДоговорКонтрагента.ВалютаВзаиморасчетов КАК Валюта,
    |	КурсыВалютСрезПоследних.Курс КАК КурсВалютыДенежныхСредств,
    |	КурсыВалютСрезПоследних.Кратность КАК КратностьВалютыДенежныхСредств
    |ИЗ
    |	РегистрНакопления.РасчетыСКонтрагентами.Остатки(
    |		&ДатаДокумента, 
    |		Организация = &Организация ИЛИ &ПоВсемОрганизациям
    |	) КАК РасчетысКонтрагентамиОстатки
    |	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&ДатаДокумента, ) КАК КурсыВалютСрезПоследних
    |		ПО КурсыВалютСрезПоследних.Валюта = РасчетысКонтрагентамиОстатки.ДоговорКонтрагента.ВалютаВзаиморасчетов
    |ГДЕ
    |	КурсыВалютСрезПоследних.Курс ЕСТЬ НЕ NULL
    |	И ВЫРАЗИТЬ(РасчетысКонтрагентамиОстатки.Сделка КАК Документ.ПлатежноеПоручениеВходящее) ЕСТЬ NULL
    |
    |ДЛЯ ИЗМЕНЕНИЯ
    |	РегистрНакопления.РасчетыСКонтрагентами.Остатки";
    
    ТаблицаРезультата = Запрос.Выполнить.Выгрузить();
    ТаблицаРезультата.Колонки.Добавить("СуммаУпр");
    РассчитатьРазницуДляСуммыУпр(ТаблицаРезультата, СтруктураШапкиДокумента);
    
    Возврат ТаблицаРезультата;
    
КонецФункции

---

Важное примечание по механизму переоценки в 1С

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

  1. Регистр бухгалтерии (Хозрасчетный): Переоценка для целей бухгалтерского учета строится на остатках по счетам расчетов (60, 62, 76). Запросы к регистру бухгалтерии обычно находятся в других функциях (например, ПодготовитьТаблицуПоБухгалтерскомуУчету). Там анализируются субконто счетов. Если третьим субконто выступает "Документы расчетов с контрагентами", вам необходимо наложить аналогичный фильтр ВЫРАЗИТЬ(ХозрасчетныйОстатки.Субконто3 КАК Документ.ПлатежноеПоручениеВходящее) ЕСТЬ NULL в соответствующем запросе.
  2. Регистр "Взаиморасчеты по документам расчетов": В конфигурациях с детализированным ведением расчетов остатки могут храниться в отдельном регистре накопления ВзаиморасчетыСКонтрагентамиПоДокументамРасчетов. Если ваша база использует данный режим, обязательно найдите функции, выбирающие данные из этого регистра, и скорректируйте их запросы аналогичным образом.

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

← На главную