Как в 1С:ЗУП 2.5 программно рассчитать отпуск и получить средний заработок?

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

При автоматизации кадрового учета и расчета заработной платы (например, при формировании отчетов по резервам отпусков или оценочным обязательствам) часто возникает необходимость программно рассчитать средний заработок сотрудника. В конфигурации «Зарплата и управление персоналом 2.5» типовой расчет привязан к документу НачислениеОтпускаРаботникамОрганизаций. Разберем, как программно создать, рассчитать этот документ и извлечь рассчитанный среднедневной заработок без открытия интерактивной формы.

Особенности архитектуры расчета в ЗУП 2.5

В отличие от более новых редакций на управляемых формах (ЗУП 3.x), в ЗУП 2.5 логика проведения расчетов сосредоточена в модулях объектов документов и специализированных общих модулях. Это позволяет выполнять расчет без инициализации экранных форм. Однако здесь действует важное техническое ограничение: выполнить расчет исключительно в оперативной памяти невозможно. Метод Рассчитать() обращается к виртуальным таблицам регистров расчета, используя ссылку на сам документ. Поэтому перед запуском расчета документ обязательно должен быть записан в базу данных (хотя бы в режиме РежимЗаписиДокумента.Запись).

Пошаговый алгоритм программного расчета

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

  1. Создаем новый экземпляр документа через Документы.НачислениеОтпускаРаботникамОрганизаций.СоздатьДокумент().
  2. Заполняем реквизиты шапки: организацию, сотрудника, дату начала события и границы отпуска.
  3. Определяем рабочий год и период расчета среднего заработка с помощью типовых процедур общих модулей ОстаткиОтпусков и ЗаполнениеДокументовЗК.
  4. Записываем документ, чтобы получить валидную ссылку в информационной базе.
  5. Формируем управляющую структуру мРассчитываемыеТаблицы со свойствами Начисления и РасчетСреднего.
  6. Вызываем метод объекта документа Рассчитать(мРассчитываемыеТаблицы).
  7. Используем функцию РасчетСреднегоЗаработка переопределяемого модуля для получения итогового значения ставки среднедневного заработка.

Пример реализации с использованием транзакции

Проанализируем практический пример кода, который применяется для массового расчета среднедневного заработка сотрудников в обработках и отчетах по резервам отпусков. Чтобы не засорять базу временными документами, расчет выполняется внутри транзакции, которая затем отменяется:


// Задаем параметры расчета
мСведенияОВидахРасчета = Новый Соответствие;

НачатьТранзакцию();

Док = Документы.НачислениеОтпускаРаботникамОрганизаций.СоздатьДокумент();
Док.Дата = КонецГода(ТекущаяДата());
Док.Комментарий = "Служебный документ для расчета резерва";
Док.Организация = ТекОрганизация;
Док.Сотрудник = ВыбранныйСотрудник;

// Обязательная первичная запись для формирования ссылки
Док.Записать(РежимЗаписиДокумента.Запись);

// Заполнение параметров основного отпуска
Док.ДатаНачалаСобытия = ДатаОтчета;
Док.ОсновнойОтпуск = Истина;
Док.ПорядокРасчетаОсновногоОтпуска = Перечисления.ПорядокРасчетаОтпуска.ПоКалендарнымДням;
Док.ДатаНачалаОсновногоОтпуска = ДатаОтчета;
Док.КоличествоДнейОсновногоОтпуска = 1;
Док.ДатаОкончанияОсновногоОтпуска = ДатаОтчета;

ЗаполнениеДокументовЗК.ЗаполнитьДатуВыплатыОтпускных(Док, Док.ДатаНачалаОсновногоОтпуска);

// Определение рабочего года для периода отпуска
ПараметрыРабочегоГода = ОстаткиОтпусков.РабочийГодПериодаОтпуска(
    Док.Сотрудник, 
    Док.ДатаНачалаОсновногоОтпуска, 
    Док.ДатаОкончанияОсновногоОтпуска, 
    Док.Ссылка, 
    Док.ПорядокРасчетаОсновногоОтпуска, 
    Не Док.ОсновнойОтпуск, 
    Док.ВидРасчетаДополнительногоОтпуска, 
    Док.ДатаНачалаДополнительногоОтпуска, 
    Док.ДатаОкончанияДополнительногоОтпуска
);
ЗаполнитьЗначенияСвойств(Док, ПараметрыРабочегоГода);

// Расчет расчетного периода для среднего заработка
ВидРасчета = Док.ОсновнойВариантРасчетаДокумента().ВидРасчета;
Если ЗначениеЗаполнено(ВидРасчета) Тогда
    СведениеОВидеРасчета = ПроведениеРасчетов.ПолучитьСведенияОВидеРасчета(мСведенияОВидахРасчета, ВидРасчета);
    МесяцевРасчетногоПериода = ?(СведениеОВидеРасчета.ПорядокОпределенияРасчетногоПериодаСреднегоЗаработка = Перечисления.ПорядокОпределенияРасчетногоПериодаСреднегоЗаработка.ПоКолдоговору,
        СведениеОВидеРасчета.ПериодРасчетаСреднегоЗаработка,
        НачислениеОтпускаРаботникамОрганизацийПереопределяемый.КоличествоМесяцевРасчетаПоЗакону(Док));
    
    Док.ПериодРасчетаСреднегоЗаработкаНачало = ДобавитьМесяц(НачалоМесяца(Док.ДатаНачалаСобытия), - МесяцевРасчетногоПериода);
    Док.ПериодРасчетаСреднегоЗаработкаОкончание = НачалоМесяца(Док.ДатаНачалаСобытия) - 1;
Иначе
    МесяцевРасчетногоПериода = 12;
КонецЕсли;

Док.ПериодРегистрации = НачалоМесяца(ДатаОтчета);
Док.Записать(РежимЗаписиДокумента.Запись);

// Формирование структуры и запуск встроенного механизма расчета
мРассчитываемыеТаблицы = Новый Структура("Начисления,РасчетСреднего");
мРассчитываемыеТаблицы.Начисления = Истина;
мРассчитываемыеТаблицы.РасчетСреднего = Истина;

Док.Рассчитать(мРассчитываемыеТаблицы);

// Извлечение рассчитанной среднедневной ставки
СреднедневнойЗаработок = 0;
Если ЗначениеЗаполнено(ВидРасчета) Тогда
    СреднедневнойЗаработок = НачислениеОтпускаРаботникамОрганизацийПереопределяемый.РасчетСреднегоЗаработка(
        Док, 
        СведениеОВидеРасчета.СпособРасчета, 
        МесяцевРасчетногоПериода, 
        Док.ПериодРасчетаСреднегоЗаработкаНачало, 
        Док.ПериодРасчетаСреднегоЗаработкаОкончание
    );
КонецЕсли;

// Откатываем транзакцию, чтобы документ не остался в базе
ОтменитьТранзакцию();

Подводные камни и рекомендации по оптимизации

Рассмотрим важные нюансы, которые следует учитывать при использовании данного подхода:

  1. Блокировки и нумерация: Постоянное открытие транзакций с последующей отменой (ОтменитьТранзакцию()) в многопользовательском режиме может вызывать эскалацию блокировок таблиц СУБД, а также приводить к пропускам («дырам») в номерах документов. При массовой обработке сотен сотрудников предпочтительнее создавать один временный документ вне транзакции, последовательно рассчитывать в нем каждого сотрудника, а по окончании обработки удалять объект или просто не сохранять последние изменения.
  2. Сотрудники без заработка: Если у сотрудника не было начислений за последние 12 месяцев (например, декретный отпуск или отпуск по уходу за ребенком), функция вернет нулевое значение. По законодательству в таких случаях средний заработок должен рассчитываться из установленной тарифной ставки или оклада, что требует отдельной программной проверки возвращенного результата.
  3. Альтернатива через запрос: Для тяжелых регламентных заданий создание документов является ресурсоемкой операцией. Наиболее производительным решением для оценки резервов является расчет базы напрямую запросом к виртуальной таблице РегистрРасчета.ОсновныеНачисленияРаботниковОрганизаций.БазаОсновныеНачисленияРаботниковОрганизаций с учетом фактически отработанного времени за расчетный период.
← На главную