@outbuild-company/schedule-core 1.17.2 → 1.18.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
@@ -191,6 +191,11 @@ interface CoreActivity {
191
191
  freeSlackHours: number | null;
192
192
  /** Inclusive calendar-day span, or `null` when unavailable. */
193
193
  calendarDuration?: number | null;
194
+ /**
195
+ * Signed working days between the activity end and its visible baseline end,
196
+ * or `null` when no visible baseline applies. Positive means finishing late.
197
+ */
198
+ baselineVarianceDays: number | null;
194
199
  /** Expected progress percentage, or `null` when unavailable. */
195
200
  expectedProgress: number | null;
196
201
  /** Baseline expected progress percentage, or `null` when unavailable. */
@@ -347,6 +352,7 @@ declare const ACTIVITY_SAVE_FIELD_POLICY: {
347
352
  readonly isCritical: "ignored";
348
353
  readonly freeSlackHours: "ignored";
349
354
  readonly calendarDuration: "ignored";
355
+ readonly baselineVarianceDays: "ignored";
350
356
  readonly expectedProgress: "ignored";
351
357
  readonly expectedProgressBaseline: "ignored";
352
358
  readonly status: "ignored";
@@ -428,6 +434,7 @@ declare const COLUMN: {
428
434
  readonly DURATION_BASE: "duration_base";
429
435
  readonly COST_BASE: "cost_base";
430
436
  readonly WORK_BASE: "work_base";
437
+ readonly BASELINE_VARIANCE: "baseline_variance";
431
438
  readonly CALENDAR_DURATION: "calendarDuration";
432
439
  readonly IS_CRITICAL: "is_critical";
433
440
  readonly EARLY_START: "earlyStart";
@@ -555,6 +562,7 @@ declare const FILTER_FIELD: {
555
562
  readonly TOTAL_SLACK_DAYS: "totalSlackDays";
556
563
  readonly EXPECTED_PROGRESS_BASELINE: "expectedProgressBaseline";
557
564
  readonly BASELINE_DURATION_DAYS: "baselineDurationDays";
565
+ readonly BASELINE_VARIANCE_DAYS: "baselineVarianceDays";
558
566
  readonly BASELINE_COST: "baselineCost";
559
567
  readonly BASELINE_WORK_HOURS: "baselineWorkHours";
560
568
  readonly EXPECTED_PROGRESS: "expectedProgress";
@@ -600,7 +608,7 @@ type MembershipOperator = typeof FILTER_OPERATOR.SOME_OF | typeof FILTER_OPERATO
600
608
  /** Operator accepted by at least one public filter field. */
601
609
  type FilterOperator = StringOperator | NumberOperator | DateOperator | typeof FILTER_OPERATOR.BETWEEN | MembershipOperator;
602
610
  type StringFilterField = typeof FILTER_FIELD.NAME | typeof FILTER_FIELD.DESCRIPTION | typeof FILTER_FIELD.CUSTOM_ID;
603
- type NumberFilterField = typeof FILTER_FIELD.CORRELATIVE_ID | typeof FILTER_FIELD.UNIQUE_CORRELATIVE_ID | typeof FILTER_FIELD.PROGRESS | typeof FILTER_FIELD.DURATION_DAYS | typeof FILTER_FIELD.CALENDAR_DURATION | typeof FILTER_FIELD.COST | typeof FILTER_FIELD.USED_COST | typeof FILTER_FIELD.REAL_COST | typeof FILTER_FIELD.WORK_HOURS | typeof FILTER_FIELD.REAL_WORK_HOURS | typeof FILTER_FIELD.PONDERATOR | typeof FILTER_FIELD.FREE_SLACK_DAYS | typeof FILTER_FIELD.TOTAL_SLACK_DAYS | typeof FILTER_FIELD.EXPECTED_PROGRESS_BASELINE | typeof FILTER_FIELD.BASELINE_DURATION_DAYS | typeof FILTER_FIELD.BASELINE_COST | typeof FILTER_FIELD.BASELINE_WORK_HOURS | typeof FILTER_FIELD.EXPECTED_PROGRESS;
611
+ type NumberFilterField = typeof FILTER_FIELD.CORRELATIVE_ID | typeof FILTER_FIELD.UNIQUE_CORRELATIVE_ID | typeof FILTER_FIELD.PROGRESS | typeof FILTER_FIELD.DURATION_DAYS | typeof FILTER_FIELD.CALENDAR_DURATION | typeof FILTER_FIELD.COST | typeof FILTER_FIELD.USED_COST | typeof FILTER_FIELD.REAL_COST | typeof FILTER_FIELD.WORK_HOURS | typeof FILTER_FIELD.REAL_WORK_HOURS | typeof FILTER_FIELD.PONDERATOR | typeof FILTER_FIELD.FREE_SLACK_DAYS | typeof FILTER_FIELD.TOTAL_SLACK_DAYS | typeof FILTER_FIELD.EXPECTED_PROGRESS_BASELINE | typeof FILTER_FIELD.BASELINE_DURATION_DAYS | typeof FILTER_FIELD.BASELINE_VARIANCE_DAYS | typeof FILTER_FIELD.BASELINE_COST | typeof FILTER_FIELD.BASELINE_WORK_HOURS | typeof FILTER_FIELD.EXPECTED_PROGRESS;
604
612
  type DateFilterField = typeof FILTER_FIELD.START_DATE | typeof FILTER_FIELD.END_DATE | typeof FILTER_FIELD.CONSTRAINT_DATE | typeof FILTER_FIELD.BASELINE_START_DATE | typeof FILTER_FIELD.BASELINE_END_DATE | typeof FILTER_FIELD.EARLY_START | typeof FILTER_FIELD.EARLY_FINISH | typeof FILTER_FIELD.LATE_START | typeof FILTER_FIELD.LATE_FINISH;
605
613
  type IdArrayFilterField = typeof FILTER_FIELD.RESPONSABLE_IDS | typeof FILTER_FIELD.TAG_IDS;
606
614
  type EnumFilterField = typeof FILTER_FIELD.STATUS | typeof FILTER_FIELD.CONSTRAINT_TYPE;
package/dist/index.d.ts CHANGED
@@ -191,6 +191,11 @@ interface CoreActivity {
191
191
  freeSlackHours: number | null;
192
192
  /** Inclusive calendar-day span, or `null` when unavailable. */
193
193
  calendarDuration?: number | null;
194
+ /**
195
+ * Signed working days between the activity end and its visible baseline end,
196
+ * or `null` when no visible baseline applies. Positive means finishing late.
197
+ */
198
+ baselineVarianceDays: number | null;
194
199
  /** Expected progress percentage, or `null` when unavailable. */
195
200
  expectedProgress: number | null;
196
201
  /** Baseline expected progress percentage, or `null` when unavailable. */
@@ -347,6 +352,7 @@ declare const ACTIVITY_SAVE_FIELD_POLICY: {
347
352
  readonly isCritical: "ignored";
348
353
  readonly freeSlackHours: "ignored";
349
354
  readonly calendarDuration: "ignored";
355
+ readonly baselineVarianceDays: "ignored";
350
356
  readonly expectedProgress: "ignored";
351
357
  readonly expectedProgressBaseline: "ignored";
352
358
  readonly status: "ignored";
@@ -428,6 +434,7 @@ declare const COLUMN: {
428
434
  readonly DURATION_BASE: "duration_base";
429
435
  readonly COST_BASE: "cost_base";
430
436
  readonly WORK_BASE: "work_base";
437
+ readonly BASELINE_VARIANCE: "baseline_variance";
431
438
  readonly CALENDAR_DURATION: "calendarDuration";
432
439
  readonly IS_CRITICAL: "is_critical";
433
440
  readonly EARLY_START: "earlyStart";
@@ -555,6 +562,7 @@ declare const FILTER_FIELD: {
555
562
  readonly TOTAL_SLACK_DAYS: "totalSlackDays";
556
563
  readonly EXPECTED_PROGRESS_BASELINE: "expectedProgressBaseline";
557
564
  readonly BASELINE_DURATION_DAYS: "baselineDurationDays";
565
+ readonly BASELINE_VARIANCE_DAYS: "baselineVarianceDays";
558
566
  readonly BASELINE_COST: "baselineCost";
559
567
  readonly BASELINE_WORK_HOURS: "baselineWorkHours";
560
568
  readonly EXPECTED_PROGRESS: "expectedProgress";
@@ -600,7 +608,7 @@ type MembershipOperator = typeof FILTER_OPERATOR.SOME_OF | typeof FILTER_OPERATO
600
608
  /** Operator accepted by at least one public filter field. */
601
609
  type FilterOperator = StringOperator | NumberOperator | DateOperator | typeof FILTER_OPERATOR.BETWEEN | MembershipOperator;
602
610
  type StringFilterField = typeof FILTER_FIELD.NAME | typeof FILTER_FIELD.DESCRIPTION | typeof FILTER_FIELD.CUSTOM_ID;
603
- type NumberFilterField = typeof FILTER_FIELD.CORRELATIVE_ID | typeof FILTER_FIELD.UNIQUE_CORRELATIVE_ID | typeof FILTER_FIELD.PROGRESS | typeof FILTER_FIELD.DURATION_DAYS | typeof FILTER_FIELD.CALENDAR_DURATION | typeof FILTER_FIELD.COST | typeof FILTER_FIELD.USED_COST | typeof FILTER_FIELD.REAL_COST | typeof FILTER_FIELD.WORK_HOURS | typeof FILTER_FIELD.REAL_WORK_HOURS | typeof FILTER_FIELD.PONDERATOR | typeof FILTER_FIELD.FREE_SLACK_DAYS | typeof FILTER_FIELD.TOTAL_SLACK_DAYS | typeof FILTER_FIELD.EXPECTED_PROGRESS_BASELINE | typeof FILTER_FIELD.BASELINE_DURATION_DAYS | typeof FILTER_FIELD.BASELINE_COST | typeof FILTER_FIELD.BASELINE_WORK_HOURS | typeof FILTER_FIELD.EXPECTED_PROGRESS;
611
+ type NumberFilterField = typeof FILTER_FIELD.CORRELATIVE_ID | typeof FILTER_FIELD.UNIQUE_CORRELATIVE_ID | typeof FILTER_FIELD.PROGRESS | typeof FILTER_FIELD.DURATION_DAYS | typeof FILTER_FIELD.CALENDAR_DURATION | typeof FILTER_FIELD.COST | typeof FILTER_FIELD.USED_COST | typeof FILTER_FIELD.REAL_COST | typeof FILTER_FIELD.WORK_HOURS | typeof FILTER_FIELD.REAL_WORK_HOURS | typeof FILTER_FIELD.PONDERATOR | typeof FILTER_FIELD.FREE_SLACK_DAYS | typeof FILTER_FIELD.TOTAL_SLACK_DAYS | typeof FILTER_FIELD.EXPECTED_PROGRESS_BASELINE | typeof FILTER_FIELD.BASELINE_DURATION_DAYS | typeof FILTER_FIELD.BASELINE_VARIANCE_DAYS | typeof FILTER_FIELD.BASELINE_COST | typeof FILTER_FIELD.BASELINE_WORK_HOURS | typeof FILTER_FIELD.EXPECTED_PROGRESS;
604
612
  type DateFilterField = typeof FILTER_FIELD.START_DATE | typeof FILTER_FIELD.END_DATE | typeof FILTER_FIELD.CONSTRAINT_DATE | typeof FILTER_FIELD.BASELINE_START_DATE | typeof FILTER_FIELD.BASELINE_END_DATE | typeof FILTER_FIELD.EARLY_START | typeof FILTER_FIELD.EARLY_FINISH | typeof FILTER_FIELD.LATE_START | typeof FILTER_FIELD.LATE_FINISH;
605
613
  type IdArrayFilterField = typeof FILTER_FIELD.RESPONSABLE_IDS | typeof FILTER_FIELD.TAG_IDS;
606
614
  type EnumFilterField = typeof FILTER_FIELD.STATUS | typeof FILTER_FIELD.CONSTRAINT_TYPE;
package/dist/index.js CHANGED
@@ -289,6 +289,7 @@ var READ_ONLY = {
289
289
  DURATION_BASE: "duration_base",
290
290
  COST_BASE: "cost_base",
291
291
  WORK_BASE: "work_base",
292
+ BASELINE_VARIANCE: "baseline_variance",
292
293
  CALENDAR_DURATION: "calendarDuration",
293
294
  IS_CRITICAL: "is_critical",
294
295
  EARLY_START: "earlyStart",
@@ -1042,6 +1043,7 @@ var FILTER_FIELD = {
1042
1043
  TOTAL_SLACK_DAYS: "totalSlackDays",
1043
1044
  EXPECTED_PROGRESS_BASELINE: "expectedProgressBaseline",
1044
1045
  BASELINE_DURATION_DAYS: "baselineDurationDays",
1046
+ BASELINE_VARIANCE_DAYS: "baselineVarianceDays",
1045
1047
  BASELINE_COST: "baselineCost",
1046
1048
  BASELINE_WORK_HOURS: "baselineWorkHours",
1047
1049
  EXPECTED_PROGRESS: "expectedProgress",
@@ -1209,6 +1211,10 @@ var FIELD_REGISTRY = {
1209
1211
  valueKind: FILTER_VALUE_KIND.NUMBER,
1210
1212
  extract: (activity) => activity.baselineSnapshot?.durationDays ?? null
1211
1213
  },
1214
+ [FILTER_FIELD.BASELINE_VARIANCE_DAYS]: {
1215
+ valueKind: FILTER_VALUE_KIND.NUMBER,
1216
+ extract: (activity) => activity.baselineVarianceDays
1217
+ },
1212
1218
  [FILTER_FIELD.BASELINE_COST]: {
1213
1219
  valueKind: FILTER_VALUE_KIND.NUMBER,
1214
1220
  extract: (activity) => activity.baselineSnapshot?.cost ?? null
@@ -2354,7 +2360,8 @@ var ACTIVITY_PROPERTY = {
2354
2360
  CUSTOM_ID: "customId",
2355
2361
  NEW_ACTIVITIES_ARRAY: "newActivityIds",
2356
2362
  HAS_NEW_ACTIVITIES: "hasNewActivities",
2357
- CALENDAR_DURATION: "calendarDuration"};
2363
+ CALENDAR_DURATION: "calendarDuration",
2364
+ BASELINE_VARIANCE_DAYS: "baselineVarianceDays"};
2358
2365
  var LINK_PROPERTY = {
2359
2366
  SOURCE: "source",
2360
2367
  TARGET: "target",
@@ -6286,12 +6293,16 @@ function mergeCorrelativeShiftIntoDiff(diff, correlativeBefore, activityId, live
6286
6293
  after: liveActivity.correlativeId
6287
6294
  };
6288
6295
  }
6296
+ var DERIVED_DISPLAY_FIELDS = [
6297
+ ACTIVITY_PROPERTY.CALENDAR_DURATION,
6298
+ ACTIVITY_PROPERTY.BASELINE_VARIANCE_DAYS
6299
+ ];
6289
6300
  function diffActivity(before, after) {
6290
6301
  const fields = {};
6291
6302
  const beforeRec = before ?? {};
6292
6303
  const afterRec = after;
6293
6304
  const keys = /* @__PURE__ */ new Set([...Object.keys(beforeRec), ...Object.keys(afterRec)]);
6294
- keys.delete(ACTIVITY_PROPERTY.CALENDAR_DURATION);
6305
+ for (const derived of DERIVED_DISPLAY_FIELDS) keys.delete(derived);
6295
6306
  for (const k of keys) {
6296
6307
  if (Object.hasOwn(beforeRec, k) !== Object.hasOwn(afterRec, k) || !fieldValueEqual(beforeRec[k], afterRec[k])) {
6297
6308
  fields[k] = {
@@ -6541,6 +6552,43 @@ function applyStatusPass(state, criteria) {
6541
6552
  return changed;
6542
6553
  }
6543
6554
 
6555
+ // src/propagations/shared/get-visible-baseline.ts
6556
+ function getVisibleBaseline(activity) {
6557
+ const points = activity?.baselinePoints;
6558
+ if (!Array.isArray(points) || points.length === 0) return null;
6559
+ return points.find((point) => point.isVisibleVersion) ?? null;
6560
+ }
6561
+
6562
+ // src/columns/baseline-variance/compute.ts
6563
+ var VARIANCE_DECIMAL_SCALE = 100;
6564
+ var roundToVariancePrecision = (days) => Math.round(days * VARIANCE_DECIMAL_SCALE) / VARIANCE_DECIMAL_SCALE;
6565
+ function computeBaselineVarianceDays(activity, calendars, hoursPerDay) {
6566
+ if (hoursPerDay <= 0) return null;
6567
+ const baselineEnd = getVisibleBaseline(activity)?.endDate;
6568
+ const activityEnd = activity.endDate;
6569
+ if (!baselineEnd || !activityEnd) return null;
6570
+ const finishesLate = activityEnd.getTime() > baselineEnd.getTime();
6571
+ const finishesEarly = baselineEnd.getTime() > activityEnd.getTime();
6572
+ if (!finishesLate && !finishesEarly) return 0;
6573
+ const durationHours = calendars.calculateDuration({
6574
+ startDate: finishesLate ? baselineEnd : activityEnd,
6575
+ endDate: finishesLate ? activityEnd : baselineEnd,
6576
+ task: activity
6577
+ });
6578
+ const magnitude = roundToVariancePrecision(durationHours / hoursPerDay);
6579
+ return finishesLate ? magnitude : -magnitude;
6580
+ }
6581
+ function applyBaselineVariancePass(state, hoursPerDay) {
6582
+ const changed = [];
6583
+ state.forEachActivity((activity, id) => {
6584
+ const variance = computeBaselineVarianceDays(activity, state, hoursPerDay);
6585
+ if (activity.baselineVarianceDays === variance) return;
6586
+ activity.baselineVarianceDays = variance;
6587
+ changed.push(String(id));
6588
+ });
6589
+ return changed;
6590
+ }
6591
+
6544
6592
  // src/critical-path/legacy/shims/moment.js
6545
6593
  function moment(input) {
6546
6594
  const d = input instanceof Date ? new Date(input.getTime()) : input && typeof input === "object" && typeof input.valueOf === "function" ? new Date(input.valueOf()) : new Date(input);
@@ -11265,6 +11313,7 @@ var DERIVED_PASS = {
11265
11313
  REAL_WORK: "real-work",
11266
11314
  EXPECTED_PROGRESS: "expected-progress",
11267
11315
  REAL_COST: "real-cost",
11316
+ BASELINE_VARIANCE: "baseline-variance",
11268
11317
  CRITICAL_PATH: "critical-path"
11269
11318
  };
11270
11319
  function resolveDerivedPasses(action, autoSchedule, options) {
@@ -11282,6 +11331,9 @@ function resolveDerivedPasses(action, autoSchedule, options) {
11282
11331
  if (runsLoadRecomputes && resolveRunRealCost(action)) {
11283
11332
  passes.add(DERIVED_PASS.REAL_COST);
11284
11333
  }
11334
+ if (dispatchChangesSchedulingState(action)) {
11335
+ passes.add(DERIVED_PASS.BASELINE_VARIANCE);
11336
+ }
11285
11337
  const runsCriticalPath = runsLoadRecomputes && resolveRunCriticalPath(action) && !options.skipCriticalPath;
11286
11338
  if (runsCriticalPath) passes.add(DERIVED_PASS.CRITICAL_PATH);
11287
11339
  return passes;
@@ -11944,6 +11996,9 @@ async function runDerivedRecomputes(deps, passes, now) {
11944
11996
  if (passes.has(DERIVED_PASS.REAL_COST)) {
11945
11997
  emitRealCost(deps.adapter);
11946
11998
  }
11999
+ if (passes.has(DERIVED_PASS.BASELINE_VARIANCE)) {
12000
+ applyBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
12001
+ }
11947
12002
  if (passes.has(DERIVED_PASS.CRITICAL_PATH)) {
11948
12003
  await applyCriticalPath(deps.adapter, deps.sector.hoursPerDay);
11949
12004
  }
@@ -12891,6 +12946,7 @@ var NEW_ACTIVITY_DEFAULTS = Object.freeze({
12891
12946
  tagIds: [],
12892
12947
  baselinePoints: [],
12893
12948
  baselineSnapshot: null,
12949
+ baselineVarianceDays: null,
12894
12950
  recursiveDurationHours: 0,
12895
12951
  isCritical: false,
12896
12952
  freeSlackHours: null,
@@ -16622,6 +16678,7 @@ async function dispatchBaselineApply(action, deps) {
16622
16678
  deps.reporter,
16623
16679
  deps.defaultBaseCalendarId ?? null
16624
16680
  );
16681
+ applyBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
16625
16682
  if (activeBaselineSignature(deps) !== activeBaselineBefore) {
16626
16683
  recomputeBaselineWeightedFields(
16627
16684
  deps,
@@ -21709,6 +21766,7 @@ var ACTIVITY_SAVE_FIELD_POLICY = {
21709
21766
  isCritical: SAVE_FIELD_POLICY.IGNORED,
21710
21767
  freeSlackHours: SAVE_FIELD_POLICY.IGNORED,
21711
21768
  calendarDuration: SAVE_FIELD_POLICY.IGNORED,
21769
+ baselineVarianceDays: SAVE_FIELD_POLICY.IGNORED,
21712
21770
  expectedProgress: SAVE_FIELD_POLICY.IGNORED,
21713
21771
  expectedProgressBaseline: SAVE_FIELD_POLICY.IGNORED,
21714
21772
  status: SAVE_FIELD_POLICY.IGNORED,
@@ -22335,6 +22393,7 @@ function parseActivity(raw, context) {
22335
22393
  isMilestone
22336
22394
  ),
22337
22395
  baselineSnapshot: null,
22396
+ baselineVarianceDays: null,
22338
22397
  recursiveDurationHours: durationToHours(
22339
22398
  Number(raw.sumOfDurationRecursively ?? 0),
22340
22399
  context.hoursPerDay
@@ -23557,6 +23616,7 @@ async function runInitialPasses(deps) {
23557
23616
  applyExpectedProgressLive(state, now);
23558
23617
  applyStatusPass(state, sector.statusCriteria);
23559
23618
  }
23619
+ applyBaselineVariancePass(state, sector.hoursPerDay);
23560
23620
  return;
23561
23621
  }
23562
23622
  const result = await scheduler.schedule({});
@@ -23569,6 +23629,7 @@ async function runInitialPasses(deps) {
23569
23629
  applyExpectedProgressLive(state, now);
23570
23630
  applyStatusPass(state, sector.statusCriteria);
23571
23631
  }
23632
+ applyBaselineVariancePass(state, sector.hoursPerDay);
23572
23633
  }
23573
23634
  function recomputeSkippedLeafEnds(state) {
23574
23635
  state.forEachActivity((activity) => {