При разработке подсистем планирования или учета рабочего времени в 1С программисты часто сталкиваются с необходимостью отображения сменных графиков (например, 2/2, 3/2 или индивидуальных расписаний). Основная сложность заключается в том, что графики могут быть циклическими, прерываться праздниками или иметь несколько рабочих интервалов в течение одного дня. Попытка написать «свой велосипед» для расчета таких дат часто приводит к ошибкам: графики начинают «плыть» при появлении праздничных дней, а данные в запросах превращаются в неструктурированный шум.
В этой статье мы проанализируем типичные ошибки при работе с регистрами сведений и разберем, как правильно использовать возможности Библиотеки стандартных подсистем (БСП) для работы с графиками работы.
Рассмотрим ситуацию, с которой столкнулся автор темы. Имеется справочник с расписанием, где указаны номера дней цикла и время работы. При попытке соединить эти данные с календарем в запросе и вычислить рабочие дни через циклы в коде, возникают следующие проблемы:
Посмотрим на пример кода, который содержал ошибку и приводил к некорректным результатам:
// Пример ошибочного подхода с ручным вычислением итератора
Для Итератор = 0 По КоличествоДнейПериода - 1 Цикл
НачалоПериода = Период.ДатаНачала + (86400 * Итератор) + (86400 * НеРабочийДень);
// Попытка вычислить НомерДня вручную через условия
Если Итератор = КоличествоДнейГрафика ИЛИ НомерДняНачала = КоличествоДнейГрафика Тогда
НомерДняНачала = 1;
КонецЕсли;
// ... дальнейшая сложная логика поиска в таблице значений
КонецЦикла;
Проанализируем ситуацию: такой код крайне сложно отлаживать, и он не учитывает переносы выходных. Решение кроется в использовании стандартных механизмов.
Если ваша конфигурация основана на БСП (что актуально для большинства современных решений на платформе 1С 8.3), вам не нужно писать запросы к регистру КалендарныеГрафики вручную. В системе уже существует общий модуль ГрафикиРаботы, который берет на себя всю математику периодов.
Рассмотрим основные методы, которые помогут решить задачу:
ГрафикиРаботы.ДанныеГрафиковЗаПериод — этот метод возвращает таблицу значений, в которой для каждой даты периода указано, является ли день рабочим и сколько в нем рабочих часов.ГрафикиРаботы.РасписаниеРаботыЗаПериод — позволяет получить детальную информацию о временных интервалах (начало и окончание смен) внутри каждого дня.Пример правильного получения данных за период:
ПараметрыЗаполнения = ГрафикиРаботы.ПараметрыЗаполненияГрафика();
ТаблицаДанных = ГрафикиРаботы.ДанныеГрафиковЗаПериод(ВыбранныйГрафик, ДатаНачала, ДатаОкончания);
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
Если СтрокаДанных.РабочийДень Тогда
// Здесь мы точно знаем, что день рабочий, с учетом всех праздников БСП
Сообщить("Дата: " + СтрокаДанных.Дата + " Часов: " + СтрокаДанных.ВремяВЧасах);
КонецЕсли;
КонецЦикла;
Если вы работаете в конфигурации без БСП, разберем математическую логику, которая позволит графику не «плыть». Для этого вводится понятие Даты отсчета (даты начала работы по данному графику).
Выясним причину корректного расчета: нам нужно использовать оператор остатка от деления %. Пусть цикл составляет 4 дня (2 рабочих, 2 выходных). Общая длительность цикла — 4 дня.
// Определяем номер дня в цикле
СекундВСутках = 86400;
РазницаВТаймстампе = ТекущаяДата - ДатаОтсчета;
ДнейПрошло = Целое(РазницаВТаймстампе / СекундВСутках);
НомерДняЦикла = (ДнейПрошло % ДлительностьЦикла) + 1;
// Если НомерДняЦикла 1 или 2 — день рабочий, если 3 или 4 — выходной.
Важный момент: Чтобы учесть праздники в этой схеме, необходимо перед расчетом проверить текущую дату по Регистру Сведений ПроизводственныйКалендарь. Если день праздничный, мы либо пропускаем его (смена сдвигается), либо считаем выходным (смена «сгорает»). Это настраивается в логике заполнения регистра КалендарныеГрафики.
Проанализируем ситуацию, когда у сотрудника есть основной график, но в конкретном месяце он работает по-другому. Для этого в типовых решениях (ЗУП, ERP) используется механизм Индивидуальных графиков.
При построении запроса или использовании функций планировщика важно соблюдать приоритетность:
Для корректной сортировки в планировщике используйте объединение по дате и времени начала. В запросе это выглядит так:
ВЫБРАТЬ
Графики.Дата КАК Дата,
Графики.ВремяНачала КАК ВремяНачала,
Графики.ВремяОкончания КАК ВремяОкончания
ИЗ
РегистрСведений.КалендарныеГрафики КАК Графики
ГДЕ
Графики.Календарь = &КалендарьСотрудника
И Графики.Дата МЕЖДУ &НачалоПериода И &КонецПериода
УПОРЯДОЧИТЬ ПО
Дата,
ВремяНачала
Использование типового функционала ГрафикиРаботы из состава БСП — это наиболее надежный способ реализации подсистемы планирования. Он избавляет разработчика от необходимости вручную обрабатывать високосные годы, переносы праздников и сложные циклы сменности. Если же вы реализуете механизм самостоятельно, всегда опирайтесь на фикисированную дату начала цикла и производственный календарь, а не на порядковые номера строк в результате запроса.