Как вывести индивидуальные надбавки сотрудников в печатной форме списочного документа 1С

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

При разработке и кастомизации печатных форм в решениях на платформе 1С:Предприятие 8 (таких как "Зарплата и управление персоналом", "ERP Управление предприятием" или "Комплексная автоматизация") разработчики часто сталкиваются со списочными кадровыми документами. Яркий пример — документ "Кадровый перевод списком" или "Прием на работу списком". Главная задача при формировании печатных форм для таких документов — сделать так, чтобы для каждого сотрудника выводились только его собственные надбавки и начисления, а не весь массив данных по документу.

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

Проблема 1: Пропуск записей и неявный сдвиг выборки в цикле

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


Если Не ОписанияНачисленийПоПериодам.Получить(ОписанияНачислений) = ВыборкаПоСотрудникам.Следующий Тогда

Выясним, почему такое условие полностью ломает логику работы цикла и приводит к потере данных. Метод Следующий() объекта выборки выполняет одновременно две задачи: проверяет наличие следующей записи в выборке базы данных и, если она есть, физически переносит указатель на эту запись (сдвигает итератор). Когда данный метод вызывается внутри условия Если, в то время как выше по коду уже объявлен цикл Пока ВыборкаПоСотрудникам.Следующий(), происходит двойной сдвиг указателя выборки за одну итерацию.

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

Решение: Никогда не вызывайте метод Следующий() внутри тела цикла для выполнения логических проверок. Для проверки наличия данных по ключу в коллекциях типа Соответствие или Структура используйте метод Получить(). Если ключ (в нашем случае — ссылка на сотрудника) отсутствует в коллекции, метод вернет значение Неопределено. Проверку нужно реализовать следующим образом:


// Внутри цикла "Пока ВыборкаПоСотрудникам.Следующий() Цикл"
ОписанияНачислений = ОписанияНачисленийПоПериодам.Получить(ВыборкаПоСотрудникам.Сотрудник);
Если ОписанияНачислений = Неопределено Тогда
    ОписанияНачислений = Новый Соответствие;
    ОписанияНачисленийПоПериодам.Вставить(ВыборкаПоСотрудникам.Сотрудник, ОписанияНачислений);
КонецЕсли;

Проблема 2: Попытка фильтрации данных по строковому представлению ФИО

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


Если СтрокаТаб.Сотрудник.ФизическоеЛицо.Наименование = ПараметрыМакета.Работник Тогда
    // Вывод параметров в область макета
КонецЕсли;

Рассмотрим подробнее, почему этот подход работает некорректно и считается грубой ошибкой разработки в среде 1С:

  1. Несовпадение склонений и форматов строк. Переменная ПараметрыМакета.Работник в типовых механизмах печати ЗУП формируется встроенными функциями склонения (например, ФИО выводится в дательном падеже: "Иванову Ивану Ивановичу") или может быть представлена в сокращенном виде с инициалами. В то же время реквизит Наименование справочника ФизическиеЛица всегда хранит полное имя в именительном падеже ("Иванов Иван Иванович"). Из-за этого строковое сравнение вернет Ложь, и табличная часть начислений останется пустой для всех сотрудников.
  2. Критическое падение производительности (N+1 запрос к СУБД). Конструкция обращения "через точку" СтрокаТаб.Сотрудник.ФизическоеЛицо.Наименование внутри цикла заставляет платформу выполнять неявные SQL-запросы к базе данных для каждой строки таблицы. Если документ содержит 50 сотрудников, у каждого из которых по 3-4 надбавки, система выполнит сотни лишних обращений к базе данных. Это приведет к сильным зависаниям программы при выводе формы на экран.

Решение: Сравнение и сопоставление данных всегда должно производиться по уникальным ссылкам (идентификаторам), а не по строковым наименованиям. В коллекции МассивДанныхЗаполнения (откуда извлекаются ПараметрыМакета) всегда присутствуют готовые ссылки на объекты. Нам достаточно сопоставить ссылку на сотрудника напрямую:

Если СтрокаТаб.Сотрудник = ПараметрыМакета.Сотрудник Тогда

Решение по шагам: Реализация оптимального алгоритма вывода надбавок

Полный перебор всей таблицы начислений вложенным циклом для каждого сотрудника — крайне неэффективный алгоритм. Если в списке будет много сотрудников, печать будет занимать длительное время. Мы применим наиболее быстродействующий и чистый способ фильтрации данных — метод таблицы значений НайтиСтроки(), работающий на уровне платформы.

Разберем по шагам правильный алгоритм вывода печатной формы:

  1. Получаем нужную область макета (например, строку "Надбавки").
  2. Организуем цикл по массиву данных для заполнения (по каждому сотруднику, для которого печатается приказ).
  3. Перед выводом надбавок инициализируем структуру отбора, где ключом будет имя колонки Сотрудник, а значением — ссылка на текущего сотрудника из параметров макета.
  4. Используя метод НайтиСтроки(), мгновенно извлекаем только те строки начислений, которые относятся к текущему сотруднику.
  5. Запускаем цикл по полученному массиву строк и выводим их параметры в табличный документ.

Посмотрим на программную реализацию этого алгоритма:


// Получаем область вывода надбавок из макета
ОбластьНадбавки = Макет.ПолучитьОбласть("Надбавки");

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

Важные рекомендации по работе с печатными формами

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

← На главную