Еще одна статья и пример практического применения менеджера расчета в подсистеме расчета зарплаты конфигураций ЗУП 3.1 и ERP. Ранее уже писал про менеджер расчета в статье Использование менеджера расчета для расчета зарплаты в ЗУП 3.1 и в Программное создание и расчет документов начисления зарплаты в конфигурации ЗУП 3.1 и ERP (по подразделениям). Но чтобы было понятнее, что же это такое, сделал еще один практический пример.
Например у нас гипотетическая задача сделать расчет ФОТ на год вперед, вам нужно посчитать затраты на зарплату на базе существующей системы, сотрудников, позиций штатного расписания и действующих плановых начислений. Для этой задачи не будем плодить сущностей и сделаем простой внешний отчет.
В итоге наш отчет выглядит примерно так:
"Период" - Произвольный период. При расчете дата начала и дата окончания приводятся к началу и окончанию месяца соответственно.
"Организация" и "подразделение" - ключевые параметры, по которым строим отчет.
"Использовать время за предыдущий период" - параметр отвечающий за время. Например в моей развернутой демобазе графики работы за 2022 год расчитаны и расчет зарплаты за этот период не вызывает проблем. А если сместить период на 2023 год, то графиков нет и в формулах расчета начислений НормаДней, НормаЧасов окажутся равными нулю, что вызовет ошибку расчета.
Данная галка позволяет расчитать время за предыдущий год и подставить фиксированными показателями период, где нет этих данных. В качестве времени выбрано только рабочее, поэтому это достаточно упрощенный пример. В полноценной системе расчета необходимо предусмотреть заполнение таких показателей как праздничные дни, отпуска и еще другие показатели, которые вы используете при расчете зарплаты за текущий период.
А теперь к реализации.
Реализовано через отчет с простой схемой компоновки данных, использующую внешний набор данных.
Структура вывода СКД - в таблицу, где в колонках у нас выводится месяц и сумма.
Программный вывод СКД в отчет, думаю, тоже у вас не должен вызывать проблем
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
НастройкиКД = КомпоновщикНастроек.ПолучитьНастройки();
ПараметрыРасчета = Новый Структура;
Организация = ОтчетыКлиентСервер.НайтиПараметр(НастройкиКД,, "Организация").Значение;
Период = ОтчетыКлиентСервер.НайтиПараметр(НастройкиКД,, "Период").Значение;
ИспользоватьВремяЗаПредыдущийГод = ОтчетыКлиентСервер.НайтиПараметр(НастройкиКД,, "ИспользоватьВремяЗаПредыдущийГод").Значение;
ПараметрПодразделение = ОтчетыКлиентСервер.НайтиПараметр(НастройкиКД,, "Подразделение");
Если ПараметрПодразделение.Использование Тогда
ПараметрыРасчета.Вставить("Подразделение", ПараметрПодразделение.Значение);
КонецЕсли;
ПараметрыРасчета.Вставить("Организация", Организация);
ПараметрыРасчета.Вставить("Период", Период);
ПараметрыРасчета.Вставить("ИспользоватьВремяЗаПредыдущийГод", ИспользоватьВремяЗаПредыдущийГод);
ДанныеФОТ = ДанныеФОТ(ПараметрыРасчета);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКД, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, Новый Структура("НаборДанных", ДанныеФОТ), ДанныеРасшифровки, Истина);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Создадим таблицу для СКД
ДанныеФОТ = Новый ТаблицаЗначений;
ДанныеФОТ.Колонки.Добавить("Сотрудник");
ДанныеФОТ.Колонки.Добавить("ДолжностьПоШтатномуРасписанию");
ДанныеФОТ.Колонки.Добавить("Подразделение");
ДанныеФОТ.Колонки.Добавить("Организация");
ДанныеФОТ.Колонки.Добавить("Начисление");
ДанныеФОТ.Колонки.Добавить("Период");
ДанныеФОТ.Колонки.Добавить("Результат");
Получим сотрудников организации для расчета
ПараметрыПолученияСотрудников = КадровыйУчет.ПараметрыПолученияСотрудниковОрганизацийПоСпискуФизическихЛиц();
ПараметрыПолученияСотрудников.Организация = ПараметрыРасчета.Организация;
Если ПараметрыРасчета.Свойство("Подразделение") Тогда
ПараметрыПолученияСотрудников.Подразделение = ПараметрыРасчета.Подразделение;
КонецЕсли;
ПараметрыПолученияСотрудников.НачалоПериода = ПараметрыРасчета.Период.ДатаНачала;
ПараметрыПолученияСотрудников.ОкончаниеПериода = КонецМесяца(ПараметрыРасчета.Период.ДатаОкончания);
КадровыйУчетРасширенный.ПрименитьОтборПоФункциональнойОпцииВыполнятьРасчетЗарплатыПоПодразделениям(ПараметрыПолученияСотрудников);
КадровыйУчет.СоздатьВТСотрудникиОрганизации(Запрос.МенеджерВременныхТаблиц, Истина, ПараметрыПолученияСотрудников);
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Сотрудник КАК Сотрудник,
| Сотрудники.ФизическоеЛицо КАК ФизическоеЛицо
|ИЗ
| ВТСотрудникиОрганизации КАК Сотрудники";
Сотрудники = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Сотрудник");
Теперь инициализируем менеджер расчета
МенеджерРасчета = СоздатьМенеджерРасчета(ПараметрыРасчета.Период, ПараметрыРасчета.Организация);
Тут немного подробнее. Классическая инициализация происходит с помощью вызова метода РасчетЗарплатыРасширенный.СоздатьМенеджерРасчета
Но этот вызов инициализирует менеджер только за один месяц, что меня не устраивает. Поэтому напишем свой метод инициализации за период
Функция СоздатьМенеджерРасчета(ПериодРасчета, Организация) Экспорт
УстановитьПривилегированныйРежим(Истина);
Если ЗарплатаКадры.ВыполнятьРасчетЗарплатыБезОптимизации() Тогда
МенеджерРасчета = Обработки.МенеджерРасчетаЗарплатыАрхивный.Создать();
Иначе
МенеджерРасчета = Обработки.МенеджерРасчетаЗарплаты.Создать();
КонецЕсли;
УстановитьПривилегированныйРежим(Ложь);
МенеджерРасчета.Инициализировать(ПериодРасчета, Организация);
Возврат МенеджерРасчета;
КонецФункции
Далее все просто. Заполняем настройки менеджера расчета по ранее полученным сотрудникам. С помощью вызова МенеджерРасчета.ЗаполнитьНачислениеЗарплаты заполняем все плановые начисления этих сотрудников
МенеджерРасчета.НастройкиРасчета.Сотрудники = Сотрудники;
МенеджерРасчета.НастройкиРасчета.РассчитыватьНачисления = Истина;
МенеджерРасчета.НастройкиРасчета.ОкончательныйРасчет = Истина;
МенеджерРасчета.НастройкиРасчета.РассчитыватьНДФЛ = Ложь;
МенеджерРасчета.НастройкиНДФЛ.Сотрудники = Сотрудники;
МенеджерРасчета.НастройкиНДФЛ.ОкончательныйРасчет = Истина;
СотрудникиДляНачислений = МенеджерРасчета.ТаблицаСотрудников();
МесяцНачисления = НачалоМесяца(ПараметрыРасчета.Период.ДатаНачала);
Пока МесяцНачисления < ПараметрыРасчета.Период.ДатаОкончания Цикл
Для Каждого Сотрудник Из Сотрудники Цикл
НоваяСтрока = СотрудникиДляНачислений.Добавить();
НоваяСтрока.Сотрудник = Сотрудник;
НоваяСтрока.ДатаНачала = МесяцНачисления;
НоваяСтрока.ДатаОкончания = КонецМесяца(МесяцНачисления);
КонецЦикла;
МесяцНачисления = ДобавитьМесяц(МесяцНачисления, 1);
КонецЦикла;
ОтборМенеджераРасчета = МенеджерРасчета.СоздатьОтборы();
Если ПараметрыРасчета.Свойство("Подразделение") Тогда
ОтборМенеджераРасчета.Подразделение = ПараметрыРасчета.Подразделение;
КонецЕсли;
МенеджерРасчета.ЗаполнитьНачислениеЗарплаты(СотрудникиДляНачислений, ОтборМенеджераРасчета);
И вызываем метод расчета зарплаты
МенеджерРасчета.РассчитатьЗарплату();
Итоговый результат перекладываем в ранее созданную таблицу для СКД
Для Каждого СтрокаНачисления Из МенеджерРасчета.Зарплата.Начисления Цикл
НоваяСтрока = ДанныеФОТ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаНачисления);
НоваяСтрока.Период = СтрокаНачисления.ПериодРегистрации;
КонецЦикла;
В данном случае в таблице МенеджерРасчета.Зарплата.Начисления у нас все результаты расчета, а так же вспомогательные данные. Т.к. в отчете не использую расшифровок, то показатели у строк начисления нет необходимости сохранять.
Как я получаю время за предыдущий период в процедуре ЗаполнитьВремяПоПрошломуПериоду().
Создадим таблицу периодов
ЗарплатаКадрыОбщиеНаборыДанных.СоздатьВТПериоды(
Запрос.МенеджерВременныхТаблиц,
НачалоМесяца(ПараметрыРасчета.Период.ДатаНачала),
КонецМесяца(ПараметрыРасчета.Период.ДатаОкончания),
"МЕСЯЦ",
"КонецМесяца",
"ВТМесяцы",
Истина);
Получим промежуточную временную таблицу с сотрудниками и периодами за текущий и прошлый период для анализа
|ПОМЕСТИТЬ ВТСотрудники
|ИЗ
| ВТСотрудникиОрганизации КАК ВТСотрудникиОрганизации
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТМесяцы КАК ВТМесяцы
| ПО (ИСТИНА)
Время получаем с помощью типового метода
ПараметрыПолученияДанныхОВремени = УчетРабочегоВремениРасширенный.ПараметрыДляСоздатьВТДанныеУчетаРабочегоВремениСотрудников();
УчетРабочегоВремениРасширенный.СоздатьВТДанныеУчетаРабочегоВремениСотрудников(Запрос.МенеджерВременныхТаблиц, Истина, ПараметрыПолученияДанныхОВремени);
А далее просто проверяем, что если для выбранного сотрудника и периода отсутствует время, то вручную устанавливаем показатели времени за предыдущий период с помощью метода МенеджерРасчета.ДобавитьИзвестноеЗначениеПоказателя()
ДанныеУчетаРабочегоВремени = Запрос.Выполнить().Выгрузить();
ДанныеУчетаРабочегоВремени.Индексы.Добавить("Сотрудник, Месяц");
Для Каждого СтрокаНачисления Из МенеджерРасчета.Зарплата.Начисления Цикл
НайденныеСтроки = ДанныеУчетаРабочегоВремени.НайтиСтроки(Новый Структура("Сотрудник, Месяц", СтрокаНачисления.Сотрудник, СтрокаНачисления.ПериодРегистрации));
Если НайденныеСтроки.Количество() = 0 Тогда
НайденныеСтроки = ДанныеУчетаРабочегоВремени.НайтиСтроки(
Новый Структура("Сотрудник, Месяц", СтрокаНачисления.Сотрудник, ДобавитьМесяц(СтрокаНачисления.ПериодРегистрации, -12)));
Если НайденныеСтроки.Количество() Тогда
МенеджерРасчета.ДобавитьИзвестноеЗначениеПоказателя(СтрокаНачисления, ПоказательВремяВДнях, НайденныеСтроки[0].Дней);
МенеджерРасчета.ДобавитьИзвестноеЗначениеПоказателя(СтрокаНачисления, ПоказательНормаДнейПоГрафикуПолногоРабочегоВремени, НайденныеСтроки[0].НормаДней);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Еще раз повторюсь, что в данном случае это очень упрощенный пример. В реальной системе используются более сложные алгоритмы по получению показателей по статистике за предыдущий период или по другим алгоритмам.
Как всегда уточняю, что все тестировалось в конфигурации Зарплата и управление персоналом КОРП, редакция 3.1 (3.1.20.71) на платформе 1С:Предприятие 8.3 (8.3.20.1613).
PS: И наверное просто уже добавлю вне статьи. Коллеги разработчики. Используйте в своих обработках типовые методы подключения внешних обработок. Не нужно изобретать методы как самим инициализировать возвращаемые параметры регистрации обработки. Есть прекрасный метод БСП ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке.
Тут даже документация не нужна, все описание есть в вызываемых методах функций и процедур. Ну и вообще, есть прекрасный документ Система стандартов и методик разработки конфигураций для платформы 1С:Предприятие 8.