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


# Preview leveling

Show the user what leveling would change before it touches the project, then keep the leveled plan or throw it away.

[`Project#level`](<https://docs.dhtmlx.com/planning-engine/java/api/project-level.md>) commits. It writes `levelingDelay` and `pauses` into the project and accepts the leveled dates as the new baseline. To get a proposal instead, level a copy. [`Project#copy`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project.md#copy>) is cheap, because the data stays shared until one copy is edited.

## Prerequisites

- A solved [`Project`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project.md>): [`solve(data, options)`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) has run.
- The data has `resources` and `assignments`, so there's overallocation to level.
- The project is forward-scheduled. `level` refuses `config.scheduleFromEnd = true` with an [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>).

## Steps

1. Copy the solved project:

   ```java
   Project draft = project.copy();
   ```

2. Level the copy. The original keeps its dates, its baseline and its overallocation:

   ```java
   LevelingResult proposal = draft.level(new LevelingOptions());
   ```

3. Show the proposal. [`proposal.schedule.updates`](<https://docs.dhtmlx.com/planning-engine/java/api/resources/leveling-result.md#schedule>) lists the tasks leveling moved, with their new dates, `levelingDelay` and `pauses`. The diff is relative to the state the call started from, and that state matches the original. Apply the entries to the model you already hold.

   ```java
   for (Mutation update : proposal.schedule.updates) {
       System.out.println(update.id + ": start " + update.startDate
           + ", delay " + update.levelingDelay + ", pauses " + update.pauses);
   }
   ```

4. Show what leveling couldn't fix. [`proposal.schedule.overallocations`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md#overallocations>) holds the windows left after leveling. It's `null` when the project has no assignments. Each window has a `reason`, such as `fixed-task` or `priority-1000`:

   ```java
   if (proposal.schedule.overallocations != null) {
       for (ResourceOverallocation over : proposal.schedule.overallocations) {
           for (OverallocationWindow window : over.windows) {
               System.out.println(over.resource + ": " + window.from + " .. " + window.to
                   + ", " + window.reason);
           }
       }
   }
   ```

5. Apply the user's choice:
   - Accept: replace the original with the copy, `project = draft;`. The project now holds the leveled baseline.
   - Reject: drop `draft`. Nothing else needs undoing.

   Replace the original only if nobody edited it after you copied it. Otherwise the proposal comes from an older state, so level a new copy.

The full example levels two tasks that book the same developer full time on Monday and Tuesday:

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

public class PreviewLeveling {
  static TaskData task(String id, Moment start, double duration) {
    return new TaskData(Id.str(id)).withStartDate(start).withDuration(duration);
  }

  static AssignmentData booking(String task, String resource) {
    return new AssignmentData()
        .withTask(Id.str(task))
        .withResource(Id.str(resource))
        .withValue(1.0);
  }

  public static void main(String[] args) throws Exception {
    // Monday, August 10, 2026, on the default calendar.
    var monday = Moment.fromYmdHm(2026, 8, 10, 8, 0);
    var data = new ProjectData()
        .withTasks(List.of(task("A", monday, 2.0), task("B", monday, 2.0)))
        .withResources(List.of(new ResourceData().withId(Id.str("dev1"))))
        .withAssignments(List.of(booking("A", "dev1"), booking("B", "dev1")));

    var project = new Project();
    project.solve(data, new SolveOptions());

    Project draft = project.copy();
    LevelingResult proposal = draft.level(new LevelingOptions());

    for (Mutation update : proposal.schedule.updates) {
      if (update.startDate != null && update.endDate != null) {
        System.out.println(update.id + ": " + update.startDate + " .. " + update.endDate
            + ", delay " + update.levelingDelay);
      }
    }
    var over = proposal.schedule.overallocations;
    System.out.println("overallocated resources left: " + (over == null ? 0 : over.size()));

    var original = project.solve(null, new SolveOptions());
    System.out.println("original still overallocated: " + original.overallocations.size());

    boolean userAccepts = true;
    if (userAccepts) {
      project = draft;
    }
    var plan = project.solve(null, new SolveOptions());
    System.out.println("baseline matches proposal: "
        + plan.baselineId.equals(proposal.schedule.baselineId));
  }
}
```

## Result

The example prints the one task leveling moved. The proposal has no overallocation left. The original still has one:

```text
B: 2026-08-12 08:00 .. 2026-08-14 08:00, delay 2.0
overallocated resources left: 0
original still overallocated: 1
baseline matches proposal: true
```

`B` waits two days for `A`, so its `levelingDelay` is `2.0`. Until you replace `project` with `draft`, `project` still reports the Monday-Tuesday window on `dev1`. After the swap, `baselineId` is the token from the leveling result. Baseline tokens are opaque, so compare them only within one process.

The copy is a full solved project, so you can also call [`draft.loadProfile(...)`](<https://docs.dhtmlx.com/planning-engine/java/api/project-load-profile.md>) on it to draw the leveled load. See [Resource load](<https://docs.dhtmlx.com/planning-engine/java/resources-and-assignment/resource-load.md>). To compare leveling across several alternative change sets with scores, use [`evaluate`](<https://docs.dhtmlx.com/planning-engine/java/api/project-evaluate.md>) with [`EvaluateOptions.level`](<https://docs.dhtmlx.com/planning-engine/java/api/evaluate/evaluate-options.md#level>). See [Evaluating alternatives](<https://docs.dhtmlx.com/planning-engine/java/resources-and-assignment/evaluating-alternatives.md>). For options, reasons and splits, see [Leveling resources](<https://docs.dhtmlx.com/planning-engine/java/resources-and-assignment/leveling-resources.md>).
