Clear leveling#

Remove the delays and splits that level wrote into a project, so every task goes back to its unleveled dates.

Leveling stores its result in two task fields: levelingDelay and pauses. To clear leveling, set levelingDelay to 0 and pauses to an empty list on every task that has either, then reschedule. For how leveling sets these fields, see Leveling resources.

Prerequisites#

  • A solved Project that carries leveling, either from a level call or from loaded data with a non-zero levelingDelay or non-empty pauses.
  • The ids of the tasks that carry a delay or pauses. Take them from your own model, or from schedule.updates of the LevelingResult.

Steps#

  1. Build one TaskUpdate per leveled task. Set levelingDelay to Patch.of(0.0) and pauses to Patch.of(new Vec<Pause>()), and put all the updates into one SchedulingChanges:

    static SchedulingChanges clearLeveling(List<Id> ids) {
      var changes = new SchedulingChanges();
      for (var id : ids) {
        changes.tasks.update.add(
            new TaskUpdate(id)
                .withLevelingDelay(Patch.of(0.0))
                .withPauses(Patch.of(new Vec<Pause>())));
      }
      return changes;
    }
    

    Don't put dates in the update. When the update has no date, the levelingDelay change moves the stored start back by the delay. The pauses change keeps the start and duration and recomputes the end.

  2. Apply the changes with apply and check the ApplyReport:

    var report = project.apply(clearLeveling(ids));
    if (!report.rejected.isEmpty()) {
      // Resync with project.solve(data, ...) before the next edit.
    }
    
  3. Reschedule with solve(null, …) and write result.updates back to your model:

    var result = project.solve(null, new SolveOptions());
    

    The cleared dates become the accepted baseline. The resource contention that leveling resolved shows up again in result.overallocations.

The full example below levels three 2-day tasks that share dev1, then clears the leveling. It uses the default calendar (Monday to Friday, 08:00-12:00 and 13:00-17:00) and starts on Monday, August 10, 2026:

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

public class ClearLeveling {
  static Moment aug(int day) {
    return Moment.fromYmdHm(2026, 8, day, 8, 0);
  }

  static TaskData task(String id) {
    return new TaskData(Id.str(id)).withStartDate(aug(10)).withDuration(2.0);
  }

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

  static SchedulingChanges clearLeveling(List<Id> ids) {
    var changes = new SchedulingChanges();
    for (var id : ids) {
      changes.tasks.update.add(
          new TaskUpdate(id)
              .withLevelingDelay(Patch.of(0.0))
              .withPauses(Patch.of(new Vec<Pause>())));
    }
    return changes;
  }

  public static void main(String[] args) throws Exception {
    var data = new ProjectData()
        .withTasks(List.of(task("A"), task("B"), task("C")))
        .withResources(List.of(new ResourceData().withId(Id.str("dev1"))))
        .withAssignments(List.of(booking("A"), booking("B"), booking("C")));

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

    // B is delayed to Wednesday, C to Friday.
    var leveled = project.level(new LevelingOptions());

    // The tasks that leveling delayed or split.
    var ids = new ArrayList<Id>();
    for (var m : leveled.schedule.updates) {
      boolean delayed = m.levelingDelay != null && m.levelingDelay != 0.0;
      boolean split = m.pauses != null && !m.pauses.isEmpty();
      if (delayed || split) ids.add(m.id);
    }

    var report = project.apply(clearLeveling(ids));
    if (!report.rejected.isEmpty()) {
      throw new IllegalStateException("clearing was rejected, resync with solve(data, ...)");
    }

    var result = project.solve(null, new SolveOptions());
    for (var m : result.updates) {
      if (m.startDate != null && m.endDate != null) {
        System.out.println(m.id + ": " + m.startDate + " .. " + m.endDate);
      }
    }
    for (var over : result.overallocations) {
      for (var w : over.windows) {
        System.out.println(over.resource + ": " + w.from + " .. " + w.to + ", peak " + w.peak);
      }
    }
  }
}

Re-level from scratch instead#

To replace old leveling with a fresh run, skip the steps above and call level again. LevelingOptions.clearFirst is true by default. The run first sets levelingDelay to 0 and pauses to an empty list on every in-scope task that has either, then levels the clean plan:

var result = project.level(new LevelingOptions());

result.schedule.updates is a diff against the state the call started from. A task whose old delay was cleared and not replaced still gets an update entry, so writing the updates back keeps your model in sync.

With a scope, the run clears only in-scope tasks. Set clearFirst to false to keep the existing delays and pauses and level on top of them.

Things to know#

  • Patch.nullValue() also works. On levelingDelay it restores the default of 0, and on pauses it clears the list. Don't use Patch.of(null) on pauses: it throws NullPointerException when applied.
  • Only leveling produces pauses. Every pause counts as leveling-owned, whether or not its by is set, so clearing removes all of them.
  • To see what clearing would do before you commit it, pass the same changes to preview. The project stays untouched. See Previewing changes.
  • In a live editor, set changes.baselineId to the baseline your model shows. apply then throws ProjectException with code ProjectError.STALE_BASELINE if the project has moved on. See Error handling.
  • If apply rejects an operation, report.needsResync is true, and solve(null, …) throws ProjectError.NEEDS_RESYNC until you send the full data with solve(data, …). See Recover from rejected changes.

Result#

The example prints B and C back on Monday, and the overallocation window on dev1 returns:

B: 2026-08-10 08:00 .. 2026-08-12 08:00
C: 2026-08-10 08:00 .. 2026-08-12 08:00
dev1: 2026-08-10 08:00 .. 2026-08-11 17:00, peak 3.0