Как получить оклад сотрудника запросом к СрезПоследних и вывести его в печатную форму?

Программист 1С:Зарплата и Управление Персоналом
← На главную

При разработке печатных форм в 1С часто возникает задача получить какое-то одно конкретное значение, например, оклад сотрудника на определенную дату. При этом можно столкнуться с двумя типичными проблемами: запрос возвращает лишние пустые строки или вместо нужного значения в ячейке макета отображается системный текст, например, "ТаблицаЗначений". Разберем на конкретном примере, как решить обе эти проблемы.

Проанализируем ситуацию: разработчик пишет запрос к виртуальной таблице регистра сведений ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудников.СрезПоследних. Цель — получить только сумму оклада. Однако результат содержит лишние строки, а при попытке вывести данные в макет появляется надпись "ТаблицаЗначений".

Выясним причины и рассмотрим правильные подходы к решению этих задач.

Проблема №1: Запрос возвращает лишние строки с пустым значением оклада

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


ВЫБРАТЬ
    ВЫБОР
        КОГДА ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудниковСрезПоследних.Показатель = &Оклад
            ТОГДА ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудниковСрезПоследних.Значение
    КОНЕЦ КАК СуммаОклада
ИЗ
    РегистрСведений.ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудников.СрезПоследних(&Период, ) КАК ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудниковСрезПоследних
ГДЕ
    ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудниковСрезПоследних.Сотрудник В(&Сотрудник)

Причина ошибки:

  1. Конструкция ВЫБОР ... КОГДА в секции ВЫБРАТЬ не фильтрует строки. Она лишь определяет, какое значение будет в вычисляемом поле СуммаОклада. Если условие Показатель = &Оклад не выполняется, поле СуммаОклада для этой строки будет иметь значение NULL, но сама строка все равно попадет в результат запроса.
  2. Условие отбора по сотруднику находится в секции ГДЕ. При работе с виртуальными таблицами, такими как СрезПоследних, это крайне неэффективно. Сначала система получает срез всех записей на указанную дату, и только потом применяет фильтр. Это создает лишнюю нагрузку на базу данных.

Правильное решение: перенос условий в параметры виртуальной таблицы.

Чтобы запрос работал корректно и оптимально, все условия отбора необходимо передавать непосредственно в параметры самой виртуальной таблицы. Это позволяет СУБД сразу выбрать только нужные записи, используя индексы, что значительно ускоряет выполнение запроса.

Синтаксис виртуальной таблицы СрезПоследних выглядит так: СрезПоследних(&ДатаСреза, &УсловиеОтбора).

Давайте исправим запрос, перенеся условия по показателю и сотруднику из ГДЕ и ВЫБОР во второй параметр виртуальной таблицы.

Оптимальный вариант запроса:


ВЫБРАТЬ
    ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудниковСрезПоследних.Значение КАК СуммаОклада
ИЗ
    РегистрСведений.ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудников.СрезПоследних(
            &Период,
            Сотрудник В (&Сотрудник) И Показатель = &Оклад)
            КАК ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудниковСрезПоследних

Такой запрос сразу выберет из базы данных только одну запись — значение показателя "Оклад" для указанного сотрудника на заданную дату. Лишних строк в результате не будет.

Проблема №2: Вместо суммы оклада в печатной форме выводится "ТаблицаЗначений"

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


// ... в процедуре вывода на печать
РезультатНачисления = ДанныеДляПечатиДопникОсновныеНачисления(МассивДанныхЗаполнения);
Макет.Параметры.СуммаОклада = РезультатНачисления;

// ... функция получения данных
Функция ДанныеДляПечатиДопникОсновныеНачисления(МассивОбъектов)
    Запрос = Новый Запрос;
    // ... текст запроса
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Причина ошибки:

Метод Запрос.Выполнить().Выгрузить() всегда возвращает объект типа ТаблицаЗначений, даже если в результате запроса всего одна строка и одна колонка. Когда мы пытаемся присвоить этот объект параметру макета (Макет.Параметры.СуммаОклада), который ожидает простое значение (число или строку), система 1С преобразует тип объекта в строку. Для ТаблицаЗначений таким строковым представлением является "ТаблицаЗначений", что мы и видим в печатной форме.

Чтобы это исправить, нужно из результата запроса извлечь не всю таблицу, а значение из конкретной ячейки.

Решение 1: Использование выборки (рекомендуемый способ)

Наиболее правильный и универсальный способ получить одно значение из запроса — использовать выборку. Вместо метода Выгрузить() нужно использовать метод Выбрать(), который возвращает объект ВыборкаИзРезультатаЗапроса.

Далее нужно спозиционироваться на первой строке результата с помощью метода Следующий() и получить значение нужного поля.

Исправленный код функции:


Функция ДанныеДляПечатиДопникОсновныеНачисления(МассивОбъектов)
    Запрос = Новый Запрос;
    // ... устанавливаем текст и параметры запроса ...
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Проверяем, вернул ли запрос хотя бы одну строку
    Если Выборка.Следующий() Тогда
        // Если да, возвращаем значение из колонки СуммаОклада
        Возврат Выборка.СуммаОклада;
    Иначе
        // Если запрос ничего не нашел, возвращаем значение по умолчанию (например, 0 или Неопределено)
        Возврат 0; 
    КонецЕсли;
    
КонецФункции

Этот способ является наиболее надежным. Проверка Если Выборка.Следующий() Тогда защищает от ошибок в случае, если запрос по какой-либо причине не вернет ни одной записи (например, сотруднику еще не назначен оклад).

Решение 2: Обращение к таблице значений по индексу

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

В процедуре вывода на печать код мог бы выглядеть так:


ТаблицаРезультата = ДанныеДляПечатиДопникОсновныеНачисления(МассивДанныхЗаполнения);

// Обязательно проверяем, что таблица не пустая, чтобы избежать ошибки
Если ТаблицаРезультата.Количество() > 0 Тогда
    Макет.Параметры.СуммаОклада = ТаблицаРезультата[0].СуммаОклада;
Иначе
    Макет.Параметры.СуммаОклада = 0;
КонецЕсли;

Важно понимать недостатки этого подхода:

Поэтому первый способ с использованием выборки является предпочтительным.

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

← На главную