Как правильно проверить на соответствие содержимое двух табличных частей документа в 1С?

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

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

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

Анализ проблемы и выбор метода сравнения

На первый взгляд кажется, что задачу можно решить простым перебором строк или использованием ПОЛНОЕ СОЕДИНЕНИЕ в запросе. Однако при работе с табличными частями (ТЧ) мы сталкиваемся с рядом нюансов:

  1. Дублирование строк: В одной таблице товар может быть разбит на две строки, а в другой — указан одной строкой с общей суммой. Прямое соединение «строка к строке» здесь выдаст ошибку.
  2. Отсутствующие позиции: Если в одной таблице товар есть, а в другой нет, при соединении появятся значения NULL, которые требуют дополнительной обработки функцией ЕСТЬNULL.
  3. Производительность: Сравнение больших таблиц в памяти (обходом коллекций) нагружает сервер приложений, тогда как запрос выполняет эту операцию на уровне СУБД значительно быстрее.

Проанализируем наиболее изящный и надежный способ решения — метод «зеркального» суммирования через объединение таблиц.

Оптимальное решение: Использование ОБЪЕДИНИТЬ ВСЕ и группировки

Суть метода заключается в создании единого набора данных из двух таблиц. Для первой таблицы мы берем количество со знаком «плюс», а для второй — со знаком «минус». Если содержимое таблиц идентично, то после группировки по ключевым полям (Номенклатура, Характеристика) сумма количеств по каждой позиции должна обнулиться.

Разберем реализацию этого алгоритма на примере запроса:


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

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

    ВЫБРАТЬ
        ТЧ_Товары.Номенклатура,
        ТЧ_Товары.Характеристика,
        -ТЧ_Товары.Количество
    ИЗ
        Документ.ПредварительныйЗаказПоставщику.Товары КАК ТЧ_Товары
    ГДЕ
        ТЧ_Товары.Ссылка = &Ссылка) КАК Табличка

СГРУППИРОВАТЬ ПО
    Табличка.Номенклатура,
    Табличка.Характеристика

ИМЕЮЩИЕ
    СУММА(Табличка.Количество) <> 0

Рассмотрим подробнее, как работает этот механизм:

  1. Первый блок выборки: Выбирает данные из фактической таблицы (например, ОтметкиСклада) с положительным числом в поле Количество.
  2. Второй блок выборки: Выбирает данные из плановой таблицы (Товары), но инвертирует количество, добавляя оператор «минус» (-ТЧ_Товары.Количество).
  3. Группировка: Схлопывает все одинаковые пары «Номенклатура + Характеристика» в одну строку, складывая их количества.
  4. Условие ИМЕЮЩИЕ: Это ключевой фильтр. Мы оставляем в результате только те строки, где итоговая сумма не равна нулю. Если таблицы равны, запрос вернет пустой результат.

Реализация проверки в программном коде

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


Запрос = Новый Запрос;
Запрос.Текст = "...(текст запроса выше)...";
Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);

РезультатЗапроса = Запрос.Выполнить();

Если РезультатЗапроса.Пустой() Тогда
    // Таблицы идентичны
    Объект.Статус = Перечисления.СтатусыЗаказов.Закрыт;
Иначе
    // Найдены расхождения
    Сообщить("Обнаружены расхождения между планом и фактом поступления!");
    
    // При необходимости можно вывести список расхождений
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщить("Товар: " + Выборка.Номенклатура + " Разница: " + Выборка.ПолеРазницы);
    КонецЦикла;
КонецЕсли;

Почему ПОЛНОЕ СОЕДИНЕНИЕ — это не всегда правильно?

Рассмотрим ситуацию, которую часто обсуждают на форумах: использование ПОЛНОЕ СОЕДИНЕНИЕ. Проанализируем типичный неудачный пример кода:


// ПРИМЕР ТОГО, КАК ДЕЛАТЬ НЕ РЕКОМЕНДУЕТСЯ
ИЗ ТЧ1 ПОЛНОЕ СОЕДИНЕНИЕ ТЧ2 ПО ТЧ1.Номенклатура = ТЧ2.Номенклатура 
ГДЕ ТЧ1.Количество <> ТЧ2.Количество

Проблема такого подхода:

Важные дополнения при работе с характеристиками и типами

При использовании данного метода важно учитывать следующие моменты:

  1. Пустые характеристики: Если в вашей конфигурации не всегда используются характеристики, убедитесь, что поле Характеристика имеет один и тот же тип в обеих таблицах. Если в одной таблице это ссылка на справочник, а в другой поле может отсутствовать, запрос может работать некорректно.
  2. Точность чисел: Для весового товара (тип Число с большой точностью) иногда может возникать микроскопическая разница из-за особенностей хранения данных. В таких случаях в секции ИМЕЮЩИЕ вместо <> 0 можно использовать проверку на вхождение в допустимый диапазон погрешности.
  3. Отбор по статусам: Если в документе есть строки, помеченные на удаление или имеющие статус «Отменено», их необходимо исключить из выборки в обоих блоках ОБЪЕДИНИТЬ ВСЕ, добавив соответствующие условия в ГДЕ.

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

← На главную