При разработке отчетов по расчету страховых взносов в программах "1С:Зарплата и управление персоналом" или "1С:ERP" разработчики часто сталкиваются с задачей получения списка сотрудников, за которых уплачивались дополнительные взносы за вредные или тяжелые условия труда. Однако стандартный запрос к регистру накопления СтраховыеВзносыИсчисленные может преподнести сюрприз: вместо ожидаемых имен сотрудников в результате запроса выводятся пустые строки.
В этой статье мы подробно разберем, почему возникает такая проблема, проанализируем архитектуру регистра исчисленных взносов, выясним причины некорректного поведения запроса и рассмотрим правильные, оптимизированные способы решения этой задачи.
Рассмотрим ситуацию на примере запроса, который часто пытаются написать начинающие разработчики:
ВЫБРАТЬ
СтраховыеВзносыИсчисленные.Период,
ВЫБОР
КОГДА СУММА(СтраховыеВзносыИсчисленные.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный2) > 0
ТОГДА СтраховыеВзносыИсчисленные.ФизЛицо.Наименование
ИНАЧЕ ""
КОНЕЦ КАК Сотрудник
ИЗ
РегистрНакопления.СтраховыеВзносыИсчисленные КАК СтраховыеВзносыИсчисленные
ГДЕ
СтраховыеВзносыИсчисленные.ДатаПолученияДохода МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
СтраховыеВзносыИсчисленные.Период,
СтраховыеВзносыИсчисленные.ФизЛицо.Наименование
При выполнении такого запроса для периодов, где заведомо должны быть сотрудники-вредники, колонка "Сотрудник" остается абсолютно пустой. Проанализируем ситуацию и выделим две основные ошибки в этом коде: методическую и архитектурную.
Методическая ошибка: Использование агрегатной функции СУММА() внутри условной конструкции ВЫБОР КОГДА при наличии группировки. Написание подобных конструкций заставляет СУБД выполнять избыточные вычисления. Кроме того, группировка по строковому представлению ФизЛицо.Наименование (обращение через точку к реквизиту справочника) приводит к неявному левому соединению с таблицей физических лиц, что крайне негативно сказывается на производительности СУБД.
Архитектурная ошибка (главная причина пустого результата): Неверно выбранный ресурс регистра. Автор запроса проверяет только одно конкретное поле — ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный2. Если в вашей организации по результатам специальной оценки условий труда (СОУТ) сотрудникам установлен иной класс вредности (например, "Вредный 1", "Вредный 3" или спецоценка вообще не проводилась), то в выбранной колонке гарантированно будут нулевые значения. В результате условие ВЫБОР КОГДА всегда возвращает ветку ИНАЧЕ "".
Выясним причину, почему в полях этого регистра так легко запутаться. Дело в том, что учет дополнительных тарифов за вредность в 1С реализован с высокой степенью детализации. Ресурсы регистра делятся по двум ключевым признакам:
ПФРЗаЗанятыхНаПодземныхИВредныхРаботах...ПФРЗаЗанятыхНаТяжелыхИПрочихРаботах......БезСпецОценки или в базовые ресурсы общего характера....Опасный...Вредный4...Вредный3...Вредный2 (именно его ошибочно выбрал автор)...Вредный1Таким образом, если вы проверяете только одно поле, вы упускаете из виду сотрудников, у которых в кадровых документах или результатах СОУТ указан другой подкласс вредности.
Для того чтобы исправить логику запроса и уйти от неоптимальной конструкции ВЫБОР КОГДА в полях выборки, мы должны использовать секцию ИМЕЮЩИЕ (аналог HAVING в SQL). Это позволит отсечь строки с нулевыми суммами еще на этапе группировки СУБД.
Рассмотрим пример исправленного запроса, ориентированного на конкретный вид взносов:
ВЫБРАТЬ
СтраховыеВзносыИсчисленные.Период КАК Период,
СтраховыеВзносыИсчисленные.ФизЛицо КАК ФизЛицо
ИЗ
РегистрНакопления.СтраховыеВзносыИсчисленные КАК СтраховыеВзносыИсчисленные
ГДЕ
СтраховыеВзносыИсчисленные.ДатаПолученияДохода МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
СтраховыеВзносыИсчисленные.Период,
СтраховыеВзносыИсчисленные.ФизЛицо
ИМЕЮЩИЕ
СУММА(СтраховыеВзносыИсчисленные.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный2) > 0
В данном случае мы группируем данные по ссылочному полю ФизЛицо, а фильтрацию осуществляем в секции ИМЕЮЩИЕ. Если по выбранному ресурсу сумма взносов за период окажется нулевой, строка просто не попадет в итоговую выборку.
Если наша задача — гарантированно и без ошибок получить список всех "вредников" компании за период, мы не имеем права завязываться на какой-то один конкретный подкласс условий труда. Нам необходимо суммировать абсолютно все возможные доп. тарифы, которые могут быть начислены сотрудникам.
Разберем по шагам создание оптимального и надежного запроса с использованием временной таблицы (ВТ). Это лучший подход с точки зрения производительности и качества кода.
Шаг 1. Создание временной таблицы с группировкой и фильтрацией по ссылкам.
Сначала мы выберем данные из регистра, просуммируем все возможные поля дополнительных взносов (и по Списку №1, и по Списку №2, включая все классы СОУТ) и сгруппируем их по Период и ссылке ФизЛицо. Отбор строк с нулевыми суммами сделаем через ИМЕЮЩИЕ.
ВЫБРАТЬ
СтраховыеВзносы.Период КАК Период,
СтраховыеВзносы.ФизЛицо КАК ФизЛицо,
СУММА(
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботах +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахБезСпецОценки +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный1 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный2 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный3 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный4 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахОпасный +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботах +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахБезСпецОценки +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный1 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный2 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный3 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный4 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахОпасный
) КАК СуммаДопВзносов
ПОМЕСТИТЬ ВТ_СотрудникиВредники
ИЗ
РегистрНакопления.СтраховыеВзносыИсчисленные КАК СтраховыеВзносы
ГДЕ
СтраховыеВзносы.ДатаПолученияДохода МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
СтраховыеВзносы.Период,
СтраховыеВзносы.ФизЛицо
ИМЕЮЩИЕ
СУММА(
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботах +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахБезСпецОценки +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный1 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный2 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный3 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахВредный4 +
СтраховыеВзносы.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахОпасный +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботах +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахБезСпецОценки +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный1 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный2 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный3 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахВредный4 +
СтраховыеВзносы.ПФРЗаЗанятыхНаТяжелыхИПрочихРаботахОпасный
) > 0
;
Шаг 2. Получение строковых представлений из временной таблицы.
Теперь, когда у нас сформирован минимальный и точный массив физических лиц во временной таблице ВТ_СотрудникиВредники, мы можем безопасно обратиться к их наименованиям. СУБД выполнит соединение только для тех строк, которые реально прошли отбор, что существенно ускорит выполнение запроса.
ВЫБРАТЬ
ВТ_СотрудникиВредники.Период КАК Период,
ВТ_СотрудникиВредники.ФизЛицо.Наименование КАК Сотрудник,
ВТ_СотрудникиВредники.СуммаДопВзносов КАК СуммаДопВзносов
ИЗ
ВТ_СотрудникиВредники КАК ВТ_СотрудникиВредники
УПОРЯДОЧИТЬ ПО
Период,
Сотрудник
Рассмотрим подробнее, почему предложенное решение во втором варианте является наиболее качественным:
Применяйте данные рекомендации на практике, внимательно анализируйте структуру используемых регистров, и ваши отчеты всегда будут работать быстро и возвращать корректные данные!