@outbuild-company/schedule-core 1.10.3 → 1.11.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.d.cts CHANGED
@@ -382,6 +382,8 @@ interface ActivityBatchAction {
382
382
  type DispatchAction = {
383
383
  kind: 'activity-copy';
384
384
  eventSource?: string;
385
+ } | {
386
+ kind: 'calendar-reconcile';
385
387
  } | {
386
388
  kind: 'activity-clipboard-clear';
387
389
  eventSource?: string;
@@ -885,7 +887,6 @@ declare class ScheduleCore {
885
887
  private _reapplyActiveFilter;
886
888
  private _recordScheduleMutation;
887
889
  private _startCriticalPathForCurrentRevision;
888
- private _runCriticalPathAndCapture;
889
890
  undo(options?: UndoOptions): Promise<ChangeSet | null>;
890
891
  private _runUndo;
891
892
  redo(options?: {
@@ -982,6 +983,7 @@ type DispatchActionKind = DispatchAction['kind'];
982
983
  declare const DISPATCH_ACTION_KIND: {
983
984
  readonly ACTIVITY_CLIPBOARD_CLEAR: "activity-clipboard-clear";
984
985
  readonly ACTIVITY_COPY: "activity-copy";
986
+ readonly CALENDAR_RECONCILE: "calendar-reconcile";
985
987
  readonly ACTIVITY_PASTE_COPIED: "activity-paste-copied";
986
988
  readonly PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge";
987
989
  readonly BASELINE_APPLY: "baseline-apply";
package/dist/index.d.ts CHANGED
@@ -382,6 +382,8 @@ interface ActivityBatchAction {
382
382
  type DispatchAction = {
383
383
  kind: 'activity-copy';
384
384
  eventSource?: string;
385
+ } | {
386
+ kind: 'calendar-reconcile';
385
387
  } | {
386
388
  kind: 'activity-clipboard-clear';
387
389
  eventSource?: string;
@@ -885,7 +887,6 @@ declare class ScheduleCore {
885
887
  private _reapplyActiveFilter;
886
888
  private _recordScheduleMutation;
887
889
  private _startCriticalPathForCurrentRevision;
888
- private _runCriticalPathAndCapture;
889
890
  undo(options?: UndoOptions): Promise<ChangeSet | null>;
890
891
  private _runUndo;
891
892
  redo(options?: {
@@ -982,6 +983,7 @@ type DispatchActionKind = DispatchAction['kind'];
982
983
  declare const DISPATCH_ACTION_KIND: {
983
984
  readonly ACTIVITY_CLIPBOARD_CLEAR: "activity-clipboard-clear";
984
985
  readonly ACTIVITY_COPY: "activity-copy";
986
+ readonly CALENDAR_RECONCILE: "calendar-reconcile";
985
987
  readonly ACTIVITY_PASTE_COPIED: "activity-paste-copied";
986
988
  readonly PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge";
987
989
  readonly BASELINE_APPLY: "baseline-apply";
package/dist/index.js CHANGED
@@ -4282,15 +4282,28 @@ var setDuration = (value) => ({
4282
4282
  });
4283
4283
 
4284
4284
  // src/columns/start-date/start-date-pipeline.ts
4285
+ var SIR_APPLY_EVENT_SOURCE = "sir-apply";
4285
4286
  var startDatePipeline = {
4286
4287
  targetField: "startDate",
4287
- canEdit(activity, hierarchy) {
4288
+ canEdit(activity, hierarchy, edit) {
4288
4289
  if (isSummaryActivity2(activity, hierarchy)) {
4289
4290
  return cannotEdit("summary", "summary_column_value");
4290
4291
  }
4291
- if (hasProgress(activity)) {
4292
+ if (!hasProgress(activity)) return canEdit();
4293
+ if (edit?.eventSource !== SIR_APPLY_EVENT_SOURCE) {
4292
4294
  return cannotEdit("has_progress", "cant_edit_activity_with_progress");
4293
4295
  }
4296
+ if (isCompletedActivity(activity)) {
4297
+ return cannotEdit("completed", "cant_edit_completed_activity");
4298
+ }
4299
+ if (!isValidDate(edit.newValue)) return canEdit();
4300
+ if (isSameDay(activity.startDate, edit.newValue)) return canEdit();
4301
+ if (edit.newValue.getTime() > activity.startDate.getTime()) {
4302
+ return cannotEdit(
4303
+ "sir_start_after_progress",
4304
+ "sir_cant_move_start_after_progress"
4305
+ );
4306
+ }
4294
4307
  return canEdit();
4295
4308
  },
4296
4309
  parseInput(rawValue, _activity, _ctx) {
@@ -13860,7 +13873,7 @@ async function dispatchBulkEdit(action, options, deps) {
13860
13873
  const warnings = [];
13861
13874
  let needsAutoSchedule = false;
13862
13875
  for (const edit of action.edits) {
13863
- const outcome = applyOneEdit2(edit, deps);
13876
+ const outcome = applyOneEdit2(edit, deps, action.eventSource);
13864
13877
  verdicts.push(outcome.verdict);
13865
13878
  if (!outcome.verdict.ok || !outcome.changes) continue;
13866
13879
  const constraintWarning = toConstraintWarning(
@@ -13956,7 +13969,7 @@ async function dispatchBulkEdit(action, options, deps) {
13956
13969
  })
13957
13970
  };
13958
13971
  }
13959
- function applyOneEdit2(edit, deps) {
13972
+ function applyOneEdit2(edit, deps, eventSource) {
13960
13973
  const { ctx } = deps;
13961
13974
  const activityId = edit.activityId;
13962
13975
  const pipeline = findPipelineForColumn(edit.column);
@@ -13981,7 +13994,10 @@ function applyOneEdit2(edit, deps) {
13981
13994
  changes: null
13982
13995
  };
13983
13996
  }
13984
- const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
13997
+ const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy, {
13998
+ eventSource,
13999
+ newValue: edit.newValue
14000
+ });
13985
14001
  if (!editGate.allowed) {
13986
14002
  return {
13987
14003
  verdict: {
@@ -14388,6 +14404,64 @@ async function dispatchStatusCriteriaSet(action, deps) {
14388
14404
  };
14389
14405
  }
14390
14406
 
14407
+ // src/dispatch/handlers/calendar-reconcile.ts
14408
+ async function dispatchCalendarReconcile(action, options, deps) {
14409
+ const beforeSnap = /* @__PURE__ */ new Map();
14410
+ const touchedIds = /* @__PURE__ */ new Set();
14411
+ for (const activity of deps.adapter.getAllActivities()) {
14412
+ if (!shouldReconcile(activity, deps)) continue;
14413
+ const calendar = deps.ctx.calendars.getCalendar(activity.calendarId);
14414
+ if (!calendar) continue;
14415
+ const startDate = calendar.getClosestWorkTime({
14416
+ date: activity.startDate,
14417
+ direction: WORK_TIME_DIRECTION.FUTURE,
14418
+ unit: CALENDAR_UNIT.HOUR
14419
+ });
14420
+ const endDate = calendar.calculateEndDate(
14421
+ startDate,
14422
+ activity.durationHours
14423
+ );
14424
+ if (sameInstant(startDate, activity.startDate) && sameInstant(endDate, activity.endDate)) {
14425
+ continue;
14426
+ }
14427
+ const activityId = String(activity.id);
14428
+ beforeSnap.set(activityId, structuredCloneActivity(activity));
14429
+ touchedIds.add(activityId);
14430
+ deps.adapter.setActivityFields(activityId, { startDate, endDate });
14431
+ }
14432
+ const { scheduledIds } = await runPostMutation(
14433
+ {
14434
+ adapter: deps.adapter,
14435
+ scheduler: deps.scheduler,
14436
+ sector: deps.sector,
14437
+ defaultBaseCalendarId: deps.defaultBaseCalendarId
14438
+ },
14439
+ {
14440
+ action,
14441
+ autoscheduleFrom: "roots",
14442
+ recomputeParentsFrom: touchedIds,
14443
+ now: deps.now,
14444
+ options
14445
+ }
14446
+ );
14447
+ return {
14448
+ ok: true,
14449
+ changes: await assembleChangeSet(deps.adapter, {
14450
+ source: action,
14451
+ beforeSnap,
14452
+ touchedIds,
14453
+ scheduledIds,
14454
+ hoursPerDay: deps.sector.hoursPerDay
14455
+ })
14456
+ };
14457
+ }
14458
+ function shouldReconcile(activity, deps) {
14459
+ return activity.progress === 0 && !isSummary(activity.type, deps.adapter.hasChildren(activity.id));
14460
+ }
14461
+ function sameInstant(first, second) {
14462
+ return first.getTime() === second.getTime();
14463
+ }
14464
+
14391
14465
  // src/activity-copy-paste/policy.ts
14392
14466
  var ACTIVITY_COPY_FIELDS = [
14393
14467
  "name",
@@ -14597,6 +14671,9 @@ async function performDispatch(action, options, deps) {
14597
14671
  clipboard: deps.activityClipboard
14598
14672
  });
14599
14673
  }
14674
+ if (action.kind === "calendar-reconcile") {
14675
+ return dispatchCalendarReconcile(action, options, deps);
14676
+ }
14600
14677
  if (action.kind === "activity-clipboard-clear") {
14601
14678
  return dispatchActivityClipboardClear(action, deps.activityClipboard);
14602
14679
  }
@@ -18555,7 +18632,7 @@ function getDispatchHistoryPolicy(action) {
18555
18632
  if (action.kind === "persistence-acknowledge") {
18556
18633
  return "checkpoint-on-success";
18557
18634
  }
18558
- if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "activity-clipboard-clear" || action.kind === "activity-copy" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
18635
+ if (action.kind === "sir-sync" || action.kind === "calendar-reconcile" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "activity-clipboard-clear" || action.kind === "activity-copy" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
18559
18636
  return "skip";
18560
18637
  }
18561
18638
  return "record";
@@ -21344,11 +21421,7 @@ var ScheduleCore = class _ScheduleCore {
21344
21421
  );
21345
21422
  });
21346
21423
  this._criticalPathReady = this._initPromise.then(
21347
- () => this._enqueue(async () => {
21348
- const result = await this._runCriticalPathAndCapture();
21349
- this._criticalPathRevision = this._scheduleRevision;
21350
- return result;
21351
- })
21424
+ () => this._startCriticalPathForCurrentRevision()
21352
21425
  );
21353
21426
  const waitForCriticalPathOnLoad = input.criticalPathOnLoad === true;
21354
21427
  this._ready = waitForCriticalPathOnLoad ? this._criticalPathReady.then(() => void 0) : this._initPromise;
@@ -21986,39 +22059,6 @@ var ScheduleCore = class _ScheduleCore {
21986
22059
  void promise.then(clearIfActive, clearIfActive);
21987
22060
  return promise;
21988
22061
  }
21989
- async _runCriticalPathAndCapture() {
21990
- if (this._status === SCHEDULE_CORE_STATUS.DESTROYED) {
21991
- return {
21992
- changes: {
21993
- source: { kind: "init" },
21994
- activities: [],
21995
- links: [],
21996
- calendars: [],
21997
- trackingEvents: []
21998
- }
21999
- };
22000
- }
22001
- this.coreRuntime.state.beginWriteCapture();
22002
- this.coreRuntime.customIdTracker.beginCustomIdTransaction();
22003
- let changes;
22004
- try {
22005
- await applyCriticalPath(
22006
- this.coreRuntime.state,
22007
- this.coreRuntime.sector.hoursPerDay
22008
- );
22009
- changes = await assembleChangeSet(this.coreRuntime.state, {
22010
- source: { kind: "init" },
22011
- beforeSnap: /* @__PURE__ */ new Map(),
22012
- touchedIds: [],
22013
- scheduledIds: [],
22014
- hoursPerDay: this.coreRuntime.sector.hoursPerDay
22015
- });
22016
- } finally {
22017
- this.coreRuntime.state.endWriteCapture();
22018
- this.coreRuntime.customIdTracker.commitCustomIdTransaction();
22019
- }
22020
- return { changes };
22021
- }
22022
22062
  undo(options = {}) {
22023
22063
  const admitted = this._admitScheduleMutation();
22024
22064
  let changedState = false;
@@ -22216,6 +22256,7 @@ var DEFAULT_HOURS_PER_DAY = 8;
22216
22256
  var DISPATCH_ACTION_KIND = {
22217
22257
  ACTIVITY_CLIPBOARD_CLEAR: "activity-clipboard-clear",
22218
22258
  ACTIVITY_COPY: "activity-copy",
22259
+ CALENDAR_RECONCILE: "calendar-reconcile",
22219
22260
  ACTIVITY_PASTE_COPIED: "activity-paste-copied",
22220
22261
  PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge",
22221
22262
  BASELINE_APPLY: "baseline-apply",
@@ -22245,6 +22286,7 @@ var DISPATCH_ACTION_KIND = {
22245
22286
  var KIND_CATALOG_COVERS_UNION = {
22246
22287
  [DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR]: true,
22247
22288
  [DISPATCH_ACTION_KIND.ACTIVITY_COPY]: true,
22289
+ [DISPATCH_ACTION_KIND.CALENDAR_RECONCILE]: true,
22248
22290
  [DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED]: true,
22249
22291
  [DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE]: true,
22250
22292
  [DISPATCH_ACTION_KIND.BASELINE_APPLY]: true,