В процессе разработки на платформе 1С часто возникает задача сравнения двух наборов данных, например, табличных частей. Классический пример — система автоматических скидок или акций. У нас есть условия для получения бонуса (например, "купи Молоко 3 шт. и Печенье 1 шт."), и нам нужно проверить, выполнены ли эти условия в заказе клиента. Иначе говоря, как убедиться, что все строки из табличной части с условиями (ТЧ1) присутствуют в табличной части документа (ТЧ2), причем в количестве не меньшем, чем требуется?
Рассмотрим два основных подхода к решению этой задачи: итеративный (с помощью циклов) и декларативный (с помощью запроса). Проанализируем их сильные и слабые стороны.
Этот метод интуитивно понятен и часто первым приходит на ум. Его логика заключается в последовательном переборе строк из таблицы с условиями и поиске каждой из них в таблице с товарами заказа. Если хотя бы одно условие не выполняется (товар не найден или его количество недостаточно), мы прерываем проверку и фиксируем отрицательный результат.
Однако простой перебор может привести к ошибкам. Основная проблема, на которую указали на форуме, — это наличие нескольких строк с одной и той же номенклатурой в документе-заказе. Например, если условие — "Молоко 10 шт.", а в заказе две строки "Молоко 5 шт." и "Молоко 5 шт.", наивная проверка может не сработать. Чтобы избежать этого, необходимо сначала агрегировать (свернуть) данные в таблице заказа.
Посмотрим на улучшенный алгоритм:
Ложь и прерываем цикл.Рассмотрим пример кода:
// ТЧ1 - Табличная часть с условиями (например, из справочника скидок)
// ТЧ2 - Табличная часть товаров из документа (заказа)
// Шаг 1: Агрегируем товары в заказе, чтобы суммировать количество для одинаковых позиций.
// Это ключевой шаг, который решает проблему с дублирующимися строками.
ТЗ_ЗаказСвернутая = ТЧ2.Выгрузить();
ТЗ_ЗаказСвернутая.Свернуть("Номенклатура", "Количество");
// Шаг 2: Проверяем выполнение каждого условия
ФлагВсеУсловияВыполнены = Истина;
Для Каждого СтрокаУсловия Из ТЧ1 Цикл
// Ищем номенклатуру из условия в свернутой таблице заказа
ПараметрыПоиска = Новый Структура("Номенклатура", СтрокаУсловия.Номенклатура);
НайденныеСтроки = ТЗ_ЗаказСвернутая.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() = 0 Тогда
// Товар из условия вообще не найден в заказе
ФлагВсеУсловияВыполнены = Ложь;
Прервать;
Иначе
// Товар найден, теперь проверяем количество.
// Так как мы свернули таблицу, найденная строка будет только одна.
НайденнаяСтрокаЗаказа = НайденныеСтроки[0];
Если НайденнаяСтрокаЗаказа.Количество < СтрокаУсловия.Количество Тогда
// Количество в заказе меньше требуемого по условию
ФлагВсеУсловияВыполнены = Ложь;
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если ФлагВсеУсловияВыполнены Тогда
Сообщить("Все условия выполнены, можно добавлять подарок!");
Иначе
Сообщить("Условия для получения подарка не выполнены.");
КонецЕсли;
Преимущества:
Недостатки:
НайтиСтроки() внутри цикла для больших таблиц (сотни и тысячи строк) будет работать значительно медленнее, чем один запрос.Это наиболее правильный, производительный и масштабируемый способ решения подобных задач в 1С. Вся логика сравнения и вычислений переносится на сторону системы управления базами данных (СУБД), что работает на порядки быстрее, чем обработка данных в цикле средствами языка 1С.
Логику запроса построим "от обратного": мы будем искать те условия, которые не выполняются. Если в результате запроса мы не получим ни одной строки, это будет означать, что все условия выполнены.
Разберем по шагам:
ВТ_Условия.ВТ_Заказ. При этом сразу же группируем данные по номенклатуре и суммируем количество, чтобы решить проблему дублирующихся строк.ЛЕВОЕ СОЕДИНЕНИЕ таблицы ВТ_Условия с таблицей ВТ_Заказ по полю номенклатуры.ГДЕ ищем ситуации, когда условие не выполнено:
ВТ_Заказ будет равно NULL).Полностью рабочий и прокомментированный код:
// ТЧ1 - Табличная часть с условиями
// ТЧ2 - Табличная часть товаров из заказа
// Создаем запрос и менеджер временных таблиц
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Помещаем таблицу с условиями во временную таблицу (ВТ)
Запрос.Текст =
"ВЫБРАТЬ
| Условия.Номенклатура КАК Номенклатура,
| Условия.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Условия
|ИЗ
| &ТЧ_Условия КАК Условия";
Запрос.УстановитьПараметр("ТЧ_Условия", ТЧ1.Выгрузить());
Запрос.Выполнить();
// Помещаем таблицу заказа во вторую ВТ, сразу же сворачивая ее
Запрос.Текст =
"ВЫБРАТЬ
| Заказ.Номенклатура КАК Номенклатура,
| СУММА(Заказ.Количество) КАК Количество
|ПОМЕСТИТЬ ВТ_Заказ
|ИЗ
| &ТЧ_Заказ КАК Заказ
|
|СГРУППИРОВАТЬ ПО
| Заказ.Номенклатура";
Запрос.УстановитьПараметр("ТЧ_Заказ", ТЧ2.Выгрузить());
Запрос.Выполнить();
// Финальный запрос: ищем НЕвыполненные условия
Запрос.Текст =
"ВЫБРАТЬ
| Условия.Номенклатура
|ИЗ
| ВТ_Условия КАК Условия
| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Заказ КАК Заказ
| ПО Условия.Номенклатура = Заказ.Номенклатура
|ГДЕ
| // Условие 1: Товар из условия не найден в заказе
| Заказ.Номенклатура ЕСТЬ NULL
| // Условие 2: Товар найден, но его количество в заказе меньше требуемого
| ИЛИ Условия.Количество > Заказ.Количество";
РезультатЗапроса = Запрос.Выполнить();
// Проверяем результат
Если РезультатЗапроса.Пустой() Тогда
Сообщить("Все условия выполнены!");
Иначе
Сообщить("Не все условия выполнены.");
// При необходимости можно вывести, какие именно условия не выполнились
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Не выполнено условие для номенклатуры: " + Выборка.Номенклатура);
КонецЦикла;
КонецЕсли;
// Важно! Менеджер временных таблиц нужно закрывать для освобождения ресурсов
Запрос.МенеджерВременныхТаблиц.Закрыть();
Преимущества:
Таким образом, хотя решение через циклы может показаться проще на первый взгляд, подход с использованием запросов является промышленным стандартом в 1С для подобных задач, обеспечивая производительность, надежность и простоту поддержки кода в долгосрочной перспективе.