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