@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.cjs CHANGED
@@ -4024,7 +4024,10 @@ var MAX_CUSTOM_ID_LENGTH = 20;
4024
4024
  // src/columns/custom-id/custom-id-pipeline.ts
4025
4025
  var customIdPipeline = {
4026
4026
  targetField: "customId",
4027
- canEdit() {
4027
+ canEdit(activity, hierarchy) {
4028
+ if (isSummaryActivity2(activity, hierarchy)) {
4029
+ return cannotEdit("summary", "summary_column_value");
4030
+ }
4028
4031
  return canEdit();
4029
4032
  },
4030
4033
  parseInput(rawValue) {
@@ -10337,7 +10340,6 @@ function buildParentMutations(input) {
10337
10340
  fields.constraintType = CONSTRAINT_TYPE2.ASAP;
10338
10341
  fields.constraintDate = null;
10339
10342
  fields.progress = 0;
10340
- fields.expectedProgressBaseline = 0;
10341
10343
  }
10342
10344
  }
10343
10345
  return {
@@ -10403,6 +10405,18 @@ function findFirstSelectedSibling(taskId, selected, adapter) {
10403
10405
  }
10404
10406
  return firstSelected;
10405
10407
  }
10408
+ function hasAncestorInSet(activityId, candidates, adapter) {
10409
+ const visited = /* @__PURE__ */ new Set([activityId]);
10410
+ let parentId = normalizeParentKey(
10411
+ adapter.getActivity(activityId)?.parentId
10412
+ );
10413
+ while (parentId !== ROOT_PARENT_ID && !visited.has(parentId)) {
10414
+ if (candidates.has(parentId)) return true;
10415
+ visited.add(parentId);
10416
+ parentId = normalizeParentKey(adapter.getActivity(parentId)?.parentId);
10417
+ }
10418
+ return false;
10419
+ }
10406
10420
  function findPrevSiblingHelper(taskId, adapter) {
10407
10421
  const a = adapter.getActivity(taskId);
10408
10422
  if (!a) return null;
@@ -10469,16 +10483,13 @@ async function createActivityCore(action, deps, opts = {}) {
10469
10483
  const initialTouched = /* @__PURE__ */ new Set([newId]);
10470
10484
  if (parent) initialTouched.add(parent.id);
10471
10485
  const parentWasLeaf = parent !== null && adapter.getChildren(parent.id).length === 0;
10486
+ const parentCustomId = parentWasLeaf ? parent?.customId : null;
10472
10487
  const beforeSnap = opts.skipBeforeSnap ? /* @__PURE__ */ new Map() : await snapshotActivities(adapter, initialTouched);
10473
10488
  adapter.addActivity(newActivity);
10474
- applyParentMutations(
10475
- action,
10476
- newId,
10477
- parent,
10478
- adapter,
10479
- opts.preserveParentCustomId ?? false,
10480
- parentWasLeaf
10481
- );
10489
+ applyParentMutations(action, newId, parent, adapter, parentWasLeaf);
10490
+ if (parentCustomId) {
10491
+ deps.customIdTracker.releaseCustomId(parentCustomId);
10492
+ }
10482
10493
  recomputeHhCascadeForActivity(newId, adapter);
10483
10494
  recomputeUsedCostCascadeForActivity(newId, adapter);
10484
10495
  recomputeCostCascadeForActivity(newId, adapter);
@@ -10548,7 +10559,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10548
10559
  return;
10549
10560
  }
10550
10561
  }
10551
- const customIdAnchorId = anchorSiblingId ?? opts.customIdReferenceId;
10562
+ const customIdAnchorId = opts.customIdReferenceId ?? anchorSiblingId;
10552
10563
  const derivesFromAnchor = customIdAnchorId !== void 0;
10553
10564
  const referenceActivity = derivesFromAnchor ? adapter.getActivity(customIdAnchorId) : parent;
10554
10565
  newActivity.customId = customIdTracker.generateCustomIdForNewChild({
@@ -10556,7 +10567,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10556
10567
  useActivityCustomIdAsReference: derivesFromAnchor
10557
10568
  });
10558
10569
  }
10559
- function applyParentMutations(action, newId, parent, adapter, preserveParentCustomId, parentWasLeaf) {
10570
+ function applyParentMutations(action, newId, parent, adapter, parentWasLeaf) {
10560
10571
  if (!parent) return;
10561
10572
  const parentMutations = buildParentMutations({
10562
10573
  parentId: action.parentId,
@@ -10566,8 +10577,6 @@ function applyParentMutations(action, newId, parent, adapter, preserveParentCust
10566
10577
  });
10567
10578
  if (!parentMutations) return;
10568
10579
  for (const [key, value] of Object.entries(parentMutations.fields)) {
10569
- const skipCustomIdMutation = key === ACTIVITY_PROPERTY.CUSTOM_ID && preserveParentCustomId;
10570
- if (skipCustomIdMutation) continue;
10571
10580
  setActivityFieldDynamic(adapter, parentMutations.parentId, key, value);
10572
10581
  }
10573
10582
  if (parentMutations.promotionSnapshot) {
@@ -10721,10 +10730,7 @@ async function dispatchActivityPaste(action, options, deps) {
10721
10730
  {
10722
10731
  skipCorrelativeRecompute: true,
10723
10732
  skipBeforeSnap: true,
10724
- ...mappedParent !== void 0 ? {
10725
- customIdReferenceId: action.referenceActivityId,
10726
- preserveParentCustomId: true
10727
- } : {}
10733
+ customIdReferenceId: action.referenceActivityId
10728
10734
  }
10729
10735
  );
10730
10736
  if (!core.ok) return core;
@@ -11435,6 +11441,7 @@ async function dispatchActivityIndent(action, options, deps) {
11435
11441
  const failed = [];
11436
11442
  const succeededIds = [];
11437
11443
  const moves = [];
11444
+ const plannedIds = /* @__PURE__ */ new Set();
11438
11445
  const blockingSet = new Set(selectedSet);
11439
11446
  const sortedIds = [...action.activityIds].sort((a, b) => {
11440
11447
  const aSnap = adapter.getActivity(a);
@@ -11453,6 +11460,7 @@ async function dispatchActivityIndent(action, options, deps) {
11453
11460
  blockingSet.delete(id);
11454
11461
  continue;
11455
11462
  }
11463
+ if (hasAncestorInSet(id, plannedIds, adapter)) continue;
11456
11464
  const targetParentId = findPreviousNonSelectedSibling(
11457
11465
  id,
11458
11466
  blockingSet,
@@ -11469,6 +11477,7 @@ async function dispatchActivityIndent(action, options, deps) {
11469
11477
  const oldParentRaw = adapter.getActivity(id)?.parentId;
11470
11478
  const oldParentId = isRootParent(oldParentRaw) ? ROOT_PARENT_ID : String(oldParentRaw);
11471
11479
  moves.push({ activityId: id, newParentId: targetParentId, oldParentId });
11480
+ plannedIds.add(id);
11472
11481
  touched.add(id);
11473
11482
  touched.add(targetParentId);
11474
11483
  if (oldParentId !== ROOT_PARENT_ID) touched.add(oldParentId);
@@ -11631,6 +11640,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11631
11640
  const succeededIds = [];
11632
11641
  const oldParents = /* @__PURE__ */ new Set();
11633
11642
  const planned = [];
11643
+ const plannedIds = /* @__PURE__ */ new Set();
11634
11644
  const sortedIds = [...action.activityIds].sort((a, b) => {
11635
11645
  const aSnap = adapter.getActivity(a);
11636
11646
  const bSnap = adapter.getActivity(b);
@@ -11648,6 +11658,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11648
11658
  });
11649
11659
  continue;
11650
11660
  }
11661
+ if (hasAncestorInSet(key, plannedIds, adapter)) continue;
11651
11662
  const oldParent = activity.parentId;
11652
11663
  if (isRootParent(oldParent)) {
11653
11664
  failed.push({
@@ -11689,6 +11700,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11689
11700
  oldParentKey,
11690
11701
  targetIndexInGrandparent: targetIdx
11691
11702
  });
11703
+ plannedIds.add(key);
11692
11704
  touched.add(String(key));
11693
11705
  touched.add(String(oldParentKey));
11694
11706
  if (grandparentKey !== ROOT_PARENT_ID) touched.add(String(grandparentKey));
@@ -12378,6 +12390,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12378
12390
  for (const assignment of activities) {
12379
12391
  const before = deps.adapter.getActivity(assignment.id);
12380
12392
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12393
+ const previousProplannerId = before.proplannerId;
12381
12394
  deps.adapter.setActivityField(
12382
12395
  assignment.id,
12383
12396
  "proplannerId",
@@ -12388,7 +12401,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12388
12401
  kind: "updated",
12389
12402
  fields: {
12390
12403
  proplannerId: {
12391
- before: before.proplannerId,
12404
+ before: previousProplannerId,
12392
12405
  after: assignment.proplannerId
12393
12406
  }
12394
12407
  },
@@ -12398,13 +12411,14 @@ function dispatchPersistenceAcknowledge(action, deps) {
12398
12411
  for (const assignment of links) {
12399
12412
  const before = deps.adapter.getLink(assignment.id);
12400
12413
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12414
+ const previousProplannerId = before.proplannerId;
12401
12415
  deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
12402
12416
  linkChanges.push({
12403
12417
  id: assignment.id,
12404
12418
  kind: "updated",
12405
12419
  fields: {
12406
12420
  proplannerId: {
12407
- before: before.proplannerId,
12421
+ before: previousProplannerId,
12408
12422
  after: assignment.proplannerId
12409
12423
  }
12410
12424
  },
@@ -17035,7 +17049,6 @@ var PERSISTABLE_ACTIVITY_FIELDS = [
17035
17049
  "isLookahead",
17036
17050
  "newActivityIds",
17037
17051
  "hasNewActivities",
17038
- "pendingRequestIds",
17039
17052
  "responsableIds",
17040
17053
  "tagIds"
17041
17054
  ];
@@ -17047,7 +17060,6 @@ var DATE_FIELDS2 = /* @__PURE__ */ new Set([
17047
17060
  ]);
17048
17061
  var ID_ARRAY_FIELDS = /* @__PURE__ */ new Set([
17049
17062
  "newActivityIds",
17050
- "pendingRequestIds",
17051
17063
  "responsableIds",
17052
17064
  "tagIds"
17053
17065
  ]);
@@ -17105,10 +17117,15 @@ var SaveTracker = class {
17105
17117
  this.activitiesAtLastSave.clear();
17106
17118
  for (const activity of activities) {
17107
17119
  if (activity.proplannerId == null) continue;
17108
- this.activitiesAtLastSave.set(
17109
- String(activity.id),
17110
- projectPersistableActivity(activity)
17111
- );
17120
+ this.activitiesAtLastSave.set(String(activity.id), {
17121
+ persistable: projectPersistableActivity(activity),
17122
+ deletedSnapshot: {
17123
+ id: activity.id,
17124
+ proplannerId: activity.proplannerId,
17125
+ parentId: activity.parentId,
17126
+ hadBaseline: activity.baselineSnapshot !== null || activity.baselinePoints.length > 0
17127
+ }
17128
+ });
17112
17129
  }
17113
17130
  }
17114
17131
  snapshotLinks(links) {
@@ -17116,11 +17133,51 @@ var SaveTracker = class {
17116
17133
  for (const link of links) {
17117
17134
  if (link.proplannerId == null) continue;
17118
17135
  this.linksAtLastSave.set(String(link.id), {
17136
+ id: link.id,
17137
+ proplannerId: link.proplannerId,
17138
+ source: link.source,
17139
+ target: link.target,
17119
17140
  lag: link.lag,
17120
17141
  type: link.type
17121
17142
  });
17122
17143
  }
17123
17144
  }
17145
+ hasUnsavedChanges(activities, links) {
17146
+ if (!this.initialized) return false;
17147
+ if (this.deletedActivities(activities).length > 0) return true;
17148
+ if (this.deletedLinks(links).length > 0) return true;
17149
+ const activityChanged = activities.some((activity) => {
17150
+ if (activity.proplannerId == null) return true;
17151
+ const saved = this.activitiesAtLastSave.get(String(activity.id));
17152
+ return !saved || activityChangedSince(activity, saved.persistable);
17153
+ });
17154
+ if (activityChanged) return true;
17155
+ return links.some((link) => {
17156
+ if (link.proplannerId == null) return true;
17157
+ const saved = this.linksAtLastSave.get(String(link.id));
17158
+ return !saved || link.lag !== saved.lag || link.type !== saved.type;
17159
+ });
17160
+ }
17161
+ deletedActivities(activities) {
17162
+ if (!this.initialized) return [];
17163
+ const currentIds = new Set(
17164
+ activities.map((activity) => String(activity.id))
17165
+ );
17166
+ const deleted = [];
17167
+ for (const [activityId, saved] of this.activitiesAtLastSave) {
17168
+ if (currentIds.has(activityId)) continue;
17169
+ deleted.push({ ...saved.deletedSnapshot });
17170
+ }
17171
+ return deleted;
17172
+ }
17173
+ deletedLinks(links) {
17174
+ if (!this.initialized) return [];
17175
+ const currentIds = new Set(links.map((link) => String(link.id)));
17176
+ return Array.from(
17177
+ this.linksAtLastSave.entries(),
17178
+ ([linkId, saved]) => currentIds.has(linkId) ? null : { ...saved }
17179
+ ).filter((link) => link !== null);
17180
+ }
17124
17181
  modifiedActivities(activities) {
17125
17182
  if (!this.initialized) return [];
17126
17183
  return activities.filter((activity) => {
@@ -17128,7 +17185,7 @@ var SaveTracker = class {
17128
17185
  if (activity.assignedDefaultCalendar) return true;
17129
17186
  const saved = this.activitiesAtLastSave.get(String(activity.id));
17130
17187
  if (!saved) return true;
17131
- return activityChangedSince(activity, saved);
17188
+ return activityChangedSince(activity, saved.persistable);
17132
17189
  });
17133
17190
  }
17134
17191
  modifiedLinks(links) {
@@ -17288,7 +17345,7 @@ function parseActivity(raw, context) {
17288
17345
  hasCustomPonderator: raw.hasCustomPonderator,
17289
17346
  correlativeId: raw.correlative_id,
17290
17347
  uniqueCorrelativeId: raw.unique_correlative_id,
17291
- customId: raw.custom_id,
17348
+ customId: normalizeCustomId(raw),
17292
17349
  subcontractId: raw.subcontractId,
17293
17350
  isLookahead: raw.isOnLookahead,
17294
17351
  hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
@@ -17312,6 +17369,9 @@ function parseActivity(raw, context) {
17312
17369
  criticalPath: null
17313
17370
  };
17314
17371
  }
17372
+ function normalizeCustomId(raw) {
17373
+ return raw.has_childs ? null : raw.custom_id;
17374
+ }
17315
17375
  function forceRootProjectToRoot(activities) {
17316
17376
  const rootProject = activities.find(
17317
17377
  (activity) => activity.correlativeId === 0 && activity.type === "project"
@@ -18723,6 +18783,21 @@ var ScheduleCore = class {
18723
18783
  this.assertReady();
18724
18784
  return this._saveTracker.modifiedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18725
18785
  }
18786
+ hasUnsavedChanges() {
18787
+ this.assertReady();
18788
+ return this._saveTracker.hasUnsavedChanges(
18789
+ this.coreRuntime.state.getAllActivities(),
18790
+ this.getAllLinksView()
18791
+ );
18792
+ }
18793
+ getDeletedActivitiesSinceLastSave() {
18794
+ this.assertReady();
18795
+ return this._saveTracker.deletedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18796
+ }
18797
+ getDeletedLinksSinceLastSave() {
18798
+ this.assertReady();
18799
+ return this._saveTracker.deletedLinks(this.getAllLinksView()).map(cloneDomainValue);
18800
+ }
18726
18801
  _enqueue(work) {
18727
18802
  const workPromise = this._opQueue.then(work, work);
18728
18803
  this._opQueue = workPromise.then(
@@ -19043,7 +19118,7 @@ var ScheduleCore = class {
19043
19118
  applyStatusPass(state, this.coreRuntime.sector.statusCriteria);
19044
19119
  }
19045
19120
  }
19046
- undo() {
19121
+ undo(options = {}) {
19047
19122
  const operation = this._enqueue(async () => {
19048
19123
  this.assertReady();
19049
19124
  const entry = this._undo.takeUndo();
@@ -19063,7 +19138,7 @@ var ScheduleCore = class {
19063
19138
  });
19064
19139
  void operation.then(
19065
19140
  (changes) => {
19066
- if (changes) {
19141
+ if (changes && options.skipCriticalPath !== true) {
19067
19142
  void this.recomputeCriticalPath().catch((error) => {
19068
19143
  this.coreRuntime.reporter.error(
19069
19144
  "[ScheduleCore] Critical Path recompute after undo failed",
@@ -19076,7 +19151,7 @@ var ScheduleCore = class {
19076
19151
  );
19077
19152
  return operation;
19078
19153
  }
19079
- redo() {
19154
+ redo(options = {}) {
19080
19155
  const operation = this._enqueue(async () => {
19081
19156
  this.assertReady();
19082
19157
  const entry = this._undo.takeRedo();
@@ -19096,7 +19171,7 @@ var ScheduleCore = class {
19096
19171
  });
19097
19172
  void operation.then(
19098
19173
  (changes) => {
19099
- if (changes) {
19174
+ if (changes && options.skipCriticalPath !== true) {
19100
19175
  void this.recomputeCriticalPath().catch((error) => {
19101
19176
  this.coreRuntime.reporter.error(
19102
19177
  "[ScheduleCore] Critical Path recompute after redo failed",