При переходе на конфигурацию 1С:Зарплата и управление персоналом 3.1 (ЗУП 3.1) многие разработчики сталкиваются с задачей построения кастомных отчетов по остаткам отпусков. Попытка написать прямой запрос к регистрам сведений и накопления часто приводит к неожиданному результату: для сотрудников, принятых до даты переноса данных (например, в 2012 или 2014 годах), отчет не показывает старые рабочие периоды. При этом стандартная форма Т-2 (Личная карточка) чудесным образом отображает всю историю работы и отгулянных отпусков корректно. Выясним причину этого поведения и разберем правильный способ решения задачи.
Для того чтобы понять суть проблемы, проанализируем структуру хранения данных в ЗУП 3.1 после переноса данных (особенно при «рекомендованном» или «кратком» переносе). База данных хранит сведения об отпусках в разных местах для разных целей:
Реестр отпусков. При переносе данных документ с кодом РОтп (Реестр отпусков) записывает в него исторические данные в виде готовых текстовых строк исключительно для заполнения печатной формы Т-2. Эти записи никак не участвуют в математических расчетах остатков.Фактические отпуска и регистр сведений Заработанные права на отпуска. Однако при переносе данных детальная история по дням за прошлые годы в эти регистры не переносится, так как это сильно перегрузило бы базу данных. Вместо этого на дату начала эксплуатации системы (например, 01.01.2020) вводится документ Ввод начальных остатков отпусков, который фиксирует одну готовую «свернутую» цифру остатка.Таким образом, когда мы пытаемся написать прямой запрос к регистрам ЗаработанныеПраваНаОтпуска и ФактическиеОтпуска за периоды до начала эксплуатации базы, мы получаем пустоту. Программа просто не содержит детальных записей по дням за те годы в расчетных регистрах.
Даже если бы вся история была перенесена детально, написание собственного SQL-запроса для расчета остатков отпусков в ЗУП 3.1 крайне не рекомендуется по следующим причинам:
Поэтому единственным верным и поддерживаемым решением является использование штатного программного интерфейса (API) конфигурации.
В конфигурации ЗУП 3.1 заложен мощный механизм расчета, сосредоточенный в общем модуле ОстаткиОтпусков. Чтобы получить точные данные по остаткам отпусков в разрезе рабочих лет, видов отпусков и сотрудников, нам необходимо использовать функцию ТаблицаОстатковОтпусков.
Рассмотрим подробнее, как инициализировать параметры и вызвать эту функцию на встроенном языке 1С:
// Шаг 1. Подготовим параметры расчета
Параметры = ОстаткиОтпусков.ОписаниеПараметровДляОстаткиОтпусков();
Параметры.ДатаОстатков = РабочаяДата; // Дата, на которую нам нужны остатки
// Шаг 2. Заполним список сотрудников (можно передать массив или таблицу)
СписокСотрудников = Новый Массив;
СписокСотрудников.Добавить(ВыбранныйСотрудник);
Параметры.Сотрудники = СписокСотрудников;
// Укажем, что нам требуется детальный расчет по рабочим годам
Параметры.РассчитыватьОстатки = Истина;
// Шаг 3. Вызываем расчетную функцию
ТаблицаРезультат = ОстаткиОтпусков.ТаблицаОстатковОтпусков(Параметры);
Возвращаемая таблица значений ТаблицаРезультат содержит всю необходимую информацию, включая колонки:
Сотрудник — ссылка на справочник сотрудников;ВидЕжегодногоОтпуска — ссылка на справочник видов отпусков;РабочийГодНачало — дата начала рабочего года;РабочийГодОкончание — дата окончания рабочего года;Остаток — количество оставшихся (или перерасходованных, если число отрицательное) дней отпуска.Для того чтобы построить полноценный отчет на базе Системы компоновки данных (СКД) с использованием программного расчета остатков, мы применим подход с использованием Набора данных - объект. Разберем по шагам, как это сделать.
Шаг 1. Настройка схемы компоновки данных
В схеме компоновки данных вместо традиционного «Набора данных - запрос» создаем «Набор данных - объект». Назовем его, например, ТаблицаОстатков. В этом наборе вручную объявляем поля, которые вернет нам функция общего модуля:
Сотрудник (тип: СправочникСсылка.Сотрудники);ВидОтпуска (тип: СправочникСсылка.ВидыОтпусков);РабочийГодНачало (тип: Дата);РабочийГодОкончание (тип: Дата);ОстатокДней (тип: Число).Шаг 2. Написание кода заполнения в модуле объекта отчета
В модуле объекта отчета переопределяем стандартное событие ПриКомпоновкеРезультата. Нам нужно перехватить процесс компоновки, выполнить программный расчет остатков отпусков по выбранным пользователем параметрам и передать готовую таблицу в СКД.
Посмотрим на пример реализации этого обработчика:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем настройки пользователя
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
// Извлечем дату отчета из параметров СКД
ДатаОтчета = ТекущаяДата();
ПараметрДата = КомпоновщикНастроек.ПолучитьНастройки().ПараметрыДанных.Элементы.Найти("ДатаОтчета");
Если ПараметрДата <> Неопределено И ПараметрДата.Использование Тогда
ДатаОтчета = ПараметрДата.Значение;
КонецЕсли;
// Получаем список сотрудников, отобранных пользователем в настройках отчета
СписокСотрудников = ПолучитьСпискомСотрудниковИзОтбора(); // Вспомогательный метод сбора сотрудников по отборам СКД
// Инициализация структуры параметров для общего модуля
ПараметрыРасчета = ОстаткиОтпусков.ОписаниеПараметровДляОстаткиОтпусков();
ПараметрыРасчета.Сотрудники = СписокСотрудников;
ПараметрыРасчета.ДатаОстатков = ДатаОтчета;
ПараметрыРасчета.РассчитыватьОстатки = Истина;
// Выполняем вычисление остатков через API ЗУП
ВнешняяТаблицаОстатков = ОстаткиОтпусков.ТаблицаОстатковОтпусков(ПараметрыРасчета);
// Приведем колонки к именам, объявленным в Наборе данных - объект
ТаблицаДляСКД = Новый ТаблицаЗначений;
ТаблицаДляСКД.Колонки.Добавить("Сотрудник", Новый ОписаниеТипов("СправочникСсылка.Сотрудники"));
ТаблицаДляСКД.Колонки.Добавить("ВидОтпуска", Новый ОписаниеТипов("СправочникСсылка.ВидыОтпусков"));
ТаблицаДляСКД.Колонки.Добавить("РабочийГодНачало", Новый ОписаниеТипов("Дата"));
ТаблицаДляСКД.Колонки.Добавить("РабочийГодОкончание", Новый ОписаниеТипов("Дата"));
ТаблицаДляСКД.Колонки.Добавить("ОстатокДней", Новый ОписаниеТипов("Число"));
Для Каждого СтрокаОстатка Из ВнешняяТаблицаОстатков Цикл
НоваяСтрока = ТаблицаДляСКД.Добавить();
НоваяСтрока.Сотрудник = СтрокаОстатка.Сотрудник;
НоваяСтрока.ВидОтпуска = СтрокаОстатка.ВидЕжегодногоОтпуска;
НоваяСтрока.РабочийГодНачало = СтрокаОстатка.РабочийГодНачало;
НоваяСтрока.РабочийГодОкончание = СтрокаОстатка.РабочийГодОкончание;
НоваяСтрока.ОстатокДней = СтрокаОстатка.Остаток;
КонецЦикла;
// Передаем таблицу в процессор компоновки данных
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаОстатков", ТаблицаДляСКД);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Реализовав получение данных через встроенный программный интерфейс ОстаткиОтпусков, мы получаем ряд неоспоримых преимуществ:
Используйте этот надежный метод и избегайте написания громоздких прямых запросов к регистрам накопления для расчета остатков отпусков в ЗУП 3.1.