Как реализовать расчет графиков работы в 1С и избежать «плывущих» дат

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

При разработке подсистем планирования или учета рабочего времени в 1С программисты часто сталкиваются с необходимостью отображения сменных графиков (например, 2/2, 3/2 или индивидуальных расписаний). Основная сложность заключается в том, что графики могут быть циклическими, прерываться праздниками или иметь несколько рабочих интервалов в течение одного дня. Попытка написать «свой велосипед» для расчета таких дат часто приводит к ошибкам: графики начинают «плыть» при появлении праздничных дней, а данные в запросах превращаются в неструктурированный шум.

В этой статье мы проанализируем типичные ошибки при работе с регистрами сведений и разберем, как правильно использовать возможности Библиотеки стандартных подсистем (БСП) для работы с графиками работы.

Проблема: Почему «плывет» самописный график

Рассмотрим ситуацию, с которой столкнулся автор темы. Имеется справочник с расписанием, где указаны номера дней цикла и время работы. При попытке соединить эти данные с календарем в запросе и вычислить рабочие дни через циклы в коде, возникают следующие проблемы:

  1. Отсутствие привязки к точке отсчета: Для циклических графиков (2/2) критически важно иметь дату начала цикла. Без нее невозможно определить, каким днем является «сегодня» — первым рабочим, вторым или выходным.
  2. Неправильная обработка праздников: Если в алгоритме не учтен производственный календарь, то любой общегосударственный праздник, который должен быть выходным, либо игнорируется, либо сдвигает всю цепочку рабочих смен на один день вперед, что недопустимо.
  3. Дублирование данных в запросе: При левом соединении таблиц расписания и календарных графиков часто возникают «лишние» строки, если у сотрудника в один день задано несколько интервалов времени (например, утренняя и вечерняя смены).

Посмотрим на пример кода, который содержал ошибку и приводил к некорректным результатам:


// Пример ошибочного подхода с ручным вычислением итератора
Для Итератор = 0 По КоличествоДнейПериода - 1 Цикл
    НачалоПериода = Период.ДатаНачала + (86400 * Итератор) + (86400 * НеРабочийДень);
    // Попытка вычислить НомерДня вручную через условия
    Если Итератор = КоличествоДнейГрафика ИЛИ НомерДняНачала = КоличествоДнейГрафика Тогда
        НомерДняНачала = 1;
    КонецЕсли;
    // ... дальнейшая сложная логика поиска в таблице значений
КонецЦикла;

Проанализируем ситуацию: такой код крайне сложно отлаживать, и он не учитывает переносы выходных. Решение кроется в использовании стандартных механизмов.

Решение 1: Использование общего модуля ГрафикиРаботы (БСП)

Если ваша конфигурация основана на БСП (что актуально для большинства современных решений на платформе 1С 8.3), вам не нужно писать запросы к регистру КалендарныеГрафики вручную. В системе уже существует общий модуль ГрафикиРаботы, который берет на себя всю математику периодов.

Рассмотрим основные методы, которые помогут решить задачу:

Пример правильного получения данных за период:


ПараметрыЗаполнения = ГрафикиРаботы.ПараметрыЗаполненияГрафика();
ТаблицаДанных = ГрафикиРаботы.ДанныеГрафиковЗаПериод(ВыбранныйГрафик, ДатаНачала, ДатаОкончания);

Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
    Если СтрокаДанных.РабочийДень Тогда
        // Здесь мы точно знаем, что день рабочий, с учетом всех праздников БСП
        Сообщить("Дата: " + СтрокаДанных.Дата + " Часов: " + СтрокаДанных.ВремяВЧасах);
    КонецЕсли;
КонецЦикла;

Решение 2: Математика циклического графика (если нет БСП)

Если вы работаете в конфигурации без БСП, разберем математическую логику, которая позволит графику не «плыть». Для этого вводится понятие Даты отсчета (даты начала работы по данному графику).

Выясним причину корректного расчета: нам нужно использовать оператор остатка от деления %. Пусть цикл составляет 4 дня (2 рабочих, 2 выходных). Общая длительность цикла — 4 дня.


// Определяем номер дня в цикле
СекундВСутках = 86400;
РазницаВТаймстампе = ТекущаяДата - ДатаОтсчета;
ДнейПрошло = Целое(РазницаВТаймстампе / СекундВСутках);

НомерДняЦикла = (ДнейПрошло % ДлительностьЦикла) + 1;
// Если НомерДняЦикла 1 или 2 — день рабочий, если 3 или 4 — выходной.

Важный момент: Чтобы учесть праздники в этой схеме, необходимо перед расчетом проверить текущую дату по Регистру Сведений ПроизводственныйКалендарь. Если день праздничный, мы либо пропускаем его (смена сдвигается), либо считаем выходным (смена «сгорает»). Это настраивается в логике заполнения регистра КалендарныеГрафики.

Решение 3: Индивидуальные графики и приоритеты

Проанализируем ситуацию, когда у сотрудника есть основной график, но в конкретном месяце он работает по-другому. Для этого в типовых решениях (ЗУП, ERP) используется механизм Индивидуальных графиков.

При построении запроса или использовании функций планировщика важно соблюдать приоритетность:

  1. Сначала проверяем наличие записи в регистре индивидуальных графиков на конкретную дату.
  2. Если записи нет — берем данные из основного графика сотрудника.
  3. Если и основного графика нет — считаем по пятидневке производственного календаря.

Для корректной сортировки в планировщике используйте объединение по дате и времени начала. В запросе это выглядит так:


ВЫБРАТЬ
    Графики.Дата КАК Дата,
    Графики.ВремяНачала КАК ВремяНачала,
    Графики.ВремяОкончания КАК ВремяОкончания
ИЗ
    РегистрСведений.КалендарныеГрафики КАК Графики
ГДЕ
    Графики.Календарь = &КалендарьСотрудника
    И Графики.Дата МЕЖДУ &НачалоПериода И &КонецПериода
УПОРЯДОЧИТЬ ПО
    Дата,
    ВремяНачала

Заключение

Использование типового функционала ГрафикиРаботы из состава БСП — это наиболее надежный способ реализации подсистемы планирования. Он избавляет разработчика от необходимости вручную обрабатывать високосные годы, переносы праздников и сложные циклы сменности. Если же вы реализуете механизм самостоятельно, всегда опирайтесь на фикисированную дату начала цикла и производственный календарь, а не на порядковые номера строк в результате запроса.

← На главную