[Canonical page](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Project#apply

Writes edits into the accepted state of a [`Project`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project.md>) and marks it unsolved. It doesn't schedule anything: call [`solve(null, …)`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) or [`level`](<https://docs.dhtmlx.com/planning-engine/java/api/project-level.md>) afterwards to get the new dates.

## Signature

```java
public ApplyReport apply(SchedulingChanges changes)
```

Synchronous and CPU-only. Changes the project's state. Not synchronized: serialize calls on one instance.

## Parameters

| Name | Type | Description |
|---|---|---|
| `changes` | [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/scheduling-changes.md>) | The edits to commit. Must not be `null` |

## Behavior

- Operations run in order and are partially accepted. A rejected operation goes into `ApplyReport.rejected` and isn't thrown.
- Any rejection sets `needsResync`. From then on `solve(null, …)`, `apply` and `level` throw `NEEDS_RESYNC` until a full `solve(data, …)`.
- `changes.baselineId`, when set, must name the current baseline. `null` skips the check.
- The project is unsolved afterwards. [`preview`](<https://docs.dhtmlx.com/planning-engine/java/api/project-preview.md>), [`loadProfile`](<https://docs.dhtmlx.com/planning-engine/java/api/project-load-profile.md>), [`evaluate`](<https://docs.dhtmlx.com/planning-engine/java/api/project-evaluate.md>), [`suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/java/api/project-suggest-assignments.md>) and [`roster`](<https://docs.dhtmlx.com/planning-engine/java/api/project-roster.md>) need a solve first.
- Tasks added or updated by successful operations accumulate across calls. The next acyclic `solve(null, …)` or `level` reports each one as a direct update.
- An explicit start that falls on non-working time is kept unsnapped when the task has no predecessor. The end is computed forward from it on the working calendar.

## Returns

An [`ApplyReport`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md>) with the applied and rejected operations, their side effects and the new revision.

## Errors

All exceptions are unchecked.

| Error | When |
|---|---|
| [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/project-exception.md>) with `code == ProjectError.NEEDS_RESYNC` | An earlier `apply` rejected an operation and no `solve(data, …)` followed. Checked first |
| [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/project-exception.md>) with `code == ProjectError.STALE_BASELINE` | `changes.baselineId` doesn't name the current baseline |
| `NullPointerException` | `changes` is `null` |

## Example

Commit an edit, resync with the full data if part of it is rejected, and reschedule.

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

public class ApplyExample {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 8, 10, 8, 0);
    ProjectData data = new ProjectData()
        .withTasks(List.of(new TaskData(Id.num(1)).withStartDate(monday).withDuration(2.0)));

    SolveOptions options = new SolveOptions();
    Project project = new Project();
    ScheduleResult solved = project.solve(data, options);

    SchedulingChanges changes = new SchedulingChanges().withBaselineId(solved.baselineId);
    changes.tasks.update.add(new TaskUpdate(Id.num(1)).withDuration(Patch.of(3.0)));

    ApplyReport report = project.apply(changes);
    for (RejectedOperation rejected : report.rejected) {
      System.out.println("rejected at " + rejected.path + ": " + rejected.message);
    }

    // After a rejection, only a full solve with data is accepted.
    ScheduleResult result = report.needsResync
        ? project.solve(data, options)
        : project.solve(null, options);
    System.out.println(result.updates.size() + " task(s) moved"); // 1 task(s) moved
  }
}
```

## Related

- [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/scheduling-changes.md>) - the change set it commits
- [`ApplyReport`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md>) - applied and rejected operations and the new revision
- [`Project#solve`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) - reschedules the committed edits
- [Data updates](<https://docs.dhtmlx.com/planning-engine/java/live-projects/editing-a-project.md>) - change sets, patches, rejections and resync
- [Recover from rejected changes](<https://docs.dhtmlx.com/planning-engine/java/how-to/recover-from-rejected-changes.md>) - resyncing after a rejected operation
