@outbuild-company/schedule-core 1.22.0 → 1.24.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
@@ -342,6 +342,7 @@ var LINK_REJECTION_REASON = {
342
342
  // src/dispatch/action-kinds.ts
343
343
  var DISPATCH_ACTION_KIND = {
344
344
  ACTIVITY_CLIPBOARD_CLEAR: "activity-clipboard-clear",
345
+ ACTIVITY_CLIPBOARD_IMPORT: "activity-clipboard-import",
345
346
  ACTIVITY_COPY: "activity-copy",
346
347
  CALENDAR_RECONCILE: "calendar-reconcile",
347
348
  ACTIVITY_PASTE_COPIED: "activity-paste-copied",
@@ -524,6 +525,7 @@ function createActivitiesApi(engine) {
524
525
  hasChildren: (parentId) => engine.hasChild(parentId),
525
526
  indentTargetParentIds: (ids) => engine.getIndentTargetParentIds(ids),
526
527
  isCustomIdInUse: (customId, currentCustomId) => engine.isCustomIdInUse(customId, currentCustomId ?? null),
528
+ clipboard: () => engine.getActivityClipboardView(),
527
529
  create: (input, options) => engine.dispatch(
528
530
  { ...input, kind: DISPATCH_ACTION_KIND.ACTIVITY_CREATE },
529
531
  options
@@ -606,6 +608,12 @@ function createActivitiesApi(engine) {
606
608
  ...context,
607
609
  kind: DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR
608
610
  }),
611
+ importClipboard: (snapshot, references, context = {}) => engine.dispatch({
612
+ ...context,
613
+ kind: DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_IMPORT,
614
+ snapshot,
615
+ ...references === void 0 ? {} : { references }
616
+ }),
609
617
  lookaheadSync: (input) => engine.dispatch({
610
618
  ...input,
611
619
  kind: DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC
@@ -948,6 +956,7 @@ var REJECTION_REASON = {
948
956
  ACTIVITY_IDS_EMPTY: "activity_ids_empty",
949
957
  NO_ACTIVITY_SELECTION: "no_activity_selection",
950
958
  CLIPBOARD_EMPTY: "clipboard_empty",
959
+ CLIPBOARD_SNAPSHOT_INVALID: "clipboard_snapshot_invalid",
951
960
  ACTIVITY_PASTE_INVARIANT_FAILED: "activity_paste_invariant_failed",
952
961
  SOLE_SCHEDULE_ROOT: "sole_schedule_root",
953
962
  ANCHOR_SIBLING_CONFLICT: "anchor_sibling_conflict",
@@ -11744,6 +11753,7 @@ var NON_SCHEDULING_INLINE_COLUMNS = /* @__PURE__ */ new Set([
11744
11753
  ]);
11745
11754
  var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
11746
11755
  DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR,
11756
+ DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_IMPORT,
11747
11757
  DISPATCH_ACTION_KIND.ACTIVITY_COPY,
11748
11758
  DISPATCH_ACTION_KIND.CELL_CLIPBOARD_CLEAR,
11749
11759
  DISPATCH_ACTION_KIND.CELL_COPY,
@@ -11764,6 +11774,7 @@ var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
11764
11774
  ]);
11765
11775
  var PURE_VIEW_STATE_KINDS = /* @__PURE__ */ new Set([
11766
11776
  DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR,
11777
+ DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_IMPORT,
11767
11778
  DISPATCH_ACTION_KIND.ACTIVITY_COPY,
11768
11779
  DISPATCH_ACTION_KIND.CELL_CLIPBOARD_CLEAR,
11769
11780
  DISPATCH_ACTION_KIND.CELL_COPY,
@@ -11968,14 +11979,65 @@ function createOutbuildP6ActivityDraft(activity) {
11968
11979
  if (state === void 0) return void 0;
11969
11980
  return {
11970
11981
  ...state,
11971
- remainingDurationHours: draftRemainingDuration(activity)
11982
+ ...outbuildProgressExecution(activity, activity.durationHours)
11972
11983
  };
11973
11984
  }
11974
- function draftRemainingDuration(activity) {
11985
+ var COMPLETED_PHYSICAL_PERCENT = 100;
11986
+ var NOT_STARTED_PHYSICAL_PERCENT = 0;
11987
+ function outbuildProgressExecution(activity, originalDurationHours) {
11975
11988
  if (activity.progress === NOT_STARTED_PROGRESS) {
11976
- return activity.durationHours;
11989
+ return {
11990
+ executionStatus: P6_EXECUTION_STATUS.NOT_STARTED,
11991
+ actualStart: null,
11992
+ actualFinish: null,
11993
+ remainingDurationHours: originalDurationHours,
11994
+ physicalPercentComplete: NOT_STARTED_PHYSICAL_PERCENT,
11995
+ actualDurationHours: 0,
11996
+ atCompletionDurationHours: originalDurationHours,
11997
+ durationPercentComplete: 0
11998
+ };
11977
11999
  }
11978
- return isCompletedProgress(activity.progress) ? 0 : null;
12000
+ const actualStart = cloneValidDate(activity.startDate, "actualStart");
12001
+ if (isCompletedProgress(activity.progress)) {
12002
+ const hasMeasurableDuration = originalDurationHours > 0;
12003
+ return {
12004
+ executionStatus: P6_EXECUTION_STATUS.COMPLETED,
12005
+ actualStart,
12006
+ actualFinish: cloneValidDate(activity.endDate, "actualFinish"),
12007
+ remainingDurationHours: 0,
12008
+ physicalPercentComplete: COMPLETED_PHYSICAL_PERCENT,
12009
+ actualDurationHours: originalDurationHours,
12010
+ atCompletionDurationHours: originalDurationHours,
12011
+ durationPercentComplete: hasMeasurableDuration ? COMPLETED_PHYSICAL_PERCENT : NOT_STARTED_PHYSICAL_PERCENT
12012
+ };
12013
+ }
12014
+ return {
12015
+ executionStatus: P6_EXECUTION_STATUS.IN_PROGRESS,
12016
+ actualStart,
12017
+ actualFinish: null,
12018
+ remainingDurationHours: originalDurationHours,
12019
+ physicalPercentComplete: activity.progress,
12020
+ actualDurationHours: 0,
12021
+ atCompletionDurationHours: originalDurationHours,
12022
+ durationPercentComplete: 0
12023
+ };
12024
+ }
12025
+ function awaitsOutbuildExecution(draft) {
12026
+ return draft.executionStatus === P6_EXECUTION_STATUS.NOT_STARTED && draft.actualStart === null;
12027
+ }
12028
+ function reconcileOutbuildProgress(activity, draft) {
12029
+ const isProgressed = activity.progress !== NOT_STARTED_PROGRESS;
12030
+ if (!isProgressed || !awaitsOutbuildExecution(draft)) return draft;
12031
+ const inferred = outbuildProgressExecution(
12032
+ activity,
12033
+ draft.originalDurationHours
12034
+ );
12035
+ const keepsRecordedRemaining = inferred.executionStatus === P6_EXECUTION_STATUS.IN_PROGRESS && draft.remainingDurationHours !== null;
12036
+ return {
12037
+ ...draft,
12038
+ ...inferred,
12039
+ ...keepsRecordedRemaining ? { remainingDurationHours: draft.remainingDurationHours } : {}
12040
+ };
11979
12041
  }
11980
12042
 
11981
12043
  // src/p6/inactive-drafts.ts
@@ -12028,17 +12090,41 @@ function ordinaryInputChanges(before, activity) {
12028
12090
  ...before.startDate.getTime() !== activity.startDate.getTime() ? { plannedStart: new Date(activity.startDate) } : {},
12029
12091
  ...before.endDate.getTime() !== activity.endDate.getTime() ? { plannedFinish: new Date(activity.endDate) } : {},
12030
12092
  ...durationChanged ? { originalDurationHours: activity.durationHours } : {},
12031
- ...durationChanged || before.progress !== activity.progress ? remainingInputChanges(before, activity) : {}
12093
+ ...durationChanged || before.progress !== activity.progress ? executionInputChanges(before, activity, durationChanged) : {}
12032
12094
  };
12033
12095
  }
12034
- function remainingInputChanges(before, activity) {
12096
+ function executionInputChanges(before, activity, durationChanged) {
12035
12097
  const current = activity.p6;
12036
- if (current.actualStart !== null || current.remainingDurationHours !== before.p6.remainingDurationHours)
12037
- return {};
12038
- const remaining = draftRemainingDuration(activity);
12098
+ const hasExplicitRemainingCorrection = current.remainingDurationHours !== before.p6.remainingDurationHours;
12099
+ if (hasExplicitRemainingCorrection) return {};
12100
+ const progressChanged = before.progress !== activity.progress;
12101
+ const resetsToNotStarted = progressChanged && activity.progress === NOT_STARTED_PROGRESS;
12102
+ if (awaitsOutbuildExecution(current) || resetsToNotStarted) {
12103
+ const originalDurationHours = durationChanged ? activity.durationHours : current.originalDurationHours;
12104
+ return outbuildProgressExecution(activity, originalDurationHours);
12105
+ }
12106
+ const isStarted = current.actualStart !== null;
12107
+ if (!isStarted || !progressChanged) return {};
12108
+ return startedProgressChanges(current, activity);
12109
+ }
12110
+ function startedProgressChanges(current, activity) {
12111
+ const mirrorsPhysicalPercent = current.percentCompleteType === P6_PERCENT_COMPLETE_TYPE.PHYSICAL;
12112
+ if (!isCompletedProgress(activity.progress)) {
12113
+ return {
12114
+ ...current.actualFinish === null ? {} : {
12115
+ executionStatus: P6_EXECUTION_STATUS.IN_PROGRESS,
12116
+ actualFinish: null
12117
+ },
12118
+ ...mirrorsPhysicalPercent ? { physicalPercentComplete: activity.progress } : {}
12119
+ };
12120
+ }
12121
+ const finishesAfterActualStart = current.actualStart !== null && activity.endDate.getTime() >= current.actualStart.getTime();
12122
+ if (current.actualFinish !== null || !finishesAfterActualStart) return {};
12039
12123
  return {
12040
- remainingDurationHours: remaining,
12041
- ...remaining !== null ? { atCompletionDurationHours: remaining } : {}
12124
+ executionStatus: P6_EXECUTION_STATUS.COMPLETED,
12125
+ actualFinish: new Date(activity.endDate),
12126
+ remainingDurationHours: 0,
12127
+ physicalPercentComplete: COMPLETED_PROGRESS
12042
12128
  };
12043
12129
  }
12044
12130
 
@@ -18381,8 +18467,7 @@ function dispatchActivityCopy(action, deps) {
18381
18467
  if (!selection.ok) return selection;
18382
18468
  return { ...selection, changes: { ...selection.changes, source: action } };
18383
18469
  }
18384
- function dispatchActivityClipboardClear(action, clipboard) {
18385
- clipboard.value = null;
18470
+ function clipboardOnlyResult(action) {
18386
18471
  return {
18387
18472
  ok: true,
18388
18473
  changes: {
@@ -18394,6 +18479,57 @@ function dispatchActivityClipboardClear(action, clipboard) {
18394
18479
  }
18395
18480
  };
18396
18481
  }
18482
+ function dispatchActivityClipboardClear(action, clipboard) {
18483
+ clipboard.value = null;
18484
+ return clipboardOnlyResult(action);
18485
+ }
18486
+ function keepKnownIds(ids, known) {
18487
+ if (ids === void 0 || known === void 0) return ids;
18488
+ const knownSet = new Set(known);
18489
+ return ids.filter((id) => knownSet.has(id));
18490
+ }
18491
+ function localizeOverrides(overrides, deps, references) {
18492
+ const localized = cloneDomainValue(overrides);
18493
+ const hasUnknownCalendar = localized.calendarId != null && !deps.calendars.has(localized.calendarId);
18494
+ if (hasUnknownCalendar) delete localized.calendarId;
18495
+ const hasUnknownSubcontract = references?.subcontractIds !== void 0 && localized.subcontractId != null && !references.subcontractIds.includes(localized.subcontractId);
18496
+ if (hasUnknownSubcontract) localized.subcontractId = null;
18497
+ const responsableIds = keepKnownIds(
18498
+ localized.responsableIds,
18499
+ references?.responsableIds
18500
+ );
18501
+ if (responsableIds !== void 0)
18502
+ localized.responsableIds = [...responsableIds];
18503
+ const tagIds = keepKnownIds(localized.tagIds, references?.tagIds);
18504
+ if (tagIds !== void 0) localized.tagIds = [...tagIds];
18505
+ return localized;
18506
+ }
18507
+ function hasDanglingLink(snapshot) {
18508
+ const copiedIds = new Set(
18509
+ snapshot.activities.map((entry) => String(entry.originalId))
18510
+ );
18511
+ return snapshot.links.some(
18512
+ (link) => !copiedIds.has(String(link.source)) || !copiedIds.has(String(link.target))
18513
+ );
18514
+ }
18515
+ function dispatchActivityClipboardImport(action, deps) {
18516
+ const { snapshot, references } = action;
18517
+ if (snapshot.activities.length === 0) {
18518
+ return { ok: false, reason: REJECTION_REASON.CLIPBOARD_EMPTY };
18519
+ }
18520
+ if (hasDanglingLink(snapshot)) {
18521
+ return { ok: false, reason: REJECTION_REASON.CLIPBOARD_SNAPSHOT_INVALID };
18522
+ }
18523
+ deps.clipboard.value = {
18524
+ activities: snapshot.activities.map((entry) => ({
18525
+ originalId: entry.originalId,
18526
+ originalParentId: entry.originalParentId,
18527
+ overrides: localizeOverrides(entry.overrides, deps, references)
18528
+ })),
18529
+ links: snapshot.links.map((link) => cloneDomainValue(link))
18530
+ };
18531
+ return clipboardOnlyResult(action);
18532
+ }
18397
18533
 
18398
18534
  // src/activity-copy-paste/paste.ts
18399
18535
  function resolveDestination(afterSiblingId, adapter) {
@@ -18999,6 +19135,12 @@ async function performDispatch(action, options, deps) {
18999
19135
  if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR) {
19000
19136
  return dispatchActivityClipboardClear(action, deps.activityClipboard);
19001
19137
  }
19138
+ if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_IMPORT) {
19139
+ return dispatchActivityClipboardImport(action, {
19140
+ clipboard: deps.activityClipboard,
19141
+ calendars: deps.calendars
19142
+ });
19143
+ }
19002
19144
  if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED) {
19003
19145
  return dispatchActivityPasteCopied(action, options, deps);
19004
19146
  }
@@ -23415,7 +23557,7 @@ function getDispatchHistoryPolicy(action) {
23415
23557
  if (action.kind === DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE) {
23416
23558
  return DISPATCH_HISTORY_POLICY.CHECKPOINT_ON_SUCCESS;
23417
23559
  }
23418
- if (action.kind === DISPATCH_ACTION_KIND.SIR_SYNC || action.kind === DISPATCH_ACTION_KIND.CALENDAR_RECONCILE || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC || action.kind === DISPATCH_ACTION_KIND.BASELINE_APPLY || action.kind === DISPATCH_ACTION_KIND.PONDERATOR_CRITERION_SET || action.kind === DISPATCH_ACTION_KIND.STATUS_CRITERIA_SET || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_COPY || action.kind === DISPATCH_ACTION_KIND.SELECTION_TOGGLE || action.kind === DISPATCH_ACTION_KIND.SELECTION_REPLACE || action.kind === DISPATCH_ACTION_KIND.VISIBILITY_SET || action.kind === DISPATCH_ACTION_KIND.FILTER_SET || action.kind === DISPATCH_ACTION_KIND.SORT_SET || action.kind === DISPATCH_ACTION_KIND.VIEW_ATTRIBUTES_SET || action.kind === DISPATCH_ACTION_KIND.GROUP_SET || action.kind === DISPATCH_ACTION_KIND.WBS_DEPTH_SET) {
23560
+ if (action.kind === DISPATCH_ACTION_KIND.SIR_SYNC || action.kind === DISPATCH_ACTION_KIND.CALENDAR_RECONCILE || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC || action.kind === DISPATCH_ACTION_KIND.BASELINE_APPLY || action.kind === DISPATCH_ACTION_KIND.PONDERATOR_CRITERION_SET || action.kind === DISPATCH_ACTION_KIND.STATUS_CRITERIA_SET || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_IMPORT || action.kind === DISPATCH_ACTION_KIND.ACTIVITY_COPY || action.kind === DISPATCH_ACTION_KIND.SELECTION_TOGGLE || action.kind === DISPATCH_ACTION_KIND.SELECTION_REPLACE || action.kind === DISPATCH_ACTION_KIND.VISIBILITY_SET || action.kind === DISPATCH_ACTION_KIND.FILTER_SET || action.kind === DISPATCH_ACTION_KIND.SORT_SET || action.kind === DISPATCH_ACTION_KIND.VIEW_ATTRIBUTES_SET || action.kind === DISPATCH_ACTION_KIND.GROUP_SET || action.kind === DISPATCH_ACTION_KIND.WBS_DEPTH_SET) {
23419
23561
  return DISPATCH_HISTORY_POLICY.SKIP;
23420
23562
  }
23421
23563
  return DISPATCH_HISTORY_POLICY.RECORD;
@@ -26178,8 +26320,8 @@ var P6ActivityStatusError = class extends Error {
26178
26320
  }
26179
26321
  code;
26180
26322
  };
26181
- var COMPLETED_PHYSICAL_PERCENT = 100;
26182
- var NOT_STARTED_PHYSICAL_PERCENT = 0;
26323
+ var COMPLETED_PHYSICAL_PERCENT2 = 100;
26324
+ var NOT_STARTED_PHYSICAL_PERCENT2 = 0;
26183
26325
  function validateP6ActivityStatus(activity) {
26184
26326
  validateRemainingDuration(activity.remainingDurationHours);
26185
26327
  validateDates(activity);
@@ -26205,11 +26347,11 @@ function normalizeP6ActualDatePatch(current, patch) {
26205
26347
  executionStatus,
26206
26348
  ...actualFinish !== null ? {
26207
26349
  remainingDurationHours: 0,
26208
- physicalPercentComplete: COMPLETED_PHYSICAL_PERCENT
26350
+ physicalPercentComplete: COMPLETED_PHYSICAL_PERCENT2
26209
26351
  } : {},
26210
26352
  ...unStarting ? {
26211
26353
  remainingDurationHours: current.originalDurationHours,
26212
- physicalPercentComplete: NOT_STARTED_PHYSICAL_PERCENT
26354
+ physicalPercentComplete: NOT_STARTED_PHYSICAL_PERCENT2
26213
26355
  } : {}
26214
26356
  };
26215
26357
  validateP6ActivityDraftStatus({ ...current, ...normalized2 });
@@ -30242,7 +30384,7 @@ function initializeDataDate(input, activities) {
30242
30384
  function attachImportedDataDate(activities, imported, settings) {
30243
30385
  for (const activity of activities) {
30244
30386
  const importedP6 = Object.hasOwn(imported, activity.id) ? imported[activity.id] : void 0;
30245
- const p6 = importedP6 ?? createOutbuildP6ActivityDraft(activity);
30387
+ const p6 = importedP6 === void 0 ? createOutbuildP6ActivityDraft(activity) : reconcileOutbuildProgress(activity, importedP6);
30246
30388
  if (p6 === void 0) continue;
30247
30389
  activity.p6 = p6;
30248
30390
  if (!hasActiveDataDate(settings)) continue;
@@ -31171,6 +31313,18 @@ var ScheduleCore = class _ScheduleCore {
31171
31313
  this.assertReady();
31172
31314
  return readSelectedActivityIds(this.coreRuntime.state);
31173
31315
  }
31316
+ /**
31317
+ * Reads the core-owned activity clipboard as a portable snapshot.
31318
+ *
31319
+ * The snapshot can be imported into another `ScheduleCore` through the
31320
+ * `activity-clipboard-import` command, which is how a copy survives the
31321
+ * replacement of the schedule instance.
31322
+ * @returns A defensive copy, or `null` when nothing has been copied.
31323
+ */
31324
+ getActivityClipboardView() {
31325
+ this.assertReady();
31326
+ return cloneDomainValue(this._activityClipboard.value);
31327
+ }
31174
31328
  /** @returns A defensive snapshot of explicitly hidden activity IDs. */
31175
31329
  getHiddenActivityIds() {
31176
31330
  this.assertReady();