При разработке обработок периодического расчета бонусов, кэшбэка или ретро-бонусов разработчики часто сталкиваются с ситуацией, когда процедура завершается без видимых ошибок, но начисление не происходит. При пошаговой отладке выясняется, что указатель обхода выборки даже не заходит внутрь цикла Пока Выборка.Следующий() Цикл. Разберем по шагам, почему возникает такое поведение, как исправить ошибки в тексте запроса и параметрах, а также как грамотно выстроить учет начислений в системе 1С.
Если отладчик сразу переходит на строку КонецЦикла, это означает, что объект РезультатЗапроса пуст. Выборка не содержит ни одной записи, удовлетворяющей условиям секции ГДЕ.
Выясним причину с помощью первичной проверки:
РезультатЗапроса = Запрос.Выполнить();
Сообщить(РезультатЗапроса.Пустой());
Истина, значит, в информационной базе нет записей, подходящих под установленные параметры.
НачПериода = НачалоДня(ТекущаяДата() - 86400);
Вычитание 86400 секунд (суток) из текущей даты дает интервал всего в один день, а не в месяц. Если за предыдущие сутки проведенных документов по дилерам с признаком кэшбэка не было, запрос вернет пустой результат.Запрос.УстановитьПараметр("Истина", Истина); является избыточной. В языке запросов 1С булевы литералы ИСТИНА и ЛОЖЬ поддерживаются напрямую, а для проверки булевых реквизитов достаточно написать само имя поля: ДилЗаказ.Проведен.Рассмотрим подробнее установку интервалов дат. Использование конструкции МЕЖДУ &НачПериода И &КонПериода включает обе граничные даты. При регулярном запуске обработки (например, ежемесячно) документы, проведенные ровно в полночь на стыке периодов, могут быть посчитаны дважды.
Кроме того, если передавать в параметр дату без вызова функции КонецДня(), документы, проведенные в течение последнего дня периода, не попадут в выборку, так как время даты без явного указания интерпретируется как 00:00:00.
Разберем безопасный вариант задания периода через строгое сравнение:
// Период расчета: предыдущий месяц целиком
НачПериода = НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1));
КонПериода = НачалоМесяца(ТекущаяДата()); // Правая граница не включается
Запрос.УстановитьПараметр("НачПериода", НачПериода);
Запрос.УстановитьПараметр("КонПериода", КонПериода);
В тексте запроса условие по дате задаем следующим образом:
ГДЕ
ДилЗаказ.Проведен
И ДилЗаказ.Дилер.Кэшбэк
И ДилЗаказ.Дата >= &НачПериода
И ДилЗаказ.Дата < &КонПериода
Проанализируем текст запроса. Расчет суммы бонуса эффективнее выполнять непосредственно в СУБД, а не вычислять в цикле на клиенте или сервере. При этом финансовые расчеты обязательно требуют явного округления с помощью функции ВЫРАЗИТЬ, чтобы исключить погрешности дробных чисел.
Посмотрим на исправленный и оптимизированный пример запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДилЗаказ.Дилер КАК Дилер,
| СУММА(ДилЗаказ.Сумма_Итог) КАК СуммаПродаж,
| ВЫРАЗИТЬ(СУММА(ДилЗаказ.Сумма_Итог) * 0.06 КАК ЧИСЛО(15, 2)) КАК СуммаКэшбэка
|ИЗ
| Документ.ДилЗаказ КАК ДилЗаказ
|ГДЕ
| ДилЗаказ.Проведен
| И ДилЗаказ.Дилер.Кэшбэк
| И ДилЗаказ.Дата >= &НачПериода
| И ДилЗаказ.Дата < &КонПериода
|СГРУППИРОВАТЬ ПО
| ДилЗаказ.Дилер";
Запрос.УстановитьПараметр("НачПериода", НачПериода);
Запрос.УстановитьПараметр("КонПериода", КонПериода);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(СтрШаблон("Дилер: %1, Кэшбэк: %2", Выборка.Дилер, Выборка.СуммаКэшбэка));
КонецЦикла;
При отладке сложных выборок удобно выгружать результат в таблицу значений методом РезультатЗапроса.Выгрузить(), что позволяет быстро просматривать всю результирующую таблицу в отладчике.
Прямой расчет начислений по таблицам документов несет в себе критические методологические риски. Проанализируем основные архитектурные недостатки такого подхода:
Кэшбэк в справочнике Контрагенты является статическим. Если флаг включили в середине месяца, обработка задним числом рассчитает бонус по всем заказам с начала периода, что исказит учет. Решение: использование периодического регистра сведений с историей действия условий программы лояльности.Продажи или специализированного регистра УчитываемыеПродажи).НачислениеБонусов.Для исключения человеческого фактора начисление бонусов переносят из интерактивной внешней обработки в серверный модуль, вызываемый по расписанию через РегламентныеЗадания. Код выполняется в транзакции с предварительной установкой управляемой блокировки на расчетный период, предотвращая одновременный двойной пересчет данных несколькими сеансами.