[Canonical page](<https://docs.dhtmlx.com/planning-engine/java/how-to/split-assignment-work/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Split assignment work

Hand one task's work to several people, for example one developer for the first two days and another for the rest, without changing the task's dates.

The `offset` and `duration` fields of [`AssignmentData`](<https://docs.dhtmlx.com/planning-engine/java/api/data/assignment-data.md>) do this. Each assignment books only its own part of the task, called a slice. For how slices affect load, see [Resources and assignments](<https://docs.dhtmlx.com/planning-engine/java/resources-and-assignment/resources-and-assignments.md#assignment-slices>).

## Steps

1. Book each person on their part of the task. Both fields are `Double` and count worked time from the task start, in the task's [`durationUnit`](<https://docs.dhtmlx.com/planning-engine/java/api/data/task-data.md>):

   - `offset`: where the slice starts. `null` means the task start.
   - `duration`: how much work the slice books. `null` means up to the task end.

   ```java
   // Anna works the first 2 days of `build`.
   var anna = new AssignmentData()
       .withId(Id.str("build:anna"))
       .withTask(Id.str("build"))
       .withResource(Id.str("anna"))
       .withValue(1.0)
       .withDuration(2.0);
   // Ben starts after 2 days of work and finishes the task.
   var ben = new AssignmentData()
       .withId(Id.str("build:ben"))
       .withTask(Id.str("build"))
       .withResource(Id.str("ben"))
       .withValue(1.0)
       .withOffset(2.0);
   ```

2. Pass the assignments to [`solve(data, options)`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) as usual. Then check the booked load with [`Project#loadProfile`](<https://docs.dhtmlx.com/planning-engine/java/api/project-load-profile.md>):

   ```java
   var query = new ProfileQuery(from, to);
   query.basis = Basis.DAY;
   LoadProfile profile = project.loadProfile(new LoadProfileOptions(query));
   ```

3. To move the handover in a live project, send an [`AssignmentUpdate`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/assignment-update.md>) for each slice, then call `solve(null, options)`:

   ```java
   var changes = new SchedulingChanges();
   changes.assignments.update.add(
       new AssignmentUpdate(Id.str("build:anna")).withDuration(Patch.of(3.0)));
   changes.assignments.update.add(
       new AssignmentUpdate(Id.str("build:ben")).withOffset(Patch.of(3.0)));
   ```

4. To book the whole task again, set the field to [`Patch.nullValue()`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/patch.md#nullvalue>). A cleared `offset` goes back to the task start. A cleared `duration` runs up to the task end.

The full example splits the 4-day `build` task between Anna and Ben, prints their daily load, then moves the handover one day later:

```java
import java.util.ArrayList;
import java.util.List;
import org.dhtmlx.plen.*;

public class SplitAssignmentWork {
  static AssignmentData slice(String resource, Double offset, Double duration) {
    return new AssignmentData()
        .withId(Id.str("build:" + resource))
        .withTask(Id.str("build"))
        .withResource(Id.str(resource))
        .withValue(1.0)
        .withOffset(offset)
        .withDuration(duration);
  }

  static void printLoad(Project project) {
    // Daily load from Monday 2026-08-10 to Friday 2026-08-14.
    var query = new ProfileQuery(
        Moment.fromYmdHm(2026, 8, 10, 0, 0), Moment.fromYmdHm(2026, 8, 15, 0, 0));
    query.basis = Basis.DAY;
    LoadProfile profile = project.loadProfile(new LoadProfileOptions(query));
    for (ResourceLoad resource : profile.resources) {
      var loads = new ArrayList<Double>();
      for (LoadBucket bucket : resource.buckets) loads.add(bucket.load);
      System.out.println("  " + resource.resource + ": " + loads);
    }
  }

  public static void main(String[] args) throws Exception {
    // `build`: 4 working days, Monday to Thursday.
    var build = new TaskData(Id.str("build"))
        .withStartDate(Moment.fromYmdHm(2026, 8, 10, 8, 0))
        .withDuration(4.0);

    var data = new ProjectData()
        .withTasks(List.of(build))
        .withResources(List.of(
            new ResourceData().withId(Id.str("anna")),
            new ResourceData().withId(Id.str("ben"))))
        .withAssignments(List.of(
            // Anna works the first 2 days of `build`.
            slice("anna", null, 2.0),
            // Ben starts after 2 days of work and finishes the task.
            slice("ben", 2.0, null)));

    var project = new Project();
    ScheduleResult first = project.solve(data, new SolveOptions());
    System.out.println("handover after day 2");
    printLoad(project);

    // Move the handover one day later: Anna works 3 days, Ben the last one.
    var changes = new SchedulingChanges().withBaselineId(first.baselineId);
    changes.assignments.update.add(
        new AssignmentUpdate(Id.str("build:anna")).withDuration(Patch.of(3.0)));
    changes.assignments.update.add(
        new AssignmentUpdate(Id.str("build:ben")).withOffset(Patch.of(3.0)));

    ApplyReport report = project.apply(changes);
    if (!report.rejected.isEmpty()) {
      throw new IllegalStateException("rejected: " + report.rejected);
    }
    ScheduleResult result = project.solve(null, new SolveOptions());
    System.out.println("handover after day 3, moved tasks: " + result.updates.size());
    printLoad(project);
  }
}
```

`loadProfile` needs a solved project, so call `solve(null, options)` after [`apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>) and before you read the load again.

## Things to check

- Slices never move tasks. A slice edit leaves every task date as it was, but it changes the `baselineId`. A [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/scheduling-changes.md>) that still carries the old token fails with a [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/project-exception.md>) whose `code` is `STALE_BASELINE`.
- The engine clips the slice to the task's work when it books load. A slice that runs past the task end books only up to the end. An `offset` at or past the task's duration books nothing. If the task gets shorter later, the slice follows it.
- Non-working time and leveling pauses don't count as worked time. A pause or a `levelingDelay` moves the slice together with the work.
- Two slices of the same resource on the same task add up where they overlap.
- Strict validation rejects an `offset` below `0` or not finite, and a `duration` of `0` or less or not finite. [`solve(data, options)`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) throws an [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>) with `path` set to `assignments[i].offset` or `assignments[i].duration`. In [`apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>), the same values show up as rejected operations in [`ApplyReport.rejected`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md#rejected>), at paths like `assignments.update[0]`. See [Recover from rejected changes](<https://docs.dhtmlx.com/planning-engine/java/how-to/recover-from-rejected-changes.md>).
- With [`Validation.REPAIR`](<https://docs.dhtmlx.com/planning-engine/java/api/project/validation.md>), the engine clears an invalid value and adds a [`Note.Repaired`](<https://docs.dhtmlx.com/planning-engine/java/api/results/note.md>), so the assignment books the whole task. See [Accept messy input](<https://docs.dhtmlx.com/planning-engine/java/how-to/accept-messy-input.md>).
- An assignment on an unscheduled task keeps its slice and books it again when the task is scheduled.

## Result

The example prints each person's load for Monday to Friday, before and after the handover moves:

```text
handover after day 2
  anna: [1.0, 1.0, 0.0, 0.0, 0.0]
  ben: [0.0, 0.0, 1.0, 1.0, 0.0]
handover after day 3, moved tasks: 0
  anna: [1.0, 1.0, 1.0, 0.0, 0.0]
  ben: [0.0, 0.0, 0.0, 1.0, 0.0]
```

The load moves from Ben to Anna on Wednesday. `build` keeps its dates, so [`solve(null, options)`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) reports no moved tasks.
