В практике доработки конфигурации «1С:Зарплата и управление персоналом» (ЗУП) программисты часто сталкиваются с необходимостью создания собственных формул расчета. Одна из распространенных задач — реализация специфических условий оплаты работы в выходные и праздничные дни. Однако при внедрении собственных механизмов, основанных на периодических показателях, можно столкнуться с неочевидным поведением системы: показатель, записанный в регистр, игнорируется при расчете именно в последний день месяца.
В этой статье мы подробно разберем ситуацию, когда пользовательский показатель (флаг) установлен, но формула расчета возвращает ноль. Проанализируем технические причины, связанные с регистрами сведений, и рассмотрим правильные методы решения.
Рассмотрим конкретный сценарий. В систему внедрена нетиповая логика расчета оплаты выходного дня. Вместо стандартного механизма используется произвольная формула начисления:
?(ОплатаВыходногоДня = 1, ВремяВДняхЧасах * СтоимостьДняЧаса, 0)
Где ОплатаВыходногоДня — это созданный пользователем показатель расчета зарплаты.
Работа механизма организована следующим образом:
ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудников.Симптомы проблемы: При расчете заработной платы за май, строка с начислением оказывается пустой или равной нулю, несмотря на то, что сотрудник реально работал 31 мая, и запись в регистре присутствует. Если же аналогичная ситуация происходит в середине месяца, расчет выполняется корректно.
Чтобы понять, почему начисление не видит показатель, нам необходимо изучить то, как платформа 1С и механизм расчета зарплаты (движок ЗУП) работают с периодическими данными.
Регистр сведений ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудников хранит историю изменений значений, по аналогии с тем, как ведется кадровая история сотрудников интервальный. Когда мы указываем дату окончания действия («Действует до»), система должна определить интервал, в котором показатель активен.
В описанном случае период записи (начало действия) — 31.05.2024 0:00:00. Дата окончания действия («Действует до») также установлена как 31.05.2024. Здесь возникает коллизия, которую можно трактовать двояко:
Особенно остро это проявляется именно в последний день месяца, так как это граничная дата периода регистрации документа «Начисление зарплаты». В середине месяца (например, 15-го числа) следующий день (16-е) еще принадлежит текущему периоду расчета, и алгоритмы могут «прощать» неточности в часах/секундах, но на стыке месяцев логика становится строже.
Если мы хотим сохранить текущую архитектуру («велосипед») и заставить показатель работать, нам необходимо явно указать системе, что показатель действует весь день 31 мая.
Для этого при программной записи движений в регистр сведений следует устанавливать дату окончания действия (поле «Действует до») не текущим днем, а началом следующего дня или первым числом следующего месяца.
Давайте посмотрим, как следует изменить логику заполнения набора записей:
// Неправильный подход, который приводит к ошибке:
НаборЗаписей.ДействуетДо = ДатаДокумента; // Например, 31.05.2024
// Правильный подход:
// Устанавливаем дату окончания действия на 1-е число следующего месяца
// или на следующий день, чтобы перекрыть весь текущий день.
НаборЗаписей.ДействуетДо = КонецДня(ДатаДокумента) + 1;
// Или еще надежнее:
НаборЗаписей.ДействуетДо = НачалоДня(ДобавитьМесяц(ДатаДокумента, 1)); // 01.06.2024
Установив дату «Действует до» равной 01.06.2024, мы гарантируем, что в любой момент времени 31 мая (будь то начало, середина или конец смены) показатель ОплатаВыходногоДня будет равен 1.
Второй способ решения проблемы заключается в пересмотре настроек самого показателя в режиме «Предприятие». Проблема, описанная выше, характерна для показателей с типом назначения «Периодический» (постоянного использования), которые мы пытаемся использовать как разовые события.
Давайте изменим настройки показателя ОплатаВыходногоДня:
В этом случае нам не придется управлять датой отмены действия («Действует до»). Значение будет привязано к конкретному месяцу или дате через документ «Данные для расчета зарплаты», и механизм расчета корректно учтет его при финальном расчете, просуммировав все вводы за месяц (если выбран накапливаемый вариант) или использовав факт ввода.
Если вы используете программное создание документа «Данные для расчета зарплаты» вместо прямой записи в регистр, это также решит проблему целостности данных, исключая необходимость редактирования движений в регистрах.
Наиболее надежный и профессиональный путь — отказаться от использования искусственного показателя-флага ОплатаВыходногоДня. Типовая конфигурация ЗУП 3.1 уже содержит мощный механизм учета работы в выходные дни, который не требует доработок кода.
Давайте разберем, как это работает в стандарте:
ВремяВДняхЧасах * СтоимостьДняЧаса * 2
ВремяВДняхЧасах является предопределенным. Самое важное: он автоматически возвращает количество часов только за те дни, когда зафиксирован соответствующий вид времени (РВ).Почему это лучше?
ВремяВДняхЧасах по этому виду начисления будет равно 0. Результат формулы = 0.?(Условие, ..., 0), так как умножение на ноль времени само дает ноль.Использование типового механизма избавляет от проблем с «границами дат», «срезами последних» и необходимостью поддерживать сложный кастомный код при обновлениях 1С.
Если вы столкнулись с подобной ситуацией и хотите точно убедиться в причине, рекомендуем использовать следующие инструменты:
Значения периодических показателей расчета зарплаты (для сотрудников).
Отберите записи по проблемному сотруднику и показателю. Внимательно посмотрите на колонки «Период» и «Действует до». Если они совпадают с точностью до секунды или дня (в зависимости от типа поля), это верный признак проблемы.
Подводя итог: при работе с регистрами сведений, имеющими период действия, всегда следите за тем, чтобы дата окончания («Действует до») была больше даты начала, и желательно перекрывала весь необходимый временной отрезок, особенно на стыке календарных месяцев.