@outbuild-company/schedule-core 1.10.2 → 1.10.4

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
@@ -4284,15 +4284,28 @@ var setDuration = (value) => ({
4284
4284
  });
4285
4285
 
4286
4286
  // src/columns/start-date/start-date-pipeline.ts
4287
+ var SIR_APPLY_EVENT_SOURCE = "sir-apply";
4287
4288
  var startDatePipeline = {
4288
4289
  targetField: "startDate",
4289
- canEdit(activity, hierarchy) {
4290
+ canEdit(activity, hierarchy, edit) {
4290
4291
  if (isSummaryActivity2(activity, hierarchy)) {
4291
4292
  return cannotEdit("summary", "summary_column_value");
4292
4293
  }
4293
- if (hasProgress(activity)) {
4294
+ if (!hasProgress(activity)) return canEdit();
4295
+ if (edit?.eventSource !== SIR_APPLY_EVENT_SOURCE) {
4294
4296
  return cannotEdit("has_progress", "cant_edit_activity_with_progress");
4295
4297
  }
4298
+ if (isCompletedActivity(activity)) {
4299
+ return cannotEdit("completed", "cant_edit_completed_activity");
4300
+ }
4301
+ if (!isValidDate(edit.newValue)) return canEdit();
4302
+ if (isSameDay(activity.startDate, edit.newValue)) return canEdit();
4303
+ if (edit.newValue.getTime() > activity.startDate.getTime()) {
4304
+ return cannotEdit(
4305
+ "sir_start_after_progress",
4306
+ "sir_cant_move_start_after_progress"
4307
+ );
4308
+ }
4296
4309
  return canEdit();
4297
4310
  },
4298
4311
  parseInput(rawValue, _activity, _ctx) {
@@ -9703,13 +9716,41 @@ async function createGanttAdapter(state, hoursPerDay) {
9703
9716
 
9704
9717
  // src/critical-path/run-critical-path.ts
9705
9718
  var CP_WRITE_YIELD_EVERY = 1e3;
9706
- function fieldsForLink(rawResidual, sourceIsCritical, targetIsCritical, targetProgress) {
9707
- const freeSlackHours = typeof rawResidual === "number" && Number.isFinite(rawResidual) ? Object.is(rawResidual, -0) ? 0 : rawResidual : null;
9719
+ var finiteHoursOrNull = (value) => {
9720
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
9721
+ return Object.is(value, -0) ? 0 : value;
9722
+ };
9723
+ var linkFreeFloatDaysToHours = (valueDays, hoursPerDay) => {
9724
+ if (typeof valueDays !== "number" || valueDays < 0) return null;
9725
+ return slackDaysToHours(valueDays, hoursPerDay);
9726
+ };
9727
+ function fieldsForLink(input) {
9728
+ const freeSlackHours = linkFreeFloatDaysToHours(
9729
+ input.freeFloatDays,
9730
+ input.hoursPerDay
9731
+ );
9732
+ const residualHours = finiteHoursOrNull(input.residualHours);
9708
9733
  return {
9709
9734
  freeSlackHours,
9710
- isCritical: freeSlackHours !== null && freeSlackHours <= 0 && sourceIsCritical && targetIsCritical && targetProgress === 0
9735
+ isCritical: residualHours !== null && residualHours <= 0 && input.sourceIsCritical && input.targetIsCritical && input.targetProgress === 0
9711
9736
  };
9712
9737
  }
9738
+ async function collectLinkFreeFloatDays(freeFloat, isCurrent) {
9739
+ const values = /* @__PURE__ */ new Map();
9740
+ let sinceYield = 0;
9741
+ for (const calculation of freeFloat.values()) {
9742
+ for (const linkId in calculation.linksFreeFloat) {
9743
+ if (!Object.hasOwn(calculation.linksFreeFloat, linkId)) continue;
9744
+ if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
9745
+ sinceYield = 0;
9746
+ await yieldToBrowser();
9747
+ if (!isCurrent()) return null;
9748
+ }
9749
+ values.set(String(linkId), calculation.linksFreeFloat[linkId]);
9750
+ }
9751
+ }
9752
+ return isCurrent() ? values : null;
9753
+ }
9713
9754
  async function collectActivityFields(state, maps, hoursPerDay, toCanonicalActivityId, isCurrent) {
9714
9755
  const fields = /* @__PURE__ */ new Map();
9715
9756
  let sinceYield = 0;
@@ -9735,7 +9776,7 @@ async function collectActivityFields(state, maps, hoursPerDay, toCanonicalActivi
9735
9776
  }
9736
9777
  return isCurrent() ? fields : null;
9737
9778
  }
9738
- async function collectLinkFields(state, linkResidualHours, activityFields, isCurrent) {
9779
+ async function collectLinkFields(state, linkFreeFloatDays, linkResidualHours, activityFields, hoursPerDay, isCurrent) {
9739
9780
  const fields = /* @__PURE__ */ new Map();
9740
9781
  let sinceYield = 0;
9741
9782
  for (const link of state.getAllLinks()) {
@@ -9746,12 +9787,14 @@ async function collectLinkFields(state, linkResidualHours, activityFields, isCur
9746
9787
  }
9747
9788
  fields.set(
9748
9789
  String(link.id),
9749
- fieldsForLink(
9750
- linkResidualHours.get(String(link.id)),
9751
- activityFields.get(link.source)?.isCritical === true,
9752
- activityFields.get(link.target)?.isCritical === true,
9753
- state.getActivity(link.target)?.progress
9754
- )
9790
+ fieldsForLink({
9791
+ freeFloatDays: linkFreeFloatDays.get(String(link.id)),
9792
+ residualHours: linkResidualHours.get(String(link.id)),
9793
+ hoursPerDay,
9794
+ sourceIsCritical: activityFields.get(link.source)?.isCritical === true,
9795
+ targetIsCritical: activityFields.get(link.target)?.isCritical === true,
9796
+ targetProgress: state.getActivity(link.target)?.progress
9797
+ })
9755
9798
  );
9756
9799
  }
9757
9800
  return isCurrent() ? fields : null;
@@ -9834,10 +9877,17 @@ async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => t
9834
9877
  isCurrent
9835
9878
  );
9836
9879
  if (!activities) return null;
9880
+ const linkFreeFloatDays = await collectLinkFreeFloatDays(
9881
+ maps.freeFloat,
9882
+ isCurrent
9883
+ );
9884
+ if (!linkFreeFloatDays) return null;
9837
9885
  const links = await collectLinkFields(
9838
9886
  state,
9887
+ linkFreeFloatDays,
9839
9888
  criticalPath.linkResidualHours,
9840
9889
  activities,
9890
+ hoursPerDay,
9841
9891
  isCurrent
9842
9892
  );
9843
9893
  return links ? { activities, links } : null;
@@ -13825,7 +13875,7 @@ async function dispatchBulkEdit(action, options, deps) {
13825
13875
  const warnings = [];
13826
13876
  let needsAutoSchedule = false;
13827
13877
  for (const edit of action.edits) {
13828
- const outcome = applyOneEdit2(edit, deps);
13878
+ const outcome = applyOneEdit2(edit, deps, action.eventSource);
13829
13879
  verdicts.push(outcome.verdict);
13830
13880
  if (!outcome.verdict.ok || !outcome.changes) continue;
13831
13881
  const constraintWarning = toConstraintWarning(
@@ -13921,7 +13971,7 @@ async function dispatchBulkEdit(action, options, deps) {
13921
13971
  })
13922
13972
  };
13923
13973
  }
13924
- function applyOneEdit2(edit, deps) {
13974
+ function applyOneEdit2(edit, deps, eventSource) {
13925
13975
  const { ctx } = deps;
13926
13976
  const activityId = edit.activityId;
13927
13977
  const pipeline = findPipelineForColumn(edit.column);
@@ -13946,7 +13996,10 @@ function applyOneEdit2(edit, deps) {
13946
13996
  changes: null
13947
13997
  };
13948
13998
  }
13949
- const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
13999
+ const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy, {
14000
+ eventSource,
14001
+ newValue: edit.newValue
14002
+ });
13950
14003
  if (!editGate.allowed) {
13951
14004
  return {
13952
14005
  verdict: {
@@ -21309,11 +21362,7 @@ var ScheduleCore = class _ScheduleCore {
21309
21362
  );
21310
21363
  });
21311
21364
  this._criticalPathReady = this._initPromise.then(
21312
- () => this._enqueue(async () => {
21313
- const result = await this._runCriticalPathAndCapture();
21314
- this._criticalPathRevision = this._scheduleRevision;
21315
- return result;
21316
- })
21365
+ () => this._startCriticalPathForCurrentRevision()
21317
21366
  );
21318
21367
  const waitForCriticalPathOnLoad = input.criticalPathOnLoad === true;
21319
21368
  this._ready = waitForCriticalPathOnLoad ? this._criticalPathReady.then(() => void 0) : this._initPromise;
@@ -21951,39 +22000,6 @@ var ScheduleCore = class _ScheduleCore {
21951
22000
  void promise.then(clearIfActive, clearIfActive);
21952
22001
  return promise;
21953
22002
  }
21954
- async _runCriticalPathAndCapture() {
21955
- if (this._status === SCHEDULE_CORE_STATUS.DESTROYED) {
21956
- return {
21957
- changes: {
21958
- source: { kind: "init" },
21959
- activities: [],
21960
- links: [],
21961
- calendars: [],
21962
- trackingEvents: []
21963
- }
21964
- };
21965
- }
21966
- this.coreRuntime.state.beginWriteCapture();
21967
- this.coreRuntime.customIdTracker.beginCustomIdTransaction();
21968
- let changes;
21969
- try {
21970
- await applyCriticalPath(
21971
- this.coreRuntime.state,
21972
- this.coreRuntime.sector.hoursPerDay
21973
- );
21974
- changes = await assembleChangeSet(this.coreRuntime.state, {
21975
- source: { kind: "init" },
21976
- beforeSnap: /* @__PURE__ */ new Map(),
21977
- touchedIds: [],
21978
- scheduledIds: [],
21979
- hoursPerDay: this.coreRuntime.sector.hoursPerDay
21980
- });
21981
- } finally {
21982
- this.coreRuntime.state.endWriteCapture();
21983
- this.coreRuntime.customIdTracker.commitCustomIdTransaction();
21984
- }
21985
- return { changes };
21986
- }
21987
22003
  undo(options = {}) {
21988
22004
  const admitted = this._admitScheduleMutation();
21989
22005
  let changedState = false;