@outbuild-company/schedule-core 1.7.0 → 1.8.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 +1756 -998
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -57
- package/dist/index.d.ts +20 -57
- package/dist/index.js +1756 -998
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -139,12 +139,11 @@ var REJECTION_REASON = {
|
|
|
139
139
|
ANCHOR_SIBLING_NOT_FOUND: "anchor_sibling_not_found",
|
|
140
140
|
ANCHOR_SIBLING_WRONG_PARENT: "anchor_sibling_wrong_parent",
|
|
141
141
|
PARENT_FROZEN_BY_SIR: "parent_frozen_by_sir",
|
|
142
|
-
SIR_REQUEST_NOT_PENDING: "sir_request_not_pending",
|
|
143
|
-
PREVIEW_UNSUPPORTED_ACTION: "preview_unsupported_action",
|
|
144
142
|
CANNOT_MOVE_INTO_OWN_DESCENDANT: "cannot_move_into_own_descendant",
|
|
145
143
|
INDENT_NO_ELIGIBLE_SIBLING: "indent_no_eligible_sibling",
|
|
146
144
|
OUTDENT_ROOT_LEVEL_NOT_EDITABLE: "outdent_root_level_not_editable",
|
|
147
145
|
HIERARCHY_CYCLE: "hierarchy_cycle",
|
|
146
|
+
SANDBOX_PERSISTENCE_FORBIDDEN: "sandbox_persistence_forbidden",
|
|
148
147
|
INVALID_PROGRESS_VALUE: "invalid_progress_value_must_be_0_or_100"
|
|
149
148
|
};
|
|
150
149
|
|
|
@@ -1791,6 +1790,8 @@ function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag,
|
|
|
1791
1790
|
target: targetId,
|
|
1792
1791
|
type: originalLink.type,
|
|
1793
1792
|
lag: originalLink.lag,
|
|
1793
|
+
freeSlackHours: null,
|
|
1794
|
+
isCritical: false,
|
|
1794
1795
|
_sourceLag: sourceLag,
|
|
1795
1796
|
_targetLag: targetLag,
|
|
1796
1797
|
_trueLag: trueLag,
|
|
@@ -2868,7 +2869,9 @@ function applyLinkCreates(links, deps) {
|
|
|
2868
2869
|
source: operation.source,
|
|
2869
2870
|
target: operation.target,
|
|
2870
2871
|
type: operation.type,
|
|
2871
|
-
lag: operation.lag
|
|
2872
|
+
lag: operation.lag,
|
|
2873
|
+
freeSlackHours: null,
|
|
2874
|
+
isCritical: false
|
|
2872
2875
|
}))
|
|
2873
2876
|
];
|
|
2874
2877
|
const effectiveVerdict = validateTopologyCommit({
|
|
@@ -2887,6 +2890,8 @@ function applyLinkCreates(links, deps) {
|
|
|
2887
2890
|
target: operation.target,
|
|
2888
2891
|
type: operation.type,
|
|
2889
2892
|
lag: operation.lag,
|
|
2893
|
+
freeSlackHours: null,
|
|
2894
|
+
isCritical: false,
|
|
2890
2895
|
...preserved?.ganttId === void 0 ? {} : { ganttId: preserved.ganttId },
|
|
2891
2896
|
...preserved?.sectorId === void 0 ? {} : { sectorId: preserved.sectorId },
|
|
2892
2897
|
...preserved?.proplannerId === void 0 ? {} : { proplannerId: preserved.proplannerId }
|
|
@@ -2929,7 +2934,9 @@ function applyCreate(op, deps) {
|
|
|
2929
2934
|
source: op.source,
|
|
2930
2935
|
target: op.target,
|
|
2931
2936
|
type: op.type,
|
|
2932
|
-
lag: op.lag
|
|
2937
|
+
lag: op.lag,
|
|
2938
|
+
freeSlackHours: null,
|
|
2939
|
+
isCritical: false
|
|
2933
2940
|
};
|
|
2934
2941
|
const effectiveVerdict = validateTopologyCommit({
|
|
2935
2942
|
port,
|
|
@@ -3443,19 +3450,44 @@ async function recomputeParentsCooperatively(parents, adapter, progressMode, opt
|
|
|
3443
3450
|
}
|
|
3444
3451
|
function findAncestorsOfDirty(adapter, dirtyIds) {
|
|
3445
3452
|
const ancestors = /* @__PURE__ */ new Set();
|
|
3446
|
-
|
|
3453
|
+
const parentById = /* @__PURE__ */ new Map();
|
|
3454
|
+
for (const id of dirtyIds) {
|
|
3455
|
+
walkAncestors(id, adapter, ancestors, parentById);
|
|
3456
|
+
}
|
|
3447
3457
|
if (ancestors.size === 0) return [];
|
|
3448
3458
|
const ids = Array.from(ancestors);
|
|
3449
|
-
const depths =
|
|
3459
|
+
const depths = computeKnownDepths(ids, parentById);
|
|
3450
3460
|
return sortDeepestFirst(ids, depths);
|
|
3451
3461
|
}
|
|
3452
|
-
function walkAncestors(id, adapter, collector) {
|
|
3462
|
+
function walkAncestors(id, adapter, collector, parentById) {
|
|
3453
3463
|
let current = adapter.getParentId(id);
|
|
3454
3464
|
while (current && !isRootParent(current)) {
|
|
3455
3465
|
if (collector.has(current)) return;
|
|
3456
3466
|
collector.add(current);
|
|
3457
|
-
|
|
3467
|
+
const parent = adapter.getParentId(current);
|
|
3468
|
+
parentById.set(current, parent);
|
|
3469
|
+
current = parent;
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
function computeKnownDepths(ids, parentById) {
|
|
3473
|
+
const depths = /* @__PURE__ */ new Map();
|
|
3474
|
+
for (const id of ids) {
|
|
3475
|
+
if (depths.has(id)) continue;
|
|
3476
|
+
const path = [];
|
|
3477
|
+
const inPath = /* @__PURE__ */ new Set();
|
|
3478
|
+
let current = id;
|
|
3479
|
+
while (current && !isRootParent(current) && !depths.has(current) && !inPath.has(current)) {
|
|
3480
|
+
path.push(current);
|
|
3481
|
+
inPath.add(current);
|
|
3482
|
+
current = parentById.get(current) ?? null;
|
|
3483
|
+
}
|
|
3484
|
+
let depth = current ? depths.get(current) ?? 0 : 0;
|
|
3485
|
+
for (let index = path.length - 1; index >= 0; index -= 1) {
|
|
3486
|
+
depth += 1;
|
|
3487
|
+
depths.set(path[index], depth);
|
|
3488
|
+
}
|
|
3458
3489
|
}
|
|
3490
|
+
return depths;
|
|
3459
3491
|
}
|
|
3460
3492
|
function aggregateChildren(childrenIds, adapter, includeRealWork) {
|
|
3461
3493
|
let minStart = null;
|
|
@@ -3679,19 +3711,6 @@ function durationToHours(durationDays, hoursPerDay) {
|
|
|
3679
3711
|
return Math.round(Math.ceil(durationDays * hoursPerDay * 60) / 60);
|
|
3680
3712
|
}
|
|
3681
3713
|
|
|
3682
|
-
// src/columns/shared/error-codes.ts
|
|
3683
|
-
var PARSE_ERROR = {
|
|
3684
|
-
NOT_A_NUMBER: "not_a_number",
|
|
3685
|
-
// ISSUE-057 (CP6): la unidad de una duración se declara, no se adivina.
|
|
3686
|
-
MISSING_INPUT_UNIT: "missing_input_unit",
|
|
3687
|
-
// D-OWNER-5: una duración en días es entera; una fracción se rechaza sin
|
|
3688
|
-
// redondear (el redondeo silencioso era la puerta del bug de las 320 horas).
|
|
3689
|
-
FRACTIONAL_DAYS: "fractional_days"
|
|
3690
|
-
};
|
|
3691
|
-
var VALIDATION_REASON = {
|
|
3692
|
-
UNCHANGED: "unchanged",
|
|
3693
|
-
NEGATIVE_DURATION: "negative_duration"};
|
|
3694
|
-
|
|
3695
3714
|
// src/columns/end-date/setter.ts
|
|
3696
3715
|
var setEndDate = (value) => ({
|
|
3697
3716
|
endDate: value
|
|
@@ -3793,6 +3812,10 @@ var POST_PROCESSOR = {
|
|
|
3793
3812
|
RECORD_LAST_START_DATE: "recordLastStartDate"
|
|
3794
3813
|
};
|
|
3795
3814
|
|
|
3815
|
+
// src/columns/shared/error-codes.ts
|
|
3816
|
+
var VALIDATION_REASON = {
|
|
3817
|
+
NEGATIVE_DURATION: "negative_duration"};
|
|
3818
|
+
|
|
3796
3819
|
// src/columns/shared/constraint-validation.ts
|
|
3797
3820
|
function checkConstraintViolation2(constraintType, constraintDate, projectedDate) {
|
|
3798
3821
|
return checkConstraintViolation(constraintType, constraintDate, projectedDate);
|
|
@@ -3817,18 +3840,7 @@ var durationPipeline = {
|
|
|
3817
3840
|
},
|
|
3818
3841
|
parseInput(rawValue, _activity, ctx) {
|
|
3819
3842
|
const numericValue = parseFloat(String(rawValue));
|
|
3820
|
-
if (!Number.isFinite(numericValue))
|
|
3821
|
-
return parseError(PARSE_ERROR.NOT_A_NUMBER);
|
|
3822
|
-
}
|
|
3823
|
-
if (ctx.inputUnit === void 0) {
|
|
3824
|
-
return parseError(PARSE_ERROR.MISSING_INPUT_UNIT);
|
|
3825
|
-
}
|
|
3826
|
-
if (ctx.inputUnit === "hours") {
|
|
3827
|
-
return parsed(numericValue);
|
|
3828
|
-
}
|
|
3829
|
-
if (!Number.isInteger(numericValue)) {
|
|
3830
|
-
return parseError(PARSE_ERROR.FRACTIONAL_DAYS);
|
|
3831
|
-
}
|
|
3843
|
+
if (!Number.isFinite(numericValue)) return parseError("not_a_number");
|
|
3832
3844
|
return parsed(durationToHours(numericValue, ctx.hoursPerDay));
|
|
3833
3845
|
},
|
|
3834
3846
|
validate(_activity, oldValue, newValue) {
|
|
@@ -4864,7 +4876,9 @@ var ACTIVITY_PROPERTY = {
|
|
|
4864
4876
|
CALENDAR_DURATION: "calendarDuration"};
|
|
4865
4877
|
var LINK_PROPERTY = {
|
|
4866
4878
|
TYPE: "type",
|
|
4867
|
-
LAG: "lag"
|
|
4879
|
+
LAG: "lag",
|
|
4880
|
+
FREE_SLACK_HOURS: "freeSlackHours",
|
|
4881
|
+
IS_CRITICAL: "isCritical"};
|
|
4868
4882
|
|
|
4869
4883
|
// src/internal/state/dynamic-writes.ts
|
|
4870
4884
|
function setActivityFieldDynamic(state, activityId, field, value) {
|
|
@@ -5720,10 +5734,11 @@ function mutateTheDateToFutureOrPastInBaseRestriction(dateBaseToCalculate, restr
|
|
|
5720
5734
|
|
|
5721
5735
|
// src/critical-path/legacy/base/parents-calculations/forward.js
|
|
5722
5736
|
var CalculateForwardParentsWithLinks = class {
|
|
5723
|
-
constructor(linkedActivitiesData, activity, gantt) {
|
|
5737
|
+
constructor(linkedActivitiesData, activity, gantt, linkCandidates) {
|
|
5724
5738
|
this.linkedActivitiesData = linkedActivitiesData;
|
|
5725
5739
|
this.activity = activity;
|
|
5726
5740
|
this.gantt = gantt;
|
|
5741
|
+
this.linkCandidates = linkCandidates;
|
|
5727
5742
|
this.activityCalendar = this.gantt.getCalendar(this.activity.calendar_id);
|
|
5728
5743
|
if (!this.activityCalendar) {
|
|
5729
5744
|
throw new Error(
|
|
@@ -5768,6 +5783,10 @@ var CalculateForwardParentsWithLinks = class {
|
|
|
5768
5783
|
continue;
|
|
5769
5784
|
}
|
|
5770
5785
|
if (calculation) {
|
|
5786
|
+
this.linkCandidates?.set(linkedActivity.linkId, {
|
|
5787
|
+
targetId: String(this.activity.id),
|
|
5788
|
+
...calculation
|
|
5789
|
+
});
|
|
5771
5790
|
calculations.push(calculation);
|
|
5772
5791
|
}
|
|
5773
5792
|
}
|
|
@@ -6215,7 +6234,8 @@ function parentWithLinks(params) {
|
|
|
6215
6234
|
linkDirection,
|
|
6216
6235
|
calculations,
|
|
6217
6236
|
gantt,
|
|
6218
|
-
direction
|
|
6237
|
+
direction,
|
|
6238
|
+
linkCandidates
|
|
6219
6239
|
} = params;
|
|
6220
6240
|
const linksArray = parent[linkProperty];
|
|
6221
6241
|
const parentData = extractParentData(parent);
|
|
@@ -6230,7 +6250,8 @@ function parentWithLinks(params) {
|
|
|
6230
6250
|
calculation = new forward_default(
|
|
6231
6251
|
linksWithActivityData,
|
|
6232
6252
|
parent,
|
|
6233
|
-
gantt
|
|
6253
|
+
gantt,
|
|
6254
|
+
linkCandidates
|
|
6234
6255
|
);
|
|
6235
6256
|
calculation = calculation.calculate();
|
|
6236
6257
|
} else {
|
|
@@ -6275,6 +6296,7 @@ function getLinksWithActivityDataForward(linksArray, linkDirection, calculations
|
|
|
6275
6296
|
return null;
|
|
6276
6297
|
}
|
|
6277
6298
|
return {
|
|
6299
|
+
linkId: String(linkData.id),
|
|
6278
6300
|
es: calculationsOfLinkedActivity.es,
|
|
6279
6301
|
ef: calculationsOfLinkedActivity.ef,
|
|
6280
6302
|
linkLag: linkData.lag,
|
|
@@ -6375,6 +6397,7 @@ var CalculationOfParent = class {
|
|
|
6375
6397
|
this.calculationOfParentsFromLinks = params.calculationOfParentsFromLinks;
|
|
6376
6398
|
this.singleParents = params.singleParents;
|
|
6377
6399
|
this.parentCalculationFromChildren = params.parentCalculationFromChildren;
|
|
6400
|
+
this.linkCandidates = params.linkCandidates;
|
|
6378
6401
|
this.gantt = params.gantt;
|
|
6379
6402
|
}
|
|
6380
6403
|
handleParentWithLinks() {
|
|
@@ -6422,7 +6445,8 @@ var CalculationOfParent = class {
|
|
|
6422
6445
|
linkDirection: this.linkDirection,
|
|
6423
6446
|
gantt: this.gantt,
|
|
6424
6447
|
calculations: this.calculations,
|
|
6425
|
-
direction: this.direction
|
|
6448
|
+
direction: this.direction,
|
|
6449
|
+
linkCandidates: this.linkCandidates
|
|
6426
6450
|
});
|
|
6427
6451
|
this.calculationsOfParentThatImpactChildrens.set(activity.id, calculation);
|
|
6428
6452
|
this.calculationOfParentsFromLinks.add(activity.id);
|
|
@@ -7071,6 +7095,7 @@ var CriticalPathHelpers = class {
|
|
|
7071
7095
|
calculationOfParentsFromLinks: this.calculationOfParentsFromLinks,
|
|
7072
7096
|
singleParents: this.singleParents,
|
|
7073
7097
|
parentCalculationFromChildren: this.parentCalculationFromChildren,
|
|
7098
|
+
linkCandidates: this.linkCandidates,
|
|
7074
7099
|
gantt: this.gantt
|
|
7075
7100
|
});
|
|
7076
7101
|
if (this.pendingParentsWithLinks.has(activity.id)) {
|
|
@@ -7138,6 +7163,7 @@ var LinksRulesCalculationsMethods = class {
|
|
|
7138
7163
|
this.parentsWithLinks = calculationObject.parentsWithLinks;
|
|
7139
7164
|
this.parentsCalculations = calculationObject.parentsCalculations;
|
|
7140
7165
|
this.gantt = calculationObject.ganttInstance;
|
|
7166
|
+
this.linkCandidates = calculationObject.linkCandidates;
|
|
7141
7167
|
}
|
|
7142
7168
|
getEsAndEfFromPreviousCalculatedActivities(activity) {
|
|
7143
7169
|
const isAlap = this.alapMap.has(activity);
|
|
@@ -7309,7 +7335,12 @@ var LinksConstraintCalculator = class extends LinksRulesCalculationsMethods_defa
|
|
|
7309
7335
|
if (!linkData) {
|
|
7310
7336
|
throw new Error("Link data not found", { cause: { linkId: link } });
|
|
7311
7337
|
}
|
|
7312
|
-
|
|
7338
|
+
const calculation = this.calculateLink(linkData);
|
|
7339
|
+
this.linkCandidates?.set(String(linkData.id), {
|
|
7340
|
+
targetId: String(this.referenceActivity.id),
|
|
7341
|
+
...calculation
|
|
7342
|
+
});
|
|
7343
|
+
return calculation;
|
|
7313
7344
|
});
|
|
7314
7345
|
const parent = Number(this.referenceActivity.parent);
|
|
7315
7346
|
if (this.parentsWithLinks.has(parent)) {
|
|
@@ -7575,6 +7606,7 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7575
7606
|
this.totalFloatSnltFnlt = /* @__PURE__ */ new Map();
|
|
7576
7607
|
this.totalFloatForMsoMfo = /* @__PURE__ */ new Map();
|
|
7577
7608
|
this.totalFloatForAsap = /* @__PURE__ */ new Map();
|
|
7609
|
+
this.linkCandidates = /* @__PURE__ */ new Map();
|
|
7578
7610
|
this.calculationOfParentsFromLinks = /* @__PURE__ */ new Set();
|
|
7579
7611
|
this.parentCalculationFromChildren = /* @__PURE__ */ new Set();
|
|
7580
7612
|
this.forbiddenActivitiesLinkedToParentsWithLinks = /* @__PURE__ */ new Set();
|
|
@@ -7673,7 +7705,8 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7673
7705
|
constraint: constraintType,
|
|
7674
7706
|
parentsWithLinks: this.pendingParentsWithLinks,
|
|
7675
7707
|
parentsCalculations: this.calculationsOfParentThatImpactChildrens,
|
|
7676
|
-
ganttInstance: this.gantt
|
|
7708
|
+
ganttInstance: this.gantt,
|
|
7709
|
+
linkCandidates: this.linkCandidates
|
|
7677
7710
|
}).calculate();
|
|
7678
7711
|
if (constraintType === CONSTRAINT_TYPES.ASAP) {
|
|
7679
7712
|
this.totalFloatForAsap.set(activity, {
|
|
@@ -7691,7 +7724,8 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7691
7724
|
constraint: constraintType,
|
|
7692
7725
|
parentsWithLinks: this.pendingParentsWithLinks,
|
|
7693
7726
|
parentsCalculations: this.calculationsOfParentThatImpactChildrens,
|
|
7694
|
-
ganttInstance: this.gantt
|
|
7727
|
+
ganttInstance: this.gantt,
|
|
7728
|
+
linkCandidates: this.linkCandidates
|
|
7695
7729
|
}).calculate());
|
|
7696
7730
|
}
|
|
7697
7731
|
if (constraintType === CONSTRAINT_TYPES.ALAP) {
|
|
@@ -8728,7 +8762,6 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8728
8762
|
}
|
|
8729
8763
|
}
|
|
8730
8764
|
if ([CONSTRAINT_TYPES.SNLT].includes(activity.constraint_type)) {
|
|
8731
|
-
const constraintDate = activity.constraint_date;
|
|
8732
8765
|
const parentLinkCalcualation2 = this.calculationsOfParentThatImpactChildrens.get(parent);
|
|
8733
8766
|
const lsToComparison = addDurationToDate(
|
|
8734
8767
|
this.gantt.getCalendar(activity.calendar_id),
|
|
@@ -8736,6 +8769,7 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8736
8769
|
-activity.duration,
|
|
8737
8770
|
activity
|
|
8738
8771
|
);
|
|
8772
|
+
const constraintDate = activity.constraint_date ?? lsToComparison;
|
|
8739
8773
|
ls = new Date(
|
|
8740
8774
|
Math.min(constraintDate.getTime(), lsToComparison.getTime())
|
|
8741
8775
|
);
|
|
@@ -8747,8 +8781,8 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8747
8781
|
);
|
|
8748
8782
|
}
|
|
8749
8783
|
if ([CONSTRAINT_TYPES.FNLT].includes(activity.constraint_type)) {
|
|
8750
|
-
const constraintDate = activity.constraint_date;
|
|
8751
8784
|
const parentLinkCalcualation2 = this.calculationsOfParentThatImpactChildrens.get(parent);
|
|
8785
|
+
const constraintDate = activity.constraint_date ?? parentLinkCalcualation2.lf;
|
|
8752
8786
|
ls = new Date(
|
|
8753
8787
|
Math.min(constraintDate.getTime(), parentLinkCalcualation2.lf.getTime())
|
|
8754
8788
|
);
|
|
@@ -9132,18 +9166,20 @@ var FreeFloat = class {
|
|
|
9132
9166
|
);
|
|
9133
9167
|
}
|
|
9134
9168
|
calculateFreeFloatForEndOfProject(constraint) {
|
|
9169
|
+
const calculation = constraint === CONSTRAINT_TYPES.ALAP ? this.backwardMap.get(this.activity.id) : this.forwardMap.get(this.activity.id);
|
|
9135
9170
|
return this.parseHoursToDays(
|
|
9136
9171
|
this.calculationWithCalendar(
|
|
9137
9172
|
this.gantt.getSubtaskDates().end_date,
|
|
9138
|
-
constraint === CONSTRAINT_TYPES.ALAP ?
|
|
9173
|
+
constraint === CONSTRAINT_TYPES.ALAP ? calculation?.lf : calculation?.ef
|
|
9139
9174
|
)
|
|
9140
9175
|
);
|
|
9141
9176
|
}
|
|
9142
9177
|
calculateFreeFloatOfRestriction(constraint) {
|
|
9178
|
+
const calculation = this.forwardMap.get(this.activity.id);
|
|
9143
9179
|
return this.parseHoursToDays(
|
|
9144
9180
|
this.calculationWithCalendar(
|
|
9145
9181
|
this.activity.constraint_date,
|
|
9146
|
-
constraint === CONSTRAINT_TYPES.FNLT ?
|
|
9182
|
+
constraint === CONSTRAINT_TYPES.FNLT ? calculation?.ef : calculation?.es
|
|
9147
9183
|
)
|
|
9148
9184
|
);
|
|
9149
9185
|
}
|
|
@@ -9161,111 +9197,33 @@ var FreeFloat = class {
|
|
|
9161
9197
|
sucessorId,
|
|
9162
9198
|
predecessorId
|
|
9163
9199
|
}) {
|
|
9164
|
-
const
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9200
|
+
const successor = (isSuscessorAlap ? this.backwardMap : this.forwardMap).get(sucessorId);
|
|
9201
|
+
const predecessor = (constraint === CONSTRAINT_TYPES.ALAP ? this.backwardMap : this.forwardMap).get(predecessorId);
|
|
9202
|
+
const predecessorStart = constraint === CONSTRAINT_TYPES.ALAP ? predecessor?.ls : predecessor?.es;
|
|
9203
|
+
const predecessorFinish = constraint === CONSTRAINT_TYPES.ALAP ? predecessor?.lf : predecessor?.ef;
|
|
9204
|
+
return {
|
|
9205
|
+
fs: {
|
|
9206
|
+
sucessor: successor?.ls ?? successor?.es,
|
|
9207
|
+
predecessor: predecessorFinish
|
|
9208
|
+
},
|
|
9209
|
+
ss: {
|
|
9210
|
+
sucessor: successor?.ls ?? successor?.es,
|
|
9211
|
+
predecessor: predecessorStart
|
|
9212
|
+
},
|
|
9213
|
+
ff: {
|
|
9214
|
+
sucessor: successor?.lf ?? successor?.ef,
|
|
9215
|
+
predecessor: predecessorFinish
|
|
9216
|
+
},
|
|
9217
|
+
sf: {
|
|
9218
|
+
sucessor: successor?.lf ?? successor?.ef,
|
|
9219
|
+
predecessor: predecessorStart
|
|
9170
9220
|
}
|
|
9171
|
-
return this.forwardMap.get(predecessorId)[direction];
|
|
9172
9221
|
};
|
|
9173
|
-
if (constraint === CONSTRAINT_TYPES.ASAP || constraint === CONSTRAINT_TYPES.SNET || constraint === CONSTRAINT_TYPES.FNET || constraint === CONSTRAINT_TYPES.MSO || constraint === CONSTRAINT_TYPES.MFO) {
|
|
9174
|
-
return {
|
|
9175
|
-
fs: {
|
|
9176
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9177
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9178
|
-
},
|
|
9179
|
-
ss: {
|
|
9180
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9181
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9182
|
-
},
|
|
9183
|
-
ff: {
|
|
9184
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9185
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9186
|
-
},
|
|
9187
|
-
sf: {
|
|
9188
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9189
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9190
|
-
}
|
|
9191
|
-
};
|
|
9192
|
-
}
|
|
9193
|
-
if (constraint === CONSTRAINT_TYPES.ALAP) {
|
|
9194
|
-
return {
|
|
9195
|
-
fs: {
|
|
9196
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9197
|
-
predecessor: getPredecessorDate({
|
|
9198
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9199
|
-
direction: "lf"
|
|
9200
|
-
})
|
|
9201
|
-
},
|
|
9202
|
-
ss: {
|
|
9203
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9204
|
-
predecessor: getPredecessorDate({
|
|
9205
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9206
|
-
direction: "ls"
|
|
9207
|
-
})
|
|
9208
|
-
},
|
|
9209
|
-
ff: {
|
|
9210
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9211
|
-
predecessor: getPredecessorDate({
|
|
9212
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9213
|
-
direction: "lf"
|
|
9214
|
-
})
|
|
9215
|
-
},
|
|
9216
|
-
sf: {
|
|
9217
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9218
|
-
predecessor: getPredecessorDate({
|
|
9219
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9220
|
-
direction: "ls"
|
|
9221
|
-
})
|
|
9222
|
-
}
|
|
9223
|
-
};
|
|
9224
|
-
}
|
|
9225
|
-
if (constraint === CONSTRAINT_TYPES.SNLT) {
|
|
9226
|
-
return {
|
|
9227
|
-
fs: {
|
|
9228
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9229
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9230
|
-
},
|
|
9231
|
-
ss: {
|
|
9232
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9233
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9234
|
-
},
|
|
9235
|
-
ff: {
|
|
9236
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9237
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9238
|
-
},
|
|
9239
|
-
sf: {
|
|
9240
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9241
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9242
|
-
}
|
|
9243
|
-
};
|
|
9244
|
-
}
|
|
9245
|
-
if (constraint === CONSTRAINT_TYPES.FNLT) {
|
|
9246
|
-
return {
|
|
9247
|
-
fs: {
|
|
9248
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9249
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9250
|
-
},
|
|
9251
|
-
ss: {
|
|
9252
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9253
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9254
|
-
},
|
|
9255
|
-
ff: {
|
|
9256
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9257
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9258
|
-
},
|
|
9259
|
-
sf: {
|
|
9260
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9261
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9262
|
-
}
|
|
9263
|
-
};
|
|
9264
|
-
}
|
|
9265
9222
|
}
|
|
9266
9223
|
calculateMsoMfo(constraint) {
|
|
9267
9224
|
const CONSTRAINT_DATE2 = this.activity.constraint_date || /* @__PURE__ */ new Date();
|
|
9268
|
-
const
|
|
9225
|
+
const calculation = this.forwardMap.get(this.activity.id);
|
|
9226
|
+
const DATE_TO_CALCULATE = constraint === CONSTRAINT_TYPES.MSO ? calculation?.es : calculation?.ef;
|
|
9269
9227
|
const freeSlack = this.calculationWithCalendar(
|
|
9270
9228
|
CONSTRAINT_DATE2,
|
|
9271
9229
|
DATE_TO_CALCULATE
|
|
@@ -9279,6 +9237,9 @@ var FreeFloat = class {
|
|
|
9279
9237
|
return filterValues.length ? Math.min(...filterValues) : 0;
|
|
9280
9238
|
}
|
|
9281
9239
|
calculationWithCalendar(dateOne, dateTwo) {
|
|
9240
|
+
if (!(dateOne instanceof Date) || !(dateTwo instanceof Date) || !Number.isFinite(dateOne.getTime()) || !Number.isFinite(dateTwo.getTime())) {
|
|
9241
|
+
return Number.NaN;
|
|
9242
|
+
}
|
|
9282
9243
|
const key = `${dateTwo.getTime()}_${dateOne.getTime()}`;
|
|
9283
9244
|
if (this.durationCache.has(key)) {
|
|
9284
9245
|
return this.durationCache.get(key);
|
|
@@ -9292,7 +9253,7 @@ var FreeFloat = class {
|
|
|
9292
9253
|
return result;
|
|
9293
9254
|
}
|
|
9294
9255
|
parseHoursToDays(date2) {
|
|
9295
|
-
if (
|
|
9256
|
+
if (date2 === void 0 || date2 === null) {
|
|
9296
9257
|
return 0;
|
|
9297
9258
|
}
|
|
9298
9259
|
return date2 / this.gantt.config.hoursPerDay;
|
|
@@ -9358,6 +9319,7 @@ var CriticalPath = class {
|
|
|
9358
9319
|
this.backwardPathCalculations = null;
|
|
9359
9320
|
this.totalFloat = /* @__PURE__ */ new Map();
|
|
9360
9321
|
this.freeFloat = /* @__PURE__ */ new Map();
|
|
9322
|
+
this.linkResidualHours = /* @__PURE__ */ new Map();
|
|
9361
9323
|
this.currentCalculationId = 0;
|
|
9362
9324
|
}
|
|
9363
9325
|
getCurrentCalculationId = () => this.currentCalculationId;
|
|
@@ -9388,6 +9350,13 @@ var CriticalPath = class {
|
|
|
9388
9350
|
if (!isCurrent()) return false;
|
|
9389
9351
|
this.forwardPathCalculations = forwardPathCalculations;
|
|
9390
9352
|
this.backwardPathCalculations = backwardPath.calculations;
|
|
9353
|
+
const linkResidualHours = await this.calculateLinkResidualHours(
|
|
9354
|
+
forwardPath.linkCandidates,
|
|
9355
|
+
forwardPathCalculations,
|
|
9356
|
+
isCurrent
|
|
9357
|
+
);
|
|
9358
|
+
if (!linkResidualHours || !isCurrent()) return false;
|
|
9359
|
+
this.linkResidualHours = linkResidualHours;
|
|
9391
9360
|
const { totalFloatMap, freeFloatMap } = await this.calculateFloatsAsync(
|
|
9392
9361
|
forwardPath,
|
|
9393
9362
|
backwardPath,
|
|
@@ -9407,6 +9376,38 @@ var CriticalPath = class {
|
|
|
9407
9376
|
throw error;
|
|
9408
9377
|
}
|
|
9409
9378
|
}
|
|
9379
|
+
async calculateLinkResidualHours(linkCandidates, forwardCalculations, isCurrent = () => true) {
|
|
9380
|
+
const residuals = /* @__PURE__ */ new Map();
|
|
9381
|
+
let sliceStartTime = performance.now();
|
|
9382
|
+
for (const [linkId, candidate] of linkCandidates) {
|
|
9383
|
+
sliceStartTime = await yieldIfNeeded2(sliceStartTime);
|
|
9384
|
+
if (!isCurrent()) return null;
|
|
9385
|
+
const targetId = Number(candidate.targetId);
|
|
9386
|
+
const target = this.gantt.getTask(targetId);
|
|
9387
|
+
const settled = forwardCalculations.get(targetId);
|
|
9388
|
+
const candidateEf = candidate.ef;
|
|
9389
|
+
const settledEf = settled?.ef;
|
|
9390
|
+
const calendar = target && this.gantt.getCalendar(target.calendar_id);
|
|
9391
|
+
if (!calendar || !(candidateEf instanceof Date) || !(settledEf instanceof Date) || !Number.isFinite(candidateEf.getTime()) || !Number.isFinite(settledEf.getTime())) {
|
|
9392
|
+
residuals.set(linkId, null);
|
|
9393
|
+
continue;
|
|
9394
|
+
}
|
|
9395
|
+
const candidateTime = candidateEf.getTime();
|
|
9396
|
+
const settledTime = settledEf.getTime();
|
|
9397
|
+
const duration = settledTime >= candidateTime ? calendar.calculateDuration({
|
|
9398
|
+
start_date: candidateEf,
|
|
9399
|
+
end_date: settledEf
|
|
9400
|
+
}) : -calendar.calculateDuration({
|
|
9401
|
+
start_date: settledEf,
|
|
9402
|
+
end_date: candidateEf
|
|
9403
|
+
});
|
|
9404
|
+
residuals.set(
|
|
9405
|
+
linkId,
|
|
9406
|
+
Number.isFinite(duration) ? Object.is(duration, -0) ? 0 : duration : null
|
|
9407
|
+
);
|
|
9408
|
+
}
|
|
9409
|
+
return residuals;
|
|
9410
|
+
}
|
|
9410
9411
|
async calculateFloatsAsync(forward, backward, isCurrent) {
|
|
9411
9412
|
try {
|
|
9412
9413
|
const projectDates = this.gantt.getSubtaskDates();
|
|
@@ -9510,14 +9511,52 @@ function computeLevels(state) {
|
|
|
9510
9511
|
for (const id of activitiesById.keys()) resolveLevel(id, /* @__PURE__ */ new Set());
|
|
9511
9512
|
return levelById;
|
|
9512
9513
|
}
|
|
9513
|
-
function
|
|
9514
|
+
function createLegacyIds(state) {
|
|
9515
|
+
const canonicalIds = /* @__PURE__ */ new Set();
|
|
9516
|
+
state.forEachActivity((activity, id) => {
|
|
9517
|
+
canonicalIds.add(String(id));
|
|
9518
|
+
if (activity.parentId !== null) canonicalIds.add(String(activity.parentId));
|
|
9519
|
+
});
|
|
9520
|
+
const numericCounts = /* @__PURE__ */ new Map();
|
|
9521
|
+
for (const id of canonicalIds) {
|
|
9522
|
+
const numeric = Number(id);
|
|
9523
|
+
if (Number.isSafeInteger(numeric) && numeric !== 0) {
|
|
9524
|
+
numericCounts.set(numeric, (numericCounts.get(numeric) ?? 0) + 1);
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
const canonicalToLegacy = /* @__PURE__ */ new Map();
|
|
9528
|
+
const legacyToCanonical = /* @__PURE__ */ new Map();
|
|
9529
|
+
for (const id of canonicalIds) {
|
|
9530
|
+
const numeric = Number(id);
|
|
9531
|
+
if (Number.isSafeInteger(numeric) && numeric !== 0 && numericCounts.get(numeric) === 1) {
|
|
9532
|
+
canonicalToLegacy.set(id, numeric);
|
|
9533
|
+
legacyToCanonical.set(numeric, id);
|
|
9534
|
+
}
|
|
9535
|
+
}
|
|
9536
|
+
let next = 1;
|
|
9537
|
+
const toLegacy = (rawId) => {
|
|
9538
|
+
const id = String(rawId);
|
|
9539
|
+
const existing = canonicalToLegacy.get(id);
|
|
9540
|
+
if (existing !== void 0) return existing;
|
|
9541
|
+
while (legacyToCanonical.has(next)) next += 1;
|
|
9542
|
+
canonicalToLegacy.set(id, next);
|
|
9543
|
+
legacyToCanonical.set(next, id);
|
|
9544
|
+
return next++;
|
|
9545
|
+
};
|
|
9546
|
+
for (const id of canonicalIds) toLegacy(id);
|
|
9547
|
+
return {
|
|
9548
|
+
toLegacy,
|
|
9549
|
+
toCanonical: (id) => typeof id === "number" ? legacyToCanonical.get(id) ?? null : canonicalToLegacy.has(String(id)) ? String(id) : null
|
|
9550
|
+
};
|
|
9551
|
+
}
|
|
9552
|
+
function toLegacyTasks(state, ids) {
|
|
9514
9553
|
const levelById = computeLevels(state);
|
|
9515
9554
|
const leveled = /* @__PURE__ */ new Map();
|
|
9516
9555
|
state.forEachActivity((activity, id) => {
|
|
9517
9556
|
const key = String(id);
|
|
9518
9557
|
leveled.set(key, {
|
|
9519
|
-
id:
|
|
9520
|
-
parent: activity.parentId === null ? 0 :
|
|
9558
|
+
id: ids.toLegacy(key),
|
|
9559
|
+
parent: activity.parentId === null ? 0 : ids.toLegacy(activity.parentId),
|
|
9521
9560
|
$level: levelById.get(key) ?? 0,
|
|
9522
9561
|
$source: state.getOutgoingLinkIds(key),
|
|
9523
9562
|
$target: state.getIncomingLinkIds(key),
|
|
@@ -9591,7 +9630,8 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9591
9630
|
};
|
|
9592
9631
|
const bounds = projectBounds(state);
|
|
9593
9632
|
await yieldToBrowser();
|
|
9594
|
-
const
|
|
9633
|
+
const ids = createLegacyIds(state);
|
|
9634
|
+
const leveled = toLegacyTasks(state, ids);
|
|
9595
9635
|
await yieldToBrowser();
|
|
9596
9636
|
const byTime = [...leveled.values()].sort(
|
|
9597
9637
|
(a, b) => (a.start_date?.getTime() ?? 0) - (b.start_date?.getTime() ?? 0)
|
|
@@ -9601,24 +9641,31 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9601
9641
|
return {
|
|
9602
9642
|
fullyParsed: true,
|
|
9603
9643
|
config: { hoursPerDay },
|
|
9604
|
-
getTask: (id) =>
|
|
9644
|
+
getTask: (id) => {
|
|
9645
|
+
const canonicalId = ids.toCanonical(id);
|
|
9646
|
+
return canonicalId === null ? null : leveled.get(canonicalId) ?? null;
|
|
9647
|
+
},
|
|
9605
9648
|
getLink: (id) => {
|
|
9606
9649
|
const link = state.getLink(id);
|
|
9607
9650
|
if (!link) return null;
|
|
9608
9651
|
return {
|
|
9609
9652
|
...link,
|
|
9610
|
-
source:
|
|
9611
|
-
target:
|
|
9653
|
+
source: ids.toLegacy(link.source),
|
|
9654
|
+
target: ids.toLegacy(link.target),
|
|
9612
9655
|
lag: link.lag
|
|
9613
9656
|
};
|
|
9614
9657
|
},
|
|
9615
9658
|
getCalendar: resolveCalendar,
|
|
9616
|
-
getChildren: (parentId) =>
|
|
9659
|
+
getChildren: (parentId) => {
|
|
9660
|
+
const canonicalId = ids.toCanonical(parentId);
|
|
9661
|
+
return canonicalId === null ? [] : state.getChildren(canonicalId).map(ids.toLegacy);
|
|
9662
|
+
},
|
|
9617
9663
|
eachTask: (cb) => {
|
|
9618
9664
|
for (const task of treeOrdered) cb(task);
|
|
9619
9665
|
},
|
|
9620
9666
|
getTaskByTime: () => byTime,
|
|
9621
9667
|
getSubtaskDates: () => bounds,
|
|
9668
|
+
getCanonicalActivityId: ids.toCanonical,
|
|
9622
9669
|
refreshData: () => {
|
|
9623
9670
|
}
|
|
9624
9671
|
};
|
|
@@ -9626,21 +9673,92 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9626
9673
|
|
|
9627
9674
|
// src/critical-path/run-critical-path.ts
|
|
9628
9675
|
var CP_WRITE_YIELD_EVERY = 1e3;
|
|
9629
|
-
function
|
|
9630
|
-
const
|
|
9676
|
+
function fieldsForLink(rawResidual, sourceIsCritical, targetIsCritical, targetProgress) {
|
|
9677
|
+
const freeSlackHours = typeof rawResidual === "number" && Number.isFinite(rawResidual) ? Object.is(rawResidual, -0) ? 0 : rawResidual : null;
|
|
9678
|
+
return {
|
|
9679
|
+
freeSlackHours,
|
|
9680
|
+
isCritical: freeSlackHours !== null && freeSlackHours <= 0 && sourceIsCritical && targetIsCritical && targetProgress === 0
|
|
9681
|
+
};
|
|
9682
|
+
}
|
|
9683
|
+
async function collectActivityFields(state, maps, hoursPerDay, toCanonicalActivityId, isCurrent) {
|
|
9684
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9685
|
+
let sinceYield = 0;
|
|
9686
|
+
for (const calculationId of maps.forward?.keys() ?? []) {
|
|
9687
|
+
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9688
|
+
sinceYield = 0;
|
|
9689
|
+
await yieldToBrowser();
|
|
9690
|
+
if (!isCurrent()) return null;
|
|
9691
|
+
}
|
|
9692
|
+
const activityId = toCanonicalActivityId(calculationId);
|
|
9693
|
+
if (activityId === null) continue;
|
|
9694
|
+
const activity = state.getActivity(activityId);
|
|
9695
|
+
if (!activity) continue;
|
|
9696
|
+
fields.set(
|
|
9697
|
+
String(activityId),
|
|
9698
|
+
fieldsForActivity(
|
|
9699
|
+
calculationId,
|
|
9700
|
+
Number(activity.progress ?? 0),
|
|
9701
|
+
maps,
|
|
9702
|
+
hoursPerDay
|
|
9703
|
+
)
|
|
9704
|
+
);
|
|
9705
|
+
}
|
|
9706
|
+
return isCurrent() ? fields : null;
|
|
9707
|
+
}
|
|
9708
|
+
async function collectLinkFields(state, linkResidualHours, activityFields, isCurrent) {
|
|
9709
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9710
|
+
let sinceYield = 0;
|
|
9711
|
+
for (const link of state.getAllLinks()) {
|
|
9712
|
+
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9713
|
+
sinceYield = 0;
|
|
9714
|
+
await yieldToBrowser();
|
|
9715
|
+
if (!isCurrent()) return null;
|
|
9716
|
+
}
|
|
9717
|
+
fields.set(
|
|
9718
|
+
String(link.id),
|
|
9719
|
+
fieldsForLink(
|
|
9720
|
+
linkResidualHours.get(String(link.id)),
|
|
9721
|
+
activityFields.get(link.source)?.isCritical === true,
|
|
9722
|
+
activityFields.get(link.target)?.isCritical === true,
|
|
9723
|
+
state.getActivity(link.target)?.progress
|
|
9724
|
+
)
|
|
9725
|
+
);
|
|
9726
|
+
}
|
|
9727
|
+
return isCurrent() ? fields : null;
|
|
9728
|
+
}
|
|
9729
|
+
var slackDaysToHours = (valueDays, hoursPerDay) => {
|
|
9730
|
+
if (typeof valueDays !== "number" || !Number.isFinite(valueDays) || !Number.isFinite(hoursPerDay) || hoursPerDay <= 0) {
|
|
9731
|
+
return null;
|
|
9732
|
+
}
|
|
9733
|
+
const hours = valueDays * hoursPerDay;
|
|
9734
|
+
if (!Number.isFinite(hours)) return null;
|
|
9735
|
+
return Object.is(hours, -0) ? 0 : hours;
|
|
9736
|
+
};
|
|
9737
|
+
function fieldsForActivity(activityId, progress, maps, hoursPerDay) {
|
|
9738
|
+
const toDate = (value) => {
|
|
9739
|
+
if (value == null) return null;
|
|
9740
|
+
const date2 = new Date(value);
|
|
9741
|
+
return Number.isFinite(date2.getTime()) ? date2 : null;
|
|
9742
|
+
};
|
|
9631
9743
|
const forward = maps.forward?.get(activityId);
|
|
9632
9744
|
const backward = maps.backward?.get(activityId);
|
|
9633
|
-
const
|
|
9745
|
+
const totalSlackHours = slackDaysToHours(
|
|
9746
|
+
maps.totalFloat.get(activityId),
|
|
9747
|
+
hoursPerDay
|
|
9748
|
+
);
|
|
9634
9749
|
return {
|
|
9635
9750
|
criticalPath: {
|
|
9636
9751
|
earlyStart: toDate(forward?.es),
|
|
9637
9752
|
earlyFinish: toDate(forward?.ef),
|
|
9638
9753
|
lateStart: toDate(backward?.ls),
|
|
9639
9754
|
lateFinish: toDate(backward?.lf),
|
|
9640
|
-
totalSlackHours
|
|
9755
|
+
totalSlackHours
|
|
9641
9756
|
},
|
|
9642
|
-
isCritical: progress < 100 &&
|
|
9643
|
-
freeSlackHours:
|
|
9757
|
+
isCritical: progress < 100 && totalSlackHours !== null && totalSlackHours <= 0,
|
|
9758
|
+
freeSlackHours: slackDaysToHours(
|
|
9759
|
+
maps.freeFloat.get(activityId)?.activityFreeFloat,
|
|
9760
|
+
hoursPerDay
|
|
9761
|
+
)
|
|
9644
9762
|
};
|
|
9645
9763
|
}
|
|
9646
9764
|
var datesEqual = (left, right) => left === right || left !== null && right !== null && left.getTime() === right.getTime();
|
|
@@ -9653,6 +9771,14 @@ function setCriticalPathFieldsIfChanged(state, activityId, fields) {
|
|
|
9653
9771
|
}
|
|
9654
9772
|
state.setActivityFields(activityId, fields);
|
|
9655
9773
|
}
|
|
9774
|
+
function setCriticalPathLinkFieldsIfChanged(state, linkId, fields) {
|
|
9775
|
+
const link = state.getLink(linkId);
|
|
9776
|
+
if (!link || Object.is(link.freeSlackHours, fields.freeSlackHours) && Object.is(link.isCritical, fields.isCritical)) {
|
|
9777
|
+
return;
|
|
9778
|
+
}
|
|
9779
|
+
state.setLinkField(linkId, "freeSlackHours", fields.freeSlackHours);
|
|
9780
|
+
state.setLinkField(linkId, "isCritical", fields.isCritical);
|
|
9781
|
+
}
|
|
9656
9782
|
async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => true) {
|
|
9657
9783
|
if (!isCurrent()) return null;
|
|
9658
9784
|
const gantt = await createGanttAdapter(state, hoursPerDay);
|
|
@@ -9670,35 +9796,32 @@ async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => t
|
|
|
9670
9796
|
totalFloat: criticalPath.totalFloat,
|
|
9671
9797
|
freeFloat: criticalPath.freeFloat
|
|
9672
9798
|
};
|
|
9673
|
-
const
|
|
9674
|
-
const fieldsByActivity = /* @__PURE__ */ new Map();
|
|
9675
|
-
let sinceYield = 0;
|
|
9676
|
-
for (const activityId of activityIds) {
|
|
9677
|
-
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9678
|
-
sinceYield = 0;
|
|
9679
|
-
await yieldToBrowser();
|
|
9680
|
-
if (!isCurrent()) return null;
|
|
9681
|
-
}
|
|
9682
|
-
const activity = state.getActivity(activityId);
|
|
9683
|
-
if (!activity) continue;
|
|
9684
|
-
const progress = Number(activity.progress ?? 0);
|
|
9685
|
-
fieldsByActivity.set(
|
|
9686
|
-
activityId,
|
|
9687
|
-
fieldsForActivity(activityId, progress, maps)
|
|
9688
|
-
);
|
|
9689
|
-
}
|
|
9690
|
-
return isCurrent() ? fieldsByActivity : null;
|
|
9691
|
-
}
|
|
9692
|
-
async function applyCriticalPath(state, hoursPerDay, isCurrent = () => true) {
|
|
9693
|
-
const fieldsByActivity = await computeCriticalPathFields(
|
|
9799
|
+
const activities = await collectActivityFields(
|
|
9694
9800
|
state,
|
|
9801
|
+
maps,
|
|
9695
9802
|
hoursPerDay,
|
|
9803
|
+
gantt.getCanonicalActivityId,
|
|
9804
|
+
isCurrent
|
|
9805
|
+
);
|
|
9806
|
+
if (!activities) return null;
|
|
9807
|
+
const links = await collectLinkFields(
|
|
9808
|
+
state,
|
|
9809
|
+
criticalPath.linkResidualHours,
|
|
9810
|
+
activities,
|
|
9696
9811
|
isCurrent
|
|
9697
9812
|
);
|
|
9698
|
-
|
|
9699
|
-
|
|
9813
|
+
return links ? { activities, links } : null;
|
|
9814
|
+
}
|
|
9815
|
+
async function applyCriticalPath(state, hoursPerDay, isCurrent = () => true) {
|
|
9816
|
+
const fields = await computeCriticalPathFields(state, hoursPerDay, isCurrent);
|
|
9817
|
+
if (!fields || !isCurrent()) return false;
|
|
9818
|
+
for (const [activityId, activityFields] of fields.activities) {
|
|
9700
9819
|
if (!isCurrent()) return false;
|
|
9701
|
-
setCriticalPathFieldsIfChanged(state, activityId,
|
|
9820
|
+
setCriticalPathFieldsIfChanged(state, activityId, activityFields);
|
|
9821
|
+
}
|
|
9822
|
+
for (const [linkId, linkFields] of fields.links) {
|
|
9823
|
+
if (!isCurrent()) return false;
|
|
9824
|
+
setCriticalPathLinkFieldsIfChanged(state, linkId, linkFields);
|
|
9702
9825
|
}
|
|
9703
9826
|
return true;
|
|
9704
9827
|
}
|
|
@@ -9800,9 +9923,15 @@ function isSchedulingDispatch(action, structuralStaleKinds) {
|
|
|
9800
9923
|
return true;
|
|
9801
9924
|
}
|
|
9802
9925
|
function dispatchChangesSchedulingState(action) {
|
|
9926
|
+
if (action.kind === "activity-lookahead-sync") {
|
|
9927
|
+
return action.persistedProgress !== void 0;
|
|
9928
|
+
}
|
|
9803
9929
|
return isSchedulingDispatch(action, NO_STRUCTURAL_EXEMPTIONS);
|
|
9804
9930
|
}
|
|
9805
9931
|
function resolveRunCriticalPath(action) {
|
|
9932
|
+
if (action.kind === "activity-lookahead-sync") {
|
|
9933
|
+
return action.persistedProgress !== void 0;
|
|
9934
|
+
}
|
|
9806
9935
|
return isSchedulingDispatch(
|
|
9807
9936
|
action,
|
|
9808
9937
|
STRUCTURAL_KINDS_LEAVING_CRITICAL_PATH_STALE
|
|
@@ -10088,6 +10217,23 @@ function detectSirAutoReject(beforeSnap, adapter, touchedIds) {
|
|
|
10088
10217
|
return effects;
|
|
10089
10218
|
}
|
|
10090
10219
|
|
|
10220
|
+
// src/dispatch/conversion.ts
|
|
10221
|
+
function snapshotToLink(l) {
|
|
10222
|
+
const view = {
|
|
10223
|
+
id: String(l.id),
|
|
10224
|
+
source: String(l.source),
|
|
10225
|
+
target: String(l.target),
|
|
10226
|
+
type: l.type,
|
|
10227
|
+
lag: Number(l.lag),
|
|
10228
|
+
freeSlackHours: typeof l.freeSlackHours === "number" && Number.isFinite(l.freeSlackHours) ? l.freeSlackHours : null,
|
|
10229
|
+
isCritical: l.isCritical === true
|
|
10230
|
+
};
|
|
10231
|
+
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
10232
|
+
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
10233
|
+
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
10234
|
+
return view;
|
|
10235
|
+
}
|
|
10236
|
+
|
|
10091
10237
|
// src/dispatch/shared/change-set.ts
|
|
10092
10238
|
async function assembleChangeSet(adapter, args) {
|
|
10093
10239
|
const allTouched = collectChangeSetIds(adapter, args);
|
|
@@ -10097,7 +10243,7 @@ async function assembleChangeSet(adapter, args) {
|
|
|
10097
10243
|
const insertedIds = collectInsertedIds(captured);
|
|
10098
10244
|
mergeDirtyBeforeImages(adapter, captured, insertedIds, before, allTouched);
|
|
10099
10245
|
}
|
|
10100
|
-
const sirDetection = args.
|
|
10246
|
+
const sirDetection = args.sirDetection;
|
|
10101
10247
|
const beforeDiffEffects = sirDetection === "before-diff" ? detectSirAutoReject(before, adapter, allTouched) : [];
|
|
10102
10248
|
const correlativeBefore = buildCorrelativeBeforeMap(
|
|
10103
10249
|
args.correlativeShifts,
|
|
@@ -10111,16 +10257,89 @@ async function assembleChangeSet(adapter, args) {
|
|
|
10111
10257
|
);
|
|
10112
10258
|
const effects = sirDetection === "after-diff" ? detectSirAutoReject(before, adapter, allTouched) : beforeDiffEffects;
|
|
10113
10259
|
const warnings = args.warnings ?? [];
|
|
10260
|
+
const linkChanges = mergeLinkChanges(
|
|
10261
|
+
args.links ?? [],
|
|
10262
|
+
captured ? buildCapturedCriticalPathLinkChanges(adapter, captured) : []
|
|
10263
|
+
);
|
|
10114
10264
|
return {
|
|
10115
10265
|
source: args.source,
|
|
10116
10266
|
activities: activityChanges,
|
|
10117
|
-
links:
|
|
10267
|
+
links: linkChanges,
|
|
10118
10268
|
calendars: [],
|
|
10119
10269
|
trackingEvents: args.trackingEvents ?? [],
|
|
10120
10270
|
...effects.length > 0 ? { effects } : {},
|
|
10121
10271
|
...warnings.length > 0 ? { warnings } : {}
|
|
10122
10272
|
};
|
|
10123
10273
|
}
|
|
10274
|
+
function buildCapturedCriticalPathLinkChanges(adapter, captured) {
|
|
10275
|
+
const changes = [];
|
|
10276
|
+
for (const operation of captured.ops) {
|
|
10277
|
+
if (operation.kind !== "link-field") continue;
|
|
10278
|
+
const after = adapter.getLink(operation.linkId);
|
|
10279
|
+
if (!after) continue;
|
|
10280
|
+
const fields = {};
|
|
10281
|
+
for (const field of ["freeSlackHours", "isCritical"]) {
|
|
10282
|
+
if (!Object.is(operation.before[field], after[field])) {
|
|
10283
|
+
fields[field] = {
|
|
10284
|
+
before: operation.before[field],
|
|
10285
|
+
after: after[field]
|
|
10286
|
+
};
|
|
10287
|
+
}
|
|
10288
|
+
}
|
|
10289
|
+
if (Object.keys(fields).length === 0) continue;
|
|
10290
|
+
changes.push({
|
|
10291
|
+
id: operation.linkId,
|
|
10292
|
+
kind: "updated",
|
|
10293
|
+
fields,
|
|
10294
|
+
after: snapshotToLink(after)
|
|
10295
|
+
});
|
|
10296
|
+
}
|
|
10297
|
+
return changes;
|
|
10298
|
+
}
|
|
10299
|
+
function mergeLinkChanges(explicit, captured) {
|
|
10300
|
+
const merged = [];
|
|
10301
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
10302
|
+
for (const change of explicit) {
|
|
10303
|
+
const index = indexById.get(change.id);
|
|
10304
|
+
if (index === void 0) {
|
|
10305
|
+
indexById.set(change.id, merged.length);
|
|
10306
|
+
merged.push(change);
|
|
10307
|
+
continue;
|
|
10308
|
+
}
|
|
10309
|
+
const current = merged[index];
|
|
10310
|
+
if ((current.kind === "updated" || current.kind === "created") && change.kind === "updated") {
|
|
10311
|
+
merged[index] = {
|
|
10312
|
+
...current,
|
|
10313
|
+
fields: { ...current.fields, ...change.fields },
|
|
10314
|
+
after: change.after
|
|
10315
|
+
};
|
|
10316
|
+
continue;
|
|
10317
|
+
}
|
|
10318
|
+
merged[index] = change;
|
|
10319
|
+
}
|
|
10320
|
+
for (const change of captured) {
|
|
10321
|
+
const index = indexById.get(change.id);
|
|
10322
|
+
if (index === void 0) {
|
|
10323
|
+
indexById.set(change.id, merged.length);
|
|
10324
|
+
merged.push(change);
|
|
10325
|
+
continue;
|
|
10326
|
+
}
|
|
10327
|
+
const current = merged[index];
|
|
10328
|
+
if (current.kind === "deleted") continue;
|
|
10329
|
+
const capturedFields = current.kind === "created" ? Object.fromEntries(
|
|
10330
|
+
Object.entries(change.fields ?? {}).map(([field, value]) => [
|
|
10331
|
+
field,
|
|
10332
|
+
{ before: void 0, after: value.after }
|
|
10333
|
+
])
|
|
10334
|
+
) : change.fields;
|
|
10335
|
+
merged[index] = {
|
|
10336
|
+
...current,
|
|
10337
|
+
fields: { ...current.fields, ...capturedFields },
|
|
10338
|
+
after: change.after
|
|
10339
|
+
};
|
|
10340
|
+
}
|
|
10341
|
+
return merged;
|
|
10342
|
+
}
|
|
10124
10343
|
function buildCorrelativeBeforeMap(shifts, touched) {
|
|
10125
10344
|
if (!shifts || shifts.length === 0) return void 0;
|
|
10126
10345
|
const correlativeBefore = /* @__PURE__ */ new Map();
|
|
@@ -10156,21 +10375,6 @@ function collectInsertedIds(captured) {
|
|
|
10156
10375
|
);
|
|
10157
10376
|
}
|
|
10158
10377
|
|
|
10159
|
-
// src/dispatch/conversion.ts
|
|
10160
|
-
function snapshotToLink(l) {
|
|
10161
|
-
const view = {
|
|
10162
|
-
id: String(l.id),
|
|
10163
|
-
source: String(l.source),
|
|
10164
|
-
target: String(l.target),
|
|
10165
|
-
type: l.type,
|
|
10166
|
-
lag: Number(l.lag)
|
|
10167
|
-
};
|
|
10168
|
-
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
10169
|
-
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
10170
|
-
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
10171
|
-
return view;
|
|
10172
|
-
}
|
|
10173
|
-
|
|
10174
10378
|
// src/dispatch/shared/helpers.ts
|
|
10175
10379
|
function buildLinkChangesForBatch(applied, beforeLinks, adapter) {
|
|
10176
10380
|
const out = [];
|
|
@@ -10438,7 +10642,6 @@ async function dispatchInlineEdit(action, options, deps) {
|
|
|
10438
10642
|
touchedIds,
|
|
10439
10643
|
scheduledIds,
|
|
10440
10644
|
sirDetection: "before-diff",
|
|
10441
|
-
preview: options.preview,
|
|
10442
10645
|
links: linkChanges,
|
|
10443
10646
|
trackingEvents,
|
|
10444
10647
|
...constraintWarning ? { warnings: [constraintWarning] } : {},
|
|
@@ -10516,6 +10719,26 @@ function collectDeterministicIds(operations) {
|
|
|
10516
10719
|
});
|
|
10517
10720
|
return ids.size > 0 ? ids : void 0;
|
|
10518
10721
|
}
|
|
10722
|
+
function resolveDeterministicIds(deterministicIds, deps) {
|
|
10723
|
+
if (deterministicIds === void 0) return void 0;
|
|
10724
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
10725
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
10726
|
+
const nextAvailableId = () => {
|
|
10727
|
+
let candidate = deps.linkIdGen.next();
|
|
10728
|
+
while (deps.adapter.getLink(candidate) !== null || deps.isLinkIdReserved(String(candidate)) || assigned.has(String(candidate))) {
|
|
10729
|
+
candidate = deps.linkIdGen.next();
|
|
10730
|
+
}
|
|
10731
|
+
return candidate;
|
|
10732
|
+
};
|
|
10733
|
+
for (const [operationIndex, explicitId] of deterministicIds) {
|
|
10734
|
+
const isReserved = deps.adapter.getLink(explicitId) !== null || deps.isLinkIdReserved(String(explicitId)) || assigned.has(String(explicitId));
|
|
10735
|
+
const linkId = isReserved ? nextAvailableId() : explicitId;
|
|
10736
|
+
if (!isReserved) deps.linkIdGen.bump(explicitId);
|
|
10737
|
+
assigned.add(String(linkId));
|
|
10738
|
+
resolved.set(operationIndex, linkId);
|
|
10739
|
+
}
|
|
10740
|
+
return resolved;
|
|
10741
|
+
}
|
|
10519
10742
|
function batchOpToLinkOperation(op, hoursPerDay) {
|
|
10520
10743
|
if (op.kind === "create") {
|
|
10521
10744
|
return {
|
|
@@ -10548,7 +10771,7 @@ async function dispatchLinkBatch(operations, source, options, deps, deterministi
|
|
|
10548
10771
|
operations,
|
|
10549
10772
|
adapter,
|
|
10550
10773
|
linkIdGen,
|
|
10551
|
-
deterministicIds
|
|
10774
|
+
resolveDeterministicIds(deterministicIds, deps)
|
|
10552
10775
|
);
|
|
10553
10776
|
if (operations.length === 1 && applied[0] && applied[0].rejected !== null && source.kind !== "inline-edit") {
|
|
10554
10777
|
return { ok: false, reason: applied[0].rejected };
|
|
@@ -10730,26 +10953,19 @@ function recomputeHhCascadeForParent(startParentId, adapter) {
|
|
|
10730
10953
|
if (!startParentId || startParentId === "0") return;
|
|
10731
10954
|
let currentParentId = String(startParentId);
|
|
10732
10955
|
while (currentParentId !== null) {
|
|
10733
|
-
const
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
const child = adapter.getActivity(childId);
|
|
10738
|
-
if (!child) continue;
|
|
10739
|
-
const hh = readNumber(child, ACTIVITY_PROPERTY.WORK_HOURS);
|
|
10740
|
-
const rw = readChildRealWork(childId, child, hh, adapter);
|
|
10741
|
-
hhSum += hh;
|
|
10742
|
-
rwSum += rw;
|
|
10743
|
-
}
|
|
10956
|
+
const { workHours, realWorkHours } = computeParentHhRollup(
|
|
10957
|
+
currentParentId,
|
|
10958
|
+
adapter
|
|
10959
|
+
);
|
|
10744
10960
|
adapter.setActivityField(
|
|
10745
10961
|
currentParentId,
|
|
10746
10962
|
ACTIVITY_PROPERTY.WORK_HOURS,
|
|
10747
|
-
|
|
10963
|
+
workHours
|
|
10748
10964
|
);
|
|
10749
10965
|
adapter.setActivityField(
|
|
10750
10966
|
currentParentId,
|
|
10751
10967
|
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
10752
|
-
|
|
10968
|
+
realWorkHours
|
|
10753
10969
|
);
|
|
10754
10970
|
const parentSnap = adapter.getActivity(currentParentId);
|
|
10755
10971
|
if (!parentSnap) break;
|
|
@@ -10765,6 +10981,40 @@ function recomputeHhCascadeForActivity(activityId, adapter) {
|
|
|
10765
10981
|
if (!parentId || isRootParent(parentId)) return;
|
|
10766
10982
|
recomputeHhCascadeForParent(parentId, adapter);
|
|
10767
10983
|
}
|
|
10984
|
+
function recomputeRealWorkFromProgress(progressIds, parentIdsDeepestFirst, adapter) {
|
|
10985
|
+
for (const activityId of progressIds) {
|
|
10986
|
+
if (adapter.hasChildren(activityId)) continue;
|
|
10987
|
+
const activity = adapter.getActivity(activityId);
|
|
10988
|
+
if (!activity) continue;
|
|
10989
|
+
adapter.setActivityField(
|
|
10990
|
+
activityId,
|
|
10991
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
10992
|
+
computeLeafRealWork(
|
|
10993
|
+
readNumber(activity, ACTIVITY_PROPERTY.WORK_HOURS),
|
|
10994
|
+
readNumber(activity, ACTIVITY_PROPERTY.PROGRESS)
|
|
10995
|
+
)
|
|
10996
|
+
);
|
|
10997
|
+
}
|
|
10998
|
+
for (const parentId of parentIdsDeepestFirst) {
|
|
10999
|
+
adapter.setActivityField(
|
|
11000
|
+
parentId,
|
|
11001
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
11002
|
+
computeParentHhRollup(parentId, adapter).realWorkHours
|
|
11003
|
+
);
|
|
11004
|
+
}
|
|
11005
|
+
}
|
|
11006
|
+
function computeParentHhRollup(parentId, adapter) {
|
|
11007
|
+
let workHours = 0;
|
|
11008
|
+
let realWorkHours = 0;
|
|
11009
|
+
for (const childId of adapter.getChildren(parentId)) {
|
|
11010
|
+
const child = adapter.getActivity(childId);
|
|
11011
|
+
if (!child) continue;
|
|
11012
|
+
const childWorkHours = readNumber(child, ACTIVITY_PROPERTY.WORK_HOURS);
|
|
11013
|
+
workHours += childWorkHours;
|
|
11014
|
+
realWorkHours += readChildRealWork(childId, child, childWorkHours, adapter);
|
|
11015
|
+
}
|
|
11016
|
+
return { workHours, realWorkHours };
|
|
11017
|
+
}
|
|
10768
11018
|
function readNumber(activity, key) {
|
|
10769
11019
|
const v = activity[key];
|
|
10770
11020
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
@@ -11137,6 +11387,36 @@ function computeFractionalCidAtIndex(adapter, parentKey, targetIndex, excludeId)
|
|
|
11137
11387
|
return (before + after) / 2;
|
|
11138
11388
|
}
|
|
11139
11389
|
|
|
11390
|
+
// src/dispatch/creation-overrides.ts
|
|
11391
|
+
var PUBLIC_CREATION_OVERRIDE_FIELDS = /* @__PURE__ */ new Set([
|
|
11392
|
+
...[...CREATION_OVERRIDE_FIELDS].filter((field) => field !== "durationHours"),
|
|
11393
|
+
"durationDays"
|
|
11394
|
+
]);
|
|
11395
|
+
function normalizeActivityCreationOverrides(raw, hoursPerDay) {
|
|
11396
|
+
if (raw === void 0) return { ok: true, value: {} };
|
|
11397
|
+
const prototype = Object.getPrototypeOf(raw);
|
|
11398
|
+
if (prototype !== null && prototype !== Object.prototype) {
|
|
11399
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11400
|
+
}
|
|
11401
|
+
for (const key of Reflect.ownKeys(raw)) {
|
|
11402
|
+
if (typeof key === "symbol" || !PUBLIC_CREATION_OVERRIDE_FIELDS.has(key)) {
|
|
11403
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11404
|
+
}
|
|
11405
|
+
}
|
|
11406
|
+
const { durationDays, ...rest } = raw;
|
|
11407
|
+
if (durationDays !== void 0 && (!Number.isFinite(durationDays) || durationDays < 0)) {
|
|
11408
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11409
|
+
}
|
|
11410
|
+
return {
|
|
11411
|
+
ok: true,
|
|
11412
|
+
value: {
|
|
11413
|
+
...rest,
|
|
11414
|
+
...durationDays === 0 && (rest.type === void 0 || rest.type === "task") ? { type: "milestone" } : {},
|
|
11415
|
+
...durationDays === void 0 ? {} : { durationHours: durationToHours(durationDays, hoursPerDay) }
|
|
11416
|
+
}
|
|
11417
|
+
};
|
|
11418
|
+
}
|
|
11419
|
+
|
|
11140
11420
|
// src/dispatch/handlers/activity-create.ts
|
|
11141
11421
|
async function createActivityCore(action, deps, opts = {}) {
|
|
11142
11422
|
const { adapter, scheduler, sector, calendars, activityIdGen, uidGen } = deps;
|
|
@@ -11144,10 +11424,15 @@ async function createActivityCore(action, deps, opts = {}) {
|
|
|
11144
11424
|
const target = validateCreateTarget(action, adapter);
|
|
11145
11425
|
if (!target.ok) return target;
|
|
11146
11426
|
const { parent, anchorSiblingId, isRoot } = target;
|
|
11147
|
-
const newId = resolveNewActivityId(
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11427
|
+
const newId = resolveNewActivityId(
|
|
11428
|
+
action.activityId,
|
|
11429
|
+
adapter,
|
|
11430
|
+
activityIdGen,
|
|
11431
|
+
deps.isActivityIdReserved
|
|
11432
|
+
);
|
|
11433
|
+
const newUid = uidGen.next();
|
|
11434
|
+
const activityInput = {
|
|
11435
|
+
id: newId,
|
|
11151
11436
|
parentId: isRoot ? null : action.parentId,
|
|
11152
11437
|
sector: {
|
|
11153
11438
|
id: sector.id,
|
|
@@ -11237,9 +11522,18 @@ function validateAnchorSibling(action, adapter, isRoot, anchorSiblingId) {
|
|
|
11237
11522
|
}
|
|
11238
11523
|
return null;
|
|
11239
11524
|
}
|
|
11240
|
-
function resolveNewActivityId(explicitId, adapter, activityIdGen) {
|
|
11241
|
-
|
|
11242
|
-
|
|
11525
|
+
function resolveNewActivityId(explicitId, adapter, activityIdGen, isActivityIdReserved) {
|
|
11526
|
+
const nextAvailableId = () => {
|
|
11527
|
+
let candidate = activityIdGen.next();
|
|
11528
|
+
while (adapter.activityExists(candidate) || isActivityIdReserved(String(candidate))) {
|
|
11529
|
+
candidate = activityIdGen.next();
|
|
11530
|
+
}
|
|
11531
|
+
return candidate;
|
|
11532
|
+
};
|
|
11533
|
+
if (explicitId === void 0) return nextAvailableId();
|
|
11534
|
+
if (adapter.activityExists(explicitId) || isActivityIdReserved(String(explicitId))) {
|
|
11535
|
+
return nextAvailableId();
|
|
11536
|
+
}
|
|
11243
11537
|
activityIdGen.bump(explicitId);
|
|
11244
11538
|
return explicitId;
|
|
11245
11539
|
}
|
|
@@ -11318,7 +11612,15 @@ async function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, h
|
|
|
11318
11612
|
}
|
|
11319
11613
|
async function dispatchActivityCreate(action, options, deps) {
|
|
11320
11614
|
const { adapter, scheduler, sector } = deps;
|
|
11321
|
-
const
|
|
11615
|
+
const normalized2 = normalizeActivityCreationOverrides(
|
|
11616
|
+
action.overrides,
|
|
11617
|
+
sector.hoursPerDay
|
|
11618
|
+
);
|
|
11619
|
+
if (!normalized2.ok) return normalized2;
|
|
11620
|
+
const core = await createActivityCore(
|
|
11621
|
+
{ ...action, overrides: normalized2.value },
|
|
11622
|
+
deps
|
|
11623
|
+
);
|
|
11322
11624
|
if (!core.ok) return core;
|
|
11323
11625
|
const { newId } = core;
|
|
11324
11626
|
const createdAutoScheduling = core.newActivity.autoScheduling;
|
|
@@ -11530,7 +11832,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11530
11832
|
for (const linkId of incidentLinkIds) {
|
|
11531
11833
|
adapter.removeLink(linkId);
|
|
11532
11834
|
}
|
|
11533
|
-
const
|
|
11835
|
+
const viewStateChanges2 = captureDeletedViewState(adapter, toDelete);
|
|
11534
11836
|
let removed = 0;
|
|
11535
11837
|
for (const id of toDelete) {
|
|
11536
11838
|
removed += 1;
|
|
@@ -11612,7 +11914,6 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11612
11914
|
touchedIds,
|
|
11613
11915
|
scheduledIds,
|
|
11614
11916
|
sirDetection: "after-diff",
|
|
11615
|
-
preview: options.preview,
|
|
11616
11917
|
links: buildLinkDeletions(beforeLinks),
|
|
11617
11918
|
trackingEvents: [trackingEvent],
|
|
11618
11919
|
hoursPerDay: sector.hoursPerDay,
|
|
@@ -11620,7 +11921,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11620
11921
|
});
|
|
11621
11922
|
return {
|
|
11622
11923
|
ok: true,
|
|
11623
|
-
changes:
|
|
11924
|
+
changes: viewStateChanges2.length > 0 ? { ...changes, viewState: viewStateChanges2 } : changes,
|
|
11624
11925
|
__beforeSnap: deletedRowsSnap,
|
|
11625
11926
|
__beforeLinks: beforeLinksByStringId
|
|
11626
11927
|
};
|
|
@@ -11728,7 +12029,7 @@ async function dispatchActivityBatch(action, options, deps) {
|
|
|
11728
12029
|
source: link.source,
|
|
11729
12030
|
target: link.target,
|
|
11730
12031
|
type: link.type,
|
|
11731
|
-
lag:
|
|
12032
|
+
lag: lagDaysToHours(link.lag, deps.sector.hoursPerDay)
|
|
11732
12033
|
},
|
|
11733
12034
|
linkId: link.linkId
|
|
11734
12035
|
}));
|
|
@@ -11773,7 +12074,6 @@ async function dispatchActivityBatch(action, options, deps) {
|
|
|
11773
12074
|
links: buildLinkChangesForBatch(appliedLinks, beforeLinks, deps.adapter),
|
|
11774
12075
|
hoursPerDay: deps.sector.hoursPerDay,
|
|
11775
12076
|
correlativeShifts: existingCorrelativeShifts,
|
|
11776
|
-
preview: options.preview,
|
|
11777
12077
|
...action.mode === "replace" ? { sirDetection: "after-diff" } : {}
|
|
11778
12078
|
});
|
|
11779
12079
|
return {
|
|
@@ -11801,7 +12101,7 @@ function validateBatch(action, deps) {
|
|
|
11801
12101
|
}
|
|
11802
12102
|
if (item.activityId !== void 0) {
|
|
11803
12103
|
const activityId = String(item.activityId);
|
|
11804
|
-
if (activityId.length === 0 || activityId === "0" || explicitActivityIds.has(activityId)
|
|
12104
|
+
if (activityId.length === 0 || activityId === "0" || explicitActivityIds.has(activityId)) {
|
|
11805
12105
|
return REJECTION_REASON.INVALID;
|
|
11806
12106
|
}
|
|
11807
12107
|
explicitActivityIds.add(activityId);
|
|
@@ -11823,7 +12123,7 @@ function validateBatch(action, deps) {
|
|
|
11823
12123
|
}
|
|
11824
12124
|
if (link.linkId !== void 0) {
|
|
11825
12125
|
const linkId = String(link.linkId);
|
|
11826
|
-
if (linkId.length === 0 || explicitLinkIds.has(linkId)
|
|
12126
|
+
if (linkId.length === 0 || explicitLinkIds.has(linkId)) {
|
|
11827
12127
|
return REJECTION_REASON.INVALID;
|
|
11828
12128
|
}
|
|
11829
12129
|
explicitLinkIds.add(linkId);
|
|
@@ -11859,24 +12159,38 @@ function hasParentCycle(items) {
|
|
|
11859
12159
|
return false;
|
|
11860
12160
|
}
|
|
11861
12161
|
function buildBatchPlan(action, deps) {
|
|
12162
|
+
const normalizedOverrides = /* @__PURE__ */ new Map();
|
|
11862
12163
|
for (const item of action.items) {
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
12164
|
+
const normalized2 = normalizeActivityCreationOverrides(
|
|
12165
|
+
item.overrides,
|
|
12166
|
+
deps.sector.hoursPerDay
|
|
12167
|
+
);
|
|
12168
|
+
if (!normalized2.ok) return normalized2;
|
|
12169
|
+
normalizedOverrides.set(item.batchId, normalized2.value);
|
|
11866
12170
|
}
|
|
11867
12171
|
const activityIdsByBatchId = /* @__PURE__ */ new Map();
|
|
12172
|
+
const plannedActivityIds = /* @__PURE__ */ new Set();
|
|
11868
12173
|
for (const item of action.items) {
|
|
11869
|
-
|
|
11870
|
-
item.
|
|
11871
|
-
|
|
12174
|
+
const activityId = resolvePlannedActivityId(
|
|
12175
|
+
item.activityId,
|
|
12176
|
+
action.mode,
|
|
12177
|
+
plannedActivityIds,
|
|
12178
|
+
deps
|
|
11872
12179
|
);
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
const linkIds = action.links.map(
|
|
11878
|
-
|
|
11879
|
-
|
|
12180
|
+
plannedActivityIds.add(String(activityId));
|
|
12181
|
+
activityIdsByBatchId.set(item.batchId, activityId);
|
|
12182
|
+
}
|
|
12183
|
+
const plannedLinkIds = /* @__PURE__ */ new Set();
|
|
12184
|
+
const linkIds = action.links.map((link) => {
|
|
12185
|
+
const linkId = resolvePlannedLinkId(
|
|
12186
|
+
link.linkId,
|
|
12187
|
+
action.mode,
|
|
12188
|
+
plannedLinkIds,
|
|
12189
|
+
deps
|
|
12190
|
+
);
|
|
12191
|
+
plannedLinkIds.add(String(linkId));
|
|
12192
|
+
return linkId;
|
|
12193
|
+
});
|
|
11880
12194
|
const insertionCorrelativeIds = planInsertionCorrelatives(
|
|
11881
12195
|
action,
|
|
11882
12196
|
activityIdsByBatchId,
|
|
@@ -11899,7 +12213,8 @@ function buildBatchPlan(action, deps) {
|
|
|
11899
12213
|
activityId,
|
|
11900
12214
|
parentId,
|
|
11901
12215
|
parentBatchId,
|
|
11902
|
-
insertionCorrelativeId
|
|
12216
|
+
insertionCorrelativeId,
|
|
12217
|
+
overrides: normalizedOverrides.get(item.batchId) ?? {}
|
|
11903
12218
|
});
|
|
11904
12219
|
}
|
|
11905
12220
|
const links = [];
|
|
@@ -11920,6 +12235,30 @@ function buildBatchPlan(action, deps) {
|
|
|
11920
12235
|
}
|
|
11921
12236
|
return { ok: true, plan: { items, links } };
|
|
11922
12237
|
}
|
|
12238
|
+
function resolvePlannedActivityId(explicitId, mode, plannedIds, deps) {
|
|
12239
|
+
const explicitIsAvailable = explicitId !== void 0 && (mode === "replace" || deps.adapter.getActivity(explicitId) === null && !deps.isActivityIdReserved(String(explicitId))) && !plannedIds.has(String(explicitId));
|
|
12240
|
+
if (explicitIsAvailable) {
|
|
12241
|
+
deps.activityIdGen.bump(explicitId);
|
|
12242
|
+
return explicitId;
|
|
12243
|
+
}
|
|
12244
|
+
let candidate = deps.activityIdGen.next();
|
|
12245
|
+
while (plannedIds.has(String(candidate)) || mode === "append" && (deps.adapter.getActivity(candidate) !== null || deps.isActivityIdReserved(String(candidate)))) {
|
|
12246
|
+
candidate = deps.activityIdGen.next();
|
|
12247
|
+
}
|
|
12248
|
+
return candidate;
|
|
12249
|
+
}
|
|
12250
|
+
function resolvePlannedLinkId(explicitId, mode, plannedIds, deps) {
|
|
12251
|
+
const explicitIsAvailable = explicitId !== void 0 && (mode === "replace" || deps.adapter.getLink(explicitId) === null && !deps.isLinkIdReserved(String(explicitId))) && !plannedIds.has(String(explicitId));
|
|
12252
|
+
if (explicitIsAvailable) {
|
|
12253
|
+
deps.linkIdGen.bump(explicitId);
|
|
12254
|
+
return explicitId;
|
|
12255
|
+
}
|
|
12256
|
+
let candidate = deps.linkIdGen.next();
|
|
12257
|
+
while (plannedIds.has(String(candidate)) || mode === "append" && (deps.adapter.getLink(candidate) !== null || deps.isLinkIdReserved(String(candidate)))) {
|
|
12258
|
+
candidate = deps.linkIdGen.next();
|
|
12259
|
+
}
|
|
12260
|
+
return candidate;
|
|
12261
|
+
}
|
|
11923
12262
|
function planInsertionCorrelatives(action, activityIdsByBatchId, deps) {
|
|
11924
12263
|
if (isSimpleRootAppend(action)) {
|
|
11925
12264
|
const firstCorrelativeId = deps.adapter.activityCount();
|
|
@@ -12029,7 +12368,7 @@ async function createPlannedActivities(action, plannedItems, deps) {
|
|
|
12029
12368
|
{
|
|
12030
12369
|
parentId: item.parentId,
|
|
12031
12370
|
activityId: item.activityId,
|
|
12032
|
-
|
|
12371
|
+
overrides: item.overrides,
|
|
12033
12372
|
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
12034
12373
|
},
|
|
12035
12374
|
deps,
|
|
@@ -12106,301 +12445,280 @@ function dispatchSirSync(action, deps) {
|
|
|
12106
12445
|
};
|
|
12107
12446
|
}
|
|
12108
12447
|
|
|
12109
|
-
// src/
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12448
|
+
// src/propagations/upward/progress-rollup.ts
|
|
12449
|
+
function recomputeAllProgressRollup(adapter) {
|
|
12450
|
+
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
12451
|
+
rollupParent(parentId, adapter);
|
|
12452
|
+
}
|
|
12453
|
+
}
|
|
12454
|
+
function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter) {
|
|
12455
|
+
for (const parentId of parentIdsDeepestFirst) {
|
|
12456
|
+
rollupParent(parentId, adapter);
|
|
12457
|
+
}
|
|
12458
|
+
}
|
|
12459
|
+
function parentsDeepestFirst(adapter) {
|
|
12460
|
+
const parents = /* @__PURE__ */ new Set();
|
|
12461
|
+
adapter.forEachActivity((_activity, id) => {
|
|
12462
|
+
if (adapter.getChildren(id).length > 0) parents.add(id);
|
|
12463
|
+
});
|
|
12464
|
+
const depthById = /* @__PURE__ */ new Map();
|
|
12465
|
+
for (const id of parents) depthById.set(id, depthOf2(id, adapter));
|
|
12466
|
+
return [...parents].sort(
|
|
12467
|
+
(a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
|
|
12115
12468
|
);
|
|
12116
|
-
|
|
12117
|
-
|
|
12469
|
+
}
|
|
12470
|
+
function depthOf2(id, adapter) {
|
|
12471
|
+
let depth = 0;
|
|
12472
|
+
let current = id;
|
|
12473
|
+
while (current && current !== "0") {
|
|
12474
|
+
if (!adapter.getActivity(current)) break;
|
|
12475
|
+
depth += 1;
|
|
12476
|
+
current = adapter.getParentId(current);
|
|
12118
12477
|
}
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
const
|
|
12123
|
-
|
|
12124
|
-
const
|
|
12125
|
-
const
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
for (const touchedId of editTouched) touchedIds.add(touchedId);
|
|
12140
|
-
recordConstraintPrior(constraintPriorsByActivity, edit, beforeSnap);
|
|
12141
|
-
if (isExplicitConstraintDateEdit(edit.column)) {
|
|
12142
|
-
explicitDateEditActivities.add(String(edit.activityId));
|
|
12143
|
-
}
|
|
12144
|
-
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
12145
|
-
const preEditSnapshot = snapshotSingleActivity(
|
|
12146
|
-
adapter,
|
|
12147
|
-
String(edit.activityId)
|
|
12148
|
-
);
|
|
12149
|
-
const customIdBeforeApply = readLiveCustomId(edit, deps);
|
|
12150
|
-
const changes = outcome.changes;
|
|
12151
|
-
applyFieldChanges(adapter, edit.activityId, changes);
|
|
12152
|
-
runPostProcessorsOnAdapter(
|
|
12153
|
-
edit.activityId,
|
|
12154
|
-
outcome.changes.postProcessors ?? [],
|
|
12155
|
-
adapter,
|
|
12156
|
-
preEditSnapshot
|
|
12157
|
-
);
|
|
12158
|
-
syncCustomIdTracker(edit, customIdBeforeApply, deps);
|
|
12159
|
-
linkChanges.push(
|
|
12160
|
-
...diffIncomingLinkLagChanges(beforeLinkLags, adapter, edit.activityId)
|
|
12478
|
+
return depth;
|
|
12479
|
+
}
|
|
12480
|
+
function rollupParent(parentId, adapter) {
|
|
12481
|
+
const childIds = adapter.getChildren(parentId);
|
|
12482
|
+
if (childIds.length === 0) return;
|
|
12483
|
+
const children = [];
|
|
12484
|
+
for (const childId of childIds) {
|
|
12485
|
+
const child = adapter.getActivity(childId);
|
|
12486
|
+
if (!child) continue;
|
|
12487
|
+
children.push({
|
|
12488
|
+
progress: Number(child.progress ?? 0),
|
|
12489
|
+
ponderator: Number(child.ponderator ?? 0)
|
|
12490
|
+
});
|
|
12491
|
+
}
|
|
12492
|
+
const rollup = computeWeightedProgressRollup(children);
|
|
12493
|
+
if (rollup !== null) {
|
|
12494
|
+
adapter.setActivityField(
|
|
12495
|
+
parentId,
|
|
12496
|
+
COLUMN.PROGRESS,
|
|
12497
|
+
roundProgressPerLevel(rollup)
|
|
12161
12498
|
);
|
|
12162
|
-
needsAutoSchedule ||= Boolean(outcome.changes.needsAutoSchedule);
|
|
12163
|
-
for (const trackingEvent of outcome.changes.trackingEvents ?? []) {
|
|
12164
|
-
trackingEvents.push({
|
|
12165
|
-
name: trackingEvent.name,
|
|
12166
|
-
properties: trackingEvent.properties ?? {}
|
|
12167
|
-
});
|
|
12168
|
-
}
|
|
12169
12499
|
}
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
{
|
|
12180
|
-
adapter,
|
|
12181
|
-
scheduler,
|
|
12182
|
-
sector,
|
|
12183
|
-
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12184
|
-
},
|
|
12185
|
-
{
|
|
12186
|
-
postMutationArgs,
|
|
12187
|
-
revertTargets: [...constraintPriorsByActivity].filter(
|
|
12188
|
-
([activityId]) => !explicitDateEditActivities.has(activityId)
|
|
12189
|
-
).map(([activityId, priorConstraintDate]) => ({
|
|
12190
|
-
activityId,
|
|
12191
|
-
priorConstraintDate
|
|
12192
|
-
})),
|
|
12193
|
-
revert: (activityId, priorConstraintDate) => revertNoOpConstraintEdit(
|
|
12194
|
-
activityId,
|
|
12195
|
-
priorConstraintDate,
|
|
12196
|
-
adapter,
|
|
12197
|
-
deps.ctx.calendars
|
|
12198
|
-
)
|
|
12500
|
+
}
|
|
12501
|
+
|
|
12502
|
+
// src/dispatch/handlers/persistence-acknowledge.ts
|
|
12503
|
+
function persistedOwnersOf(entities) {
|
|
12504
|
+
const owners = /* @__PURE__ */ new Map();
|
|
12505
|
+
for (const entity of entities) {
|
|
12506
|
+
const proplannerId = entity.proplannerId;
|
|
12507
|
+
if (typeof proplannerId === "number" && Number.isSafeInteger(proplannerId) && proplannerId > 0) {
|
|
12508
|
+
owners.set(proplannerId, String(entity.id));
|
|
12199
12509
|
}
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
adapter,
|
|
12203
|
-
scheduler,
|
|
12204
|
-
sector,
|
|
12205
|
-
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12206
|
-
},
|
|
12207
|
-
postMutationArgs
|
|
12208
|
-
));
|
|
12209
|
-
return {
|
|
12210
|
-
ok: true,
|
|
12211
|
-
verdicts,
|
|
12212
|
-
changes: await assembleChangeSet(adapter, {
|
|
12213
|
-
source: action,
|
|
12214
|
-
beforeSnap,
|
|
12215
|
-
touchedIds,
|
|
12216
|
-
scheduledIds,
|
|
12217
|
-
sirDetection: "before-diff",
|
|
12218
|
-
preview: options.preview,
|
|
12219
|
-
links: linkChanges,
|
|
12220
|
-
trackingEvents,
|
|
12221
|
-
warnings,
|
|
12222
|
-
hoursPerDay: sector.hoursPerDay
|
|
12223
|
-
})
|
|
12224
|
-
};
|
|
12510
|
+
}
|
|
12511
|
+
return owners;
|
|
12225
12512
|
}
|
|
12226
|
-
function
|
|
12227
|
-
const {
|
|
12228
|
-
const
|
|
12229
|
-
|
|
12230
|
-
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
ok: false,
|
|
12235
|
-
reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN
|
|
12236
|
-
},
|
|
12237
|
-
changes: null
|
|
12238
|
-
};
|
|
12513
|
+
function assertAllNewEntitiesAcknowledged(entities, assignments, entityName) {
|
|
12514
|
+
const acknowledgedIds = new Set(assignments.map(({ id }) => String(id)));
|
|
12515
|
+
for (const entity of entities) {
|
|
12516
|
+
if ((entity.proplannerId == null || entity.proplannerId === 0) && !acknowledgedIds.has(String(entity.id))) {
|
|
12517
|
+
throw new Error(
|
|
12518
|
+
`persistence-acknowledge: missing ${entityName} proplannerId for "${String(entity.id)}"`
|
|
12519
|
+
);
|
|
12520
|
+
}
|
|
12239
12521
|
}
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
},
|
|
12248
|
-
changes: null
|
|
12249
|
-
};
|
|
12522
|
+
}
|
|
12523
|
+
function assertValidAssignment(assignment, entityName, readCurrent, ownerByBackendId, seenIds, seenBackendIds) {
|
|
12524
|
+
const id = String(assignment.id);
|
|
12525
|
+
if (id.length === 0 || seenIds.has(id)) {
|
|
12526
|
+
throw new Error(
|
|
12527
|
+
`persistence-acknowledge: duplicate or empty ${entityName} id "${id}"`
|
|
12528
|
+
);
|
|
12250
12529
|
}
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
activityId,
|
|
12256
|
-
ok: false,
|
|
12257
|
-
reason: editGate.reason ?? REJECTION_REASON.CANNOT_EDIT,
|
|
12258
|
-
...editGate.alertKey ? { alertKey: editGate.alertKey } : {}
|
|
12259
|
-
},
|
|
12260
|
-
changes: null
|
|
12261
|
-
};
|
|
12530
|
+
if (!Number.isSafeInteger(assignment.proplannerId) || assignment.proplannerId <= 0) {
|
|
12531
|
+
throw new Error(
|
|
12532
|
+
`persistence-acknowledge: invalid ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
12533
|
+
);
|
|
12262
12534
|
}
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
activityId,
|
|
12268
|
-
ok: false,
|
|
12269
|
-
reason: parsed2.reason ?? REJECTION_REASON.PARSE_ERROR
|
|
12270
|
-
},
|
|
12271
|
-
changes: null
|
|
12272
|
-
};
|
|
12535
|
+
if (seenBackendIds.has(assignment.proplannerId)) {
|
|
12536
|
+
throw new Error(
|
|
12537
|
+
`persistence-acknowledge: duplicate ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
12538
|
+
);
|
|
12273
12539
|
}
|
|
12274
|
-
const
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
ctx
|
|
12280
|
-
);
|
|
12281
|
-
if (!validation.valid) {
|
|
12282
|
-
return {
|
|
12283
|
-
verdict: {
|
|
12284
|
-
activityId,
|
|
12285
|
-
ok: false,
|
|
12286
|
-
reason: validation.reason ?? REJECTION_REASON.INVALID,
|
|
12287
|
-
...validation.notification?.messageKey ? { alertKey: validation.notification.messageKey } : {}
|
|
12288
|
-
},
|
|
12289
|
-
changes: null
|
|
12290
|
-
};
|
|
12540
|
+
const state = readCurrent(id);
|
|
12541
|
+
if (!state.exists) {
|
|
12542
|
+
throw new Error(
|
|
12543
|
+
`persistence-acknowledge: unknown ${entityName} id "${id}"`
|
|
12544
|
+
);
|
|
12291
12545
|
}
|
|
12292
|
-
if (
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
if (deps.customIdTracker.isCustomIdInUse(String(parsed2.value), oldCustomId)) {
|
|
12297
|
-
return {
|
|
12298
|
-
verdict: {
|
|
12299
|
-
activityId,
|
|
12300
|
-
ok: false,
|
|
12301
|
-
reason: REJECTION_REASON.CUSTOM_ID_DUPLICATE
|
|
12302
|
-
},
|
|
12303
|
-
changes: null
|
|
12304
|
-
};
|
|
12305
|
-
}
|
|
12546
|
+
if (state.current !== null && state.current !== 0 && state.current !== assignment.proplannerId) {
|
|
12547
|
+
throw new Error(
|
|
12548
|
+
`persistence-acknowledge: ${entityName} "${id}" already has proplannerId ${String(state.current)}`
|
|
12549
|
+
);
|
|
12306
12550
|
}
|
|
12307
|
-
const
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
);
|
|
12313
|
-
return { verdict: { activityId, ok: true }, changes };
|
|
12314
|
-
}
|
|
12315
|
-
function readLiveCustomId(edit, deps) {
|
|
12316
|
-
if (edit.column !== CUSTOM_ID_COLUMN) return null;
|
|
12317
|
-
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
12318
|
-
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
12319
|
-
return typeof rawValue === "string" ? rawValue : null;
|
|
12320
|
-
}
|
|
12321
|
-
function syncCustomIdTracker(edit, oldCustomId, deps) {
|
|
12322
|
-
if (edit.column !== CUSTOM_ID_COLUMN) return;
|
|
12323
|
-
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
12324
|
-
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
12325
|
-
const newCustomId = typeof rawValue === "string" ? rawValue : null;
|
|
12326
|
-
if (oldCustomId !== newCustomId) {
|
|
12327
|
-
deps.customIdTracker.trackCustomIdChange(oldCustomId, newCustomId);
|
|
12551
|
+
const existingOwner = ownerByBackendId.get(assignment.proplannerId);
|
|
12552
|
+
if (existingOwner !== void 0 && existingOwner !== id) {
|
|
12553
|
+
throw new Error(
|
|
12554
|
+
`persistence-acknowledge: ${entityName} proplannerId ${String(assignment.proplannerId)} already belongs to "${existingOwner}"`
|
|
12555
|
+
);
|
|
12328
12556
|
}
|
|
12557
|
+
seenIds.add(id);
|
|
12558
|
+
seenBackendIds.add(assignment.proplannerId);
|
|
12329
12559
|
}
|
|
12330
|
-
function
|
|
12331
|
-
|
|
12332
|
-
const
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12560
|
+
function assertValidAssignments(assignments, entityName, readCurrent, ownerByBackendId) {
|
|
12561
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
12562
|
+
const seenBackendIds = /* @__PURE__ */ new Set();
|
|
12563
|
+
for (const assignment of assignments) {
|
|
12564
|
+
assertValidAssignment(
|
|
12565
|
+
assignment,
|
|
12566
|
+
entityName,
|
|
12567
|
+
readCurrent,
|
|
12568
|
+
ownerByBackendId,
|
|
12569
|
+
seenIds,
|
|
12570
|
+
seenBackendIds
|
|
12571
|
+
);
|
|
12342
12572
|
}
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12573
|
+
}
|
|
12574
|
+
function assertValidReconciledProgress(entries, deps) {
|
|
12575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12576
|
+
for (const entry of entries) {
|
|
12577
|
+
const id = String(entry.activityId);
|
|
12578
|
+
if (id.length === 0 || seen.has(id)) {
|
|
12579
|
+
throw new Error(
|
|
12580
|
+
`persistence-acknowledge: duplicate or empty reconciled activity id "${id}"`
|
|
12581
|
+
);
|
|
12582
|
+
}
|
|
12583
|
+
if (!Number.isFinite(entry.progress) || entry.progress < 0 || entry.progress > 100) {
|
|
12584
|
+
throw new Error(
|
|
12585
|
+
`persistence-acknowledge: invalid reconciled progress ${String(entry.progress)} for "${id}"`
|
|
12586
|
+
);
|
|
12587
|
+
}
|
|
12588
|
+
if (!deps.adapter.getActivity(id)) {
|
|
12589
|
+
throw new Error(
|
|
12590
|
+
`persistence-acknowledge: unknown reconciled activity id "${id}"`
|
|
12591
|
+
);
|
|
12348
12592
|
}
|
|
12593
|
+
seen.add(id);
|
|
12349
12594
|
}
|
|
12350
12595
|
}
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
const
|
|
12360
|
-
|
|
12361
|
-
|
|
12596
|
+
async function applyReconciledProgress(entries, deps) {
|
|
12597
|
+
const progressToApply = entries.filter((entry) => {
|
|
12598
|
+
const current = deps.adapter.getActivity(String(entry.activityId));
|
|
12599
|
+
return current !== null && current.progress !== entry.progress;
|
|
12600
|
+
});
|
|
12601
|
+
const progressIds = new Set(
|
|
12602
|
+
progressToApply.map((entry) => String(entry.activityId))
|
|
12603
|
+
);
|
|
12604
|
+
const ancestors = findAncestorsOfDirty(deps.adapter, progressIds);
|
|
12605
|
+
const touched = new Set(progressIds);
|
|
12606
|
+
for (const parentId of ancestors) {
|
|
12607
|
+
touched.add(String(parentId));
|
|
12362
12608
|
}
|
|
12363
|
-
const
|
|
12364
|
-
adapter.
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12609
|
+
const before = await snapshotActivities(deps.adapter, touched);
|
|
12610
|
+
deps.adapter.batchUpdate(() => {
|
|
12611
|
+
for (const entry of progressToApply) {
|
|
12612
|
+
deps.adapter.setActivityField(
|
|
12613
|
+
String(entry.activityId),
|
|
12614
|
+
"progress",
|
|
12615
|
+
entry.progress
|
|
12616
|
+
);
|
|
12617
|
+
}
|
|
12618
|
+
recomputeProgressRollupForParents(ancestors, deps.adapter);
|
|
12619
|
+
recomputeRealWorkFromProgress(progressIds, ancestors, deps.adapter);
|
|
12620
|
+
});
|
|
12621
|
+
return [...await buildActivityChanges(deps.adapter, before, touched)];
|
|
12622
|
+
}
|
|
12623
|
+
async function dispatchPersistenceAcknowledge(action, deps) {
|
|
12624
|
+
const activities = action.activities ?? [];
|
|
12625
|
+
const links = action.links ?? [];
|
|
12626
|
+
const reconciledProgress = action.reconciledProgress ?? [];
|
|
12627
|
+
assertValidReconciledProgress(reconciledProgress, deps);
|
|
12628
|
+
const currentActivities = deps.adapter.getAllActivities();
|
|
12629
|
+
const currentLinks = deps.adapter.getAllLinks();
|
|
12630
|
+
const activityOwnerByBackendId = persistedOwnersOf(currentActivities);
|
|
12631
|
+
const linkOwnerByBackendId = persistedOwnersOf(currentLinks);
|
|
12632
|
+
assertValidAssignments(
|
|
12633
|
+
activities,
|
|
12634
|
+
"activity",
|
|
12635
|
+
(id) => {
|
|
12636
|
+
const activity = deps.adapter.getActivity(id);
|
|
12637
|
+
return {
|
|
12638
|
+
exists: activity !== null,
|
|
12639
|
+
current: activity?.proplannerId ?? null
|
|
12640
|
+
};
|
|
12387
12641
|
},
|
|
12388
|
-
|
|
12642
|
+
activityOwnerByBackendId
|
|
12643
|
+
);
|
|
12644
|
+
assertValidAssignments(
|
|
12645
|
+
links,
|
|
12646
|
+
"link",
|
|
12647
|
+
(id) => {
|
|
12648
|
+
const link = deps.adapter.getLink(id);
|
|
12649
|
+
return { exists: link !== null, current: link?.proplannerId ?? null };
|
|
12650
|
+
},
|
|
12651
|
+
linkOwnerByBackendId
|
|
12652
|
+
);
|
|
12653
|
+
assertAllNewEntitiesAcknowledged(currentActivities, activities, "activity");
|
|
12654
|
+
assertAllNewEntitiesAcknowledged(currentLinks, links, "link");
|
|
12655
|
+
const activityChanges = await applyReconciledProgress(
|
|
12656
|
+
reconciledProgress,
|
|
12389
12657
|
deps
|
|
12390
12658
|
);
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
(
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12659
|
+
const linkChanges = [];
|
|
12660
|
+
deps.adapter.batchUpdate(() => {
|
|
12661
|
+
for (const assignment of activities) {
|
|
12662
|
+
const before = deps.adapter.getActivity(assignment.id);
|
|
12663
|
+
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
12664
|
+
const previousProplannerId = before.proplannerId;
|
|
12665
|
+
deps.adapter.setActivityField(
|
|
12666
|
+
assignment.id,
|
|
12667
|
+
"proplannerId",
|
|
12668
|
+
assignment.proplannerId
|
|
12669
|
+
);
|
|
12670
|
+
activityChanges.push({
|
|
12671
|
+
id: assignment.id,
|
|
12672
|
+
kind: "updated",
|
|
12673
|
+
fields: {
|
|
12674
|
+
proplannerId: {
|
|
12675
|
+
before: previousProplannerId,
|
|
12676
|
+
after: assignment.proplannerId
|
|
12677
|
+
}
|
|
12678
|
+
},
|
|
12679
|
+
after: { ...before, proplannerId: assignment.proplannerId }
|
|
12680
|
+
});
|
|
12681
|
+
}
|
|
12682
|
+
for (const assignment of links) {
|
|
12683
|
+
const before = deps.adapter.getLink(assignment.id);
|
|
12684
|
+
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
12685
|
+
const previousProplannerId = before.proplannerId;
|
|
12686
|
+
deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
|
|
12687
|
+
linkChanges.push({
|
|
12688
|
+
id: assignment.id,
|
|
12689
|
+
kind: "updated",
|
|
12690
|
+
fields: {
|
|
12691
|
+
proplannerId: {
|
|
12692
|
+
before: previousProplannerId,
|
|
12693
|
+
after: assignment.proplannerId
|
|
12694
|
+
}
|
|
12695
|
+
},
|
|
12696
|
+
after: { ...before, proplannerId: assignment.proplannerId }
|
|
12697
|
+
});
|
|
12698
|
+
}
|
|
12699
|
+
});
|
|
12700
|
+
return {
|
|
12701
|
+
ok: true,
|
|
12702
|
+
changes: {
|
|
12703
|
+
source: action,
|
|
12704
|
+
activities: activityChanges,
|
|
12705
|
+
links: linkChanges,
|
|
12706
|
+
calendars: [],
|
|
12707
|
+
trackingEvents: []
|
|
12708
|
+
}
|
|
12709
|
+
};
|
|
12399
12710
|
}
|
|
12400
12711
|
|
|
12401
12712
|
// src/dispatch/handlers/activity-lookahead-sync.ts
|
|
12402
|
-
function dispatchActivityLookaheadSync(action, deps) {
|
|
12713
|
+
async function dispatchActivityLookaheadSync(action, options, deps) {
|
|
12714
|
+
if (hasInvalidPersistedProgress(action)) {
|
|
12715
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
12716
|
+
}
|
|
12403
12717
|
const ids = [...new Set(action.activityIds.map(String))];
|
|
12718
|
+
const acknowledgesUnknownPersistedActivity = action.acknowledgePersistedLookahead === true && ids.some((activityId) => !deps.isActivityIdReserved(activityId));
|
|
12719
|
+
if (acknowledgesUnknownPersistedActivity) {
|
|
12720
|
+
return { ok: false, reason: REJECTION_REASON.INVALID };
|
|
12721
|
+
}
|
|
12404
12722
|
const snapshots = ids.map((id) => deps.adapter.getActivity(id));
|
|
12405
12723
|
if (snapshots.some((snapshot) => !snapshot)) {
|
|
12406
12724
|
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
@@ -12440,6 +12758,39 @@ function dispatchActivityLookaheadSync(action, deps) {
|
|
|
12440
12758
|
}
|
|
12441
12759
|
});
|
|
12442
12760
|
}
|
|
12761
|
+
if (action.persistedProgress !== void 0) {
|
|
12762
|
+
const progressChanges = await applyReconciledProgress(
|
|
12763
|
+
[{ activityId: ids[0], progress: action.persistedProgress }],
|
|
12764
|
+
deps
|
|
12765
|
+
);
|
|
12766
|
+
if (progressChanges.length > 0) {
|
|
12767
|
+
await runPostMutation(
|
|
12768
|
+
{
|
|
12769
|
+
adapter: deps.adapter,
|
|
12770
|
+
scheduler: deps.scheduler,
|
|
12771
|
+
sector: deps.sector,
|
|
12772
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12773
|
+
},
|
|
12774
|
+
{
|
|
12775
|
+
action,
|
|
12776
|
+
autoscheduleFrom: null,
|
|
12777
|
+
recomputeParentsFrom: [],
|
|
12778
|
+
now: deps.now,
|
|
12779
|
+
options
|
|
12780
|
+
}
|
|
12781
|
+
);
|
|
12782
|
+
return {
|
|
12783
|
+
ok: true,
|
|
12784
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
12785
|
+
source: action,
|
|
12786
|
+
beforeSnap: /* @__PURE__ */ new Map(),
|
|
12787
|
+
touchedIds: [],
|
|
12788
|
+
scheduledIds: [],
|
|
12789
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
12790
|
+
})
|
|
12791
|
+
};
|
|
12792
|
+
}
|
|
12793
|
+
}
|
|
12443
12794
|
return {
|
|
12444
12795
|
ok: true,
|
|
12445
12796
|
changes: {
|
|
@@ -12451,6 +12802,10 @@ function dispatchActivityLookaheadSync(action, deps) {
|
|
|
12451
12802
|
}
|
|
12452
12803
|
};
|
|
12453
12804
|
}
|
|
12805
|
+
function hasInvalidPersistedProgress(action) {
|
|
12806
|
+
const progress = action.persistedProgress;
|
|
12807
|
+
return progress !== void 0 && (action.activityIds.length !== 1 || !Number.isFinite(progress) || progress < 0 || progress > 100);
|
|
12808
|
+
}
|
|
12454
12809
|
|
|
12455
12810
|
// src/internal/hierarchy/invalid-links.ts
|
|
12456
12811
|
function collectInvalidLinkIdsForCycle(movedActivityIds, adapter) {
|
|
@@ -12718,7 +13073,6 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
12718
13073
|
touchedIds: touched,
|
|
12719
13074
|
scheduledIds,
|
|
12720
13075
|
sirDetection: "before-diff",
|
|
12721
|
-
preview: options.preview,
|
|
12722
13076
|
trackingEvents: [trackingEvent],
|
|
12723
13077
|
hoursPerDay: sector.hoursPerDay
|
|
12724
13078
|
})
|
|
@@ -13260,11 +13614,11 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
13260
13614
|
const warnings = [];
|
|
13261
13615
|
let needsAutoSchedule = false;
|
|
13262
13616
|
for (const edit of action.edits) {
|
|
13263
|
-
const outcome =
|
|
13617
|
+
const outcome = applyOneEdit(edit, deps);
|
|
13264
13618
|
verdicts.push(outcome.verdict);
|
|
13265
13619
|
if (!outcome.verdict.ok || !outcome.changes) continue;
|
|
13266
13620
|
const editTouched = collectTouchedIds(edit.activityId, outcome.changes);
|
|
13267
|
-
|
|
13621
|
+
mergeBeforeSnapshots(beforeSnap, editTouched, deps);
|
|
13268
13622
|
for (const id of editTouched) touchedIds.add(id);
|
|
13269
13623
|
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
13270
13624
|
const preEditSnapshot = snapshotSingleActivity(
|
|
@@ -13318,7 +13672,6 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
13318
13672
|
touchedIds,
|
|
13319
13673
|
scheduledIds,
|
|
13320
13674
|
sirDetection: "before-diff",
|
|
13321
|
-
preview: options.preview,
|
|
13322
13675
|
links: linkChanges,
|
|
13323
13676
|
trackingEvents,
|
|
13324
13677
|
warnings,
|
|
@@ -13326,7 +13679,7 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
13326
13679
|
})
|
|
13327
13680
|
};
|
|
13328
13681
|
}
|
|
13329
|
-
function
|
|
13682
|
+
function applyOneEdit(edit, deps) {
|
|
13330
13683
|
const { ctx } = deps;
|
|
13331
13684
|
const activityId = edit.activityId;
|
|
13332
13685
|
const pipeline = findPipelineForColumn(edit.column);
|
|
@@ -13388,7 +13741,7 @@ function applyOneEdit2(edit, deps) {
|
|
|
13388
13741
|
);
|
|
13389
13742
|
return { verdict: { activityId, ok: true }, changes };
|
|
13390
13743
|
}
|
|
13391
|
-
function
|
|
13744
|
+
function mergeBeforeSnapshots(beforeSnap, ids, deps) {
|
|
13392
13745
|
const missing = /* @__PURE__ */ new Set();
|
|
13393
13746
|
for (const id of ids) {
|
|
13394
13747
|
if (!beforeSnap.has(id)) missing.add(id);
|
|
@@ -13402,212 +13755,246 @@ function mergeBeforeSnapshots2(beforeSnap, ids, deps) {
|
|
|
13402
13755
|
}
|
|
13403
13756
|
}
|
|
13404
13757
|
|
|
13405
|
-
// src/dispatch/handlers/
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13414
|
-
throw new Error(
|
|
13415
|
-
`persistence-acknowledge: invalid ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
13416
|
-
);
|
|
13758
|
+
// src/dispatch/handlers/bulk-edit.ts
|
|
13759
|
+
var CUSTOM_ID_COLUMN = COLUMN.CUSTOM_ID;
|
|
13760
|
+
async function dispatchBulkEdit(action, options, deps) {
|
|
13761
|
+
const { adapter, scheduler, sector } = deps;
|
|
13762
|
+
const editsIncludeDuration = action.edits.some(
|
|
13763
|
+
(edit) => edit.column === COLUMN.DURATION
|
|
13764
|
+
);
|
|
13765
|
+
if (editsIncludeDuration) {
|
|
13766
|
+
scheduler.invalidateExpandedLinksCache();
|
|
13417
13767
|
}
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13768
|
+
const verdicts = [];
|
|
13769
|
+
const beforeSnap = /* @__PURE__ */ new Map();
|
|
13770
|
+
const constraintPriorsByActivity = /* @__PURE__ */ new Map();
|
|
13771
|
+
const explicitDateEditActivities = /* @__PURE__ */ new Set();
|
|
13772
|
+
const touchedIds = /* @__PURE__ */ new Set();
|
|
13773
|
+
const trackingEvents = [];
|
|
13774
|
+
const linkChanges = [];
|
|
13775
|
+
const warnings = [];
|
|
13776
|
+
let needsAutoSchedule = false;
|
|
13777
|
+
for (const edit of action.edits) {
|
|
13778
|
+
const outcome = applyOneEdit2(edit, deps);
|
|
13779
|
+
verdicts.push(outcome.verdict);
|
|
13780
|
+
if (!outcome.verdict.ok || !outcome.changes) continue;
|
|
13781
|
+
const constraintWarning = toConstraintWarning(
|
|
13782
|
+
edit.activityId,
|
|
13783
|
+
outcome.changes.constraintWarning
|
|
13421
13784
|
);
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13785
|
+
if (constraintWarning) warnings.push(constraintWarning);
|
|
13786
|
+
const editTouched = collectTouchedIds(edit.activityId, outcome.changes);
|
|
13787
|
+
mergeBeforeSnapshots2(beforeSnap, editTouched, deps);
|
|
13788
|
+
for (const touchedId of editTouched) touchedIds.add(touchedId);
|
|
13789
|
+
recordConstraintPrior(constraintPriorsByActivity, edit, beforeSnap);
|
|
13790
|
+
if (isExplicitConstraintDateEdit(edit.column)) {
|
|
13791
|
+
explicitDateEditActivities.add(String(edit.activityId));
|
|
13792
|
+
}
|
|
13793
|
+
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
13794
|
+
const preEditSnapshot = snapshotSingleActivity(
|
|
13795
|
+
adapter,
|
|
13796
|
+
String(edit.activityId)
|
|
13427
13797
|
);
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13798
|
+
const customIdBeforeApply = readLiveCustomId(edit, deps);
|
|
13799
|
+
const changes = outcome.changes;
|
|
13800
|
+
applyFieldChanges(adapter, edit.activityId, changes);
|
|
13801
|
+
runPostProcessorsOnAdapter(
|
|
13802
|
+
edit.activityId,
|
|
13803
|
+
outcome.changes.postProcessors ?? [],
|
|
13804
|
+
adapter,
|
|
13805
|
+
preEditSnapshot
|
|
13432
13806
|
);
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
13436
|
-
throw new Error(
|
|
13437
|
-
`persistence-acknowledge: ${entityName} proplannerId ${String(assignment.proplannerId)} already belongs to "${existingOwner}"`
|
|
13807
|
+
syncCustomIdTracker(edit, customIdBeforeApply, deps);
|
|
13808
|
+
linkChanges.push(
|
|
13809
|
+
...diffIncomingLinkLagChanges(beforeLinkLags, adapter, edit.activityId)
|
|
13438
13810
|
);
|
|
13811
|
+
needsAutoSchedule ||= Boolean(outcome.changes.needsAutoSchedule);
|
|
13812
|
+
for (const trackingEvent of outcome.changes.trackingEvents ?? []) {
|
|
13813
|
+
trackingEvents.push({
|
|
13814
|
+
name: trackingEvent.name,
|
|
13815
|
+
properties: trackingEvent.properties ?? {}
|
|
13816
|
+
});
|
|
13817
|
+
}
|
|
13439
13818
|
}
|
|
13440
|
-
|
|
13441
|
-
|
|
13819
|
+
const postMutationArgs = {
|
|
13820
|
+
action,
|
|
13821
|
+
autoscheduleFrom: needsAutoSchedule ? "roots" : null,
|
|
13822
|
+
recomputeParentsFrom: touchedIds,
|
|
13823
|
+
now: deps.now,
|
|
13824
|
+
options
|
|
13825
|
+
};
|
|
13826
|
+
const batchHasConstraintEdits = constraintPriorsByActivity.size > 0;
|
|
13827
|
+
const { scheduledIds } = await (batchHasConstraintEdits ? settleConstraintEdits(
|
|
13828
|
+
{
|
|
13829
|
+
adapter,
|
|
13830
|
+
scheduler,
|
|
13831
|
+
sector,
|
|
13832
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
13833
|
+
},
|
|
13834
|
+
{
|
|
13835
|
+
postMutationArgs,
|
|
13836
|
+
revertTargets: [...constraintPriorsByActivity].filter(
|
|
13837
|
+
([activityId]) => !explicitDateEditActivities.has(activityId)
|
|
13838
|
+
).map(([activityId, priorConstraintDate]) => ({
|
|
13839
|
+
activityId,
|
|
13840
|
+
priorConstraintDate
|
|
13841
|
+
})),
|
|
13842
|
+
revert: (activityId, priorConstraintDate) => revertNoOpConstraintEdit(
|
|
13843
|
+
activityId,
|
|
13844
|
+
priorConstraintDate,
|
|
13845
|
+
adapter,
|
|
13846
|
+
deps.ctx.calendars
|
|
13847
|
+
)
|
|
13848
|
+
}
|
|
13849
|
+
) : runPostMutation(
|
|
13850
|
+
{
|
|
13851
|
+
adapter,
|
|
13852
|
+
scheduler,
|
|
13853
|
+
sector,
|
|
13854
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
13855
|
+
},
|
|
13856
|
+
postMutationArgs
|
|
13857
|
+
));
|
|
13858
|
+
return {
|
|
13859
|
+
ok: true,
|
|
13860
|
+
verdicts,
|
|
13861
|
+
changes: await assembleChangeSet(adapter, {
|
|
13862
|
+
source: action,
|
|
13863
|
+
beforeSnap,
|
|
13864
|
+
touchedIds,
|
|
13865
|
+
scheduledIds,
|
|
13866
|
+
sirDetection: "before-diff",
|
|
13867
|
+
links: linkChanges,
|
|
13868
|
+
trackingEvents,
|
|
13869
|
+
warnings,
|
|
13870
|
+
hoursPerDay: sector.hoursPerDay
|
|
13871
|
+
})
|
|
13872
|
+
};
|
|
13442
13873
|
}
|
|
13443
|
-
function
|
|
13444
|
-
const
|
|
13445
|
-
const
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13874
|
+
function applyOneEdit2(edit, deps) {
|
|
13875
|
+
const { ctx } = deps;
|
|
13876
|
+
const activityId = edit.activityId;
|
|
13877
|
+
const pipeline = findPipelineForColumn(edit.column);
|
|
13878
|
+
if (!pipeline) {
|
|
13879
|
+
return {
|
|
13880
|
+
verdict: {
|
|
13881
|
+
activityId,
|
|
13882
|
+
ok: false,
|
|
13883
|
+
reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN
|
|
13884
|
+
},
|
|
13885
|
+
changes: null
|
|
13886
|
+
};
|
|
13455
13887
|
}
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
}
|
|
13466
|
-
if (!Number.isFinite(entry.progress) || entry.progress < 0 || entry.progress > 100) {
|
|
13467
|
-
throw new Error(
|
|
13468
|
-
`persistence-acknowledge: invalid reconciled progress ${String(entry.progress)} for "${id}"`
|
|
13469
|
-
);
|
|
13470
|
-
}
|
|
13471
|
-
if (!deps.adapter.getActivity(id)) {
|
|
13472
|
-
throw new Error(
|
|
13473
|
-
`persistence-acknowledge: unknown reconciled activity id "${id}"`
|
|
13474
|
-
);
|
|
13475
|
-
}
|
|
13476
|
-
seen.add(id);
|
|
13888
|
+
const activitySnap = ctx.activityReader.getActivity(edit.activityId);
|
|
13889
|
+
if (!activitySnap) {
|
|
13890
|
+
return {
|
|
13891
|
+
verdict: {
|
|
13892
|
+
activityId,
|
|
13893
|
+
ok: false,
|
|
13894
|
+
reason: REJECTION_REASON.ACTIVITY_NOT_FOUND
|
|
13895
|
+
},
|
|
13896
|
+
changes: null
|
|
13897
|
+
};
|
|
13477
13898
|
}
|
|
13478
|
-
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
}
|
|
13899
|
+
const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
|
|
13900
|
+
if (!editGate.allowed) {
|
|
13901
|
+
return {
|
|
13902
|
+
verdict: {
|
|
13903
|
+
activityId,
|
|
13904
|
+
ok: false,
|
|
13905
|
+
reason: editGate.reason ?? REJECTION_REASON.CANNOT_EDIT,
|
|
13906
|
+
...editGate.alertKey ? { alertKey: editGate.alertKey } : {}
|
|
13907
|
+
},
|
|
13908
|
+
changes: null
|
|
13909
|
+
};
|
|
13489
13910
|
}
|
|
13490
|
-
const
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13911
|
+
const parsed2 = pipeline.parseInput(edit.newValue, activitySnap, ctx);
|
|
13912
|
+
if (!parsed2.ok) {
|
|
13913
|
+
return {
|
|
13914
|
+
verdict: {
|
|
13915
|
+
activityId,
|
|
13916
|
+
ok: false,
|
|
13917
|
+
reason: parsed2.reason ?? REJECTION_REASON.PARSE_ERROR
|
|
13918
|
+
},
|
|
13919
|
+
changes: null
|
|
13920
|
+
};
|
|
13495
13921
|
}
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13922
|
+
const oldValue = Reflect.get(activitySnap, pipeline.targetField);
|
|
13923
|
+
const validation = pipeline.validate(
|
|
13924
|
+
activitySnap,
|
|
13925
|
+
oldValue,
|
|
13926
|
+
parsed2.value,
|
|
13927
|
+
ctx
|
|
13928
|
+
);
|
|
13929
|
+
if (!validation.valid) {
|
|
13930
|
+
return {
|
|
13931
|
+
verdict: {
|
|
13932
|
+
activityId,
|
|
13933
|
+
ok: false,
|
|
13934
|
+
reason: validation.reason ?? REJECTION_REASON.INVALID,
|
|
13935
|
+
...validation.notification?.messageKey ? { alertKey: validation.notification.messageKey } : {}
|
|
13936
|
+
},
|
|
13937
|
+
changes: null
|
|
13938
|
+
};
|
|
13939
|
+
}
|
|
13940
|
+
if (edit.column === CUSTOM_ID_COLUMN && parsed2.value !== null) {
|
|
13941
|
+
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
13942
|
+
const oldCustomIdRaw = liveActivity ? liveActivity.customId : null;
|
|
13943
|
+
const oldCustomId = typeof oldCustomIdRaw === "string" ? oldCustomIdRaw : null;
|
|
13944
|
+
if (deps.customIdTracker.isCustomIdInUse(String(parsed2.value), oldCustomId)) {
|
|
13501
13945
|
return {
|
|
13502
|
-
|
|
13503
|
-
|
|
13946
|
+
verdict: {
|
|
13947
|
+
activityId,
|
|
13948
|
+
ok: false,
|
|
13949
|
+
reason: REJECTION_REASON.CUSTOM_ID_DUPLICATE
|
|
13950
|
+
},
|
|
13951
|
+
changes: null
|
|
13504
13952
|
};
|
|
13505
|
-
},
|
|
13506
|
-
activityOwnerByBackendId
|
|
13507
|
-
);
|
|
13508
|
-
assertValidAssignments(
|
|
13509
|
-
links,
|
|
13510
|
-
"link",
|
|
13511
|
-
(id) => {
|
|
13512
|
-
const link = deps.adapter.getLink(id);
|
|
13513
|
-
return { exists: link !== null, current: link?.proplannerId ?? null };
|
|
13514
|
-
},
|
|
13515
|
-
linkOwnerByBackendId
|
|
13516
|
-
);
|
|
13517
|
-
const activityChanges = [];
|
|
13518
|
-
const linkChanges = [];
|
|
13519
|
-
const progressToApply = reconciledProgress.filter((entry) => {
|
|
13520
|
-
const current = deps.adapter.getActivity(String(entry.activityId));
|
|
13521
|
-
return current !== null && current.progress !== entry.progress;
|
|
13522
|
-
});
|
|
13523
|
-
const touchedForProgress = new Set(
|
|
13524
|
-
progressToApply.map((entry) => String(entry.activityId))
|
|
13525
|
-
);
|
|
13526
|
-
const parentsToRollup = /* @__PURE__ */ new Set();
|
|
13527
|
-
for (const entry of progressToApply) {
|
|
13528
|
-
const activity = deps.adapter.getActivity(String(entry.activityId));
|
|
13529
|
-
if (activity && !isRootParent(activity.parentId)) {
|
|
13530
|
-
parentsToRollup.add(String(activity.parentId));
|
|
13531
|
-
touchedForProgress.add(String(activity.parentId));
|
|
13532
13953
|
}
|
|
13533
13954
|
}
|
|
13534
|
-
const
|
|
13535
|
-
|
|
13536
|
-
|
|
13955
|
+
const changes = pipeline.transform(
|
|
13956
|
+
activitySnap,
|
|
13957
|
+
parsed2.value,
|
|
13958
|
+
ctx,
|
|
13959
|
+
parsed2.raw
|
|
13537
13960
|
);
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
13548
|
-
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
|
|
13552
|
-
|
|
13553
|
-
|
|
13554
|
-
|
|
13555
|
-
|
|
13556
|
-
|
|
13557
|
-
|
|
13558
|
-
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
|
|
13564
|
-
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
|
|
13571
|
-
|
|
13572
|
-
|
|
13573
|
-
kind: "updated",
|
|
13574
|
-
fields: {
|
|
13575
|
-
proplannerId: {
|
|
13576
|
-
before: previousProplannerId,
|
|
13577
|
-
after: assignment.proplannerId
|
|
13578
|
-
}
|
|
13579
|
-
},
|
|
13580
|
-
after: { ...before, proplannerId: assignment.proplannerId }
|
|
13581
|
-
});
|
|
13582
|
-
}
|
|
13583
|
-
for (const assignment of links) {
|
|
13584
|
-
const before = deps.adapter.getLink(assignment.id);
|
|
13585
|
-
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
13586
|
-
const previousProplannerId = before.proplannerId;
|
|
13587
|
-
deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
|
|
13588
|
-
linkChanges.push({
|
|
13589
|
-
id: assignment.id,
|
|
13590
|
-
kind: "updated",
|
|
13591
|
-
fields: {
|
|
13592
|
-
proplannerId: {
|
|
13593
|
-
before: previousProplannerId,
|
|
13594
|
-
after: assignment.proplannerId
|
|
13595
|
-
}
|
|
13596
|
-
},
|
|
13597
|
-
after: { ...before, proplannerId: assignment.proplannerId }
|
|
13598
|
-
});
|
|
13599
|
-
}
|
|
13600
|
-
});
|
|
13601
|
-
return {
|
|
13602
|
-
ok: true,
|
|
13603
|
-
changes: {
|
|
13604
|
-
source: action,
|
|
13605
|
-
activities: activityChanges,
|
|
13606
|
-
links: linkChanges,
|
|
13607
|
-
calendars: [],
|
|
13608
|
-
trackingEvents: []
|
|
13961
|
+
return { verdict: { activityId, ok: true }, changes };
|
|
13962
|
+
}
|
|
13963
|
+
function readLiveCustomId(edit, deps) {
|
|
13964
|
+
if (edit.column !== CUSTOM_ID_COLUMN) return null;
|
|
13965
|
+
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
13966
|
+
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
13967
|
+
return typeof rawValue === "string" ? rawValue : null;
|
|
13968
|
+
}
|
|
13969
|
+
function syncCustomIdTracker(edit, oldCustomId, deps) {
|
|
13970
|
+
if (edit.column !== CUSTOM_ID_COLUMN) return;
|
|
13971
|
+
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
13972
|
+
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
13973
|
+
const newCustomId = typeof rawValue === "string" ? rawValue : null;
|
|
13974
|
+
if (oldCustomId !== newCustomId) {
|
|
13975
|
+
deps.customIdTracker.trackCustomIdChange(oldCustomId, newCustomId);
|
|
13976
|
+
}
|
|
13977
|
+
}
|
|
13978
|
+
function recordConstraintPrior(priors, edit, beforeSnap) {
|
|
13979
|
+
if (!isConstraintEditColumn(edit.column)) return;
|
|
13980
|
+
const activityKey = String(edit.activityId);
|
|
13981
|
+
if (priors.has(activityKey)) return;
|
|
13982
|
+
const preBatchSnapshot = beforeSnap.get(activityKey);
|
|
13983
|
+
if (!preBatchSnapshot) return;
|
|
13984
|
+
priors.set(activityKey, preBatchSnapshot.constraintDate ?? null);
|
|
13985
|
+
}
|
|
13986
|
+
function mergeBeforeSnapshots2(beforeSnap, ids, deps) {
|
|
13987
|
+
const missing = /* @__PURE__ */ new Set();
|
|
13988
|
+
for (const candidateId of ids) {
|
|
13989
|
+
if (!beforeSnap.has(candidateId)) missing.add(candidateId);
|
|
13990
|
+
}
|
|
13991
|
+
if (missing.size === 0) return;
|
|
13992
|
+
for (const missingId of missing) {
|
|
13993
|
+
const liveActivity = deps.adapter.getActivity(missingId);
|
|
13994
|
+
if (liveActivity) {
|
|
13995
|
+
beforeSnap.set(missingId, structuredCloneActivity(liveActivity));
|
|
13609
13996
|
}
|
|
13610
|
-
}
|
|
13997
|
+
}
|
|
13611
13998
|
}
|
|
13612
13999
|
|
|
13613
14000
|
// src/baselines/normalize-baseline-point.ts
|
|
@@ -13784,74 +14171,25 @@ function sumRecursiveBaselineDurationDays(activityId, adapter) {
|
|
|
13784
14171
|
sum += sumRecursiveBaselineDurationDays(String(childId), adapter);
|
|
13785
14172
|
} else if (type !== "milestone") {
|
|
13786
14173
|
sum += toNumber(active.durationDays);
|
|
13787
|
-
}
|
|
13788
|
-
}
|
|
13789
|
-
return sum;
|
|
13790
|
-
}
|
|
13791
|
-
function toNumber(v) {
|
|
13792
|
-
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
13793
|
-
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
13794
|
-
}
|
|
13795
|
-
|
|
13796
|
-
// src/propagations/downward/recompute-all-ponderators.ts
|
|
13797
|
-
function recomputeAllPonderators(criterion, adapter) {
|
|
13798
|
-
adapter.forEachActivity((_a, id) => {
|
|
13799
|
-
adapter.setActivityField(id, COLUMN.PONDERATOR, 0);
|
|
13800
|
-
});
|
|
13801
|
-
adapter.forEachActivity((_a, id) => {
|
|
13802
|
-
if (adapter.getChildren(id).length > 0) {
|
|
13803
|
-
recomputePonderatorsForParent(String(id), criterion, adapter);
|
|
13804
|
-
}
|
|
13805
|
-
});
|
|
13806
|
-
}
|
|
13807
|
-
|
|
13808
|
-
// src/propagations/upward/progress-rollup.ts
|
|
13809
|
-
function recomputeAllProgressRollup(adapter) {
|
|
13810
|
-
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
13811
|
-
rollupParent(parentId, adapter);
|
|
13812
|
-
}
|
|
13813
|
-
}
|
|
13814
|
-
function parentsDeepestFirst(adapter) {
|
|
13815
|
-
const parents = /* @__PURE__ */ new Set();
|
|
13816
|
-
adapter.forEachActivity((_activity, id) => {
|
|
13817
|
-
if (adapter.getChildren(id).length > 0) parents.add(id);
|
|
13818
|
-
});
|
|
13819
|
-
const depthById = /* @__PURE__ */ new Map();
|
|
13820
|
-
for (const id of parents) depthById.set(id, depthOf2(id, adapter));
|
|
13821
|
-
return [...parents].sort(
|
|
13822
|
-
(a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
|
|
13823
|
-
);
|
|
13824
|
-
}
|
|
13825
|
-
function depthOf2(id, adapter) {
|
|
13826
|
-
let depth = 0;
|
|
13827
|
-
let current = id;
|
|
13828
|
-
while (current && current !== "0") {
|
|
13829
|
-
if (!adapter.getActivity(current)) break;
|
|
13830
|
-
depth += 1;
|
|
13831
|
-
current = adapter.getParentId(current);
|
|
13832
|
-
}
|
|
13833
|
-
return depth;
|
|
13834
|
-
}
|
|
13835
|
-
function rollupParent(parentId, adapter) {
|
|
13836
|
-
const childIds = adapter.getChildren(parentId);
|
|
13837
|
-
if (childIds.length === 0) return;
|
|
13838
|
-
const children = [];
|
|
13839
|
-
for (const childId of childIds) {
|
|
13840
|
-
const child = adapter.getActivity(childId);
|
|
13841
|
-
if (!child) continue;
|
|
13842
|
-
children.push({
|
|
13843
|
-
progress: Number(child.progress ?? 0),
|
|
13844
|
-
ponderator: Number(child.ponderator ?? 0)
|
|
13845
|
-
});
|
|
13846
|
-
}
|
|
13847
|
-
const rollup = computeWeightedProgressRollup(children);
|
|
13848
|
-
if (rollup !== null) {
|
|
13849
|
-
adapter.setActivityField(
|
|
13850
|
-
parentId,
|
|
13851
|
-
COLUMN.PROGRESS,
|
|
13852
|
-
roundProgressPerLevel(rollup)
|
|
13853
|
-
);
|
|
14174
|
+
}
|
|
13854
14175
|
}
|
|
14176
|
+
return sum;
|
|
14177
|
+
}
|
|
14178
|
+
function toNumber(v) {
|
|
14179
|
+
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
14180
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
14181
|
+
}
|
|
14182
|
+
|
|
14183
|
+
// src/propagations/downward/recompute-all-ponderators.ts
|
|
14184
|
+
function recomputeAllPonderators(criterion, adapter) {
|
|
14185
|
+
adapter.forEachActivity((_a, id) => {
|
|
14186
|
+
adapter.setActivityField(id, COLUMN.PONDERATOR, 0);
|
|
14187
|
+
});
|
|
14188
|
+
adapter.forEachActivity((_a, id) => {
|
|
14189
|
+
if (adapter.getChildren(id).length > 0) {
|
|
14190
|
+
recomputePonderatorsForParent(String(id), criterion, adapter);
|
|
14191
|
+
}
|
|
14192
|
+
});
|
|
13855
14193
|
}
|
|
13856
14194
|
|
|
13857
14195
|
// src/dispatch/shared/baseline-weighted-fields.ts
|
|
@@ -14243,11 +14581,8 @@ async function performDispatch(action, options, deps) {
|
|
|
14243
14581
|
if (action.kind === "sir-sync") {
|
|
14244
14582
|
return dispatchSirSync(action, deps);
|
|
14245
14583
|
}
|
|
14246
|
-
if (action.kind === "sir-approve") {
|
|
14247
|
-
return dispatchSirApprove(action, options, deps);
|
|
14248
|
-
}
|
|
14249
14584
|
if (action.kind === "activity-lookahead-sync") {
|
|
14250
|
-
return dispatchActivityLookaheadSync(action, deps);
|
|
14585
|
+
return dispatchActivityLookaheadSync(action, options, deps);
|
|
14251
14586
|
}
|
|
14252
14587
|
if (action.kind === "inline-edit" && (action.column === COLUMN.CUSTOM_PREDECESSORS || action.column === COLUMN.CUSTOM_SUCESSORS)) {
|
|
14253
14588
|
return dispatchInlineEditLinks(action, options, deps);
|
|
@@ -17397,7 +17732,7 @@ var ViewStateStore = class {
|
|
|
17397
17732
|
};
|
|
17398
17733
|
|
|
17399
17734
|
// src/internal/state/schedule-state.ts
|
|
17400
|
-
var ScheduleState = class {
|
|
17735
|
+
var ScheduleState = class _ScheduleState {
|
|
17401
17736
|
_activities = /* @__PURE__ */ new Map();
|
|
17402
17737
|
_lastStartDate = /* @__PURE__ */ new Map();
|
|
17403
17738
|
_promotionSnapshot = /* @__PURE__ */ new Map();
|
|
@@ -17419,22 +17754,52 @@ var ScheduleState = class {
|
|
|
17419
17754
|
_promotionSnapshotBefore = null;
|
|
17420
17755
|
_calendar;
|
|
17421
17756
|
_flags;
|
|
17757
|
+
_calendarSnapshot;
|
|
17422
17758
|
constructor(snapshot, buildOrderComparator = () => null) {
|
|
17423
17759
|
this._buildOrderComparator = buildOrderComparator;
|
|
17760
|
+
this._calendarSnapshot = cloneDomainValue(snapshot.calendars);
|
|
17424
17761
|
for (const activity of snapshot.activities) {
|
|
17425
17762
|
this._activities.set(activity.id, activity);
|
|
17426
17763
|
}
|
|
17427
17764
|
for (const link of snapshot.links) {
|
|
17428
|
-
|
|
17429
|
-
|
|
17765
|
+
const normalized2 = {
|
|
17766
|
+
...link,
|
|
17767
|
+
freeSlackHours: link.freeSlackHours ?? null,
|
|
17768
|
+
isCritical: link.isCritical === true
|
|
17769
|
+
};
|
|
17770
|
+
this._links.set(String(link.id), normalized2);
|
|
17771
|
+
this._indexLink(normalized2);
|
|
17430
17772
|
}
|
|
17431
17773
|
this._calendar = new CalendarCalculator(
|
|
17432
|
-
createCalendarSetup(
|
|
17774
|
+
createCalendarSetup(this._calendarSnapshot)
|
|
17433
17775
|
);
|
|
17434
17776
|
this._flags = buildFlags(snapshot);
|
|
17435
17777
|
this._hierarchy = new HierarchyIndex(this._activities);
|
|
17436
17778
|
this._hierarchy.rebuild();
|
|
17437
17779
|
}
|
|
17780
|
+
fork() {
|
|
17781
|
+
const fork = new _ScheduleState(
|
|
17782
|
+
{
|
|
17783
|
+
activities: this.getAllActivities().map(cloneCoreActivity),
|
|
17784
|
+
links: this.getAllLinks().map((link) => ({ ...link })),
|
|
17785
|
+
calendars: cloneDomainValue(this._calendarSnapshot)
|
|
17786
|
+
},
|
|
17787
|
+
this._buildOrderComparator
|
|
17788
|
+
);
|
|
17789
|
+
fork._viewState.restore(cloneDomainValue(this._viewState.snapshot()));
|
|
17790
|
+
fork._lastStartDate = new Map(
|
|
17791
|
+
Array.from(this._lastStartDate, ([id, date2]) => [id, new Date(date2)])
|
|
17792
|
+
);
|
|
17793
|
+
fork._promotionSnapshot = new Map(
|
|
17794
|
+
Array.from(this._promotionSnapshot, ([id, snapshot]) => [
|
|
17795
|
+
id,
|
|
17796
|
+
cloneDomainValue(snapshot)
|
|
17797
|
+
])
|
|
17798
|
+
);
|
|
17799
|
+
fork._flags = cloneDomainValue(this._flags);
|
|
17800
|
+
fork._filterProjection = cloneDomainValue(this._filterProjection);
|
|
17801
|
+
return fork;
|
|
17802
|
+
}
|
|
17438
17803
|
getAllActivities() {
|
|
17439
17804
|
return Array.from(this._activities.values());
|
|
17440
17805
|
}
|
|
@@ -17693,8 +18058,13 @@ var ScheduleState = class {
|
|
|
17693
18058
|
this._writeCapture.noteLinkAdd(String(link.id));
|
|
17694
18059
|
const superseded = this._links.get(String(link.id));
|
|
17695
18060
|
if (superseded) this._deindexLink(superseded);
|
|
17696
|
-
|
|
17697
|
-
|
|
18061
|
+
const normalized2 = {
|
|
18062
|
+
...link,
|
|
18063
|
+
freeSlackHours: link.freeSlackHours ?? null,
|
|
18064
|
+
isCritical: link.isCritical === true
|
|
18065
|
+
};
|
|
18066
|
+
this._links.set(String(link.id), normalized2);
|
|
18067
|
+
this._indexLink(normalized2);
|
|
17698
18068
|
}
|
|
17699
18069
|
_indexLink(link) {
|
|
17700
18070
|
pushLinkRef(this._outgoing, String(link.source), link.id);
|
|
@@ -17847,7 +18217,7 @@ var ScheduleState = class {
|
|
|
17847
18217
|
};
|
|
17848
18218
|
var EMPTY_LINK_IDS = [];
|
|
17849
18219
|
function isMutableLinkField(field) {
|
|
17850
|
-
return field === LINK_PROPERTY.LAG || field === LINK_PROPERTY.TYPE;
|
|
18220
|
+
return field === LINK_PROPERTY.LAG || field === LINK_PROPERTY.TYPE || field === LINK_PROPERTY.FREE_SLACK_HOURS || field === LINK_PROPERTY.IS_CRITICAL;
|
|
17851
18221
|
}
|
|
17852
18222
|
function fieldAffectsVisualOrder(field) {
|
|
17853
18223
|
return field === ACTIVITY_PROPERTY.PARENT || field === ACTIVITY_PROPERTY.CORRELATIVE_ID;
|
|
@@ -17968,12 +18338,7 @@ function buildPipelineContext(adapter, options) {
|
|
|
17968
18338
|
activityReader: createStateBackedActivityReader(adapter),
|
|
17969
18339
|
dateFormat: options.dateFormat ?? "YYYY-MM-DD",
|
|
17970
18340
|
customHours: options.customHours ?? {},
|
|
17971
|
-
hoursPerDay: options.hoursPerDay
|
|
17972
|
-
// ISSUE-057 (CP6): sin default. Un comando de duración sin inputUnit se
|
|
17973
|
-
// rechaza en el pipeline; el resto de columnas no lo leen. La clave se
|
|
17974
|
-
// OMITE cuando no se declaró (exactOptionalPropertyTypes), no se pone a
|
|
17975
|
-
// undefined.
|
|
17976
|
-
...options.inputUnit !== void 0 ? { inputUnit: options.inputUnit } : {}
|
|
18341
|
+
hoursPerDay: options.hoursPerDay
|
|
17977
18342
|
};
|
|
17978
18343
|
}
|
|
17979
18344
|
|
|
@@ -17983,15 +18348,26 @@ function willRunCriticalPath(action) {
|
|
|
17983
18348
|
}
|
|
17984
18349
|
|
|
17985
18350
|
// src/dispatch/undo/undo-entry.ts
|
|
17986
|
-
|
|
17987
|
-
|
|
18351
|
+
var DERIVED_LINK_FIELDS = /* @__PURE__ */ new Set(["freeSlackHours", "isCritical"]);
|
|
18352
|
+
function withoutDerivedFields(entry, derivedFields) {
|
|
18353
|
+
const fields = Object.fromEntries(
|
|
18354
|
+
Object.entries(entry.fields ?? {}).filter(
|
|
18355
|
+
([field]) => !derivedFields.has(field)
|
|
18356
|
+
)
|
|
18357
|
+
);
|
|
18358
|
+
if (entry.kind === "updated" && Object.keys(fields).length === 0) return null;
|
|
18359
|
+
return {
|
|
17988
18360
|
...entry,
|
|
18361
|
+
...entry.fields === void 0 ? {} : { fields },
|
|
17989
18362
|
after: null
|
|
17990
|
-
}
|
|
17991
|
-
|
|
18363
|
+
};
|
|
18364
|
+
}
|
|
18365
|
+
function buildHistoryChangeSet(changes) {
|
|
18366
|
+
const activitiesWithoutAfter = changes.activities.map((entry) => ({
|
|
17992
18367
|
...entry,
|
|
17993
18368
|
after: null
|
|
17994
18369
|
}));
|
|
18370
|
+
const linksWithoutAfter = changes.links.map((entry) => withoutDerivedFields(entry, DERIVED_LINK_FIELDS)).filter((entry) => entry !== null);
|
|
17995
18371
|
const source = changes.source.kind === "activity-batch" ? { ...changes.source, items: [], links: [] } : changes.source;
|
|
17996
18372
|
return cloneDomainValue({
|
|
17997
18373
|
...changes,
|
|
@@ -18001,7 +18377,22 @@ function buildHistoryChangeSet(changes) {
|
|
|
18001
18377
|
});
|
|
18002
18378
|
}
|
|
18003
18379
|
function buildUndoEntry(changeSet, beforeSnap, beforeLinks, afterSnap, afterLinks) {
|
|
18004
|
-
return {
|
|
18380
|
+
return {
|
|
18381
|
+
changeSet,
|
|
18382
|
+
beforeSnap,
|
|
18383
|
+
beforeLinks: resetDerivedLinkSnapshots(beforeLinks),
|
|
18384
|
+
afterSnap,
|
|
18385
|
+
afterLinks: resetDerivedLinkSnapshots(afterLinks)
|
|
18386
|
+
};
|
|
18387
|
+
}
|
|
18388
|
+
function resetDerivedLinkSnapshots(snapshots) {
|
|
18389
|
+
if (!snapshots) return void 0;
|
|
18390
|
+
return new Map(
|
|
18391
|
+
Array.from(snapshots, ([id, link]) => [
|
|
18392
|
+
id,
|
|
18393
|
+
{ ...link, freeSlackHours: null, isCritical: false }
|
|
18394
|
+
])
|
|
18395
|
+
);
|
|
18005
18396
|
}
|
|
18006
18397
|
function captureCreatedSnapshots(state, changeSet) {
|
|
18007
18398
|
const afterSnap = /* @__PURE__ */ new Map();
|
|
@@ -18053,8 +18444,7 @@ function mergeCoalesced(top, next) {
|
|
|
18053
18444
|
};
|
|
18054
18445
|
return buildUndoEntry(changeSet);
|
|
18055
18446
|
}
|
|
18056
|
-
function getDispatchHistoryPolicy(action
|
|
18057
|
-
if (options?.preview === true) return "skip";
|
|
18447
|
+
function getDispatchHistoryPolicy(action) {
|
|
18058
18448
|
if (action.kind === "persistence-acknowledge") return "clear-on-success";
|
|
18059
18449
|
if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "activity-clipboard-clear" || action.kind === "activity-copy" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
|
|
18060
18450
|
return "skip";
|
|
@@ -18300,7 +18690,7 @@ function applyChangeSetSide(state, entry, side) {
|
|
|
18300
18690
|
// src/dispatch/undo/undo-recorder.ts
|
|
18301
18691
|
var DEFAULT_MAX_DEPTH = 100;
|
|
18302
18692
|
var COALESCE_WINDOW_MS = 600;
|
|
18303
|
-
var UndoRecorder = class {
|
|
18693
|
+
var UndoRecorder = class _UndoRecorder {
|
|
18304
18694
|
constructor(maxDepth = DEFAULT_MAX_DEPTH) {
|
|
18305
18695
|
this.maxDepth = maxDepth;
|
|
18306
18696
|
}
|
|
@@ -18309,6 +18699,14 @@ var UndoRecorder = class {
|
|
|
18309
18699
|
redoStack = [];
|
|
18310
18700
|
_lastKey = null;
|
|
18311
18701
|
_lastTime = 0;
|
|
18702
|
+
fork() {
|
|
18703
|
+
const fork = new _UndoRecorder(this.maxDepth);
|
|
18704
|
+
fork.undoStack.push(...cloneDomainValue(this.undoStack));
|
|
18705
|
+
fork.redoStack.push(...cloneDomainValue(this.redoStack));
|
|
18706
|
+
fork._lastKey = this._lastKey;
|
|
18707
|
+
fork._lastTime = this._lastTime;
|
|
18708
|
+
return fork;
|
|
18709
|
+
}
|
|
18312
18710
|
record(entry, coalesceKey = null, now = 0) {
|
|
18313
18711
|
const top = this.undoStack[this.undoStack.length - 1];
|
|
18314
18712
|
if (coalesceKey != null && coalesceKey === this._lastKey && now - this._lastTime <= COALESCE_WINDOW_MS && top !== void 0 && canCoalesce(top, entry)) {
|
|
@@ -18432,40 +18830,56 @@ function buildStateSnapshot(activities, links, calendars, baseCalendars = []) {
|
|
|
18432
18830
|
}
|
|
18433
18831
|
|
|
18434
18832
|
// src/boundary/save/activity-changes.ts
|
|
18435
|
-
var
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
18447
|
-
|
|
18448
|
-
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
"
|
|
18465
|
-
"
|
|
18466
|
-
"
|
|
18467
|
-
"
|
|
18468
|
-
|
|
18833
|
+
var ACTIVITY_SAVE_FIELD_POLICY = {
|
|
18834
|
+
id: "ignored",
|
|
18835
|
+
proplannerId: "ignored",
|
|
18836
|
+
parentId: "tracked",
|
|
18837
|
+
name: "tracked",
|
|
18838
|
+
description: "tracked",
|
|
18839
|
+
type: "tracked",
|
|
18840
|
+
startDate: "tracked",
|
|
18841
|
+
endDate: "tracked",
|
|
18842
|
+
durationHours: "tracked",
|
|
18843
|
+
constraintType: "tracked",
|
|
18844
|
+
constraintDate: "tracked",
|
|
18845
|
+
autoScheduling: "tracked",
|
|
18846
|
+
calendarId: "tracked",
|
|
18847
|
+
assignedDefaultCalendar: "ignored",
|
|
18848
|
+
dateOrigin: "tracked",
|
|
18849
|
+
progress: "tracked",
|
|
18850
|
+
cost: "tracked",
|
|
18851
|
+
usedCost: "tracked",
|
|
18852
|
+
realCost: "tracked",
|
|
18853
|
+
workHours: "tracked",
|
|
18854
|
+
realWorkHours: "tracked",
|
|
18855
|
+
ponderator: "tracked",
|
|
18856
|
+
hasCustomPonderator: "tracked",
|
|
18857
|
+
correlativeId: "tracked",
|
|
18858
|
+
uniqueCorrelativeId: "tracked",
|
|
18859
|
+
customId: "tracked",
|
|
18860
|
+
subcontractId: "tracked",
|
|
18861
|
+
isLookahead: "tracked",
|
|
18862
|
+
hasLookaheadTasks: "ignored",
|
|
18863
|
+
newActivityIds: "tracked",
|
|
18864
|
+
hasNewActivities: "tracked",
|
|
18865
|
+
isNewActivity: "ignored",
|
|
18866
|
+
pendingRequestIds: "ignored",
|
|
18867
|
+
responsableIds: "tracked",
|
|
18868
|
+
tagIds: "tracked",
|
|
18869
|
+
baselinePoints: "ignored",
|
|
18870
|
+
baselineSnapshot: "ignored",
|
|
18871
|
+
recursiveDurationHours: "ignored",
|
|
18872
|
+
isCritical: "ignored",
|
|
18873
|
+
freeSlackHours: "ignored",
|
|
18874
|
+
calendarDuration: "ignored",
|
|
18875
|
+
expectedProgress: "ignored",
|
|
18876
|
+
expectedProgressBaseline: "ignored",
|
|
18877
|
+
status: "ignored",
|
|
18878
|
+
criticalPath: "ignored"
|
|
18879
|
+
};
|
|
18880
|
+
var PERSISTABLE_ACTIVITY_FIELDS = Object.keys(ACTIVITY_SAVE_FIELD_POLICY).filter((field) => {
|
|
18881
|
+
return ACTIVITY_SAVE_FIELD_POLICY[field] === "tracked";
|
|
18882
|
+
});
|
|
18469
18883
|
var DATE_FIELDS2 = /* @__PURE__ */ new Set([
|
|
18470
18884
|
"startDate",
|
|
18471
18885
|
"endDate",
|
|
@@ -18493,7 +18907,6 @@ function snapshotFieldValue(field, value) {
|
|
|
18493
18907
|
return value;
|
|
18494
18908
|
}
|
|
18495
18909
|
function activityChangedSince(current, saved) {
|
|
18496
|
-
if (current.assignedDefaultCalendar) return true;
|
|
18497
18910
|
for (const field of PERSISTABLE_ACTIVITY_FIELDS) {
|
|
18498
18911
|
if (!fieldEquals(field, current[field], saved[field])) return true;
|
|
18499
18912
|
}
|
|
@@ -18511,15 +18924,35 @@ function dateEquals(current, saved) {
|
|
|
18511
18924
|
return currentTime === savedTime;
|
|
18512
18925
|
}
|
|
18513
18926
|
function idArrayEquals(current, saved) {
|
|
18514
|
-
const currentIds = Array.isArray(current) ?
|
|
18515
|
-
const savedIds = Array.isArray(saved) ?
|
|
18516
|
-
if (currentIds.
|
|
18517
|
-
|
|
18927
|
+
const currentIds = new Set(Array.isArray(current) ? current.map(String) : []);
|
|
18928
|
+
const savedIds = new Set(Array.isArray(saved) ? saved.map(String) : []);
|
|
18929
|
+
if (currentIds.size !== savedIds.size) return false;
|
|
18930
|
+
for (const currentId of currentIds) {
|
|
18931
|
+
if (!savedIds.has(currentId)) return false;
|
|
18932
|
+
}
|
|
18933
|
+
return true;
|
|
18518
18934
|
}
|
|
18519
18935
|
|
|
18520
18936
|
// src/boundary/save/link-changes.ts
|
|
18937
|
+
var LINK_SAVE_FIELD_POLICY = {
|
|
18938
|
+
id: "ignored",
|
|
18939
|
+
source: "tracked",
|
|
18940
|
+
target: "tracked",
|
|
18941
|
+
type: "tracked",
|
|
18942
|
+
lag: "tracked",
|
|
18943
|
+
freeSlackHours: "ignored",
|
|
18944
|
+
isCritical: "ignored",
|
|
18945
|
+
ganttId: "ignored",
|
|
18946
|
+
sectorId: "ignored",
|
|
18947
|
+
proplannerId: "ignored"
|
|
18948
|
+
};
|
|
18949
|
+
var TRACKED_LINK_FIELDS = Object.keys(LINK_SAVE_FIELD_POLICY).filter((field) => {
|
|
18950
|
+
return LINK_SAVE_FIELD_POLICY[field] === "tracked";
|
|
18951
|
+
});
|
|
18521
18952
|
function linkChangedSince(baseline, current) {
|
|
18522
|
-
return
|
|
18953
|
+
return TRACKED_LINK_FIELDS.some(
|
|
18954
|
+
(field) => baseline[field] !== current[field]
|
|
18955
|
+
);
|
|
18523
18956
|
}
|
|
18524
18957
|
function checkNoUpdatedLinks(baseline, current) {
|
|
18525
18958
|
const baselineById = new Map(
|
|
@@ -18533,23 +18966,73 @@ function checkNoUpdatedLinks(baseline, current) {
|
|
|
18533
18966
|
}
|
|
18534
18967
|
|
|
18535
18968
|
// src/boundary/save/save-tracker.ts
|
|
18536
|
-
|
|
18969
|
+
function hasPersistedIdentity(proplannerId) {
|
|
18970
|
+
const isNewIdentity = proplannerId == null || proplannerId === 0;
|
|
18971
|
+
if (isNewIdentity) return false;
|
|
18972
|
+
const isValidPersistedIdentity = typeof proplannerId === "number" && Number.isSafeInteger(proplannerId) && proplannerId > 0;
|
|
18973
|
+
if (!isValidPersistedIdentity) {
|
|
18974
|
+
throw new Error(`invalid proplannerId ${String(proplannerId)}`);
|
|
18975
|
+
}
|
|
18976
|
+
return true;
|
|
18977
|
+
}
|
|
18978
|
+
function assertValidPersistedIdentities(activities, links) {
|
|
18979
|
+
for (const activity of activities) {
|
|
18980
|
+
hasPersistedIdentity(activity.proplannerId);
|
|
18981
|
+
}
|
|
18982
|
+
for (const link of links) {
|
|
18983
|
+
hasPersistedIdentity(link.proplannerId);
|
|
18984
|
+
}
|
|
18985
|
+
}
|
|
18986
|
+
var SaveTracker = class _SaveTracker {
|
|
18987
|
+
trackingEnabled;
|
|
18537
18988
|
initialized = false;
|
|
18538
18989
|
dirtyActivityIds = /* @__PURE__ */ new Set();
|
|
18539
18990
|
dirtyLinkIds = /* @__PURE__ */ new Set();
|
|
18991
|
+
initialCalendarRepairIds = /* @__PURE__ */ new Set();
|
|
18540
18992
|
linksAtLastSave = /* @__PURE__ */ new Map();
|
|
18541
18993
|
activitiesAtLastSave = /* @__PURE__ */ new Map();
|
|
18994
|
+
constructor(trackingEnabled = true) {
|
|
18995
|
+
this.trackingEnabled = trackingEnabled;
|
|
18996
|
+
}
|
|
18997
|
+
fork() {
|
|
18998
|
+
const fork = new _SaveTracker(false);
|
|
18999
|
+
fork.initialized = this.initialized;
|
|
19000
|
+
for (const [linkId, link] of this.linksAtLastSave) {
|
|
19001
|
+
fork.linksAtLastSave.set(linkId, cloneDomainValue(link));
|
|
19002
|
+
}
|
|
19003
|
+
for (const [activityId, activity] of this.activitiesAtLastSave) {
|
|
19004
|
+
fork.activitiesAtLastSave.set(activityId, cloneDomainValue(activity));
|
|
19005
|
+
}
|
|
19006
|
+
return fork;
|
|
19007
|
+
}
|
|
18542
19008
|
init(activities, links) {
|
|
19009
|
+
if (!this.trackingEnabled) return;
|
|
19010
|
+
assertValidPersistedIdentities(activities, links);
|
|
18543
19011
|
this.snapshotActivities(activities);
|
|
18544
19012
|
this.snapshotLinks(links);
|
|
19013
|
+
for (const activity of activities) {
|
|
19014
|
+
if (activity.assignedDefaultCalendar) {
|
|
19015
|
+
const activityId = String(activity.id);
|
|
19016
|
+
this.initialCalendarRepairIds.add(activityId);
|
|
19017
|
+
this.dirtyActivityIds.add(activityId);
|
|
19018
|
+
}
|
|
19019
|
+
}
|
|
18545
19020
|
this.initialized = true;
|
|
18546
19021
|
}
|
|
19022
|
+
acknowledge(activities, links) {
|
|
19023
|
+
if (!this.trackingEnabled) return;
|
|
19024
|
+
assertValidPersistedIdentities(activities, links);
|
|
19025
|
+
this.snapshotActivities(activities);
|
|
19026
|
+
this.snapshotLinks(links);
|
|
19027
|
+
}
|
|
18547
19028
|
snapshotActivities(activities) {
|
|
19029
|
+
if (!this.trackingEnabled) return;
|
|
18548
19030
|
this.activitiesAtLastSave.clear();
|
|
18549
19031
|
this.dirtyActivityIds.clear();
|
|
19032
|
+
this.initialCalendarRepairIds.clear();
|
|
18550
19033
|
for (const activity of activities) {
|
|
18551
19034
|
const activityId = String(activity.id);
|
|
18552
|
-
if (activity.proplannerId
|
|
19035
|
+
if (!hasPersistedIdentity(activity.proplannerId)) {
|
|
18553
19036
|
this.dirtyActivityIds.add(activityId);
|
|
18554
19037
|
continue;
|
|
18555
19038
|
}
|
|
@@ -18569,11 +19052,12 @@ var SaveTracker = class {
|
|
|
18569
19052
|
}
|
|
18570
19053
|
}
|
|
18571
19054
|
snapshotLinks(links) {
|
|
19055
|
+
if (!this.trackingEnabled) return;
|
|
18572
19056
|
this.linksAtLastSave.clear();
|
|
18573
19057
|
this.dirtyLinkIds.clear();
|
|
18574
19058
|
for (const link of links) {
|
|
18575
19059
|
const linkId = String(link.id);
|
|
18576
|
-
if (link.proplannerId
|
|
19060
|
+
if (!hasPersistedIdentity(link.proplannerId)) {
|
|
18577
19061
|
this.dirtyLinkIds.add(linkId);
|
|
18578
19062
|
continue;
|
|
18579
19063
|
}
|
|
@@ -18588,12 +19072,11 @@ var SaveTracker = class {
|
|
|
18588
19072
|
}
|
|
18589
19073
|
}
|
|
18590
19074
|
recordChanges(activities, links, getActivity, getLink) {
|
|
18591
|
-
if (!this.initialized) return;
|
|
19075
|
+
if (!this.trackingEnabled || !this.initialized) return;
|
|
18592
19076
|
for (const change of activities) {
|
|
18593
19077
|
const activityId = String(change.id);
|
|
18594
19078
|
const current = getActivity(activityId);
|
|
18595
|
-
|
|
18596
|
-
if (current === null ? saved !== void 0 : current.proplannerId == null || saved === void 0 || activityChangedSince(current, saved.persistable)) {
|
|
19079
|
+
if (this.activityIsDirty(activityId, current)) {
|
|
18597
19080
|
this.dirtyActivityIds.add(activityId);
|
|
18598
19081
|
} else {
|
|
18599
19082
|
this.dirtyActivityIds.delete(activityId);
|
|
@@ -18602,81 +19085,147 @@ var SaveTracker = class {
|
|
|
18602
19085
|
for (const change of links) {
|
|
18603
19086
|
const linkId = String(change.id);
|
|
18604
19087
|
const current = getLink(linkId);
|
|
18605
|
-
|
|
18606
|
-
if (current === null ? saved !== void 0 : current.proplannerId == null || saved === void 0 || linkChangedSince(saved, current)) {
|
|
19088
|
+
if (this.linkIsDirty(linkId, current)) {
|
|
18607
19089
|
this.dirtyLinkIds.add(linkId);
|
|
18608
19090
|
} else {
|
|
18609
19091
|
this.dirtyLinkIds.delete(linkId);
|
|
18610
19092
|
}
|
|
18611
19093
|
}
|
|
18612
19094
|
}
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
19095
|
+
activityIsDirty(activityId, current) {
|
|
19096
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19097
|
+
if (current === null) return saved !== void 0;
|
|
19098
|
+
return this.initialCalendarRepairIds.has(activityId) || !hasPersistedIdentity(current.proplannerId) || saved === void 0 || activityChangedSince(current, saved.persistable);
|
|
19099
|
+
}
|
|
19100
|
+
linkIsDirty(linkId, current) {
|
|
19101
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19102
|
+
if (current === null) return saved !== void 0;
|
|
19103
|
+
return !hasPersistedIdentity(current.proplannerId) || saved === void 0 || linkChangedSince(saved, current);
|
|
19104
|
+
}
|
|
19105
|
+
acknowledgePersistedLookahead(changes, getActivity) {
|
|
19106
|
+
if (!this.trackingEnabled || !this.initialized) return;
|
|
19107
|
+
const acknowledgements = changes.map((change) => {
|
|
19108
|
+
const activityId = typeof change === "string" ? change : String(change.id);
|
|
18616
19109
|
const current = getActivity(activityId);
|
|
18617
19110
|
const saved = this.activitiesAtLastSave.get(activityId);
|
|
18618
|
-
|
|
19111
|
+
const isUnknownPersistedActivity = current === null || saved === void 0;
|
|
19112
|
+
if (isUnknownPersistedActivity) {
|
|
19113
|
+
throw new Error(`unknown persisted activity "${activityId}"`);
|
|
19114
|
+
}
|
|
19115
|
+
return {
|
|
19116
|
+
activityId,
|
|
19117
|
+
acknowledgesLookahead: typeof change === "string" || Boolean(change.fields?.isLookahead),
|
|
19118
|
+
acknowledgesProgress: typeof change !== "string" && Boolean(change.fields?.progress),
|
|
19119
|
+
current,
|
|
19120
|
+
saved
|
|
19121
|
+
};
|
|
19122
|
+
});
|
|
19123
|
+
for (const {
|
|
19124
|
+
activityId,
|
|
19125
|
+
acknowledgesLookahead,
|
|
19126
|
+
acknowledgesProgress,
|
|
19127
|
+
current,
|
|
19128
|
+
saved
|
|
19129
|
+
} of acknowledgements) {
|
|
18619
19130
|
const persistable = {
|
|
18620
19131
|
...saved.persistable,
|
|
18621
|
-
isLookahead: current.isLookahead
|
|
19132
|
+
...acknowledgesLookahead ? { isLookahead: current.isLookahead } : {},
|
|
19133
|
+
...acknowledgesProgress ? { progress: current.progress } : {}
|
|
18622
19134
|
};
|
|
18623
19135
|
this.activitiesAtLastSave.set(activityId, {
|
|
18624
19136
|
...saved,
|
|
18625
19137
|
persistable
|
|
18626
19138
|
});
|
|
18627
|
-
if (current.proplannerId
|
|
19139
|
+
if (this.initialCalendarRepairIds.has(activityId) || !hasPersistedIdentity(current.proplannerId) || activityChangedSince(current, persistable)) {
|
|
18628
19140
|
this.dirtyActivityIds.add(activityId);
|
|
18629
19141
|
} else {
|
|
18630
19142
|
this.dirtyActivityIds.delete(activityId);
|
|
18631
19143
|
}
|
|
18632
19144
|
}
|
|
18633
19145
|
}
|
|
19146
|
+
hasCheckpointActivityId(activityId) {
|
|
19147
|
+
return this.activitiesAtLastSave.has(activityId);
|
|
19148
|
+
}
|
|
19149
|
+
hasCheckpointLinkId(linkId) {
|
|
19150
|
+
return this.linksAtLastSave.has(linkId);
|
|
19151
|
+
}
|
|
18634
19152
|
hasUnsavedChanges() {
|
|
18635
|
-
return this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0);
|
|
19153
|
+
return this.trackingEnabled && this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0);
|
|
19154
|
+
}
|
|
19155
|
+
newActivities(getActivity) {
|
|
19156
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19157
|
+
const activities = [];
|
|
19158
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19159
|
+
const activity = getActivity(activityId);
|
|
19160
|
+
const isNewActivity = activity !== null && !hasPersistedIdentity(activity.proplannerId);
|
|
19161
|
+
if (isNewActivity) activities.push(activity);
|
|
19162
|
+
}
|
|
19163
|
+
return activities;
|
|
19164
|
+
}
|
|
19165
|
+
newLinks(getLink) {
|
|
19166
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19167
|
+
const links = [];
|
|
19168
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19169
|
+
const link = getLink(linkId);
|
|
19170
|
+
const isNewLink = link !== null && !hasPersistedIdentity(link.proplannerId);
|
|
19171
|
+
if (isNewLink) links.push(link);
|
|
19172
|
+
}
|
|
19173
|
+
return links;
|
|
19174
|
+
}
|
|
19175
|
+
deletedActivities(getActivity) {
|
|
19176
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19177
|
+
const deleted = [];
|
|
19178
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19179
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19180
|
+
if (saved && getActivity(activityId) === null) {
|
|
19181
|
+
deleted.push({ ...saved.deletedSnapshot });
|
|
19182
|
+
}
|
|
19183
|
+
}
|
|
19184
|
+
return deleted;
|
|
18636
19185
|
}
|
|
18637
|
-
|
|
18638
|
-
if (!this.initialized) return [];
|
|
18639
|
-
const currentIds = new Set(
|
|
18640
|
-
activities.map((activity) => String(activity.id))
|
|
18641
|
-
);
|
|
19186
|
+
deletedLinks(getLink) {
|
|
19187
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
18642
19188
|
const deleted = [];
|
|
18643
|
-
for (const
|
|
18644
|
-
|
|
18645
|
-
deleted.push({ ...saved
|
|
19189
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19190
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19191
|
+
if (saved && getLink(linkId) === null) deleted.push({ ...saved });
|
|
18646
19192
|
}
|
|
18647
19193
|
return deleted;
|
|
18648
19194
|
}
|
|
18649
|
-
|
|
18650
|
-
if (!this.initialized) return [];
|
|
18651
|
-
const
|
|
18652
|
-
|
|
18653
|
-
|
|
18654
|
-
(
|
|
18655
|
-
|
|
18656
|
-
|
|
18657
|
-
|
|
18658
|
-
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
|
|
18662
|
-
|
|
18663
|
-
if (!saved) return true;
|
|
18664
|
-
return activityChangedSince(activity, saved.persistable);
|
|
18665
|
-
});
|
|
19195
|
+
modifiedActivities(getActivity) {
|
|
19196
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19197
|
+
const modified = [];
|
|
19198
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19199
|
+
const activity = getActivity(activityId);
|
|
19200
|
+
if (activity === null || !hasPersistedIdentity(activity.proplannerId)) {
|
|
19201
|
+
continue;
|
|
19202
|
+
}
|
|
19203
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19204
|
+
if (saved === void 0 || this.initialCalendarRepairIds.has(activityId) || activityChangedSince(activity, saved.persistable)) {
|
|
19205
|
+
modified.push(activity);
|
|
19206
|
+
}
|
|
19207
|
+
}
|
|
19208
|
+
return modified;
|
|
18666
19209
|
}
|
|
18667
|
-
modifiedLinks(
|
|
18668
|
-
if (!this.initialized) return [];
|
|
18669
|
-
|
|
18670
|
-
|
|
18671
|
-
const
|
|
18672
|
-
if (
|
|
18673
|
-
|
|
18674
|
-
|
|
19210
|
+
modifiedLinks(getLink) {
|
|
19211
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19212
|
+
const modified = [];
|
|
19213
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19214
|
+
const link = getLink(linkId);
|
|
19215
|
+
if (link === null) continue;
|
|
19216
|
+
if (!hasPersistedIdentity(link.proplannerId)) continue;
|
|
19217
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19218
|
+
if (saved === void 0 || linkChangedSince(saved, link)) {
|
|
19219
|
+
modified.push(link);
|
|
19220
|
+
}
|
|
19221
|
+
}
|
|
19222
|
+
return modified;
|
|
18675
19223
|
}
|
|
18676
19224
|
clear() {
|
|
18677
19225
|
this.initialized = false;
|
|
18678
19226
|
this.dirtyActivityIds.clear();
|
|
18679
19227
|
this.dirtyLinkIds.clear();
|
|
19228
|
+
this.initialCalendarRepairIds.clear();
|
|
18680
19229
|
this.linksAtLastSave.clear();
|
|
18681
19230
|
this.activitiesAtLastSave.clear();
|
|
18682
19231
|
}
|
|
@@ -18730,14 +19279,14 @@ function isAutoSchedulable(progress) {
|
|
|
18730
19279
|
}
|
|
18731
19280
|
function parseConstraintLabel(label, reporter) {
|
|
18732
19281
|
if (!label) return null;
|
|
18733
|
-
const
|
|
18734
|
-
if (
|
|
19282
|
+
const normalized2 = CONSTRAINT_LABEL_MAP[label.toUpperCase()];
|
|
19283
|
+
if (normalized2 === void 0) {
|
|
18735
19284
|
reporter.warn(
|
|
18736
19285
|
`[parseFromBackend] unknown constraint label "${label}" \u2014 defaulting to "asap"`
|
|
18737
19286
|
);
|
|
18738
19287
|
return DEFAULT_CONSTRAINT_TYPE;
|
|
18739
19288
|
}
|
|
18740
|
-
return
|
|
19289
|
+
return normalized2;
|
|
18741
19290
|
}
|
|
18742
19291
|
function resolveConstraint(raw, reporter) {
|
|
18743
19292
|
const constraintType = parseConstraintLabel(raw.constraint, reporter);
|
|
@@ -18778,6 +19327,24 @@ function parseIdList(raw) {
|
|
|
18778
19327
|
function parseParentId(rawParentId) {
|
|
18779
19328
|
return rawParentId === ROOT_PARENT_ID ? null : String(rawParentId);
|
|
18780
19329
|
}
|
|
19330
|
+
function normalizePersistedActivityId(rawId) {
|
|
19331
|
+
if (rawId == null) return null;
|
|
19332
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19333
|
+
if (!isValidIdentity) {
|
|
19334
|
+
throw new Error(`invalid activity proplannerId ${String(rawId)}`);
|
|
19335
|
+
}
|
|
19336
|
+
return rawId;
|
|
19337
|
+
}
|
|
19338
|
+
function parseCriticality(raw, reporter) {
|
|
19339
|
+
if (typeof raw.is_critical === "boolean") return raw.is_critical;
|
|
19340
|
+
reporter.error(
|
|
19341
|
+
"[parseFromBackend] invalid activity is_critical \u2014 defaulting to false",
|
|
19342
|
+
new TypeError(
|
|
19343
|
+
`activity "${raw.unique_id}" received ${typeof raw.is_critical}`
|
|
19344
|
+
)
|
|
19345
|
+
);
|
|
19346
|
+
return false;
|
|
19347
|
+
}
|
|
18781
19348
|
function parseBaselinePoints(raw, reporter) {
|
|
18782
19349
|
if (!Array.isArray(raw)) return [];
|
|
18783
19350
|
return raw.map((point) => normalizeBaselinePoint(point, reporter)).filter((point) => point !== null);
|
|
@@ -18798,9 +19365,10 @@ function parseActivity(raw, context) {
|
|
|
18798
19365
|
const startDate = parseBackendDate(raw.start_date);
|
|
18799
19366
|
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
18800
19367
|
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
19368
|
+
const isCritical = parseCriticality(raw, context.reporter);
|
|
18801
19369
|
return {
|
|
18802
19370
|
id: String(raw.unique_id),
|
|
18803
|
-
proplannerId:
|
|
19371
|
+
proplannerId: normalizePersistedActivityId(raw.id),
|
|
18804
19372
|
parentId: parseParentId(raw.parent_id),
|
|
18805
19373
|
name: raw.name,
|
|
18806
19374
|
description: raw.description ?? "",
|
|
@@ -18845,7 +19413,7 @@ function parseActivity(raw, context) {
|
|
|
18845
19413
|
startDate,
|
|
18846
19414
|
endDate
|
|
18847
19415
|
}),
|
|
18848
|
-
isCritical
|
|
19416
|
+
isCritical,
|
|
18849
19417
|
freeSlackHours: null,
|
|
18850
19418
|
expectedProgress: null,
|
|
18851
19419
|
expectedProgressBaseline: null,
|
|
@@ -19102,6 +19670,14 @@ function resolveLinkType(rawType) {
|
|
|
19102
19670
|
if (isLinkTypeCode(rawType)) return rawType;
|
|
19103
19671
|
return null;
|
|
19104
19672
|
}
|
|
19673
|
+
function normalizePersistedLinkId(rawId) {
|
|
19674
|
+
if (rawId == null) return void 0;
|
|
19675
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19676
|
+
if (!isValidIdentity) {
|
|
19677
|
+
throw new Error(`invalid link proplannerId ${String(rawId)}`);
|
|
19678
|
+
}
|
|
19679
|
+
return rawId;
|
|
19680
|
+
}
|
|
19105
19681
|
function normalizeLinks(rawLinks, context) {
|
|
19106
19682
|
const parsedLinks = [];
|
|
19107
19683
|
for (const rawLink of rawLinks) {
|
|
@@ -19140,16 +19716,18 @@ function parseLink(link, activityIds, hoursPerDay) {
|
|
|
19140
19716
|
}
|
|
19141
19717
|
const ganttIdRaw = link.ganttId;
|
|
19142
19718
|
const sectorIdRaw = link.sectorId;
|
|
19143
|
-
const
|
|
19719
|
+
const proplannerId = normalizePersistedLinkId(link.id);
|
|
19144
19720
|
return {
|
|
19145
19721
|
id,
|
|
19146
19722
|
source,
|
|
19147
19723
|
target,
|
|
19148
19724
|
type,
|
|
19149
19725
|
lag: lagDaysToHours(Number(link.lag ?? 0), hoursPerDay),
|
|
19726
|
+
freeSlackHours: null,
|
|
19727
|
+
isCritical: false,
|
|
19150
19728
|
...typeof ganttIdRaw === "number" ? { ganttId: ganttIdRaw } : {},
|
|
19151
19729
|
...typeof sectorIdRaw === "number" ? { sectorId: sectorIdRaw } : {},
|
|
19152
|
-
...
|
|
19730
|
+
...proplannerId === void 0 ? {} : { proplannerId }
|
|
19153
19731
|
};
|
|
19154
19732
|
}
|
|
19155
19733
|
function dedupeLinksBySignature(links) {
|
|
@@ -19276,6 +19854,9 @@ function createActivityIdGenerator(options = {}) {
|
|
|
19276
19854
|
}
|
|
19277
19855
|
}
|
|
19278
19856
|
return {
|
|
19857
|
+
fork() {
|
|
19858
|
+
return createActivityIdGenerator({ seed: counter });
|
|
19859
|
+
},
|
|
19279
19860
|
next() {
|
|
19280
19861
|
counter += 1;
|
|
19281
19862
|
return String(counter);
|
|
@@ -19307,6 +19888,9 @@ function createLinkIdGenerator(options = {}) {
|
|
|
19307
19888
|
}
|
|
19308
19889
|
}
|
|
19309
19890
|
return {
|
|
19891
|
+
fork() {
|
|
19892
|
+
return createLinkIdGenerator({ seed: counter });
|
|
19893
|
+
},
|
|
19310
19894
|
next() {
|
|
19311
19895
|
counter += 1;
|
|
19312
19896
|
return String(counter);
|
|
@@ -19342,6 +19926,9 @@ function createUniqueCorrelativeIdGenerator(options = {}) {
|
|
|
19342
19926
|
}
|
|
19343
19927
|
}
|
|
19344
19928
|
return {
|
|
19929
|
+
fork() {
|
|
19930
|
+
return createUniqueCorrelativeIdGenerator({ seed: counter });
|
|
19931
|
+
},
|
|
19345
19932
|
next() {
|
|
19346
19933
|
counter += 1;
|
|
19347
19934
|
return String(counter);
|
|
@@ -19469,7 +20056,7 @@ function selectGenerationStrategy(customId, useActivityCustomIdAsReference) {
|
|
|
19469
20056
|
function cloneStats(stats) {
|
|
19470
20057
|
return { ...stats, usedSuffixes: new Set(stats.usedSuffixes) };
|
|
19471
20058
|
}
|
|
19472
|
-
var CustomIdTracker = class {
|
|
20059
|
+
var CustomIdTracker = class _CustomIdTracker {
|
|
19473
20060
|
prefixMap = /* @__PURE__ */ new Map();
|
|
19474
20061
|
processedIds = /* @__PURE__ */ new Set();
|
|
19475
20062
|
_journalPrefixBefore = null;
|
|
@@ -19530,6 +20117,19 @@ var CustomIdTracker = class {
|
|
|
19530
20117
|
this.defaultPrefix = options.defaultPrefix ?? DEFAULT_PREFIX;
|
|
19531
20118
|
this.suffixIncrement = options.suffixIncrement ?? DEFAULT_SUFFIX_INCREMENT;
|
|
19532
20119
|
}
|
|
20120
|
+
fork() {
|
|
20121
|
+
const fork = new _CustomIdTracker({
|
|
20122
|
+
defaultPrefix: this.defaultPrefix,
|
|
20123
|
+
suffixIncrement: this.suffixIncrement
|
|
20124
|
+
});
|
|
20125
|
+
for (const [prefix, stats] of this.prefixMap) {
|
|
20126
|
+
fork.prefixMap.set(prefix, cloneStats(stats));
|
|
20127
|
+
}
|
|
20128
|
+
for (const customId of this.processedIds) {
|
|
20129
|
+
fork.processedIds.add(customId);
|
|
20130
|
+
}
|
|
20131
|
+
return fork;
|
|
20132
|
+
}
|
|
19533
20133
|
extractPrefix(customId) {
|
|
19534
20134
|
return extractPrefix(customId);
|
|
19535
20135
|
}
|
|
@@ -20136,6 +20736,133 @@ function initializeCore(input) {
|
|
|
20136
20736
|
};
|
|
20137
20737
|
}
|
|
20138
20738
|
|
|
20739
|
+
// src/previsualization/canonical-digest.ts
|
|
20740
|
+
function normalized(value) {
|
|
20741
|
+
if (value === void 0) return ["undefined"];
|
|
20742
|
+
if (value === null) return ["null"];
|
|
20743
|
+
if (typeof value === "string") return ["string", value];
|
|
20744
|
+
if (typeof value === "boolean") return ["boolean", value];
|
|
20745
|
+
if (typeof value === "number") {
|
|
20746
|
+
return ["number", Object.is(value, -0) ? "-0" : String(value)];
|
|
20747
|
+
}
|
|
20748
|
+
if (typeof value === "bigint") return ["bigint", String(value)];
|
|
20749
|
+
if (value instanceof Date) return ["date", String(value.getTime())];
|
|
20750
|
+
if (Array.isArray(value)) return ["array", value.map(normalized)];
|
|
20751
|
+
if (value instanceof Map) {
|
|
20752
|
+
return [
|
|
20753
|
+
"map",
|
|
20754
|
+
Array.from(value, ([key, entry]) => [
|
|
20755
|
+
normalized(key),
|
|
20756
|
+
normalized(entry)
|
|
20757
|
+
]).sort(
|
|
20758
|
+
(left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))
|
|
20759
|
+
)
|
|
20760
|
+
];
|
|
20761
|
+
}
|
|
20762
|
+
if (value instanceof Set) {
|
|
20763
|
+
return [
|
|
20764
|
+
"set",
|
|
20765
|
+
Array.from(value, normalized).sort(
|
|
20766
|
+
(left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))
|
|
20767
|
+
)
|
|
20768
|
+
];
|
|
20769
|
+
}
|
|
20770
|
+
if (typeof value === "object") {
|
|
20771
|
+
return [
|
|
20772
|
+
"object",
|
|
20773
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalized(entry)])
|
|
20774
|
+
];
|
|
20775
|
+
}
|
|
20776
|
+
throw new TypeError(`Unsupported canonical value: ${typeof value}`);
|
|
20777
|
+
}
|
|
20778
|
+
function serializeCanonicalSchedule(value) {
|
|
20779
|
+
return JSON.stringify(normalized(value));
|
|
20780
|
+
}
|
|
20781
|
+
|
|
20782
|
+
// src/previsualization/reconciliation.ts
|
|
20783
|
+
function entityChanges(target, source) {
|
|
20784
|
+
const targetById = new Map(
|
|
20785
|
+
target.map((entity) => [String(entity.id), entity])
|
|
20786
|
+
);
|
|
20787
|
+
const sourceById = new Map(
|
|
20788
|
+
source.map((entity) => [String(entity.id), entity])
|
|
20789
|
+
);
|
|
20790
|
+
const ids = [.../* @__PURE__ */ new Set([...targetById.keys(), ...sourceById.keys()])].sort();
|
|
20791
|
+
const changes = [];
|
|
20792
|
+
for (const id of ids) {
|
|
20793
|
+
const before = sourceById.get(id);
|
|
20794
|
+
const after = targetById.get(id);
|
|
20795
|
+
if (after === void 0) {
|
|
20796
|
+
changes.push({ id, kind: "deleted", after: null });
|
|
20797
|
+
continue;
|
|
20798
|
+
}
|
|
20799
|
+
if (before === void 0) {
|
|
20800
|
+
changes.push({ id, kind: "created", after: cloneDomainValue(after) });
|
|
20801
|
+
continue;
|
|
20802
|
+
}
|
|
20803
|
+
const fields = changedFields(before, after);
|
|
20804
|
+
if (Object.keys(fields).length > 0) {
|
|
20805
|
+
changes.push({
|
|
20806
|
+
id,
|
|
20807
|
+
kind: "updated",
|
|
20808
|
+
fields,
|
|
20809
|
+
after: cloneDomainValue(after)
|
|
20810
|
+
});
|
|
20811
|
+
}
|
|
20812
|
+
}
|
|
20813
|
+
return changes;
|
|
20814
|
+
}
|
|
20815
|
+
function changedFields(before, after) {
|
|
20816
|
+
const fields = {};
|
|
20817
|
+
const keys = [
|
|
20818
|
+
.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])
|
|
20819
|
+
].sort();
|
|
20820
|
+
for (const key of keys) {
|
|
20821
|
+
if (serializeCanonicalSchedule(before[key]) !== serializeCanonicalSchedule(after[key])) {
|
|
20822
|
+
fields[key] = {
|
|
20823
|
+
before: cloneDomainValue(before[key]),
|
|
20824
|
+
after: cloneDomainValue(after[key])
|
|
20825
|
+
};
|
|
20826
|
+
}
|
|
20827
|
+
}
|
|
20828
|
+
return fields;
|
|
20829
|
+
}
|
|
20830
|
+
function viewStateChanges(target, source) {
|
|
20831
|
+
const selectedBefore = new Set(source.selectedIds.map(String));
|
|
20832
|
+
const selectedAfter = new Set(target.selectedIds.map(String));
|
|
20833
|
+
const hiddenBefore = new Set(source.hiddenIds.map(String));
|
|
20834
|
+
const hiddenAfter = new Set(target.hiddenIds.map(String));
|
|
20835
|
+
const targetIds = target.activities.map(({ id }) => String(id)).sort();
|
|
20836
|
+
const changes = [];
|
|
20837
|
+
for (const activityId of targetIds) {
|
|
20838
|
+
const checkedBefore = selectedBefore.has(activityId);
|
|
20839
|
+
const checkedAfter = selectedAfter.has(activityId);
|
|
20840
|
+
const visibleBefore = !hiddenBefore.has(activityId);
|
|
20841
|
+
const visibleAfter = !hiddenAfter.has(activityId);
|
|
20842
|
+
if (checkedBefore === checkedAfter && visibleBefore === visibleAfter)
|
|
20843
|
+
continue;
|
|
20844
|
+
changes.push({
|
|
20845
|
+
activityId,
|
|
20846
|
+
...checkedBefore === checkedAfter ? {} : { checked: { before: checkedBefore, after: checkedAfter } },
|
|
20847
|
+
...visibleBefore === visibleAfter ? {} : { visible: { before: visibleBefore, after: visibleAfter } }
|
|
20848
|
+
});
|
|
20849
|
+
}
|
|
20850
|
+
return changes;
|
|
20851
|
+
}
|
|
20852
|
+
function createProjectionChangeSet(target, source) {
|
|
20853
|
+
const orderChanged = serializeCanonicalSchedule(target.order) !== serializeCanonicalSchedule(source.order);
|
|
20854
|
+
return {
|
|
20855
|
+
source: { kind: "init" },
|
|
20856
|
+
activities: entityChanges(target.activities, source.activities),
|
|
20857
|
+
links: entityChanges(target.links, source.links),
|
|
20858
|
+
calendars: entityChanges(target.calendars, source.calendars),
|
|
20859
|
+
trackingEvents: [],
|
|
20860
|
+
effects: [],
|
|
20861
|
+
viewState: viewStateChanges(target, source),
|
|
20862
|
+
...orderChanged ? { order: cloneDomainValue(target.order) } : {}
|
|
20863
|
+
};
|
|
20864
|
+
}
|
|
20865
|
+
|
|
20139
20866
|
// src/propagations/upward/date-gesture-preview.ts
|
|
20140
20867
|
var emptyFormula = () => ({
|
|
20141
20868
|
constantStart: null,
|
|
@@ -20281,7 +21008,10 @@ function evaluateEnd(formula, proposedEdgeTimeMs) {
|
|
|
20281
21008
|
}
|
|
20282
21009
|
|
|
20283
21010
|
// src/init/schedule-core.ts
|
|
20284
|
-
var
|
|
21011
|
+
var FORK_SOURCE = /* @__PURE__ */ Symbol("ScheduleCore.fork.source");
|
|
21012
|
+
function acknowledgesPersistence(action) {
|
|
21013
|
+
return action.kind === "persistence-acknowledge" || action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true;
|
|
21014
|
+
}
|
|
20285
21015
|
function changesRequireFilterProjectionRebuild(changes) {
|
|
20286
21016
|
for (const change of changes.activities) {
|
|
20287
21017
|
if (change.kind === "deleted") return true;
|
|
@@ -20305,10 +21035,11 @@ function isCurrentFilterProjection(projection, filter, context) {
|
|
|
20305
21035
|
const hasCurrentLocale = projection.context.locale === context.locale;
|
|
20306
21036
|
return hasCurrentHours && hasCurrentLocale;
|
|
20307
21037
|
}
|
|
20308
|
-
var ScheduleCore = class {
|
|
21038
|
+
var ScheduleCore = class _ScheduleCore {
|
|
20309
21039
|
_activityClipboard = { value: null };
|
|
20310
21040
|
_status = SCHEDULE_CORE_STATUS.READY;
|
|
20311
21041
|
coreRuntime;
|
|
21042
|
+
_isFork;
|
|
20312
21043
|
_undo = new UndoRecorder();
|
|
20313
21044
|
_opQueue = Promise.resolve();
|
|
20314
21045
|
_scheduleRevision = 0;
|
|
@@ -20339,6 +21070,42 @@ var ScheduleCore = class {
|
|
|
20339
21070
|
_criticalPathRevision = -1;
|
|
20340
21071
|
_activeCriticalPath = null;
|
|
20341
21072
|
constructor(input) {
|
|
21073
|
+
if (FORK_SOURCE in input) {
|
|
21074
|
+
this._isFork = true;
|
|
21075
|
+
const source = input[FORK_SOURCE];
|
|
21076
|
+
const state = source.coreRuntime.state.fork();
|
|
21077
|
+
this.coreRuntime = {
|
|
21078
|
+
state,
|
|
21079
|
+
scheduler: new AutoScheduler(state, source.coreRuntime.reporter),
|
|
21080
|
+
sector: cloneDomainValue(source.coreRuntime.sector),
|
|
21081
|
+
activityIdGenerator: source.coreRuntime.activityIdGenerator.fork(),
|
|
21082
|
+
linkIdGenerator: source.coreRuntime.linkIdGenerator.fork(),
|
|
21083
|
+
uniqueCorrelativeIdGenerator: source.coreRuntime.uniqueCorrelativeIdGenerator.fork(),
|
|
21084
|
+
customIdTracker: source.coreRuntime.customIdTracker.fork(),
|
|
21085
|
+
calendars: new Map(
|
|
21086
|
+
Array.from(source.coreRuntime.calendars, ([id, calendar]) => [
|
|
21087
|
+
id,
|
|
21088
|
+
cloneDomainValue(calendar)
|
|
21089
|
+
])
|
|
21090
|
+
),
|
|
21091
|
+
baseCalendars: cloneDomainValue(source.coreRuntime.baseCalendars),
|
|
21092
|
+
clock: source.coreRuntime.clock,
|
|
21093
|
+
reporter: source.coreRuntime.reporter
|
|
21094
|
+
};
|
|
21095
|
+
this._activityClipboard.value = cloneDomainValue(
|
|
21096
|
+
source._activityClipboard.value
|
|
21097
|
+
);
|
|
21098
|
+
this._undo = source._undo.fork();
|
|
21099
|
+
this._saveTracker = source._saveTracker.fork();
|
|
21100
|
+
this._scheduleRevision = source._scheduleRevision;
|
|
21101
|
+
this._pendingScheduleRevision = source._scheduleRevision;
|
|
21102
|
+
this._criticalPathRevision = source._criticalPathRevision;
|
|
21103
|
+
this._initPromise = Promise.resolve();
|
|
21104
|
+
this._criticalPathReady = Promise.resolve(null);
|
|
21105
|
+
this._ready = this._initPromise;
|
|
21106
|
+
return;
|
|
21107
|
+
}
|
|
21108
|
+
this._isFork = false;
|
|
20342
21109
|
const initialized = initializeCore(input);
|
|
20343
21110
|
this.coreRuntime = {
|
|
20344
21111
|
state: initialized.state,
|
|
@@ -20375,6 +21142,15 @@ var ScheduleCore = class {
|
|
|
20375
21142
|
get ready() {
|
|
20376
21143
|
return this._ready;
|
|
20377
21144
|
}
|
|
21145
|
+
fork() {
|
|
21146
|
+
return this._enqueue(async () => {
|
|
21147
|
+
await this._ready;
|
|
21148
|
+
this.assertReady();
|
|
21149
|
+
return new _ScheduleCore({
|
|
21150
|
+
[FORK_SOURCE]: this
|
|
21151
|
+
});
|
|
21152
|
+
});
|
|
21153
|
+
}
|
|
20378
21154
|
get criticalPathReady() {
|
|
20379
21155
|
return this._criticalPathReady;
|
|
20380
21156
|
}
|
|
@@ -20427,6 +21203,21 @@ var ScheduleCore = class {
|
|
|
20427
21203
|
this.assertReady();
|
|
20428
21204
|
return readLink(this.coreRuntime.state, id);
|
|
20429
21205
|
}
|
|
21206
|
+
getLinksForActivityView(activityId) {
|
|
21207
|
+
this.assertReady();
|
|
21208
|
+
const state = this.coreRuntime.state;
|
|
21209
|
+
const links = [];
|
|
21210
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21211
|
+
const append = (linkId) => {
|
|
21212
|
+
if (seen.has(linkId)) return;
|
|
21213
|
+
seen.add(linkId);
|
|
21214
|
+
const link = readLink(state, linkId);
|
|
21215
|
+
if (link) links.push(link);
|
|
21216
|
+
};
|
|
21217
|
+
for (const linkId of state.getOutgoingLinkIds(activityId)) append(linkId);
|
|
21218
|
+
for (const linkId of state.getIncomingLinkIds(activityId)) append(linkId);
|
|
21219
|
+
return links;
|
|
21220
|
+
}
|
|
20430
21221
|
getAllLinksView() {
|
|
20431
21222
|
this.assertReady();
|
|
20432
21223
|
return readAllLinks(this.coreRuntime.state);
|
|
@@ -20524,91 +21315,65 @@ var ScheduleCore = class {
|
|
|
20524
21315
|
}
|
|
20525
21316
|
getModifiedLinks() {
|
|
20526
21317
|
this.assertReady();
|
|
20527
|
-
return this._saveTracker.modifiedLinks(this.
|
|
21318
|
+
return this._saveTracker.modifiedLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
21319
|
+
}
|
|
21320
|
+
getNewLinks() {
|
|
21321
|
+
this.assertReady();
|
|
21322
|
+
return this._saveTracker.newLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
20528
21323
|
}
|
|
20529
21324
|
getModifiedActivities() {
|
|
20530
21325
|
this.assertReady();
|
|
20531
|
-
return this._saveTracker.modifiedActivities(
|
|
21326
|
+
return this._saveTracker.modifiedActivities(
|
|
21327
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21328
|
+
).map(cloneDomainValue);
|
|
21329
|
+
}
|
|
21330
|
+
getNewActivities() {
|
|
21331
|
+
this.assertReady();
|
|
21332
|
+
return this._saveTracker.newActivities(
|
|
21333
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21334
|
+
).map(cloneDomainValue);
|
|
20532
21335
|
}
|
|
20533
21336
|
hasUnsavedChanges() {
|
|
20534
21337
|
this.assertReady();
|
|
20535
21338
|
return this._saveTracker.hasUnsavedChanges();
|
|
20536
21339
|
}
|
|
20537
|
-
/**
|
|
20538
|
-
* Read-only view of the internal schedule revision. It moves once per
|
|
20539
|
-
* substantive CANONICAL mutation (dispatch, undo, redo). It does NOT move
|
|
20540
|
-
* for a preview dispatch nor for `applyPreviewInverse`: comparing the value
|
|
20541
|
-
* captured in `PreviewDispatchInfo.revision` against the current one
|
|
20542
|
-
* answers "did the schedule change while the preview was open".
|
|
20543
|
-
*/
|
|
21340
|
+
/** Read-only revision; moves once per substantive mutation. */
|
|
20544
21341
|
getScheduleRevision() {
|
|
20545
21342
|
return this._scheduleRevision;
|
|
20546
21343
|
}
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
);
|
|
20569
|
-
}
|
|
20570
|
-
}
|
|
20571
|
-
const afterLinks = /* @__PURE__ */ new Map();
|
|
20572
|
-
for (const change of inverse.links) {
|
|
20573
|
-
if (change.kind !== "deleted" && change.after) {
|
|
20574
|
-
afterLinks.set(String(change.id), { ...change.after });
|
|
20575
|
-
}
|
|
20576
|
-
}
|
|
20577
|
-
const entry = buildUndoEntry(
|
|
20578
|
-
inverse,
|
|
20579
|
-
void 0,
|
|
20580
|
-
void 0,
|
|
20581
|
-
afterSnap,
|
|
20582
|
-
afterLinks
|
|
20583
|
-
);
|
|
20584
|
-
applyRedo(state, entry);
|
|
20585
|
-
if (needsResync(entry)) this._resyncCustomIdTrackerFromModel();
|
|
20586
|
-
const restored = {
|
|
20587
|
-
...inverse,
|
|
20588
|
-
activities: inverse.activities.map((change) => {
|
|
20589
|
-
if (change.kind === "deleted") return { ...change, after: null };
|
|
20590
|
-
const live = state.getActivity(String(change.id));
|
|
20591
|
-
return {
|
|
20592
|
-
...change,
|
|
20593
|
-
after: live ? structuredCloneActivity(live) : null
|
|
20594
|
-
};
|
|
20595
|
-
}),
|
|
20596
|
-
links: inverse.links.map((change) => {
|
|
20597
|
-
if (change.kind === "deleted") return { ...change, after: null };
|
|
20598
|
-
const live = state.getLink(String(change.id));
|
|
20599
|
-
return { ...change, after: live ? snapshotToLink(live) : null };
|
|
20600
|
-
})
|
|
20601
|
-
};
|
|
20602
|
-
return this._reapplyViewState(restored) ?? restored;
|
|
21344
|
+
getCanonicalDigest() {
|
|
21345
|
+
this.assertReady();
|
|
21346
|
+
const byId = (left, right) => String(left.id).localeCompare(String(right.id));
|
|
21347
|
+
return serializeCanonicalSchedule({
|
|
21348
|
+
sector: this.getSector(),
|
|
21349
|
+
activities: [...this.getAllActivitiesView()].sort(byId),
|
|
21350
|
+
links: [...this.getAllLinksView()].sort(byId),
|
|
21351
|
+
calendars: [...this.getAllCalendars()].sort(byId),
|
|
21352
|
+
baseCalendars: [...this.getBaseCalendars()].sort(byId)
|
|
21353
|
+
});
|
|
21354
|
+
}
|
|
21355
|
+
createProjectionChangeSetFrom(source) {
|
|
21356
|
+
this.assertReady();
|
|
21357
|
+
source.assertReady();
|
|
21358
|
+
const projectionOf = (core) => ({
|
|
21359
|
+
activities: core.getAllActivitiesView(),
|
|
21360
|
+
links: core.getAllLinksView(),
|
|
21361
|
+
calendars: core.getAllCalendars(),
|
|
21362
|
+
selectedIds: core.getSelectedActivityIds(),
|
|
21363
|
+
hiddenIds: core.getHiddenActivityIds(),
|
|
21364
|
+
order: collectBranchOrder(core.coreRuntime.state)
|
|
20603
21365
|
});
|
|
21366
|
+
return createProjectionChangeSet(projectionOf(this), projectionOf(source));
|
|
20604
21367
|
}
|
|
20605
21368
|
getDeletedActivitiesSinceLastSave() {
|
|
20606
21369
|
this.assertReady();
|
|
20607
|
-
return this._saveTracker.deletedActivities(
|
|
21370
|
+
return this._saveTracker.deletedActivities(
|
|
21371
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21372
|
+
).map(cloneDomainValue);
|
|
20608
21373
|
}
|
|
20609
21374
|
getDeletedLinksSinceLastSave() {
|
|
20610
21375
|
this.assertReady();
|
|
20611
|
-
return this._saveTracker.deletedLinks(this.
|
|
21376
|
+
return this._saveTracker.deletedLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
20612
21377
|
}
|
|
20613
21378
|
_enqueue(work) {
|
|
20614
21379
|
const workPromise = this._opQueue.then(work, work);
|
|
@@ -20684,11 +21449,11 @@ var ScheduleCore = class {
|
|
|
20684
21449
|
});
|
|
20685
21450
|
}
|
|
20686
21451
|
async dispatch(action, options = {}) {
|
|
20687
|
-
const admitted =
|
|
21452
|
+
const admitted = this._beginScheduleMutation(action);
|
|
20688
21453
|
return this._enqueue(() => this._dispatchInner(action, options, admitted));
|
|
20689
21454
|
}
|
|
20690
21455
|
async applyActivityBatch(action, options = {}) {
|
|
20691
|
-
const admitted =
|
|
21456
|
+
const admitted = this._beginScheduleMutation(action);
|
|
20692
21457
|
return this._enqueue(() => this._dispatchInner(action, options, admitted));
|
|
20693
21458
|
}
|
|
20694
21459
|
async _dispatchInner(action, options = {}, admitted = false) {
|
|
@@ -20703,23 +21468,20 @@ var ScheduleCore = class {
|
|
|
20703
21468
|
}
|
|
20704
21469
|
async _runDispatch(action, options) {
|
|
20705
21470
|
this.assertReady();
|
|
20706
|
-
if (
|
|
21471
|
+
if (this._isFork && acknowledgesPersistence(action)) {
|
|
20707
21472
|
return {
|
|
20708
21473
|
result: {
|
|
20709
21474
|
ok: false,
|
|
20710
|
-
reason: REJECTION_REASON.
|
|
21475
|
+
reason: REJECTION_REASON.SANDBOX_PERSISTENCE_FORBIDDEN
|
|
20711
21476
|
},
|
|
20712
21477
|
changedState: false
|
|
20713
21478
|
};
|
|
20714
21479
|
}
|
|
20715
21480
|
const pipelineContext = buildPipelineContext(this.coreRuntime.state, {
|
|
20716
21481
|
hoursPerDay: this.coreRuntime.sector.hoursPerDay,
|
|
20717
|
-
dateFormat: this.coreRuntime.sector.dateFormat
|
|
20718
|
-
// ISSUE-057 (CP6): sin default silencioso 'days'; la clave se omite si
|
|
20719
|
-
// no se declaró (exactOptionalPropertyTypes).
|
|
20720
|
-
...options.inputUnit !== void 0 ? { inputUnit: options.inputUnit } : {}
|
|
21482
|
+
dateFormat: this.coreRuntime.sector.dateFormat
|
|
20721
21483
|
});
|
|
20722
|
-
const historyPolicy = getDispatchHistoryPolicy(action
|
|
21484
|
+
const historyPolicy = getDispatchHistoryPolicy(action);
|
|
20723
21485
|
const generatorSnapshot = [
|
|
20724
21486
|
this.coreRuntime.activityIdGenerator.snapshot(),
|
|
20725
21487
|
this.coreRuntime.linkIdGenerator.snapshot(),
|
|
@@ -20737,6 +21499,8 @@ var ScheduleCore = class {
|
|
|
20737
21499
|
calendars: this.coreRuntime.calendars,
|
|
20738
21500
|
activityIdGen: this.coreRuntime.activityIdGenerator,
|
|
20739
21501
|
linkIdGen: this.coreRuntime.linkIdGenerator,
|
|
21502
|
+
isActivityIdReserved: (activityId) => this._saveTracker.hasCheckpointActivityId(activityId),
|
|
21503
|
+
isLinkIdReserved: (linkId) => this._saveTracker.hasCheckpointLinkId(linkId),
|
|
20740
21504
|
uidGen: this.coreRuntime.uniqueCorrelativeIdGenerator,
|
|
20741
21505
|
customIdTracker: this.coreRuntime.customIdTracker,
|
|
20742
21506
|
reporter: this.coreRuntime.reporter,
|
|
@@ -20773,17 +21537,20 @@ var ScheduleCore = class {
|
|
|
20773
21537
|
}
|
|
20774
21538
|
if (result.ok) {
|
|
20775
21539
|
if (historyPolicy === "clear-on-success") {
|
|
20776
|
-
this._saveTracker.
|
|
20777
|
-
this.coreRuntime.state.getAllActivities()
|
|
21540
|
+
this._saveTracker.acknowledge(
|
|
21541
|
+
this.coreRuntime.state.getAllActivities(),
|
|
21542
|
+
this.coreRuntime.state.getAllLinks()
|
|
20778
21543
|
);
|
|
20779
|
-
this._saveTracker.snapshotLinks(this.getAllLinksView());
|
|
20780
21544
|
this._undo.clear();
|
|
20781
21545
|
} else if (action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true) {
|
|
20782
21546
|
this._saveTracker.acknowledgePersistedLookahead(
|
|
20783
|
-
|
|
21547
|
+
result.changes.activities,
|
|
20784
21548
|
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
20785
21549
|
);
|
|
20786
|
-
|
|
21550
|
+
if (action.persistedProgress !== void 0) {
|
|
21551
|
+
this._undo.clear();
|
|
21552
|
+
}
|
|
21553
|
+
} else {
|
|
20787
21554
|
this._saveTracker.recordChanges(
|
|
20788
21555
|
result.changes.activities,
|
|
20789
21556
|
result.changes.links,
|
|
@@ -20794,20 +21561,6 @@ var ScheduleCore = class {
|
|
|
20794
21561
|
}
|
|
20795
21562
|
result = this._withReappliedViewState(action, result);
|
|
20796
21563
|
if (!result.ok) return { result, changedState: false };
|
|
20797
|
-
if (options.preview === true) {
|
|
20798
|
-
const inverse = buildInverseChangeSet(
|
|
20799
|
-
this.coreRuntime.state,
|
|
20800
|
-
buildUndoEntry(buildHistoryChangeSet(result.changes)),
|
|
20801
|
-
"before"
|
|
20802
|
-
);
|
|
20803
|
-
return {
|
|
20804
|
-
result: {
|
|
20805
|
-
...toPublicDispatchResult(result),
|
|
20806
|
-
preview: { inverse, revision: this._scheduleRevision }
|
|
20807
|
-
},
|
|
20808
|
-
changedState: false
|
|
20809
|
-
};
|
|
20810
|
-
}
|
|
20811
21564
|
if (dispatchChangesSchedulingState(action) && changeSetIsSubstantive(result.changes)) {
|
|
20812
21565
|
this._recordScheduleMutation(
|
|
20813
21566
|
options.skipAutoSchedule !== true && options.skipCriticalPath !== true && resolveRunCriticalPath(action)
|
|
@@ -21060,13 +21813,20 @@ var ScheduleCore = class {
|
|
|
21060
21813
|
this.coreRuntime.customIdTracker.beginCustomIdTransaction();
|
|
21061
21814
|
let changes;
|
|
21062
21815
|
try {
|
|
21063
|
-
for (const [activityId, fields] of calculatedFields) {
|
|
21816
|
+
for (const [activityId, fields] of calculatedFields.activities) {
|
|
21064
21817
|
setCriticalPathFieldsIfChanged(
|
|
21065
21818
|
this.coreRuntime.state,
|
|
21066
21819
|
activityId,
|
|
21067
21820
|
fields
|
|
21068
21821
|
);
|
|
21069
21822
|
}
|
|
21823
|
+
for (const [linkId, fields] of calculatedFields.links) {
|
|
21824
|
+
setCriticalPathLinkFieldsIfChanged(
|
|
21825
|
+
this.coreRuntime.state,
|
|
21826
|
+
linkId,
|
|
21827
|
+
fields
|
|
21828
|
+
);
|
|
21829
|
+
}
|
|
21070
21830
|
changes = await assembleChangeSet(this.coreRuntime.state, {
|
|
21071
21831
|
source: { kind: "init" },
|
|
21072
21832
|
beforeSnap: /* @__PURE__ */ new Map(),
|
|
@@ -21348,7 +22108,6 @@ var DISPATCH_ACTION_KIND = {
|
|
|
21348
22108
|
FILTER_SET: "filter-set",
|
|
21349
22109
|
SORT_SET: "sort-set",
|
|
21350
22110
|
SIR_SYNC: "sir-sync",
|
|
21351
|
-
SIR_APPROVE: "sir-approve",
|
|
21352
22111
|
ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync"
|
|
21353
22112
|
};
|
|
21354
22113
|
var KIND_CATALOG_COVERS_UNION = {
|
|
@@ -21378,7 +22137,6 @@ var KIND_CATALOG_COVERS_UNION = {
|
|
|
21378
22137
|
[DISPATCH_ACTION_KIND.FILTER_SET]: true,
|
|
21379
22138
|
[DISPATCH_ACTION_KIND.SORT_SET]: true,
|
|
21380
22139
|
[DISPATCH_ACTION_KIND.SIR_SYNC]: true,
|
|
21381
|
-
[DISPATCH_ACTION_KIND.SIR_APPROVE]: true,
|
|
21382
22140
|
[DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC]: true
|
|
21383
22141
|
};
|
|
21384
22142
|
var DISPATCH_ACTION_KINDS = Object.keys(KIND_CATALOG_COVERS_UNION);
|