В процессе разработки отчетов или механизмов сверки данных в системе «1С:Предприятие» часто возникает задача выявления «зависших» долгов. Типичный сценарий: заказ клиента формально считается выполненным (отгруженным), но в учетной системе по нему все еще числится дебиторская или кредиторская задолженность. Для решения этой задачи нам необходимо сопоставить данные двух регистров накопления: ЗаказыКлиентов и РасчетыСКлиентами. В данной статье мы детально разберем, как правильно построить запрос, чтобы получить список закрытых заказов, по которым остались незавершенные расчеты.
Рассмотрим структуру метаданных, с которыми нам предстоит работать. У нас есть два регистра накопления остатков:
Заказ, Ресурс: Сумма. Наличие остатка по этому регистру означает, что заказ еще не полностью отгружен или не аннулирован. Если остаток равен нулю (запись отсутствует), заказ считается «закрытым» с точки зрения товародвижения.Заказ, Ресурс: Сумма. Остаток по этому регистру показывает сумму фактического долга клиента перед нами или нашей задолженности перед ним.Наша цель — получить таблицу с колонками Заказ и СуммаОстаток (из регистра расчетов), но только для тех заказов, по которым остаток в регистре ЗаказыКлиентов равен нулю.
Проанализируем самый очевидный способ реализации задачи. Мы можем использовать левое соединение двух виртуальных таблиц остатков. Основная таблица — это остатки взаиморасчетов, к которой мы «подтягиваем» остатки заказов.
Разберем логику построения такого запроса:
ВЫБРАТЬ
РасчетыОстатки.Заказ КАК Заказ,
РасчетыОстатки.СуммаОстаток КАК СуммаДолга
ИЗ
РегистрНакопления.РасчетыСКлиентами.Остатки(&Период, ) КАК РасчетыОстатки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыКлиентов.Остатки(&Период, ) КАК ЗаказыОстатки
ПО РасчетыОстатки.Заказ = ЗаказыОстатки.Заказ
ГДЕ
ЗаказыОстатки.Заказ ЕСТЬ NULL
Почему это работает? Мы берем все заказы, по которым есть долги. Пытаемся найти эти же заказы в таблице «активных» (неотгруженных) заказов. Если заказ в таблице ЗаказыОстатки не найден (значение NULL), это означает, что остатка по заказу нет, а значит, он закрыт. Именно такие записи нам и нужны.
Рассмотрим ситуацию, когда база данных содержит миллионы записей. Прямое соединение двух виртуальных таблиц «на лету» может работать медленно, так как СУБД приходится выполнять сложные операции соединения больших объемов данных в оперативной памяти. Проанализируем более профессиональный подход с использованием временных таблиц и индексации.
Пошаговый алгоритм реализации:
Заказ.ЗаказыКлиентов) во вторую временную таблицу и также проиндексируем.
// Шаг 1: Получаем долги
ВЫБРАТЬ
РасчетыОстатки.Заказ КАК Заказ,
РасчетыОстатки.СуммаОстаток КАК СуммаОстаток
ПОМЕСТИТЬ ВТ_Долги
ИЗ
РегистрНакопления.РасчетыСКлиентами.Остатки(&Период, ) КАК РасчетыОстатки
ИНДЕКСИРОВАТЬ ПО
Заказ
;
// Шаг 2: Получаем активные (не закрытые) заказы
ВЫБРАТЬ
ЗаказыОстатки.Заказ КАК Заказ
ПОМЕСТИТЬ ВТ_АктивныеЗаказы
ИЗ
РегистрНакопления.ЗаказыКлиентов.Остатки(&Период, ) КАК ЗаказыОстатки
ИНДЕКСИРОВАТЬ ПО
Заказ
;
// Шаг 3: Соединяем и фильтруем
ВЫБРАТЬ
ВТ_Долги.Заказ,
ВТ_Долги.СуммаОстаток
ИЗ
ВТ_Долги КАК ВТ_Долги
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_АктивныеЗаказы КАК ВТ_АктивныеЗаказы
ПО ВТ_Долги.Заказ = ВТ_АктивныеЗаказы.Заказ
ГДЕ
ВТ_АктивныеЗаказы.Заказ ЕСТЬ NULL
Разберем несколько критически важных моментов, которые влияют на корректность и скорость работы запроса:
1. Условие ЕСТЬ NULL vs ЕСТЬNULL(..., 0) = 0. Для исключения записей всегда лучше использовать ГДЕ Таблица.Поле ЕСТЬ NULL. Это условие проверяет именно отсутствие записи во второй таблице при левом соединении. Использование функции ЕстьNULL с последующим сравнением с нулем может привести к тому, что СУБД не сможет эффективно использовать индексы, что замедлит выполнение запроса.
2. Использование параметров виртуальных таблиц. Выясним причину, по которой всегда нужно передавать параметр Период в виртуальные таблицы Остатки. Если параметр не указан, система рассчитывает остатки на текущую секунду (включая будущие документы). В отчетах же чаще всего требуется срез данных на начало дня или конкретную дату. Кроме того, расчет остатков на конкретную дату по итогам работает быстрее, чем расчет «на сейчас».
3. Проблема «копеек» и отрицательных остатков. В реальном учете возможны ситуации, когда по заказу в регистре ЗаказыКлиентов висит остаток 0.01 (из-за округлений) или отрицательное число (перевыполнение). В таком случае условие ЕСТЬ NULL не сработает, так как запись в регистре формально существует. Если ваша бизнес-логика допускает такие случаи, во вторую временную таблицу следует добавить условие:
ГДЕ
ЗаказыОстатки.СуммаОстаток > 0
Это позволит считать заказ закрытым, даже если по нему есть перепоставка (отрицательный остаток) или если по нему вовсе нет записей.
Проанализируем ситуацию с точки зрения типовых конфигураций (ERP 2.5, УТ 11). В этих системах отсутствие остатка в регистре ЗаказыКлиентов — это лишь технический признак завершения отгрузки. Однако у документа Заказ клиента часто есть реквизит Статус или специальный регистр сведений СостоянияЗаказовКлиентов. Если ваша задача — найти долги именно по тем заказам, которые менеджер вручную перевел в статус «Закрыт», логику запроса нужно менять. В этом случае вместо виртуальной таблицы ЗаказыКлиентов.Остатки следует использовать соединение с таблицей документа или соответствующим регистром сведений по статусам.
Таким образом, мы рассмотрели как базовый вариант решения задачи через ЛЕВОЕ СОЕДИНЕНИЕ, так и оптимизированный путь с использованием временных таблиц, который является стандартом качественной разработки на платформе 1С.