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