Расчет Рабочего Времени

По умолчанию, dhtmlxGantt измеряет продолжительность задач в календарном времени, что означает, что выходные и праздники включены в общую продолжительность.

Обратитесь к статье Отображение даты окончания задачи и включение конечных дат для получения деталей о том, как форматируются даты окончания задачи.

Включение Расчета Рабочего Времени

Если вы хотите, чтобы продолжительность задач рассчитывалась только на основе рабочего времени, вы можете использовать опцию work_time:

Включение расчетов рабочего времени для продолжительности задач

gantt.config.work_time = true;  // исключает нерабочее время из расчетов gantt.config.skip_off_time = true;    // скрывает нерабочее время на графике 
gantt.init("gantt_here");

Имейте в виду, что конфигурационная опция skip_off_time доступна только в PRO версии.

Related sample:  Duration includes only working days

В зависимости от значения duration_unit, dhtmlxGantt рассчитывает продолжительность задач в различных единицах времени (например, если duration_unit = "hour", продолжительность измеряется в рабочих часах).

Продолжительность Задачи в Десятичном Формате

Эта функция доступна только в PRO издании.

Начиная с версии 6.3, dhtmlxGantt поддерживает указание продолжительности задач в десятичных форматах, таких как "2.5 дня", "0.5 часа" или "3.75 часа", с использованием модуля Форматировщик Продолжительности.

Стоит отметить, что Gantt внутренне хранит продолжительность задач как целочисленные значения. Модуль помогает преобразовывать введенные пользователем десятичные значения в формат, хранящийся в Gantt (например, "1.5 часа" становится 90 минутами) и наоборот, преобразовывая хранимые значения обратно в читаемый формат (например, 12 часов становится "0.5 дня").

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

Как Использовать Десятичный Формат

Чтобы отображать продолжительность задач в десятичном формате, выполните следующие шаги:

gantt.config.work_time = true;
gantt.config.duration_unit = "minute";

Убедитесь, что продолжительность задач хранится в меньших единицах, чем отображаемый десятичный формат. Например: - Чтобы позволить пользователям указывать продолжительность в дробных частях часа (например, "0.5 часа"), установите duration_unit в "minute". - Чтобы позволить пользователям указывать продолжительность в дробных частях дня, установите duration_unit в "hour". В этом случае "0.5 дня" будет работать, но "0.5 часа" округлится до 1 часа, так как продолжительность хранится как целые часы.

По умолчанию, даты задач привязываются к временной шкале. Если ваша шкала установлена на дни, вам, возможно, потребуется отключить привязку, чтобы позволить перетаскивать задачи на конкретные часы. Отключите round_dnd_dates и настройте time_step соответствующим образом.

Например:

// Шаги времени по 15 минут, требует "minute" как единицу продолжительности
gantt.config.time_step = 15;
gantt.config.round_dnd_dates = false;

или

// Шаги времени по 1 часу, подходит, когда единица продолжительности "hour"
gantt.config.time_step = 60;
gantt.config.round_dnd_dates = false;
  • Создайте объект formatter для обработки форматирования продолжительности задач:
// Форматирование продолжительности
var formatter = gantt.ext.formatters.durationFormatter({
    enter: "day", 
    store: "minute", // duration_unit
    format: "day",
    hoursPerDay: 8,
    hoursPerWeek: 40,
    daysPerMonth: 30
});
  • Добавьте объект formatter в колонку "Продолжительность", определив функцию шаблона в параметре columns:
gantt.config.columns = [
    {name: "text", tree: true, width: 170, resize: true, editor: textEditor},
    {name: "start_date", align: "center", resize: true, editor: dateEditor},
    {name: "duration", label:"Duration", resize: true, align: "center", 
        template: function(task) {             return formatter.format(task.duration);         }, width: 100},
    {name: "add", width: 44}
];
  • Используйте объект formatter в секции lightbox, установив свойство formatter для управления time:
gantt.config.lightbox.sections = [
    {name: "description", height: 70, map_to: "text", type: "textarea", focus: true},
    {name: "time", type: "duration", map_to: "auto", formatter: formatter}
];
  • Если включено редактирование в гриде, добавьте объект formatter в объект durationEditor:
var durationEditor = {
    type: "duration", 
    map_to: "duration", 
    formatter: formatter,     min:0, max:1000
    };
gantt.config.columns = [
    {name: "text", tree: true, width: 170, resize: true},
    {name: "start_date", align: "center", resize: true},
    {name: "duration", label:"Duration", resize: true, align: "center", 
        template: function(task) {
            return formatter.format(task.duration);
    }, editor: durationEditor, width: 100},     {name: "add", width: 44}
];

Если продолжительность уже хранится в таких единицах, как минуты или часы, вы все равно можете использовать модуль Форматировщик Продолжительности для отображения их в десятичном формате.

Глобальные Настройки

Установка Рабочего Времени

По умолчанию, рабочее время определяется как:

  • Рабочие дни: с понедельника по пятницу.
  • Рабочие часы: 8:00–12:00, 13:00–17:00.

Чтобы настроить эти параметры, используйте метод setWorkTime:

Настройка рабочего времени

// Установить рабочие часы для будних дней
gantt.setWorkTime({ hours:["9:00-18:00"] });
 
// Обозначить пятницы как выходные дни
gantt.setWorkTime({ day:5, hours:false });
 
// Установить конкретные часы для пятниц и суббот
gantt.setWorkTime({day : 5, hours : ["8:00-12:00"]});
gantt.setWorkTime({day : 6, hours : ["8:00-12:00"]});
 
// Сделать конкретную дату рабочим днем
gantt.setWorkTime({date : new Date(2019, 2, 31)});
 
// Сделать конкретную дату выходным днем
gantt.setWorkTime({date:new Date(2019,0,1), hours:false})

Related sample:  Custom working days and time

Установка Рабочих Часов для Ночных Смен

При установке рабочих часов убедитесь, что атрибут hours в setWorkTime находится в порядке возрастания. В противном случае некоторые интервалы будут проигнорированы. Например:

// Эти настройки не будут работать как ожидается
gantt.setWorkTime({day : 5, hours : ["16:00-18:00", "14:00-15:00",  "08:00-10:00"]});
gantt.setWorkTime({day : 5, hours : ["16:00-18:00", "00:00-04:00",  "05:00-06:00"]});

Для ночных смен разделите часы на два дня:

gantt.setWorkTime({day : 5, hours : ["16:00-18:00"]});
gantt.setWorkTime({day : 6, hours : ["00:00-04:00",  "05:00-06:00"]});

Настройка Правил Рабочего Времени

Вы можете определить различные правила рабочего времени для определенных периодов, используя атрибут customWeeks в setWorkTime. Например, установите разные часы для зимних месяцев:

// Настройка рабочих часов для зимних месяцев
gantt.setWorkTime({
    customWeeks: {
        winter: {
            from: new Date(2018, 11, 1), // 1 декабря 2018 года
            to: new Date(2019, 2, 1), // 1 марта 2019 года
            hours: ["9:00-13:00", "14:00-16:00"],
            days: [ 1, 1, 1, 1, 0, 0, 0]
        }
    }
});

Если вам нужно указать рабочие часы с минутами (например, "8:15-12:45"), установите duration_unit в "minute".

Настройка рабочих часов с точностью до минуты

gantt.config.duration_unit = "minute";
 
// Установка рабочих часов с точностью до минуты
gantt.setWorkTime({hours:["8:15-12:45"]});

Старый формат рабочего времени (использовавшийся до версии 7.0) все еще поддерживается:

gantt.setWorkTime({hours:[9, 18]})

Обновление Правила Рабочего Времени

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

gantt.setWorkTime({hours:["8:00-12:00"]});
gantt.setWorkTime({hours:["13:00-17:00"]});
// Результирующее рабочее время будет с 13:00 до 17:00,
// а не комбинацией двух команд.

Удаление Рабочего Времени

Чтобы удалить настройку рабочего времени, вы можете использовать метод gantt.unsetWorkTime():

// Устанавливает рабочее время для рабочих дней на ["8:00-12:00"]
gantt.setWorkTime({hours:["8:00-12:00"]});
// Удаляет рабочее время
gantt.unsetWorkTime({hours:["8:00-12:00"]});

Проверка Рабочего Времени

Чтобы узнать, попадает ли конкретная дата в рабочее время, используйте метод gantt.isWorkTime():

// Обозначает 1 января 2019 года как выходной
gantt.setWorkTime({date:new Date(2019,0,1), hours:false});
gantt.isWorkTime(new Date(2019,0,1)) // -> false   
// Устанавливает 15 марта 2019 года как рабочий день с 9:00 до 18:00
gantt.setWorkTime({date : new Date(2019, 2, 15), hours:["8:00-17:00"]});
gantt.isWorkTime(new Date(2019, 2, 15,10,0), "hour"); // -> true  gantt.isWorkTime(new Date(2019, 2, 15,8,0), "hour"); // -> false

Related sample:  Correct task position on drag

Получение Рабочего Времени

Чтобы получить рабочие часы для конкретной даты, вы можете использовать метод gantt.getWorkHours():

gantt.getWorkHours(new Date(2019,3,30))// -> ["8:00-17:00"]

Если вам нужен ближайший рабочий день к заданной дате, используйте метод gantt.getClosestWorkTime():

gantt.getClosestWorkTime(new Date(2019,3,30));

Установка Повторяющегося Рабочего Времени

Иногда вы можете захотеть определить рабочие часы, которые повторяются в определенные дни, например, сделать последний пятницу каждого месяца коротким днем или обозначить 25 декабря как праздник. В настоящее время dhtmlxGantt не имеет встроенных опций для этого. Он позволяет вам:

  • Установить рабочие часы для определенных дней недели (например, понедельник, вторник и т. д.)
  • Определить рабочие часы для конкретных дат (например, 4 июня 2020 года)
  • Переопределить рабочие часы для диапазона дат (например, с 1 июня 2020 года по 1 сентября 2020 года)

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

Например, если проект длится пять лет, и вы хотите, чтобы 1 января всегда был выходным днем, вы можете жестко закодировать это так:

gantt.setWorkTime({hours:false, date: new Date(2021, 0, 1)});
gantt.setWorkTime({hours:false, date: new Date(2022, 0, 1)});
gantt.setWorkTime({hours:false, date: new Date(2023, 0, 1)});
gantt.setWorkTime({hours:false, date: new Date(2024, 0, 1)});
gantt.setWorkTime({hours:false, date: new Date(2025, 0, 1)});

Чтобы установить последний пятницу каждого месяца как короткий день, вы можете использовать следующий код:

function lastFridayOfMonth(date) { 
    var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
    if (lastDay.getDay() < 5) {
        lastDay.setDate(lastDay.getDate() - 7);
    }
    lastDay.setDate(lastDay.getDate() - (lastDay.getDay() - 5));
    return lastDay;
}
 
var projectStart = new Date(2020, 5, 1);
var projectEnd = new Date(2025, 5, 1);
var currentDate = new Date(projectStart);
 
while (currentDate.valueOf() <= projectEnd.valueOf()) {
   var lastFriday = lastFridayOfMonth(currentDate);
   gantt.setWorkTime({hours:["8:00-12:00", "13:00-15:00"], date: lastFriday});
   currentDate = gantt.date.add(currentDate, 1, "month");
}

Выделение Нерабочих Времен

Чтобы визуально выделить нерабочие времена на графике, вы можете использовать шаблон gantt.templates.timeline_cell_class:

gantt.templates.timeline_cell_class = function(task, date){
    if (!gantt.isWorkTime({task:task, date: date}))
        return "week_end";
    return "";
};

Related sample:  Custom working days and time

Вы можете ознакомиться с более подробной информацией в статье подсветка временных интервалов.

Чтобы скрыть нерабочие времена, обратитесь к технике, описанной в статье настраиваемая шкала.

Множественные Календари Рабочего Времени

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

Создание Календаря Рабочего Времени

Вы можете создать новый календарь, используя метод gantt.createCalendar(). Этот метод предоставляет два варианта:

  • Без параметров, он создает календарь полного времени (24 часа в сутки, 7 дней в неделю):
var calendar = gantt.createCalendar();
  • Чтобы повторно использовать существующий календарь в качестве основы, передайте его в качестве параметра:
var newCalendar = gantt.createCalendar(calendar);

После создания календарь отсоединен от Gantt и не будет влиять ни на что, пока не будет добавлен.

Добавление Календаря в Gantt

После создания календаря добавьте его в Gantt, используя метод gantt.addCalendar(). Вы можете:

  • Добавить существующую конфигурацию календаря:
var calendarId = gantt.addCalendar(calendar);
  • Определить новую конфигурацию календаря, включая его ID и объект worktime с рабочими днями и часами:
var calendarId = gantt.addCalendar({
    id: "custom", // не обязательно
    worktime: {
        hours: ["8:00-17:00"],
        days: [1, 1, 1, 1, 1, 1, 1]
    }
});

Эта опция также может быть использована для создания календаря.

Различные Рабочие Часы для Определенных Периодов

Начиная с версии 7.1, вы можете установить разные рабочие часы для определенных периодов в одном календаре. Например, вы можете определить отдельное расписание для зимних месяцев, используя свойство customWeeks в методе addCalendar:

var calendarId = gantt.addCalendar({
    id: "global", // не обязательно
    worktime: {
        hours: ["8:00-17:00"],
        days: [1, 1, 1, 1, 1, 1, 1],
        customWeeks: {
            winter: {
                from: new Date(2018, 11, 1), // 1 декабря 2018 года
                to: new Date(2019, 2, 1), // 1 марта 2019 года
                hours: ["9:00-13:00", "14:00-16:00"],
                days: [1, 1, 1, 1, 0, 0, 0]
            }
        }
    },
});

Related sample:  Different worktimes for different time periods

Изменение Рабочих Часов

Чтобы обновить рабочие часы для определенных дней в календаре, используйте метод setWorkTime():

var calendar = gantt.getCalendar("custom");
calendar.setWorkTime({day: 6, hours: ["8:00-12:00"]});
calendar.setWorkTime({date: new Date(2021, 0, 1), hours: ["8:00-12:00"]});

Получение Календарей

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

Доступ к Глобальному Календарю Gantt

Чтобы получить глобальный календарь Gantt, используйте метод gantt.getCalendar():

var calendar = gantt.getCalendar(id);

По умолчанию глобальный календарь можно получить, используя предопределенный ID "global":

var globalSettings = gantt.getCalendar("global");

Этот календарь является стандартным для задач, если не указан другой календарь.

Доступ к Календарю Задачи

Чтобы получить календарь, назначенный конкретной задаче, используйте метод gantt.getTaskCalendar(), передав объект задачи:

var task = gantt.getTask(taskId);
 
var calendar = gantt.getTaskCalendar(task);
 
if (calendar.isWorkTime(date)) {
    alert("TaskWorkTime");
}

Related sample:  Task level calendars

Если рабочее время отключено в конфигурации Gantt, метод вернет календарь рабочего времени 24/7.

Использование Глобальных Методов для Календарей

Gantt предоставляет глобальные методы для расчета продолжительности задач или проверки дат без прямого доступа к календарю задачи:

  • gantt.isWorkTime:
if (gantt.isWorkTime({date: date, task: task})) {
    alert("work time of a task" + task.text);
}

Эквивалентно:

var calendar = gantt.getTaskCalendar(task);
if (calendar.isWorkTime({date: date})) {
    alert("work time of a task" + task.text);
}
  • gantt.calculateEndDate:
var end_date = gantt.calculateEndDate({start_date: date, duration: duration, task: task});
 
// или
var end_date = gantt.calculateEndDate(task);
  • gantt.calculateDuration:
var duration = gantt.calculateDuration({start_date: start, end_date: end, task: task});
 
// или
var duration = gantt.calculateDuration(task);
  • gantt.getClosestWorkTime:
var closestTime = gantt.getClosestWorkTime({date: date, task: task});

Получение Всех Календарей Gantt

Чтобы получить все календари, добавленные в Gantt (глобальные и специфичные для задач), используйте метод gantt.getCalendars():

var calendars = gantt.getCalendars();

Этот метод возвращает массив объектов календарей.

Удаление Календарей

Если календарь больше не нужен, вы можете удалить его, используя метод gantt.deleteCalendar(). Просто передайте ID календаря:

// Добавление календаря
gantt.addCalendar({
    id: "custom",
    worktime: {
        hours: ["8:00-17:00"],
        days: [1, 1, 1, 1, 1, 1, 1]
    }
});
 
// Удаление календаря
gantt.deleteCalendar("custom");

Назначение Календарей Задачам

Чтобы назначить календарь задаче, сначала добавьте календарь с его ID и настройками рабочего времени:

gantt.addCalendar({
    id: "custom", // не обязательно
    worktime: {
        hours: ["8:00-17:00"],
        days: [1, 1, 1, 1, 1, 1, 1]
    }
});

Затем установите ID календаря в атрибуте "calendar_id" объекта задачи:

{ 
  "id": 2, "calendar_id": "custom", "text": "Task #1", "start_date": "02-04-2019", 
  "duration": "8", "parent": "1", "progress": 0.5, "open": true
}

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

gantt.config.calendar_property = "property_name";

Related sample:  Task level calendars

Назначение Календаря Ресурсу

Эта функциональность доступна только в PRO издании.

Вы можете назначить определенные рабочие календари задачам, которым необходимы определенные ресурсы, такие как люди или оборудование. Например, вы можете захотеть, чтобы задачи следовали индивидуальным календарям в зависимости от пользователя, которому они назначены. Вот как это сделать:

  • Сначала настройте свойство в объекте задачи для хранения ID ресурса, используя конфигурационный атрибут resource_property. В примере ниже свойство user используется для хранения ID пользователей:
gantt.config.resource_property = "user";
  • Затем используйте конфигурационную опцию resource_calendars для определения календарей для каждого пользователя. Эти календари сгруппированы в один объект.
gantt.config.resource_calendars = {
    1 : gantt.addCalendar({
        worktime: {
            days: [0, 1, 1, 1, 1, 1, 0]
        }
    }),
    2 : gantt.addCalendar({
        worktime: {
            days: [1, 0, 0, 0, 0, 0, 1]
        }   
    }),
    3 : gantt.addCalendar({
        worktime: {
            days: [0, 1, 1, 1, 0, 1, 1]
        }
    })
};

Этот объект содержит пары ключ-значение, где ключ - это ID ресурса, а значение - ID календаря, возвращаемое методом addCalendar.

  • Наконец, включите атрибут user в объекты задач. Используйте ключ из объекта календаря, созданного в конфигурационной опции resource_calendars, в качестве значения для этого атрибута:
{ "id":1, "user":"1", "text":"Project #2", "start_date":"01-04-2019", "duration":"5" },
{ "id":2, "user":"0", "text":"Task #1", "start_date":"02-04-2019", "duration":"2" },
{ "id":3, "user":"2", "text":"Task #2", "start_date":"11-04-2019", "duration":"4" },
{ "id":4, "user":"3", "text":"Task #3", "start_date":"13-04-2019", "duration":"3" },
{ "id":5, "user":"0", "text":"Task #1.1", "start_date":"02-04-2019", "duration":"7" },
{ "id":6, "user":"1", "text":"Task #1.2", "start_date":"03-04-2019", "duration":"7" }

Related sample:  Resource level calendars

Если у задачи назначены как пользовательский календарь, так и календарь ресурса, приоритет отдается пользовательскому календарю, и он переопределяет календарь ресурса.

Объединение Нескольких Календарей

Начиная с версии 7.0, вы можете объединять несколько календарей в один. Например, если два ресурса с разными рабочими часами назначены одной задаче, их рабочие часы могут быть объединены в один календарь. Например, если один ресурс работает с 9:00 до 15:00, а другой с 12:00 до 17:00, объединенный календарь будет охватывать с 12:00 до 15:00.

Чтобы включить эту функцию автоматически, установите конфигурацию dynamic_resource_calendars в true:

gantt.config.dynamic_resource_calendars = true;

Related sample:  Merge work Calendars of different resources

Кроме того, вы можете объединить календари вручную, используя метод mergeCalendars:

const johnCalendarId = gantt.addCalendar({
    worktime: {
        hours: ["0:00-24:00"],
        days: [0, 1, 1, 1, 1, 1, 0]
    }
});
const mikeCalendarId = gantt.addCalendar({
    worktime: {
        hours: ["8:00-12:00", "13:00-17:00"],
        days: [0, 1, 1, 1, 1, 1, 0]
    }
});
 
const joinedCalendar = gantt.mergeCalendars(
    gantt.getCalendar(mikeCalendarId),
    gantt.getCalendar(johnCalendarId)
);

Проверьте статью mergeCalendars() для получения деталей о том, как объединяются рабочие часы.

Назначение Календаря Проекту

Эта функциональность доступна только в PRO издании.

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

  • Когда календарь назначен субпроекту, все его задачи будут следовать этому календарю, если у задачи не назначен свой собственный календарь.
  • Если у задачи есть свой собственный календарь, она будет использовать его вместо календаря проекта.

Чтобы включить эту функцию, установите конфигурационную опцию inherit_calendar в true. По умолчанию эта опция отключена.

gantt.config.inherit_calendar = true;
  • Если установлено значение true, задачи без конкретного календаря будут наследовать календарь их родительского проекта (который также может наследовать от своего родителя).
  • Если установлено значение false, задачи без конкретного календаря будут использовать глобальный календарь.

В примере ниже задачи по умолчанию наследуют календари от своих родительских проектов. Однако, если у задачи назначен собственный календарь, она будет использовать его вместо. Например, задачи #2.2 и #3 используют календари "Полная неделя" вместо календаря их родительского проекта:

Рабочий календарь для проекта

Related sample:  Project level calendars

Динамическое Изменение Календаря

Начиная с версии 7.0, Gantt автоматически обнаруживает изменения в календаре задачи и пересчитывает расписание задачи.

Однако, при необходимости, вы можете вручную обновить расписание задачи, когда ее календарь изменяется. Например, вы можете захотеть обновить расписание, когда календарь изменяется через lightbox:

function updateTaskTiming(task) {
  task.start_date = gantt.getClosestWorkTime({
     dir: "future",
     date: task.start_date,
     unit: gantt.config.duration_unit,
     task: task
  });
  task.end_date = gantt.calculateEndDate(task);
}
 
gantt.attachEvent("onLightboxSave", function(id, task, is_new){
  updateTaskTiming(task);
  return true;
});

Кроме того, вы можете определить пересчет для всех задач, когда это необходимо:

gantt.batchUpdate(function(){
  gantt.eachTask(function(task){
    task.start_date = gantt.getClosestWorkTime({
      dir: "future",
      date: task.start_date,
      unit: gantt.config.duration_unit,
      task: task
    });
    task.end_date = gantt.calculateEndDate(task);
    gantt.updateTask(task.id);
  });
});
К началу