Как в 1С:ЗУП получить сумму разового начисления сотрудника вместо значения из регистра свойств?

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

При доработке отчетов и внешних обработок в конфигурации «1С:Зарплата и Управление Персоналом 2.5» разработчики часто сталкиваются с ситуацией, когда данные по компенсациям (например, за аренду личного автотранспорта) ранее считывались из дополнительных реквизитов (регистра сведений ЗначенияСвойствОбъектов), но бизнес-логика изменилась, и реальные начисления стали вводиться расчетными документами. Попытки переписать функцию напрямую на обращение к документу нередко приводят к ошибкам Преобразование значения к типу Число не может быть выполнено, неверной группировке сумм или дублированию записей. Разберем по шагам, как исправить логику функции, корректно построить запрос и получить точные числовые данные.

Анализ исходной проблемы и типичных ошибок

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


Функция АрендаАТ(Сотрудник1)
    Значения = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
    Значения.Отбор.Объект.Установить(Сотрудник1);
    Значения.Отбор.Свойство.Установить(ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("0009"));
    Значения.Прочитать();
    Если Значения.Количество() > 0 Тогда
        Возврат(Значения[0].Значение);
    Иначе
        Возврат(0);
    КонецЕсли;
КонецФункции

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

  1. Выборка вложенных таблиц в запросе. Использование вложенных конструкций вида РегистрацияРазовыхНачислений.ДополнительныеНачисления.(ВидРасчета, Результат) возвращает объект типа ТаблицаЗначений внутри поля строки. При попытке прибавить такую таблицу к числу платформа закономерно выдает исключение Преобразование значения к типу Число не может быть выполнено. Источник данных нужно указывать как плоскую таблицу: Документ.РегистрацияРазовыхНачисленийРаботниковОрганизаций.ДополнительныеНачисления.
  2. Отсутствие оператора Возврат. Если функция выполняет выборку, но в теле отсутствует возврат полученного значения или выборка оказалась пустой без инициализации переменной, функция возвращает Неопределено, что также ломает математические операции в вызывающем коде.
  3. Ошибки в отборах по периоду. Передача реквизита Дата, когда он не определен в контексте выполнения, либо установка одинаковых границ через КонецМесяца искажает условия отбора или приводит к аварийному завершению модуля.
  4. Отсутствие агрегации и группировки. Если в расчетном месяце по сотруднику создано несколько документов или строк с одним и тем же видом начисления, перебор в цикле с простым присвоением оставит только значение последней строки, либо при неверных условиях соединений выдаст общую сумму по всем сотрудникам сразу.

Вариант 1. Запрос к табличной части документа «Регистрация разовых начислений»

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

Разберем готовый пример функции с учетом проверки на пустые значения:


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

Обратим внимание на важные детали:

  1. Мы использовали функцию ЕСТЬNULL() и агрегатную функцию СУММА(). Это гарантирует, что даже при нескольких записях за месяц сумма будет корректно посчитана, а при отсутствии записей вернется ноль, а не значение NULL.
  2. В качестве периода отбора начислений в типовой зарплате используется реквизит документа ПериодРегистрации (первое число расчетного месяца). Отбор по полю Ссылка.Дата часто отсекает документы, введенные в первых числах следующего месяца за предыдущий расчетный период.

Вариант 2. Архитектурно правильное получение данных из регистра расчета

Проанализируем архитектуру конфигурации «Зарплата и Управление Персоналом». Документ является лишь средством ввода данных. Проведенный документ разовых начислений делает движения по специализированному регистру расчета ДополнительныеНачисленияРаботниковОрганизаций. Запрос к регистру надежнее, так как он автоматически отсекает непроведенные документы, учитывает исправления, сторнирующие записи и фактические расчетные итоги.

Посмотрим на пример запроса к регистру расчета:


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

Оптимизация: избавляемся от запроса в цикле

Вызов функции с запросом внутри цикла обхода строк отчета (например, при выгрузке сотрудников в лист Excel) является классическим антипаттерном («запрос в цикле»). При штате даже в 100 человек это порождает 100 отдельных обращений к СУБД, что существенно замедляет формирование отчета.

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


// 1. Формируем единый запрос по всем сотрудникам за расчетный месяц
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   РегНачисления.Сотрудник КАК Сотрудник,
|   СУММА(РегНачисления.Результат) КАК СуммаАренды
|ИЗ
|   РегистрРасчета.ДополнительныеНачисленияРаботниковОрганизаций КАК РегНачисления
|ГДЕ
|   РегНачисления.ВидРасчета = &ВидРасчета
|   И РегНачисления.ПериодРегистрации = НАЧАЛОПЕРИОДА(&ПериодРегистрации, МЕСЯЦ)
|СГРУППИРОВАТЬ ПО
|   РегНачисления.Сотрудник";

Запрос.УстановитьПараметр("ВидРасчета", ПланыВидовРасчета.ДополнительныеНачисленияОрганизаций.НайтиПоНаименованию("Аренда автотранспорта"));
Запрос.УстановитьПараметр("ПериодРегистрации", ДатаОтчета);

ТаблицаСумм = Запрос.Выполнить().Выгрузить();

// 2. Помещаем результат в Соответствие для быстрого поиска по ключу-ссылке
КартаАренды = Новый Соответствие;
Для Каждого Стр Из ТаблицаСумм Цикл
    КартаАренды.Вставить(Стр.Сотрудник, Стр.СуммаАренды);
КонецЦикла;

// 3. Быстрое заполнение таблицы/Excel в основном цикле без обращений к базе
Пока ВыборкаСотрудников.Следующий() Цикл
    
    СуммаСотрудника = КартаАренды[ВыборкаСотрудников.Ссылка];
    Если СуммаСотрудника = Неопределено Тогда
        СуммаСотрудника = 0;
    КонецЕсли;
    
    ВсегоСтрок = ВсегоСтрок + 1;
    Лист.Cells(ВсегоСтрок, 9).Value = СуммаСотрудника;
    ИтоговаяСумма = ИтоговаяСумма + СуммаСотрудника;
    
КонецЦикла;

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

← На главную