@outbuild-company/schedule-core 1.4.0 → 1.6.0

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;
@@ -10471,14 +10485,7 @@ async function createActivityCore(action, deps, opts = {}) {
10471
10485
  const parentWasLeaf = parent !== null && adapter.getChildren(parent.id).length === 0;
10472
10486
  const beforeSnap = opts.skipBeforeSnap ? /* @__PURE__ */ new Map() : await snapshotActivities(adapter, initialTouched);
10473
10487
  adapter.addActivity(newActivity);
10474
- applyParentMutations(
10475
- action,
10476
- newId,
10477
- parent,
10478
- adapter,
10479
- opts.preserveParentCustomId ?? false,
10480
- parentWasLeaf
10481
- );
10488
+ applyParentMutations(action, newId, parent, adapter, parentWasLeaf);
10482
10489
  recomputeHhCascadeForActivity(newId, adapter);
10483
10490
  recomputeUsedCostCascadeForActivity(newId, adapter);
10484
10491
  recomputeCostCascadeForActivity(newId, adapter);
@@ -10548,7 +10555,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10548
10555
  return;
10549
10556
  }
10550
10557
  }
10551
- const customIdAnchorId = anchorSiblingId ?? opts.customIdReferenceId;
10558
+ const customIdAnchorId = opts.customIdReferenceId ?? anchorSiblingId;
10552
10559
  const derivesFromAnchor = customIdAnchorId !== void 0;
10553
10560
  const referenceActivity = derivesFromAnchor ? adapter.getActivity(customIdAnchorId) : parent;
10554
10561
  newActivity.customId = customIdTracker.generateCustomIdForNewChild({
@@ -10556,7 +10563,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10556
10563
  useActivityCustomIdAsReference: derivesFromAnchor
10557
10564
  });
10558
10565
  }
10559
- function applyParentMutations(action, newId, parent, adapter, preserveParentCustomId, parentWasLeaf) {
10566
+ function applyParentMutations(action, newId, parent, adapter, parentWasLeaf) {
10560
10567
  if (!parent) return;
10561
10568
  const parentMutations = buildParentMutations({
10562
10569
  parentId: action.parentId,
@@ -10566,8 +10573,6 @@ function applyParentMutations(action, newId, parent, adapter, preserveParentCust
10566
10573
  });
10567
10574
  if (!parentMutations) return;
10568
10575
  for (const [key, value] of Object.entries(parentMutations.fields)) {
10569
- const skipCustomIdMutation = key === ACTIVITY_PROPERTY.CUSTOM_ID && preserveParentCustomId;
10570
- if (skipCustomIdMutation) continue;
10571
10576
  setActivityFieldDynamic(adapter, parentMutations.parentId, key, value);
10572
10577
  }
10573
10578
  if (parentMutations.promotionSnapshot) {
@@ -10721,10 +10726,7 @@ async function dispatchActivityPaste(action, options, deps) {
10721
10726
  {
10722
10727
  skipCorrelativeRecompute: true,
10723
10728
  skipBeforeSnap: true,
10724
- ...mappedParent !== void 0 ? {
10725
- customIdReferenceId: action.referenceActivityId,
10726
- preserveParentCustomId: true
10727
- } : {}
10729
+ customIdReferenceId: action.referenceActivityId
10728
10730
  }
10729
10731
  );
10730
10732
  if (!core.ok) return core;
@@ -11435,6 +11437,7 @@ async function dispatchActivityIndent(action, options, deps) {
11435
11437
  const failed = [];
11436
11438
  const succeededIds = [];
11437
11439
  const moves = [];
11440
+ const plannedIds = /* @__PURE__ */ new Set();
11438
11441
  const blockingSet = new Set(selectedSet);
11439
11442
  const sortedIds = [...action.activityIds].sort((a, b) => {
11440
11443
  const aSnap = adapter.getActivity(a);
@@ -11453,6 +11456,7 @@ async function dispatchActivityIndent(action, options, deps) {
11453
11456
  blockingSet.delete(id);
11454
11457
  continue;
11455
11458
  }
11459
+ if (hasAncestorInSet(id, plannedIds, adapter)) continue;
11456
11460
  const targetParentId = findPreviousNonSelectedSibling(
11457
11461
  id,
11458
11462
  blockingSet,
@@ -11469,6 +11473,7 @@ async function dispatchActivityIndent(action, options, deps) {
11469
11473
  const oldParentRaw = adapter.getActivity(id)?.parentId;
11470
11474
  const oldParentId = isRootParent(oldParentRaw) ? ROOT_PARENT_ID : String(oldParentRaw);
11471
11475
  moves.push({ activityId: id, newParentId: targetParentId, oldParentId });
11476
+ plannedIds.add(id);
11472
11477
  touched.add(id);
11473
11478
  touched.add(targetParentId);
11474
11479
  if (oldParentId !== ROOT_PARENT_ID) touched.add(oldParentId);
@@ -11631,6 +11636,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11631
11636
  const succeededIds = [];
11632
11637
  const oldParents = /* @__PURE__ */ new Set();
11633
11638
  const planned = [];
11639
+ const plannedIds = /* @__PURE__ */ new Set();
11634
11640
  const sortedIds = [...action.activityIds].sort((a, b) => {
11635
11641
  const aSnap = adapter.getActivity(a);
11636
11642
  const bSnap = adapter.getActivity(b);
@@ -11648,6 +11654,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11648
11654
  });
11649
11655
  continue;
11650
11656
  }
11657
+ if (hasAncestorInSet(key, plannedIds, adapter)) continue;
11651
11658
  const oldParent = activity.parentId;
11652
11659
  if (isRootParent(oldParent)) {
11653
11660
  failed.push({
@@ -11689,6 +11696,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11689
11696
  oldParentKey,
11690
11697
  targetIndexInGrandparent: targetIdx
11691
11698
  });
11699
+ plannedIds.add(key);
11692
11700
  touched.add(String(key));
11693
11701
  touched.add(String(oldParentKey));
11694
11702
  if (grandparentKey !== ROOT_PARENT_ID) touched.add(String(grandparentKey));
@@ -12378,6 +12386,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12378
12386
  for (const assignment of activities) {
12379
12387
  const before = deps.adapter.getActivity(assignment.id);
12380
12388
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12389
+ const previousProplannerId = before.proplannerId;
12381
12390
  deps.adapter.setActivityField(
12382
12391
  assignment.id,
12383
12392
  "proplannerId",
@@ -12388,7 +12397,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12388
12397
  kind: "updated",
12389
12398
  fields: {
12390
12399
  proplannerId: {
12391
- before: before.proplannerId,
12400
+ before: previousProplannerId,
12392
12401
  after: assignment.proplannerId
12393
12402
  }
12394
12403
  },
@@ -12398,13 +12407,14 @@ function dispatchPersistenceAcknowledge(action, deps) {
12398
12407
  for (const assignment of links) {
12399
12408
  const before = deps.adapter.getLink(assignment.id);
12400
12409
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12410
+ const previousProplannerId = before.proplannerId;
12401
12411
  deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
12402
12412
  linkChanges.push({
12403
12413
  id: assignment.id,
12404
12414
  kind: "updated",
12405
12415
  fields: {
12406
12416
  proplannerId: {
12407
- before: before.proplannerId,
12417
+ before: previousProplannerId,
12408
12418
  after: assignment.proplannerId
12409
12419
  }
12410
12420
  },
@@ -17035,7 +17045,6 @@ var PERSISTABLE_ACTIVITY_FIELDS = [
17035
17045
  "isLookahead",
17036
17046
  "newActivityIds",
17037
17047
  "hasNewActivities",
17038
- "pendingRequestIds",
17039
17048
  "responsableIds",
17040
17049
  "tagIds"
17041
17050
  ];
@@ -17047,7 +17056,6 @@ var DATE_FIELDS2 = /* @__PURE__ */ new Set([
17047
17056
  ]);
17048
17057
  var ID_ARRAY_FIELDS = /* @__PURE__ */ new Set([
17049
17058
  "newActivityIds",
17050
- "pendingRequestIds",
17051
17059
  "responsableIds",
17052
17060
  "tagIds"
17053
17061
  ]);
@@ -17105,10 +17113,15 @@ var SaveTracker = class {
17105
17113
  this.activitiesAtLastSave.clear();
17106
17114
  for (const activity of activities) {
17107
17115
  if (activity.proplannerId == null) continue;
17108
- this.activitiesAtLastSave.set(
17109
- String(activity.id),
17110
- projectPersistableActivity(activity)
17111
- );
17116
+ this.activitiesAtLastSave.set(String(activity.id), {
17117
+ persistable: projectPersistableActivity(activity),
17118
+ deletedSnapshot: {
17119
+ id: activity.id,
17120
+ proplannerId: activity.proplannerId,
17121
+ parentId: activity.parentId,
17122
+ hadBaseline: activity.baselineSnapshot !== null || activity.baselinePoints.length > 0
17123
+ }
17124
+ });
17112
17125
  }
17113
17126
  }
17114
17127
  snapshotLinks(links) {
@@ -17116,11 +17129,51 @@ var SaveTracker = class {
17116
17129
  for (const link of links) {
17117
17130
  if (link.proplannerId == null) continue;
17118
17131
  this.linksAtLastSave.set(String(link.id), {
17132
+ id: link.id,
17133
+ proplannerId: link.proplannerId,
17134
+ source: link.source,
17135
+ target: link.target,
17119
17136
  lag: link.lag,
17120
17137
  type: link.type
17121
17138
  });
17122
17139
  }
17123
17140
  }
17141
+ hasUnsavedChanges(activities, links) {
17142
+ if (!this.initialized) return false;
17143
+ if (this.deletedActivities(activities).length > 0) return true;
17144
+ if (this.deletedLinks(links).length > 0) return true;
17145
+ const activityChanged = activities.some((activity) => {
17146
+ if (activity.proplannerId == null) return true;
17147
+ const saved = this.activitiesAtLastSave.get(String(activity.id));
17148
+ return !saved || activityChangedSince(activity, saved.persistable);
17149
+ });
17150
+ if (activityChanged) return true;
17151
+ return links.some((link) => {
17152
+ if (link.proplannerId == null) return true;
17153
+ const saved = this.linksAtLastSave.get(String(link.id));
17154
+ return !saved || link.lag !== saved.lag || link.type !== saved.type;
17155
+ });
17156
+ }
17157
+ deletedActivities(activities) {
17158
+ if (!this.initialized) return [];
17159
+ const currentIds = new Set(
17160
+ activities.map((activity) => String(activity.id))
17161
+ );
17162
+ const deleted = [];
17163
+ for (const [activityId, saved] of this.activitiesAtLastSave) {
17164
+ if (currentIds.has(activityId)) continue;
17165
+ deleted.push({ ...saved.deletedSnapshot });
17166
+ }
17167
+ return deleted;
17168
+ }
17169
+ deletedLinks(links) {
17170
+ if (!this.initialized) return [];
17171
+ const currentIds = new Set(links.map((link) => String(link.id)));
17172
+ return Array.from(
17173
+ this.linksAtLastSave.entries(),
17174
+ ([linkId, saved]) => currentIds.has(linkId) ? null : { ...saved }
17175
+ ).filter((link) => link !== null);
17176
+ }
17124
17177
  modifiedActivities(activities) {
17125
17178
  if (!this.initialized) return [];
17126
17179
  return activities.filter((activity) => {
@@ -17128,7 +17181,7 @@ var SaveTracker = class {
17128
17181
  if (activity.assignedDefaultCalendar) return true;
17129
17182
  const saved = this.activitiesAtLastSave.get(String(activity.id));
17130
17183
  if (!saved) return true;
17131
- return activityChangedSince(activity, saved);
17184
+ return activityChangedSince(activity, saved.persistable);
17132
17185
  });
17133
17186
  }
17134
17187
  modifiedLinks(links) {
@@ -17288,7 +17341,7 @@ function parseActivity(raw, context) {
17288
17341
  hasCustomPonderator: raw.hasCustomPonderator,
17289
17342
  correlativeId: raw.correlative_id,
17290
17343
  uniqueCorrelativeId: raw.unique_correlative_id,
17291
- customId: raw.custom_id,
17344
+ customId: normalizeCustomId(raw),
17292
17345
  subcontractId: raw.subcontractId,
17293
17346
  isLookahead: raw.isOnLookahead,
17294
17347
  hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
@@ -17312,6 +17365,9 @@ function parseActivity(raw, context) {
17312
17365
  criticalPath: null
17313
17366
  };
17314
17367
  }
17368
+ function normalizeCustomId(raw) {
17369
+ return raw.has_childs ? null : raw.custom_id;
17370
+ }
17315
17371
  function forceRootProjectToRoot(activities) {
17316
17372
  const rootProject = activities.find(
17317
17373
  (activity) => activity.correlativeId === 0 && activity.type === "project"
@@ -18723,6 +18779,21 @@ var ScheduleCore = class {
18723
18779
  this.assertReady();
18724
18780
  return this._saveTracker.modifiedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18725
18781
  }
18782
+ hasUnsavedChanges() {
18783
+ this.assertReady();
18784
+ return this._saveTracker.hasUnsavedChanges(
18785
+ this.coreRuntime.state.getAllActivities(),
18786
+ this.getAllLinksView()
18787
+ );
18788
+ }
18789
+ getDeletedActivitiesSinceLastSave() {
18790
+ this.assertReady();
18791
+ return this._saveTracker.deletedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18792
+ }
18793
+ getDeletedLinksSinceLastSave() {
18794
+ this.assertReady();
18795
+ return this._saveTracker.deletedLinks(this.getAllLinksView()).map(cloneDomainValue);
18796
+ }
18726
18797
  _enqueue(work) {
18727
18798
  const workPromise = this._opQueue.then(work, work);
18728
18799
  this._opQueue = workPromise.then(
@@ -19043,7 +19114,7 @@ var ScheduleCore = class {
19043
19114
  applyStatusPass(state, this.coreRuntime.sector.statusCriteria);
19044
19115
  }
19045
19116
  }
19046
- undo() {
19117
+ undo(options = {}) {
19047
19118
  const operation = this._enqueue(async () => {
19048
19119
  this.assertReady();
19049
19120
  const entry = this._undo.takeUndo();
@@ -19063,7 +19134,7 @@ var ScheduleCore = class {
19063
19134
  });
19064
19135
  void operation.then(
19065
19136
  (changes) => {
19066
- if (changes) {
19137
+ if (changes && options.skipCriticalPath !== true) {
19067
19138
  void this.recomputeCriticalPath().catch((error) => {
19068
19139
  this.coreRuntime.reporter.error(
19069
19140
  "[ScheduleCore] Critical Path recompute after undo failed",
@@ -19076,7 +19147,7 @@ var ScheduleCore = class {
19076
19147
  );
19077
19148
  return operation;
19078
19149
  }
19079
- redo() {
19150
+ redo(options = {}) {
19080
19151
  const operation = this._enqueue(async () => {
19081
19152
  this.assertReady();
19082
19153
  const entry = this._undo.takeRedo();
@@ -19096,7 +19167,7 @@ var ScheduleCore = class {
19096
19167
  });
19097
19168
  void operation.then(
19098
19169
  (changes) => {
19099
- if (changes) {
19170
+ if (changes && options.skipCriticalPath !== true) {
19100
19171
  void this.recomputeCriticalPath().catch((error) => {
19101
19172
  this.coreRuntime.reporter.error(
19102
19173
  "[ScheduleCore] Critical Path recompute after redo failed",