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