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