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