При разработке сложных систем мотивации в 1С:Зарплата и управление персоналом (ЗУП 3.1) или 1С:ERP часто возникает задача: рассчитать премию в текущем месяце, основываясь на данных прошлого месяца, но при этом корректно учесть кадровые изменения (переводы в другие подразделения, смену должностей). В этой статье мы подробно разберем, как программно получить расчетную базу, используя инструменты платформы и типовых конфигураций.
Многие разработчики начинают с попыток использовать метод ПолучитьБазу регистра расчета Начисления. Однако на практике этот метод в чистом виде редко дает нужный результат в типовых конфигурациях, так как не учитывает сложную логику вытеснений, распределений по подразделениям и специфику МенеджераРасчета. Основная сложность заключается в том, что при кадровом переводе в середине месяца база должна «разрезаться» пропорционально периодам работы в разных состояниях. Если просто запросить сумму за прошлый месяц, мы можем потерять аналитику по подразделениям, которая важна для формирования проводок.
В современных конфигурациях на базе «1С:Предприятие 8.3» наиболее правильным способом является использование программного интерфейса РасчетЗарплатыРасширенный. Это позволяет симулировать расчет так, как это делает документ «Начисление зарплаты и взносов». Рассмотрим пример кода, который позволяет инициализировать расчетный механизм:
УстановитьПривилегированныйРежим(Истина);
НачатьТранзакцию();
// Создаем менеджер расчета для нужного периода и организации
МенеджерРасчета = РасчетЗарплатыРасширенный.СоздатьМенеджерРасчета(Объект.МесяцНачисления, Объект.Организация);
МенеджерРасчета.НастройкиРасчета.РассчитыватьНачисления = Истина;
// Исключаем текущий документ из расчетов, чтобы избежать зацикливания или двойного счета
МенеджерРасчета.ИсключаемыйРегистратор = Объект.Ссылка;
СотрудникиДляНачислений = МенеджерРасчета.ТаблицаСотрудников;
СотрудникиДляРассчета = Новый Массив;
// Предположим, у нас есть коллекция сотрудников с датами перемещений
Для Каждого Стр Из КадровыеПереводыСотрудников Цикл
СотрудникиДляРассчета.Добавить(Стр.Сотрудник);
НоваяСтрока = СотрудникиДляНачислений.Добавить();
НоваяСтрока.Сотрудник = Стр.Сотрудник;
// Важно: указываем интервалы, за которые нужно получить расчет
НоваяСтрока.ДатаНачала = Стр.Начало;
НоваяСтрока.ДатаОкончания = Стр.Окончание;
КонецЦикла;
МенеджерРасчета.НастройкиРасчета.Сотрудники = СотрудникиДляРассчета;
// Настраиваем отборы, если расчет идет по конкретному подразделению
ОтборМенеджераРасчета = МенеджерРасчета.СоздатьОтборы();
ОтборМенеджераРасчета.Подразделение = Объект.Подразделение;
// Заполняем и рассчитываем
МенеджерРасчета.ЗаполнитьНачислениеЗарплаты(СотрудникиДляНачислений, ОтборМенеджераРасчета);
МенеджерРасчета.РассчитатьЗарплату();
// Данные рассчитаны в памяти менеджера, теперь их можно анализировать
// После получения данных отменяем транзакцию, чтобы не зафиксировать фиктивные расчеты в базе
ОтменитьТранзакцию();
УстановитьПривилегированныйРежим(Ложь);
Разберем, что происходит в этом коде. Мы используем фиктивный расчет в транзакции. Это мощный прием: мы заставляем систему выполнить все тяжелые расчеты базы, включая сбор данных по прошлым периодам, но в конце делаем ОтменитьТранзакцию(), чтобы результаты не записались в регистры. Это гарантирует, что мы получим актуальные цифры, не «мусоря» в базе данных.
Часто возникает вопрос: почему при смене периода на прошлый месяц расчет выдает пустые значения? Проанализируем причину. В системе 1С существует разделение на ПериодРегистрации и ПериодДействия.
Если в настройках начисления «Премия» указано, что база берется за «Предыдущий месяц», то система автоматически при расчете записи в Сентябре посмотрит все начисления за Август. Если в Августе был кадровый перевод, то в регистре за Август уже существуют две (или более) записи с разными подразделениями. Чтобы премия «увидела» их правильно, само начисление премии должно иметь такие настройки в справочнике Начисления:
Если использование МенеджераРасчета кажется слишком избыточным (например, когда нужно просто получить отчетную информацию), можно воспользоваться запросом к виртуальной таблице РегистрРасчета.Начисления.РасчетнаяБаза. Проанализируем параметры этой таблицы:
Основной секрет здесь кроется в параметре ИзмеренияБазовогоРегистра. Если вы хотите, чтобы база учитывала кадровые перемещения по подразделениям, необходимо передать «Подразделение» в этот параметр. В противном случае система может «схлопнуть» всю базу по сотруднику в одну общую сумму, проигнорировав, что часть месяца он работал в одном отделе, а часть — в другом.
Выясним причину, по которой расчетная база может быть неверной, даже если код написан правильно. В ЗУП 3.1 существует строгая очередность начислений. Если ваша премия является зависимой от других начислений, которые еще не рассчитаны или находятся в этом же документе ниже по списку, база может быть пустой.
При использовании МенеджерРасчета.РассчитатьЗарплату() система старается соблюдать очередность, но важно убедиться, что базовые начисления за прошлый период уже окончательно зафиксированы в базе (проведены). Если кадровое перемещение было введено «задним числом», необходимо убедиться, что записи пересчета (регистр сведений ПересчетыЗарплаты) обработаны, иначе МенеджерРасчета может взять старые, неактуальные данные до перевода.
Для гарантированного получения базы с учетом перемещений рекомендуем следующий алгоритм:
МенеджерРасчета для выполнения полноценного программного расчета.ДатаНачала и ДатаОкончания в таблице СотрудникиДляНачислений — они должны соответствовать фактическим периодам работы сотрудника в каждом подразделении в базовом месяце.Сотрудник и Подразделение.ОтменитьТранзакцию() позволит вам получить данные без побочных эффектов для учета.