@outbuild-company/schedule-core 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +614 -294
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -18
- package/dist/index.d.ts +36 -18
- package/dist/index.js +614 -294
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -926,7 +926,8 @@ function expandParentLinks(links, adapter) {
|
|
|
926
926
|
targetIds,
|
|
927
927
|
link,
|
|
928
928
|
adapter,
|
|
929
|
-
virtualIdCounter
|
|
929
|
+
virtualIdCounter,
|
|
930
|
+
linksByActivity
|
|
930
931
|
);
|
|
931
932
|
}
|
|
932
933
|
return expanded;
|
|
@@ -943,19 +944,96 @@ function expandedEndpoints(activityId, summary, adapter, linksByActivity, source
|
|
|
943
944
|
sourceSide
|
|
944
945
|
);
|
|
945
946
|
}
|
|
946
|
-
function
|
|
947
|
+
function deriveSummaryBoundsWithDates(summaryId, adapter, resolveDates) {
|
|
948
|
+
const leaves = getLeafDescendants(summaryId, adapter);
|
|
949
|
+
if (leaves.length === 0) return null;
|
|
950
|
+
let earliestStart = null;
|
|
951
|
+
let latestEnd = null;
|
|
952
|
+
for (const leafId of leaves) {
|
|
953
|
+
const leafDates = resolveDates(leafId);
|
|
954
|
+
if (!leafDates) continue;
|
|
955
|
+
const startsEarlier = earliestStart === null || leafDates.startDate < earliestStart;
|
|
956
|
+
if (startsEarlier) earliestStart = leafDates.startDate;
|
|
957
|
+
const endsLater = latestEnd === null || leafDates.endDate > latestEnd;
|
|
958
|
+
if (endsLater) latestEnd = leafDates.endDate;
|
|
959
|
+
}
|
|
960
|
+
if (earliestStart === null || latestEnd === null) return null;
|
|
961
|
+
return { startDate: earliestStart, endDate: latestEnd };
|
|
962
|
+
}
|
|
963
|
+
function deriveSummaryBounds(summaryId, adapter) {
|
|
964
|
+
return deriveSummaryBoundsWithDates(summaryId, adapter, (leafId) => {
|
|
965
|
+
const leaf = adapter.getActivity(leafId);
|
|
966
|
+
if (!leaf) return null;
|
|
967
|
+
return { startDate: leaf.startDate, endDate: leaf.endDate };
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
function endpointSpanHours(endpoint, bounds, adapter) {
|
|
971
|
+
if (!endpoint) return 0;
|
|
972
|
+
if (!bounds) return endpoint.durationHours;
|
|
973
|
+
return adapter.calculateDuration({
|
|
974
|
+
startDate: bounds.startDate,
|
|
975
|
+
endDate: bounds.endDate,
|
|
976
|
+
task: endpoint
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
function sourceLagForLeaf(leafId, source, sourceBounds, adapter) {
|
|
980
|
+
if (!source) return 0;
|
|
981
|
+
const leaf = adapter.getActivity(leafId);
|
|
982
|
+
if (!sourceBounds || !leaf) return -source.durationHours;
|
|
983
|
+
return -adapter.calculateDuration({
|
|
984
|
+
startDate: sourceBounds.startDate,
|
|
985
|
+
endDate: leaf.endDate,
|
|
986
|
+
task: source
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
function hasIncomingRawLink(leafId, linksByActivity) {
|
|
990
|
+
const leafKey = String(leafId);
|
|
991
|
+
const bucket = linksByActivity.get(leafKey) ?? [];
|
|
992
|
+
return bucket.some((rawLink) => String(rawLink.target) === leafKey);
|
|
993
|
+
}
|
|
994
|
+
function targetOffsetForLeaf(leafId, targetStart, source, adapter, linksByActivity) {
|
|
995
|
+
if (hasIncomingRawLink(leafId, linksByActivity)) return 0;
|
|
996
|
+
const leaf = adapter.getActivity(leafId);
|
|
997
|
+
if (!targetStart || !leaf || !source) return 0;
|
|
998
|
+
return adapter.calculateDuration({
|
|
999
|
+
startDate: targetStart,
|
|
1000
|
+
endDate: leaf.startDate,
|
|
1001
|
+
task: source
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
function resolveExpansionAnchors(originalLink, adapter) {
|
|
947
1005
|
const source = adapter.getActivity(originalLink.source);
|
|
948
1006
|
const target = adapter.getActivity(originalLink.target);
|
|
949
|
-
const
|
|
950
|
-
const
|
|
1007
|
+
const sourceBounds = deriveSummaryBounds(originalLink.source, adapter);
|
|
1008
|
+
const targetBounds = deriveSummaryBounds(originalLink.target, adapter);
|
|
951
1009
|
const sourceEndpointIsStart = originalLink.type === LINK_TYPE.START_TO_START || originalLink.type === LINK_TYPE.START_TO_FINISH;
|
|
952
1010
|
const targetEndpointIsFinish = originalLink.type === LINK_TYPE.FINISH_TO_FINISH || originalLink.type === LINK_TYPE.START_TO_FINISH;
|
|
953
|
-
|
|
954
|
-
|
|
1011
|
+
return {
|
|
1012
|
+
source,
|
|
1013
|
+
sourceBounds,
|
|
1014
|
+
targetStart: targetBounds?.startDate ?? target?.startDate,
|
|
1015
|
+
userLag: originalLink.lag ?? 0,
|
|
1016
|
+
sourceEndpointIsStart,
|
|
1017
|
+
targetLag: targetEndpointIsFinish ? -endpointSpanHours(target, targetBounds, adapter) : 0,
|
|
1018
|
+
summarySourceId: originalLink.type === LINK_TYPE.START_TO_START && sourceBounds ? originalLink.source : void 0
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, adapter, virtualIdCounter, linksByActivity) {
|
|
1022
|
+
const anchors = resolveExpansionAnchors(originalLink, adapter);
|
|
1023
|
+
const { source, sourceBounds, targetStart, userLag } = anchors;
|
|
1024
|
+
const { sourceEndpointIsStart, targetLag, summarySourceId } = anchors;
|
|
955
1025
|
let counter = virtualIdCounter;
|
|
956
1026
|
for (const srcId of sourceIds) {
|
|
1027
|
+
const sourceLag = sourceEndpointIsStart ? sourceLagForLeaf(srcId, source, sourceBounds, adapter) : 0;
|
|
957
1028
|
for (const tgtId of leafIds) {
|
|
958
1029
|
if (String(srcId) === String(tgtId)) continue;
|
|
1030
|
+
const offTarget = targetOffsetForLeaf(
|
|
1031
|
+
tgtId,
|
|
1032
|
+
targetStart,
|
|
1033
|
+
source,
|
|
1034
|
+
adapter,
|
|
1035
|
+
linksByActivity
|
|
1036
|
+
);
|
|
959
1037
|
expanded.push(
|
|
960
1038
|
buildVirtualLink(
|
|
961
1039
|
originalLink,
|
|
@@ -964,10 +1042,9 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
|
|
|
964
1042
|
counter,
|
|
965
1043
|
sourceLag,
|
|
966
1044
|
targetLag,
|
|
967
|
-
userLag,
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
adapter
|
|
1045
|
+
userLag + offTarget,
|
|
1046
|
+
summarySourceId,
|
|
1047
|
+
offTarget !== 0 && targetLag === 0 ? originalLink.target : void 0
|
|
971
1048
|
)
|
|
972
1049
|
);
|
|
973
1050
|
counter += 1;
|
|
@@ -975,13 +1052,7 @@ function pushOffsetPreservingLinks(expanded, sourceIds, leafIds, originalLink, a
|
|
|
975
1052
|
}
|
|
976
1053
|
return counter;
|
|
977
1054
|
}
|
|
978
|
-
function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag, targetLag,
|
|
979
|
-
const leaf = adapter.getActivity(targetId);
|
|
980
|
-
const offTarget = targetStart && leaf && source ? adapter.calculateDuration({
|
|
981
|
-
startDate: targetStart,
|
|
982
|
-
endDate: leaf.startDate,
|
|
983
|
-
task: source
|
|
984
|
-
}) : 0;
|
|
1055
|
+
function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag, targetLag, trueLag, summarySourceId, offsetSummaryTargetId) {
|
|
985
1056
|
return {
|
|
986
1057
|
id: `virtual_${originalLink.id}_${counter}`,
|
|
987
1058
|
source: sourceId,
|
|
@@ -990,7 +1061,9 @@ function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag,
|
|
|
990
1061
|
lag: originalLink.lag,
|
|
991
1062
|
_sourceLag: sourceLag,
|
|
992
1063
|
_targetLag: targetLag,
|
|
993
|
-
_trueLag:
|
|
1064
|
+
_trueLag: trueLag,
|
|
1065
|
+
...summarySourceId === void 0 ? {} : { _summarySourceId: summarySourceId },
|
|
1066
|
+
...offsetSummaryTargetId === void 0 ? {} : { _summaryTargetId: offsetSummaryTargetId }
|
|
994
1067
|
};
|
|
995
1068
|
}
|
|
996
1069
|
|
|
@@ -1122,33 +1195,49 @@ function normalize(predecessor, successor, link) {
|
|
|
1122
1195
|
};
|
|
1123
1196
|
}
|
|
1124
1197
|
}
|
|
1125
|
-
function
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1198
|
+
function summaryAnchoredSourceLag(link, predecessor, adapter) {
|
|
1199
|
+
if (link._summarySourceId === void 0) return null;
|
|
1200
|
+
const summarySource = adapter.getActivity(link._summarySourceId);
|
|
1201
|
+
if (!summarySource) return null;
|
|
1202
|
+
const summaryBounds = deriveSummaryBounds(link._summarySourceId, adapter);
|
|
1203
|
+
if (!summaryBounds) return null;
|
|
1204
|
+
return -adapter.calculateDuration({
|
|
1205
|
+
startDate: summaryBounds.startDate,
|
|
1206
|
+
endDate: predecessor.endDate,
|
|
1207
|
+
task: summarySource
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
function noLagSuccessorStart(predecessor, successor, link, adapter, isEndAnchoredType) {
|
|
1211
|
+
const snapsToPast = successor.durationHours === 0 && isEndAnchoredType;
|
|
1212
|
+
return {
|
|
1213
|
+
successorStart: adapter.getClosestWorkTime({
|
|
1214
|
+
date: predecessor.endDate,
|
|
1215
|
+
dir: snapsToPast ? "past" : "future",
|
|
1216
|
+
task: successor
|
|
1217
|
+
}),
|
|
1129
1218
|
link
|
|
1130
|
-
|
|
1131
|
-
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
function calculateSuccessorStartFromLink(predecessor, successor, link, adapter) {
|
|
1222
|
+
const {
|
|
1223
|
+
sourceLag: frozenSourceLag,
|
|
1224
|
+
targetLag,
|
|
1225
|
+
trueLag,
|
|
1226
|
+
isFS,
|
|
1227
|
+
isFF
|
|
1228
|
+
} = normalize(predecessor, successor, link);
|
|
1229
|
+
const sourceLag = summaryAnchoredSourceLag(link, predecessor, adapter) ?? frozenSourceLag;
|
|
1230
|
+
const totalLag = sourceLag + targetLag + trueLag;
|
|
1231
|
+
const isZeroDurationSuccessor = successor.durationHours === 0;
|
|
1232
|
+
const hasLag = isZeroDurationSuccessor ? totalLag !== 0 : sourceLag !== 0 || targetLag !== 0 || trueLag !== 0;
|
|
1132
1233
|
if (!hasLag) {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
}),
|
|
1141
|
-
link
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
return {
|
|
1145
|
-
successorStart: adapter.getClosestWorkTime({
|
|
1146
|
-
date: predecessor.endDate,
|
|
1147
|
-
dir: "future",
|
|
1148
|
-
task: successor
|
|
1149
|
-
}),
|
|
1150
|
-
link
|
|
1151
|
-
};
|
|
1234
|
+
return noLagSuccessorStart(
|
|
1235
|
+
predecessor,
|
|
1236
|
+
successor,
|
|
1237
|
+
link,
|
|
1238
|
+
adapter,
|
|
1239
|
+
isFS || isFF
|
|
1240
|
+
);
|
|
1152
1241
|
}
|
|
1153
1242
|
const baseTask = isFS ? successor : predecessor;
|
|
1154
1243
|
let date2 = adapter.getClosestWorkTime({
|
|
@@ -1369,10 +1458,12 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
|
|
|
1369
1458
|
const incoming = incomingLinks.get(String(activityId)) ?? [];
|
|
1370
1459
|
let maxStart = null;
|
|
1371
1460
|
let drivingLinkId = null;
|
|
1461
|
+
let drivingOffsetIsStale = false;
|
|
1372
1462
|
for (const link of incoming) {
|
|
1373
1463
|
const predecessorSnap = adapter.getActivity(link.source);
|
|
1374
1464
|
if (!predecessorSnap) continue;
|
|
1375
1465
|
const predDates = getActivityDates(link.source);
|
|
1466
|
+
const predecessorMovedThisRun = predDates.startDate.getTime() !== predecessorSnap.startDate.getTime() || predDates.endDate.getTime() !== predecessorSnap.endDate.getTime();
|
|
1376
1467
|
const effectivePred = {
|
|
1377
1468
|
...predecessorSnap,
|
|
1378
1469
|
startDate: predDates.startDate,
|
|
@@ -1402,8 +1493,13 @@ async function asapPass(orderedIds, links, adapter, options, isCurrent, plans) {
|
|
|
1402
1493
|
if (!maxStart || result.successorStart > maxStart) {
|
|
1403
1494
|
maxStart = result.successorStart;
|
|
1404
1495
|
drivingLinkId = String(link.id);
|
|
1496
|
+
drivingOffsetIsStale = link._summaryTargetId !== void 0 && !predecessorMovedThisRun;
|
|
1405
1497
|
}
|
|
1406
1498
|
}
|
|
1499
|
+
const staleOffsetWouldPullBack = maxStart !== null && drivingOffsetIsStale && maxStart < activity.startDate;
|
|
1500
|
+
if (staleOffsetWouldPullBack) {
|
|
1501
|
+
maxStart = new Date(activity.startDate);
|
|
1502
|
+
}
|
|
1407
1503
|
const existing = plans.get(activityId);
|
|
1408
1504
|
const plan = existing ? { ...existing } : {
|
|
1409
1505
|
activityId,
|
|
@@ -1571,31 +1667,33 @@ async function alapPass(reversedIds, links, asapPlans, adapter, _options, isCurr
|
|
|
1571
1667
|
latestEnd = boundary;
|
|
1572
1668
|
}
|
|
1573
1669
|
}
|
|
1670
|
+
const incomingForKind = incomingLinks.get(String(activityId)) ?? [];
|
|
1671
|
+
const isFinishMarker = incomingForKind.some(
|
|
1672
|
+
(incomingLink) => incomingLink.type === LINK_TYPE.FINISH_TO_START || incomingLink.type === LINK_TYPE.FINISH_TO_FINISH
|
|
1673
|
+
);
|
|
1674
|
+
const latestEndAtDayStart = new Date(
|
|
1675
|
+
Date.UTC(
|
|
1676
|
+
latestEnd.getUTCFullYear(),
|
|
1677
|
+
latestEnd.getUTCMonth(),
|
|
1678
|
+
latestEnd.getUTCDate()
|
|
1679
|
+
)
|
|
1680
|
+
);
|
|
1681
|
+
const finishMarkerEnd = () => adapter.getClosestWorkTime({
|
|
1682
|
+
date: latestEndAtDayStart,
|
|
1683
|
+
dir: "past",
|
|
1684
|
+
task: activity
|
|
1685
|
+
});
|
|
1686
|
+
const latestEndLandsInsideWorkingDay = adapter.getClosestWorkTime({ date: latestEnd, dir: "future", task: activity }).getTime() === latestEnd.getTime();
|
|
1574
1687
|
let snappedEnd;
|
|
1575
1688
|
if (activity.durationHours === 0 && successorAnchor === null) {
|
|
1576
|
-
snappedEnd = adapter.getClosestWorkTime({
|
|
1689
|
+
snappedEnd = isFinishMarker && latestEndLandsInsideWorkingDay ? finishMarkerEnd() : adapter.getClosestWorkTime({
|
|
1577
1690
|
date: latestEnd,
|
|
1578
1691
|
dir: "past",
|
|
1579
1692
|
task: activity
|
|
1580
1693
|
});
|
|
1581
1694
|
} else if (activity.durationHours === 0) {
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
(l) => l.type === LINK_TYPE.FINISH_TO_START || l.type === LINK_TYPE.FINISH_TO_FINISH
|
|
1585
|
-
);
|
|
1586
|
-
if (isFinishKind) {
|
|
1587
|
-
const dayStart = new Date(
|
|
1588
|
-
Date.UTC(
|
|
1589
|
-
latestEnd.getUTCFullYear(),
|
|
1590
|
-
latestEnd.getUTCMonth(),
|
|
1591
|
-
latestEnd.getUTCDate()
|
|
1592
|
-
)
|
|
1593
|
-
);
|
|
1594
|
-
snappedEnd = adapter.getClosestWorkTime({
|
|
1595
|
-
date: dayStart,
|
|
1596
|
-
dir: "past",
|
|
1597
|
-
task: activity
|
|
1598
|
-
});
|
|
1695
|
+
if (isFinishMarker) {
|
|
1696
|
+
snappedEnd = finishMarkerEnd();
|
|
1599
1697
|
} else {
|
|
1600
1698
|
snappedEnd = adapter.getClosestWorkTime({
|
|
1601
1699
|
date: latestEnd,
|
|
@@ -1628,10 +1726,16 @@ async function alapPass(reversedIds, links, asapPlans, adapter, _options, isCurr
|
|
|
1628
1726
|
plan.endDate = snappedEnd;
|
|
1629
1727
|
plan.drivingLinkId = null;
|
|
1630
1728
|
plan.kind = "alap";
|
|
1729
|
+
const predecessorFloor = plan.earliestSchedulingStart ?? null;
|
|
1631
1730
|
plan.earliestSchedulingStart = null;
|
|
1632
1731
|
plan.earliestSchedulingEnd = null;
|
|
1633
|
-
|
|
1634
|
-
plan.
|
|
1732
|
+
const alapTarget = predecessorFloor !== null && predecessorFloor.getTime() > snappedStart.getTime() ? predecessorFloor : snappedStart;
|
|
1733
|
+
plan.latestSchedulingStart = alapTarget;
|
|
1734
|
+
plan.latestSchedulingEnd = alapTarget === snappedStart ? snappedEnd : adapter.calculateEndDate({
|
|
1735
|
+
startDate: alapTarget,
|
|
1736
|
+
durationHours: activity.durationHours,
|
|
1737
|
+
task: activity
|
|
1738
|
+
});
|
|
1635
1739
|
limitPlanDates(activity, plan, adapter);
|
|
1636
1740
|
plans.set(activityId, plan);
|
|
1637
1741
|
}
|
|
@@ -2066,18 +2170,9 @@ var AutoScheduler = class {
|
|
|
2066
2170
|
}
|
|
2067
2171
|
const phaseScope = startPhase("schedule.scoping");
|
|
2068
2172
|
const allIds = this.adapter.getAllIds();
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
if (options.triggerId !== void 0 && options.triggerId !== null) {
|
|
2073
|
-
const group = findConnectedGroupForTrigger(
|
|
2074
|
-
options.triggerId,
|
|
2075
|
-
activeLinks
|
|
2076
|
-
);
|
|
2077
|
-
scopedLinks = group.links;
|
|
2078
|
-
scopedIds = Array.from(group.activityIds);
|
|
2079
|
-
scopedIdSet = new Set(scopedIds);
|
|
2080
|
-
}
|
|
2173
|
+
const scope = resolveScope(options, activeLinks, this.adapter);
|
|
2174
|
+
const scopedLinks = scope.links;
|
|
2175
|
+
const scopedIdSet = scope.idSet;
|
|
2081
2176
|
endPhase(phaseScope);
|
|
2082
2177
|
const phaseTopo = startPhase("schedule.topologicalSort");
|
|
2083
2178
|
const topoResult = resolveTopoResult(
|
|
@@ -2174,6 +2269,34 @@ function endPhase(handle) {
|
|
|
2174
2269
|
const elapsed = performance.now() - handle.startedAt;
|
|
2175
2270
|
console.log(`[scheduler] ${handle.name} ${elapsed.toFixed(1)}ms`);
|
|
2176
2271
|
}
|
|
2272
|
+
function resolveScope(options, activeLinks, adapter) {
|
|
2273
|
+
const triggerId = options.triggerId;
|
|
2274
|
+
if (triggerId === void 0 || triggerId === null) {
|
|
2275
|
+
return { idSet: null, links: activeLinks };
|
|
2276
|
+
}
|
|
2277
|
+
const group = findConnectedGroupForTrigger(triggerId, activeLinks);
|
|
2278
|
+
return widenScopeToAlapActivities(group, activeLinks, adapter);
|
|
2279
|
+
}
|
|
2280
|
+
function widenScopeToAlapActivities(group, allLinks, adapter) {
|
|
2281
|
+
const idSet = new Set(group.activityIds);
|
|
2282
|
+
const widenedAlapKeys = /* @__PURE__ */ new Set();
|
|
2283
|
+
for (const activity of adapter.getAllActivities()) {
|
|
2284
|
+
const activityKey = String(activity.id);
|
|
2285
|
+
const isOutOfComponentAlap = activity.constraintType === CONSTRAINT_TYPE2.ALAP && !idSet.has(activityKey);
|
|
2286
|
+
if (!isOutOfComponentAlap) continue;
|
|
2287
|
+
widenedAlapKeys.add(activityKey);
|
|
2288
|
+
idSet.add(activityKey);
|
|
2289
|
+
}
|
|
2290
|
+
if (widenedAlapKeys.size === 0) {
|
|
2291
|
+
return { idSet, links: group.links };
|
|
2292
|
+
}
|
|
2293
|
+
const links = [...group.links];
|
|
2294
|
+
for (const link of allLinks) {
|
|
2295
|
+
const touchesWidenedAlap = widenedAlapKeys.has(String(link.source)) || widenedAlapKeys.has(String(link.target));
|
|
2296
|
+
if (touchesWidenedAlap) links.push(link);
|
|
2297
|
+
}
|
|
2298
|
+
return { idSet, links };
|
|
2299
|
+
}
|
|
2177
2300
|
function resolveTopoResult(cache, allIds, allLinks, scopedIdSet) {
|
|
2178
2301
|
const fullTopo = cache.get(allIds, allLinks);
|
|
2179
2302
|
if (fullTopo.hasCycles) {
|
|
@@ -2191,9 +2314,10 @@ function resolveTopoResult(cache, allIds, allLinks, scopedIdSet) {
|
|
|
2191
2314
|
}
|
|
2192
2315
|
|
|
2193
2316
|
// src/internal/post-processors/adjust-link-lag-on-task-move.ts
|
|
2194
|
-
function adjustLinkLagOnTaskMove(targetId, adapter) {
|
|
2317
|
+
function adjustLinkLagOnTaskMove(targetId, adapter, preEditTarget) {
|
|
2195
2318
|
const target = adapter.getActivity(targetId);
|
|
2196
2319
|
if (!target) return;
|
|
2320
|
+
const targetForLagMath = preEditTarget ? { ...target, endDate: preEditTarget.endDate } : target;
|
|
2197
2321
|
const calendarApi = adapter.calendarReader.getCalendar(
|
|
2198
2322
|
String(target.calendarId)
|
|
2199
2323
|
);
|
|
@@ -2203,9 +2327,11 @@ function adjustLinkLagOnTaskMove(targetId, adapter) {
|
|
|
2203
2327
|
const source = adapter.getActivity(link.source);
|
|
2204
2328
|
if (!source) continue;
|
|
2205
2329
|
if (source.constraintType === "alap") continue;
|
|
2330
|
+
const isEndAnchoredIntoMilestone = isMilestoneType(target.type) && (link.type === LINK_TYPE.START_TO_FINISH || link.type === LINK_TYPE.FINISH_TO_FINISH);
|
|
2331
|
+
if (isEndAnchoredIntoMilestone) continue;
|
|
2206
2332
|
const { sourceDate, targetDate } = pickDatesForLinkType(
|
|
2207
2333
|
source,
|
|
2208
|
-
|
|
2334
|
+
targetForLagMath,
|
|
2209
2335
|
link.type
|
|
2210
2336
|
);
|
|
2211
2337
|
const newLag = dateMath.computeLagBetweenTasks(
|
|
@@ -2253,14 +2379,15 @@ function computeLeafRealWork(workHours, progress) {
|
|
|
2253
2379
|
// src/propagations/upward/parent-bounds.ts
|
|
2254
2380
|
async function updateParentBoundsFromChildren(adapter, dirtyIds, recomputeProgress = true, options = {}) {
|
|
2255
2381
|
const parents = dirtyIds ? findAncestorsOfDirty(adapter, dirtyIds) : findAllParents(adapter);
|
|
2256
|
-
|
|
2382
|
+
const progressMode = recomputeProgress ? options.progressMode ?? "freeze-when-no-contribution" : "skip";
|
|
2383
|
+
await recomputeParentsCooperatively(parents, adapter, progressMode, {
|
|
2257
2384
|
recomputeRealWork: options.recomputeRealWork ?? false
|
|
2258
2385
|
});
|
|
2259
2386
|
}
|
|
2260
2387
|
function recomputeSingleParentDisplayBounds(parentId, adapter) {
|
|
2261
|
-
recomputeParentFromChildren(parentId, adapter,
|
|
2388
|
+
recomputeParentFromChildren(parentId, adapter, "skip");
|
|
2262
2389
|
}
|
|
2263
|
-
async function recomputeParentsCooperatively(parents, adapter,
|
|
2390
|
+
async function recomputeParentsCooperatively(parents, adapter, progressMode, options = {
|
|
2264
2391
|
recomputeRealWork: false
|
|
2265
2392
|
}) {
|
|
2266
2393
|
const YIELD_EVERY_N_PARENTS = 200;
|
|
@@ -2269,7 +2396,7 @@ async function recomputeParentsCooperatively(parents, adapter, recomputeProgress
|
|
|
2269
2396
|
recomputeParentFromChildren(
|
|
2270
2397
|
parentId,
|
|
2271
2398
|
adapter,
|
|
2272
|
-
|
|
2399
|
+
progressMode,
|
|
2273
2400
|
options.recomputeRealWork
|
|
2274
2401
|
);
|
|
2275
2402
|
processed += 1;
|
|
@@ -2319,7 +2446,7 @@ function aggregateChildren(childrenIds, adapter, includeRealWork) {
|
|
|
2319
2446
|
}
|
|
2320
2447
|
return { minStart, maxEnd, realWorkSum, weightedChildren };
|
|
2321
2448
|
}
|
|
2322
|
-
function recomputeParentFromChildren(parentId, adapter,
|
|
2449
|
+
function recomputeParentFromChildren(parentId, adapter, progressMode = "freeze-when-no-contribution", recomputeRealWork = false) {
|
|
2323
2450
|
const parent = adapter.getActivity(parentId);
|
|
2324
2451
|
if (!parent) return;
|
|
2325
2452
|
const childrenIds = adapter.getChildren(parentId);
|
|
@@ -2337,11 +2464,12 @@ function recomputeParentFromChildren(parentId, adapter, recomputeProgress = true
|
|
|
2337
2464
|
const progressRollup = computeWeightedProgressRollup(
|
|
2338
2465
|
aggregate.weightedChildren
|
|
2339
2466
|
);
|
|
2340
|
-
|
|
2467
|
+
const shouldWriteProgress = progressMode !== "skip" && (progressRollup !== null || progressMode === "zero-when-no-contribution");
|
|
2468
|
+
if (shouldWriteProgress) {
|
|
2341
2469
|
adapter.setActivityField(
|
|
2342
2470
|
parentId,
|
|
2343
2471
|
"progress",
|
|
2344
|
-
roundProgressPerLevel(progressRollup)
|
|
2472
|
+
roundProgressPerLevel(progressRollup ?? 0)
|
|
2345
2473
|
);
|
|
2346
2474
|
}
|
|
2347
2475
|
if (recomputeRealWork) {
|
|
@@ -2403,7 +2531,7 @@ function runRecordLastStartDate(activityId, adapter) {
|
|
|
2403
2531
|
const activity = adapter.getActivity(activityId);
|
|
2404
2532
|
if (activity) adapter.setLastStartDate(activityId, activity.startDate);
|
|
2405
2533
|
}
|
|
2406
|
-
function runPostProcessorsOnAdapter(activityId, processorNames, adapter) {
|
|
2534
|
+
function runPostProcessorsOnAdapter(activityId, processorNames, adapter, preEditActivity) {
|
|
2407
2535
|
for (const name of processorNames) {
|
|
2408
2536
|
const fn = name in POST_PROCESSORS ? POST_PROCESSORS[name] : void 0;
|
|
2409
2537
|
if (!fn) {
|
|
@@ -2412,7 +2540,7 @@ function runPostProcessorsOnAdapter(activityId, processorNames, adapter) {
|
|
|
2412
2540
|
);
|
|
2413
2541
|
continue;
|
|
2414
2542
|
}
|
|
2415
|
-
fn(activityId, adapter);
|
|
2543
|
+
fn(activityId, adapter, preEditActivity ?? null);
|
|
2416
2544
|
}
|
|
2417
2545
|
}
|
|
2418
2546
|
function runNoOp(name) {
|
|
@@ -2685,10 +2813,7 @@ var durationPipeline = {
|
|
|
2685
2813
|
);
|
|
2686
2814
|
return {
|
|
2687
2815
|
...fieldChanges(extraFields, {
|
|
2688
|
-
|
|
2689
|
-
// anchored and Finish moves. Running the ALAP pass would instead
|
|
2690
|
-
// preserve Finish and pull Start, which is not the observed UI flow.
|
|
2691
|
-
autoSchedule: activity.constraintType !== "alap",
|
|
2816
|
+
autoSchedule: true,
|
|
2692
2817
|
postProcessors: [POST_PROCESSOR.RECORD_LAST_START_DATE]
|
|
2693
2818
|
}),
|
|
2694
2819
|
...constraintCheck.violated ? {
|
|
@@ -2850,16 +2975,22 @@ function buildCanonicalRealWorkOverlay(activity, progress, hierarchy) {
|
|
|
2850
2975
|
realWorkByActivity.set(activity.id, selfRealWork);
|
|
2851
2976
|
return realWorkByActivity;
|
|
2852
2977
|
}
|
|
2978
|
+
function summariesOverSummariesDeepestFirst(activity, hierarchy) {
|
|
2979
|
+
const subtreeIds = [activity.id, ...hierarchy.getDescendantIds(activity.id)];
|
|
2980
|
+
const hasSummaryChild = (parentId) => hierarchy.getChildProgressInfo(parentId).some((child) => hierarchy.hasChildren(child.id));
|
|
2981
|
+
const depthOf3 = (activityId) => hierarchy.getAncestorIds(activityId).length;
|
|
2982
|
+
return subtreeIds.filter((candidateId) => hasSummaryChild(String(candidateId))).map(String).sort((first, second) => depthOf3(second) - depthOf3(first));
|
|
2983
|
+
}
|
|
2853
2984
|
function buildAncestorRollupCascade(activity, newValue, hierarchy) {
|
|
2854
2985
|
const overlay = /* @__PURE__ */ new Map();
|
|
2855
2986
|
overlay.set(activity.id, newValue);
|
|
2856
2987
|
for (const descendantId of hierarchy.getDescendantIds(activity.id)) {
|
|
2857
2988
|
overlay.set(descendantId, newValue);
|
|
2858
2989
|
}
|
|
2859
|
-
const chain = [
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2990
|
+
const chain = [
|
|
2991
|
+
...summariesOverSummariesDeepestFirst(activity, hierarchy),
|
|
2992
|
+
...hierarchy.getAncestorIds(activity.id)
|
|
2993
|
+
];
|
|
2863
2994
|
const mutations = [];
|
|
2864
2995
|
for (const id of chain) {
|
|
2865
2996
|
const rollup = computeWeightedProgress(id, overlay, hierarchy);
|
|
@@ -3256,6 +3387,8 @@ function checkIsWorkingDay(newDate, activity, ctx) {
|
|
|
3256
3387
|
return null;
|
|
3257
3388
|
}
|
|
3258
3389
|
function detectEndDateConstraintWarning(activity, newEndDate) {
|
|
3390
|
+
const cannotViolateStartPin = effectiveConstraintType(activity) === CONSTRAINT_TYPE2.MSO;
|
|
3391
|
+
if (cannotViolateStartPin) return void 0;
|
|
3259
3392
|
const projectedDate = pickProjectedDateForConstraint(
|
|
3260
3393
|
effectiveConstraintType(activity),
|
|
3261
3394
|
activity.startDate,
|
|
@@ -3276,7 +3409,8 @@ function detectEndDateConstraintWarning(activity, newEndDate) {
|
|
|
3276
3409
|
function buildEndDateExtraFields(activity, newEndDate, ctx, violatesConstraint = false) {
|
|
3277
3410
|
const fields = {};
|
|
3278
3411
|
const isMilestone = isMilestoneType(activity.type);
|
|
3279
|
-
|
|
3412
|
+
const seedsMilestoneDate = isMilestone && !violatesConstraint && hasDatelessConstraintType(activity);
|
|
3413
|
+
if (seedsMilestoneDate) {
|
|
3280
3414
|
Object.assign(fields, setConstraintDate(activity.startDate));
|
|
3281
3415
|
} else if (violatesConstraint || hasDatelessConstraintType(activity)) {
|
|
3282
3416
|
Object.assign(
|
|
@@ -3286,10 +3420,8 @@ function buildEndDateExtraFields(activity, newEndDate, ctx, violatesConstraint =
|
|
|
3286
3420
|
);
|
|
3287
3421
|
}
|
|
3288
3422
|
const newDuration = calculateNewDuration(activity, newEndDate, ctx);
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
if (flippedType) fields.type = flippedType;
|
|
3292
|
-
}
|
|
3423
|
+
const flippedType = typeForNewDuration(activity.durationHours, newDuration);
|
|
3424
|
+
if (flippedType) fields.type = flippedType;
|
|
3293
3425
|
Object.assign(fields, setDuration(newDuration));
|
|
3294
3426
|
fields.endDate = newEndDate;
|
|
3295
3427
|
return fields;
|
|
@@ -3706,6 +3838,49 @@ function setLinkFieldDynamic(state, linkId, field, value) {
|
|
|
3706
3838
|
state.setLinkFieldDynamic(linkId, field, value);
|
|
3707
3839
|
}
|
|
3708
3840
|
|
|
3841
|
+
// src/internal/state/clone-core-activity.ts
|
|
3842
|
+
function cloneCoreActivity(activity) {
|
|
3843
|
+
return {
|
|
3844
|
+
...activity,
|
|
3845
|
+
startDate: new Date(activity.startDate),
|
|
3846
|
+
endDate: new Date(activity.endDate),
|
|
3847
|
+
constraintDate: cloneNullableDate(activity.constraintDate),
|
|
3848
|
+
dateOrigin: cloneNullableDate(activity.dateOrigin),
|
|
3849
|
+
newActivityIds: [...activity.newActivityIds],
|
|
3850
|
+
pendingRequestIds: [...activity.pendingRequestIds],
|
|
3851
|
+
responsableIds: [...activity.responsableIds],
|
|
3852
|
+
tagIds: [...activity.tagIds],
|
|
3853
|
+
baselinePoints: activity.baselinePoints.map((point) => ({
|
|
3854
|
+
...point,
|
|
3855
|
+
startDate: cloneNullableDate(point.startDate),
|
|
3856
|
+
endDate: cloneNullableDate(point.endDate)
|
|
3857
|
+
})),
|
|
3858
|
+
baselineSnapshot: cloneBaselineSnapshot(activity.baselineSnapshot),
|
|
3859
|
+
criticalPath: cloneCriticalPath(activity.criticalPath)
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
function cloneNullableDate(value) {
|
|
3863
|
+
return value === null ? null : new Date(value);
|
|
3864
|
+
}
|
|
3865
|
+
function cloneBaselineSnapshot(value) {
|
|
3866
|
+
if (value === null) return null;
|
|
3867
|
+
return {
|
|
3868
|
+
...value,
|
|
3869
|
+
startDate: cloneNullableDate(value.startDate),
|
|
3870
|
+
endDate: cloneNullableDate(value.endDate)
|
|
3871
|
+
};
|
|
3872
|
+
}
|
|
3873
|
+
function cloneCriticalPath(value) {
|
|
3874
|
+
if (value === null) return null;
|
|
3875
|
+
return {
|
|
3876
|
+
...value,
|
|
3877
|
+
earlyStart: cloneNullableDate(value.earlyStart),
|
|
3878
|
+
earlyFinish: cloneNullableDate(value.earlyFinish),
|
|
3879
|
+
lateStart: cloneNullableDate(value.lateStart),
|
|
3880
|
+
lateFinish: cloneNullableDate(value.lateFinish)
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3709
3884
|
// src/dispatch/shared/snapshots.ts
|
|
3710
3885
|
function collectTouchedIds(primary, changes) {
|
|
3711
3886
|
const ids = /* @__PURE__ */ new Set([String(primary)]);
|
|
@@ -3723,13 +3898,7 @@ function snapshotActivities(adapter, ids) {
|
|
|
3723
3898
|
return out;
|
|
3724
3899
|
}
|
|
3725
3900
|
function structuredCloneActivity(activity) {
|
|
3726
|
-
return
|
|
3727
|
-
...activity,
|
|
3728
|
-
startDate: new Date(activity.startDate),
|
|
3729
|
-
endDate: new Date(activity.endDate),
|
|
3730
|
-
constraintDate: activity.constraintDate ? new Date(activity.constraintDate) : null,
|
|
3731
|
-
dateOrigin: activity.dateOrigin ? new Date(activity.dateOrigin) : null
|
|
3732
|
-
};
|
|
3901
|
+
return cloneCoreActivity(activity);
|
|
3733
3902
|
}
|
|
3734
3903
|
function applyFieldChanges(adapter, activityId, changes) {
|
|
3735
3904
|
applyCanonicalPatch(adapter, activityId, changes.patch);
|
|
@@ -3782,12 +3951,33 @@ function diffActivity(before, after) {
|
|
|
3782
3951
|
const afterRec = after;
|
|
3783
3952
|
const keys = /* @__PURE__ */ new Set([...Object.keys(beforeRec), ...Object.keys(afterRec)]);
|
|
3784
3953
|
for (const k of keys) {
|
|
3785
|
-
if (!
|
|
3954
|
+
if (Object.hasOwn(beforeRec, k) !== Object.hasOwn(afterRec, k) || !fieldValueEqual(beforeRec[k], afterRec[k])) {
|
|
3786
3955
|
fields[k] = { before: beforeRec[k], after: afterRec[k] };
|
|
3787
3956
|
}
|
|
3788
3957
|
}
|
|
3789
3958
|
return fields;
|
|
3790
3959
|
}
|
|
3960
|
+
function fieldValueEqual(left, right) {
|
|
3961
|
+
if (Object.is(left, right)) return true;
|
|
3962
|
+
if (left instanceof Date && right instanceof Date) {
|
|
3963
|
+
return left.getTime() === right.getTime();
|
|
3964
|
+
}
|
|
3965
|
+
if (left == null || right == null) return false;
|
|
3966
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
3967
|
+
if (!Array.isArray(left) || !Array.isArray(right)) return false;
|
|
3968
|
+
if (left.length !== right.length) return false;
|
|
3969
|
+
return left.every((value, index) => fieldValueEqual(value, right[index]));
|
|
3970
|
+
}
|
|
3971
|
+
if (typeof left !== "object" || typeof right !== "object") return false;
|
|
3972
|
+
const leftRecord = left;
|
|
3973
|
+
const rightRecord = right;
|
|
3974
|
+
const leftKeys = Object.keys(leftRecord);
|
|
3975
|
+
const rightKeys = Object.keys(rightRecord);
|
|
3976
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
3977
|
+
return leftKeys.every(
|
|
3978
|
+
(key) => Object.hasOwn(rightRecord, key) && fieldValueEqual(leftRecord[key], rightRecord[key])
|
|
3979
|
+
);
|
|
3980
|
+
}
|
|
3791
3981
|
function collectDirtyForInlineEdit(triggerId, touchedIds, scheduledIds) {
|
|
3792
3982
|
const dirty = /* @__PURE__ */ new Set();
|
|
3793
3983
|
dirty.add(triggerId);
|
|
@@ -3813,21 +4003,6 @@ function foldCorrelativeShifts(adapter, shifts, excludeIds, beforeSnap, touched)
|
|
|
3813
4003
|
beforeSnap.set(shift.activityId, beforeImage);
|
|
3814
4004
|
}
|
|
3815
4005
|
}
|
|
3816
|
-
function shallowEqual(a, b) {
|
|
3817
|
-
if (a === b) return true;
|
|
3818
|
-
if (a instanceof Date && b instanceof Date)
|
|
3819
|
-
return a.getTime() === b.getTime();
|
|
3820
|
-
if (a == null || b == null) return false;
|
|
3821
|
-
if (typeof a !== typeof b) return false;
|
|
3822
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
3823
|
-
if (a.length !== b.length) return false;
|
|
3824
|
-
for (let i = 0; i < a.length; i++) {
|
|
3825
|
-
if (!shallowEqual(a[i], b[i])) return false;
|
|
3826
|
-
}
|
|
3827
|
-
return true;
|
|
3828
|
-
}
|
|
3829
|
-
return false;
|
|
3830
|
-
}
|
|
3831
4006
|
|
|
3832
4007
|
// src/shared/backend-date.ts
|
|
3833
4008
|
var BACKEND_DATE_FORMAT_REGEX = /^(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{2})$/;
|
|
@@ -9357,6 +9532,9 @@ function resolveDerivedPasses(action, autoSchedule, options) {
|
|
|
9357
9532
|
// src/dispatch/shared/post-mutation.ts
|
|
9358
9533
|
async function runPostMutation(deps, args) {
|
|
9359
9534
|
const passes = resolvePasses(args);
|
|
9535
|
+
if (passes.has(DERIVED_PASS.AUTOSCHEDULE)) {
|
|
9536
|
+
await refreshMutatedParentBounds(deps.adapter, args);
|
|
9537
|
+
}
|
|
9360
9538
|
const schedule = passes.has(DERIVED_PASS.AUTOSCHEDULE) ? await runAutoschedule(deps, args.autoscheduleFrom) : emptySchedule();
|
|
9361
9539
|
await rollUpParentBounds(deps.adapter, args, schedule.scheduledIds, passes);
|
|
9362
9540
|
await runDerivedRecomputes(deps, passes, args.now);
|
|
@@ -9365,7 +9543,11 @@ async function runPostMutation(deps, args) {
|
|
|
9365
9543
|
function resolvePasses(args) {
|
|
9366
9544
|
const requested = args.autoscheduleFrom !== null;
|
|
9367
9545
|
const ran = !args.options.skipAutoSchedule && requested;
|
|
9368
|
-
|
|
9546
|
+
const passes = new Set(
|
|
9547
|
+
resolveDerivedPasses(args.action, { ran }, args.options)
|
|
9548
|
+
);
|
|
9549
|
+
if (args.recomputeParentProgress) passes.add(DERIVED_PASS.PARENT_PROGRESS);
|
|
9550
|
+
return passes;
|
|
9369
9551
|
}
|
|
9370
9552
|
async function runAutoschedule(deps, autoscheduleFrom) {
|
|
9371
9553
|
const options = autoscheduleFrom === "roots" ? {} : { triggerId: autoscheduleFrom };
|
|
@@ -9376,20 +9558,23 @@ async function runAutoschedule(deps, autoscheduleFrom) {
|
|
|
9376
9558
|
}
|
|
9377
9559
|
function captureScheduledRows(adapter, updatedIds) {
|
|
9378
9560
|
const scheduledIds = [];
|
|
9379
|
-
const scheduledBeforeImages = /* @__PURE__ */ new Map();
|
|
9380
9561
|
for (const id of updatedIds) {
|
|
9381
9562
|
const activityId = String(id);
|
|
9382
9563
|
scheduledIds.push(activityId);
|
|
9383
9564
|
const snap = adapter.getActivity(activityId);
|
|
9384
9565
|
if (snap) {
|
|
9385
|
-
scheduledBeforeImages.set(activityId, structuredCloneActivity(snap));
|
|
9386
9566
|
adapter.noteScheduledBefore(activityId, snap);
|
|
9387
9567
|
}
|
|
9388
9568
|
}
|
|
9389
|
-
return { scheduledIds
|
|
9569
|
+
return { scheduledIds };
|
|
9390
9570
|
}
|
|
9391
9571
|
function emptySchedule() {
|
|
9392
|
-
return { scheduledIds: []
|
|
9572
|
+
return { scheduledIds: [] };
|
|
9573
|
+
}
|
|
9574
|
+
async function refreshMutatedParentBounds(adapter, args) {
|
|
9575
|
+
const mutated = args.collectDirty ? args.collectDirty([]) : collectDirtyForStructural(args.recomputeParentsFrom, []);
|
|
9576
|
+
if (mutated.size === 0) return;
|
|
9577
|
+
await updateParentBoundsFromChildren(adapter, mutated, false);
|
|
9393
9578
|
}
|
|
9394
9579
|
async function rollUpParentBounds(adapter, args, scheduledIds, passes) {
|
|
9395
9580
|
const dirty = args.collectDirty ? args.collectDirty(scheduledIds) : collectDirtyForStructural(args.recomputeParentsFrom, scheduledIds);
|
|
@@ -9398,17 +9583,20 @@ async function rollUpParentBounds(adapter, args, scheduledIds, passes) {
|
|
|
9398
9583
|
dirty,
|
|
9399
9584
|
passes.has(DERIVED_PASS.PARENT_PROGRESS),
|
|
9400
9585
|
{
|
|
9401
|
-
recomputeRealWork: passes.has(DERIVED_PASS.REAL_WORK)
|
|
9586
|
+
recomputeRealWork: passes.has(DERIVED_PASS.REAL_WORK),
|
|
9587
|
+
progressMode: args.recomputeParentProgress ? "zero-when-no-contribution" : "freeze-when-no-contribution"
|
|
9402
9588
|
}
|
|
9403
9589
|
);
|
|
9404
9590
|
}
|
|
9405
9591
|
async function runDerivedRecomputes(deps, passes, now) {
|
|
9406
9592
|
if (passes.has(DERIVED_PASS.EXPECTED_PROGRESS) && now !== null) {
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9593
|
+
if (hasActiveBaseline(deps.adapter)) {
|
|
9594
|
+
runExpectedProgressBase(
|
|
9595
|
+
deps.adapter,
|
|
9596
|
+
now,
|
|
9597
|
+
deps.defaultBaseCalendarId ?? null
|
|
9598
|
+
);
|
|
9599
|
+
}
|
|
9412
9600
|
applyExpectedProgressLive(deps.adapter, now);
|
|
9413
9601
|
applyStatusPass(deps.adapter, deps.sector.statusCriteria);
|
|
9414
9602
|
}
|
|
@@ -9419,6 +9607,13 @@ async function runDerivedRecomputes(deps, passes, now) {
|
|
|
9419
9607
|
await applyCriticalPath(deps.adapter, deps.sector.hoursPerDay);
|
|
9420
9608
|
}
|
|
9421
9609
|
}
|
|
9610
|
+
function hasActiveBaseline(adapter) {
|
|
9611
|
+
let active = false;
|
|
9612
|
+
adapter.forEachActivity((activity) => {
|
|
9613
|
+
if (getActiveBaseline(activity)) active = true;
|
|
9614
|
+
});
|
|
9615
|
+
return active;
|
|
9616
|
+
}
|
|
9422
9617
|
|
|
9423
9618
|
// src/dispatch/handlers/revert-no-op-constraint-edit.ts
|
|
9424
9619
|
var CONSTRAINT_EDIT_COLUMNS = /* @__PURE__ */ new Set([
|
|
@@ -9429,6 +9624,9 @@ var MUST_CONSTRAINTS = /* @__PURE__ */ new Set(["mso", "mfo"]);
|
|
|
9429
9624
|
function isConstraintEditColumn(column) {
|
|
9430
9625
|
return CONSTRAINT_EDIT_COLUMNS.has(column);
|
|
9431
9626
|
}
|
|
9627
|
+
function isExplicitConstraintDateEdit(column) {
|
|
9628
|
+
return column === COLUMN.CONSTRAINT_DATE;
|
|
9629
|
+
}
|
|
9432
9630
|
function revertNoOpConstraintEdit(activityId, priorConstraintDate, adapter, calendars) {
|
|
9433
9631
|
const activity = adapter.getActivity(activityId);
|
|
9434
9632
|
if (!activity) return false;
|
|
@@ -9472,7 +9670,7 @@ function sameDate(first, second) {
|
|
|
9472
9670
|
|
|
9473
9671
|
// src/dispatch/shared/constraint-settle.ts
|
|
9474
9672
|
async function settleConstraintEdits(deps, { postMutationArgs, revertTargets, revert }) {
|
|
9475
|
-
let { scheduledIds
|
|
9673
|
+
let { scheduledIds } = await runPostMutation(
|
|
9476
9674
|
deps,
|
|
9477
9675
|
withCriticalPathSuppressed(postMutationArgs)
|
|
9478
9676
|
);
|
|
@@ -9487,15 +9685,11 @@ async function settleConstraintEdits(deps, { postMutationArgs, revertTargets, re
|
|
|
9487
9685
|
withCriticalPathSuppressed(postMutationArgs)
|
|
9488
9686
|
);
|
|
9489
9687
|
scheduledIds = mergeScheduledIds(scheduledIds, resettled.scheduledIds);
|
|
9490
|
-
scheduledBeforeImages = mergeScheduledBeforeImagesFirstWins(
|
|
9491
|
-
scheduledBeforeImages,
|
|
9492
|
-
resettled.scheduledBeforeImages
|
|
9493
|
-
);
|
|
9494
9688
|
}
|
|
9495
9689
|
if (resolveRunCriticalPath(postMutationArgs.action)) {
|
|
9496
9690
|
await runPostMutation(deps, criticalPathOnlyArgs(postMutationArgs));
|
|
9497
9691
|
}
|
|
9498
|
-
return { scheduledIds
|
|
9692
|
+
return { scheduledIds };
|
|
9499
9693
|
}
|
|
9500
9694
|
function withCriticalPathSuppressed(args) {
|
|
9501
9695
|
return {
|
|
@@ -9517,13 +9711,6 @@ function mergeScheduledIds(firstSettleIds, secondSettleIds) {
|
|
|
9517
9711
|
for (const scheduledId of secondSettleIds) merged.add(scheduledId);
|
|
9518
9712
|
return [...merged];
|
|
9519
9713
|
}
|
|
9520
|
-
function mergeScheduledBeforeImagesFirstWins(firstSettleImages, secondSettleImages) {
|
|
9521
|
-
const merged = new Map(firstSettleImages);
|
|
9522
|
-
for (const [imageId, image] of secondSettleImages) {
|
|
9523
|
-
if (!merged.has(imageId)) merged.set(imageId, image);
|
|
9524
|
-
}
|
|
9525
|
-
return merged;
|
|
9526
|
-
}
|
|
9527
9714
|
|
|
9528
9715
|
// src/dispatch/sir-auto-reject.ts
|
|
9529
9716
|
function datesChanged(before, after) {
|
|
@@ -9558,33 +9745,12 @@ function detectSirAutoReject(beforeSnap, adapter, touchedIds) {
|
|
|
9558
9745
|
|
|
9559
9746
|
// src/dispatch/shared/change-set.ts
|
|
9560
9747
|
function assembleChangeSet(adapter, args) {
|
|
9561
|
-
const allTouched =
|
|
9562
|
-
for (const id of args.touchedIds) allTouched.add(id);
|
|
9563
|
-
for (const id of args.scheduledIds) allTouched.add(id);
|
|
9564
|
-
if (args.includeAllIds) {
|
|
9565
|
-
for (const id of adapter.getAllIds()) allTouched.add(id);
|
|
9566
|
-
}
|
|
9748
|
+
const allTouched = collectChangeSetIds(adapter, args);
|
|
9567
9749
|
const before = new Map(args.beforeSnap);
|
|
9568
9750
|
const captured = adapter.peekWriteCapture();
|
|
9569
9751
|
if (captured) {
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
const beforeImg = captured.before.get(id);
|
|
9573
|
-
const live = adapter.getActivity(id);
|
|
9574
|
-
if (!beforeImg || !live) continue;
|
|
9575
|
-
if (Object.keys(diffActivity(beforeImg, live)).length === 0) continue;
|
|
9576
|
-
before.set(id, beforeImg);
|
|
9577
|
-
allTouched.add(id);
|
|
9578
|
-
}
|
|
9579
|
-
}
|
|
9580
|
-
if (args.scheduledBeforeImages) {
|
|
9581
|
-
for (const [id, img] of args.scheduledBeforeImages) {
|
|
9582
|
-
const sid = String(id);
|
|
9583
|
-
if (!before.has(sid)) {
|
|
9584
|
-
before.set(sid, img);
|
|
9585
|
-
allTouched.add(sid);
|
|
9586
|
-
}
|
|
9587
|
-
}
|
|
9752
|
+
const insertedIds = collectInsertedIds(captured);
|
|
9753
|
+
mergeDirtyBeforeImages(adapter, captured, insertedIds, before, allTouched);
|
|
9588
9754
|
}
|
|
9589
9755
|
const beforeDiffEffects = args.sirDetection === "before-diff" ? detectSirAutoReject(before, adapter, allTouched) : [];
|
|
9590
9756
|
const activityChanges = buildActivityChanges(adapter, before, allTouched);
|
|
@@ -9601,6 +9767,31 @@ function assembleChangeSet(adapter, args) {
|
|
|
9601
9767
|
...warnings.length > 0 ? { warnings } : {}
|
|
9602
9768
|
};
|
|
9603
9769
|
}
|
|
9770
|
+
function collectChangeSetIds(adapter, args) {
|
|
9771
|
+
const ids = /* @__PURE__ */ new Set();
|
|
9772
|
+
for (const id of args.touchedIds) ids.add(id);
|
|
9773
|
+
for (const id of args.scheduledIds) ids.add(id);
|
|
9774
|
+
if (args.includeAllIds) {
|
|
9775
|
+
for (const id of adapter.getAllIds()) ids.add(id);
|
|
9776
|
+
}
|
|
9777
|
+
return ids;
|
|
9778
|
+
}
|
|
9779
|
+
function mergeDirtyBeforeImages(adapter, captured, insertedIds, before, touched) {
|
|
9780
|
+
for (const id of captured.dirty) {
|
|
9781
|
+
if (insertedIds.has(id) || before.has(id)) continue;
|
|
9782
|
+
const beforeImage = captured.before.get(id);
|
|
9783
|
+
const live = adapter.getActivity(id);
|
|
9784
|
+
if (!beforeImage || !live) continue;
|
|
9785
|
+
if (Object.keys(diffActivity(beforeImage, live)).length === 0) continue;
|
|
9786
|
+
before.set(id, beforeImage);
|
|
9787
|
+
touched.add(id);
|
|
9788
|
+
}
|
|
9789
|
+
}
|
|
9790
|
+
function collectInsertedIds(captured) {
|
|
9791
|
+
return new Set(
|
|
9792
|
+
captured.ops.filter((operation) => operation.kind === "insert").map((operation) => operation.activityId)
|
|
9793
|
+
);
|
|
9794
|
+
}
|
|
9604
9795
|
|
|
9605
9796
|
// src/dispatch/conversion.ts
|
|
9606
9797
|
function snapshotToLink(l) {
|
|
@@ -9783,7 +9974,8 @@ async function dispatchInlineEdit(action, options, deps) {
|
|
|
9783
9974
|
runPostProcessorsOnAdapter(
|
|
9784
9975
|
action.activityId,
|
|
9785
9976
|
changes.postProcessors ?? [],
|
|
9786
|
-
adapter
|
|
9977
|
+
adapter,
|
|
9978
|
+
beforeSnap.get(String(action.activityId)) ?? null
|
|
9787
9979
|
);
|
|
9788
9980
|
const linkChanges = diffIncomingLinkLagChanges(
|
|
9789
9981
|
beforeLinkLags,
|
|
@@ -9808,7 +10000,7 @@ async function dispatchInlineEdit(action, options, deps) {
|
|
|
9808
10000
|
options
|
|
9809
10001
|
});
|
|
9810
10002
|
const isConstraintPath = isConstraintEditColumn(action.column);
|
|
9811
|
-
const { scheduledIds
|
|
10003
|
+
const { scheduledIds } = await (isConstraintPath ? settleConstraintEdits(
|
|
9812
10004
|
{
|
|
9813
10005
|
adapter,
|
|
9814
10006
|
scheduler,
|
|
@@ -9817,9 +10009,7 @@ async function dispatchInlineEdit(action, options, deps) {
|
|
|
9817
10009
|
},
|
|
9818
10010
|
{
|
|
9819
10011
|
postMutationArgs: buildPostMutationArgs(),
|
|
9820
|
-
revertTargets: [
|
|
9821
|
-
{ activityId: action.activityId, priorConstraintDate }
|
|
9822
|
-
],
|
|
10012
|
+
revertTargets: isExplicitConstraintDateEdit(action.column) ? [] : [{ activityId: action.activityId, priorConstraintDate }],
|
|
9823
10013
|
revert: (activityId, capturedConstraintDate) => revertNoOpConstraintEdit(
|
|
9824
10014
|
activityId,
|
|
9825
10015
|
capturedConstraintDate,
|
|
@@ -9851,7 +10041,6 @@ async function dispatchInlineEdit(action, options, deps) {
|
|
|
9851
10041
|
beforeSnap,
|
|
9852
10042
|
touchedIds,
|
|
9853
10043
|
scheduledIds,
|
|
9854
|
-
scheduledBeforeImages,
|
|
9855
10044
|
sirDetection: "before-diff",
|
|
9856
10045
|
links: linkChanges,
|
|
9857
10046
|
trackingEvents,
|
|
@@ -9865,6 +10054,40 @@ function columnInvalidatesExpandedLinks(column) {
|
|
|
9865
10054
|
}
|
|
9866
10055
|
|
|
9867
10056
|
// src/dispatch/handlers/link.ts
|
|
10057
|
+
function collectBatchContext(operations, adapter) {
|
|
10058
|
+
const activityIds = /* @__PURE__ */ new Set();
|
|
10059
|
+
const links = /* @__PURE__ */ new Map();
|
|
10060
|
+
for (const operation of operations) {
|
|
10061
|
+
if (operation.kind === "create") {
|
|
10062
|
+
activityIds.add(String(operation.source));
|
|
10063
|
+
activityIds.add(String(operation.target));
|
|
10064
|
+
continue;
|
|
10065
|
+
}
|
|
10066
|
+
const link = adapter.getLink(operation.linkId);
|
|
10067
|
+
if (!link) continue;
|
|
10068
|
+
activityIds.add(String(link.source));
|
|
10069
|
+
activityIds.add(String(link.target));
|
|
10070
|
+
links.set(operation.linkId, { ...link });
|
|
10071
|
+
}
|
|
10072
|
+
return { activityIds, links };
|
|
10073
|
+
}
|
|
10074
|
+
function applyBatchOperations(operations, adapter, linkIdGen, deterministicIds) {
|
|
10075
|
+
return operations.map((operation, index) => {
|
|
10076
|
+
const deterministicId = operation.kind === "create" ? deterministicIds?.get(index) : void 0;
|
|
10077
|
+
const result = applyLinkOperation(operation, {
|
|
10078
|
+
port: adapter,
|
|
10079
|
+
newLinkId: deterministicId ? () => deterministicId : linkIdGen.next
|
|
10080
|
+
});
|
|
10081
|
+
return {
|
|
10082
|
+
op: operation,
|
|
10083
|
+
finalLinkId: result.applied ? result.linkId : null,
|
|
10084
|
+
rejected: result.applied ? null : result.rejected ?? "link_op_rejected"
|
|
10085
|
+
};
|
|
10086
|
+
});
|
|
10087
|
+
}
|
|
10088
|
+
function stringKeyedLinkSnapshots(links) {
|
|
10089
|
+
return new Map([...links].map(([id, link]) => [String(id), link]));
|
|
10090
|
+
}
|
|
9868
10091
|
async function dispatchLink(action, options, deps) {
|
|
9869
10092
|
if (action.kind === "link-create" || action.kind === "link-update") {
|
|
9870
10093
|
const type = action.type;
|
|
@@ -9922,40 +10145,14 @@ function batchOpToLinkOperation(op, hoursPerDay) {
|
|
|
9922
10145
|
async function dispatchLinkBatch(operations, source, options, deps, deterministicIds) {
|
|
9923
10146
|
const { adapter, scheduler, sector, linkIdGen } = deps;
|
|
9924
10147
|
scheduler.invalidateAllCaches();
|
|
9925
|
-
const affectedActivityIds
|
|
9926
|
-
const beforeLinks = /* @__PURE__ */ new Map();
|
|
9927
|
-
for (const op of operations) {
|
|
9928
|
-
if (op.kind === "create") {
|
|
9929
|
-
affectedActivityIds.add(String(op.source));
|
|
9930
|
-
affectedActivityIds.add(String(op.target));
|
|
9931
|
-
} else {
|
|
9932
|
-
const link = adapter.getLink(op.linkId);
|
|
9933
|
-
if (link) {
|
|
9934
|
-
affectedActivityIds.add(String(link.source));
|
|
9935
|
-
affectedActivityIds.add(String(link.target));
|
|
9936
|
-
beforeLinks.set(op.linkId, { ...link });
|
|
9937
|
-
}
|
|
9938
|
-
}
|
|
9939
|
-
}
|
|
10148
|
+
const { activityIds: affectedActivityIds, links: beforeLinks } = collectBatchContext(operations, adapter);
|
|
9940
10149
|
const beforeActivities = snapshotActivities(adapter, affectedActivityIds);
|
|
9941
|
-
const applied =
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
// Si el caller pasó un id deterministic (test override, o el bridge
|
|
9948
|
-
// pasando el id que DHTMLX iba a usar), lo respetamos. Si no, el
|
|
9949
|
-
// generador del core arranca desde Date.now() — mismo formato que
|
|
9950
|
-
// DHTMLX uid(). Ver `generators/link-id-generator.ts`.
|
|
9951
|
-
newLinkId: detId ? () => detId : linkIdGen.next
|
|
9952
|
-
});
|
|
9953
|
-
applied.push({
|
|
9954
|
-
op,
|
|
9955
|
-
finalLinkId: result.applied ? result.linkId : null,
|
|
9956
|
-
rejected: result.applied ? null : result.rejected ?? "link_op_rejected"
|
|
9957
|
-
});
|
|
9958
|
-
}
|
|
10150
|
+
const applied = applyBatchOperations(
|
|
10151
|
+
operations,
|
|
10152
|
+
adapter,
|
|
10153
|
+
linkIdGen,
|
|
10154
|
+
deterministicIds
|
|
10155
|
+
);
|
|
9959
10156
|
if (operations.length === 1 && applied[0] && applied[0].rejected !== null && source.kind !== "inline-edit") {
|
|
9960
10157
|
return { ok: false, reason: applied[0].rejected };
|
|
9961
10158
|
}
|
|
@@ -9975,20 +10172,23 @@ async function dispatchLinkBatch(operations, source, options, deps, deterministi
|
|
|
9975
10172
|
options
|
|
9976
10173
|
}
|
|
9977
10174
|
);
|
|
9978
|
-
for (const id of scheduledIds) {
|
|
9979
|
-
if (!beforeActivities.has(id)) {
|
|
9980
|
-
const a = adapter.getActivity(id);
|
|
9981
|
-
if (a) beforeActivities.set(id, structuredCloneActivity(a));
|
|
9982
|
-
}
|
|
9983
|
-
}
|
|
9984
10175
|
const linkChanges = buildLinkChangesForBatch(applied, beforeLinks, adapter);
|
|
9985
|
-
const beforeLinksByStringId = /* @__PURE__ */ new Map();
|
|
9986
|
-
for (const [linkId, snap] of beforeLinks) {
|
|
9987
|
-
beforeLinksByStringId.set(String(linkId), snap);
|
|
9988
|
-
}
|
|
9989
10176
|
return {
|
|
9990
10177
|
ok: true,
|
|
9991
|
-
|
|
10178
|
+
linkVerdicts: applied.map(
|
|
10179
|
+
(operation, operationIndex) => operation.rejected === null ? {
|
|
10180
|
+
operationIndex,
|
|
10181
|
+
kind: operation.op.kind,
|
|
10182
|
+
ok: true,
|
|
10183
|
+
linkId: String(operation.finalLinkId)
|
|
10184
|
+
} : {
|
|
10185
|
+
operationIndex,
|
|
10186
|
+
kind: operation.op.kind,
|
|
10187
|
+
ok: false,
|
|
10188
|
+
reason: operation.rejected
|
|
10189
|
+
}
|
|
10190
|
+
),
|
|
10191
|
+
__beforeLinks: stringKeyedLinkSnapshots(beforeLinks),
|
|
9992
10192
|
changes: assembleChangeSet(adapter, {
|
|
9993
10193
|
source,
|
|
9994
10194
|
beforeSnap: beforeActivities,
|
|
@@ -10361,8 +10561,7 @@ var NEW_ACTIVITY_DEFAULTS = Object.freeze({
|
|
|
10361
10561
|
expectedProgress: null,
|
|
10362
10562
|
expectedProgressBaseline: null,
|
|
10363
10563
|
status: null,
|
|
10364
|
-
criticalPath: null
|
|
10365
|
-
promotionRestore: null
|
|
10564
|
+
criticalPath: null
|
|
10366
10565
|
});
|
|
10367
10566
|
var FIRST_ACTIVITY_TEXT = "New Master Plan";
|
|
10368
10567
|
var NEW_ACTIVITY_TEXT = "New Activity";
|
|
@@ -10467,6 +10666,23 @@ function resolveStartDate(parent, fallback, override) {
|
|
|
10467
10666
|
}
|
|
10468
10667
|
|
|
10469
10668
|
// src/creation/parent-mutation-pipeline.ts
|
|
10669
|
+
function snapshotOf(parent) {
|
|
10670
|
+
const type = parent.type;
|
|
10671
|
+
if (type !== "task" && type !== "milestone") return null;
|
|
10672
|
+
const startDate = parent.startDate;
|
|
10673
|
+
const endDate = parent.endDate;
|
|
10674
|
+
if (!(startDate instanceof Date) || !(endDate instanceof Date)) return null;
|
|
10675
|
+
return {
|
|
10676
|
+
type,
|
|
10677
|
+
startDate: new Date(startDate),
|
|
10678
|
+
endDate: new Date(endDate),
|
|
10679
|
+
durationHours: Number(parent.durationHours ?? 0),
|
|
10680
|
+
expectedProgressBaseline: typeof parent.expectedProgressBaseline === "number" ? parent.expectedProgressBaseline : null,
|
|
10681
|
+
constraintType: parent.constraintType ?? null,
|
|
10682
|
+
constraintDate: parent.constraintDate instanceof Date ? new Date(parent.constraintDate) : null,
|
|
10683
|
+
progress: Number(parent.progress ?? 0)
|
|
10684
|
+
};
|
|
10685
|
+
}
|
|
10470
10686
|
function buildParentMutations(input) {
|
|
10471
10687
|
const parent = input.getParent(input.parentId);
|
|
10472
10688
|
if (!parent) return null;
|
|
@@ -10485,14 +10701,18 @@ function buildParentMutations(input) {
|
|
|
10485
10701
|
}
|
|
10486
10702
|
if (wasPromotableLeaf) {
|
|
10487
10703
|
fields.type = PROMOTION_TARGET_TYPE;
|
|
10488
|
-
fields.constraintType = CONSTRAINT_TYPE2.ASAP;
|
|
10489
|
-
fields.constraintDate = null;
|
|
10490
10704
|
if ((input.promotionSource ?? "create") === "create") {
|
|
10705
|
+
fields.constraintType = CONSTRAINT_TYPE2.ASAP;
|
|
10706
|
+
fields.constraintDate = null;
|
|
10491
10707
|
fields.progress = 0;
|
|
10492
10708
|
fields.expectedProgressBaseline = 0;
|
|
10493
10709
|
}
|
|
10494
10710
|
}
|
|
10495
|
-
return {
|
|
10711
|
+
return {
|
|
10712
|
+
parentId: input.parentId,
|
|
10713
|
+
fields,
|
|
10714
|
+
promotionSnapshot: snapshotOf(parent)
|
|
10715
|
+
};
|
|
10496
10716
|
}
|
|
10497
10717
|
function isPromotableLeaf(parent) {
|
|
10498
10718
|
return isPromotableLeafType(parent.type) && (!Array.isArray(parent.newActivityIds) || parent.newActivityIds.length === 0);
|
|
@@ -10718,6 +10938,12 @@ function applyParentMutations(action, newId, parent, adapter, preserveParentCust
|
|
|
10718
10938
|
if (skipCustomIdMutation) continue;
|
|
10719
10939
|
setActivityFieldDynamic(adapter, parentMutations.parentId, key, value);
|
|
10720
10940
|
}
|
|
10941
|
+
if (parentMutations.promotionSnapshot) {
|
|
10942
|
+
adapter.setPromotionSnapshot(
|
|
10943
|
+
parentMutations.parentId,
|
|
10944
|
+
parentMutations.promotionSnapshot
|
|
10945
|
+
);
|
|
10946
|
+
}
|
|
10721
10947
|
}
|
|
10722
10948
|
function recomputeAndFoldCorrelatives(adapter, newId, beforeSnap, initialTouched, opts) {
|
|
10723
10949
|
if (opts.skipCorrelativeRecompute) return;
|
|
@@ -10731,7 +10957,7 @@ function recomputeAndFoldCorrelatives(adapter, newId, beforeSnap, initialTouched
|
|
|
10731
10957
|
initialTouched
|
|
10732
10958
|
);
|
|
10733
10959
|
}
|
|
10734
|
-
function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, hoursPerDay
|
|
10960
|
+
function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, hoursPerDay) {
|
|
10735
10961
|
const { newId, newActivity, beforeSnap, initialTouched } = coreResult;
|
|
10736
10962
|
const trackingEvent = {
|
|
10737
10963
|
name: DISPATCH_TRACK_EVENT.ACTIVITY_CREATION,
|
|
@@ -10744,17 +10970,11 @@ function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, hoursPe
|
|
|
10744
10970
|
if (action.eventSource !== void 0) {
|
|
10745
10971
|
trackingEvent.properties.event_source = action.eventSource;
|
|
10746
10972
|
}
|
|
10747
|
-
const existingScheduledBeforeImages = scheduledBeforeImages ? new Map(
|
|
10748
|
-
[...scheduledBeforeImages].filter(
|
|
10749
|
-
([scheduledId]) => String(scheduledId) !== String(newId)
|
|
10750
|
-
)
|
|
10751
|
-
) : void 0;
|
|
10752
10973
|
return assembleChangeSet(adapter, {
|
|
10753
10974
|
source: action,
|
|
10754
10975
|
beforeSnap,
|
|
10755
10976
|
touchedIds: initialTouched,
|
|
10756
10977
|
scheduledIds,
|
|
10757
|
-
scheduledBeforeImages: existingScheduledBeforeImages,
|
|
10758
10978
|
trackingEvents: [trackingEvent]});
|
|
10759
10979
|
}
|
|
10760
10980
|
async function dispatchActivityCreate(action, options, deps) {
|
|
@@ -10784,14 +11004,13 @@ async function dispatchActivityCreate(action, options, deps) {
|
|
|
10784
11004
|
} finally {
|
|
10785
11005
|
adapter.setActivityField(newId, "autoScheduling", createdAutoScheduling);
|
|
10786
11006
|
}
|
|
10787
|
-
const { scheduledIds
|
|
11007
|
+
const { scheduledIds } = scheduleOutcome;
|
|
10788
11008
|
const changeset = buildCreateChangeSet(
|
|
10789
11009
|
adapter,
|
|
10790
11010
|
action,
|
|
10791
11011
|
core,
|
|
10792
11012
|
scheduledIds,
|
|
10793
|
-
sector.hoursPerDay
|
|
10794
|
-
scheduledBeforeImages
|
|
11013
|
+
sector.hoursPerDay
|
|
10795
11014
|
);
|
|
10796
11015
|
return { ok: true, changes: changeset };
|
|
10797
11016
|
}
|
|
@@ -10936,7 +11155,7 @@ async function dispatchActivityPaste(action, options, deps) {
|
|
|
10936
11155
|
rejected: res.applied ? null : res.rejected ?? "link_op_rejected"
|
|
10937
11156
|
});
|
|
10938
11157
|
}
|
|
10939
|
-
const { scheduledIds
|
|
11158
|
+
const { scheduledIds } = await runPostMutation(
|
|
10940
11159
|
{
|
|
10941
11160
|
adapter,
|
|
10942
11161
|
scheduler,
|
|
@@ -10963,14 +11182,6 @@ async function dispatchActivityPaste(action, options, deps) {
|
|
|
10963
11182
|
beforeSnap,
|
|
10964
11183
|
touchedIds: touched,
|
|
10965
11184
|
scheduledIds,
|
|
10966
|
-
// Existing rows the autoscheduler moved need a before-image (the global
|
|
10967
|
-
// beforeSnap used to supply it). Pass scheduledBeforeImages but filter out
|
|
10968
|
-
// the pasted rows — their pre-schedule image would flip them to `updated`.
|
|
10969
|
-
scheduledBeforeImages: new Map(
|
|
10970
|
-
[...scheduledBeforeImages].filter(
|
|
10971
|
-
([schedId]) => !createdIdSet.has(String(schedId))
|
|
10972
|
-
)
|
|
10973
|
-
),
|
|
10974
11185
|
links: linkChanges,
|
|
10975
11186
|
hoursPerDay: sector.hoursPerDay
|
|
10976
11187
|
})
|
|
@@ -11105,10 +11316,8 @@ function collectIncidentLinkIds(activityIds, adapter) {
|
|
|
11105
11316
|
}
|
|
11106
11317
|
|
|
11107
11318
|
// src/internal/hierarchy/parent-demotion.ts
|
|
11108
|
-
function restoredPromotionFields(
|
|
11109
|
-
|
|
11110
|
-
if (typeof restore !== "object" || restore === null) return null;
|
|
11111
|
-
if (restore.type !== "task" && restore.type !== "milestone") return null;
|
|
11319
|
+
function restoredPromotionFields(restore) {
|
|
11320
|
+
if (!restore) return null;
|
|
11112
11321
|
return {
|
|
11113
11322
|
type: restore.type,
|
|
11114
11323
|
startDate: new Date(restore.startDate),
|
|
@@ -11117,16 +11326,20 @@ function restoredPromotionFields(parent) {
|
|
|
11117
11326
|
expectedProgressBaseline: restore.expectedProgressBaseline,
|
|
11118
11327
|
constraintType: restore.constraintType,
|
|
11119
11328
|
constraintDate: restore.constraintDate ? new Date(restore.constraintDate) : null,
|
|
11120
|
-
progress: restore.progress
|
|
11121
|
-
promotionRestore: null
|
|
11329
|
+
progress: restore.progress
|
|
11122
11330
|
};
|
|
11123
11331
|
}
|
|
11332
|
+
function canonicalEndDateField(parent, durationHours, computeEndDate) {
|
|
11333
|
+
const startDate = parent.startDate;
|
|
11334
|
+
if (!computeEndDate || !(startDate instanceof Date)) return {};
|
|
11335
|
+
return { endDate: computeEndDate(startDate, durationHours) };
|
|
11336
|
+
}
|
|
11124
11337
|
function buildParentDemotionMutations(input) {
|
|
11125
11338
|
if (input.remainingChildIds.length > 0) return null;
|
|
11126
11339
|
const parent = input.parent;
|
|
11127
11340
|
const canonicalDuration = input.defaultDurationHours;
|
|
11128
11341
|
const hasCanonicalDefaults = Number.isFinite(canonicalDuration);
|
|
11129
|
-
const fields = restoredPromotionFields(
|
|
11342
|
+
const fields = restoredPromotionFields(input.promotionSnapshot) ?? (hasCanonicalDefaults ? {
|
|
11130
11343
|
type: DEMOTION_TARGET_TYPE,
|
|
11131
11344
|
durationHours: canonicalDuration,
|
|
11132
11345
|
progress: 0,
|
|
@@ -11134,7 +11347,11 @@ function buildParentDemotionMutations(input) {
|
|
|
11134
11347
|
constraintType: "asap",
|
|
11135
11348
|
constraintDate: null,
|
|
11136
11349
|
hasNewActivities: false,
|
|
11137
|
-
|
|
11350
|
+
...canonicalEndDateField(
|
|
11351
|
+
parent,
|
|
11352
|
+
canonicalDuration,
|
|
11353
|
+
input.computeEndDate
|
|
11354
|
+
)
|
|
11138
11355
|
} : { type: DEMOTION_TARGET_TYPE });
|
|
11139
11356
|
if (input.idsRemoved && input.idsRemoved.size > 0) {
|
|
11140
11357
|
const existing = Array.isArray(parent.newActivityIds) ? parent.newActivityIds : [];
|
|
@@ -11231,6 +11448,8 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11231
11448
|
parent,
|
|
11232
11449
|
remainingChildIds: remainingChildren.map(String),
|
|
11233
11450
|
defaultDurationHours: sector.hoursPerDay,
|
|
11451
|
+
promotionSnapshot: adapter.getPromotionSnapshot(parentId),
|
|
11452
|
+
computeEndDate: (startDate, durationHours) => adapter.calculateEndDate({ startDate, durationHours, task: parent }),
|
|
11234
11453
|
idsRemoved: toDelete
|
|
11235
11454
|
});
|
|
11236
11455
|
if (!demotion) {
|
|
@@ -11266,7 +11485,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11266
11485
|
if (shift.before !== void 0) beforeImage.correlativeId = shift.before;
|
|
11267
11486
|
beforeSnap.set(shift.activityId, beforeImage);
|
|
11268
11487
|
}
|
|
11269
|
-
const { scheduledIds
|
|
11488
|
+
const { scheduledIds } = await runPostMutation(
|
|
11270
11489
|
{
|
|
11271
11490
|
adapter,
|
|
11272
11491
|
scheduler,
|
|
@@ -11303,7 +11522,6 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11303
11522
|
beforeSnap,
|
|
11304
11523
|
touchedIds,
|
|
11305
11524
|
scheduledIds,
|
|
11306
|
-
scheduledBeforeImages,
|
|
11307
11525
|
sirDetection: "after-diff",
|
|
11308
11526
|
links: buildLinkDeletions(beforeLinks),
|
|
11309
11527
|
trackingEvents: [trackingEvent],
|
|
@@ -11475,6 +11693,12 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
11475
11693
|
for (const [key, value] of Object.entries(promotion.fields)) {
|
|
11476
11694
|
setActivityFieldDynamic(adapter, promotion.parentId, key, value);
|
|
11477
11695
|
}
|
|
11696
|
+
if (promotion.promotionSnapshot) {
|
|
11697
|
+
adapter.setPromotionSnapshot(
|
|
11698
|
+
promotion.parentId,
|
|
11699
|
+
promotion.promotionSnapshot
|
|
11700
|
+
);
|
|
11701
|
+
}
|
|
11478
11702
|
if (promotion.fields.customId === null) {
|
|
11479
11703
|
releaseClearedCustomId(oldNewParentCustomId, deps.customIdTracker);
|
|
11480
11704
|
}
|
|
@@ -11489,6 +11713,12 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
11489
11713
|
parent: oldParentActivity,
|
|
11490
11714
|
remainingChildIds: remaining.map(String),
|
|
11491
11715
|
defaultDurationHours: sector.hoursPerDay,
|
|
11716
|
+
promotionSnapshot: adapter.getPromotionSnapshot(oldParentKey),
|
|
11717
|
+
computeEndDate: (startDate, durationHours) => adapter.calculateEndDate({
|
|
11718
|
+
startDate,
|
|
11719
|
+
durationHours,
|
|
11720
|
+
task: oldParentActivity
|
|
11721
|
+
}),
|
|
11492
11722
|
idsRemoved: /* @__PURE__ */ new Set([action.activityId])
|
|
11493
11723
|
});
|
|
11494
11724
|
if (demotion) {
|
|
@@ -11528,7 +11758,8 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
11528
11758
|
if (shift.before !== void 0) beforeImage.correlativeId = shift.before;
|
|
11529
11759
|
beforeSnap.set(shift.activityId, beforeImage);
|
|
11530
11760
|
}
|
|
11531
|
-
const
|
|
11761
|
+
const sourceParentDirtyIds = parentChanged && oldParentKey !== ROOT_PARENT_ID ? adapter.getChildren(oldParentKey).map(String) : [];
|
|
11762
|
+
const { scheduledIds } = await runPostMutation(
|
|
11532
11763
|
{
|
|
11533
11764
|
adapter,
|
|
11534
11765
|
scheduler,
|
|
@@ -11538,9 +11769,14 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
11538
11769
|
{
|
|
11539
11770
|
action,
|
|
11540
11771
|
autoscheduleFrom: action.activityId,
|
|
11541
|
-
recomputeParentsFrom: [
|
|
11772
|
+
recomputeParentsFrom: [
|
|
11773
|
+
action.activityId,
|
|
11774
|
+
action.parentId,
|
|
11775
|
+
...sourceParentDirtyIds
|
|
11776
|
+
],
|
|
11542
11777
|
now: deps.now,
|
|
11543
|
-
options
|
|
11778
|
+
options,
|
|
11779
|
+
recomputeParentProgress: parentChanged
|
|
11544
11780
|
}
|
|
11545
11781
|
);
|
|
11546
11782
|
const invalidLinkIds = parentChanged ? collectInvalidLinkIdsForCycle([action.activityId], adapter) : [];
|
|
@@ -11564,7 +11800,6 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
11564
11800
|
beforeSnap,
|
|
11565
11801
|
touchedIds: touched,
|
|
11566
11802
|
scheduledIds,
|
|
11567
|
-
scheduledBeforeImages,
|
|
11568
11803
|
sirDetection: "before-diff",
|
|
11569
11804
|
trackingEvents: [trackingEvent],
|
|
11570
11805
|
hoursPerDay: sector.hoursPerDay
|
|
@@ -11654,6 +11889,12 @@ async function dispatchActivityIndent(action, options, deps) {
|
|
|
11654
11889
|
for (const [key, value] of Object.entries(promotion.fields)) {
|
|
11655
11890
|
setActivityFieldDynamic(adapter, promotion.parentId, key, value);
|
|
11656
11891
|
}
|
|
11892
|
+
if (promotion.promotionSnapshot) {
|
|
11893
|
+
adapter.setPromotionSnapshot(
|
|
11894
|
+
promotion.parentId,
|
|
11895
|
+
promotion.promotionSnapshot
|
|
11896
|
+
);
|
|
11897
|
+
}
|
|
11657
11898
|
if (!wasTrackedAsNewChild) {
|
|
11658
11899
|
adapter.setActivityField(
|
|
11659
11900
|
newParentId,
|
|
@@ -11699,7 +11940,7 @@ async function dispatchActivityIndent(action, options, deps) {
|
|
|
11699
11940
|
if (shift.before !== void 0) beforeImage.correlativeId = shift.before;
|
|
11700
11941
|
beforeSnap.set(shift.activityId, beforeImage);
|
|
11701
11942
|
}
|
|
11702
|
-
const { scheduledIds
|
|
11943
|
+
const { scheduledIds } = await runPostMutation(
|
|
11703
11944
|
{
|
|
11704
11945
|
adapter,
|
|
11705
11946
|
scheduler,
|
|
@@ -11756,12 +11997,21 @@ async function dispatchActivityIndent(action, options, deps) {
|
|
|
11756
11997
|
}
|
|
11757
11998
|
return {
|
|
11758
11999
|
ok: true,
|
|
12000
|
+
activityVerdicts: action.activityIds.map((activityId) => {
|
|
12001
|
+
const failure = failed.find(
|
|
12002
|
+
(entry) => String(entry.activityId) === String(activityId)
|
|
12003
|
+
);
|
|
12004
|
+
return failure ? {
|
|
12005
|
+
activityId: String(activityId),
|
|
12006
|
+
ok: false,
|
|
12007
|
+
reason: failure.reason
|
|
12008
|
+
} : { activityId: String(activityId), ok: true };
|
|
12009
|
+
}),
|
|
11759
12010
|
changes: assembleChangeSet(adapter, {
|
|
11760
12011
|
source: action,
|
|
11761
12012
|
beforeSnap,
|
|
11762
12013
|
touchedIds: touched,
|
|
11763
12014
|
scheduledIds,
|
|
11764
|
-
scheduledBeforeImages,
|
|
11765
12015
|
trackingEvents: [trackingEvent],
|
|
11766
12016
|
hoursPerDay: sector.hoursPerDay
|
|
11767
12017
|
})
|
|
@@ -11883,6 +12133,12 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
11883
12133
|
parent: parentActivity,
|
|
11884
12134
|
remainingChildIds: remaining.map(String),
|
|
11885
12135
|
defaultDurationHours: sector.hoursPerDay,
|
|
12136
|
+
promotionSnapshot: adapter.getPromotionSnapshot(oldParentKey),
|
|
12137
|
+
computeEndDate: (startDate, durationHours) => adapter.calculateEndDate({
|
|
12138
|
+
startDate,
|
|
12139
|
+
durationHours,
|
|
12140
|
+
task: parentActivity
|
|
12141
|
+
}),
|
|
11886
12142
|
idsRemoved: new Set(
|
|
11887
12143
|
planned.filter((plan) => plan.oldParentKey === oldParentKey).map((plan) => plan.activityId)
|
|
11888
12144
|
)
|
|
@@ -11928,7 +12184,7 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
11928
12184
|
if (shift.before !== void 0) beforeImage.correlativeId = shift.before;
|
|
11929
12185
|
beforeSnap.set(shift.activityId, beforeImage);
|
|
11930
12186
|
}
|
|
11931
|
-
const { scheduledIds
|
|
12187
|
+
const { scheduledIds } = await runPostMutation(
|
|
11932
12188
|
{
|
|
11933
12189
|
adapter,
|
|
11934
12190
|
scheduler,
|
|
@@ -11961,12 +12217,21 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
11961
12217
|
}
|
|
11962
12218
|
return {
|
|
11963
12219
|
ok: true,
|
|
12220
|
+
activityVerdicts: action.activityIds.map((activityId) => {
|
|
12221
|
+
const failure = failed.find(
|
|
12222
|
+
(entry) => String(entry.activityId) === String(activityId)
|
|
12223
|
+
);
|
|
12224
|
+
return failure ? {
|
|
12225
|
+
activityId: String(activityId),
|
|
12226
|
+
ok: false,
|
|
12227
|
+
reason: failure.reason
|
|
12228
|
+
} : { activityId: String(activityId), ok: true };
|
|
12229
|
+
}),
|
|
11964
12230
|
changes: assembleChangeSet(adapter, {
|
|
11965
12231
|
source: action,
|
|
11966
12232
|
beforeSnap,
|
|
11967
12233
|
touchedIds: touched,
|
|
11968
12234
|
scheduledIds,
|
|
11969
|
-
scheduledBeforeImages,
|
|
11970
12235
|
trackingEvents: [trackingEvent],
|
|
11971
12236
|
hoursPerDay: sector.hoursPerDay
|
|
11972
12237
|
})
|
|
@@ -12058,11 +12323,15 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
12058
12323
|
mergeBeforeSnapshots(beforeSnap, editTouched, deps);
|
|
12059
12324
|
for (const id of editTouched) touchedIds.add(id);
|
|
12060
12325
|
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
12326
|
+
const preEditSnapshot = snapshotActivities(adapter, /* @__PURE__ */ new Set([String(edit.activityId)])).get(
|
|
12327
|
+
String(edit.activityId)
|
|
12328
|
+
) ?? null;
|
|
12061
12329
|
applyFieldChanges(adapter, edit.activityId, outcome.changes);
|
|
12062
12330
|
runPostProcessorsOnAdapter(
|
|
12063
12331
|
edit.activityId,
|
|
12064
12332
|
outcome.changes.postProcessors ?? [],
|
|
12065
|
-
adapter
|
|
12333
|
+
adapter,
|
|
12334
|
+
preEditSnapshot
|
|
12066
12335
|
);
|
|
12067
12336
|
linkChanges.push(
|
|
12068
12337
|
...diffIncomingLinkLagChanges(beforeLinkLags, adapter, edit.activityId)
|
|
@@ -12197,6 +12466,7 @@ async function dispatchBulkEdit(action, options, deps) {
|
|
|
12197
12466
|
const verdicts = [];
|
|
12198
12467
|
const beforeSnap = /* @__PURE__ */ new Map();
|
|
12199
12468
|
const constraintPriorsByActivity = /* @__PURE__ */ new Map();
|
|
12469
|
+
const explicitDateEditActivities = /* @__PURE__ */ new Set();
|
|
12200
12470
|
const touchedIds = /* @__PURE__ */ new Set();
|
|
12201
12471
|
const trackingEvents = [];
|
|
12202
12472
|
const linkChanges = [];
|
|
@@ -12215,14 +12485,21 @@ async function dispatchBulkEdit(action, options, deps) {
|
|
|
12215
12485
|
mergeBeforeSnapshots2(beforeSnap, editTouched, deps);
|
|
12216
12486
|
for (const touchedId of editTouched) touchedIds.add(touchedId);
|
|
12217
12487
|
recordConstraintPrior(constraintPriorsByActivity, edit, beforeSnap);
|
|
12488
|
+
if (isExplicitConstraintDateEdit(edit.column)) {
|
|
12489
|
+
explicitDateEditActivities.add(String(edit.activityId));
|
|
12490
|
+
}
|
|
12218
12491
|
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
12492
|
+
const preEditSnapshot = snapshotActivities(adapter, /* @__PURE__ */ new Set([String(edit.activityId)])).get(
|
|
12493
|
+
String(edit.activityId)
|
|
12494
|
+
) ?? null;
|
|
12219
12495
|
const customIdBeforeApply = readLiveCustomId(edit, deps);
|
|
12220
12496
|
const changes = outcome.changes;
|
|
12221
12497
|
applyFieldChanges(adapter, edit.activityId, changes);
|
|
12222
12498
|
runPostProcessorsOnAdapter(
|
|
12223
12499
|
edit.activityId,
|
|
12224
12500
|
outcome.changes.postProcessors ?? [],
|
|
12225
|
-
adapter
|
|
12501
|
+
adapter,
|
|
12502
|
+
preEditSnapshot
|
|
12226
12503
|
);
|
|
12227
12504
|
syncCustomIdTracker(edit, customIdBeforeApply, deps);
|
|
12228
12505
|
linkChanges.push(
|
|
@@ -12244,7 +12521,7 @@ async function dispatchBulkEdit(action, options, deps) {
|
|
|
12244
12521
|
options
|
|
12245
12522
|
};
|
|
12246
12523
|
const batchHasConstraintEdits = constraintPriorsByActivity.size > 0;
|
|
12247
|
-
const { scheduledIds
|
|
12524
|
+
const { scheduledIds } = await (batchHasConstraintEdits ? settleConstraintEdits(
|
|
12248
12525
|
{
|
|
12249
12526
|
adapter,
|
|
12250
12527
|
scheduler,
|
|
@@ -12253,12 +12530,12 @@ async function dispatchBulkEdit(action, options, deps) {
|
|
|
12253
12530
|
},
|
|
12254
12531
|
{
|
|
12255
12532
|
postMutationArgs,
|
|
12256
|
-
revertTargets: [...constraintPriorsByActivity].
|
|
12257
|
-
([activityId
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
),
|
|
12533
|
+
revertTargets: [...constraintPriorsByActivity].filter(
|
|
12534
|
+
([activityId]) => !explicitDateEditActivities.has(activityId)
|
|
12535
|
+
).map(([activityId, priorConstraintDate]) => ({
|
|
12536
|
+
activityId,
|
|
12537
|
+
priorConstraintDate
|
|
12538
|
+
})),
|
|
12262
12539
|
revert: (activityId, priorConstraintDate) => revertNoOpConstraintEdit(
|
|
12263
12540
|
activityId,
|
|
12264
12541
|
priorConstraintDate,
|
|
@@ -12286,7 +12563,6 @@ async function dispatchBulkEdit(action, options, deps) {
|
|
|
12286
12563
|
// Los dependientes que mueve el autoscheduler no están en beforeSnap;
|
|
12287
12564
|
// sin su before-image el diff los marca FALSE-CREATED y el undo los
|
|
12288
12565
|
// borra (fix 4bbccbc de inline-edit).
|
|
12289
|
-
scheduledBeforeImages,
|
|
12290
12566
|
sirDetection: "before-diff",
|
|
12291
12567
|
links: linkChanges,
|
|
12292
12568
|
trackingEvents,
|
|
@@ -15885,8 +16161,15 @@ var WriteCapture = class {
|
|
|
15885
16161
|
/** Pre-applyResults clone of an activity the autoscheduler will move (first-wins). */
|
|
15886
16162
|
noteScheduledBefore(activityId, current) {
|
|
15887
16163
|
const journal = this._journal;
|
|
15888
|
-
if (!journal
|
|
15889
|
-
|
|
16164
|
+
if (!journal) return;
|
|
16165
|
+
const beforeImage = cloneCoreActivity(current);
|
|
16166
|
+
if (!journal.scheduledBefore.has(activityId)) {
|
|
16167
|
+
journal.scheduledBefore.set(activityId, beforeImage);
|
|
16168
|
+
}
|
|
16169
|
+
if (!journal.before.has(activityId)) {
|
|
16170
|
+
journal.before.set(activityId, beforeImage);
|
|
16171
|
+
}
|
|
16172
|
+
journal.dirty.add(activityId);
|
|
15890
16173
|
}
|
|
15891
16174
|
/** Field write on a link — pre-mutation clone on first write per link id. */
|
|
15892
16175
|
noteLinkField(linkId, current) {
|
|
@@ -15900,20 +16183,6 @@ var WriteCapture = class {
|
|
|
15900
16183
|
});
|
|
15901
16184
|
}
|
|
15902
16185
|
};
|
|
15903
|
-
function cloneCoreActivity(activity) {
|
|
15904
|
-
return {
|
|
15905
|
-
...activity,
|
|
15906
|
-
startDate: new Date(activity.startDate),
|
|
15907
|
-
endDate: new Date(activity.endDate),
|
|
15908
|
-
constraintDate: activity.constraintDate ? new Date(activity.constraintDate) : null,
|
|
15909
|
-
dateOrigin: activity.dateOrigin ? new Date(activity.dateOrigin) : null,
|
|
15910
|
-
newActivityIds: [...activity.newActivityIds],
|
|
15911
|
-
pendingRequestIds: [...activity.pendingRequestIds],
|
|
15912
|
-
responsableIds: [...activity.responsableIds],
|
|
15913
|
-
tagIds: [...activity.tagIds],
|
|
15914
|
-
baselinePoints: [...activity.baselinePoints]
|
|
15915
|
-
};
|
|
15916
|
-
}
|
|
15917
16186
|
function cloneLink(link) {
|
|
15918
16187
|
return { ...link };
|
|
15919
16188
|
}
|
|
@@ -16167,6 +16436,13 @@ var ScheduleState = class {
|
|
|
16167
16436
|
// whether a soft constraint repositioned the activity. Never persisted,
|
|
16168
16437
|
// never in the ChangeSet — not domain data on CoreActivity.
|
|
16169
16438
|
_lastStartDate = /* @__PURE__ */ new Map();
|
|
16439
|
+
// Transient gesture state: what a leaf looked like right before it gained its
|
|
16440
|
+
// first child and became a summary. Read by the demotion to give the activity
|
|
16441
|
+
// its own values back instead of the aggregate its children left behind. Not
|
|
16442
|
+
// persisted and not in the ChangeSet: it only makes sense inside the session
|
|
16443
|
+
// that performed the promotion. Kept (not consumed) so undo/redo of a
|
|
16444
|
+
// demotion restores identically every time.
|
|
16445
|
+
_promotionSnapshot = /* @__PURE__ */ new Map();
|
|
16170
16446
|
_links = /* @__PURE__ */ new Map();
|
|
16171
16447
|
_outgoing = /* @__PURE__ */ new Map();
|
|
16172
16448
|
_incoming = /* @__PURE__ */ new Map();
|
|
@@ -16182,6 +16458,7 @@ var ScheduleState = class {
|
|
|
16182
16458
|
_viewState = new ViewStateStore();
|
|
16183
16459
|
_viewStateBefore = null;
|
|
16184
16460
|
_lastStartDateBefore = null;
|
|
16461
|
+
_promotionSnapshotBefore = null;
|
|
16185
16462
|
// Calendar arithmetic + in-dispatch memoization. Assigned in the
|
|
16186
16463
|
// constructor once the calendar reader is built. See `CalendarCalculator`.
|
|
16187
16464
|
_calendar;
|
|
@@ -16266,6 +16543,19 @@ var ScheduleState = class {
|
|
|
16266
16543
|
this._captureLastStartDateOnce();
|
|
16267
16544
|
this._lastStartDate.set(String(activityId), startDate);
|
|
16268
16545
|
}
|
|
16546
|
+
getPromotionSnapshot(activityId) {
|
|
16547
|
+
return this._promotionSnapshot.get(String(activityId)) ?? null;
|
|
16548
|
+
}
|
|
16549
|
+
setPromotionSnapshot(activityId, snapshot) {
|
|
16550
|
+
this._capturePromotionSnapshotOnce();
|
|
16551
|
+
this._promotionSnapshot.set(String(activityId), snapshot);
|
|
16552
|
+
}
|
|
16553
|
+
_capturePromotionSnapshotOnce() {
|
|
16554
|
+
if (this._writeCapture.peek() === null) return;
|
|
16555
|
+
if (this._promotionSnapshotBefore === null) {
|
|
16556
|
+
this._promotionSnapshotBefore = new Map(this._promotionSnapshot);
|
|
16557
|
+
}
|
|
16558
|
+
}
|
|
16269
16559
|
_captureLastStartDateOnce() {
|
|
16270
16560
|
if (this._writeCapture.peek() === null) return;
|
|
16271
16561
|
if (this._lastStartDateBefore === null) {
|
|
@@ -16382,6 +16672,7 @@ var ScheduleState = class {
|
|
|
16382
16672
|
this._writeCapture.begin();
|
|
16383
16673
|
this._viewStateBefore = null;
|
|
16384
16674
|
this._lastStartDateBefore = null;
|
|
16675
|
+
this._promotionSnapshotBefore = null;
|
|
16385
16676
|
}
|
|
16386
16677
|
/** The live journal, or null when no capture is active. */
|
|
16387
16678
|
peekWriteCapture() {
|
|
@@ -16392,6 +16683,7 @@ var ScheduleState = class {
|
|
|
16392
16683
|
this._writeCapture.end();
|
|
16393
16684
|
this._viewStateBefore = null;
|
|
16394
16685
|
this._lastStartDateBefore = null;
|
|
16686
|
+
this._promotionSnapshotBefore = null;
|
|
16395
16687
|
}
|
|
16396
16688
|
// -- Replay-specific mutators --------------------------------------------
|
|
16397
16689
|
setActivityField(activityId, field, value) {
|
|
@@ -16543,6 +16835,9 @@ var ScheduleState = class {
|
|
|
16543
16835
|
if (this._viewStateBefore !== null) {
|
|
16544
16836
|
this._viewState.restore(this._viewStateBefore);
|
|
16545
16837
|
}
|
|
16838
|
+
if (this._promotionSnapshotBefore !== null) {
|
|
16839
|
+
this._promotionSnapshot = this._promotionSnapshotBefore;
|
|
16840
|
+
}
|
|
16546
16841
|
if (this._lastStartDateBefore !== null) {
|
|
16547
16842
|
this._lastStartDate = this._lastStartDateBefore;
|
|
16548
16843
|
}
|
|
@@ -16675,6 +16970,13 @@ function readSelectedActivityIds(state) {
|
|
|
16675
16970
|
return state.checkedIds().map(String);
|
|
16676
16971
|
}
|
|
16677
16972
|
function readHasChild(state, parentId) {
|
|
16973
|
+
if (parentId === ROOT_PARENT_ID) {
|
|
16974
|
+
let hasRoot = false;
|
|
16975
|
+
state.forEachActivity((activity) => {
|
|
16976
|
+
hasRoot ||= isRootParent(activity.parentId);
|
|
16977
|
+
});
|
|
16978
|
+
return hasRoot;
|
|
16979
|
+
}
|
|
16678
16980
|
return state.getChildren(parentId).length > 0;
|
|
16679
16981
|
}
|
|
16680
16982
|
function readLink(state, id) {
|
|
@@ -16817,6 +17119,7 @@ function buildInverseChangeSet(state, entry, side) {
|
|
|
16817
17119
|
activities.push({
|
|
16818
17120
|
id: activityId,
|
|
16819
17121
|
kind: "updated",
|
|
17122
|
+
fields: historyFields(change.fields, side),
|
|
16820
17123
|
after: snapshot ? structuredCloneActivity(snapshot) : null
|
|
16821
17124
|
});
|
|
16822
17125
|
} else if (change.kind === "deleted") {
|
|
@@ -16850,6 +17153,7 @@ function buildInverseChangeSet(state, entry, side) {
|
|
|
16850
17153
|
links.push({
|
|
16851
17154
|
id: linkId,
|
|
16852
17155
|
kind: "updated",
|
|
17156
|
+
fields: historyFields(change.fields, side),
|
|
16853
17157
|
after: lk ? snapshotToLink(lk) : null
|
|
16854
17158
|
});
|
|
16855
17159
|
} else if (change.kind === "deleted") {
|
|
@@ -16889,6 +17193,21 @@ function buildInverseChangeSet(state, entry, side) {
|
|
|
16889
17193
|
viewState
|
|
16890
17194
|
};
|
|
16891
17195
|
}
|
|
17196
|
+
function historyFields(fields, side) {
|
|
17197
|
+
if (fields == null) return void 0;
|
|
17198
|
+
return Object.fromEntries(
|
|
17199
|
+
Object.entries(fields).map(([field, values]) => [
|
|
17200
|
+
field,
|
|
17201
|
+
side === "before" ? {
|
|
17202
|
+
before: cloneDomainValue(values.after),
|
|
17203
|
+
after: cloneDomainValue(values.before)
|
|
17204
|
+
} : {
|
|
17205
|
+
before: cloneDomainValue(values.before),
|
|
17206
|
+
after: cloneDomainValue(values.after)
|
|
17207
|
+
}
|
|
17208
|
+
])
|
|
17209
|
+
);
|
|
17210
|
+
}
|
|
16892
17211
|
function invertViewStateChange(change) {
|
|
16893
17212
|
return {
|
|
16894
17213
|
activityId: change.activityId,
|
|
@@ -17427,8 +17746,7 @@ function parseActivity(raw, context) {
|
|
|
17427
17746
|
expectedProgress: null,
|
|
17428
17747
|
expectedProgressBaseline: null,
|
|
17429
17748
|
status: null,
|
|
17430
|
-
criticalPath: null
|
|
17431
|
-
promotionRestore: null
|
|
17749
|
+
criticalPath: null
|
|
17432
17750
|
};
|
|
17433
17751
|
}
|
|
17434
17752
|
function forceRootProjectToRoot(activities) {
|
|
@@ -18864,9 +19182,11 @@ var ScheduleCore = class {
|
|
|
18864
19182
|
* ineligible to commit.
|
|
18865
19183
|
*/
|
|
18866
19184
|
recomputeCriticalPath() {
|
|
18867
|
-
|
|
19185
|
+
const operation = this._enqueue(async () => ({
|
|
18868
19186
|
job: this._startCriticalPathForCurrentRevision()
|
|
18869
19187
|
})).then(({ job }) => job);
|
|
19188
|
+
this._criticalPathReady = operation;
|
|
19189
|
+
return operation;
|
|
18870
19190
|
}
|
|
18871
19191
|
isCriticalPathSettled() {
|
|
18872
19192
|
return this._criticalPathRevision === this._scheduleRevision && this._activeCriticalPath === null;
|
|
@@ -19000,11 +19320,11 @@ var ScheduleCore = class {
|
|
|
19000
19320
|
if (this.coreRuntime.clock) {
|
|
19001
19321
|
const now = endOfLocalDay(this.coreRuntime.clock());
|
|
19002
19322
|
applyExpectedProgressLive(state, now);
|
|
19003
|
-
let
|
|
19323
|
+
let hasActiveBaseline2 = false;
|
|
19004
19324
|
state.forEachActivity((activity) => {
|
|
19005
|
-
|
|
19325
|
+
hasActiveBaseline2 ||= getActiveBaseline(activity) !== null;
|
|
19006
19326
|
});
|
|
19007
|
-
if (
|
|
19327
|
+
if (hasActiveBaseline2) {
|
|
19008
19328
|
runExpectedProgressBase(
|
|
19009
19329
|
state,
|
|
19010
19330
|
now,
|