Почему программный расчет остатков отпусков в ЗУП 3.1 выдает на 2.33 дня меньше, чем типовой отчет?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом
← На главную

При разработке внешних печатных форм или кастомных отчетов в конфигурации 1С:Зарплата и Управление Персоналом 3.1 программисты часто сталкиваются с неприятной особенностью: функция программного получения остатка отпуска выдает результат, который отличается от типовой справки об отпусках ровно на 2.33 дня. Как мы знаем, 2.33 — это стандартное количество дней отпуска, полагающееся сотруднику за один полностью отработанный месяц (при норме 28 дней в году).

Разберем подробно, почему возникает эта «потеря» месяца и как правильно вызывать программные функции, чтобы их результат на 100% совпадал с данными кадрового учета.

Причина 1: Магия «граничного момента» времени

Проанализируем ситуацию, когда мы передаем дату в функцию ОстаткиОтпусковСотрудниковНаДату. Внутри программного кода типовой конфигурации эта функция часто принудительно приводит переданную дату к началу дня с помощью оператора НачалоДня(ДатаОстатков).

Рассмотрим, как это влияет на логику программы. В 1С:ЗУП 3.1 право на очередной месяц отпуска (те самые 2.33 дня) возникает, когда сотрудник отработал более 15 дней в текущем рабочем месяце. Если дата остатков попадает точно на границу, когда до выполнения условия «отработано полных 15 дней» не хватает буквально одной секунды, программа отбрасывает этот месяц.

Типовые отчеты (например, «Справка об остатках отпусков») при формировании «на дату» фактически рассчитывают данные на 00:00:00 следующего дня. Это позволяет включить весь рабочий день выбранной даты в стаж сотрудника. Если же мы вызываем функцию программно и передаем туда дату без учета времени, она воспринимается как '2023-05-20 00:00:00'. В итоге 20-е число в расчет не попадает, и если именно этого дня не хватало для закрытия очередного месяца стажа, мы получаем заниженный результат.

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

Для того чтобы программный вызов функции ОстаткиОтпусковСотрудниковНаДату вернул те же данные, что и пользователь видит в интерфейсе, попробуем передавать дату «с запасом» в один день или использовать конец дня.

Посмотрим на пример кода, который часто приводит к ошибке:


// Ошибочный вариант: может не хватать 2.33 дня
МассивСотрудников = Новый Массив;
МассивСотрудников.Добавить(СсылкаНаСотрудника);
Остатки = ОстаткиОтпусков.ОстаткиОтпусковСотрудниковНаДату(МассивСотрудников, ДатаОтчета);

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


// Правильный вариант №1: Смещение на начало следующего дня
ДатаРасчета = НачалоДня(ДатаОтчета) + 86400; // Добавляем сутки
Остатки = ОстаткиОтпусков.ОстаткиОтпусковСотрудниковНаДату(МассивСотрудников, ДатаРасчета);

// Правильный вариант №2: Использование КонецМесяца (если расчет идет на конец периода)
Остатки = ОстаткиОтпусков.ОстаткиОтпусковСотрудниковНаДату(МассивСотрудников, КонецМесяца(ДатаОтчета));

Причина 2: Разница между функцией-оберткой и сложным расчетом в СКД

Выясним еще один важный технический момент. Функция ОстаткиОтпусковСотрудниковНаДату в общем модуле ОстаткиОтпусков является упрощенной «оберткой». Типовые отчеты в ЗУП 3.1 строятся на системе компоновки данных (СКД), которая обращается к виртуальным таблицам и более сложным внутренним механизмам, учитывающим не только фактические остатки, но и планируемые отпуска, а также корректировки.

Если простая корректировка даты не помогает, стоит обратить внимание на процедуру СформироватьТаблицуОстатковОтпусков. Она позволяет получить более детальную структуру данных. Проанализируем пример ее вызова:


СтруктураПараметров = ОстаткиОтпусков.ОписаниеПараметровДляОстаткиОтпусков();
СтруктураПараметров.Сотрудники = МассивСотрудников;
СтруктураПараметров.ДатаОстатков = КонецДня(ДатаОтчета);
// Установка флага, чтобы расчет соответствовал регламентированному учету
СтруктураПараметров.ВидыОтпусков = ВидыОтпусков; 

РезультатТаблица = ОстаткиОтпусков.ОстаткиОтпусков(СтруктураПараметров);

Именно использование расширенной структуры параметров позволяет избежать «потери» данных из-за специфических настроек учета (например, если в программе включено использование общих остатков отпусков или ведутся сложные расчеты по нескольким видам отпусков одновременно).

Причина 3: Исключаемые периоды и сдвиг рабочего года

Разберем ситуацию, связанную с отпусками за свой счет (без сохранения оплаты). Согласно законодательству, если сотрудник берет более 14 дней за свой счет в течение рабочего года, дата окончания его рабочего года смещается.

Типовой отчет при формировании справки анализирует все документы-отклонения. Если ваша программная функция вызывается в контексте, где не обновлены временные таблицы запроса или не учтены последние проведенные документы (в случае внешней обработки), расчет может «промахнуться» мимо даты окончания рабочего года на те же 2.33 дня. Всегда проверяйте, чтобы на момент вызова функции все кадровые приказы были проведены.

Подведем итоги

Если вы обнаружили расхождение в 2.33 дня при использовании программного кода в ЗУП 3.1, выполните следующие шаги:

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

Помните, что в версии 3.1 разработчики 1С заложили иную логику по сравнению с версией 2.5, и именно дата среза теперь играет ключевую роль в определении права на накопленные дни отдыха.

← На главную