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