[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/scheduling-config/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# SchedulingConfig

Project-wide scheduling settings. It's the `config` field of [`ProjectData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/project-data.md>), so it travels with every snapshot passed to [`schedule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/schedule.md>), [`Project::solve`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>) or [`Project::analyze`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#analyze>). `SchedulingConfig::default()` gives the defaults listed below; set only the fields you need and fill the rest with `..Default::default()`.

## Declaration

```rust
#[derive(Clone, Debug)]
pub struct SchedulingConfig {
    pub apply_constraints: bool,
    pub schedule_from_end: bool,
    pub gap_behavior: GapBehavior,
    pub move_projects: bool,
    pub project_constraint: bool,
    pub group_mode: bool,
    pub use_progress: bool,
    pub descendant_links: bool,
    pub calendar_mode: CalendarMode,
    pub strict_calendar: bool,
    pub max_iterations: u32,
    pub project_start: Option<DateTime>,
    pub project_end: Option<DateTime>,
    pub duration_unit: TimeUnit,
    pub use_calendars: bool,
    pub default_resource_capacity: f64,
    pub root_id: Id,
}

impl Default for SchedulingConfig { /* … */ }
```

## Fields

### apply_constraints

```rust
pub apply_constraints: bool
```

Enables task constraints ([`ConstraintType`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/constraint-type.md>)) as hard bounds. With `false`, constraints are ignored.

Default: `false`.

### schedule_from_end

```rust
pub schedule_from_end: bool
```

Plans the project backward (ALAP) from its end instead of forward (ASAP) from its start. [`Project::level`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#level>) rejects a project with this set.

Default: `false`.

### gap_behavior

```rust
pub gap_behavior: GapBehavior
```

Whether tasks keep their current dates where links allow (`Preserve`) or move to the earliest position (`Compress`).

Default: `GapBehavior::Preserve`.

### move_projects

```rust
pub move_projects: bool
```

`true` moves a summary and its descendants as one block. `false` schedules the summary's leaves individually, keeping their offsets within the summary.

Default: `true`.

### project_constraint

```rust
pub project_constraint: bool
```

A child task without its own constraint inherits its parent's constraint. Has no effect while `group_mode` is `true`.

Default: `false`.

### group_mode

```rust
pub group_mode: bool
```

Turns off the constraint inheritance of `project_constraint`. It has no other effect.

Default: `false`.

### use_progress

```rust
pub use_progress: bool
```

Treats a task with `progress == 1` as completed: it keeps its dates.

Default: `false`.

### descendant_links

```rust
pub descendant_links: bool
```

Allows links between a summary and its own descendant. With `false`, such a link is dropped and reported in the result's `notes`.

Default: `false`.

### calendar_mode

```rust
pub calendar_mode: CalendarMode
```

Which calendars place the work: resource calendars (`Resource`) or task calendars only (`Task`, `TaskInherited`).

Default: `CalendarMode::Resource`.

### strict_calendar

```rust
pub strict_calendar: bool
```

Reports a [`Conflict::CalendarNonWorking`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/conflict.md#calendarnonworking>) when a moving summary block lands a task on non-working time.

Default: `false`.

### max_iterations

```rust
pub max_iterations: u32
```

Upper limit on scheduling passes. Must be at least `1`.

Default: `8`.

### project_start

```rust
pub project_start: Option<DateTime>
```

Earliest date of the project: no task starts before it. Also the date source for tasks that have no dates of their own.

Default: `None`.

### project_end

```rust
pub project_end: Option<DateTime>
```

Upper bound of the project, used by backward (`schedule_from_end`) plans.

Default: `None`.

### duration_unit

```rust
pub duration_unit: TimeUnit
```

Unit of task `duration` and link `lag`. A task can override it with its own [`TaskData::duration_unit`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/task-data.md#duration_unit>).

Default: `TimeUnit::Day`.

### use_calendars

```rust
pub use_calendars: bool
```

`true` schedules on working calendars. `false` treats every calendar as working time around the clock.

Default: `true`.

### default_resource_capacity

```rust
pub default_resource_capacity: f64
```

Capacity of a resource that doesn't set its own, in assignment `value` units (`1.0` = 100 %). Must be finite and non-negative.

Default: `1.0`.

### root_id

```rust
pub root_id: Id
```

A task `parent` equal to this id (or `""`) means the task has no parent. Ids compare as normalized keys, so the default matches both `0` and `"0"`. On ingress such a parent is stored as no parent. Set it when your data uses another root id, such as `-1` or `"root"`; a task with id `0` is then an ordinary parent.

Default: `Id::Num(0.0)`.

## Errors

The config is checked when the snapshot is imported. Both [`Validation`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>) modes reject these values with [`Error::Input`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>), reported at the field path:

| Path | When it occurs |
|---|---|
| `config.max_iterations` | `max_iterations` is `0` |
| `config.default_resource_capacity` | the value is negative, `NaN` or infinite |

## Example

```rust
use plen_core::{
    schedule, DateTime, ProjectData, SchedulingConfig, SolveOptions, TaskData, TimeUnit,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let start = DateTime::from_ymd_hm(2026, 8, 14, 8, 0).ok_or("invalid date")?;

    let mut task = TaskData::new("a");
    task.start_date = Some(start);
    task.duration = Some(16.0);

    let data = ProjectData {
        config: SchedulingConfig {
            duration_unit: TimeUnit::Hour,
            apply_constraints: true,
            project_start: Some(start),
            ..Default::default()
        },
        tasks: vec![task],
        ..Default::default()
    };

    let result = schedule(
        data,
        &SolveOptions {
            include_unchanged: true,
            ..Default::default()
        },
    )?;
    println!("{:?}", result.updates[0].end_date);
    Ok(())
}
```

## Related

- [`TaskData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/task-data.md>) - per-task fields that override or depend on these settings
- [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>) - live edits, which can't change the config
- [Project data](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/project-data.md>) - setting the config in an input snapshot
- [Links, constraints and summaries](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/links-constraints-summaries.md>) - how constraint, summary and gap settings change placement
- [Data Updates](<https://docs.dhtmlx.com/planning-engine/rust/live-projects/editing-a-project.md>) - why the config stays fixed until the next full solve
