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


# AssignmentData

One resource booked on one task at a given rate. Assignments go in [`ProjectData::assignments`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/project-data.md>), and new ones are added to a live project through [`SchedulingChanges::assignments`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>) `.add`. Build it with a struct literal; it has no constructor and no `Default`.

## Declaration

```rust
#[derive(Clone, Debug)]
pub struct AssignmentData {
    pub id: Option<Id>,
    pub task: Id,
    pub resource: Id,
    pub value: f64,
    pub offset: Option<f64>,
    pub duration: Option<f64>,
}
```

## Fields

| Field | Type | Default | Description |
|---|---|---|---|
| `id` | `Option<`[`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>)`>` | `None` | Assignment id. `None` gets a generated id on import. |
| `task` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | required | The task being worked on. |
| `resource` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | required | The resource doing the work. |
| `value` | `f64` | required | Utilization rate: `1.0` is 100 % of one unit of capacity. |
| `offset` | `Option<f64>` | `None` (`0`) | Worked time from the task start where the slice begins. |
| `duration` | `Option<f64>` | `None` (to the task end) | Worked time the slice books. |

### id

```rust
pub id: Option<Id>
```

The key that [`AssignmentUpdate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/assignment-update.md>) and [`SchedulingChanges::assignments.remove`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>) refer to. A generated id never collides with an explicit id in the same list. Give every assignment an explicit id if you plan to edit it later.

### task

```rust
pub task: Id
```

Id of a task in the project. An assignment on an unscheduled task stays in the project but books no load; it's reported as [`Note::DroppedAssignment`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/note.md>) and books again when the task is scheduled.

### resource

```rust
pub resource: Id
```

Id of a resource in [`ProjectData::resources`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/project-data.md>).

### value

```rust
pub value: f64
```

Units of the resource the assignment uses while it works, in the same units as [`ResourceData::capacity`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/resource-data.md#capacity>). `0.5` is half-time; `2.0` is two full units. Must be finite and greater than `0`.

### offset

```rust
pub offset: Option<f64>
```

Start of the slice, as worked time counted from the task start, in the task's `duration_unit`. Pause and non-working time don't count. `None` is the same as `Some(0.0)`. A slice that starts at or after the end of the task's work books nothing.

### duration

```rust
pub duration: Option<f64>
```

Amount of worked time the slice books, in the task's `duration_unit`. `None` books up to the task end. A slice that runs past the end of the task's work is cut at the task end when load is booked, so it follows a task that shrinks later.

`offset` and `duration` change resource load only. They never change task dates. For how slices are measured and booked, see [Resources and assignments](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/resources-and-assignments.md#assignment-slices>).

## Errors

The snapshot is validated when it's passed to [`schedule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/schedule.md>) or [`Project::solve(Some(data))`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>). Under [`Validation::Strict`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>), an assignment fails with [`Error::Input`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) at these paths:

| Path | When |
|---|---|
| `assignments[i].id` | the id repeats the id of an earlier assignment |
| `assignments[i].task` | no task has this id |
| `assignments[i].resource` | no resource has this id |
| `assignments[i].value` | `value` isn't finite or is `<= 0` |
| `assignments[i].offset` | `offset` isn't finite or is `< 0` |
| `assignments[i].duration` | `duration` isn't finite or is `<= 0` |
| `assignments[i]` | in [`CalendarMode::Resource`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/calendar-mode.md>), the task's own calendar and the resource's calendar share no weekly working time |

The `offset` and `duration` checks apply to assignments on unscheduled tasks too.

[`Validation::Repair`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>) keeps the last of several assignments with the same id, and clears an invalid `offset` or `duration` to `None`. Each fix is reported as [`Note::Repaired`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/note.md>).

An invalid `AssignmentData` in [`SchedulingChanges::assignments.add`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>) is a rejected operation in the [`ApplyReport`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/apply-report.md>), not an `Err`.

## Example

Two people share one 4-day task: `anna` books the first 2 days of work at full time, `ben` books the rest at half time.

```rust
use plen_core::{
    schedule, AssignmentData, DateTime, ProjectData, ResourceData, SolveOptions, TaskData,
};

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

    let data = ProjectData {
        tasks: vec![TaskData {
            start_date: Some(start),
            duration: Some(4.0),
            ..TaskData::new("build")
        }],
        resources: vec![
            ResourceData { id: "anna".into(), capacity: None, calendar_id: None },
            ResourceData { id: "ben".into(), capacity: None, calendar_id: None },
        ],
        assignments: vec![
            AssignmentData {
                id: Some("build:anna".into()),
                task: "build".into(),
                resource: "anna".into(),
                value: 1.0,
                offset: None,
                duration: Some(2.0),
            },
            AssignmentData {
                id: Some("build:ben".into()),
                task: "build".into(),
                resource: "ben".into(),
                value: 0.5,
                offset: Some(2.0),
                duration: None,
            },
        ],
        ..Default::default()
    };

    let result = schedule(data, &SolveOptions::default())?;
    println!("{:?}", result.overallocations);
    Ok(())
}
```

For a step-by-step handover between people, see [Split assignment work](<https://docs.dhtmlx.com/planning-engine/rust/how-to/split-assignment-work.md>).

## Related

- [`AssignmentUpdate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/assignment-update.md>) - edits an existing assignment in a live project
- [`ResourceData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/resource-data.md>) - the resource an assignment books
- [`ResourceOverallocation`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/resources/resource-overallocation.md>) - reports where assignments book more than a resource's capacity
- [Resources and assignments](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/resources-and-assignments.md>) - how assignments and slices book resource load
- [Split assignment work](<https://docs.dhtmlx.com/planning-engine/rust/how-to/split-assignment-work.md>) - hands one task's work from one resource to another with slices
