jupiter-dynamic-forms 1.19.9 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/dynamic-form.d.ts +22 -0
- package/dist/core/dynamic-form.d.ts.map +1 -1
- package/dist/core/form-section.d.ts +18 -0
- package/dist/core/form-section.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +102 -30
- package/dist/index.mjs.map +1 -1
- package/dist/schema/types.d.ts +1 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/utils/xbrl-form-builder.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -683,6 +683,7 @@ class XBRLFormBuilder {
|
|
|
683
683
|
var _a, _b;
|
|
684
684
|
const label = this.getPreferredLabel(concept.labels, language, concept.preferredLabel);
|
|
685
685
|
const childPeriodHint = ((_a = concept.preferredLabel) == null ? void 0 : _a.includes("periodStartLabel")) ? "start" : ((_b = concept.preferredLabel) == null ? void 0 : _b.includes("periodEndLabel")) ? "end" : ancestorPeriodHint;
|
|
686
|
+
const effectivePeriodRole = childPeriodHint === "start" ? "periodStartLabel" : childPeriodHint === "end" ? "periodEndLabel" : void 0;
|
|
686
687
|
const fields = [];
|
|
687
688
|
if (!concept.elementAbstract) {
|
|
688
689
|
let columnIds = [];
|
|
@@ -751,8 +752,10 @@ class XBRLFormBuilder {
|
|
|
751
752
|
periodType: concept.periodType,
|
|
752
753
|
balance: concept.balance,
|
|
753
754
|
// Pass through balance attribute from XBRL data
|
|
754
|
-
preferredLabel: concept.preferredLabel
|
|
755
|
+
preferredLabel: concept.preferredLabel,
|
|
755
756
|
// Pass through for period date calculation in manual columns
|
|
757
|
+
effectivePeriodRole
|
|
758
|
+
// JDF-039: own-or-inherited periodStartLabel/periodEndLabel role
|
|
756
759
|
};
|
|
757
760
|
}
|
|
758
761
|
/**
|
|
@@ -6432,50 +6435,87 @@ let JupiterFormSection = class extends LitElement {
|
|
|
6432
6435
|
this._totalChildConceptsMap.clear();
|
|
6433
6436
|
this._traverseTotalGroups(concepts);
|
|
6434
6437
|
}
|
|
6438
|
+
/**
|
|
6439
|
+
* JDF-038: XBRL uses more than one preferredLabel role to mark a "total" row — the plain
|
|
6440
|
+
* `.../role/totalLabel` and the sign-flipped `.../role/negatedTotalLabel` (used for subtotals of
|
|
6441
|
+
* negated/expense items, e.g. "Total of sum of expenses"). A case-sensitive `includes('totalLabel')`
|
|
6442
|
+
* misses the latter, because "negatedTotalLabel" contains "TotalLabel" with a capital T. Case-insensitive
|
|
6443
|
+
* matching is the single source of truth for "is this row a total" everywhere in this file.
|
|
6444
|
+
*/
|
|
6445
|
+
static _isTotalLabel(preferredLabel) {
|
|
6446
|
+
return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("totallabel"));
|
|
6447
|
+
}
|
|
6448
|
+
/**
|
|
6449
|
+
* A member of a total's sibling/child group can itself be a presentation-only abstract wrapper
|
|
6450
|
+
* (e.g. "Group equity" wrapping Equity + Third party share + "Total of group equity") rather than
|
|
6451
|
+
* a value-bearing concept. Abstract concepts never have a form field, so resolve them to the nested
|
|
6452
|
+
* totalLabel concept that actually carries their aggregate value — otherwise the outer total's
|
|
6453
|
+
* calculation silently drops that whole subgroup (formData lookup on the abstract's id is always empty).
|
|
6454
|
+
*/
|
|
6455
|
+
static _resolveValueConcept(node, depth = 0) {
|
|
6456
|
+
var _a;
|
|
6457
|
+
if (!node.abstract)
|
|
6458
|
+
return node;
|
|
6459
|
+
if (depth > 5 || !((_a = node.children) == null ? void 0 : _a.length))
|
|
6460
|
+
return null;
|
|
6461
|
+
const nestedTotal = node.children.find((c2) => JupiterFormSection._isTotalLabel(c2.preferredLabel));
|
|
6462
|
+
if (!nestedTotal)
|
|
6463
|
+
return null;
|
|
6464
|
+
return JupiterFormSection._resolveValueConcept(nestedTotal, depth + 1);
|
|
6465
|
+
}
|
|
6466
|
+
_registerTotalGroup(totalConcept, members) {
|
|
6467
|
+
const resolvedMembers = members.map((m) => JupiterFormSection._resolveValueConcept(m)).filter((m) => m !== null);
|
|
6468
|
+
if (resolvedMembers.length === 0)
|
|
6469
|
+
return;
|
|
6470
|
+
const memberIds = resolvedMembers.map((m) => m.id);
|
|
6471
|
+
this._totalChildrenMap.set(totalConcept.id, memberIds);
|
|
6472
|
+
this._totalConceptMap.set(totalConcept.id, totalConcept);
|
|
6473
|
+
this._totalChildConceptsMap.set(totalConcept.id, resolvedMembers);
|
|
6474
|
+
for (const memberId of memberIds) {
|
|
6475
|
+
this._memberParentMap.set(memberId, totalConcept.id);
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6435
6478
|
_traverseTotalGroups(concepts) {
|
|
6436
|
-
var _a, _b
|
|
6479
|
+
var _a, _b;
|
|
6437
6480
|
for (let i2 = 0; i2 < concepts.length; i2++) {
|
|
6438
6481
|
const concept = concepts[i2];
|
|
6439
|
-
if ((
|
|
6440
|
-
if ((
|
|
6441
|
-
|
|
6442
|
-
this._totalChildrenMap.set(concept.id, childIds);
|
|
6443
|
-
this._totalConceptMap.set(concept.id, concept);
|
|
6444
|
-
this._totalChildConceptsMap.set(concept.id, concept.children);
|
|
6445
|
-
for (const childId of childIds) {
|
|
6446
|
-
this._memberParentMap.set(childId, concept.id);
|
|
6447
|
-
}
|
|
6482
|
+
if (JupiterFormSection._isTotalLabel(concept.preferredLabel)) {
|
|
6483
|
+
if ((_a = concept.children) == null ? void 0 : _a.length) {
|
|
6484
|
+
this._registerTotalGroup(concept, concept.children);
|
|
6448
6485
|
} else {
|
|
6449
6486
|
let lastTotalIdx = -1;
|
|
6450
6487
|
for (let j = i2 - 1; j >= 0; j--) {
|
|
6451
|
-
if ((
|
|
6488
|
+
if (JupiterFormSection._isTotalLabel(concepts[j].preferredLabel)) {
|
|
6452
6489
|
lastTotalIdx = j;
|
|
6453
6490
|
break;
|
|
6454
6491
|
}
|
|
6455
6492
|
}
|
|
6456
6493
|
const siblings = concepts.slice(lastTotalIdx + 1, i2);
|
|
6457
6494
|
if (siblings.length > 0) {
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
this.
|
|
6461
|
-
this._totalChildConceptsMap.set(concept.id, siblings);
|
|
6462
|
-
for (const siblingId of siblingIds) {
|
|
6463
|
-
this._memberParentMap.set(siblingId, concept.id);
|
|
6464
|
-
}
|
|
6495
|
+
this._registerTotalGroup(concept, siblings);
|
|
6496
|
+
} else if (lastTotalIdx >= 0) {
|
|
6497
|
+
this._registerTotalGroup(concept, [concepts[lastTotalIdx]]);
|
|
6465
6498
|
}
|
|
6466
6499
|
}
|
|
6467
6500
|
}
|
|
6468
|
-
if ((
|
|
6501
|
+
if ((_b = concept.children) == null ? void 0 : _b.length) {
|
|
6469
6502
|
this._traverseTotalGroups(concept.children);
|
|
6470
6503
|
}
|
|
6471
6504
|
}
|
|
6472
6505
|
}
|
|
6473
6506
|
_runCalculationCheck(conceptId, columnId) {
|
|
6507
|
+
const totalIds = /* @__PURE__ */ new Set();
|
|
6508
|
+
if (this._totalConceptMap.has(conceptId))
|
|
6509
|
+
totalIds.add(conceptId);
|
|
6510
|
+
const parentTotalId = this._memberParentMap.get(conceptId);
|
|
6511
|
+
if (parentTotalId)
|
|
6512
|
+
totalIds.add(parentTotalId);
|
|
6513
|
+
for (const totalConceptId of totalIds) {
|
|
6514
|
+
this._evaluateTotal(totalConceptId, columnId);
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
_evaluateTotal(totalConceptId, columnId) {
|
|
6474
6518
|
var _a;
|
|
6475
|
-
const isSelfTotal = this._totalConceptMap.has(conceptId);
|
|
6476
|
-
const totalConceptId = isSelfTotal ? conceptId : this._memberParentMap.get(conceptId);
|
|
6477
|
-
if (!totalConceptId)
|
|
6478
|
-
return;
|
|
6479
6519
|
const totalConcept = this._totalConceptMap.get(totalConceptId);
|
|
6480
6520
|
const totalRaw = (_a = this.formData[totalConceptId]) == null ? void 0 : _a[columnId];
|
|
6481
6521
|
const key = `${totalConceptId}__${columnId}`;
|
|
@@ -10744,6 +10784,38 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10744
10784
|
}
|
|
10745
10785
|
return `${startDate} / ${endDate}`;
|
|
10746
10786
|
}
|
|
10787
|
+
/**
|
|
10788
|
+
* JDF-040: Resolves the instant date for a submission entry (Save Draft/Submit).
|
|
10789
|
+
* periodStartLabel-effective concepts (own-labelled or inherited, JDF-039) always
|
|
10790
|
+
* resolve via _resolveInstantDate — the same call the self-heal itself makes — instead
|
|
10791
|
+
* of trusting whatever happens to be cached in _periodData/field.periodInstantDate.
|
|
10792
|
+
* This matters because self-heal only ever corrects an EXISTING _periodData override
|
|
10793
|
+
* (it never fabricates one), so a cell that was never manually period-edited would
|
|
10794
|
+
* otherwise fall through to column.periodEndDate here and silently re-emit the wrong
|
|
10795
|
+
* date on every Save Draft/Submit even though field.periodInstantDate was healed.
|
|
10796
|
+
* Everything else keeps the previous fallback chain, but with field.periodInstantDate
|
|
10797
|
+
* now outranking column.periodEndDate, matching the already-correct sibling
|
|
10798
|
+
* implementations in _findFactValueForField and _addConceptDataToSubmission.
|
|
10799
|
+
*/
|
|
10800
|
+
_resolveSubmissionInstantDate(concept, field2, fieldPeriodData, column2) {
|
|
10801
|
+
const effectiveRole = this._getEffectivePeriodRole(concept);
|
|
10802
|
+
if (effectiveRole == null ? void 0 : effectiveRole.includes("periodStartLabel")) {
|
|
10803
|
+
const periodStartDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
|
|
10804
|
+
const periodEndDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate;
|
|
10805
|
+
return this._resolveInstantDate(effectiveRole, void 0, periodStartDate, periodEndDate);
|
|
10806
|
+
}
|
|
10807
|
+
return (fieldPeriodData == null ? void 0 : fieldPeriodData.instantDate) || (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || field2.periodInstantDate || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || field2.periodStartDate || this.periodStartDate;
|
|
10808
|
+
}
|
|
10809
|
+
/**
|
|
10810
|
+
* JDF-039: The label value to use for periodStartLabel/periodEndLabel checks — the
|
|
10811
|
+
* concept's own preferredLabel if it carries one of those roles, otherwise the role
|
|
10812
|
+
* inherited from the nearest ancestor that does (stamped by XBRLFormBuilder as
|
|
10813
|
+
* effectivePeriodRole). Falls back to preferredLabel for any concept built before this
|
|
10814
|
+
* fix (or otherwise missing the field).
|
|
10815
|
+
*/
|
|
10816
|
+
_getEffectivePeriodRole(concept) {
|
|
10817
|
+
return concept.effectivePeriodRole ?? concept.preferredLabel;
|
|
10818
|
+
}
|
|
10747
10819
|
_resolveInstantDate(preferredLabel, instantDate, startDate, endDate) {
|
|
10748
10820
|
if (preferredLabel == null ? void 0 : preferredLabel.includes("periodStartLabel")) {
|
|
10749
10821
|
const base = startDate || "2025-01-01";
|
|
@@ -10773,7 +10845,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10773
10845
|
_correctPeriodStartInstantDatesInConcepts(concepts, section2) {
|
|
10774
10846
|
concepts.forEach((concept) => {
|
|
10775
10847
|
var _a, _b;
|
|
10776
|
-
if (((_a = concept
|
|
10848
|
+
if (((_a = this._getEffectivePeriodRole(concept)) == null ? void 0 : _a.includes("periodStartLabel")) && concept.periodType === "instant" && ((_b = concept.fields) == null ? void 0 : _b.length)) {
|
|
10777
10849
|
concept.fields.forEach((field2) => {
|
|
10778
10850
|
this._correctFieldPeriodStartInstantDate(concept, field2, concept.id, section2);
|
|
10779
10851
|
const repeatCount = this._repeatCounts[concept.id] || 0;
|
|
@@ -10793,7 +10865,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
10793
10865
|
const fieldPeriodData = (_a = this._periodData[periodDataKey]) == null ? void 0 : _a[field2.columnId];
|
|
10794
10866
|
const periodStartDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
|
|
10795
10867
|
const periodEndDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate;
|
|
10796
|
-
const correctedInstantDate = this._resolveInstantDate(concept
|
|
10868
|
+
const correctedInstantDate = this._resolveInstantDate(this._getEffectivePeriodRole(concept), void 0, periodStartDate, periodEndDate);
|
|
10797
10869
|
if (fieldPeriodData && fieldPeriodData.instantDate !== correctedInstantDate) {
|
|
10798
10870
|
console.log(`🩹 [Legacy Period Correction] ${periodDataKey}/${field2.columnId}: instantDate ${fieldPeriodData.instantDate} → ${correctedInstantDate}`);
|
|
10799
10871
|
fieldPeriodData.instantDate = correctedInstantDate;
|
|
@@ -11003,7 +11075,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11003
11075
|
let field2;
|
|
11004
11076
|
const colStartDate = request.periodType === "instant" ? request.instantDate : request.startDate;
|
|
11005
11077
|
const colEndDate = request.periodType === "instant" ? request.instantDate : request.endDate;
|
|
11006
|
-
const instantDate = this._resolveInstantDate(concept
|
|
11078
|
+
const instantDate = this._resolveInstantDate(this._getEffectivePeriodRole(concept), request.instantDate, colStartDate, colEndDate);
|
|
11007
11079
|
if (existingField) {
|
|
11008
11080
|
field2 = {
|
|
11009
11081
|
id: `${concept.id}_${columnId}`,
|
|
@@ -11719,7 +11791,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11719
11791
|
// CRITICAL: Include conceptType for XBRL validation
|
|
11720
11792
|
periodStartDate: column2.periodStartDate,
|
|
11721
11793
|
periodEndDate: column2.periodEndDate,
|
|
11722
|
-
periodInstantDate: conceptPeriodType === "instant" ?
|
|
11794
|
+
periodInstantDate: conceptPeriodType === "instant" ? this._resolveInstantDate(this._getEffectivePeriodRole(concept), void 0, column2.periodStartDate, column2.periodEndDate) : void 0
|
|
11723
11795
|
};
|
|
11724
11796
|
if ((_a = this._periodData[concept.id]) == null ? void 0 : _a[column2.id]) {
|
|
11725
11797
|
const customPeriod = this._periodData[concept.id][column2.id];
|
|
@@ -12051,7 +12123,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12051
12123
|
}
|
|
12052
12124
|
};
|
|
12053
12125
|
if (concept.periodType === "instant") {
|
|
12054
|
-
const instantDate =
|
|
12126
|
+
const instantDate = this._resolveSubmissionInstantDate(concept, field2, fieldPeriodData, column2);
|
|
12055
12127
|
submissionEntry.period.date = instantDate;
|
|
12056
12128
|
} else {
|
|
12057
12129
|
const startDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
|
|
@@ -12167,7 +12239,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12167
12239
|
}
|
|
12168
12240
|
};
|
|
12169
12241
|
if (concept.periodType === "instant") {
|
|
12170
|
-
instanceEntry.period.date =
|
|
12242
|
+
instanceEntry.period.date = this._resolveSubmissionInstantDate(concept, field2, fieldPeriodData, column2);
|
|
12171
12243
|
} else {
|
|
12172
12244
|
instanceEntry.period.startDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
|
|
12173
12245
|
instanceEntry.period.endDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate;
|