@outbuild-company/schedule-core 1.26.0 → 1.26.1
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/README.md +17 -0
- package/dist/build-fingerprint.json +5 -0
- package/dist/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +1263 -635
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1263 -635
- package/dist/index.js.map +1 -1
- package/package.json +14 -9
package/dist/index.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
// src/activity-types/constants.ts
|
|
2
|
-
var ACTIVITY_TYPE = {
|
|
3
|
-
TASK: "task",
|
|
4
|
-
PROJECT: "project",
|
|
5
|
-
MILESTONE: "milestone"
|
|
6
|
-
};
|
|
7
|
-
var ACTIVITY_TYPES = Object.values(ACTIVITY_TYPE);
|
|
8
|
-
function isActivityType(value) {
|
|
9
|
-
return ACTIVITY_TYPES.includes(value);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
1
|
// src/types.ts
|
|
13
2
|
var NOTIFICATION_TYPE = {
|
|
14
3
|
CONSTRAINT: "constraint"};
|
|
@@ -690,7 +679,10 @@ function createLinksApi(engine) {
|
|
|
690
679
|
}
|
|
691
680
|
function createScheduleNamespaces(engine) {
|
|
692
681
|
const applyBaselinePoints2 = (points, options) => engine.dispatch(
|
|
693
|
-
{
|
|
682
|
+
{
|
|
683
|
+
kind: DISPATCH_ACTION_KIND.BASELINE_APPLY,
|
|
684
|
+
points
|
|
685
|
+
},
|
|
694
686
|
options
|
|
695
687
|
);
|
|
696
688
|
return {
|
|
@@ -1074,6 +1066,17 @@ function dispatchVisibilitySet(action, deps) {
|
|
|
1074
1066
|
return { ok: true, changes };
|
|
1075
1067
|
}
|
|
1076
1068
|
|
|
1069
|
+
// src/activity-types/constants.ts
|
|
1070
|
+
var ACTIVITY_TYPE = {
|
|
1071
|
+
TASK: "task",
|
|
1072
|
+
PROJECT: "project",
|
|
1073
|
+
MILESTONE: "milestone"
|
|
1074
|
+
};
|
|
1075
|
+
var ACTIVITY_TYPES = Object.values(ACTIVITY_TYPE);
|
|
1076
|
+
function isActivityType(value) {
|
|
1077
|
+
return ACTIVITY_TYPES.includes(value);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1077
1080
|
// src/autoscheduler/constants.ts
|
|
1078
1081
|
var TIMING = {
|
|
1079
1082
|
YIELD_SLICE_MS: 8
|
|
@@ -2440,6 +2443,7 @@ var ACTIVITY_PROPERTY = {
|
|
|
2440
2443
|
DURATION_HOURS: "durationHours",
|
|
2441
2444
|
CONSTRAINT_TYPE: "constraintType",
|
|
2442
2445
|
CONSTRAINT_DATE: "constraintDate",
|
|
2446
|
+
CALENDAR_ID: "calendarId",
|
|
2443
2447
|
PROGRESS: "progress",
|
|
2444
2448
|
COST: "cost",
|
|
2445
2449
|
USED_COST: "usedCost",
|
|
@@ -2570,6 +2574,17 @@ function filterTopoOrder(order, scopedIds) {
|
|
|
2570
2574
|
return filtered;
|
|
2571
2575
|
}
|
|
2572
2576
|
|
|
2577
|
+
// src/constants/progress.ts
|
|
2578
|
+
var NOT_STARTED_PROGRESS = 0;
|
|
2579
|
+
var COMPLETED_PROGRESS = 100;
|
|
2580
|
+
var MAX_TOLERATED_PROGRESS = COMPLETED_PROGRESS + Number.EPSILON * COMPLETED_PROGRESS * 2;
|
|
2581
|
+
function isCompletedProgress(progress) {
|
|
2582
|
+
return progress >= COMPLETED_PROGRESS && progress <= MAX_TOLERATED_PROGRESS;
|
|
2583
|
+
}
|
|
2584
|
+
function isAutoSchedulingEnabledForProgress(progress) {
|
|
2585
|
+
return progress === NOT_STARTED_PROGRESS;
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2573
2588
|
// src/autoscheduler/utils/parent-link-expansion.ts
|
|
2574
2589
|
function isSummary2(id, adapter) {
|
|
2575
2590
|
const activity = adapter.getActivity(id);
|
|
@@ -2749,12 +2764,8 @@ function deriveSummaryBoundsWithDates(summaryId, adapter, resolveDates) {
|
|
|
2749
2764
|
if (earliestStart === null || latestEnd === null) return null;
|
|
2750
2765
|
return { startDate: earliestStart, endDate: latestEnd };
|
|
2751
2766
|
}
|
|
2752
|
-
function deriveSummaryBounds(summaryId, adapter) {
|
|
2753
|
-
return deriveSummaryBoundsWithDates(summaryId, adapter,
|
|
2754
|
-
const leaf = adapter.getActivity(leafId);
|
|
2755
|
-
if (!leaf) return null;
|
|
2756
|
-
return { startDate: leaf.startDate, endDate: leaf.endDate };
|
|
2757
|
-
});
|
|
2767
|
+
function deriveSummaryBounds(summaryId, adapter, resolveDates = (leafId) => adapter.getActivity(leafId)) {
|
|
2768
|
+
return deriveSummaryBoundsWithDates(summaryId, adapter, resolveDates);
|
|
2758
2769
|
}
|
|
2759
2770
|
function endpointSpanHours(endpoint, bounds, adapter) {
|
|
2760
2771
|
if (!endpoint) return 0;
|
|
@@ -2780,31 +2791,48 @@ function hasIncomingRawLink(leafId, linksByActivity) {
|
|
|
2780
2791
|
const bucket = linksByActivity.get(leafKey) ?? [];
|
|
2781
2792
|
return bucket.some((rawLink) => String(rawLink.target) === leafKey);
|
|
2782
2793
|
}
|
|
2783
|
-
function targetOffsetForLeaf(leafId, targetStart,
|
|
2784
|
-
if (hasIncomingRawLink(leafId, linksByActivity))
|
|
2794
|
+
function targetOffsetForLeaf(leafId, targetStart, adapter, linksByActivity, targetEndpointIsFinish) {
|
|
2795
|
+
if (!targetEndpointIsFinish && hasIncomingRawLink(leafId, linksByActivity))
|
|
2796
|
+
return 0;
|
|
2785
2797
|
const leaf = adapter.getActivity(leafId);
|
|
2786
|
-
if (!targetStart || !leaf
|
|
2798
|
+
if (!targetStart || !leaf) return 0;
|
|
2787
2799
|
return adapter.calculateDuration({
|
|
2788
2800
|
startDate: targetStart,
|
|
2789
2801
|
endDate: leaf.startDate,
|
|
2790
|
-
task:
|
|
2802
|
+
task: leaf
|
|
2791
2803
|
});
|
|
2792
2804
|
}
|
|
2793
2805
|
function resolveExpansionAnchors(originalLink, adapter) {
|
|
2794
2806
|
const source = adapter.getActivity(originalLink.source);
|
|
2795
2807
|
const target = adapter.getActivity(originalLink.target);
|
|
2796
2808
|
const sourceBounds = deriveSummaryBounds(originalLink.source, adapter);
|
|
2797
|
-
const
|
|
2809
|
+
const fullTargetBounds = deriveSummaryBounds(originalLink.target, adapter);
|
|
2810
|
+
const targetBounds = deriveSummaryBoundsWithDates(
|
|
2811
|
+
originalLink.target,
|
|
2812
|
+
adapter,
|
|
2813
|
+
(leafId) => {
|
|
2814
|
+
const leaf = adapter.getActivity(leafId);
|
|
2815
|
+
if (!leaf) return null;
|
|
2816
|
+
const hasFixedDate = leaf.constraintDate !== null && (leaf.constraintType === CONSTRAINT_TYPE.MSO || leaf.constraintType === CONSTRAINT_TYPE.MFO);
|
|
2817
|
+
const canSchedule = leaf.autoScheduling && leaf.progress !== COMPLETED_PROGRESS && !hasFixedDate;
|
|
2818
|
+
if (!canSchedule) return null;
|
|
2819
|
+
return { startDate: leaf.startDate, endDate: leaf.endDate };
|
|
2820
|
+
}
|
|
2821
|
+
);
|
|
2798
2822
|
const sourceEndpointIsStart = originalLink.type === LINK_TYPE.START_TO_START || originalLink.type === LINK_TYPE.START_TO_FINISH;
|
|
2799
2823
|
const targetEndpointIsFinish = originalLink.type === LINK_TYPE.FINISH_TO_FINISH || originalLink.type === LINK_TYPE.START_TO_FINISH;
|
|
2800
2824
|
return {
|
|
2801
2825
|
source,
|
|
2802
2826
|
sourceBounds,
|
|
2803
2827
|
targetStart: targetBounds?.startDate ?? target?.startDate,
|
|
2828
|
+
targetBounds,
|
|
2829
|
+
hasFrozenTargetStart: Boolean(
|
|
2830
|
+
fullTargetBounds && targetBounds && fullTargetBounds.startDate < targetBounds.startDate
|
|
2831
|
+
),
|
|
2832
|
+
targetEndpointIsFinish,
|
|
2804
2833
|
userLag: originalLink.lag ?? 0,
|
|
2805
2834
|
sourceEndpointIsStart,
|
|
2806
|
-
|
|
2807
|
-
summarySourceId: originalLink.type === LINK_TYPE.START_TO_START && sourceBounds ? originalLink.source : void 0,
|
|
2835
|
+
summarySourceId: sourceEndpointIsStart && sourceBounds ? originalLink.source : void 0,
|
|
2808
2836
|
finishAnchoredSummaryTargetId: targetEndpointIsFinish && targetBounds ? originalLink.target : void 0
|
|
2809
2837
|
};
|
|
2810
2838
|
}
|
|
@@ -2813,7 +2841,9 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
|
|
|
2813
2841
|
const { source, sourceBounds, targetStart, userLag } = anchors;
|
|
2814
2842
|
const {
|
|
2815
2843
|
sourceEndpointIsStart,
|
|
2816
|
-
|
|
2844
|
+
targetBounds,
|
|
2845
|
+
targetEndpointIsFinish,
|
|
2846
|
+
hasFrozenTargetStart,
|
|
2817
2847
|
summarySourceId,
|
|
2818
2848
|
finishAnchoredSummaryTargetId
|
|
2819
2849
|
} = anchors;
|
|
@@ -2822,12 +2852,13 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
|
|
|
2822
2852
|
const sourceLag = sourceEndpointIsStart ? sourceLagForLeaf(srcId, source, sourceBounds, adapter) : 0;
|
|
2823
2853
|
for (const tgtId of leafIds) {
|
|
2824
2854
|
if (String(srcId) === String(tgtId)) continue;
|
|
2855
|
+
const targetLag = targetEndpointIsFinish ? -endpointSpanHours(adapter.getActivity(tgtId), targetBounds, adapter) : 0;
|
|
2825
2856
|
const offTarget = targetOffsetForLeaf(
|
|
2826
2857
|
tgtId,
|
|
2827
2858
|
targetStart,
|
|
2828
|
-
source,
|
|
2829
2859
|
adapter,
|
|
2830
|
-
linksByActivity
|
|
2860
|
+
linksByActivity,
|
|
2861
|
+
targetEndpointIsFinish
|
|
2831
2862
|
);
|
|
2832
2863
|
expanded.push(
|
|
2833
2864
|
buildVirtualLink(
|
|
@@ -2839,7 +2870,8 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
|
|
|
2839
2870
|
targetLag,
|
|
2840
2871
|
userLag + offTarget,
|
|
2841
2872
|
summarySourceId,
|
|
2842
|
-
finishAnchoredSummaryTargetId ?? (offTarget !== 0 && targetLag === 0 ? originalLink.target : void 0)
|
|
2873
|
+
finishAnchoredSummaryTargetId ?? ((offTarget !== 0 || hasFrozenTargetStart) && targetLag === 0 ? originalLink.target : void 0),
|
|
2874
|
+
targetBounds ? originalLink.target : void 0
|
|
2843
2875
|
)
|
|
2844
2876
|
);
|
|
2845
2877
|
counter += 1;
|
|
@@ -2847,7 +2879,7 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
|
|
|
2847
2879
|
}
|
|
2848
2880
|
return counter;
|
|
2849
2881
|
}
|
|
2850
|
-
function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag, targetLag, trueLag, summarySourceId, offsetSummaryTargetId) {
|
|
2882
|
+
function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag, targetLag, trueLag, summarySourceId, offsetSummaryTargetId, lagActivityId) {
|
|
2851
2883
|
return {
|
|
2852
2884
|
id: `virtual_${originalLink.id}_${counter}`,
|
|
2853
2885
|
source: sourceId,
|
|
@@ -2856,6 +2888,7 @@ function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag,
|
|
|
2856
2888
|
lag: originalLink.lag,
|
|
2857
2889
|
freeSlackHours: null,
|
|
2858
2890
|
isCritical: false,
|
|
2891
|
+
...lagActivityId === void 0 ? {} : { _lagActivityId: lagActivityId },
|
|
2859
2892
|
_sourceLag: sourceLag,
|
|
2860
2893
|
_targetLag: targetLag,
|
|
2861
2894
|
_trueLag: trueLag,
|
|
@@ -2926,17 +2959,6 @@ function findConnectedGroupForTrigger(triggerId, links) {
|
|
|
2926
2959
|
return { activityIds, links: groupLinks };
|
|
2927
2960
|
}
|
|
2928
2961
|
|
|
2929
|
-
// src/constants/progress.ts
|
|
2930
|
-
var NOT_STARTED_PROGRESS = 0;
|
|
2931
|
-
var COMPLETED_PROGRESS = 100;
|
|
2932
|
-
var MAX_TOLERATED_PROGRESS = COMPLETED_PROGRESS + Number.EPSILON * COMPLETED_PROGRESS * 2;
|
|
2933
|
-
function isCompletedProgress(progress) {
|
|
2934
|
-
return progress >= COMPLETED_PROGRESS && progress <= MAX_TOLERATED_PROGRESS;
|
|
2935
|
-
}
|
|
2936
|
-
function isAutoSchedulingEnabledForProgress(progress) {
|
|
2937
|
-
return progress === NOT_STARTED_PROGRESS;
|
|
2938
|
-
}
|
|
2939
|
-
|
|
2940
2962
|
// src/autoscheduler/engine/link-calculator.ts
|
|
2941
2963
|
var MILLISECONDS_PER_HOUR = 36e5;
|
|
2942
2964
|
var INSTANT_MILLISECONDS = 1;
|
|
@@ -3014,11 +3036,15 @@ function normalize(predecessor, successor, link) {
|
|
|
3014
3036
|
};
|
|
3015
3037
|
}
|
|
3016
3038
|
}
|
|
3017
|
-
function summaryAnchoredSourceLag(link, predecessor, adapter) {
|
|
3039
|
+
function summaryAnchoredSourceLag(link, predecessor, adapter, resolveDates) {
|
|
3018
3040
|
if (link._summarySourceId === void 0) return null;
|
|
3019
3041
|
const summarySource = adapter.getActivity(link._summarySourceId);
|
|
3020
3042
|
if (!summarySource) return null;
|
|
3021
|
-
const summaryBounds = deriveSummaryBounds(
|
|
3043
|
+
const summaryBounds = deriveSummaryBounds(
|
|
3044
|
+
link._summarySourceId,
|
|
3045
|
+
adapter,
|
|
3046
|
+
resolveDates
|
|
3047
|
+
);
|
|
3022
3048
|
if (!summaryBounds) return null;
|
|
3023
3049
|
return -adapter.calculateDuration({
|
|
3024
3050
|
startDate: summaryBounds.startDate,
|
|
@@ -3063,7 +3089,7 @@ function reverseRelationshipLagAnchor(successor, relationshipLagTask, adapter) {
|
|
|
3063
3089
|
task: relationshipLagTask
|
|
3064
3090
|
});
|
|
3065
3091
|
}
|
|
3066
|
-
function calculateSuccessorStartFromLink(predecessor, successor, link, adapter, trueLagTask = successor) {
|
|
3092
|
+
function calculateSuccessorStartFromLink(predecessor, successor, link, adapter, trueLagTask = successor, resolveDates) {
|
|
3067
3093
|
const {
|
|
3068
3094
|
sourceLag: frozenSourceLag,
|
|
3069
3095
|
targetLag,
|
|
@@ -3071,11 +3097,12 @@ function calculateSuccessorStartFromLink(predecessor, successor, link, adapter,
|
|
|
3071
3097
|
isFS,
|
|
3072
3098
|
isFF
|
|
3073
3099
|
} = normalize(predecessor, successor, link);
|
|
3074
|
-
const sourceLag = summaryAnchoredSourceLag(link, predecessor, adapter) ?? frozenSourceLag;
|
|
3100
|
+
const sourceLag = summaryAnchoredSourceLag(link, predecessor, adapter, resolveDates) ?? frozenSourceLag;
|
|
3101
|
+
const lagActivity = link._lagActivityId === void 0 ? null : adapter.getActivity(link._lagActivityId);
|
|
3075
3102
|
const totalLag = sourceLag + targetLag + trueLag;
|
|
3076
3103
|
const isZeroDurationSuccessor = successor.durationHours === 0;
|
|
3077
3104
|
const hasLag = isZeroDurationSuccessor ? totalLag !== 0 : sourceLag !== 0 || targetLag !== 0 || trueLag !== 0;
|
|
3078
|
-
if (!hasLag) {
|
|
3105
|
+
if (!hasLag && (!lagActivity || isZeroDurationSuccessor && sourceLag === 0 && targetLag === 0 && trueLag === 0)) {
|
|
3079
3106
|
return noLagSuccessorStart(
|
|
3080
3107
|
predecessor,
|
|
3081
3108
|
successor,
|
|
@@ -3084,7 +3111,7 @@ function calculateSuccessorStartFromLink(predecessor, successor, link, adapter,
|
|
|
3084
3111
|
isFS || isFF
|
|
3085
3112
|
);
|
|
3086
3113
|
}
|
|
3087
|
-
const baseTask = isFS ? successor : predecessor;
|
|
3114
|
+
const baseTask = isFS ? lagActivity ?? successor : predecessor;
|
|
3088
3115
|
let date2 = relationshipLagAnchor(predecessor, baseTask, trueLagTask, adapter);
|
|
3089
3116
|
if (sourceLag !== 0) {
|
|
3090
3117
|
date2 = adapter.calculateEndDate({
|
|
@@ -3093,15 +3120,30 @@ function calculateSuccessorStartFromLink(predecessor, successor, link, adapter,
|
|
|
3093
3120
|
task: predecessor
|
|
3094
3121
|
});
|
|
3095
3122
|
}
|
|
3096
|
-
if (
|
|
3097
|
-
date2 = adapter
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3123
|
+
if (lagActivity) {
|
|
3124
|
+
date2 = addTrueLag(date2, link.lag ?? 0, lagActivity, adapter);
|
|
3125
|
+
date2 = adapter.getClosestWorkTime({
|
|
3126
|
+
date: date2,
|
|
3127
|
+
dir: isFS || link.type === LINK_TYPE.START_TO_START ? "future" : "past",
|
|
3128
|
+
task: lagActivity
|
|
3101
3129
|
});
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3130
|
+
const leafOffset = targetLag + trueLag - (link.lag ?? 0);
|
|
3131
|
+
if (leafOffset !== 0) {
|
|
3132
|
+
date2 = adapter.calculateEndDate({
|
|
3133
|
+
startDate: date2,
|
|
3134
|
+
durationHours: leafOffset,
|
|
3135
|
+
task: successor
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
} else {
|
|
3139
|
+
if (targetLag !== 0) {
|
|
3140
|
+
date2 = adapter.calculateEndDate({
|
|
3141
|
+
startDate: date2,
|
|
3142
|
+
durationHours: targetLag,
|
|
3143
|
+
task: successor
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
3146
|
+
if (trueLag !== 0) date2 = addTrueLag(date2, trueLag, trueLagTask, adapter);
|
|
3105
3147
|
}
|
|
3106
3148
|
if (successor.durationHours === 0) {
|
|
3107
3149
|
if (isFS || isFF) {
|
|
@@ -3141,16 +3183,25 @@ function calculatePredecessorEndFromLink(successor, predecessor, link, adapter,
|
|
|
3141
3183
|
successor,
|
|
3142
3184
|
link
|
|
3143
3185
|
);
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3186
|
+
const lagActivity = link._lagActivityId === void 0 ? null : adapter.getActivity(link._lagActivityId);
|
|
3187
|
+
let date2 = lagActivity ? new Date(successor.startDate) : reverseRelationshipLagAnchor(successor, trueLagTask, adapter);
|
|
3188
|
+
if (lagActivity) {
|
|
3189
|
+
const leafOffset = targetLag + trueLag - (link.lag ?? 0);
|
|
3190
|
+
if (leafOffset !== 0)
|
|
3191
|
+
date2 = adapter.calculateEndDate({
|
|
3192
|
+
startDate: date2,
|
|
3193
|
+
durationHours: -leafOffset,
|
|
3194
|
+
task: successor
|
|
3195
|
+
});
|
|
3196
|
+
date2 = addTrueLag(date2, -(link.lag ?? 0), lagActivity, adapter);
|
|
3197
|
+
} else {
|
|
3198
|
+
if (trueLag !== 0) date2 = addTrueLag(date2, -trueLag, trueLagTask, adapter);
|
|
3199
|
+
if (targetLag !== 0)
|
|
3200
|
+
date2 = adapter.calculateEndDate({
|
|
3201
|
+
startDate: date2,
|
|
3202
|
+
durationHours: -targetLag,
|
|
3203
|
+
task: successor
|
|
3204
|
+
});
|
|
3154
3205
|
}
|
|
3155
3206
|
if (sourceLag !== 0) {
|
|
3156
3207
|
date2 = adapter.calculateEndDate({
|
|
@@ -3252,8 +3303,71 @@ function shouldSkipLagForMultiDrag(predecessorId, flags, draggedIds) {
|
|
|
3252
3303
|
return flags.dragWithMultiple && isPredecessorBeingDragged(predecessorId, draggedIds);
|
|
3253
3304
|
}
|
|
3254
3305
|
|
|
3306
|
+
// src/autoscheduler/engine/summary-finish-groups.ts
|
|
3307
|
+
function collectSummaryFinishGroups(links, adapter, resolveDates) {
|
|
3308
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3309
|
+
for (const link of links) {
|
|
3310
|
+
if (link.type !== LINK_TYPE.FINISH_TO_FINISH && link.type !== LINK_TYPE.START_TO_FINISH)
|
|
3311
|
+
continue;
|
|
3312
|
+
const summaryId = link._summaryTargetId;
|
|
3313
|
+
if (summaryId === void 0) continue;
|
|
3314
|
+
if (groups.has(summaryId)) continue;
|
|
3315
|
+
let independentlyDriven = false;
|
|
3316
|
+
const starts = /* @__PURE__ */ new Map();
|
|
3317
|
+
let movableEnd = null;
|
|
3318
|
+
let movableLeafId = null;
|
|
3319
|
+
const bounds = deriveSummaryBounds(summaryId, adapter, (id) => {
|
|
3320
|
+
const leaf = adapter.getActivity(id);
|
|
3321
|
+
if (!leaf) return null;
|
|
3322
|
+
const dates = resolveDates(id);
|
|
3323
|
+
starts.set(id, dates.startDate);
|
|
3324
|
+
if (!leaf.autoScheduling || leaf.progress === COMPLETED_PROGRESS || leaf.constraintType !== CONSTRAINT_TYPE.ASAP)
|
|
3325
|
+
independentlyDriven = true;
|
|
3326
|
+
for (const linkId of adapter.getIncomingLinkIds(id)) {
|
|
3327
|
+
const incoming = adapter.getLink(linkId);
|
|
3328
|
+
if (incoming && String(incoming.source) !== String(summaryId) && !adapter.isChildOf(incoming.source, summaryId))
|
|
3329
|
+
independentlyDriven = true;
|
|
3330
|
+
}
|
|
3331
|
+
const hasFixedDate = leaf.constraintDate !== null && (leaf.constraintType === CONSTRAINT_TYPE.MSO || leaf.constraintType === CONSTRAINT_TYPE.MFO);
|
|
3332
|
+
const movable = leaf.autoScheduling && leaf.progress !== COMPLETED_PROGRESS && !hasFixedDate;
|
|
3333
|
+
if (movable && (movableEnd === null || dates.endDate > movableEnd || dates.endDate.getTime() === movableEnd.getTime() && id < (movableLeafId ?? id))) {
|
|
3334
|
+
movableEnd = dates.endDate;
|
|
3335
|
+
movableLeafId = id;
|
|
3336
|
+
}
|
|
3337
|
+
return dates;
|
|
3338
|
+
});
|
|
3339
|
+
const drivingLeafIds = /* @__PURE__ */ new Set();
|
|
3340
|
+
const pending = movableLeafId === null ? [] : [movableLeafId];
|
|
3341
|
+
while (pending.length > 0) {
|
|
3342
|
+
const id = pending.pop();
|
|
3343
|
+
if (drivingLeafIds.has(id)) continue;
|
|
3344
|
+
drivingLeafIds.add(id);
|
|
3345
|
+
for (const linkId of [
|
|
3346
|
+
...adapter.getIncomingLinkIds(id),
|
|
3347
|
+
...adapter.getOutgoingLinkIds(id)
|
|
3348
|
+
]) {
|
|
3349
|
+
const link2 = adapter.getLink(linkId);
|
|
3350
|
+
if (!link2) continue;
|
|
3351
|
+
const neighbor = String(link2.source) === String(id) ? link2.target : link2.source;
|
|
3352
|
+
if (starts.has(neighbor) && !drivingLeafIds.has(neighbor))
|
|
3353
|
+
pending.push(neighbor);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
groups.set(
|
|
3357
|
+
summaryId,
|
|
3358
|
+
independentlyDriven && bounds ? {
|
|
3359
|
+
endDate: bounds.endDate,
|
|
3360
|
+
movableEnd,
|
|
3361
|
+
starts,
|
|
3362
|
+
drivingLeafIds
|
|
3363
|
+
} : null
|
|
3364
|
+
);
|
|
3365
|
+
}
|
|
3366
|
+
return groups;
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3255
3369
|
// src/autoscheduler/engine/asap-pass.ts
|
|
3256
|
-
async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
|
|
3370
|
+
async function asapPass(orderedIds, links, adapter, options, isCurrent, plans, deferSummaryFinishes = false) {
|
|
3257
3371
|
const incomingLinks = /* @__PURE__ */ new Map();
|
|
3258
3372
|
for (const link of links) {
|
|
3259
3373
|
const key = String(link.target);
|
|
@@ -3269,6 +3383,11 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
|
|
|
3269
3383
|
if (snap) return { startDate: snap.startDate, endDate: snap.endDate };
|
|
3270
3384
|
return { startDate: /* @__PURE__ */ new Date(), endDate: /* @__PURE__ */ new Date() };
|
|
3271
3385
|
};
|
|
3386
|
+
const summaryFinishes = collectSummaryFinishGroups(
|
|
3387
|
+
links,
|
|
3388
|
+
adapter,
|
|
3389
|
+
getActivityDates
|
|
3390
|
+
);
|
|
3272
3391
|
const flags = adapter.getFlags();
|
|
3273
3392
|
const draggedIds = options.draggedIds ?? [];
|
|
3274
3393
|
let sliceStart = performance.now();
|
|
@@ -3305,21 +3424,60 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
|
|
|
3305
3424
|
startDate: multiDragDates.startDate,
|
|
3306
3425
|
endDate: multiDragDates.endDate
|
|
3307
3426
|
};
|
|
3308
|
-
|
|
3427
|
+
let effectiveLink = shouldSkipLagForMultiDrag(
|
|
3309
3428
|
link.source,
|
|
3310
3429
|
flags,
|
|
3311
3430
|
draggedIds
|
|
3312
3431
|
) ? { ...link, lag: 0 } : link;
|
|
3432
|
+
const summaryFinish = effectiveLink._summaryTargetId === void 0 || effectiveLink.type !== LINK_TYPE.FINISH_TO_FINISH && effectiveLink.type !== LINK_TYPE.START_TO_FINISH ? null : summaryFinishes.get(effectiveLink._summaryTargetId);
|
|
3433
|
+
if (summaryFinish) {
|
|
3434
|
+
if (deferSummaryFinishes) continue;
|
|
3435
|
+
const requiredFinish = calculateSuccessorStartFromLink(
|
|
3436
|
+
adjustedPred,
|
|
3437
|
+
{ ...activity, durationHours: 0 },
|
|
3438
|
+
{ ...effectiveLink, _targetLag: 0, _trueLag: effectiveLink.lag },
|
|
3439
|
+
adapter,
|
|
3440
|
+
activity,
|
|
3441
|
+
getActivityDates
|
|
3442
|
+
).successorStart;
|
|
3443
|
+
const lagActivity = adapter.getActivity(effectiveLink._summaryTargetId) ?? activity;
|
|
3444
|
+
const satisfied = summaryFinish.endDate >= requiredFinish || adapter.calculateDuration({
|
|
3445
|
+
startDate: summaryFinish.endDate,
|
|
3446
|
+
endDate: requiredFinish,
|
|
3447
|
+
task: lagActivity
|
|
3448
|
+
}) === 0;
|
|
3449
|
+
if (satisfied || summaryFinish.movableEnd === null || !summaryFinish.drivingLeafIds.has(activityId))
|
|
3450
|
+
continue;
|
|
3451
|
+
effectiveLink = {
|
|
3452
|
+
...effectiveLink,
|
|
3453
|
+
_targetLag: -adapter.calculateDuration({
|
|
3454
|
+
startDate: summaryFinish.starts.get(activityId) ?? activity.startDate,
|
|
3455
|
+
endDate: summaryFinish.movableEnd,
|
|
3456
|
+
task: activity
|
|
3457
|
+
}),
|
|
3458
|
+
_trueLag: effectiveLink.lag
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
let summarySourceMovedThisRun = false;
|
|
3313
3462
|
const result = calculateSuccessorStartFromLink(
|
|
3314
3463
|
adjustedPred,
|
|
3315
3464
|
activity,
|
|
3316
3465
|
effectiveLink,
|
|
3317
|
-
adapter
|
|
3466
|
+
adapter,
|
|
3467
|
+
activity,
|
|
3468
|
+
(id) => {
|
|
3469
|
+
const dates = getActivityDates(id);
|
|
3470
|
+
const snapshot = adapter.getActivity(id);
|
|
3471
|
+
if (snapshot && (dates.startDate.getTime() !== snapshot.startDate.getTime() || dates.endDate.getTime() !== snapshot.endDate.getTime())) {
|
|
3472
|
+
summarySourceMovedThisRun = true;
|
|
3473
|
+
}
|
|
3474
|
+
return dates;
|
|
3475
|
+
}
|
|
3318
3476
|
);
|
|
3319
3477
|
if (!maxStart || result.successorStart > maxStart) {
|
|
3320
3478
|
maxStart = result.successorStart;
|
|
3321
3479
|
drivingLinkId = String(link.id);
|
|
3322
|
-
drivingOffsetIsStale = link._summaryTargetId !== void 0 && !predecessorMovedThisRun && (link._targetLag === 0 || options.preventSummaryTargetPullback === true);
|
|
3480
|
+
drivingOffsetIsStale = link._summaryTargetId !== void 0 && !predecessorMovedThisRun && !summarySourceMovedThisRun && (link._targetLag === 0 || options.preventSummaryTargetPullback === true);
|
|
3323
3481
|
}
|
|
3324
3482
|
}
|
|
3325
3483
|
const staleOffsetWouldPullBack = maxStart !== null && drivingOffsetIsStale && maxStart < activity.startDate;
|
|
@@ -4550,14 +4708,26 @@ var AutoScheduler = class {
|
|
|
4550
4708
|
return { ...emptyResult, cancelled: true };
|
|
4551
4709
|
}
|
|
4552
4710
|
const phaseAsap = startPhase("schedule.asapPass");
|
|
4553
|
-
const
|
|
4711
|
+
const hasSummaryFinishLinks = scopedLinks.some(
|
|
4712
|
+
(link) => link._summaryTargetId !== void 0 && (link.type === LINK_TYPE.FINISH_TO_FINISH || link.type === LINK_TYPE.START_TO_FINISH)
|
|
4713
|
+
);
|
|
4714
|
+
const initialAsapPlans = await asapPass(
|
|
4554
4715
|
topoResult.ordered,
|
|
4555
4716
|
scopedLinks,
|
|
4556
4717
|
this.adapter,
|
|
4557
4718
|
options,
|
|
4558
4719
|
isCurrent,
|
|
4559
|
-
plans
|
|
4720
|
+
plans,
|
|
4721
|
+
hasSummaryFinishLinks
|
|
4560
4722
|
);
|
|
4723
|
+
const asapPlans = hasSummaryFinishLinks ? await asapPass(
|
|
4724
|
+
topoResult.ordered,
|
|
4725
|
+
scopedLinks,
|
|
4726
|
+
this.adapter,
|
|
4727
|
+
options,
|
|
4728
|
+
isCurrent,
|
|
4729
|
+
initialAsapPlans
|
|
4730
|
+
) : initialAsapPlans;
|
|
4561
4731
|
endPhase(phaseAsap);
|
|
4562
4732
|
if ((await yieldBetweenPhases(isCurrent)).aborted) {
|
|
4563
4733
|
return { ...emptyResult, cancelled: true };
|
|
@@ -4719,14 +4889,16 @@ function roundProgressPerLevel(value) {
|
|
|
4719
4889
|
}
|
|
4720
4890
|
|
|
4721
4891
|
// src/propagations/upward/progress-rollup.ts
|
|
4722
|
-
function recomputeAllProgressRollup(adapter) {
|
|
4892
|
+
function recomputeAllProgressRollup(adapter, criterion) {
|
|
4893
|
+
const zeroCostBasis = /* @__PURE__ */ new Map();
|
|
4723
4894
|
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
4724
|
-
rollupParent(parentId, adapter);
|
|
4895
|
+
rollupParent(parentId, adapter, criterion, zeroCostBasis);
|
|
4725
4896
|
}
|
|
4726
4897
|
}
|
|
4727
|
-
function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter) {
|
|
4898
|
+
function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter, criterion) {
|
|
4899
|
+
const zeroCostBasis = /* @__PURE__ */ new Map();
|
|
4728
4900
|
for (const parentId of parentIdsDeepestFirst) {
|
|
4729
|
-
rollupParent(parentId, adapter);
|
|
4901
|
+
rollupParent(parentId, adapter, criterion, zeroCostBasis);
|
|
4730
4902
|
}
|
|
4731
4903
|
}
|
|
4732
4904
|
function parentsDeepestFirst(adapter) {
|
|
@@ -4750,7 +4922,7 @@ function depthOf(id, adapter) {
|
|
|
4750
4922
|
}
|
|
4751
4923
|
return depth;
|
|
4752
4924
|
}
|
|
4753
|
-
function rollupParent(parentId, adapter) {
|
|
4925
|
+
function rollupParent(parentId, adapter, criterion, zeroCostBasis) {
|
|
4754
4926
|
const childIds = adapter.getChildren(parentId);
|
|
4755
4927
|
if (childIds.length === 0) return;
|
|
4756
4928
|
const children = [];
|
|
@@ -4762,12 +4934,20 @@ function rollupParent(parentId, adapter) {
|
|
|
4762
4934
|
ponderator: Number(child.ponderator ?? 0)
|
|
4763
4935
|
});
|
|
4764
4936
|
}
|
|
4765
|
-
applyWeightedProgressRollup(
|
|
4937
|
+
applyWeightedProgressRollup(
|
|
4938
|
+
parentId,
|
|
4939
|
+
children,
|
|
4940
|
+
adapter,
|
|
4941
|
+
criterion,
|
|
4942
|
+
zeroCostBasis
|
|
4943
|
+
);
|
|
4766
4944
|
}
|
|
4767
|
-
function applyWeightedProgressRollup(parentId, children, adapter) {
|
|
4945
|
+
function applyWeightedProgressRollup(parentId, children, adapter, criterion, zeroCostBasis = /* @__PURE__ */ new Map()) {
|
|
4768
4946
|
const parent = adapter.getActivity(parentId);
|
|
4769
4947
|
if (!parent) return;
|
|
4770
|
-
const
|
|
4948
|
+
const weightedProgress = computeWeightedProgressRollup(children);
|
|
4949
|
+
const hasZeroCostBasis = weightedProgress === null && criterion === ACTIVITY_PONDERATOR_CRITERION.COST && hasOnlyZeroBaselineCosts(parentId, adapter, zeroCostBasis) === true;
|
|
4950
|
+
const rollup = hasZeroCostBasis ? 0 : weightedProgress;
|
|
4771
4951
|
if (rollup === null) {
|
|
4772
4952
|
const fallback = adapter.getProgressFallback(parentId);
|
|
4773
4953
|
if (fallback !== null) {
|
|
@@ -4784,6 +4964,43 @@ function applyWeightedProgressRollup(parentId, children, adapter) {
|
|
|
4784
4964
|
roundProgressPerLevel(rollup)
|
|
4785
4965
|
);
|
|
4786
4966
|
}
|
|
4967
|
+
function hasOnlyZeroBaselineCosts(parentId, adapter, zeroCostBasis) {
|
|
4968
|
+
const pending = [...adapter.getChildren(parentId)];
|
|
4969
|
+
let hasBaseline = false;
|
|
4970
|
+
while (pending.length > 0) {
|
|
4971
|
+
const activityId = pending.pop();
|
|
4972
|
+
const cachedBasis = zeroCostBasis.get(activityId);
|
|
4973
|
+
if (cachedBasis === false) {
|
|
4974
|
+
zeroCostBasis.set(parentId, false);
|
|
4975
|
+
return false;
|
|
4976
|
+
}
|
|
4977
|
+
if (cachedBasis !== void 0) {
|
|
4978
|
+
hasBaseline ||= cachedBasis === true;
|
|
4979
|
+
continue;
|
|
4980
|
+
}
|
|
4981
|
+
const activity = adapter.getActivity(activityId);
|
|
4982
|
+
if (!activity) continue;
|
|
4983
|
+
const children = adapter.getChildren(activityId);
|
|
4984
|
+
if (children.length > 0) {
|
|
4985
|
+
pending.push(...children);
|
|
4986
|
+
continue;
|
|
4987
|
+
}
|
|
4988
|
+
const basis2 = zeroBaselineCost(activity);
|
|
4989
|
+
hasBaseline ||= basis2 !== null;
|
|
4990
|
+
if (basis2 === false) {
|
|
4991
|
+
zeroCostBasis.set(parentId, false);
|
|
4992
|
+
return false;
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
const basis = hasBaseline ? true : null;
|
|
4996
|
+
zeroCostBasis.set(parentId, basis);
|
|
4997
|
+
return basis;
|
|
4998
|
+
}
|
|
4999
|
+
function zeroBaselineCost(activity) {
|
|
5000
|
+
const baseline = getActiveBaseline(activity);
|
|
5001
|
+
if (!baseline) return null;
|
|
5002
|
+
return activity.type === ACTIVITY_TYPE.MILESTONE || baseline.cost === 0;
|
|
5003
|
+
}
|
|
4787
5004
|
function setExplicitSummaryProgressFallbacks(activityId, progress, adapter) {
|
|
4788
5005
|
const pending = [activityId];
|
|
4789
5006
|
while (pending.length > 0) {
|
|
@@ -4810,7 +5027,8 @@ async function updateParentBoundsFromChildren(adapter, dirtyIds, recomputeProgre
|
|
|
4810
5027
|
const parents = dirtyIds ? findAncestorsOfDirty(adapter, dirtyIds) : findAllParents(adapter);
|
|
4811
5028
|
const progressMode = recomputeProgress ? options.progressMode ?? PARENT_PROGRESS_MODE.FREEZE_WITHOUT_CONTRIBUTION : PARENT_PROGRESS_MODE.SKIP;
|
|
4812
5029
|
await recomputeParentsCooperatively(parents, adapter, progressMode, {
|
|
4813
|
-
recomputeRealWork: options.recomputeRealWork ?? false
|
|
5030
|
+
recomputeRealWork: options.recomputeRealWork ?? false,
|
|
5031
|
+
criterion: options.criterion
|
|
4814
5032
|
});
|
|
4815
5033
|
}
|
|
4816
5034
|
function recomputeSingleParentDisplayBounds(parentId, adapter) {
|
|
@@ -4819,6 +5037,7 @@ function recomputeSingleParentDisplayBounds(parentId, adapter) {
|
|
|
4819
5037
|
async function recomputeParentsCooperatively(parents, adapter, progressMode, options = {
|
|
4820
5038
|
recomputeRealWork: false
|
|
4821
5039
|
}) {
|
|
5040
|
+
const zeroCostBasis = /* @__PURE__ */ new Map();
|
|
4822
5041
|
const YIELD_EVERY_N_PARENTS = 200;
|
|
4823
5042
|
let processed = 0;
|
|
4824
5043
|
for (const parentId of parents) {
|
|
@@ -4826,7 +5045,9 @@ async function recomputeParentsCooperatively(parents, adapter, progressMode, opt
|
|
|
4826
5045
|
parentId,
|
|
4827
5046
|
adapter,
|
|
4828
5047
|
progressMode,
|
|
4829
|
-
options.recomputeRealWork
|
|
5048
|
+
options.recomputeRealWork,
|
|
5049
|
+
options.criterion,
|
|
5050
|
+
zeroCostBasis
|
|
4830
5051
|
);
|
|
4831
5052
|
processed += 1;
|
|
4832
5053
|
if (processed % YIELD_EVERY_N_PARENTS === 0) {
|
|
@@ -4900,7 +5121,7 @@ function aggregateChildren(childrenIds, adapter, includeRealWork) {
|
|
|
4900
5121
|
}
|
|
4901
5122
|
return { minStart, maxEnd, realWorkSum, weightedChildren };
|
|
4902
5123
|
}
|
|
4903
|
-
function recomputeParentFromChildren(parentId, adapter, progressMode = PARENT_PROGRESS_MODE.FREEZE_WITHOUT_CONTRIBUTION, recomputeRealWork = false) {
|
|
5124
|
+
function recomputeParentFromChildren(parentId, adapter, progressMode = PARENT_PROGRESS_MODE.FREEZE_WITHOUT_CONTRIBUTION, recomputeRealWork = false, criterion, zeroCostBasis = /* @__PURE__ */ new Map()) {
|
|
4904
5125
|
const parent = adapter.getActivity(parentId);
|
|
4905
5126
|
if (!parent) return;
|
|
4906
5127
|
const childrenIds = adapter.getChildren(parentId);
|
|
@@ -4916,7 +5137,13 @@ function recomputeParentFromChildren(parentId, adapter, progressMode = PARENT_PR
|
|
|
4916
5137
|
adapter.setActivityField(parentId, "endDate", newEnd);
|
|
4917
5138
|
adapter.setActivityField(parentId, "durationHours", durationHours);
|
|
4918
5139
|
if (progressMode !== PARENT_PROGRESS_MODE.SKIP) {
|
|
4919
|
-
applyWeightedProgressRollup(
|
|
5140
|
+
applyWeightedProgressRollup(
|
|
5141
|
+
parentId,
|
|
5142
|
+
aggregate.weightedChildren,
|
|
5143
|
+
adapter,
|
|
5144
|
+
criterion,
|
|
5145
|
+
zeroCostBasis
|
|
5146
|
+
);
|
|
4920
5147
|
}
|
|
4921
5148
|
if (recomputeRealWork) {
|
|
4922
5149
|
adapter.setActivityField(parentId, "realWorkHours", aggregate.realWorkSum);
|
|
@@ -5248,9 +5475,13 @@ var POST_PROCESSOR = {
|
|
|
5248
5475
|
};
|
|
5249
5476
|
|
|
5250
5477
|
// src/columns/shared/error-codes.ts
|
|
5478
|
+
var PARSE_ERROR = {
|
|
5479
|
+
EMPTY_CALENDAR: "empty_calendar"};
|
|
5251
5480
|
var VALIDATION_REASON = {
|
|
5252
5481
|
UNCHANGED: "unchanged",
|
|
5253
|
-
NEGATIVE_DURATION: "negative_duration"
|
|
5482
|
+
NEGATIVE_DURATION: "negative_duration",
|
|
5483
|
+
UNKNOWN_CALENDAR: "unknown_calendar"
|
|
5484
|
+
};
|
|
5254
5485
|
var EDIT_DENIAL_REASON = {
|
|
5255
5486
|
COMPLETED: "completed",
|
|
5256
5487
|
SUMMARY: "summary",
|
|
@@ -5543,7 +5774,7 @@ var costPipeline = {
|
|
|
5543
5774
|
parseInput(rawValue) {
|
|
5544
5775
|
if (String(rawValue).trim() === "") return parsed(0);
|
|
5545
5776
|
const numericValue = parseFloat(String(rawValue));
|
|
5546
|
-
if (Number.
|
|
5777
|
+
if (!Number.isFinite(numericValue)) return parseError("not_a_number");
|
|
5547
5778
|
return parsed(numericValue);
|
|
5548
5779
|
},
|
|
5549
5780
|
validate() {
|
|
@@ -5593,7 +5824,7 @@ var usedCostPipeline = {
|
|
|
5593
5824
|
parseInput(rawValue) {
|
|
5594
5825
|
if (String(rawValue).trim() === "") return parsed(0);
|
|
5595
5826
|
const numericValue = parseFloat(String(rawValue));
|
|
5596
|
-
if (Number.
|
|
5827
|
+
if (!Number.isFinite(numericValue)) return parseError("not_a_number");
|
|
5597
5828
|
return parsed(numericValue);
|
|
5598
5829
|
},
|
|
5599
5830
|
validate() {
|
|
@@ -5646,7 +5877,7 @@ var hhWorkTimePipeline = {
|
|
|
5646
5877
|
parseInput(rawValue) {
|
|
5647
5878
|
if (String(rawValue).trim() === "") return parsed(0, rawValue);
|
|
5648
5879
|
const numericValue = parseFloat(String(rawValue));
|
|
5649
|
-
if (Number.
|
|
5880
|
+
if (!Number.isFinite(numericValue)) return parseError("not_a_number");
|
|
5650
5881
|
return parsed(numericValue);
|
|
5651
5882
|
},
|
|
5652
5883
|
validate() {
|
|
@@ -6044,27 +6275,33 @@ function isUnconstrained(activity) {
|
|
|
6044
6275
|
|
|
6045
6276
|
// src/columns/calendar-id/calendar-id-pipeline.ts
|
|
6046
6277
|
var calendarIdPipeline = {
|
|
6047
|
-
targetField:
|
|
6278
|
+
targetField: ACTIVITY_PROPERTY.CALENDAR_ID,
|
|
6048
6279
|
canEdit() {
|
|
6049
6280
|
return canEdit();
|
|
6050
6281
|
},
|
|
6051
6282
|
parseInput(rawValue) {
|
|
6283
|
+
if (rawValue === null) return parsed(null);
|
|
6052
6284
|
const value = String(rawValue).trim();
|
|
6053
|
-
if (isEmpty(value)) return parseError(
|
|
6285
|
+
if (isEmpty(value)) return parseError(PARSE_ERROR.EMPTY_CALENDAR);
|
|
6054
6286
|
return parsed(value);
|
|
6055
6287
|
},
|
|
6056
6288
|
validate(activity, _oldValue, newValue, ctx) {
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6289
|
+
const calendarId = newValue === null ? ctx.getDefaultCalendarId() : newValue;
|
|
6290
|
+
if (isUnchangedCalendar(activity.calendarId, calendarId) && (newValue !== null || activity.assignedDefaultCalendar))
|
|
6291
|
+
return invalid(VALIDATION_REASON.UNCHANGED);
|
|
6292
|
+
if (calendarId === null || !ctx.hasCalendar(calendarId))
|
|
6293
|
+
return invalid(VALIDATION_REASON.UNKNOWN_CALENDAR);
|
|
6061
6294
|
return valid();
|
|
6062
6295
|
},
|
|
6063
6296
|
transform(activity, newValue, ctx) {
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6297
|
+
const calendarId = newValue === null ? ctx.getDefaultCalendarId() : newValue;
|
|
6298
|
+
return fieldChanges(
|
|
6299
|
+
buildCalendarPatch(activity, calendarId, newValue === null, ctx),
|
|
6300
|
+
{
|
|
6301
|
+
autoSchedule: true,
|
|
6302
|
+
postProcessors: []
|
|
6303
|
+
}
|
|
6304
|
+
);
|
|
6068
6305
|
},
|
|
6069
6306
|
trackEvent: COLUMN_TRACK_EVENT.CALENDAR_ASSIGNMENT
|
|
6070
6307
|
};
|
|
@@ -6074,12 +6311,12 @@ function isEmpty(value) {
|
|
|
6074
6311
|
function isUnchangedCalendar(currentId, newId) {
|
|
6075
6312
|
return currentId === newId;
|
|
6076
6313
|
}
|
|
6077
|
-
function buildCalendarPatch(activity, newCalendarId, ctx) {
|
|
6314
|
+
function buildCalendarPatch(activity, newCalendarId, assignedDefaultCalendar, ctx) {
|
|
6078
6315
|
const fields = {
|
|
6079
6316
|
calendarId: newCalendarId,
|
|
6080
|
-
assignedDefaultCalendar
|
|
6317
|
+
assignedDefaultCalendar
|
|
6081
6318
|
};
|
|
6082
|
-
const newCalendar = ctx.calendars.getCalendar(newCalendarId);
|
|
6319
|
+
const newCalendar = newCalendarId === null ? null : ctx.calendars.getCalendar(newCalendarId);
|
|
6083
6320
|
if (newCalendar) {
|
|
6084
6321
|
const startIsWorking = newCalendar.isWorkTime(activity.startDate, "hour");
|
|
6085
6322
|
const calendarStart = newCalendar.getClosestWorkTime({
|
|
@@ -6289,6 +6526,7 @@ var P6_ACTIVITY_TYPE = {
|
|
|
6289
6526
|
TASK: "task",
|
|
6290
6527
|
LEVEL_OF_EFFORT: "level_of_effort",
|
|
6291
6528
|
START_MILESTONE: "start_milestone",
|
|
6529
|
+
WBS_SUMMARY: "wbs_summary",
|
|
6292
6530
|
RESOURCE_DEPENDENT: "resource_dependent"
|
|
6293
6531
|
};
|
|
6294
6532
|
var P6_EXECUTION_STATUS = {
|
|
@@ -6454,9 +6692,10 @@ var actualStartPipeline = {
|
|
|
6454
6692
|
transform(activity, newValue, ctx) {
|
|
6455
6693
|
if (newValue === null) return actualStartChanges(activity, null);
|
|
6456
6694
|
const calendar = resolveActivityCalendar(activity, ctx);
|
|
6695
|
+
const firstAssignment = activity.p6?.actualStart == null;
|
|
6457
6696
|
return actualStartChanges(
|
|
6458
6697
|
activity,
|
|
6459
|
-
defaultActualStartTime(newValue, calendar)
|
|
6698
|
+
firstAssignment ? defaultActualStartTime(newValue, calendar) : new Date(newValue)
|
|
6460
6699
|
);
|
|
6461
6700
|
}
|
|
6462
6701
|
};
|
|
@@ -6465,9 +6704,10 @@ var actualFinishPipeline = {
|
|
|
6465
6704
|
transform(activity, newValue, ctx) {
|
|
6466
6705
|
if (newValue === null) return actualFinishChanges(activity, null);
|
|
6467
6706
|
const calendar = resolveActivityCalendar(activity, ctx);
|
|
6707
|
+
const firstAssignment = activity.p6?.actualFinish == null;
|
|
6468
6708
|
return actualFinishChanges(
|
|
6469
6709
|
activity,
|
|
6470
|
-
defaultActualFinishTime(newValue, calendar)
|
|
6710
|
+
firstAssignment ? defaultActualFinishTime(newValue, calendar) : new Date(newValue)
|
|
6471
6711
|
);
|
|
6472
6712
|
}
|
|
6473
6713
|
};
|
|
@@ -12410,7 +12650,7 @@ function collectInsertedIds(captured) {
|
|
|
12410
12650
|
}
|
|
12411
12651
|
|
|
12412
12652
|
// src/propagations/downward/recompute-ponderator-for-parent.ts
|
|
12413
|
-
function recomputePonderatorsForParent(parentId, criterion, adapter) {
|
|
12653
|
+
function recomputePonderatorsForParent(parentId, criterion, adapter, basisById = /* @__PURE__ */ new Map()) {
|
|
12414
12654
|
if (!parentId || parentId === ROOT_PARENT_ID) return;
|
|
12415
12655
|
const childIds = adapter.getChildren(parentId);
|
|
12416
12656
|
if (childIds.length === 0) return;
|
|
@@ -12418,15 +12658,21 @@ function recomputePonderatorsForParent(parentId, criterion, adapter) {
|
|
|
12418
12658
|
const baselineScopedIds = /* @__PURE__ */ new Set();
|
|
12419
12659
|
let denominator = 0;
|
|
12420
12660
|
for (const childId of childIds) {
|
|
12421
|
-
const
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12661
|
+
const basis = baselineWeightingBasis(
|
|
12662
|
+
String(childId),
|
|
12663
|
+
criterion,
|
|
12664
|
+
adapter,
|
|
12665
|
+
basisById
|
|
12666
|
+
);
|
|
12667
|
+
pesoById.set(String(childId), basis.magnitude);
|
|
12668
|
+
denominator += basis.magnitude;
|
|
12669
|
+
if (basis.hasActiveBaseline) {
|
|
12425
12670
|
baselineScopedIds.add(String(childId));
|
|
12426
12671
|
}
|
|
12427
12672
|
}
|
|
12428
12673
|
const relationValue = denominator ? 100 / denominator : 0;
|
|
12429
|
-
const
|
|
12674
|
+
const hasEqualShare = criterion !== ACTIVITY_PONDERATOR_CRITERION.COST && baselineScopedIds.size > 0;
|
|
12675
|
+
const zeroMagnitudeShare = hasEqualShare ? 100 / baselineScopedIds.size : 0;
|
|
12430
12676
|
for (const childId of childIds) {
|
|
12431
12677
|
const child = adapter.getActivity(childId);
|
|
12432
12678
|
if (!child) continue;
|
|
@@ -12451,39 +12697,39 @@ function assignedPonderator(childId, child, pesoById, baselineScopedIds, denomin
|
|
|
12451
12697
|
}
|
|
12452
12698
|
return !Array.isArray(child.baselinePoints) || child.baselinePoints.length === 0 ? 0 : null;
|
|
12453
12699
|
}
|
|
12454
|
-
function
|
|
12455
|
-
const
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
if (!child) continue;
|
|
12477
|
-
if (child.type === ACTIVITY_TYPE.PROJECT) {
|
|
12478
|
-
sum2 += sumRecursiveBaselineMagnitude(String(childId), criterion, adapter);
|
|
12700
|
+
function baselineWeightingBasis(activityId, criterion, adapter, basisById) {
|
|
12701
|
+
const pending = [
|
|
12702
|
+
{ id: activityId, expanded: false }
|
|
12703
|
+
];
|
|
12704
|
+
while (pending.length > 0) {
|
|
12705
|
+
const { id, expanded } = pending.pop();
|
|
12706
|
+
if (basisById.has(id)) continue;
|
|
12707
|
+
const activity = adapter.getActivity(id);
|
|
12708
|
+
const baseline = getActiveBaseline(activity);
|
|
12709
|
+
if (activity?.type !== ACTIVITY_TYPE.PROJECT) {
|
|
12710
|
+
basisById.set(id, {
|
|
12711
|
+
magnitude: baseline && activity?.type !== ACTIVITY_TYPE.MILESTONE ? baselineMagnitude(baseline, criterion) : 0,
|
|
12712
|
+
hasActiveBaseline: baseline !== null
|
|
12713
|
+
});
|
|
12714
|
+
continue;
|
|
12715
|
+
}
|
|
12716
|
+
const children = adapter.getChildren(id);
|
|
12717
|
+
if (!expanded) {
|
|
12718
|
+
pending.push({ id, expanded: true });
|
|
12719
|
+
children.forEach((childId) => {
|
|
12720
|
+
pending.push({ id: String(childId), expanded: false });
|
|
12721
|
+
});
|
|
12479
12722
|
continue;
|
|
12480
12723
|
}
|
|
12481
|
-
const
|
|
12482
|
-
|
|
12483
|
-
|
|
12724
|
+
const basis = { magnitude: 0, hasActiveBaseline: baseline !== null };
|
|
12725
|
+
for (const childId of children) {
|
|
12726
|
+
const child = basisById.get(String(childId));
|
|
12727
|
+
basis.magnitude += child.magnitude;
|
|
12728
|
+
basis.hasActiveBaseline ||= child.hasActiveBaseline;
|
|
12484
12729
|
}
|
|
12730
|
+
basisById.set(id, basis);
|
|
12485
12731
|
}
|
|
12486
|
-
return
|
|
12732
|
+
return basisById.get(activityId);
|
|
12487
12733
|
}
|
|
12488
12734
|
function baselineMagnitude(baseline, criterion) {
|
|
12489
12735
|
if (criterion === ACTIVITY_PONDERATOR_CRITERION.COST) {
|
|
@@ -12499,10 +12745,72 @@ function toNumber(v) {
|
|
|
12499
12745
|
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
12500
12746
|
}
|
|
12501
12747
|
|
|
12748
|
+
// src/dispatch/shared/structural-ponderators.ts
|
|
12749
|
+
function recomputeStructuralPonderators(adapter, criterion) {
|
|
12750
|
+
const journal = adapter.peekWriteCapture();
|
|
12751
|
+
if (!journal) return;
|
|
12752
|
+
const parents = /* @__PURE__ */ new Map();
|
|
12753
|
+
const removedParents = /* @__PURE__ */ new Map();
|
|
12754
|
+
for (const operation of journal.ops) {
|
|
12755
|
+
if (operation.kind === WRITE_OPERATION_KIND.REMOVE) {
|
|
12756
|
+
removedParents.set(operation.activityId, operation.before.parentId);
|
|
12757
|
+
}
|
|
12758
|
+
}
|
|
12759
|
+
const collectParents = (parentId, lostBaseline) => {
|
|
12760
|
+
let current = parentId;
|
|
12761
|
+
while (current !== null && !isRootParent(current)) {
|
|
12762
|
+
const existing = parents.get(current);
|
|
12763
|
+
if (existing === true || existing === lostBaseline) break;
|
|
12764
|
+
parents.set(current, lostBaseline);
|
|
12765
|
+
current = removedParents.has(current) ? removedParents.get(current) ?? null : adapter.getParentId(current);
|
|
12766
|
+
}
|
|
12767
|
+
};
|
|
12768
|
+
for (const [activityId, before] of journal.before) {
|
|
12769
|
+
const after = adapter.getActivity(activityId);
|
|
12770
|
+
const parentChanged = after && before.parentId !== after.parentId;
|
|
12771
|
+
if (!parentChanged || !after) continue;
|
|
12772
|
+
const hasBaseline = getActiveBaseline(before) !== null || hasBaselineInBranch(activityId, adapter);
|
|
12773
|
+
collectParents(before.parentId, hasBaseline);
|
|
12774
|
+
collectParents(after.parentId, false);
|
|
12775
|
+
}
|
|
12776
|
+
for (const operation of journal.ops) {
|
|
12777
|
+
if (operation.kind === WRITE_OPERATION_KIND.REMOVE) {
|
|
12778
|
+
if (getActiveBaseline(operation.before))
|
|
12779
|
+
collectParents(operation.before.parentId, true);
|
|
12780
|
+
} else if (operation.kind === WRITE_OPERATION_KIND.INSERT) {
|
|
12781
|
+
const inserted = adapter.getActivity(operation.activityId);
|
|
12782
|
+
if (inserted && hasBaselineInBranch(inserted.id, adapter))
|
|
12783
|
+
collectParents(inserted.parentId, false);
|
|
12784
|
+
}
|
|
12785
|
+
}
|
|
12786
|
+
for (const [parentId, lostBaseline] of parents) {
|
|
12787
|
+
if (lostBaseline || hasBaselineInBranch(parentId, adapter)) {
|
|
12788
|
+
recomputePonderatorsForParent(parentId, criterion, adapter);
|
|
12789
|
+
}
|
|
12790
|
+
}
|
|
12791
|
+
}
|
|
12792
|
+
function hasBaselineInBranch(activityId, adapter) {
|
|
12793
|
+
const pending = [activityId];
|
|
12794
|
+
while (pending.length > 0) {
|
|
12795
|
+
const current = pending.pop();
|
|
12796
|
+
if (current === void 0) continue;
|
|
12797
|
+
const activity = adapter.getActivity(current);
|
|
12798
|
+
if (activity && getActiveBaseline(activity)) return true;
|
|
12799
|
+
pending.push(...adapter.getChildren(current));
|
|
12800
|
+
}
|
|
12801
|
+
return false;
|
|
12802
|
+
}
|
|
12803
|
+
|
|
12502
12804
|
// src/dispatch/shared/post-mutation.ts
|
|
12503
12805
|
var AUTOSCHEDULE_SCOPE = { ROOTS: "roots" };
|
|
12504
12806
|
async function runPostMutation(deps, args) {
|
|
12505
|
-
|
|
12807
|
+
const isConversion = args.action.kind === DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_CONVERT;
|
|
12808
|
+
if (!isConversion) {
|
|
12809
|
+
recomputeStructuralPonderators(
|
|
12810
|
+
deps.adapter,
|
|
12811
|
+
deps.sector.activityCreter ?? ACTIVITY_PONDERATOR_CRITERION.DURATION
|
|
12812
|
+
);
|
|
12813
|
+
}
|
|
12506
12814
|
const progressInvalidation = resolveProgressInvalidation(deps.adapter);
|
|
12507
12815
|
const effectiveArgs = resolveTransactionAutoscheduleArgs(deps.adapter, args);
|
|
12508
12816
|
const passes = resolvePasses(
|
|
@@ -12523,7 +12831,8 @@ async function runPostMutation(deps, args) {
|
|
|
12523
12831
|
effectiveArgs,
|
|
12524
12832
|
schedule.scheduledIds,
|
|
12525
12833
|
passes,
|
|
12526
|
-
progressInvalidation
|
|
12834
|
+
progressInvalidation,
|
|
12835
|
+
deps.sector.activityCreter ?? ACTIVITY_PONDERATOR_CRITERION.DURATION
|
|
12527
12836
|
);
|
|
12528
12837
|
await runDerivedRecomputes(deps, passes, args.now);
|
|
12529
12838
|
synchronizeInactiveDataDateDrafts(deps.adapter);
|
|
@@ -12572,37 +12881,6 @@ function collectLinkMutationTargets(adapter) {
|
|
|
12572
12881
|
}
|
|
12573
12882
|
return targets;
|
|
12574
12883
|
}
|
|
12575
|
-
function recomputePromotedSummaryPonderators(deps) {
|
|
12576
|
-
const journal = deps.adapter.peekWriteCapture();
|
|
12577
|
-
if (!journal) return;
|
|
12578
|
-
const visited = /* @__PURE__ */ new Set();
|
|
12579
|
-
const criterion = deps.sector.activityCreter ?? ACTIVITY_PONDERATOR_CRITERION.DURATION;
|
|
12580
|
-
for (const [activityId, before] of journal.before) {
|
|
12581
|
-
const after = deps.adapter.getActivity(activityId);
|
|
12582
|
-
if (before.type === ACTIVITY_TYPE.PROJECT || after?.type !== ACTIVITY_TYPE.PROJECT || getActiveBaseline(after) || !hasActiveBaselineDescendant(activityId, deps.adapter)) {
|
|
12583
|
-
continue;
|
|
12584
|
-
}
|
|
12585
|
-
let current = activityId;
|
|
12586
|
-
while (current !== null && !isRootParent(current)) {
|
|
12587
|
-
if (!visited.has(current)) {
|
|
12588
|
-
recomputePonderatorsForParent(current, criterion, deps.adapter);
|
|
12589
|
-
visited.add(current);
|
|
12590
|
-
}
|
|
12591
|
-
current = deps.adapter.getParentId(current);
|
|
12592
|
-
}
|
|
12593
|
-
}
|
|
12594
|
-
}
|
|
12595
|
-
function hasActiveBaselineDescendant(activityId, adapter) {
|
|
12596
|
-
const pending = [...adapter.getChildren(activityId)];
|
|
12597
|
-
while (pending.length > 0) {
|
|
12598
|
-
const childId = pending.pop();
|
|
12599
|
-
if (childId === void 0) continue;
|
|
12600
|
-
const child = adapter.getActivity(childId);
|
|
12601
|
-
if (child && getActiveBaseline(child)) return true;
|
|
12602
|
-
pending.push(...adapter.getChildren(childId));
|
|
12603
|
-
}
|
|
12604
|
-
return false;
|
|
12605
|
-
}
|
|
12606
12884
|
async function dispatchScheduleAutoSchedule(action, options, deps) {
|
|
12607
12885
|
if (action.activityId !== void 0 && !deps.adapter.activityExists(action.activityId)) {
|
|
12608
12886
|
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
@@ -12755,7 +13033,7 @@ async function refreshMutatedParentBounds(adapter, args) {
|
|
|
12755
13033
|
if (mutated.size === 0) return;
|
|
12756
13034
|
await updateParentBoundsFromChildren(adapter, mutated, false);
|
|
12757
13035
|
}
|
|
12758
|
-
async function rollUpParentBounds(adapter, args, scheduledIds, passes, progressInvalidation) {
|
|
13036
|
+
async function rollUpParentBounds(adapter, args, scheduledIds, passes, progressInvalidation, criterion) {
|
|
12759
13037
|
if (passes.has(DERIVED_PASS.PARENT_PROGRESS)) {
|
|
12760
13038
|
restoreDemotedProgressFallbacks(
|
|
12761
13039
|
adapter,
|
|
@@ -12772,6 +13050,7 @@ async function rollUpParentBounds(adapter, args, scheduledIds, passes, progressI
|
|
|
12772
13050
|
passes.has(DERIVED_PASS.PARENT_PROGRESS),
|
|
12773
13051
|
{
|
|
12774
13052
|
recomputeRealWork: passes.has(DERIVED_PASS.REAL_WORK),
|
|
13053
|
+
criterion,
|
|
12775
13054
|
progressMode: "freeze-when-no-contribution"
|
|
12776
13055
|
}
|
|
12777
13056
|
);
|
|
@@ -13021,6 +13300,8 @@ function buildLinkChangesForBatch(applied, beforeLinks, adapter) {
|
|
|
13021
13300
|
after: Number(afterLink.lag)
|
|
13022
13301
|
};
|
|
13023
13302
|
}
|
|
13303
|
+
const unchangedLink = beforeLink !== void 0 && Object.keys(fields).length === 0;
|
|
13304
|
+
if (unchangedLink) continue;
|
|
13024
13305
|
out.push({
|
|
13025
13306
|
id: String(op.linkId),
|
|
13026
13307
|
kind: ENTITY_CHANGE_KIND.UPDATED,
|
|
@@ -16124,6 +16405,69 @@ function hasInvalidPersistedProgress(action) {
|
|
|
16124
16405
|
return progress !== void 0 && (action.activityIds.length !== 1 || !Number.isFinite(progress) || progress < 0 || progress > 100);
|
|
16125
16406
|
}
|
|
16126
16407
|
|
|
16408
|
+
// src/dispatch/shared/lookahead-conversion-progress.ts
|
|
16409
|
+
function buildConversionProgressSnapshots(activities, criterion) {
|
|
16410
|
+
const snapshots = new Map(
|
|
16411
|
+
activities.map((activity) => [
|
|
16412
|
+
activity.id,
|
|
16413
|
+
structuredCloneActivity(activity)
|
|
16414
|
+
])
|
|
16415
|
+
);
|
|
16416
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
16417
|
+
for (const activity of activities) {
|
|
16418
|
+
if (activity.parentId === null) continue;
|
|
16419
|
+
const siblings = childrenByParent.get(activity.parentId) ?? [];
|
|
16420
|
+
siblings.push(activity);
|
|
16421
|
+
childrenByParent.set(activity.parentId, siblings);
|
|
16422
|
+
}
|
|
16423
|
+
const magnitudes = /* @__PURE__ */ new Map();
|
|
16424
|
+
for (const activity of [...activities].reverse()) {
|
|
16425
|
+
const children = childrenByParent.get(activity.id) ?? [];
|
|
16426
|
+
const isSummary3 = children.length > 0;
|
|
16427
|
+
const magnitude = isSummary3 ? children.reduce(
|
|
16428
|
+
(sum2, child) => sum2 + requiredMagnitude(magnitudes, child.id),
|
|
16429
|
+
0
|
|
16430
|
+
) : liveMagnitude(activity, criterion);
|
|
16431
|
+
magnitudes.set(activity.id, magnitude);
|
|
16432
|
+
if (!isSummary3) continue;
|
|
16433
|
+
const hasMagnitude = magnitude > 0;
|
|
16434
|
+
const permitsEqualShare = criterion !== ACTIVITY_PONDERATOR_CRITERION.COST;
|
|
16435
|
+
for (const child of children) {
|
|
16436
|
+
const snapshot2 = requiredSnapshot(snapshots, child.id);
|
|
16437
|
+
const ponderator = hasMagnitude ? 100 * requiredMagnitude(magnitudes, child.id) / magnitude : permitsEqualShare ? 100 / children.length : 0;
|
|
16438
|
+
snapshots.set(child.id, { ...snapshot2, ponderator });
|
|
16439
|
+
}
|
|
16440
|
+
const weightedChildren = children.map(
|
|
16441
|
+
(child) => requiredSnapshot(snapshots, child.id)
|
|
16442
|
+
);
|
|
16443
|
+
const progress = computeWeightedProgressRollup(weightedChildren);
|
|
16444
|
+
const snapshot = requiredSnapshot(snapshots, activity.id);
|
|
16445
|
+
snapshots.set(activity.id, {
|
|
16446
|
+
...snapshot,
|
|
16447
|
+
progress: roundProgressPerLevel(progress ?? 0)
|
|
16448
|
+
});
|
|
16449
|
+
}
|
|
16450
|
+
return snapshots;
|
|
16451
|
+
}
|
|
16452
|
+
function liveMagnitude(activity, criterion) {
|
|
16453
|
+
if (activity.type === ACTIVITY_TYPE.PROJECT) return 0;
|
|
16454
|
+
if (criterion === ACTIVITY_PONDERATOR_CRITERION.COST) return activity.cost;
|
|
16455
|
+
if (criterion === ACTIVITY_PONDERATOR_CRITERION.WORK_HOURS)
|
|
16456
|
+
return activity.workHours;
|
|
16457
|
+
return activity.durationHours;
|
|
16458
|
+
}
|
|
16459
|
+
function requiredMagnitude(magnitudes, activityId) {
|
|
16460
|
+
const magnitude = magnitudes.get(activityId);
|
|
16461
|
+
if (magnitude === void 0)
|
|
16462
|
+
throw new Error(`Missing conversion magnitude ${activityId}`);
|
|
16463
|
+
return magnitude;
|
|
16464
|
+
}
|
|
16465
|
+
function requiredSnapshot(snapshots, activityId) {
|
|
16466
|
+
const snapshot = snapshots.get(activityId);
|
|
16467
|
+
if (!snapshot) throw new Error(`Missing conversion snapshot ${activityId}`);
|
|
16468
|
+
return snapshot;
|
|
16469
|
+
}
|
|
16470
|
+
|
|
16127
16471
|
// src/dispatch/handlers/activity-lookahead-convert.ts
|
|
16128
16472
|
function buildLookaheadConversionPlan(input, deps) {
|
|
16129
16473
|
const { adapter } = deps;
|
|
@@ -16138,7 +16482,11 @@ function buildLookaheadConversionPlan(input, deps) {
|
|
|
16138
16482
|
(activityId) => requested.has(activityId) && !hasAncestorInSet(activityId, requested, adapter)
|
|
16139
16483
|
);
|
|
16140
16484
|
const branches = conversionRootIds.map(
|
|
16141
|
-
(rootId) => planConversionBranch(
|
|
16485
|
+
(rootId) => planConversionBranch(
|
|
16486
|
+
rootId,
|
|
16487
|
+
adapter,
|
|
16488
|
+
input.criterion ?? deps.sector.activityCreter ?? ACTIVITY_PONDERATOR_CRITERION.DURATION
|
|
16489
|
+
)
|
|
16142
16490
|
);
|
|
16143
16491
|
const conversionGroups = branches.map(
|
|
16144
16492
|
({ conversionRootId, convertedActivities }) => ({
|
|
@@ -16213,7 +16561,7 @@ async function dispatchActivityLookaheadConvert(action, options, deps) {
|
|
|
16213
16561
|
});
|
|
16214
16562
|
return buildConversionResult(plan, context, changes);
|
|
16215
16563
|
}
|
|
16216
|
-
function planConversionBranch(rootId, adapter) {
|
|
16564
|
+
function planConversionBranch(rootId, adapter, criterion) {
|
|
16217
16565
|
const root = requiredActivity(rootId, adapter);
|
|
16218
16566
|
const branch = collectDescendantIds([rootId], adapter);
|
|
16219
16567
|
branch.delete(rootId);
|
|
@@ -16230,15 +16578,19 @@ function planConversionBranch(rootId, adapter) {
|
|
|
16230
16578
|
(activity) => activity.type === ACTIVITY_TYPE.MILESTONE
|
|
16231
16579
|
);
|
|
16232
16580
|
const persistedIds = collectPersistedIds(root, convertible);
|
|
16581
|
+
const progressSnapshots = buildConversionProgressSnapshots(
|
|
16582
|
+
[root, ...convertible],
|
|
16583
|
+
criterion
|
|
16584
|
+
);
|
|
16585
|
+
const rootSnapshot = progressSnapshots.get(root.id);
|
|
16586
|
+
if (!rootSnapshot) throw new Error(`Missing conversion snapshot ${root.id}`);
|
|
16233
16587
|
return {
|
|
16234
16588
|
conversionRootId: rootId,
|
|
16235
|
-
convertedActivities:
|
|
16236
|
-
(activity) => buildConvertedSnapshot(activity, persistedIds)
|
|
16237
|
-
),
|
|
16589
|
+
convertedActivities: [...progressSnapshots.values()].filter((activity) => activity.id !== rootId).map((activity) => buildConvertedSnapshot(activity, persistedIds)),
|
|
16238
16590
|
milestoneMoves: buildMilestoneMoves(root, milestones),
|
|
16239
16591
|
activityIdsToDelete: convertible.map((activity) => activity.id),
|
|
16240
16592
|
formerParent: {
|
|
16241
|
-
activity:
|
|
16593
|
+
activity: rootSnapshot,
|
|
16242
16594
|
hasConvertedActivities: convertible.length > 0
|
|
16243
16595
|
}
|
|
16244
16596
|
};
|
|
@@ -16322,10 +16674,12 @@ function applyFormerParentState(plan, deletedIds, adapter) {
|
|
|
16322
16674
|
idsRemoved: deletedIds
|
|
16323
16675
|
});
|
|
16324
16676
|
adapter.setActivityFields(activity.id, {
|
|
16677
|
+
progress: activity.progress,
|
|
16325
16678
|
isLookahead: activity.isLookahead || hasConvertedActivities,
|
|
16326
16679
|
hasLookaheadTasks: activity.hasLookaheadTasks || hasConvertedActivities,
|
|
16327
16680
|
...cleanup?.fields ?? {}
|
|
16328
16681
|
});
|
|
16682
|
+
adapter.setProgressFallback(activity.id, null);
|
|
16329
16683
|
}
|
|
16330
16684
|
}
|
|
16331
16685
|
function recomputeConversionRollups(plan, adapter) {
|
|
@@ -17404,8 +17758,9 @@ function isDateColumn(column) {
|
|
|
17404
17758
|
return column === COLUMN.START_DATE || column === COLUMN.END_DATE;
|
|
17405
17759
|
}
|
|
17406
17760
|
async function dispatchDatesBatch(action, options, deps) {
|
|
17407
|
-
|
|
17408
|
-
|
|
17761
|
+
const rejection = validateBatchEdits(action, deps);
|
|
17762
|
+
if (rejection !== null) {
|
|
17763
|
+
return { ok: false, reason: rejection };
|
|
17409
17764
|
}
|
|
17410
17765
|
const { adapter, scheduler, sector } = deps;
|
|
17411
17766
|
const verdicts = [];
|
|
@@ -17481,6 +17836,17 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
17481
17836
|
})
|
|
17482
17837
|
};
|
|
17483
17838
|
}
|
|
17839
|
+
function validateBatchEdits(action, deps) {
|
|
17840
|
+
const hasInvalidColumn = action.edits.some(
|
|
17841
|
+
(edit) => !isDateColumn(edit.column)
|
|
17842
|
+
);
|
|
17843
|
+
if (hasInvalidColumn) return REJECTION_REASON.INVALID_VALUE;
|
|
17844
|
+
const hasMissingActivity = action.edits.some(
|
|
17845
|
+
(edit) => deps.ctx.activityReader.getActivity(edit.activityId) === null
|
|
17846
|
+
);
|
|
17847
|
+
if (hasMissingActivity) return REJECTION_REASON.ACTIVITY_NOT_FOUND;
|
|
17848
|
+
return null;
|
|
17849
|
+
}
|
|
17484
17850
|
function applyOneEdit(edit, deps, options) {
|
|
17485
17851
|
const { ctx } = deps;
|
|
17486
17852
|
const activityId = edit.activityId;
|
|
@@ -18220,12 +18586,13 @@ function applyBaselineOverlay(state, points, now, reporter, defaultBaseCalendarI
|
|
|
18220
18586
|
|
|
18221
18587
|
// src/propagations/downward/recompute-all-ponderators.ts
|
|
18222
18588
|
function recomputeAllPonderators(criterion, adapter) {
|
|
18589
|
+
const basisById = /* @__PURE__ */ new Map();
|
|
18223
18590
|
adapter.forEachActivity((_a, id) => {
|
|
18224
18591
|
adapter.setActivityField(id, COLUMN.PONDERATOR, 0);
|
|
18225
18592
|
});
|
|
18226
18593
|
adapter.forEachActivity((_a, id) => {
|
|
18227
18594
|
if (adapter.getChildren(id).length > 0) {
|
|
18228
|
-
recomputePonderatorsForParent(String(id), criterion, adapter);
|
|
18595
|
+
recomputePonderatorsForParent(String(id), criterion, adapter, basisById);
|
|
18229
18596
|
}
|
|
18230
18597
|
});
|
|
18231
18598
|
}
|
|
@@ -18233,7 +18600,7 @@ function recomputeAllPonderators(criterion, adapter) {
|
|
|
18233
18600
|
// src/dispatch/shared/baseline-weighted-fields.ts
|
|
18234
18601
|
function recomputeBaselineWeightedFields(deps, criterion) {
|
|
18235
18602
|
recomputeAllPonderators(criterion, deps.adapter);
|
|
18236
|
-
recomputeAllProgressRollup(deps.adapter);
|
|
18603
|
+
recomputeAllProgressRollup(deps.adapter, criterion);
|
|
18237
18604
|
if (deps.now) {
|
|
18238
18605
|
runExpectedProgressBase(
|
|
18239
18606
|
deps.adapter,
|
|
@@ -18277,7 +18644,8 @@ async function dispatchBaselineApply(action, deps) {
|
|
|
18277
18644
|
action.points,
|
|
18278
18645
|
deps.now,
|
|
18279
18646
|
deps.reporter,
|
|
18280
|
-
deps.defaultBaseCalendarId ?? null
|
|
18647
|
+
deps.defaultBaseCalendarId ?? null,
|
|
18648
|
+
void 0
|
|
18281
18649
|
);
|
|
18282
18650
|
applyBaselineVariancePass(deps.adapter, deps.sector.hoursPerDay);
|
|
18283
18651
|
if (activeBaselineSignature(deps) !== activeBaselineBefore) {
|
|
@@ -19008,7 +19376,10 @@ function dispatchCellCopy(action, deps) {
|
|
|
19008
19376
|
if (!pipeline) {
|
|
19009
19377
|
return { ok: false, reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN };
|
|
19010
19378
|
}
|
|
19011
|
-
const canonicalValue = readPipelineTargetValue(
|
|
19379
|
+
const canonicalValue = readPipelineTargetValue(
|
|
19380
|
+
activity,
|
|
19381
|
+
pipeline.targetField
|
|
19382
|
+
);
|
|
19012
19383
|
if (action.column === COLUMN.DURATION && !Number.isFinite(canonicalValue)) {
|
|
19013
19384
|
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
19014
19385
|
}
|
|
@@ -19334,6 +19705,7 @@ function coerceActivityFieldValue(field, value) {
|
|
|
19334
19705
|
}
|
|
19335
19706
|
|
|
19336
19707
|
// src/calendar/adapter.ts
|
|
19708
|
+
var WORK_CALENDAR_UNIT = { MINUTE: "minute" };
|
|
19337
19709
|
function createWorkCalendarAdapter(instance, options = {}) {
|
|
19338
19710
|
const defaultUnit = options.defaultUnit ?? CALENDAR_UNIT.HOUR;
|
|
19339
19711
|
return {
|
|
@@ -19354,11 +19726,18 @@ function wrapWorkCalendar(calendar, defaultUnit) {
|
|
|
19354
19726
|
});
|
|
19355
19727
|
},
|
|
19356
19728
|
calculateEndDate(start, durationHours) {
|
|
19357
|
-
|
|
19358
|
-
|
|
19729
|
+
const fractionalHours = defaultUnit === CALENDAR_UNIT.HOUR && !Number.isInteger(durationHours);
|
|
19730
|
+
const sourceRemainder = start.getUTCSeconds() * 1e3 + start.getUTCMilliseconds();
|
|
19731
|
+
const shouldPreserveSourcePrecision = fractionalHours && sourceRemainder !== 0 && calendar.isWorkTime({ date: start, unit: WORK_CALENDAR_UNIT.MINUTE });
|
|
19732
|
+
const calculationStart = shouldPreserveSourcePrecision ? new Date(start.getTime() - sourceRemainder) : start;
|
|
19733
|
+
const end = fractionalHours ? calculateFractionalEndDate(calendar, calculationStart, durationHours) : calendar.calculateEndDate({
|
|
19734
|
+
start_date: calculationStart,
|
|
19359
19735
|
duration: durationHours,
|
|
19360
19736
|
unit: defaultUnit
|
|
19361
19737
|
});
|
|
19738
|
+
if (!shouldPreserveSourcePrecision) return end;
|
|
19739
|
+
const workingEnd = nextWorkingMinute(calendar, end);
|
|
19740
|
+
return new Date(workingEnd.getTime() + sourceRemainder);
|
|
19362
19741
|
},
|
|
19363
19742
|
calculateDuration(start, end) {
|
|
19364
19743
|
return calendar.calculateDuration({
|
|
@@ -19375,6 +19754,91 @@ function wrapWorkCalendar(calendar, defaultUnit) {
|
|
|
19375
19754
|
}
|
|
19376
19755
|
};
|
|
19377
19756
|
}
|
|
19757
|
+
function calculateFractionalEndDate(calendar, start, durationHours) {
|
|
19758
|
+
const roundedMinutes = Math.round(Math.abs(durationHours) * 60);
|
|
19759
|
+
const shouldUseWorkingPrefixes = roundedMinutes > 0 && calendar.isWorkTime({ date: start, unit: WORK_CALENDAR_UNIT.MINUTE });
|
|
19760
|
+
if (!shouldUseWorkingPrefixes)
|
|
19761
|
+
return calendar.calculateEndDate({
|
|
19762
|
+
start_date: start,
|
|
19763
|
+
duration: durationHours * 60,
|
|
19764
|
+
unit: WORK_CALENDAR_UNIT.MINUTE
|
|
19765
|
+
});
|
|
19766
|
+
let end = start;
|
|
19767
|
+
let remainingMinutes = roundedMinutes;
|
|
19768
|
+
const direction = durationHours < 0 ? -1 : 1;
|
|
19769
|
+
while (remainingMinutes > 0) {
|
|
19770
|
+
const reference = direction < 0 ? new Date(end.getTime() - 1) : end;
|
|
19771
|
+
if (!calendar.isWorkTime({ date: reference, unit: WORK_CALENDAR_UNIT.MINUTE })) {
|
|
19772
|
+
end = direction < 0 ? calendar.getClosestWorkTime({
|
|
19773
|
+
date: reference,
|
|
19774
|
+
dir: WORK_TIME_DIRECTION.PAST,
|
|
19775
|
+
unit: WORK_CALENDAR_UNIT.MINUTE
|
|
19776
|
+
}) : nextWorkingMinute(calendar, end);
|
|
19777
|
+
}
|
|
19778
|
+
const prefix = calculateWorkingMinutePrefix(
|
|
19779
|
+
calendar,
|
|
19780
|
+
end,
|
|
19781
|
+
Math.min(remainingMinutes, 60),
|
|
19782
|
+
direction
|
|
19783
|
+
);
|
|
19784
|
+
end = prefix.end;
|
|
19785
|
+
remainingMinutes -= prefix.minutes;
|
|
19786
|
+
}
|
|
19787
|
+
return end;
|
|
19788
|
+
}
|
|
19789
|
+
function calculateWorkingMinutePrefix(calendar, start, maximumMinutes, direction) {
|
|
19790
|
+
const fullPrefix = calendar.calculateEndDate({
|
|
19791
|
+
start_date: start,
|
|
19792
|
+
duration: direction * maximumMinutes,
|
|
19793
|
+
unit: WORK_CALENDAR_UNIT.MINUTE
|
|
19794
|
+
});
|
|
19795
|
+
const isFullPrefixContiguous = fullPrefix.getTime() === start.getTime() + direction * maximumMinutes * 6e4;
|
|
19796
|
+
if (isFullPrefixContiguous)
|
|
19797
|
+
return { end: fullPrefix, minutes: maximumMinutes };
|
|
19798
|
+
let minutes = 0;
|
|
19799
|
+
let limit = maximumMinutes - 1;
|
|
19800
|
+
let end = start;
|
|
19801
|
+
while (minutes < limit) {
|
|
19802
|
+
const testedMinutes = Math.ceil((minutes + limit) / 2);
|
|
19803
|
+
const candidate = calendar.calculateEndDate({
|
|
19804
|
+
start_date: start,
|
|
19805
|
+
duration: direction * testedMinutes,
|
|
19806
|
+
unit: WORK_CALENDAR_UNIT.MINUTE
|
|
19807
|
+
});
|
|
19808
|
+
const isContiguousWorkingTime = candidate.getTime() === start.getTime() + direction * testedMinutes * 6e4;
|
|
19809
|
+
if (isContiguousWorkingTime) {
|
|
19810
|
+
minutes = testedMinutes;
|
|
19811
|
+
end = candidate;
|
|
19812
|
+
} else {
|
|
19813
|
+
limit = testedMinutes - 1;
|
|
19814
|
+
}
|
|
19815
|
+
}
|
|
19816
|
+
if (minutes === 0)
|
|
19817
|
+
throw new Error("Calendar cannot calculate a working-minute prefix");
|
|
19818
|
+
return { end, minutes };
|
|
19819
|
+
}
|
|
19820
|
+
function nextWorkingMinute(calendar, date2) {
|
|
19821
|
+
if (calendar.isWorkTime({ date: date2, unit: WORK_CALENDAR_UNIT.MINUTE }))
|
|
19822
|
+
return date2;
|
|
19823
|
+
const nextWorkingDay2 = calendar.calculateEndDate({
|
|
19824
|
+
start_date: date2,
|
|
19825
|
+
duration: 1,
|
|
19826
|
+
unit: CALENDAR_UNIT.DAY
|
|
19827
|
+
});
|
|
19828
|
+
const nextDayStart = new Date(nextWorkingDay2);
|
|
19829
|
+
nextDayStart.setUTCHours(0, 0, 0, 0);
|
|
19830
|
+
const nextShiftStart = calendar.getClosestWorkTime({
|
|
19831
|
+
date: nextDayStart,
|
|
19832
|
+
dir: WORK_TIME_DIRECTION.FUTURE,
|
|
19833
|
+
unit: WORK_CALENDAR_UNIT.MINUTE
|
|
19834
|
+
});
|
|
19835
|
+
const closest = calendar.getClosestWorkTime({
|
|
19836
|
+
date: date2,
|
|
19837
|
+
dir: WORK_TIME_DIRECTION.FUTURE,
|
|
19838
|
+
unit: WORK_CALENDAR_UNIT.MINUTE
|
|
19839
|
+
});
|
|
19840
|
+
return new Date(Math.min(closest.getTime(), nextShiftStart.getTime()));
|
|
19841
|
+
}
|
|
19378
19842
|
function isArray(obj) {
|
|
19379
19843
|
if (Array.isArray) {
|
|
19380
19844
|
return Array.isArray(obj);
|
|
@@ -23476,13 +23940,263 @@ function computeProjectWorkHours(calendars) {
|
|
|
23476
23940
|
return { startHour: earliestTime(starts), endHour: latestTime(ends) };
|
|
23477
23941
|
}
|
|
23478
23942
|
|
|
23479
|
-
// src/
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
|
|
23484
|
-
|
|
23485
|
-
|
|
23943
|
+
// src/boundary/backend/constants.ts
|
|
23944
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
23945
|
+
var EXCEPTION_DATE_FORMAT_REGEX = /^(\d{4})[/-](\d{1,2})[/-](\d{1,2})/;
|
|
23946
|
+
var WEEKDAYS_PER_WEEK = 7;
|
|
23947
|
+
var CALENDAR_FALLBACK_WORKING_DAYS = [false, true, true, true, true, true, false];
|
|
23948
|
+
var WORKING_DAY_TRUE_VALUES = /* @__PURE__ */ new Set([
|
|
23949
|
+
"1",
|
|
23950
|
+
"true"
|
|
23951
|
+
]);
|
|
23952
|
+
var DEFAULT_ACTIVITY_CRETER = ACTIVITY_PONDERATOR_CRITERION.DURATION;
|
|
23953
|
+
var DEFAULT_CONSTRAINT_TYPE = CONSTRAINT_TYPE.ASAP;
|
|
23954
|
+
var CONSTRAINT_LABEL_MAP = {
|
|
23955
|
+
ASAP: CONSTRAINT_TYPE.ASAP,
|
|
23956
|
+
ALAP: CONSTRAINT_TYPE.ALAP,
|
|
23957
|
+
SNET: CONSTRAINT_TYPE.SNET,
|
|
23958
|
+
SNLT: CONSTRAINT_TYPE.SNLT,
|
|
23959
|
+
FNET: CONSTRAINT_TYPE.FNET,
|
|
23960
|
+
FNLT: CONSTRAINT_TYPE.FNLT,
|
|
23961
|
+
MSO: CONSTRAINT_TYPE.MSO,
|
|
23962
|
+
MFO: CONSTRAINT_TYPE.MFO,
|
|
23963
|
+
"AS SOON AS POSSIBLE": CONSTRAINT_TYPE.ASAP,
|
|
23964
|
+
"AS LATE AS POSSIBLE": CONSTRAINT_TYPE.ALAP,
|
|
23965
|
+
"START NO EARLIER THAN": CONSTRAINT_TYPE.SNET,
|
|
23966
|
+
"START NO LATER THAN": CONSTRAINT_TYPE.SNLT,
|
|
23967
|
+
"FINISH NO EARLIER THAN": CONSTRAINT_TYPE.FNET,
|
|
23968
|
+
"FINISH NO LATER THAN": CONSTRAINT_TYPE.FNLT,
|
|
23969
|
+
"MUST START ON": CONSTRAINT_TYPE.MSO,
|
|
23970
|
+
"MUST FINISH ON": CONSTRAINT_TYPE.MFO
|
|
23971
|
+
};
|
|
23972
|
+
|
|
23973
|
+
// src/boundary/backend/normalize-calendar.ts
|
|
23974
|
+
function normalizeCalendars(schedulingCalendars, sectorCalendars, rawBaseCalendars, exceptionRange = null) {
|
|
23975
|
+
const hasSchedulingCalendars = schedulingCalendars !== void 0 && schedulingCalendars.length > 0;
|
|
23976
|
+
const source = hasSchedulingCalendars ? schedulingCalendars : sectorCalendars ?? [];
|
|
23977
|
+
const calendars = source.map(
|
|
23978
|
+
(calendar) => parseSchedulingCalendar(calendar, exceptionRange)
|
|
23979
|
+
);
|
|
23980
|
+
const baseCalendars = (rawBaseCalendars ?? []).map(
|
|
23981
|
+
(calendar) => parseBaseCalendar(calendar, exceptionRange)
|
|
23982
|
+
);
|
|
23983
|
+
return {
|
|
23984
|
+
calendars,
|
|
23985
|
+
baseCalendars,
|
|
23986
|
+
knownCalendarIds: new Set(calendars.map((calendar) => calendar.id)),
|
|
23987
|
+
defaultCalendarId: pickDefaultCalendarId(calendars)
|
|
23988
|
+
};
|
|
23989
|
+
}
|
|
23990
|
+
function pickDefaultCalendarId(calendars) {
|
|
23991
|
+
let firstCalendarId = null;
|
|
23992
|
+
for (const calendar of calendars) {
|
|
23993
|
+
firstCalendarId ??= calendar.id;
|
|
23994
|
+
if (calendar.is_default) return calendar.id;
|
|
23995
|
+
}
|
|
23996
|
+
return firstCalendarId;
|
|
23997
|
+
}
|
|
23998
|
+
function buildCalendar(raw, shape) {
|
|
23999
|
+
const workingMask = parseWorkingDays(raw.working_days);
|
|
24000
|
+
const { days, hours } = parseWorktimeDays(raw, workingMask);
|
|
24001
|
+
const dates = parseExceptionDays(
|
|
24002
|
+
shape.exceptionsSource,
|
|
24003
|
+
shape.exceptionRange
|
|
24004
|
+
);
|
|
24005
|
+
const worktime = { days, hours };
|
|
24006
|
+
if (dates) worktime.dates = dates;
|
|
24007
|
+
return {
|
|
24008
|
+
id: `${String(raw.id)}${shape.idSuffix}`,
|
|
24009
|
+
name: raw.name,
|
|
24010
|
+
is_default: shape.isDefault,
|
|
24011
|
+
...shape.baseDefault !== void 0 ? { baseDefault: shape.baseDefault } : {},
|
|
24012
|
+
worktime
|
|
24013
|
+
};
|
|
24014
|
+
}
|
|
24015
|
+
function parseSchedulingCalendar(raw, exceptionRange) {
|
|
24016
|
+
const rawBaseDefault = raw.baseDefault;
|
|
24017
|
+
const baseDefault = typeof rawBaseDefault === "boolean" ? rawBaseDefault : void 0;
|
|
24018
|
+
return buildCalendar(raw, {
|
|
24019
|
+
idSuffix: "",
|
|
24020
|
+
isDefault: Boolean(raw.is_default),
|
|
24021
|
+
baseDefault,
|
|
24022
|
+
exceptionsSource: raw.exceptiondays,
|
|
24023
|
+
exceptionRange
|
|
24024
|
+
});
|
|
24025
|
+
}
|
|
24026
|
+
function parseBaseCalendar(raw, exceptionRange) {
|
|
24027
|
+
return buildCalendar(raw, {
|
|
24028
|
+
idSuffix: "-base",
|
|
24029
|
+
isDefault: false,
|
|
24030
|
+
baseDefault: Boolean(raw.is_default),
|
|
24031
|
+
exceptionsSource: raw.baseexceptiondays ?? raw.exceptiondays,
|
|
24032
|
+
exceptionRange
|
|
24033
|
+
});
|
|
24034
|
+
}
|
|
24035
|
+
function parseExceptionDays(exceptions, range) {
|
|
24036
|
+
if (!Array.isArray(exceptions) || exceptions.length === 0) return null;
|
|
24037
|
+
const map = {};
|
|
24038
|
+
for (const exception of exceptions) {
|
|
24039
|
+
if (!exception) continue;
|
|
24040
|
+
const rangeKeys = expandRangeToUtcKeys(
|
|
24041
|
+
exception.from_date,
|
|
24042
|
+
exception.to_date,
|
|
24043
|
+
range
|
|
24044
|
+
);
|
|
24045
|
+
if (rangeKeys.length === 0) continue;
|
|
24046
|
+
const value = exceptionToDateValue(exception);
|
|
24047
|
+
if (value === void 0) continue;
|
|
24048
|
+
for (const key of rangeKeys) map[key] = value;
|
|
24049
|
+
}
|
|
24050
|
+
return Object.keys(map).length > 0 ? map : null;
|
|
24051
|
+
}
|
|
24052
|
+
function expandRangeToUtcKeys(from, to, range) {
|
|
24053
|
+
const fromTs = parseExceptionDate(from);
|
|
24054
|
+
const toTs = parseExceptionDate(to);
|
|
24055
|
+
if (fromTs === null || toTs === null) return [];
|
|
24056
|
+
if (range && (toTs < range.start || fromTs > range.end)) return [];
|
|
24057
|
+
const keys = [];
|
|
24058
|
+
let cursor = range ? Math.max(fromTs, range.start) : fromTs;
|
|
24059
|
+
const last = range ? Math.min(toTs, range.end) : toTs;
|
|
24060
|
+
while (cursor <= last) {
|
|
24061
|
+
keys.push(String(cursor));
|
|
24062
|
+
cursor += DAY_MS;
|
|
24063
|
+
}
|
|
24064
|
+
return keys;
|
|
24065
|
+
}
|
|
24066
|
+
function parseExceptionDate(raw) {
|
|
24067
|
+
if (typeof raw !== "string") return null;
|
|
24068
|
+
const match = EXCEPTION_DATE_FORMAT_REGEX.exec(raw.trim());
|
|
24069
|
+
if (!match) return null;
|
|
24070
|
+
const year = Number(match[1]);
|
|
24071
|
+
const month = Number(match[2]);
|
|
24072
|
+
const day = Number(match[3]);
|
|
24073
|
+
const isValidDate4 = Number.isFinite(year) && Number.isFinite(month) && Number.isFinite(day);
|
|
24074
|
+
if (!isValidDate4) return null;
|
|
24075
|
+
return Date.UTC(year, month - 1, day);
|
|
24076
|
+
}
|
|
24077
|
+
function exceptionToDateValue(exception) {
|
|
24078
|
+
if (exception.workable === false) return false;
|
|
24079
|
+
const shifts = exception.shifts;
|
|
24080
|
+
if (Array.isArray(shifts) && shifts.length > 0) {
|
|
24081
|
+
const windows = extractExceptionWindows(shifts);
|
|
24082
|
+
if (windows.length > 0) return windows;
|
|
24083
|
+
}
|
|
24084
|
+
return void 0;
|
|
24085
|
+
}
|
|
24086
|
+
var TIME_OF_DAY = /^\d{1,2}:\d{2}$/;
|
|
24087
|
+
function extractExceptionWindows(shifts) {
|
|
24088
|
+
const windows = [];
|
|
24089
|
+
for (const shift of shifts) {
|
|
24090
|
+
const csvs = splitShiftString(shift?.shift_string);
|
|
24091
|
+
if (!csvs) continue;
|
|
24092
|
+
const isCompactWindow = !csvs.startsCsv.includes(",") && !csvs.endsCsv.includes(",");
|
|
24093
|
+
if (isCompactWindow) {
|
|
24094
|
+
const start = csvs.startsCsv.trim();
|
|
24095
|
+
const end = csvs.endsCsv.trim();
|
|
24096
|
+
const isUsableWindow = !isMissingValue(start) && !isMissingValue(end) && TIME_OF_DAY.test(start) && TIME_OF_DAY.test(end);
|
|
24097
|
+
if (isUsableWindow) windows.push(`${start}-${end}`);
|
|
24098
|
+
continue;
|
|
24099
|
+
}
|
|
24100
|
+
const perDay = shiftCsvToPerDayWindows(csvs.startsCsv, csvs.endsCsv);
|
|
24101
|
+
const firstWindow = perDay.find((window2) => window2 !== null);
|
|
24102
|
+
if (firstWindow) windows.push(firstWindow);
|
|
24103
|
+
}
|
|
24104
|
+
windows.sort(compareWindowByStart);
|
|
24105
|
+
return windows;
|
|
24106
|
+
}
|
|
24107
|
+
function windowStartMinutes(window2) {
|
|
24108
|
+
const dash = window2.indexOf("-");
|
|
24109
|
+
const start = dash > 0 ? window2.slice(0, dash) : window2;
|
|
24110
|
+
const [hour, minute] = start.split(":");
|
|
24111
|
+
return (Number(hour) || 0) * 60 + (Number(minute) || 0);
|
|
24112
|
+
}
|
|
24113
|
+
function compareWindowByStart(first, second) {
|
|
24114
|
+
return windowStartMinutes(first) - windowStartMinutes(second);
|
|
24115
|
+
}
|
|
24116
|
+
function parseWorktimeDays(raw, workingMask) {
|
|
24117
|
+
const windowsByDay = Array.from({ length: 7 }, () => []);
|
|
24118
|
+
const addCsvPair = (startsCsv, endsCsv) => {
|
|
24119
|
+
const perDay = shiftCsvToPerDayWindows(startsCsv, endsCsv);
|
|
24120
|
+
for (let i = 0; i < 7; i += 1) {
|
|
24121
|
+
const window2 = perDay[i];
|
|
24122
|
+
const dayWindows = windowsByDay[i];
|
|
24123
|
+
if (window2 && dayWindows) dayWindows.push(window2);
|
|
24124
|
+
}
|
|
24125
|
+
};
|
|
24126
|
+
const hasNestedShifts = Array.isArray(raw.shifts) && raw.shifts.length > 0;
|
|
24127
|
+
const hasFlatShift = typeof raw.shift_start === "string" && typeof raw.shift_end === "string";
|
|
24128
|
+
if (hasNestedShifts) {
|
|
24129
|
+
for (const shift of raw.shifts) {
|
|
24130
|
+
const csvs = splitShiftString(shift?.shift_string);
|
|
24131
|
+
if (csvs) addCsvPair(csvs.startsCsv, csvs.endsCsv);
|
|
24132
|
+
}
|
|
24133
|
+
} else if (hasFlatShift) {
|
|
24134
|
+
addCsvPair(raw.shift_start, raw.shift_end);
|
|
24135
|
+
}
|
|
24136
|
+
for (const dayWindows of windowsByDay) dayWindows.sort(compareWindowByStart);
|
|
24137
|
+
const firstWorkingWindows = windowsByDay.find(
|
|
24138
|
+
(windows, i) => Boolean(workingMask[i]) && windows.length > 0
|
|
24139
|
+
);
|
|
24140
|
+
const defaultHours = firstWorkingWindows ?? [];
|
|
24141
|
+
const days = workingMask.map((isWorking, i) => {
|
|
24142
|
+
if (!isWorking) return false;
|
|
24143
|
+
const dayWindows = windowsByDay[i];
|
|
24144
|
+
return dayWindows && dayWindows.length > 0 ? dayWindows : defaultHours;
|
|
24145
|
+
});
|
|
24146
|
+
return { days, hours: defaultHours };
|
|
24147
|
+
}
|
|
24148
|
+
function shiftCsvToPerDayWindows(startsCsv, endsCsv) {
|
|
24149
|
+
const empty = Array.from(
|
|
24150
|
+
{ length: WEEKDAYS_PER_WEEK },
|
|
24151
|
+
() => null
|
|
24152
|
+
);
|
|
24153
|
+
if (typeof startsCsv !== "string" || typeof endsCsv !== "string") {
|
|
24154
|
+
return empty;
|
|
24155
|
+
}
|
|
24156
|
+
const starts = startsCsv.split(",").map((value) => value.trim());
|
|
24157
|
+
const ends = endsCsv.split(",").map((value) => value.trim());
|
|
24158
|
+
const isAlignedWeek = starts.length === ends.length && starts.length > 0 && starts.length <= WEEKDAYS_PER_WEEK;
|
|
24159
|
+
if (!isAlignedWeek) return empty;
|
|
24160
|
+
const windows = [...empty];
|
|
24161
|
+
for (let i = 0; i < starts.length; i += 1) {
|
|
24162
|
+
const start = starts[i];
|
|
24163
|
+
const end = ends[i];
|
|
24164
|
+
if (isMissingValue(start) || isMissingValue(end)) continue;
|
|
24165
|
+
windows[i] = `${start}-${end}`;
|
|
24166
|
+
}
|
|
24167
|
+
return windows;
|
|
24168
|
+
}
|
|
24169
|
+
function splitShiftString(raw) {
|
|
24170
|
+
if (typeof raw !== "string") return null;
|
|
24171
|
+
const dash = raw.indexOf("-");
|
|
24172
|
+
if (dash <= 0) return null;
|
|
24173
|
+
return {
|
|
24174
|
+
startsCsv: raw.slice(0, dash),
|
|
24175
|
+
endsCsv: raw.slice(dash + 1)
|
|
24176
|
+
};
|
|
24177
|
+
}
|
|
24178
|
+
function parseWorkingDays(csv) {
|
|
24179
|
+
if (typeof csv !== "string") return [...CALENDAR_FALLBACK_WORKING_DAYS];
|
|
24180
|
+
const parts = csv.split(",").map((value) => value.trim());
|
|
24181
|
+
if (parts.length !== WEEKDAYS_PER_WEEK) {
|
|
24182
|
+
return [...CALENDAR_FALLBACK_WORKING_DAYS];
|
|
24183
|
+
}
|
|
24184
|
+
return parts.map((part) => WORKING_DAY_TRUE_VALUES.has(part));
|
|
24185
|
+
}
|
|
24186
|
+
var MISSING_SHIFT_VALUES = /* @__PURE__ */ new Set(["false", "null"]);
|
|
24187
|
+
function isMissingValue(value) {
|
|
24188
|
+
return !value || MISSING_SHIFT_VALUES.has(value);
|
|
24189
|
+
}
|
|
24190
|
+
|
|
24191
|
+
// src/internal/state/pipeline-context.ts
|
|
24192
|
+
function buildPipelineContext(adapter, options) {
|
|
24193
|
+
return {
|
|
24194
|
+
calendars: adapter.calendarReader,
|
|
24195
|
+
hasCalendar: (calendarId) => options.calendars.has(calendarId),
|
|
24196
|
+
getDefaultCalendarId: () => pickDefaultCalendarId(options.calendars.values()),
|
|
24197
|
+
hierarchy: createStateBackedHierarchyReader(adapter),
|
|
24198
|
+
activityReader: createStateBackedActivityReader(adapter),
|
|
24199
|
+
dateFormat: options.dateFormat ?? "YYYY-MM-DD",
|
|
23486
24200
|
customHours: options.customHours ?? {},
|
|
23487
24201
|
hoursPerDay: options.hoursPerDay
|
|
23488
24202
|
};
|
|
@@ -24765,36 +25479,6 @@ var SaveTracker = class _SaveTracker {
|
|
|
24765
25479
|
}
|
|
24766
25480
|
};
|
|
24767
25481
|
|
|
24768
|
-
// src/boundary/backend/constants.ts
|
|
24769
|
-
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
24770
|
-
var EXCEPTION_DATE_FORMAT_REGEX = /^(\d{4})[/-](\d{1,2})[/-](\d{1,2})/;
|
|
24771
|
-
var WEEKDAYS_PER_WEEK = 7;
|
|
24772
|
-
var CALENDAR_FALLBACK_WORKING_DAYS = [false, true, true, true, true, true, false];
|
|
24773
|
-
var WORKING_DAY_TRUE_VALUES = /* @__PURE__ */ new Set([
|
|
24774
|
-
"1",
|
|
24775
|
-
"true"
|
|
24776
|
-
]);
|
|
24777
|
-
var DEFAULT_ACTIVITY_CRETER = ACTIVITY_PONDERATOR_CRITERION.DURATION;
|
|
24778
|
-
var DEFAULT_CONSTRAINT_TYPE = CONSTRAINT_TYPE.ASAP;
|
|
24779
|
-
var CONSTRAINT_LABEL_MAP = {
|
|
24780
|
-
ASAP: CONSTRAINT_TYPE.ASAP,
|
|
24781
|
-
ALAP: CONSTRAINT_TYPE.ALAP,
|
|
24782
|
-
SNET: CONSTRAINT_TYPE.SNET,
|
|
24783
|
-
SNLT: CONSTRAINT_TYPE.SNLT,
|
|
24784
|
-
FNET: CONSTRAINT_TYPE.FNET,
|
|
24785
|
-
FNLT: CONSTRAINT_TYPE.FNLT,
|
|
24786
|
-
MSO: CONSTRAINT_TYPE.MSO,
|
|
24787
|
-
MFO: CONSTRAINT_TYPE.MFO,
|
|
24788
|
-
"AS SOON AS POSSIBLE": CONSTRAINT_TYPE.ASAP,
|
|
24789
|
-
"AS LATE AS POSSIBLE": CONSTRAINT_TYPE.ALAP,
|
|
24790
|
-
"START NO EARLIER THAN": CONSTRAINT_TYPE.SNET,
|
|
24791
|
-
"START NO LATER THAN": CONSTRAINT_TYPE.SNLT,
|
|
24792
|
-
"FINISH NO EARLIER THAN": CONSTRAINT_TYPE.FNET,
|
|
24793
|
-
"FINISH NO LATER THAN": CONSTRAINT_TYPE.FNLT,
|
|
24794
|
-
"MUST START ON": CONSTRAINT_TYPE.MSO,
|
|
24795
|
-
"MUST FINISH ON": CONSTRAINT_TYPE.MFO
|
|
24796
|
-
};
|
|
24797
|
-
|
|
24798
25482
|
// src/boundary/backend/normalize-activity.ts
|
|
24799
25483
|
function normalizeActivities(rawActivities, context) {
|
|
24800
25484
|
const hiddenIds = [];
|
|
@@ -24913,332 +25597,115 @@ function parseBaselinePoints(raw, reporter, recordDiagnostic, activityId, isMile
|
|
|
24913
25597
|
record: recordDiagnostic,
|
|
24914
25598
|
sampleId: activityId
|
|
24915
25599
|
},
|
|
24916
|
-
isMilestone
|
|
24917
|
-
)
|
|
24918
|
-
).filter((point) => point !== null);
|
|
24919
|
-
}
|
|
24920
|
-
function parseActivity(raw, context) {
|
|
24921
|
-
const calendarId = resolveCalendarId2(
|
|
24922
|
-
raw.calendarId,
|
|
24923
|
-
context.knownCalendarIds,
|
|
24924
|
-
context.defaultCalendarId
|
|
24925
|
-
);
|
|
24926
|
-
const inheritedDefaultCalendar = raw.calendarId == null || !context.knownCalendarIds.has(String(raw.calendarId));
|
|
24927
|
-
const progress = parseProgress(
|
|
24928
|
-
raw.progress,
|
|
24929
|
-
context.reporter,
|
|
24930
|
-
context.recordDiagnostic,
|
|
24931
|
-
String(raw.unique_id)
|
|
24932
|
-
);
|
|
24933
|
-
const { constraintType, constraintDate } = resolveConstraint(
|
|
24934
|
-
raw,
|
|
24935
|
-
context.reporter,
|
|
24936
|
-
context.recordDiagnostic
|
|
24937
|
-
);
|
|
24938
|
-
const isMilestone = raw.type === ACTIVITY_TYPE.MILESTONE;
|
|
24939
|
-
const startDate = parseBackendDate(raw.start_date);
|
|
24940
|
-
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
24941
|
-
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
24942
|
-
const isCritical = parseCriticality(raw, context.reporter);
|
|
24943
|
-
return {
|
|
24944
|
-
id: String(raw.unique_id),
|
|
24945
|
-
proplannerId: normalizePersistedActivityId(raw.id),
|
|
24946
|
-
parentId: parseParentId(raw.parent_id),
|
|
24947
|
-
name: raw.name,
|
|
24948
|
-
description: raw.description ?? "",
|
|
24949
|
-
type: raw.type,
|
|
24950
|
-
startDate,
|
|
24951
|
-
endDate,
|
|
24952
|
-
durationHours: durationToHours(durationDays, context.hoursPerDay),
|
|
24953
|
-
constraintType,
|
|
24954
|
-
constraintDate,
|
|
24955
|
-
autoScheduling: isAutoSchedulingEnabledForProgress(progress),
|
|
24956
|
-
calendarId,
|
|
24957
|
-
assignedDefaultCalendar: calendarId != null && inheritedDefaultCalendar,
|
|
24958
|
-
dateOrigin: raw.date_origin == null ? null : parseBackendDate(raw.date_origin),
|
|
24959
|
-
progress,
|
|
24960
|
-
cost: Number(raw.cost),
|
|
24961
|
-
usedCost: Number(raw.used_cost ?? 0),
|
|
24962
|
-
realCost: Number(raw.real_cost ?? 0),
|
|
24963
|
-
workHours: Number(raw.hhWorkTime),
|
|
24964
|
-
realWorkHours: Number(raw.real_work ?? 0),
|
|
24965
|
-
ponderator: Number(raw.ponderator ?? 0),
|
|
24966
|
-
hasCustomPonderator: raw.hasCustomPonderator,
|
|
24967
|
-
correlativeId: raw.correlative_id,
|
|
24968
|
-
uniqueCorrelativeId: raw.unique_correlative_id,
|
|
24969
|
-
customId: normalizeCustomId(raw),
|
|
24970
|
-
subcontractId: raw.subcontractId,
|
|
24971
|
-
isLookahead: raw.isOnLookahead,
|
|
24972
|
-
hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
|
|
24973
|
-
newActivityIds: parseNewActivityIds(raw.newActivitiesArray),
|
|
24974
|
-
hasNewActivities: raw.hasNewActivities ?? false,
|
|
24975
|
-
...raw.isNewActivity === true ? { isNewActivity: true } : {},
|
|
24976
|
-
pendingRequestIds: parseIdList(raw.pendingRequests),
|
|
24977
|
-
responsableIds: parseIdList(raw.responsables),
|
|
24978
|
-
tagIds: parseIdList(raw.tags),
|
|
24979
|
-
baselinePoints: parseBaselinePoints(
|
|
24980
|
-
raw.baseline_points,
|
|
24981
|
-
context.reporter,
|
|
24982
|
-
context.recordDiagnostic,
|
|
24983
|
-
String(raw.unique_id),
|
|
24984
|
-
isMilestone
|
|
24985
|
-
),
|
|
24986
|
-
baselineSnapshot: null,
|
|
24987
|
-
baselineVarianceDays: null,
|
|
24988
|
-
recursiveDurationHours: durationToHours(
|
|
24989
|
-
Number(raw.sumOfDurationRecursively ?? 0),
|
|
24990
|
-
context.hoursPerDay
|
|
24991
|
-
),
|
|
24992
|
-
calendarDuration: computeCalendarDuration({
|
|
24993
|
-
type: raw.type,
|
|
24994
|
-
startDate,
|
|
24995
|
-
endDate
|
|
24996
|
-
}),
|
|
24997
|
-
isCritical,
|
|
24998
|
-
freeSlackHours: null,
|
|
24999
|
-
expectedProgress: null,
|
|
25000
|
-
expectedProgressBaseline: null,
|
|
25001
|
-
status: null,
|
|
25002
|
-
criticalPath: null
|
|
25003
|
-
};
|
|
25004
|
-
}
|
|
25005
|
-
function normalizeCustomId(raw) {
|
|
25006
|
-
return raw.has_childs ? null : raw.custom_id;
|
|
25007
|
-
}
|
|
25008
|
-
function forceRootProjectToRoot(activities) {
|
|
25009
|
-
const rootProject = activities.find(
|
|
25010
|
-
(activity) => activity.correlativeId === 0 && activity.type === ACTIVITY_TYPE.PROJECT
|
|
25011
|
-
);
|
|
25012
|
-
if (rootProject) rootProject.parentId = null;
|
|
25013
|
-
}
|
|
25014
|
-
function reparentOrphansToRoot(activities, ids, reporter, recordDiagnostic) {
|
|
25015
|
-
for (const activity of activities) {
|
|
25016
|
-
if (activity.parentId === null) continue;
|
|
25017
|
-
if (!ids.has(activity.parentId)) {
|
|
25018
|
-
reporter.warn(
|
|
25019
|
-
`[parseFromBackend] activity "${activity.id}" references unknown parent "${activity.parentId}" \u2014 re-parenting to root (null)`
|
|
25020
|
-
);
|
|
25021
|
-
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.ORPHAN_ACTIVITY, activity.id);
|
|
25022
|
-
activity.parentId = null;
|
|
25023
|
-
}
|
|
25024
|
-
}
|
|
25025
|
-
}
|
|
25026
|
-
|
|
25027
|
-
// src/boundary/backend/normalize-calendar.ts
|
|
25028
|
-
function normalizeCalendars(schedulingCalendars, sectorCalendars, rawBaseCalendars, exceptionRange = null) {
|
|
25029
|
-
const hasSchedulingCalendars = schedulingCalendars !== void 0 && schedulingCalendars.length > 0;
|
|
25030
|
-
const source = hasSchedulingCalendars ? schedulingCalendars : sectorCalendars ?? [];
|
|
25031
|
-
const calendars = source.map(
|
|
25032
|
-
(calendar) => parseSchedulingCalendar(calendar, exceptionRange)
|
|
25033
|
-
);
|
|
25034
|
-
const baseCalendars = (rawBaseCalendars ?? []).map(
|
|
25035
|
-
(calendar) => parseBaseCalendar(calendar, exceptionRange)
|
|
25036
|
-
);
|
|
25037
|
-
return {
|
|
25038
|
-
calendars,
|
|
25039
|
-
baseCalendars,
|
|
25040
|
-
knownCalendarIds: new Set(calendars.map((calendar) => calendar.id)),
|
|
25041
|
-
defaultCalendarId: pickDefaultCalendarId(calendars)
|
|
25042
|
-
};
|
|
25043
|
-
}
|
|
25044
|
-
function pickDefaultCalendarId(calendars) {
|
|
25045
|
-
const flaggedDefault = calendars.find((calendar) => calendar.is_default);
|
|
25046
|
-
if (flaggedDefault) return flaggedDefault.id;
|
|
25047
|
-
const firstCalendar = calendars[0];
|
|
25048
|
-
if (firstCalendar) return firstCalendar.id;
|
|
25049
|
-
return null;
|
|
25050
|
-
}
|
|
25051
|
-
function buildCalendar(raw, shape) {
|
|
25052
|
-
const workingMask = parseWorkingDays(raw.working_days);
|
|
25053
|
-
const { days, hours } = parseWorktimeDays(raw, workingMask);
|
|
25054
|
-
const dates = parseExceptionDays(
|
|
25055
|
-
shape.exceptionsSource,
|
|
25056
|
-
shape.exceptionRange
|
|
25057
|
-
);
|
|
25058
|
-
const worktime = { days, hours };
|
|
25059
|
-
if (dates) worktime.dates = dates;
|
|
25060
|
-
return {
|
|
25061
|
-
id: `${String(raw.id)}${shape.idSuffix}`,
|
|
25062
|
-
name: raw.name,
|
|
25063
|
-
is_default: shape.isDefault,
|
|
25064
|
-
...shape.baseDefault !== void 0 ? { baseDefault: shape.baseDefault } : {},
|
|
25065
|
-
worktime
|
|
25066
|
-
};
|
|
25067
|
-
}
|
|
25068
|
-
function parseSchedulingCalendar(raw, exceptionRange) {
|
|
25069
|
-
const rawBaseDefault = raw.baseDefault;
|
|
25070
|
-
const baseDefault = typeof rawBaseDefault === "boolean" ? rawBaseDefault : void 0;
|
|
25071
|
-
return buildCalendar(raw, {
|
|
25072
|
-
idSuffix: "",
|
|
25073
|
-
isDefault: Boolean(raw.is_default),
|
|
25074
|
-
baseDefault,
|
|
25075
|
-
exceptionsSource: raw.exceptiondays,
|
|
25076
|
-
exceptionRange
|
|
25077
|
-
});
|
|
25078
|
-
}
|
|
25079
|
-
function parseBaseCalendar(raw, exceptionRange) {
|
|
25080
|
-
return buildCalendar(raw, {
|
|
25081
|
-
idSuffix: "-base",
|
|
25082
|
-
isDefault: false,
|
|
25083
|
-
baseDefault: Boolean(raw.is_default),
|
|
25084
|
-
exceptionsSource: raw.baseexceptiondays ?? raw.exceptiondays,
|
|
25085
|
-
exceptionRange
|
|
25086
|
-
});
|
|
25087
|
-
}
|
|
25088
|
-
function parseExceptionDays(exceptions, range) {
|
|
25089
|
-
if (!Array.isArray(exceptions) || exceptions.length === 0) return null;
|
|
25090
|
-
const map = {};
|
|
25091
|
-
for (const exception of exceptions) {
|
|
25092
|
-
if (!exception) continue;
|
|
25093
|
-
const rangeKeys = expandRangeToUtcKeys(
|
|
25094
|
-
exception.from_date,
|
|
25095
|
-
exception.to_date,
|
|
25096
|
-
range
|
|
25097
|
-
);
|
|
25098
|
-
if (rangeKeys.length === 0) continue;
|
|
25099
|
-
const value = exceptionToDateValue(exception);
|
|
25100
|
-
if (value === void 0) continue;
|
|
25101
|
-
for (const key of rangeKeys) map[key] = value;
|
|
25102
|
-
}
|
|
25103
|
-
return Object.keys(map).length > 0 ? map : null;
|
|
25104
|
-
}
|
|
25105
|
-
function expandRangeToUtcKeys(from, to, range) {
|
|
25106
|
-
const fromTs = parseExceptionDate(from);
|
|
25107
|
-
const toTs = parseExceptionDate(to);
|
|
25108
|
-
if (fromTs === null || toTs === null) return [];
|
|
25109
|
-
if (range && (toTs < range.start || fromTs > range.end)) return [];
|
|
25110
|
-
const keys = [];
|
|
25111
|
-
let cursor = range ? Math.max(fromTs, range.start) : fromTs;
|
|
25112
|
-
const last = range ? Math.min(toTs, range.end) : toTs;
|
|
25113
|
-
while (cursor <= last) {
|
|
25114
|
-
keys.push(String(cursor));
|
|
25115
|
-
cursor += DAY_MS;
|
|
25116
|
-
}
|
|
25117
|
-
return keys;
|
|
25118
|
-
}
|
|
25119
|
-
function parseExceptionDate(raw) {
|
|
25120
|
-
if (typeof raw !== "string") return null;
|
|
25121
|
-
const match = EXCEPTION_DATE_FORMAT_REGEX.exec(raw.trim());
|
|
25122
|
-
if (!match) return null;
|
|
25123
|
-
const year = Number(match[1]);
|
|
25124
|
-
const month = Number(match[2]);
|
|
25125
|
-
const day = Number(match[3]);
|
|
25126
|
-
const isValidDate4 = Number.isFinite(year) && Number.isFinite(month) && Number.isFinite(day);
|
|
25127
|
-
if (!isValidDate4) return null;
|
|
25128
|
-
return Date.UTC(year, month - 1, day);
|
|
25129
|
-
}
|
|
25130
|
-
function exceptionToDateValue(exception) {
|
|
25131
|
-
if (exception.workable === false) return false;
|
|
25132
|
-
const shifts = exception.shifts;
|
|
25133
|
-
if (Array.isArray(shifts) && shifts.length > 0) {
|
|
25134
|
-
const windows = extractExceptionWindows(shifts);
|
|
25135
|
-
if (windows.length > 0) return windows;
|
|
25136
|
-
}
|
|
25137
|
-
return void 0;
|
|
25138
|
-
}
|
|
25139
|
-
var TIME_OF_DAY = /^\d{1,2}:\d{2}$/;
|
|
25140
|
-
function extractExceptionWindows(shifts) {
|
|
25141
|
-
const windows = [];
|
|
25142
|
-
for (const shift of shifts) {
|
|
25143
|
-
const csvs = splitShiftString(shift?.shift_string);
|
|
25144
|
-
if (!csvs) continue;
|
|
25145
|
-
const isCompactWindow = !csvs.startsCsv.includes(",") && !csvs.endsCsv.includes(",");
|
|
25146
|
-
if (isCompactWindow) {
|
|
25147
|
-
const start = csvs.startsCsv.trim();
|
|
25148
|
-
const end = csvs.endsCsv.trim();
|
|
25149
|
-
const isUsableWindow = !isMissingValue(start) && !isMissingValue(end) && TIME_OF_DAY.test(start) && TIME_OF_DAY.test(end);
|
|
25150
|
-
if (isUsableWindow) windows.push(`${start}-${end}`);
|
|
25151
|
-
continue;
|
|
25152
|
-
}
|
|
25153
|
-
const perDay = shiftCsvToPerDayWindows(csvs.startsCsv, csvs.endsCsv);
|
|
25154
|
-
const firstWindow = perDay.find((window2) => window2 !== null);
|
|
25155
|
-
if (firstWindow) windows.push(firstWindow);
|
|
25156
|
-
}
|
|
25157
|
-
windows.sort(compareWindowByStart);
|
|
25158
|
-
return windows;
|
|
25159
|
-
}
|
|
25160
|
-
function windowStartMinutes(window2) {
|
|
25161
|
-
const dash = window2.indexOf("-");
|
|
25162
|
-
const start = dash > 0 ? window2.slice(0, dash) : window2;
|
|
25163
|
-
const [hour, minute] = start.split(":");
|
|
25164
|
-
return (Number(hour) || 0) * 60 + (Number(minute) || 0);
|
|
25165
|
-
}
|
|
25166
|
-
function compareWindowByStart(first, second) {
|
|
25167
|
-
return windowStartMinutes(first) - windowStartMinutes(second);
|
|
25168
|
-
}
|
|
25169
|
-
function parseWorktimeDays(raw, workingMask) {
|
|
25170
|
-
const windowsByDay = Array.from({ length: 7 }, () => []);
|
|
25171
|
-
const addCsvPair = (startsCsv, endsCsv) => {
|
|
25172
|
-
const perDay = shiftCsvToPerDayWindows(startsCsv, endsCsv);
|
|
25173
|
-
for (let i = 0; i < 7; i += 1) {
|
|
25174
|
-
const window2 = perDay[i];
|
|
25175
|
-
const dayWindows = windowsByDay[i];
|
|
25176
|
-
if (window2 && dayWindows) dayWindows.push(window2);
|
|
25177
|
-
}
|
|
25178
|
-
};
|
|
25179
|
-
const hasNestedShifts = Array.isArray(raw.shifts) && raw.shifts.length > 0;
|
|
25180
|
-
const hasFlatShift = typeof raw.shift_start === "string" && typeof raw.shift_end === "string";
|
|
25181
|
-
if (hasNestedShifts) {
|
|
25182
|
-
for (const shift of raw.shifts) {
|
|
25183
|
-
const csvs = splitShiftString(shift?.shift_string);
|
|
25184
|
-
if (csvs) addCsvPair(csvs.startsCsv, csvs.endsCsv);
|
|
25185
|
-
}
|
|
25186
|
-
} else if (hasFlatShift) {
|
|
25187
|
-
addCsvPair(raw.shift_start, raw.shift_end);
|
|
25188
|
-
}
|
|
25189
|
-
for (const dayWindows of windowsByDay) dayWindows.sort(compareWindowByStart);
|
|
25190
|
-
const firstWorkingWindows = windowsByDay.find(
|
|
25191
|
-
(windows, i) => Boolean(workingMask[i]) && windows.length > 0
|
|
25192
|
-
);
|
|
25193
|
-
const defaultHours = firstWorkingWindows ?? [];
|
|
25194
|
-
const days = workingMask.map((isWorking, i) => {
|
|
25195
|
-
if (!isWorking) return false;
|
|
25196
|
-
const dayWindows = windowsByDay[i];
|
|
25197
|
-
return dayWindows && dayWindows.length > 0 ? dayWindows : defaultHours;
|
|
25198
|
-
});
|
|
25199
|
-
return { days, hours: defaultHours };
|
|
25200
|
-
}
|
|
25201
|
-
function shiftCsvToPerDayWindows(startsCsv, endsCsv) {
|
|
25202
|
-
const empty = Array.from(
|
|
25203
|
-
{ length: WEEKDAYS_PER_WEEK },
|
|
25204
|
-
() => null
|
|
25205
|
-
);
|
|
25206
|
-
if (typeof startsCsv !== "string" || typeof endsCsv !== "string") {
|
|
25207
|
-
return empty;
|
|
25208
|
-
}
|
|
25209
|
-
const starts = startsCsv.split(",").map((value) => value.trim());
|
|
25210
|
-
const ends = endsCsv.split(",").map((value) => value.trim());
|
|
25211
|
-
const isAlignedWeek = starts.length === ends.length && starts.length > 0 && starts.length <= WEEKDAYS_PER_WEEK;
|
|
25212
|
-
if (!isAlignedWeek) return empty;
|
|
25213
|
-
const windows = [...empty];
|
|
25214
|
-
for (let i = 0; i < starts.length; i += 1) {
|
|
25215
|
-
const start = starts[i];
|
|
25216
|
-
const end = ends[i];
|
|
25217
|
-
if (isMissingValue(start) || isMissingValue(end)) continue;
|
|
25218
|
-
windows[i] = `${start}-${end}`;
|
|
25219
|
-
}
|
|
25220
|
-
return windows;
|
|
25600
|
+
isMilestone
|
|
25601
|
+
)
|
|
25602
|
+
).filter((point) => point !== null);
|
|
25221
25603
|
}
|
|
25222
|
-
function
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
|
|
25604
|
+
function parseActivity(raw, context) {
|
|
25605
|
+
const calendarId = resolveCalendarId2(
|
|
25606
|
+
raw.calendarId,
|
|
25607
|
+
context.knownCalendarIds,
|
|
25608
|
+
context.defaultCalendarId
|
|
25609
|
+
);
|
|
25610
|
+
const inheritedDefaultCalendar = raw.calendarId == null || !context.knownCalendarIds.has(String(raw.calendarId));
|
|
25611
|
+
const progress = parseProgress(
|
|
25612
|
+
raw.progress,
|
|
25613
|
+
context.reporter,
|
|
25614
|
+
context.recordDiagnostic,
|
|
25615
|
+
String(raw.unique_id)
|
|
25616
|
+
);
|
|
25617
|
+
const { constraintType, constraintDate } = resolveConstraint(
|
|
25618
|
+
raw,
|
|
25619
|
+
context.reporter,
|
|
25620
|
+
context.recordDiagnostic
|
|
25621
|
+
);
|
|
25622
|
+
const isMilestone = raw.type === ACTIVITY_TYPE.MILESTONE;
|
|
25623
|
+
const startDate = parseBackendDate(raw.start_date);
|
|
25624
|
+
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
25625
|
+
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
25626
|
+
const isCritical = parseCriticality(raw, context.reporter);
|
|
25226
25627
|
return {
|
|
25227
|
-
|
|
25228
|
-
|
|
25628
|
+
id: String(raw.unique_id),
|
|
25629
|
+
proplannerId: normalizePersistedActivityId(raw.id),
|
|
25630
|
+
parentId: parseParentId(raw.parent_id),
|
|
25631
|
+
name: raw.name,
|
|
25632
|
+
description: raw.description ?? "",
|
|
25633
|
+
type: raw.type,
|
|
25634
|
+
startDate,
|
|
25635
|
+
endDate,
|
|
25636
|
+
durationHours: durationToHours(durationDays, context.hoursPerDay),
|
|
25637
|
+
constraintType,
|
|
25638
|
+
constraintDate,
|
|
25639
|
+
autoScheduling: isAutoSchedulingEnabledForProgress(progress),
|
|
25640
|
+
calendarId,
|
|
25641
|
+
assignedDefaultCalendar: calendarId != null && inheritedDefaultCalendar,
|
|
25642
|
+
dateOrigin: raw.date_origin == null ? null : parseBackendDate(raw.date_origin),
|
|
25643
|
+
progress,
|
|
25644
|
+
cost: Number(raw.cost),
|
|
25645
|
+
usedCost: Number(raw.used_cost ?? 0),
|
|
25646
|
+
realCost: Number(raw.real_cost ?? 0),
|
|
25647
|
+
workHours: Number(raw.hhWorkTime),
|
|
25648
|
+
realWorkHours: Number(raw.real_work ?? 0),
|
|
25649
|
+
ponderator: Number(raw.ponderator ?? 0),
|
|
25650
|
+
hasCustomPonderator: raw.hasCustomPonderator,
|
|
25651
|
+
correlativeId: raw.correlative_id,
|
|
25652
|
+
uniqueCorrelativeId: raw.unique_correlative_id,
|
|
25653
|
+
customId: normalizeCustomId(raw),
|
|
25654
|
+
subcontractId: raw.subcontractId,
|
|
25655
|
+
isLookahead: raw.isOnLookahead,
|
|
25656
|
+
hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
|
|
25657
|
+
newActivityIds: parseNewActivityIds(raw.newActivitiesArray),
|
|
25658
|
+
hasNewActivities: raw.hasNewActivities ?? false,
|
|
25659
|
+
...raw.isNewActivity === true ? { isNewActivity: true } : {},
|
|
25660
|
+
pendingRequestIds: parseIdList(raw.pendingRequests),
|
|
25661
|
+
responsableIds: parseIdList(raw.responsables),
|
|
25662
|
+
tagIds: parseIdList(raw.tags),
|
|
25663
|
+
baselinePoints: parseBaselinePoints(
|
|
25664
|
+
raw.baseline_points,
|
|
25665
|
+
context.reporter,
|
|
25666
|
+
context.recordDiagnostic,
|
|
25667
|
+
String(raw.unique_id),
|
|
25668
|
+
isMilestone
|
|
25669
|
+
),
|
|
25670
|
+
baselineSnapshot: null,
|
|
25671
|
+
baselineVarianceDays: null,
|
|
25672
|
+
recursiveDurationHours: durationToHours(
|
|
25673
|
+
Number(raw.sumOfDurationRecursively ?? 0),
|
|
25674
|
+
context.hoursPerDay
|
|
25675
|
+
),
|
|
25676
|
+
calendarDuration: computeCalendarDuration({
|
|
25677
|
+
type: raw.type,
|
|
25678
|
+
startDate,
|
|
25679
|
+
endDate
|
|
25680
|
+
}),
|
|
25681
|
+
isCritical,
|
|
25682
|
+
freeSlackHours: null,
|
|
25683
|
+
expectedProgress: null,
|
|
25684
|
+
expectedProgressBaseline: null,
|
|
25685
|
+
status: null,
|
|
25686
|
+
criticalPath: null
|
|
25229
25687
|
};
|
|
25230
25688
|
}
|
|
25231
|
-
function
|
|
25232
|
-
|
|
25233
|
-
const parts = csv.split(",").map((value) => value.trim());
|
|
25234
|
-
if (parts.length !== WEEKDAYS_PER_WEEK) {
|
|
25235
|
-
return [...CALENDAR_FALLBACK_WORKING_DAYS];
|
|
25236
|
-
}
|
|
25237
|
-
return parts.map((part) => WORKING_DAY_TRUE_VALUES.has(part));
|
|
25689
|
+
function normalizeCustomId(raw) {
|
|
25690
|
+
return raw.has_childs ? null : raw.custom_id;
|
|
25238
25691
|
}
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25692
|
+
function forceRootProjectToRoot(activities) {
|
|
25693
|
+
const rootProject = activities.find(
|
|
25694
|
+
(activity) => activity.correlativeId === 0 && activity.type === ACTIVITY_TYPE.PROJECT
|
|
25695
|
+
);
|
|
25696
|
+
if (rootProject) rootProject.parentId = null;
|
|
25697
|
+
}
|
|
25698
|
+
function reparentOrphansToRoot(activities, ids, reporter, recordDiagnostic) {
|
|
25699
|
+
for (const activity of activities) {
|
|
25700
|
+
if (activity.parentId === null) continue;
|
|
25701
|
+
if (!ids.has(activity.parentId)) {
|
|
25702
|
+
reporter.warn(
|
|
25703
|
+
`[parseFromBackend] activity "${activity.id}" references unknown parent "${activity.parentId}" \u2014 re-parenting to root (null)`
|
|
25704
|
+
);
|
|
25705
|
+
recordDiagnostic(LOAD_DIAGNOSTIC_CODE.ORPHAN_ACTIVITY, activity.id);
|
|
25706
|
+
activity.parentId = null;
|
|
25707
|
+
}
|
|
25708
|
+
}
|
|
25242
25709
|
}
|
|
25243
25710
|
|
|
25244
25711
|
// src/boundary/backend/normalize-link.ts
|
|
@@ -26173,23 +26640,40 @@ function recomputeCanonicalRealWork(state) {
|
|
|
26173
26640
|
state.forEachActivity((activity, activityId) => {
|
|
26174
26641
|
if (activity.parentId === null) rootIds.push(String(activityId));
|
|
26175
26642
|
});
|
|
26176
|
-
const
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
)
|
|
26643
|
+
const stack = rootIds.map((activityId) => ({
|
|
26644
|
+
activityId,
|
|
26645
|
+
parent: null,
|
|
26646
|
+
childIds: null,
|
|
26647
|
+
realWork: 0
|
|
26648
|
+
})).reverse();
|
|
26649
|
+
for (let frame = stack.pop(); frame; frame = stack.pop()) {
|
|
26650
|
+
if (frame.childIds === null) {
|
|
26651
|
+
const activity = state.getActivity(frame.activityId);
|
|
26652
|
+
if (!activity) continue;
|
|
26653
|
+
const childIds = [...state.getChildren(frame.activityId)];
|
|
26654
|
+
const isLeaf = childIds.length === 0;
|
|
26655
|
+
frame.childIds = childIds.values();
|
|
26656
|
+
frame.realWork = isLeaf ? computeLeafRealWork(activity.workHours, activity.progress) : 0;
|
|
26657
|
+
}
|
|
26658
|
+
const nextChild = frame.childIds.next();
|
|
26659
|
+
const hasNextChild = !nextChild.done;
|
|
26660
|
+
if (hasNextChild) {
|
|
26661
|
+
stack.push(frame, {
|
|
26662
|
+
activityId: String(nextChild.value),
|
|
26663
|
+
parent: frame,
|
|
26664
|
+
childIds: null,
|
|
26665
|
+
realWork: 0
|
|
26666
|
+
});
|
|
26667
|
+
continue;
|
|
26668
|
+
}
|
|
26184
26669
|
state.setActivityField(
|
|
26185
|
-
activityId,
|
|
26670
|
+
frame.activityId,
|
|
26186
26671
|
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
26187
|
-
realWork
|
|
26672
|
+
frame.realWork
|
|
26188
26673
|
);
|
|
26189
|
-
changedIds.push(activityId);
|
|
26190
|
-
|
|
26191
|
-
}
|
|
26192
|
-
for (const rootId of rootIds) visit(rootId);
|
|
26674
|
+
changedIds.push(frame.activityId);
|
|
26675
|
+
if (frame.parent) frame.parent.realWork += frame.realWork;
|
|
26676
|
+
}
|
|
26193
26677
|
return changedIds;
|
|
26194
26678
|
}
|
|
26195
26679
|
|
|
@@ -26202,8 +26686,16 @@ async function runInitialPasses(deps) {
|
|
|
26202
26686
|
}
|
|
26203
26687
|
normalizeLoadedConstraintDates(state);
|
|
26204
26688
|
recomputeCanonicalRealWork(state);
|
|
26689
|
+
if (sector.activityCreter === ACTIVITY_PONDERATOR_CRITERION.COST) {
|
|
26690
|
+
let hasActiveBaseline2 = false;
|
|
26691
|
+
state.forEachActivity((activity) => {
|
|
26692
|
+
hasActiveBaseline2 ||= getActiveBaseline(activity) !== null;
|
|
26693
|
+
});
|
|
26694
|
+
if (hasActiveBaseline2)
|
|
26695
|
+
recomputeAllPonderators(ACTIVITY_PONDERATOR_CRITERION.COST, state);
|
|
26696
|
+
}
|
|
26205
26697
|
if (skipAutoSchedule) {
|
|
26206
|
-
recomputeAllProgressRollup(state);
|
|
26698
|
+
recomputeAllProgressRollup(state, sector.activityCreter);
|
|
26207
26699
|
if (now) {
|
|
26208
26700
|
applyExpectedProgressLive(state, now);
|
|
26209
26701
|
applyStatusPass(state, sector.statusCriteria);
|
|
@@ -26216,7 +26708,7 @@ async function runInitialPasses(deps) {
|
|
|
26216
26708
|
normalizeMilestoneConstraintDatesForDisplay(state);
|
|
26217
26709
|
recomputeSkippedLeafEnds(state);
|
|
26218
26710
|
await updateParentBoundsFromChildren(state, void 0, false);
|
|
26219
|
-
recomputeAllProgressRollup(state);
|
|
26711
|
+
recomputeAllProgressRollup(state, sector.activityCreter);
|
|
26220
26712
|
if (now) {
|
|
26221
26713
|
applyExpectedProgressLive(state, now);
|
|
26222
26714
|
applyStatusPass(state, sector.statusCriteria);
|
|
@@ -26689,6 +27181,39 @@ function clampPercent(value) {
|
|
|
26689
27181
|
return Math.min(MAXIMUM_PERCENT, Math.max(MINIMUM_PERCENT, value));
|
|
26690
27182
|
}
|
|
26691
27183
|
|
|
27184
|
+
// src/p6/rolled-expected-finish.ts
|
|
27185
|
+
var PINNABLE_ACTIVITY_TYPES = /* @__PURE__ */ new Set([
|
|
27186
|
+
P6_ACTIVITY_TYPE.TASK,
|
|
27187
|
+
P6_ACTIVITY_TYPE.RESOURCE_DEPENDENT
|
|
27188
|
+
]);
|
|
27189
|
+
var NO_ROLLED_EXPECTED_FINISHES = /* @__PURE__ */ new Map();
|
|
27190
|
+
function planRolledExpectedFinishes(input) {
|
|
27191
|
+
const adoptsShownFinishes = input.settings.useExpectedFinishDates && rollsDataDateForward(input.previousSettings, input.settings);
|
|
27192
|
+
if (!adoptsShownFinishes) return NO_ROLLED_EXPECTED_FINISHES;
|
|
27193
|
+
const pinned = /* @__PURE__ */ new Map();
|
|
27194
|
+
for (const [activityId, state] of input.activities) {
|
|
27195
|
+
const shownFinish = state.remainingEarlyFinish;
|
|
27196
|
+
if (shownFinish === null) continue;
|
|
27197
|
+
if (!needsExpectedFinishPin(state)) continue;
|
|
27198
|
+
pinned.set(activityId, new Date(shownFinish));
|
|
27199
|
+
}
|
|
27200
|
+
return pinned;
|
|
27201
|
+
}
|
|
27202
|
+
function rollsDataDateForward(previous, next) {
|
|
27203
|
+
const previousDataDate = previous.dataDate;
|
|
27204
|
+
const nextDataDate = next.dataDate;
|
|
27205
|
+
if (previousDataDate === null || nextDataDate === null) return false;
|
|
27206
|
+
return nextDataDate.getTime() > previousDataDate.getTime();
|
|
27207
|
+
}
|
|
27208
|
+
function needsExpectedFinishPin(state) {
|
|
27209
|
+
return state.executionStatus === P6_EXECUTION_STATUS.IN_PROGRESS && state.expectedFinish === null && hasPinnableActivityType(state);
|
|
27210
|
+
}
|
|
27211
|
+
function hasPinnableActivityType(state) {
|
|
27212
|
+
const { activityType } = state;
|
|
27213
|
+
if (activityType === void 0) return true;
|
|
27214
|
+
return PINNABLE_ACTIVITY_TYPES.has(activityType);
|
|
27215
|
+
}
|
|
27216
|
+
|
|
26692
27217
|
// src/p6/scheduler-projection.ts
|
|
26693
27218
|
function buildDataDateAuthorityFields(p6, settings) {
|
|
26694
27219
|
return {
|
|
@@ -26731,20 +27256,32 @@ function buildDataDateScheduleProjection(input) {
|
|
|
26731
27256
|
}
|
|
26732
27257
|
|
|
26733
27258
|
// src/p6/live-derived.ts
|
|
26734
|
-
function prepareDataDateLiveDerived(source, before, after) {
|
|
27259
|
+
function prepareDataDateLiveDerived(source, before, after, options = {}) {
|
|
26735
27260
|
if (!dataDateChanged(before.dataDate, after.dataDate)) return /* @__PURE__ */ new Map();
|
|
26736
27261
|
if (!hasActiveDataDate(after)) return /* @__PURE__ */ new Map();
|
|
26737
27262
|
const activating = !hasActiveDataDate(before);
|
|
27263
|
+
const activities = source.getAllActivities();
|
|
27264
|
+
const rolledExpectedFinishes = planSettingsExpectedFinishes(
|
|
27265
|
+
activities,
|
|
27266
|
+
before,
|
|
27267
|
+
after,
|
|
27268
|
+
options.adoptExpectedFinish
|
|
27269
|
+
);
|
|
26738
27270
|
const patches = /* @__PURE__ */ new Map();
|
|
26739
|
-
for (const activity of
|
|
27271
|
+
for (const activity of activities) {
|
|
26740
27272
|
const p6 = activity.p6;
|
|
26741
27273
|
if (p6 === void 0) continue;
|
|
26742
27274
|
const calendar = source.calendarReader.getCalendar(activity.calendarId);
|
|
26743
27275
|
if (calendar === null) {
|
|
26744
27276
|
throw new Error(`Missing calendar for Data Date activity ${activity.id}`);
|
|
26745
27277
|
}
|
|
26746
|
-
const nextP6 = liveDerivedState(
|
|
26747
|
-
|
|
27278
|
+
const nextP6 = liveDerivedState(
|
|
27279
|
+
p6,
|
|
27280
|
+
after.dataDate,
|
|
27281
|
+
calendar,
|
|
27282
|
+
rolledExpectedFinishes.get(activity.id)
|
|
27283
|
+
);
|
|
27284
|
+
const p6Changed = !Object.is(nextP6.expectedFinish, p6.expectedFinish) || !Object.is(nextP6.schedulePercentComplete, p6.schedulePercentComplete) || !Object.is(nextP6.actualDurationHours, p6.actualDurationHours);
|
|
26748
27285
|
if (!activating && !p6Changed) continue;
|
|
26749
27286
|
patches.set(activity.id, {
|
|
26750
27287
|
...p6Changed ? { p6: nextP6 } : {},
|
|
@@ -26753,17 +27290,37 @@ function prepareDataDateLiveDerived(source, before, after) {
|
|
|
26753
27290
|
}
|
|
26754
27291
|
return patches;
|
|
26755
27292
|
}
|
|
27293
|
+
function planSettingsExpectedFinishes(activities, before, after, adoptExpectedFinish = false) {
|
|
27294
|
+
if (!adoptExpectedFinish) return /* @__PURE__ */ new Map();
|
|
27295
|
+
const overridesDataDateWithForecast = after.setDataDateAndPlannedStartToForecastStart && after.projectForecastStart !== null;
|
|
27296
|
+
if (overridesDataDateWithForecast) return /* @__PURE__ */ new Map();
|
|
27297
|
+
const scheduledActivities = new Map(
|
|
27298
|
+
activities.flatMap((activity) => {
|
|
27299
|
+
const p6 = activity.p6;
|
|
27300
|
+
const hasSchedulingState = p6 !== void 0 && isP6ActivityState(p6);
|
|
27301
|
+
if (activity.type === ACTIVITY_TYPE.PROJECT || !hasSchedulingState)
|
|
27302
|
+
return [];
|
|
27303
|
+
return [[activity.id, p6]];
|
|
27304
|
+
})
|
|
27305
|
+
);
|
|
27306
|
+
return planRolledExpectedFinishes({
|
|
27307
|
+
previousSettings: before,
|
|
27308
|
+
settings: after,
|
|
27309
|
+
activities: scheduledActivities
|
|
27310
|
+
});
|
|
27311
|
+
}
|
|
26756
27312
|
function applyDataDateLiveDerived(writer, patches) {
|
|
26757
27313
|
for (const [activityId, fields] of patches) {
|
|
26758
27314
|
writer.setActivityFields(activityId, fields);
|
|
26759
27315
|
}
|
|
26760
27316
|
}
|
|
26761
|
-
function liveDerivedState(p6, dataDate, calendar) {
|
|
27317
|
+
function liveDerivedState(p6, dataDate, calendar, rolledExpectedFinish) {
|
|
26762
27318
|
const context = { dataDate, calendar };
|
|
26763
27319
|
const schedulePercentComplete = deriveP6SchedulePercentComplete(p6, context);
|
|
26764
27320
|
const measuresActualDuration = isP6ActivityState(p6) && p6.executionStatus === P6_EXECUTION_STATUS.IN_PROGRESS;
|
|
26765
27321
|
return {
|
|
26766
27322
|
...p6,
|
|
27323
|
+
expectedFinish: rolledExpectedFinish ?? p6.expectedFinish,
|
|
26767
27324
|
schedulePercentComplete,
|
|
26768
27325
|
...measuresActualDuration ? { actualDurationHours: deriveP6ActualDurationHours(p6, context) } : {}
|
|
26769
27326
|
};
|
|
@@ -29205,8 +29762,8 @@ function compareActivityReference(leftId, rightId, adapter, settings) {
|
|
|
29205
29762
|
function collectFreeFloatPaths(endingActivityId2, adapter, forward, settings) {
|
|
29206
29763
|
const paths = [];
|
|
29207
29764
|
const assigned = /* @__PURE__ */ new Set();
|
|
29208
|
-
const
|
|
29209
|
-
|
|
29765
|
+
const pending = [endingActivityId2];
|
|
29766
|
+
for (let pathEndId = pending.pop(); pathEndId !== void 0; pathEndId = pending.pop()) {
|
|
29210
29767
|
const path = [];
|
|
29211
29768
|
let currentId = pathEndId;
|
|
29212
29769
|
while (currentId !== null && !assigned.has(currentId)) {
|
|
@@ -29216,23 +29773,24 @@ function collectFreeFloatPaths(endingActivityId2, adapter, forward, settings) {
|
|
|
29216
29773
|
path.push(currentId);
|
|
29217
29774
|
currentId = startsFreeFloatPathSegment(currentId, adapter, settings) ? null : resolvePrimaryPathPredecessor(currentId, adapter, forward, settings);
|
|
29218
29775
|
}
|
|
29219
|
-
if (path.length === 0)
|
|
29776
|
+
if (path.length === 0) continue;
|
|
29220
29777
|
paths.push(path);
|
|
29221
|
-
|
|
29778
|
+
const alternatives = [...path].reverse().flatMap((activityId) => {
|
|
29222
29779
|
const primaryPredecessor = startsFreeFloatPathSegment(
|
|
29223
29780
|
activityId,
|
|
29224
29781
|
adapter,
|
|
29225
29782
|
settings
|
|
29226
29783
|
) ? null : resolvePrimaryPathPredecessor(activityId, adapter, forward, settings);
|
|
29227
|
-
|
|
29784
|
+
return adapter.getIncomingLinkIds(activityId).map((linkId) => adapter.getLink(linkId)).filter(
|
|
29228
29785
|
(link) => link !== null && link.source !== primaryPredecessor
|
|
29229
29786
|
).sort(
|
|
29230
|
-
(left, right) =>
|
|
29787
|
+
(left, right) => right.source.localeCompare(left.source) || right.id.localeCompare(left.id)
|
|
29231
29788
|
);
|
|
29232
|
-
|
|
29789
|
+
});
|
|
29790
|
+
for (const link of alternatives) {
|
|
29791
|
+
pending.push(link.source);
|
|
29233
29792
|
}
|
|
29234
|
-
}
|
|
29235
|
-
visit(endingActivityId2);
|
|
29793
|
+
}
|
|
29236
29794
|
return paths;
|
|
29237
29795
|
}
|
|
29238
29796
|
function startsFreeFloatPathSegment(activityId, adapter, settings) {
|
|
@@ -29376,6 +29934,103 @@ function resolvePrimaryPathPredecessor(activityId, adapter, forward, settings) {
|
|
|
29376
29934
|
return resolveLongestPathPredecessors(activityId, adapter, forward, settings)[0] ?? null;
|
|
29377
29935
|
}
|
|
29378
29936
|
|
|
29937
|
+
// src/p6/settings-enums.ts
|
|
29938
|
+
function validateP6ScheduleSettingEnums(settings) {
|
|
29939
|
+
validateChoice(settings.outOfSequencePolicy, "outOfSequencePolicy", [
|
|
29940
|
+
"retained_logic",
|
|
29941
|
+
"progress_override",
|
|
29942
|
+
"actual_dates"
|
|
29943
|
+
]);
|
|
29944
|
+
validateChoice(settings.dependencyReference, "dependencyReference", [
|
|
29945
|
+
"correlative_id",
|
|
29946
|
+
"custom_id"
|
|
29947
|
+
]);
|
|
29948
|
+
validateChoice(settings.startToStartLagBasis, "startToStartLagBasis", [
|
|
29949
|
+
"actual_start",
|
|
29950
|
+
"early_start"
|
|
29951
|
+
]);
|
|
29952
|
+
validateChoice(settings.relationshipLagCalendar, "relationshipLagCalendar", [
|
|
29953
|
+
"predecessor",
|
|
29954
|
+
"successor",
|
|
29955
|
+
"project_default",
|
|
29956
|
+
"twenty_four_hour"
|
|
29957
|
+
]);
|
|
29958
|
+
validateChoice(settings.totalFloatMethod, "totalFloatMethod", [
|
|
29959
|
+
"start_float",
|
|
29960
|
+
"finish_float",
|
|
29961
|
+
"smallest"
|
|
29962
|
+
]);
|
|
29963
|
+
validateChoice(
|
|
29964
|
+
settings.criticalActivityMethod,
|
|
29965
|
+
"criticalActivityMethod",
|
|
29966
|
+
Object.values(P6_CRITICAL_ACTIVITY_METHOD)
|
|
29967
|
+
);
|
|
29968
|
+
validateChoice(settings.calculateFloatBasedOn, "calculateFloatBasedOn", [
|
|
29969
|
+
"each_project",
|
|
29970
|
+
"opened_projects"
|
|
29971
|
+
]);
|
|
29972
|
+
}
|
|
29973
|
+
function validateChoice(value, field, allowed) {
|
|
29974
|
+
if (allowed.includes(value)) return;
|
|
29975
|
+
throw new Error(`Invalid P6 schedule settings: ${field}`);
|
|
29976
|
+
}
|
|
29977
|
+
|
|
29978
|
+
// src/p6/wbs-summary-activities.ts
|
|
29979
|
+
function prependAncestor(ancestorId, matches, requested) {
|
|
29980
|
+
return requested.has(ancestorId) ? { ancestorId, next: matches } : matches;
|
|
29981
|
+
}
|
|
29982
|
+
function cacheCycleMatches(path, cycleStart, requested, cache) {
|
|
29983
|
+
const cycle = path.slice(cycleStart).reverse();
|
|
29984
|
+
let matches = null;
|
|
29985
|
+
for (const ancestorId of cycle)
|
|
29986
|
+
matches = prependAncestor(ancestorId, matches, requested);
|
|
29987
|
+
for (const ancestorId of cycle) cache.set(ancestorId, matches);
|
|
29988
|
+
return matches;
|
|
29989
|
+
}
|
|
29990
|
+
function resolveAncestorMatches(parentId, byId, requested, cache) {
|
|
29991
|
+
const path = [];
|
|
29992
|
+
const positions = /* @__PURE__ */ new Map();
|
|
29993
|
+
let currentId = parentId;
|
|
29994
|
+
while (currentId !== null && !cache.has(currentId) && !positions.has(currentId)) {
|
|
29995
|
+
positions.set(currentId, path.length);
|
|
29996
|
+
path.push(currentId);
|
|
29997
|
+
currentId = byId.get(currentId)?.parentId ?? null;
|
|
29998
|
+
}
|
|
29999
|
+
let matches = currentId === null ? null : cache.get(currentId) ?? null;
|
|
30000
|
+
const cycleStart = currentId === null ? void 0 : positions.get(currentId);
|
|
30001
|
+
let lastIndex = path.length - 1;
|
|
30002
|
+
if (cycleStart !== void 0) {
|
|
30003
|
+
matches = cacheCycleMatches(path, cycleStart, requested, cache);
|
|
30004
|
+
lastIndex = cycleStart - 1;
|
|
30005
|
+
}
|
|
30006
|
+
for (let index = lastIndex; index >= 0; index -= 1) {
|
|
30007
|
+
const ancestorId = path[index];
|
|
30008
|
+
if (ancestorId === void 0) throw new Error("Missing WBS ancestor");
|
|
30009
|
+
matches = prependAncestor(ancestorId, matches, requested);
|
|
30010
|
+
cache.set(ancestorId, matches);
|
|
30011
|
+
}
|
|
30012
|
+
return cache.get(parentId) ?? null;
|
|
30013
|
+
}
|
|
30014
|
+
function groupWbsSummaryActivities(activities, byId, ancestorIds) {
|
|
30015
|
+
const groups = /* @__PURE__ */ new Map();
|
|
30016
|
+
if (ancestorIds.size === 0) return groups;
|
|
30017
|
+
const cache = /* @__PURE__ */ new Map();
|
|
30018
|
+
for (const activity of activities) {
|
|
30019
|
+
const isRealActivity = activity.p6 !== void 0 && activity.p6.activityType !== P6_ACTIVITY_TYPE.WBS_SUMMARY;
|
|
30020
|
+
if (!isRealActivity) continue;
|
|
30021
|
+
const parentId = activity.parentId;
|
|
30022
|
+
if (parentId === null) continue;
|
|
30023
|
+
let matches = resolveAncestorMatches(parentId, byId, ancestorIds, cache);
|
|
30024
|
+
while (matches !== null) {
|
|
30025
|
+
const bucket = groups.get(matches.ancestorId);
|
|
30026
|
+
if (bucket === void 0) groups.set(matches.ancestorId, [activity]);
|
|
30027
|
+
else bucket.push(activity);
|
|
30028
|
+
matches = matches.next;
|
|
30029
|
+
}
|
|
30030
|
+
}
|
|
30031
|
+
return groups;
|
|
30032
|
+
}
|
|
30033
|
+
|
|
29379
30034
|
// src/p6/schedule.ts
|
|
29380
30035
|
var EMPTY_LEVEL_OF_EFFORT_WINDOWS = {
|
|
29381
30036
|
remainingEarlyStart: null,
|
|
@@ -29527,27 +30182,6 @@ function calculateP6Schedule(input) {
|
|
|
29527
30182
|
];
|
|
29528
30183
|
return { activities, criticalPath, warnings };
|
|
29529
30184
|
}
|
|
29530
|
-
function groupActivitiesByWbsAncestor(all, byId) {
|
|
29531
|
-
const childrenByAncestor = /* @__PURE__ */ new Map();
|
|
29532
|
-
for (const activity of all) {
|
|
29533
|
-
if (activity.p6 === void 0 || activity.p6.activityType === "wbs_summary") {
|
|
29534
|
-
continue;
|
|
29535
|
-
}
|
|
29536
|
-
let currentId = activity.parentId;
|
|
29537
|
-
const visited = /* @__PURE__ */ new Set();
|
|
29538
|
-
while (currentId !== null && !visited.has(currentId)) {
|
|
29539
|
-
visited.add(currentId);
|
|
29540
|
-
const bucket = childrenByAncestor.get(currentId);
|
|
29541
|
-
if (bucket === void 0) {
|
|
29542
|
-
childrenByAncestor.set(currentId, [activity]);
|
|
29543
|
-
} else {
|
|
29544
|
-
bucket.push(activity);
|
|
29545
|
-
}
|
|
29546
|
-
currentId = byId.get(currentId)?.parentId ?? null;
|
|
29547
|
-
}
|
|
29548
|
-
}
|
|
29549
|
-
return childrenByAncestor;
|
|
29550
|
-
}
|
|
29551
30185
|
function hasNoPrimaryConstraint(constraintType) {
|
|
29552
30186
|
return (constraintType ?? CONSTRAINT_TYPE.ASAP) === CONSTRAINT_TYPE.ASAP;
|
|
29553
30187
|
}
|
|
@@ -29858,11 +30492,20 @@ function settleIsolatedLevelOfEffort(current, scheduled, dataDate) {
|
|
|
29858
30492
|
function calculateWbsSummaries(input) {
|
|
29859
30493
|
const { adapter, settings, activities } = input;
|
|
29860
30494
|
const all = adapter.getAllActivities();
|
|
30495
|
+
const summaries = sortedP6Activities(
|
|
30496
|
+
all.filter(
|
|
30497
|
+
(activity) => activity.p6?.activityType === P6_ACTIVITY_TYPE.WBS_SUMMARY
|
|
30498
|
+
)
|
|
30499
|
+
);
|
|
30500
|
+
const ancestorIds = new Set(
|
|
30501
|
+
summaries.flatMap(
|
|
30502
|
+
(summary) => summary.parentId === null ? [] : [summary.parentId]
|
|
30503
|
+
)
|
|
30504
|
+
);
|
|
30505
|
+
if (ancestorIds.size === 0) return;
|
|
29861
30506
|
const byId = new Map(all.map((activity) => [activity.id, activity]));
|
|
29862
|
-
const childrenByAncestor =
|
|
29863
|
-
for (const summary of
|
|
29864
|
-
(activity) => activity.p6?.activityType === "wbs_summary"
|
|
29865
|
-
)) {
|
|
30507
|
+
const childrenByAncestor = groupWbsSummaryActivities(all, byId, ancestorIds);
|
|
30508
|
+
for (const summary of summaries) {
|
|
29866
30509
|
if (summary.parentId === null) continue;
|
|
29867
30510
|
const children = childrenByAncestor.get(summary.parentId) ?? [];
|
|
29868
30511
|
if (children.length === 0) continue;
|
|
@@ -29985,6 +30628,7 @@ function validateP6ScheduleSettings(settings) {
|
|
|
29985
30628
|
if (endingActivityId2 !== null && typeof endingActivityId2 !== "string" && (typeof endingActivityId2 !== "number" || !Number.isFinite(endingActivityId2))) {
|
|
29986
30629
|
throw new Error("Invalid P6 schedule settings: ending activity ID");
|
|
29987
30630
|
}
|
|
30631
|
+
validateP6ScheduleSettingEnums(settings);
|
|
29988
30632
|
}
|
|
29989
30633
|
function sortedP6Activities(activities) {
|
|
29990
30634
|
return activities.filter((activity) => activity.p6 !== void 0).sort((left, right) => String(left.id).localeCompare(String(right.id)));
|
|
@@ -30158,39 +30802,6 @@ function blockedActivities(code, message, activityIds, missingInputs = []) {
|
|
|
30158
30802
|
};
|
|
30159
30803
|
}
|
|
30160
30804
|
|
|
30161
|
-
// src/p6/rolled-expected-finish.ts
|
|
30162
|
-
var PINNABLE_ACTIVITY_TYPES = /* @__PURE__ */ new Set([
|
|
30163
|
-
P6_ACTIVITY_TYPE.TASK,
|
|
30164
|
-
P6_ACTIVITY_TYPE.RESOURCE_DEPENDENT
|
|
30165
|
-
]);
|
|
30166
|
-
var NO_ROLLED_EXPECTED_FINISHES = /* @__PURE__ */ new Map();
|
|
30167
|
-
function planRolledExpectedFinishes(input) {
|
|
30168
|
-
const adoptsShownFinishes = input.settings.useExpectedFinishDates && rollsDataDateForward(input.previousSettings, input.settings);
|
|
30169
|
-
if (!adoptsShownFinishes) return NO_ROLLED_EXPECTED_FINISHES;
|
|
30170
|
-
const pinned = /* @__PURE__ */ new Map();
|
|
30171
|
-
for (const [activityId, state] of input.activities) {
|
|
30172
|
-
const shownFinish = state.remainingEarlyFinish;
|
|
30173
|
-
if (shownFinish === null) continue;
|
|
30174
|
-
if (!needsExpectedFinishPin(state)) continue;
|
|
30175
|
-
pinned.set(activityId, new Date(shownFinish));
|
|
30176
|
-
}
|
|
30177
|
-
return pinned;
|
|
30178
|
-
}
|
|
30179
|
-
function rollsDataDateForward(previous, next) {
|
|
30180
|
-
const previousDataDate = previous.dataDate;
|
|
30181
|
-
const nextDataDate = next.dataDate;
|
|
30182
|
-
if (previousDataDate === null || nextDataDate === null) return false;
|
|
30183
|
-
return nextDataDate.getTime() > previousDataDate.getTime();
|
|
30184
|
-
}
|
|
30185
|
-
function needsExpectedFinishPin(state) {
|
|
30186
|
-
return state.executionStatus === P6_EXECUTION_STATUS.IN_PROGRESS && state.expectedFinish === null && hasPinnableActivityType(state);
|
|
30187
|
-
}
|
|
30188
|
-
function hasPinnableActivityType(state) {
|
|
30189
|
-
const { activityType } = state;
|
|
30190
|
-
if (activityType === void 0) return true;
|
|
30191
|
-
return PINNABLE_ACTIVITY_TYPES.has(activityType);
|
|
30192
|
-
}
|
|
30193
|
-
|
|
30194
30805
|
// src/p6/commands.ts
|
|
30195
30806
|
function dataDateScheduleReadiness(input) {
|
|
30196
30807
|
const preflight = getP6SchedulePreflight({
|
|
@@ -30408,6 +31019,10 @@ function throwPreflight(preflight) {
|
|
|
30408
31019
|
}
|
|
30409
31020
|
function validateActivityInputPatch(patch) {
|
|
30410
31021
|
validateDates2(patch);
|
|
31022
|
+
const executionStatus = patch.executionStatus;
|
|
31023
|
+
if ("executionStatus" in patch && executionStatus !== P6_EXECUTION_STATUS.NOT_STARTED && executionStatus !== P6_EXECUTION_STATUS.IN_PROGRESS && executionStatus !== P6_EXECUTION_STATUS.COMPLETED) {
|
|
31024
|
+
throw new Error("Invalid P6 activity input: execution status");
|
|
31025
|
+
}
|
|
30411
31026
|
for (const value of [
|
|
30412
31027
|
patch.remainingDurationHours,
|
|
30413
31028
|
patch.originalDurationHours,
|
|
@@ -31067,9 +31682,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
31067
31682
|
this._activityClipboard.value = cloneDomainValue(
|
|
31068
31683
|
source._activityClipboard.value
|
|
31069
31684
|
);
|
|
31070
|
-
this._cellClipboard.value = cloneDomainValue(
|
|
31071
|
-
source._cellClipboard.value
|
|
31072
|
-
);
|
|
31685
|
+
this._cellClipboard.value = cloneDomainValue(source._cellClipboard.value);
|
|
31073
31686
|
this._undo = source._undo.fork();
|
|
31074
31687
|
this._saveTracker = source._saveTracker.fork();
|
|
31075
31688
|
this._scheduleRevision = source._scheduleRevision;
|
|
@@ -31240,6 +31853,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
31240
31853
|
const activity = readActivity(this.coreRuntime.state, id);
|
|
31241
31854
|
if (!activity) return null;
|
|
31242
31855
|
const context = buildPipelineContext(this.coreRuntime.state, {
|
|
31856
|
+
calendars: this.coreRuntime.calendars,
|
|
31243
31857
|
hoursPerDay: this.coreRuntime.sector.hoursPerDay,
|
|
31244
31858
|
dateFormat: this.coreRuntime.sector.dateFormat
|
|
31245
31859
|
});
|
|
@@ -31274,6 +31888,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
31274
31888
|
const pipeline = findPipelineForColumn(column);
|
|
31275
31889
|
if (!activity || !pipeline) return null;
|
|
31276
31890
|
const context = buildPipelineContext(this.coreRuntime.state, {
|
|
31891
|
+
calendars: this.coreRuntime.calendars,
|
|
31277
31892
|
hoursPerDay: this.coreRuntime.sector.hoursPerDay,
|
|
31278
31893
|
dateFormat: this.coreRuntime.sector.dateFormat
|
|
31279
31894
|
});
|
|
@@ -31834,7 +32449,8 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
31834
32449
|
const liveDerived = capability.prepareLiveDerived(
|
|
31835
32450
|
this.coreRuntime.state,
|
|
31836
32451
|
before,
|
|
31837
|
-
after
|
|
32452
|
+
after,
|
|
32453
|
+
{ adoptExpectedFinish: true }
|
|
31838
32454
|
);
|
|
31839
32455
|
const touchedIds = [...liveDerived.keys()];
|
|
31840
32456
|
const beforeSnap = new Map(
|
|
@@ -32263,6 +32879,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
32263
32879
|
this.assertReady();
|
|
32264
32880
|
return buildLookaheadConversionPlan(input, {
|
|
32265
32881
|
adapter: this.coreRuntime.state,
|
|
32882
|
+
sector: this.getSector(),
|
|
32266
32883
|
stateRevision: this._stateRevision
|
|
32267
32884
|
});
|
|
32268
32885
|
}
|
|
@@ -32310,6 +32927,16 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
32310
32927
|
});
|
|
32311
32928
|
continue;
|
|
32312
32929
|
}
|
|
32930
|
+
const manualSchedulingActive = hasActiveDataDate(
|
|
32931
|
+
this.coreRuntime.state.dataDateSettings()
|
|
32932
|
+
);
|
|
32933
|
+
if (manualSchedulingActive) {
|
|
32934
|
+
results.push({
|
|
32935
|
+
ok: false,
|
|
32936
|
+
reason: REJECTION_REASON.MANUAL_SCHEDULING_ACTIVE
|
|
32937
|
+
});
|
|
32938
|
+
continue;
|
|
32939
|
+
}
|
|
32313
32940
|
const changes = emptyChangeSet2(action);
|
|
32314
32941
|
results.push({ ok: true, changes });
|
|
32315
32942
|
acceptedChangeSets.push(changes);
|
|
@@ -32411,6 +33038,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
32411
33038
|
};
|
|
32412
33039
|
}
|
|
32413
33040
|
const pipelineContext = buildPipelineContext(this.coreRuntime.state, {
|
|
33041
|
+
calendars: this.coreRuntime.calendars,
|
|
32414
33042
|
hoursPerDay: this.coreRuntime.sector.hoursPerDay,
|
|
32415
33043
|
dateFormat: this.coreRuntime.sector.dateFormat
|
|
32416
33044
|
});
|