@outbuild-company/schedule-core 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -149,6 +149,20 @@ interface Link {
149
149
  sectorId?: number;
150
150
  proplannerId?: number;
151
151
  }
152
+ interface DeletedActivitySnapshot {
153
+ readonly id: ActivityId;
154
+ readonly proplannerId: number;
155
+ readonly parentId: ActivityId | null;
156
+ readonly hadBaseline: boolean;
157
+ }
158
+ interface DeletedLinkSnapshot {
159
+ readonly id: LinkId;
160
+ readonly proplannerId: number;
161
+ readonly source: ActivityId;
162
+ readonly target: ActivityId;
163
+ readonly type: LinkType;
164
+ readonly lag: number;
165
+ }
152
166
  interface CalendarWorktime {
153
167
  days: Array<false | string[]>;
154
168
  hours: string[];
@@ -572,7 +586,16 @@ interface BackendCalendarInput {
572
586
  baseexceptiondays?: BackendCalendarExceptionInput[];
573
587
  }
574
588
  interface BackendActivityInput {
575
- id: number;
589
+ /**
590
+ * Persisted backend identity.
591
+ *
592
+ * Synthetic activities that have not been persisted yet use `null`.
593
+ * `unique_id` remains their stable local identity.
594
+ *
595
+ * A numeric zero is intentionally preserved as a numeric backend identity
596
+ * for compatibility with the certified proplanner-id/002 scenario.
597
+ */
598
+ id: number | null;
576
599
  unique_id: string;
577
600
  parent_id: string;
578
601
  unique_correlative_id: string;
@@ -707,6 +730,9 @@ declare class ScheduleCore {
707
730
  getProjectWorkHours(): ProjectWorkHours | null;
708
731
  getModifiedLinks(): ReadonlyArray<Readonly<Link>>;
709
732
  getModifiedActivities(): ReadonlyArray<Readonly<CoreActivity>>;
733
+ hasUnsavedChanges(): boolean;
734
+ getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
735
+ getDeletedLinksSinceLastSave(): ReadonlyArray<Readonly<DeletedLinkSnapshot>>;
710
736
  private _enqueue;
711
737
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
712
738
  private _dispatchInner;
@@ -766,8 +792,12 @@ declare class ScheduleCore {
766
792
  private _startCriticalPathForCurrentRevision;
767
793
  private _runCriticalPathAndCapture;
768
794
  private _recomputeAfterHistoryRestore;
769
- undo(): Promise<ChangeSet | null>;
770
- redo(): Promise<ChangeSet | null>;
795
+ undo(options?: {
796
+ skipCriticalPath?: boolean;
797
+ }): Promise<ChangeSet | null>;
798
+ redo(options?: {
799
+ skipCriticalPath?: boolean;
800
+ }): Promise<ChangeSet | null>;
771
801
  canUndo(): boolean;
772
802
  canRedo(): boolean;
773
803
  clearHistory(): void;
@@ -925,4 +955,4 @@ declare const DISPATCH_TRACK_EVENT: {
925
955
  };
926
956
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
927
957
 
928
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
958
+ export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
package/dist/index.d.ts CHANGED
@@ -149,6 +149,20 @@ interface Link {
149
149
  sectorId?: number;
150
150
  proplannerId?: number;
151
151
  }
152
+ interface DeletedActivitySnapshot {
153
+ readonly id: ActivityId;
154
+ readonly proplannerId: number;
155
+ readonly parentId: ActivityId | null;
156
+ readonly hadBaseline: boolean;
157
+ }
158
+ interface DeletedLinkSnapshot {
159
+ readonly id: LinkId;
160
+ readonly proplannerId: number;
161
+ readonly source: ActivityId;
162
+ readonly target: ActivityId;
163
+ readonly type: LinkType;
164
+ readonly lag: number;
165
+ }
152
166
  interface CalendarWorktime {
153
167
  days: Array<false | string[]>;
154
168
  hours: string[];
@@ -572,7 +586,16 @@ interface BackendCalendarInput {
572
586
  baseexceptiondays?: BackendCalendarExceptionInput[];
573
587
  }
574
588
  interface BackendActivityInput {
575
- id: number;
589
+ /**
590
+ * Persisted backend identity.
591
+ *
592
+ * Synthetic activities that have not been persisted yet use `null`.
593
+ * `unique_id` remains their stable local identity.
594
+ *
595
+ * A numeric zero is intentionally preserved as a numeric backend identity
596
+ * for compatibility with the certified proplanner-id/002 scenario.
597
+ */
598
+ id: number | null;
576
599
  unique_id: string;
577
600
  parent_id: string;
578
601
  unique_correlative_id: string;
@@ -707,6 +730,9 @@ declare class ScheduleCore {
707
730
  getProjectWorkHours(): ProjectWorkHours | null;
708
731
  getModifiedLinks(): ReadonlyArray<Readonly<Link>>;
709
732
  getModifiedActivities(): ReadonlyArray<Readonly<CoreActivity>>;
733
+ hasUnsavedChanges(): boolean;
734
+ getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
735
+ getDeletedLinksSinceLastSave(): ReadonlyArray<Readonly<DeletedLinkSnapshot>>;
710
736
  private _enqueue;
711
737
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
712
738
  private _dispatchInner;
@@ -766,8 +792,12 @@ declare class ScheduleCore {
766
792
  private _startCriticalPathForCurrentRevision;
767
793
  private _runCriticalPathAndCapture;
768
794
  private _recomputeAfterHistoryRestore;
769
- undo(): Promise<ChangeSet | null>;
770
- redo(): Promise<ChangeSet | null>;
795
+ undo(options?: {
796
+ skipCriticalPath?: boolean;
797
+ }): Promise<ChangeSet | null>;
798
+ redo(options?: {
799
+ skipCriticalPath?: boolean;
800
+ }): Promise<ChangeSet | null>;
771
801
  canUndo(): boolean;
772
802
  canRedo(): boolean;
773
803
  clearHistory(): void;
@@ -925,4 +955,4 @@ declare const DISPATCH_TRACK_EVENT: {
925
955
  };
926
956
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
927
957
 
928
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
958
+ export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
package/dist/index.js CHANGED
@@ -4022,7 +4022,10 @@ var MAX_CUSTOM_ID_LENGTH = 20;
4022
4022
  // src/columns/custom-id/custom-id-pipeline.ts
4023
4023
  var customIdPipeline = {
4024
4024
  targetField: "customId",
4025
- canEdit() {
4025
+ canEdit(activity, hierarchy) {
4026
+ if (isSummaryActivity2(activity, hierarchy)) {
4027
+ return cannotEdit("summary", "summary_column_value");
4028
+ }
4026
4029
  return canEdit();
4027
4030
  },
4028
4031
  parseInput(rawValue) {
@@ -10335,7 +10338,6 @@ function buildParentMutations(input) {
10335
10338
  fields.constraintType = CONSTRAINT_TYPE2.ASAP;
10336
10339
  fields.constraintDate = null;
10337
10340
  fields.progress = 0;
10338
- fields.expectedProgressBaseline = 0;
10339
10341
  }
10340
10342
  }
10341
10343
  return {
@@ -10401,6 +10403,18 @@ function findFirstSelectedSibling(taskId, selected, adapter) {
10401
10403
  }
10402
10404
  return firstSelected;
10403
10405
  }
10406
+ function hasAncestorInSet(activityId, candidates, adapter) {
10407
+ const visited = /* @__PURE__ */ new Set([activityId]);
10408
+ let parentId = normalizeParentKey(
10409
+ adapter.getActivity(activityId)?.parentId
10410
+ );
10411
+ while (parentId !== ROOT_PARENT_ID && !visited.has(parentId)) {
10412
+ if (candidates.has(parentId)) return true;
10413
+ visited.add(parentId);
10414
+ parentId = normalizeParentKey(adapter.getActivity(parentId)?.parentId);
10415
+ }
10416
+ return false;
10417
+ }
10404
10418
  function findPrevSiblingHelper(taskId, adapter) {
10405
10419
  const a = adapter.getActivity(taskId);
10406
10420
  if (!a) return null;
@@ -10467,16 +10481,13 @@ async function createActivityCore(action, deps, opts = {}) {
10467
10481
  const initialTouched = /* @__PURE__ */ new Set([newId]);
10468
10482
  if (parent) initialTouched.add(parent.id);
10469
10483
  const parentWasLeaf = parent !== null && adapter.getChildren(parent.id).length === 0;
10484
+ const parentCustomId = parentWasLeaf ? parent?.customId : null;
10470
10485
  const beforeSnap = opts.skipBeforeSnap ? /* @__PURE__ */ new Map() : await snapshotActivities(adapter, initialTouched);
10471
10486
  adapter.addActivity(newActivity);
10472
- applyParentMutations(
10473
- action,
10474
- newId,
10475
- parent,
10476
- adapter,
10477
- opts.preserveParentCustomId ?? false,
10478
- parentWasLeaf
10479
- );
10487
+ applyParentMutations(action, newId, parent, adapter, parentWasLeaf);
10488
+ if (parentCustomId) {
10489
+ deps.customIdTracker.releaseCustomId(parentCustomId);
10490
+ }
10480
10491
  recomputeHhCascadeForActivity(newId, adapter);
10481
10492
  recomputeUsedCostCascadeForActivity(newId, adapter);
10482
10493
  recomputeCostCascadeForActivity(newId, adapter);
@@ -10546,7 +10557,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10546
10557
  return;
10547
10558
  }
10548
10559
  }
10549
- const customIdAnchorId = anchorSiblingId ?? opts.customIdReferenceId;
10560
+ const customIdAnchorId = opts.customIdReferenceId ?? anchorSiblingId;
10550
10561
  const derivesFromAnchor = customIdAnchorId !== void 0;
10551
10562
  const referenceActivity = derivesFromAnchor ? adapter.getActivity(customIdAnchorId) : parent;
10552
10563
  newActivity.customId = customIdTracker.generateCustomIdForNewChild({
@@ -10554,7 +10565,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10554
10565
  useActivityCustomIdAsReference: derivesFromAnchor
10555
10566
  });
10556
10567
  }
10557
- function applyParentMutations(action, newId, parent, adapter, preserveParentCustomId, parentWasLeaf) {
10568
+ function applyParentMutations(action, newId, parent, adapter, parentWasLeaf) {
10558
10569
  if (!parent) return;
10559
10570
  const parentMutations = buildParentMutations({
10560
10571
  parentId: action.parentId,
@@ -10564,8 +10575,6 @@ function applyParentMutations(action, newId, parent, adapter, preserveParentCust
10564
10575
  });
10565
10576
  if (!parentMutations) return;
10566
10577
  for (const [key, value] of Object.entries(parentMutations.fields)) {
10567
- const skipCustomIdMutation = key === ACTIVITY_PROPERTY.CUSTOM_ID && preserveParentCustomId;
10568
- if (skipCustomIdMutation) continue;
10569
10578
  setActivityFieldDynamic(adapter, parentMutations.parentId, key, value);
10570
10579
  }
10571
10580
  if (parentMutations.promotionSnapshot) {
@@ -10719,10 +10728,7 @@ async function dispatchActivityPaste(action, options, deps) {
10719
10728
  {
10720
10729
  skipCorrelativeRecompute: true,
10721
10730
  skipBeforeSnap: true,
10722
- ...mappedParent !== void 0 ? {
10723
- customIdReferenceId: action.referenceActivityId,
10724
- preserveParentCustomId: true
10725
- } : {}
10731
+ customIdReferenceId: action.referenceActivityId
10726
10732
  }
10727
10733
  );
10728
10734
  if (!core.ok) return core;
@@ -11433,6 +11439,7 @@ async function dispatchActivityIndent(action, options, deps) {
11433
11439
  const failed = [];
11434
11440
  const succeededIds = [];
11435
11441
  const moves = [];
11442
+ const plannedIds = /* @__PURE__ */ new Set();
11436
11443
  const blockingSet = new Set(selectedSet);
11437
11444
  const sortedIds = [...action.activityIds].sort((a, b) => {
11438
11445
  const aSnap = adapter.getActivity(a);
@@ -11451,6 +11458,7 @@ async function dispatchActivityIndent(action, options, deps) {
11451
11458
  blockingSet.delete(id);
11452
11459
  continue;
11453
11460
  }
11461
+ if (hasAncestorInSet(id, plannedIds, adapter)) continue;
11454
11462
  const targetParentId = findPreviousNonSelectedSibling(
11455
11463
  id,
11456
11464
  blockingSet,
@@ -11467,6 +11475,7 @@ async function dispatchActivityIndent(action, options, deps) {
11467
11475
  const oldParentRaw = adapter.getActivity(id)?.parentId;
11468
11476
  const oldParentId = isRootParent(oldParentRaw) ? ROOT_PARENT_ID : String(oldParentRaw);
11469
11477
  moves.push({ activityId: id, newParentId: targetParentId, oldParentId });
11478
+ plannedIds.add(id);
11470
11479
  touched.add(id);
11471
11480
  touched.add(targetParentId);
11472
11481
  if (oldParentId !== ROOT_PARENT_ID) touched.add(oldParentId);
@@ -11629,6 +11638,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11629
11638
  const succeededIds = [];
11630
11639
  const oldParents = /* @__PURE__ */ new Set();
11631
11640
  const planned = [];
11641
+ const plannedIds = /* @__PURE__ */ new Set();
11632
11642
  const sortedIds = [...action.activityIds].sort((a, b) => {
11633
11643
  const aSnap = adapter.getActivity(a);
11634
11644
  const bSnap = adapter.getActivity(b);
@@ -11646,6 +11656,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11646
11656
  });
11647
11657
  continue;
11648
11658
  }
11659
+ if (hasAncestorInSet(key, plannedIds, adapter)) continue;
11649
11660
  const oldParent = activity.parentId;
11650
11661
  if (isRootParent(oldParent)) {
11651
11662
  failed.push({
@@ -11687,6 +11698,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11687
11698
  oldParentKey,
11688
11699
  targetIndexInGrandparent: targetIdx
11689
11700
  });
11701
+ plannedIds.add(key);
11690
11702
  touched.add(String(key));
11691
11703
  touched.add(String(oldParentKey));
11692
11704
  if (grandparentKey !== ROOT_PARENT_ID) touched.add(String(grandparentKey));
@@ -12376,6 +12388,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12376
12388
  for (const assignment of activities) {
12377
12389
  const before = deps.adapter.getActivity(assignment.id);
12378
12390
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12391
+ const previousProplannerId = before.proplannerId;
12379
12392
  deps.adapter.setActivityField(
12380
12393
  assignment.id,
12381
12394
  "proplannerId",
@@ -12386,7 +12399,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12386
12399
  kind: "updated",
12387
12400
  fields: {
12388
12401
  proplannerId: {
12389
- before: before.proplannerId,
12402
+ before: previousProplannerId,
12390
12403
  after: assignment.proplannerId
12391
12404
  }
12392
12405
  },
@@ -12396,13 +12409,14 @@ function dispatchPersistenceAcknowledge(action, deps) {
12396
12409
  for (const assignment of links) {
12397
12410
  const before = deps.adapter.getLink(assignment.id);
12398
12411
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12412
+ const previousProplannerId = before.proplannerId;
12399
12413
  deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
12400
12414
  linkChanges.push({
12401
12415
  id: assignment.id,
12402
12416
  kind: "updated",
12403
12417
  fields: {
12404
12418
  proplannerId: {
12405
- before: before.proplannerId,
12419
+ before: previousProplannerId,
12406
12420
  after: assignment.proplannerId
12407
12421
  }
12408
12422
  },
@@ -17033,7 +17047,6 @@ var PERSISTABLE_ACTIVITY_FIELDS = [
17033
17047
  "isLookahead",
17034
17048
  "newActivityIds",
17035
17049
  "hasNewActivities",
17036
- "pendingRequestIds",
17037
17050
  "responsableIds",
17038
17051
  "tagIds"
17039
17052
  ];
@@ -17045,7 +17058,6 @@ var DATE_FIELDS2 = /* @__PURE__ */ new Set([
17045
17058
  ]);
17046
17059
  var ID_ARRAY_FIELDS = /* @__PURE__ */ new Set([
17047
17060
  "newActivityIds",
17048
- "pendingRequestIds",
17049
17061
  "responsableIds",
17050
17062
  "tagIds"
17051
17063
  ]);
@@ -17103,10 +17115,15 @@ var SaveTracker = class {
17103
17115
  this.activitiesAtLastSave.clear();
17104
17116
  for (const activity of activities) {
17105
17117
  if (activity.proplannerId == null) continue;
17106
- this.activitiesAtLastSave.set(
17107
- String(activity.id),
17108
- projectPersistableActivity(activity)
17109
- );
17118
+ this.activitiesAtLastSave.set(String(activity.id), {
17119
+ persistable: projectPersistableActivity(activity),
17120
+ deletedSnapshot: {
17121
+ id: activity.id,
17122
+ proplannerId: activity.proplannerId,
17123
+ parentId: activity.parentId,
17124
+ hadBaseline: activity.baselineSnapshot !== null || activity.baselinePoints.length > 0
17125
+ }
17126
+ });
17110
17127
  }
17111
17128
  }
17112
17129
  snapshotLinks(links) {
@@ -17114,11 +17131,51 @@ var SaveTracker = class {
17114
17131
  for (const link of links) {
17115
17132
  if (link.proplannerId == null) continue;
17116
17133
  this.linksAtLastSave.set(String(link.id), {
17134
+ id: link.id,
17135
+ proplannerId: link.proplannerId,
17136
+ source: link.source,
17137
+ target: link.target,
17117
17138
  lag: link.lag,
17118
17139
  type: link.type
17119
17140
  });
17120
17141
  }
17121
17142
  }
17143
+ hasUnsavedChanges(activities, links) {
17144
+ if (!this.initialized) return false;
17145
+ if (this.deletedActivities(activities).length > 0) return true;
17146
+ if (this.deletedLinks(links).length > 0) return true;
17147
+ const activityChanged = activities.some((activity) => {
17148
+ if (activity.proplannerId == null) return true;
17149
+ const saved = this.activitiesAtLastSave.get(String(activity.id));
17150
+ return !saved || activityChangedSince(activity, saved.persistable);
17151
+ });
17152
+ if (activityChanged) return true;
17153
+ return links.some((link) => {
17154
+ if (link.proplannerId == null) return true;
17155
+ const saved = this.linksAtLastSave.get(String(link.id));
17156
+ return !saved || link.lag !== saved.lag || link.type !== saved.type;
17157
+ });
17158
+ }
17159
+ deletedActivities(activities) {
17160
+ if (!this.initialized) return [];
17161
+ const currentIds = new Set(
17162
+ activities.map((activity) => String(activity.id))
17163
+ );
17164
+ const deleted = [];
17165
+ for (const [activityId, saved] of this.activitiesAtLastSave) {
17166
+ if (currentIds.has(activityId)) continue;
17167
+ deleted.push({ ...saved.deletedSnapshot });
17168
+ }
17169
+ return deleted;
17170
+ }
17171
+ deletedLinks(links) {
17172
+ if (!this.initialized) return [];
17173
+ const currentIds = new Set(links.map((link) => String(link.id)));
17174
+ return Array.from(
17175
+ this.linksAtLastSave.entries(),
17176
+ ([linkId, saved]) => currentIds.has(linkId) ? null : { ...saved }
17177
+ ).filter((link) => link !== null);
17178
+ }
17122
17179
  modifiedActivities(activities) {
17123
17180
  if (!this.initialized) return [];
17124
17181
  return activities.filter((activity) => {
@@ -17126,7 +17183,7 @@ var SaveTracker = class {
17126
17183
  if (activity.assignedDefaultCalendar) return true;
17127
17184
  const saved = this.activitiesAtLastSave.get(String(activity.id));
17128
17185
  if (!saved) return true;
17129
- return activityChangedSince(activity, saved);
17186
+ return activityChangedSince(activity, saved.persistable);
17130
17187
  });
17131
17188
  }
17132
17189
  modifiedLinks(links) {
@@ -17286,7 +17343,7 @@ function parseActivity(raw, context) {
17286
17343
  hasCustomPonderator: raw.hasCustomPonderator,
17287
17344
  correlativeId: raw.correlative_id,
17288
17345
  uniqueCorrelativeId: raw.unique_correlative_id,
17289
- customId: raw.custom_id,
17346
+ customId: normalizeCustomId(raw),
17290
17347
  subcontractId: raw.subcontractId,
17291
17348
  isLookahead: raw.isOnLookahead,
17292
17349
  hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
@@ -17310,6 +17367,9 @@ function parseActivity(raw, context) {
17310
17367
  criticalPath: null
17311
17368
  };
17312
17369
  }
17370
+ function normalizeCustomId(raw) {
17371
+ return raw.has_childs ? null : raw.custom_id;
17372
+ }
17313
17373
  function forceRootProjectToRoot(activities) {
17314
17374
  const rootProject = activities.find(
17315
17375
  (activity) => activity.correlativeId === 0 && activity.type === "project"
@@ -18721,6 +18781,21 @@ var ScheduleCore = class {
18721
18781
  this.assertReady();
18722
18782
  return this._saveTracker.modifiedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18723
18783
  }
18784
+ hasUnsavedChanges() {
18785
+ this.assertReady();
18786
+ return this._saveTracker.hasUnsavedChanges(
18787
+ this.coreRuntime.state.getAllActivities(),
18788
+ this.getAllLinksView()
18789
+ );
18790
+ }
18791
+ getDeletedActivitiesSinceLastSave() {
18792
+ this.assertReady();
18793
+ return this._saveTracker.deletedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18794
+ }
18795
+ getDeletedLinksSinceLastSave() {
18796
+ this.assertReady();
18797
+ return this._saveTracker.deletedLinks(this.getAllLinksView()).map(cloneDomainValue);
18798
+ }
18724
18799
  _enqueue(work) {
18725
18800
  const workPromise = this._opQueue.then(work, work);
18726
18801
  this._opQueue = workPromise.then(
@@ -19041,7 +19116,7 @@ var ScheduleCore = class {
19041
19116
  applyStatusPass(state, this.coreRuntime.sector.statusCriteria);
19042
19117
  }
19043
19118
  }
19044
- undo() {
19119
+ undo(options = {}) {
19045
19120
  const operation = this._enqueue(async () => {
19046
19121
  this.assertReady();
19047
19122
  const entry = this._undo.takeUndo();
@@ -19061,7 +19136,7 @@ var ScheduleCore = class {
19061
19136
  });
19062
19137
  void operation.then(
19063
19138
  (changes) => {
19064
- if (changes) {
19139
+ if (changes && options.skipCriticalPath !== true) {
19065
19140
  void this.recomputeCriticalPath().catch((error) => {
19066
19141
  this.coreRuntime.reporter.error(
19067
19142
  "[ScheduleCore] Critical Path recompute after undo failed",
@@ -19074,7 +19149,7 @@ var ScheduleCore = class {
19074
19149
  );
19075
19150
  return operation;
19076
19151
  }
19077
- redo() {
19152
+ redo(options = {}) {
19078
19153
  const operation = this._enqueue(async () => {
19079
19154
  this.assertReady();
19080
19155
  const entry = this._undo.takeRedo();
@@ -19094,7 +19169,7 @@ var ScheduleCore = class {
19094
19169
  });
19095
19170
  void operation.then(
19096
19171
  (changes) => {
19097
- if (changes) {
19172
+ if (changes && options.skipCriticalPath !== true) {
19098
19173
  void this.recomputeCriticalPath().catch((error) => {
19099
19174
  this.coreRuntime.reporter.error(
19100
19175
  "[ScheduleCore] Critical Path recompute after redo failed",