Как в 1С определить первый документ сотрудника за месяц и рассчитать накопительную сумму

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

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

Вариант 1. Запрос к таблице документов с отбором по периоду и сотруднику

Самый наглядный способ выяснить, были ли уже оформлены документы по сотруднику в текущем месяце, — выполнить запрос к таблице документов за период с начала месяца по текущую дату. При этом важно учитывать следующие нюансы:

  1. Исключение текущего документа: если документ уже записан в базу, прямой запрос посчитает его самого. Необходимо обязательно исключать текущую ссылку через условие Ссылка <> &ТекущаяСсылка.
  2. Учет проведенности: как правило, учитывать нужно только реально проведенные документы, исключая черновики и помеченные на удаление объекты с помощью условия Проведен = Истина.
  3. Защита от значения NULL: агрегатная функция СУММА() в языке запросов возвращает NULL, если подходящих строк не найдено. Чтобы не получить ошибку несовпадения типов при арифметических операциях, используем функцию ЕСТЬNULL().

Посмотрим на пример текста запроса, который одновременно получает количество предыдущих документов и накопленную сумму за текущий месяц:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Акт.Ссылка) КАК КоличествоДокументов,
    |	ЕСТЬNULL(СУММА(Акт.СуммаДокумента), 0) КАК СуммаПоДокументам
    |ИЗ
    |	Документ.Акт КАК Акт
    |ГДЕ
    |	Акт.Дата МЕЖДУ &НачалоМесяца И &ТекущаяДата
    |	И Акт.Сотрудник = &Сотрудник
    |	И Акт.Проведен = ИСТИНА
    |	И Акт.Ссылка <> &ТекущаяСсылка";

Запрос.УстановитьПараметр("НачалоМесяца", НачалоМесяца(Объект.Дата));
Запрос.УстановитьПараметр("ТекущаяДата", Объект.Дата);
Запрос.УстановитьПараметр("Сотрудник", Объект.Сотрудник);
Запрос.УстановитьПараметр("ТекущаяСсылка", Объект.Ссылка);

Выборка = Запрос.Выполнить().Выбрать();

Как получить значение из запроса без цикла

Разберем типичную ошибку начинающих разработчиков. Конструкция РезультатЗапроса = Запрос.Выполнить().Выбрать().Следующий; возвращает булево значение (Истина или Ложь), сообщающее об успешности перехода к следующей записи, а не сами данные. В результате сложение числа с булевым значением приводит к некорректным результатам (прибавлению единицы).

Если запрос возвращает ровно одну итоговую строку, использовать цикл Пока ... Цикл не требуется. Достаточно проверить результат метода Следующий() через условие Если и обратиться к именованным полям выборки:


Если Выборка.Следующий() Тогда
    Если Выборка.КоличествоДокументов = 0 Тогда
        // Это первый документ сотрудника в текущем месяце
        ИтоговаяСумма = Объект.СуммаДокумента - 500;
    Иначе
        // Документ второй или последующий: берем сумму текущего + накопленный итог
        ИтоговаяСумма = Объект.СуммаДокумента + Выборка.СуммаПоДокументам;
    КонецЕсли;
КонецЕсли;

Тонкости фильтрации по проведенности документов

Проанализируем работу с отбором по реквизиту Проведен. В языке запросов 1С поле Проведен является булевым. Фильтрацию можно задавать двумя способами:

  1. Явным указанием логического значения: ГДЕ Акт.Проведен = ИСТИНА или через параметр ГДЕ Акт.Проведен = &Проведен.
  2. Сокращенной проверкой: ГДЕ Акт.Проведен.

Если в результат запроса все равно попадают непроведенные документы, проверьте расстановку логических скобок в секции ГДЕ при использовании оператора ИЛИ. Не сгруппированные условия могут перекрывать проверку проведения.

Архитектурные ограничения и надежные подходы

Рассмотренный выше вариант с прямым запросом к таблице документов прост в реализации, однако имеет архитектурные риски:

  1. Проведение документов задним числом: если пользователь изменит или вставит документ прошлой датой, суммы в ранее проведенных документах месяца автоматически не пересчитаются, что приведет к нарушению целостности данных.
  2. Конкурентная запись: при параллельной работе двух пользователей два документа по одному сотруднику могут одновременно выполнить запрос, посчитать себя первыми и применить скидку дважды.
  3. Совпадение по времени: если документы создаются на одну и ту же дату/секунду, сравнение по дате может дать непредсказуемый результат. В таких случаях надежнее выполнять сравнение по объекту МоментВремени.

Для построения отказоустойчивой системы в типовых и высоконагруженных конфигурациях рекомендуется переносить хранение накопительных итогов в Регистры накопления (оборотов) с измерениями Организация и Сотрудник. В процедуре ОбработкаПроведения устанавливается исключительная управляемая блокировка на измерения регистра по сотруднику, считываются накопленные обороты с начала месяца из виртуальной таблицы Обороты, выполняется расчет и формируются новые движения.

← На главную