При автоматизации кадрового учета и расчета заработной платы (например, при формировании отчетов по резервам отпусков или оценочным обязательствам) часто возникает необходимость программно рассчитать средний заработок сотрудника. В конфигурации «Зарплата и управление персоналом 2.5» типовой расчет привязан к документу НачислениеОтпускаРаботникамОрганизаций. Разберем, как программно создать, рассчитать этот документ и извлечь рассчитанный среднедневной заработок без открытия интерактивной формы.
В отличие от более новых редакций на управляемых формах (ЗУП 3.x), в ЗУП 2.5 логика проведения расчетов сосредоточена в модулях объектов документов и специализированных общих модулях. Это позволяет выполнять расчет без инициализации экранных форм. Однако здесь действует важное техническое ограничение: выполнить расчет исключительно в оперативной памяти невозможно. Метод Рассчитать() обращается к виртуальным таблицам регистров расчета, используя ссылку на сам документ. Поэтому перед запуском расчета документ обязательно должен быть записан в базу данных (хотя бы в режиме РежимЗаписиДокумента.Запись).
Рассмотрим по шагам процесс создания и расчета документа для вычисления среднего заработка:
Документы.НачислениеОтпускаРаботникамОрганизаций.СоздатьДокумент().ОстаткиОтпусков и ЗаполнениеДокументовЗК.мРассчитываемыеТаблицы со свойствами Начисления и РасчетСреднего.Рассчитать(мРассчитываемыеТаблицы).РасчетСреднегоЗаработка переопределяемого модуля для получения итогового значения ставки среднедневного заработка.Проанализируем практический пример кода, который применяется для массового расчета среднедневного заработка сотрудников в обработках и отчетах по резервам отпусков. Чтобы не засорять базу временными документами, расчет выполняется внутри транзакции, которая затем отменяется:
// Задаем параметры расчета
мСведенияОВидахРасчета = Новый Соответствие;
НачатьТранзакцию();
Док = Документы.НачислениеОтпускаРаботникамОрганизаций.СоздатьДокумент();
Док.Дата = КонецГода(ТекущаяДата());
Док.Комментарий = "Служебный документ для расчета резерва";
Док.Организация = ТекОрганизация;
Док.Сотрудник = ВыбранныйСотрудник;
// Обязательная первичная запись для формирования ссылки
Док.Записать(РежимЗаписиДокумента.Запись);
// Заполнение параметров основного отпуска
Док.ДатаНачалаСобытия = ДатаОтчета;
Док.ОсновнойОтпуск = Истина;
Док.ПорядокРасчетаОсновногоОтпуска = Перечисления.ПорядокРасчетаОтпуска.ПоКалендарнымДням;
Док.ДатаНачалаОсновногоОтпуска = ДатаОтчета;
Док.КоличествоДнейОсновногоОтпуска = 1;
Док.ДатаОкончанияОсновногоОтпуска = ДатаОтчета;
ЗаполнениеДокументовЗК.ЗаполнитьДатуВыплатыОтпускных(Док, Док.ДатаНачалаОсновногоОтпуска);
// Определение рабочего года для периода отпуска
ПараметрыРабочегоГода = ОстаткиОтпусков.РабочийГодПериодаОтпуска(
Док.Сотрудник,
Док.ДатаНачалаОсновногоОтпуска,
Док.ДатаОкончанияОсновногоОтпуска,
Док.Ссылка,
Док.ПорядокРасчетаОсновногоОтпуска,
Не Док.ОсновнойОтпуск,
Док.ВидРасчетаДополнительногоОтпуска,
Док.ДатаНачалаДополнительногоОтпуска,
Док.ДатаОкончанияДополнительногоОтпуска
);
ЗаполнитьЗначенияСвойств(Док, ПараметрыРабочегоГода);
// Расчет расчетного периода для среднего заработка
ВидРасчета = Док.ОсновнойВариантРасчетаДокумента().ВидРасчета;
Если ЗначениеЗаполнено(ВидРасчета) Тогда
СведениеОВидеРасчета = ПроведениеРасчетов.ПолучитьСведенияОВидеРасчета(мСведенияОВидахРасчета, ВидРасчета);
МесяцевРасчетногоПериода = ?(СведениеОВидеРасчета.ПорядокОпределенияРасчетногоПериодаСреднегоЗаработка = Перечисления.ПорядокОпределенияРасчетногоПериодаСреднегоЗаработка.ПоКолдоговору,
СведениеОВидеРасчета.ПериодРасчетаСреднегоЗаработка,
НачислениеОтпускаРаботникамОрганизацийПереопределяемый.КоличествоМесяцевРасчетаПоЗакону(Док));
Док.ПериодРасчетаСреднегоЗаработкаНачало = ДобавитьМесяц(НачалоМесяца(Док.ДатаНачалаСобытия), - МесяцевРасчетногоПериода);
Док.ПериодРасчетаСреднегоЗаработкаОкончание = НачалоМесяца(Док.ДатаНачалаСобытия) - 1;
Иначе
МесяцевРасчетногоПериода = 12;
КонецЕсли;
Док.ПериодРегистрации = НачалоМесяца(ДатаОтчета);
Док.Записать(РежимЗаписиДокумента.Запись);
// Формирование структуры и запуск встроенного механизма расчета
мРассчитываемыеТаблицы = Новый Структура("Начисления,РасчетСреднего");
мРассчитываемыеТаблицы.Начисления = Истина;
мРассчитываемыеТаблицы.РасчетСреднего = Истина;
Док.Рассчитать(мРассчитываемыеТаблицы);
// Извлечение рассчитанной среднедневной ставки
СреднедневнойЗаработок = 0;
Если ЗначениеЗаполнено(ВидРасчета) Тогда
СреднедневнойЗаработок = НачислениеОтпускаРаботникамОрганизацийПереопределяемый.РасчетСреднегоЗаработка(
Док,
СведениеОВидеРасчета.СпособРасчета,
МесяцевРасчетногоПериода,
Док.ПериодРасчетаСреднегоЗаработкаНачало,
Док.ПериодРасчетаСреднегоЗаработкаОкончание
);
КонецЕсли;
// Откатываем транзакцию, чтобы документ не остался в базе
ОтменитьТранзакцию();
Рассмотрим важные нюансы, которые следует учитывать при использовании данного подхода:
ОтменитьТранзакцию()) в многопользовательском режиме может вызывать эскалацию блокировок таблиц СУБД, а также приводить к пропускам («дырам») в номерах документов. При массовой обработке сотен сотрудников предпочтительнее создавать один временный документ вне транзакции, последовательно рассчитывать в нем каждого сотрудника, а по окончании обработки удалять объект или просто не сохранять последние изменения.РегистрРасчета.ОсновныеНачисленияРаботниковОрганизаций.БазаОсновныеНачисленияРаботниковОрганизаций с учетом фактически отработанного времени за расчетный период.