Разработка внешних печатных форм (ВПФ) в конфигурации «1С:Зарплата и управление персоналом 3.1» (ЗУП) имеет ряд архитектурных особенностей. В отличие от простых конфигураций на платформе 1С, здесь мы работаем в среде, построенной на жестких стандартах Библиотеки стандартных подсистем (БСП). Кроме того, кадровые и расчетные данные в ЗУП распределены по сложной системе периодических регистров сведений и регистров накопления.
В этой статье мы подробно разберем, как решить задачу автора темы: создать внешнюю печатную форму справки о зарплате за последние 3 месяца, которую пользователь сможет вызывать из разных документов (например, из документов приема, перевода или непосредственно из справочника сотрудников). Мы выясним причину распространенной ошибки Печатная форма недоступна, научимся правильно регистрировать обработку в БСП, преобразовывать разнородные входящие ссылки к сотрудникам и получать кадровые и расчетные данные с помощью официального API 1С.
---
При использовании механизма дополнительных отчетов и обработок БСП осуществляет жесткий контроль сигнатур процедур и функций. Ошибка доступа к печатной форме в коде автора вызвана двумя факторами:
ПечатьВнешнейПечатнойФормы(). Механизм БСП не знает такой функции и ожидает увидеть в модуле объекта обработки строго процедуру с именем Печать.Печать в модуле объекта ВПФ должна принимать строго определенный набор параметров, соответствующий стандартам БСП.Давайте посмотрим на правильный шаблон экспортной процедуры Печать, который необходимо разместить в модуле объекта внешней обработки:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Проверяем, есть ли в коллекции наша печатная форма
ПараметрыПечати = ДополнительныеОтчетыИОбработкиКлиентСервер.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "СправкаОДоходахЗа3Месяца");
Если ПараметрыПечати <> Неопределено Тогда
// Формируем табличный документ
ТабДок = СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати);
// Заполняем параметры вывода для БСП
ПараметрыПечати.ТабличныйДокумент = ТабДок;
ПараметрыПечати.СинонимМакета = "Справка о доходах за 3 месяца";
КонецЕсли;
КонецПроцедуры
---
Для того чтобы система распознала нашу обработку как внешнюю печатную форму и позволила прикрепить её к нескольким объектам метаданных, необходимо описать функцию СведенияОВнешнейОбработке() в модуле объекта. Мы укажем в массиве назначений все объекты конфигурации, из которых пользователь сможет распечатать справку.
Рассмотрим пример создания функции регистрации:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
ПараметрыРегистрации.Версия = "1.0";
ПараметрыРегистрации.Наименование = "Справка о зарплате сотрудника за последние 3 месяца";
ПараметрыРегистрации.БезопасныйРежим = Ложь;
// Указываем объекты, в которых будет доступна печать
МассивНазначений = Новый Массив;
МассивНазначений.Добавить("Справочник.Сотрудники");
МассивНазначений.Добавить("Документ.ПриемНаРаботу");
МассивНазначений.Добавить("Документ.КадровыйПеревод");
МассивНазначений.Добавить("Документ.НачислениеЗарплаты");
ПараметрыРегистрации.Назначение = МассивНазначений;
// Описываем команду печати
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = "Справка о доходах за 3 месяца";
НоваяКоманда.Идентификатор = "СправкаОДоходахЗа3Месяца";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
НоваяКоманда.Модификатор = "ПечатьMXL";
Возврат ПараметрыРегистрации;
КонецФункции
---
Поскольку мы разрешили вызывать печать из разных документов и справочников, во входном параметре МассивОбъектов могут оказаться ссылки совершенно разных типов (например, ДокументСсылка.ПриемНаРаботу или СправочникСсылка.Сотрудники). Нам необходимо написать универсальный метод, который извлечет ссылки на сотрудников из любого переданного объекта.
Проанализируем ситуацию и напишем функцию приведения типов:
Функция ПолучитьМассивСотрудниковИзОбъектов(МассивОбъектов)
МассивСотрудников = Новый Массив;
Для Каждого СсылкаИзМассива Из МассивОбъектов Цикл
ТипСсылки = ТипЗнч(СсылкаИзМассива);
Если ТипСсылки = Тип("СправочникСсылка.Сотрудники") Тогда
Если МассивСотрудников.Найти(СсылкаИзМассива) = Неопределено Тогда
МассивСотрудников.Добавить(СсылкаИзМассива);
КонецЕсли;
ИначеЕсли ТипСсылки = Тип("ДокументСсылка.ПриемНаРаботу")
Или ТипСсылки = Тип("ДокументСсылка.КадровыйПеревод") Тогда
// Получаем сотрудника напрямую из реквизита документа
СотрудникСсылка = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаИзМассива, "Сотрудник");
Если ЗначениеЗаполнено(СотрудникСсылка) И МассивСотрудников.Найти(СотрудникСсылка) = Неопределено Тогда
МассивСотрудников.Добавить(СотрудникСсылка);
КонецЕсли;
ИначеЕсли ТипСсылки = Тип("ДокументСсылка.НачислениеЗарплаты") Тогда
// В документах расчета зарплаты сотрудники находятся в табличной части
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ТЧНачисления.Сотрудник КАК Сотрудник
|ИЗ
| Документ.НачислениеЗарплаты.Начисления КАК ТЧНачисления
|ГДЕ
| ТЧНачисления.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаИзМассива);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если МассивСотрудников.Найти(Выборка.Sотрудник) = Неопределено Тогда
МассивСотрудников.Добавить(Выборка.Сотрудник);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Возврат МассивСотрудников;
КонецФункции
---
Автор темы пытался получить организацию и период напрямую из виртуальной таблицы РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних. Делать так в ЗУП 3.1 крайне не рекомендуется. Механизм учета кадров в ЗУП очень сложен (влияет разделение по головным организациям, физическим лицам, особенности внутренних и внешних совместителей). К тому же, структура этих регистров регулярно меняется при обновлениях.
Для безопасного и быстрого получения кадровых данных мы воспользуемся официальным программным интерфейсом конфигурации — экспортным методом КадровыеДанныеСотрудников общего модуля КадровыйУчет. Этот метод сам выполнит все необходимые запросы к базе данных с учетом всех внутренних правил конфигурации.
Рассмотрим пример получения кадровых данных сотрудников на текущую дату:
// Определяем список необходимых полей
СписокПолей = "Организация, ГоловнаяОрганизация, Должность, ДатаПриема, ФИОПолные, ДокументПредставляющийЛичность";
// Вызываем метод API кадрового учета
ТаблицаКадровыхДанных = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, СписокПолей, ТекущаяДата());
Метод возвращает заполненную таблицу значений, строки которой содержат всю необходимую информацию по каждому сотруднику: ФИО, актуальную должность, организацию работодателя, сведения об удостоверении личности и дату приема на работу.
---
Чтобы построить таблицу с начислениями сотрудника, обратимся к регистру накопления оборотов НачисленияУдержанияПоСотрудникам. Это основной регистр в ЗУП 3.1, который отражает фактические взаиморасчеты и начисленные суммы. Использование плановых начислений или регистров выплат здесь не подойдет, так как нам нужны именно начисленные по факту суммы за календарные месяцы.
Разберем по шагам логику построения запроса к виртуальной таблице оборотов. Период расчетов (3 месяца) мы будем рассчитывать динамически от текущей даты (или даты документа-источника).
Функция ПолучитьДанныеЗарплатыЗаПериод(МассивСотрудников, ДатаОкончания)
// Определяем период за последние три полных месяца
НачалоПредыдущегоМесяца = НачалоМесяца(ДобавитьМесяц(ДатаОкончания, -1));
КонецПредыдущегоМесяца = КонецМесяца(ДобавитьМесяц(ДатаОкончания, -1));
ДатаНачала = НачалоМесяца(ДобавитьМесяц(ДатаОкончания, -3));
ДатаКонца = КонецМесяца(ДобавитьМесяц(ДатаОкончания, -1));
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| НачисленияОбороты.Сотрудник КАК Сотрудник,
| НачисленияОбороты.ПериодРегистрации КАК МесяцПериода,
| СУММА(ВЫБОР
| КОГДА НачисленияОбороты.ГруппаНачисленияУдержанияВыплаты = ЗНАЧЕНИЕ(Перечисление.ГруппыНачисленияУдержанияВыплаты.Начислено)
| ТОГДА НачисленияОбороты.Сумма
| ИНАЧЕ 0
| КОНЕЦ) КАК Начислено,
| СУММА(ВЫБОР
| КОГДА НачисленияОбороты.ГруппаНачисленияУдержанияВыплаты = ЗНАЧЕНИЕ(Перечисление.ГруппыНачисленияУдержанияВыплаты.НДФЛ)
| ТОГДА НачисленияОбороты.Сумма
| ИНАЧЕ 0
| КОНЕЦ) КАК НДФЛ,
| СУММА(ВЫБОР
| КОГДА НачисленияОбороты.ГруппаНачисленияУдержанияВыплаты = ЗНАЧЕНИЕ(Перечисление.ГруппыНачисленияУдержанияВыплаты.Удержано)
| ТОГДА НачисленияОбороты.Сумма
| ИНАЧЕ 0
| КОНЕЦ) КАК Удержано
|ИЗ
| РегистрНакопления.НачисленияУдержанияПоСотрудникам.Обороты(&ДатаНачала, &ДатаКонца, Авто, Сотрудник В (&МассивСотрудников)) КАК НачисленияОбороты
|
|СГРУППИРОВАТЬ ПО
| НачисленияОбороты.Сотрудник,
| НачисленияОбороты.ПериодРегистрации
|
|УПОРЯДОЧИТЬ ПО
| Сотрудник,
| МесяцПериода";
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаКонца", ДатаКонца);
Запрос.УстановитьПараметр("МассивСотрудников", МассивСотрудников);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
---
Теперь объединим все шаги в единый алгоритм вывода табличного документа. Мы извлечем сотрудников, получим по ним кадровые сведения, выберем данные о доходах за три месяца, а затем программно заполним области нашего макета.
Посмотрим на финальный код функции формирования печатной формы:
Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати)
ТабДок = Новый ТабличныйДокумент;
ТабДок.КлючПараметровПечати = "ПараметрыПечати_СправкаОДоходах";
// Получаем макет обработки
Макет = ПолучитьМакет("ПФ_MXL_СправкаОДоходах");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
// Приводим входящий массив к массиву сотрудников
МассивСотрудников = ПолучитьМассивСотрудниковИзОбъектов(МассивОбъектов);
Если МассивСотрудников.Количество() = 0 Тогда
ВызватьИсключение "Не удалось определить сотрудников для формирования печатной формы!";
КонецЕсли;
// Получаем кадровые данные
КадровыеДанные = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, "Организация, Должность, ФИОПолные, ДатаПриема, ДокументПредставляющийЛичность", ТекущаяДата());
// Получаем зарплатные данные
ТаблицаЗарплаты = ПолучитьДанныеЗарплатыЗаПериод(МассивСотрудников, ТекущаяДата());
// Цикл по сотрудникам для формирования печатных листов
Для Каждого СтрокаКадры Из КадровыеДанные Цикл
// Разделитель страниц для каждого нового сотрудника
Если ТабДок.ВысотаТаблицы > 0 Тогда
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
// Заполняем Шапку документа данными из кадрового API
ОбластьШапка.Параметры.Заполнить(СтрокаКадры);
ОбластьШапка.Параметры.ОрганизацияНаименование = СтрокаКадры.Организация;
ОбластьШапка.Параметры.ТекущаяДата = Формат(ТекущаяДата(), "ДЛФ=DD");
ОбластьШапка.Параметры.ПаспортныеДанные = СтрокаКадры.ДокументПредставляющийЛичность;
ТабДок.Вывести(ОбластьШапка);
// Фильтруем таблицу зарплаты по текущему сотруднику и выводим строки
Отбор = Новый Структура("Сотрудник", СтрокаКадры.Сотрудник);
СтрокиЗарплаты = ТаблицаЗарплаты.НайтиСтроки(Отбор);
ИтогоНачислено = 0;
ИтогоВыплачено = 0;
Для Каждого СтрокаЗП Из СтрокиЗарплаты Цикл
ОбластьСтрока.Параметры.Месяц = Формат(СтрокаЗП.МесяцПериода, "ДФ='MMMM yyyy'");
ОбластьСтрока.Параметры.СуммаНачислено = СтрокаЗП.Начислено;
// "Чистыми на руки" = Начислено - Удержано - НДФЛ
СуммаНаРуки = СтрокаЗП.Начислено - СтрокаЗП.Удержано - СтрокаЗП.НДФЛ;
ОбластьСтрока.Параметры.СуммаНаРуки = СуммаНаРуки;
ИтогоНачислено = ИтогоНачислено + СтрокаЗП.Начислено;
ИтогоВыплачено = ИтогоВыплачено + СуммаНаРуки;
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
// Выводим Подвал
ОбластьПодвал.Параметры.ИтогоНачислено = ИтогоНачислено;
ОбластьПодвал.Параметры.ИтогоНаРуки = ИтогоВыплачено;
ТабДок.Вывести(ОбластьПодвал);
КонецЦикла;
Возврат ТабДок;
КонецФункции
---
Мы спроектировали и реализовали универсальную внешнюю печатную форму справки о доходах за 3 месяца. Мы успешно обошли технические ограничения и оптимизировали код:
МассивОбъектов, позволяющий печатать форму как напрямую из карточки сотрудника, так и из любых кадровых документов.КадровыйУчет.КадровыеДанныеСотрудников.НачисленияУдержанияПоСотрудникам.Используя данный шаблон и подходы, вы сможете с легкостью разрабатывать любые другие сложные кадровые отчеты и справки в ЗУП 3.1, которые будут стабильно работать даже после масштабных обновлений конфигурации.