В процессе разработки или поддержки систем на базе 1С:Предприятие 8, особенно в конфигурациях Зарплата и Управление Персоналом (ЗУП), программисты часто сталкиваются с загадочным поведением виртуальной таблицы ФактическийПериодДействия. Суть проблемы заключается в том, что при наложении одного начисления на другое (например, Больничного на Невыход) в результате запроса появляются микро-интервалы длиной в одну секунду (например, с 00:00:01 до 23:59:59), хотя записи должны полностью вытеснять друг друга. Разберем детально, почему это происходит и как привести данные в порядок.
Для начала проанализируем, как платформа 1С воспринимает границы времени. Существует два принципиально разных подхода к записи конца периода в регистрах расчета, и их смешивание — главная причина проблем:
23:59:59 текущего дня. Это кажется логичным с точки зрения человека, но неудобно для математических вычислений платформы.00:00:00). Например, если сотрудник болел по 16-е число включительно, то в поле ПериодДействияКонец будет стоять 17.06.2023 00:00:00. Именно этот стандарт принят в современных версиях ЗУП 3.1.Проблема «лишней секунды» возникает в тот момент, когда вытесняемая запись (например, Невыход) имеет границу 23:59:59, а вытесняющая запись (Больничный) начинается в 00:00:00. Между этими моментами остается микроскопический зазор, который платформа добросовестно возвращает нам в фактическом периоде действия как невытесненный остаток.
Прежде чем исправлять код, нам необходимо убедиться, что именно хранится в физической таблице регистра. Рассмотрим типичный запрос, который помогает выявить «грязные» данные. Мы не должны ограничиваться только виртуальной таблицей, важно посмотреть на первоисточник:
ВЫБРАТЬ
Регистр.ПериодДействияНачало,
Регистр.ПериодДействияКонец,
Регистр.ВидРасчета,
Регистр.Сотрудник
ИЗ
РегистрРасчета.ОсновныеНачисленияРаботниковОрганизаций КАК Регистр
ГДЕ
Регистр.Сотрудник = &Сотрудник
И Регистр.ПериодДействия МЕЖДУ &НачалоМесяца И &КонецМесяца
Если в результате выполнения этого запроса мы видим время, отличное от 00:00:00 (например, 16.06.2023 23:59:59 или 12.06.2023 00:00:01), значит, данные в регистр были записаны некорректно. Это могло произойти из-за использования внешних обработок, неправильно написанного программного переноса данных или ошибок в старых алгоритмах локализации.
Важно помнить, что настройка Периодичность = День в свойствах регистра расчета не означает, что время игнорируется платформой. Эта настройка лишь определяет минимально возможный квант времени. Платформа все равно оперирует типом Дата, который включает в себя часы, минуты и секунды. Если в записи регистра «проскочила» хотя бы одна секунда смещения, механизм вытеснения отработает математически точно, «разрезав» период именно по этой секунде.
Выясним причину появления странных интервалов типа 16.06.2023 0:00:01 - 16.06.2023 23:59:59. Это явный признак того, что система пытается «втиснуть» невытесненный остаток в границы одного дня, потому что либо начало вытесняющей записи началось не с начала дня, либо конец предыдущей записи был смещен.
Механизм ФактическийПериодДействия работает на основе настроек вытеснения, заданных в Плане видов расчета. Проанализируем настройки:
План видов расчета.ОсновныеНачисления.Невыход.Больничный находится в списке «Вытесняющие виды расчета».Если вытеснение настроено корректно, но разрывы остаются, значит, проблема исключительно в данных (в секундах). Если же вытеснения нет вовсе, то фактический период будет равен периоду действия, вне зависимости от наложения записей.
Чтобы исправить ситуацию и исключить появление «лишних» интервалов, мы должны принудительно нормализовать даты при записи в регистр. Рассмотрим пример кода, который гарантирует, что границы периода всегда будут установлены на 00:00:00.
// Пример нормализации дат перед записью в регистр расчета
НоваяЗапись = РегистрРасчетаМенеджер.СоздатьНаборЗаписей();
// ... заполнение набора ...
Для Каждого Запись Из НаборЗаписей Цикл
// Начало всегда должно быть началом дня
Запись.ПериодДействияНачало = НачалоДня(Запись.ПериодДействияНачало);
// Конец периода: если используем стандарт ЗУП 3.1,
// то это начало следующего дня после фактической даты окончания
Запись.ПериодДействияКонец = НачалоДня(КонецДня(Запись.ДатаОкончанияДокумента) + 1);
КонецЦикла;
НаборЗаписей.Записать();
Посмотрим на пример: если сотрудник болел с 12 по 16 июня, то ПериодДействияНачало будет 12.06.2023 00:00:00, а ПериодДействияКонец станет 17.06.2023 00:00:00. При такой записи вытеснение сработает идеально, так как границы записей будут «стыковаться» секунда в секунду.
Если мы уже имеем дело с «грязными» данными в базе и не можем их быстро исправить, при получении ФактическогоПериодаДействия в запросе рекомендуется использовать функции приведения дат, чтобы сгруппировать микро-разрывы. Однако это лишь временная мера. Правильный подход — использование условий в виртуальной таблице:
ВЫБРАТЬ
ФактическийПериод.ПериодДействияНачало,
ФактическийПериод.ПериодДействияКонец,
ФактическийПериод.ВидРасчета
ИЗ
РегистрРасчета.ОсновныеНачисления.ФактическийПериодДействия(
ВидРасчета = &НужныйВидРасчета
И Сотрудник = &Сотрудник
) КАК ФактическийПериод
Важный нюанс: Виртуальная таблица ФактическийПериодДействия не хранится в СУБД физически. Она вычисляется «на лету» в момент выполнения запроса. Платформа берет ПериодДействия и последовательно вычитает из него интервалы всех вытесняющих записей. Поэтому любые «хвосты» в секундах в исходных данных всегда приведут к дребезгу в результате этого вычисления.
Подводя итог, проанализируем основные шаги по устранению ошибок вытеснения:
00:00:00 следующего дня.00:00:01).Плане видов расчета убедитесь, что вытеснение настроено в нужную сторону (вытесняющий вид расчета должен быть в списке у вытесняемого).Соблюдение этих правил позволит избежать появления странных односекундных начислений в расчетных листках и обеспечит корректный расчет отработанного времени.