Как сравнить табличные части Счета на оплату и Реализации в 1С и вывести разницу?

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

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

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

Проблема полного соединения (FULL JOIN) в запросах 1С

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

  1. Проблема дублирования строк: Если в Счете одна и та же номенклатура указана в нескольких строках (например, с разной ценой, складами или просто введена дважды), то прямое соединение свяжет каждую строку из Счета с каждой строкой из Реализации. Произойдет так называемое «декартово произведение», из-за чего количество товара в отчете умножится, и мы получим недостоверные данные.
  2. Низкая производительность: Операция ПОЛНОЕ СОЕДИНЕНИЕ (FULL OUTER JOIN) является одной из самых тяжелых для SQL-сервера. При больших объемах данных такой отчет будет выполняться крайне медленно.
  3. Сложная обработка значений NULL: При полном соединении, если товара нет в одном из документов, поля второго документа вернут значение NULL. Любые арифметические операции с NULL (например, КоличествоСчет - КоличествоРеализация) также вернут NULL, если их предварительно не обработать функцией ЕСТЬNULL.

Для демонстрации того, как делать не нужно, посмотрим на пример некорректного запроса:


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

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

Оптимальный подход: ОБЪЕДИНИТЬ ВСЕ и Группировка

Для того чтобы избежать декартова произведения строк и повысить скорость выполнения отчета, в профессиональной разработке 1С применяется паттерн ОБЪЕДИНИТЬ ВСЕ с последующей группировкой. Этот метод гарантирует корректность данных при любом количестве одинаковых строк в документах.

Разберем алгоритм по шагам:

  1. В первом запросе выбираем данные из табличной части Счета. Поле Количество выводим как положительное число (со знаком плюс) и называем его КоличествоСчет. Вторую колонку КоличествоРеализация заполняем нулем.
  2. Во втором запросе через конструкцию ОБЪЕДИНИТЬ ВСЕ выбираем данные из табличной части Реализации. Поле Количество выводим как КоличествоРеализация, а колонку КоличествоСчет заполняем нулем. Если мы хотим получить разницу простым суммированием, то количество из реализации можно сразу выбрать со знаком минус.
  3. Помещаем объединенные данные во временную таблицу или во вложенный запрос и производим группировку по ключевым полям (в простейшем случае — по номенклатуре), суммируя показатели количества.

Давайте посмотрим на практический пример реализации такого запроса:


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

        ОБЪЕДИНИТЬ ВСЕ

        // Шаг 2: Выбираем данные из Реализации
        ВЫБРАТЬ
            РеализацияТовары.Номенклатура КАК Номенклатура,
            0 КАК КоличествоСчет,
            РеализацияТовары.Количество КАК КоличествоРеализация
        ИЗ
            Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТовары
        ГДЕ
            РеализацияТовары.Ссылка = &СсылкаРеализация
    ) КАК ВЗ

СГРУППИРОВАТЬ ПО
    ВЗ.Номенклатура

Этот запрос является абсолютно безопасным. Он корректно свернет все дублирующиеся строки внутри каждого документа, а затем сопоставит суммарные показатели по каждой номенклатуре.

Учет специфики учета: Характеристики, Серии и Упаковки

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

Использование регистров накопления (Best Practice)

Обращение напрямую к табличным частям документов оправдано только в простых базах данных (например, в некоторых версиях 1С:Бухгалтерия) или при жесткой связи «один счет — одна реализация». В крупных торговых и производственных базах такой подход считается плохим тоном (антипаттерном) по следующим причинам:

Для контроля отгрузок в типовых решениях используются специальные регистры накопления (например, Заказы клиентов или Распоряжения на отгрузку). Когда проводится Счет (Заказ), он делает приход в этот регистр. Реализация при проведении делает расход.

Если мы хотим узнать, что осталось отгрузить по конкретному счету, нам достаточно обратиться к виртуальной таблице Остатки этого регистра с отбором по нашему документу-основанию:


ВЫБРАТЬ
    ЗаказыКлиентовОстатки.Номенклатура КАК Номенклатура,
    ЗаказыКлиентовОстатки.Характеристика КАК Характеристика,
    ЗаказыКлиентовОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
    РегистрНакопления.ЗаказыКлиентов.Остатки(, ЗаказКлиента = &СсылкаСчет) КАК ЗаказыКлиентовОстатки

Преимущества работы через регистры:

  1. Высокая скорость работы запроса (БД хранит уже рассчитанные остатки).
  2. Автоматически учитываются все связанные реализации, корректировки и возвраты.
  3. Если остаток равен 0 — значит, заказ отгружен полностью. Если больше нуля — это то, что осталось доотгрузить.

Особенности реализации в Системе компоновки данных (СКД)

Если вы разрабатываете отчет в СКД, у вас есть два пути интеграции данных:

Вариант 1: Один запрос с UNION ALL (Рекомендуемый). Мы пишем один запрос (описанный во втором разделе статьи) и передаем его в СКД в качестве набора данных - запроса. СКД отлично справляется с группировками и расчетом ресурсов.

Вариант 2: Два набора данных и связь между ними. Вы можете создать один набор данных для Счета и второй для Реализации, а затем связать их на вкладке «Связи наборов данных». Однако помните важную деталь: связь наборов данных в СКД работает по принципу ЛЕВОГО СОЕДИНЕНИЯ (LEFT JOIN). Если в реализации появится позиция товара, которой вообще не было в счете, при левом соединении (где левым источником выступает Счет) этот товар просто пропадет из отчета. Поэтому для полноценного двустороннего сравнения всегда надежнее использовать единый запрос с ОБЪЕДИНИТЬ ВСЕ.

Интерактивное сравнение на уровне интерфейса

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

Подводя итог, отметим: для простых задач сопоставления табличных частей документов в реальном времени всегда используйте схему запроса со сбором данных через ОБЪЕДИНИТЬ ВСЕ и последующим суммированием. Для крупных и масштабируемых систем всегда отдавайте приоритет получению остатков по регистрам накопления.

← На главную