Как разработать универсальную внешнюю печатную форму в 1С:ЗУП 3.1 для печати справки по сотрудникам из разных документов

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

Разработка внешних печатных форм (ВПФ) в конфигурации «1С:Зарплата и управление персоналом 3.1» (ЗУП) имеет ряд архитектурных особенностей. В отличие от простых конфигураций на платформе 1С, здесь мы работаем в среде, построенной на жестких стандартах Библиотеки стандартных подсистем (БСП). Кроме того, кадровые и расчетные данные в ЗУП распределены по сложной системе периодических регистров сведений и регистров накопления.

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

---

Почему возникает ошибка «Печатная форма недоступна»?

При использовании механизма дополнительных отчетов и обработок БСП осуществляет жесткий контроль сигнатур процедур и функций. Ошибка доступа к печатной форме в коде автора вызвана двумя факторами:

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

Давайте посмотрим на правильный шаблон экспортной процедуры Печать, который необходимо разместить в модуле объекта внешней обработки:


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

---

Шаг 1. Правильная регистрация ВПФ в модуле объекта

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

Рассмотрим пример создания функции регистрации:


Функция СведенияОВнешнейОбработке() Экспорт
    
    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
    
    ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
    ПараметрыРегистрации.Версия = "1.0";
    ПараметрыРегистрации.Наименование = "Справка о зарплате сотрудника за последние 3 месяца";
    ПараметрыРегистрации.БезопасныйРежим = Ложь;
    
    // Указываем объекты, в которых будет доступна печать
    МассивНазначений = Новый Массив;
    МассивНазначений.Добавить("Справочник.Сотрудники");
    МассивНазначений.Добавить("Документ.ПриемНаРаботу");
    МассивНазначений.Добавить("Документ.КадровыйПеревод");
    МассивНазначений.Добавить("Документ.НачислениеЗарплаты");
    ПараметрыРегистрации.Назначение = МассивНазначений;
    
    // Описываем команду печати
    НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
    НоваяКоманда.Представление = "Справка о доходах за 3 месяца";
    НоваяКоманда.Идентификатор = "СправкаОДоходахЗа3Месяца";
    НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
    НоваяКоманда.Модификатор   = "ПечатьMXL";
    
    Возврат ПараметрыРегистрации;
    
КонецФункции

---

Шаг 2. Приведение разнородного «МассиваОбъектов» к сотрудникам

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

Проанализируем ситуацию и напишем функцию приведения типов:


Функция ПолучитьМассивСотрудниковИзОбъектов(МассивОбъектов)
    
    МассивСотрудников = Новый Массив;
    
    Для Каждого СсылкаИзМассива Из МассивОбъектов Цикл
        
        ТипСсылки = ТипЗнч(СсылкаИзМассива);
        
        Если ТипСсылки = Тип("СправочникСсылка.Сотрудники") Тогда
            
            Если МассивСотрудников.Найти(СсылкаИзМассива) = Неопределено Тогда
                МассивСотрудников.Добавить(СсылкаИзМассива);
            КонецЕсли;
            
        ИначеЕсли ТипСсылки = Тип("ДокументСсылка.ПриемНаРаботу") 
			Или ТипСсылки = Тип("ДокументСсылка.КадровыйПеревод") Тогда
            
            // Получаем сотрудника напрямую из реквизита документа
            СотрудникСсылка = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаИзМассива, "Сотрудник");
            Если ЗначениеЗаполнено(СотрудникСсылка) И МассивСотрудников.Найти(СотрудникСсылка) = Неопределено Тогда
                МассивСотрудников.Добавить(СотрудникСсылка);
            КонецЕсли;
            
        ИначеЕсли ТипСсылки = Тип("ДокументСсылка.НачислениеЗарплаты") Тогда
            
            // В документах расчета зарплаты сотрудники находятся в табличной части
            Запрос = Новый Запрос;
            Запрос.Текст = 
                "ВЫБРАТЬ РАЗЛИЧНЫЕ
                |	ТЧНачисления.Сотрудник КАК Сотрудник
                |ИЗ
                |	Документ.НачислениеЗарплаты.Начисления КАК ТЧНачисления
                |ГДЕ
                |	ТЧНачисления.Ссылка = &Ссылка";
            
            Запрос.УстановитьПараметр("Ссылка", СсылкаИзМассива);
            Выборка = Запрос.Выполнить().Выбрать();
            Пока Выборка.Следующий() Цикл
                Если МассивСотрудников.Найти(Выборка.Sотрудник) = Неопределено Тогда
                    МассивСотрудников.Добавить(Выборка.Сотрудник);
                КонецЕсли;
            КонецЦикла;
            
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат МассивСотрудников;
    
КонецФункции

---

Шаг 3. Получение кадровых данных сотрудников через API ЗУП 3.1

Автор темы пытался получить организацию и период напрямую из виртуальной таблицы РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних. Делать так в ЗУП 3.1 крайне не рекомендуется. Механизм учета кадров в ЗУП очень сложен (влияет разделение по головным организациям, физическим лицам, особенности внутренних и внешних совместителей). К тому же, структура этих регистров регулярно меняется при обновлениях.

Для безопасного и быстрого получения кадровых данных мы воспользуемся официальным программным интерфейсом конфигурации — экспортным методом КадровыеДанныеСотрудников общего модуля КадровыйУчет. Этот метод сам выполнит все необходимые запросы к базе данных с учетом всех внутренних правил конфигурации.

Рассмотрим пример получения кадровых данных сотрудников на текущую дату:


// Определяем список необходимых полей
СписокПолей = "Организация, ГоловнаяОрганизация, Должность, ДатаПриема, ФИОПолные, ДокументПредставляющийЛичность";

// Вызываем метод API кадрового учета
ТаблицаКадровыхДанных = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, СписокПолей, ТекущаяДата());

Метод возвращает заполненную таблицу значений, строки которой содержат всю необходимую информацию по каждому сотруднику: ФИО, актуальную должность, организацию работодателя, сведения об удостоверении личности и дату приема на работу.

---

Шаг 4. Получение данных о зарплате за последние 3 месяца

Чтобы построить таблицу с начислениями сотрудника, обратимся к регистру накопления оборотов НачисленияУдержанияПоСотрудникам. Это основной регистр в ЗУП 3.1, который отражает фактические взаиморасчеты и начисленные суммы. Использование плановых начислений или регистров выплат здесь не подойдет, так как нам нужны именно начисленные по факту суммы за календарные месяцы.

Разберем по шагам логику построения запроса к виртуальной таблице оборотов. Период расчетов (3 месяца) мы будем рассчитывать динамически от текущей даты (или даты документа-источника).


Функция ПолучитьДанныеЗарплатыЗаПериод(МассивСотрудников, ДатаОкончания)
    
    // Определяем период за последние три полных месяца
    НачалоПредыдущегоМесяца = НачалоМесяца(ДобавитьМесяц(ДатаОкончания, -1));
    КонецПредыдущегоМесяца  = КонецМесяца(ДобавитьМесяц(ДатаОкончания, -1));
    
    ДатаНачала = НачалоМесяца(ДобавитьМесяц(ДатаОкончания, -3));
    ДатаКонца  = КонецМесяца(ДобавитьМесяц(ДатаОкончания, -1));
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	НачисленияОбороты.Сотрудник КАК Сотрудник,
        |	НачисленияОбороты.ПериодРегистрации КАК МесяцПериода,
        |	СУММА(ВЫБОР
        |			КОГДА НачисленияОбороты.ГруппаНачисленияУдержанияВыплаты = ЗНАЧЕНИЕ(Перечисление.ГруппыНачисленияУдержанияВыплаты.Начислено)
        |				ТОГДА НачисленияОбороты.Сумма
        |			ИНАЧЕ 0
        |		КОНЕЦ) КАК Начислено,
        |	СУММА(ВЫБОР
        |			КОГДА НачисленияОбороты.ГруппаНачисленияУдержанияВыплаты = ЗНАЧЕНИЕ(Перечисление.ГруппыНачисленияУдержанияВыплаты.НДФЛ)
        |				ТОГДА НачисленияОбороты.Сумма
        |			ИНАЧЕ 0
        |		КОНЕЦ) КАК НДФЛ,
        |	СУММА(ВЫБОР
        |			КОГДА НачисленияОбороты.ГруппаНачисленияУдержанияВыплаты = ЗНАЧЕНИЕ(Перечисление.ГруппыНачисленияУдержанияВыплаты.Удержано)
        |				ТОГДА НачисленияОбороты.Сумма
        |			ИНАЧЕ 0
        |		КОНЕЦ) КАК Удержано
        |ИЗ
        |	РегистрНакопления.НачисленияУдержанияПоСотрудникам.Обороты(&ДатаНачала, &ДатаКонца, Авто, Сотрудник В (&МассивСотрудников)) КАК НачисленияОбороты
        |
        |СГРУППИРОВАТЬ ПО
        |	НачисленияОбороты.Сотрудник,
        |	НачисленияОбороты.ПериодРегистрации
        |
        |УПОРЯДОЧИТЬ ПО
        |	Сотрудник,
        |	МесяцПериода";
        
    Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
    Запрос.УстановитьПараметр("ДатаКонца", ДатаКонца);
    Запрос.УстановитьПараметр("МассивСотрудников", МассивСотрудников);
    
    Возврат Запрос.Выполнить().Выгрузить();
    
КонецФункции

---

Шаг 5. Формирование печатной формы

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

Посмотрим на финальный код функции формирования печатной формы:


Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати)
    
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.КлючПараметровПечати = "ПараметрыПечати_СправкаОДоходах";
    
    // Получаем макет обработки
    Макет = ПолучитьМакет("ПФ_MXL_СправкаОДоходах");
    ОбластьШапка  = Макет.ПолучитьОбласть("Шапка");
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
    ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
    
    // Приводим входящий массив к массиву сотрудников
    МассивСотрудников = ПолучитьМассивСотрудниковИзОбъектов(МассивОбъектов);
    
    Если МассивСотрудников.Количество() = 0 Тогда
        ВызватьИсключение "Не удалось определить сотрудников для формирования печатной формы!";
    КонецЕсли;
    
    // Получаем кадровые данные
    КадровыеДанные = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, "Организация, Должность, ФИОПолные, ДатаПриема, ДокументПредставляющийЛичность", ТекущаяДата());
    
    // Получаем зарплатные данные
    ТаблицаЗарплаты = ПолучитьДанныеЗарплатыЗаПериод(МассивСотрудников, ТекущаяДата());
    
    // Цикл по сотрудникам для формирования печатных листов
    Для Каждого СтрокаКадры Из КадровыеДанные Цикл
        
        // Разделитель страниц для каждого нового сотрудника
        Если ТабДок.ВысотаТаблицы > 0 Тогда
            ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
        
        // Заполняем Шапку документа данными из кадрового API
        ОбластьШапка.Параметры.Заполнить(СтрокаКадры);
        ОбластьШапка.Параметры.ОрганизацияНаименование = СтрокаКадры.Организация;
        ОбластьШапка.Параметры.ТекущаяДата = Формат(ТекущаяДата(), "ДЛФ=DD");
        ОбластьШапка.Параметры.ПаспортныеДанные = СтрокаКадры.ДокументПредставляющийЛичность;
        
        ТабДок.Вывести(ОбластьШапка);
        
        // Фильтруем таблицу зарплаты по текущему сотруднику и выводим строки
        Отбор = Новый Структура("Сотрудник", СтрокаКадры.Сотрудник);
        СтрокиЗарплаты = ТаблицаЗарплаты.НайтиСтроки(Отбор);
        
        ИтогоНачислено = 0;
        ИтогоВыплачено = 0;
        
        Для Каждого СтрокаЗП Из СтрокиЗарплаты Цикл
            
            ОбластьСтрока.Параметры.Месяц = Формат(СтрокаЗП.МесяцПериода, "ДФ='MMMM yyyy'");
            ОбластьСтрока.Параметры.СуммаНачислено = СтрокаЗП.Начислено;
            
            // "Чистыми на руки" = Начислено - Удержано - НДФЛ
            СуммаНаРуки = СтрокаЗП.Начислено - СтрокаЗП.Удержано - СтрокаЗП.НДФЛ;
            ОбластьСтрока.Параметры.СуммаНаРуки = СуммаНаРуки;
            
            ИтогоНачислено = ИтогоНачислено + СтрокаЗП.Начислено;
            ИтогоВыплачено = ИтогоВыплачено + СуммаНаРуки;
            
            ТабДок.Вывести(ОбластьСтрока);
            
        КонецЦикла;
        
        // Выводим Подвал
        ОбластьПодвал.Параметры.ИтогоНачислено = ИтогоНачислено;
        ОбластьПодвал.Параметры.ИтогоНаРуки = ИтогоВыплачено;
        
        ТабДок.Вывести(ОбластьПодвал);
        
    КонецЦикла;
    
    Возврат ТабДок;
    
КонецФункции

---

Резюме и практические советы

Мы спроектировали и реализовали универсальную внешнюю печатную форму справки о доходах за 3 месяца. Мы успешно обошли технические ограничения и оптимизировали код:

Используя данный шаблон и подходы, вы сможете с легкостью разрабатывать любые другие сложные кадровые отчеты и справки в ЗУП 3.1, которые будут стабильно работать даже после масштабных обновлений конфигурации.

← На главную