jupiter-dynamic-forms 1.20.3 → 1.20.4
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 +8 -1
- package/dist/core/dynamic-form.d.ts.map +1 -1
- package/dist/core/formula-validation-dialog.d.ts +4 -2
- package/dist/core/formula-validation-dialog.d.ts.map +1 -1
- package/dist/index.js +159 -132
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +233 -42
- package/dist/index.mjs.map +1 -1
- package/dist/schema/types.d.ts +7 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/xbrl-types.d.ts +1 -1
- package/dist/schema/xbrl-types.d.ts.map +1 -1
- package/dist/utils/formula-resolution-context.d.ts +12 -4
- package/dist/utils/formula-resolution-context.d.ts.map +1 -1
- package/dist/utils/formula-variable-resolver.d.ts +18 -0
- package/dist/utils/formula-variable-resolver.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1668,7 +1668,8 @@ const form$1 = {
|
|
|
1668
1668
|
download: "Download",
|
|
1669
1669
|
cancelValidation: "Cancel Validation",
|
|
1670
1670
|
lastValidationResults: "Last Validation Results",
|
|
1671
|
-
validationInProgress: "Validation In Progress"
|
|
1671
|
+
validationInProgress: "Validation In Progress",
|
|
1672
|
+
quickValidate: "Quick Validate"
|
|
1672
1673
|
};
|
|
1673
1674
|
const filter$1 = {
|
|
1674
1675
|
selectRoles: "Select Roles",
|
|
@@ -1873,7 +1874,8 @@ const form = {
|
|
|
1873
1874
|
download: "Downloaden",
|
|
1874
1875
|
cancelValidation: "Validatie annuleren",
|
|
1875
1876
|
lastValidationResults: "Laatste validatieresultaten",
|
|
1876
|
-
validationInProgress: "Validatie bezig"
|
|
1877
|
+
validationInProgress: "Validatie bezig",
|
|
1878
|
+
quickValidate: "Snel valideren"
|
|
1877
1879
|
};
|
|
1878
1880
|
const filter = {
|
|
1879
1881
|
selectRoles: "Rollen selecteren",
|
|
@@ -2715,22 +2717,26 @@ function collectAllCandidates(formData) {
|
|
|
2715
2717
|
}
|
|
2716
2718
|
return candidates;
|
|
2717
2719
|
}
|
|
2718
|
-
function indexConceptTree(concept, conceptByQName, conceptById, childrenIndex) {
|
|
2720
|
+
function indexConceptTree(concept, conceptByQName, conceptById, conceptIdByQName, conceptIdByQNameHasChildren, childrenIndex) {
|
|
2719
2721
|
const info = {
|
|
2720
2722
|
conceptId: concept.id,
|
|
2721
|
-
qname: concept.
|
|
2723
|
+
qname: concept.name,
|
|
2722
2724
|
balance: concept.balance
|
|
2723
2725
|
};
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
if (!conceptByQName.has(concept.conceptName))
|
|
2727
|
-
conceptByQName.set(concept.conceptName, info);
|
|
2726
|
+
conceptById.set(concept.id, info);
|
|
2727
|
+
(conceptByQName.get(concept.name) ?? conceptByQName.set(concept.name, []).get(concept.name)).push(info);
|
|
2728
2728
|
const children = concept.children || [];
|
|
2729
|
+
const hasOwnChildren = children.length > 0;
|
|
2730
|
+
const alreadyHasChildren = conceptIdByQNameHasChildren.get(concept.name) === true;
|
|
2731
|
+
if (!conceptIdByQName.has(concept.name) || hasOwnChildren && !alreadyHasChildren) {
|
|
2732
|
+
conceptIdByQName.set(concept.name, concept.id);
|
|
2733
|
+
conceptIdByQNameHasChildren.set(concept.name, hasOwnChildren);
|
|
2734
|
+
}
|
|
2729
2735
|
childrenIndex.set(
|
|
2730
2736
|
concept.id,
|
|
2731
2737
|
children.map((child) => child.id)
|
|
2732
2738
|
);
|
|
2733
|
-
children.forEach((child) => indexConceptTree(child, conceptByQName, conceptById, childrenIndex));
|
|
2739
|
+
children.forEach((child) => indexConceptTree(child, conceptByQName, conceptById, conceptIdByQName, conceptIdByQNameHasChildren, childrenIndex));
|
|
2734
2740
|
}
|
|
2735
2741
|
function indexMemberTree(member, memberQNameToId) {
|
|
2736
2742
|
if (!memberQNameToId.has(member.conceptName))
|
|
@@ -2740,21 +2746,23 @@ function indexMemberTree(member, memberQNameToId) {
|
|
|
2740
2746
|
const PRESENTATION_TOTAL_GROUP_ACCESSORS = {
|
|
2741
2747
|
getId: (concept) => concept.id,
|
|
2742
2748
|
getPreferredLabel: (concept) => concept.preferredLabel,
|
|
2743
|
-
isAbstract: (concept) => concept.
|
|
2749
|
+
isAbstract: (concept) => concept.abstract === true,
|
|
2744
2750
|
getChildren: (concept) => concept.children || []
|
|
2745
2751
|
};
|
|
2746
|
-
function buildFormulaResolutionContext(formData, columns,
|
|
2752
|
+
function buildFormulaResolutionContext(formData, columns, sections, hypercubeRoles, periodStartDate, periodEndDate) {
|
|
2747
2753
|
const conceptByQName = /* @__PURE__ */ new Map();
|
|
2748
2754
|
const conceptById = /* @__PURE__ */ new Map();
|
|
2755
|
+
const conceptIdByQNamePerRole = /* @__PURE__ */ new Map();
|
|
2749
2756
|
const childrenByRole = /* @__PURE__ */ new Map();
|
|
2750
2757
|
const totalGroupChildrenByRole = /* @__PURE__ */ new Map();
|
|
2751
|
-
|
|
2752
|
-
var _a;
|
|
2753
|
-
const concepts = ((_a = role.presentationLinkbase) == null ? void 0 : _a.concepts) || [];
|
|
2758
|
+
sections.forEach((section2) => {
|
|
2754
2759
|
const childrenIndex = /* @__PURE__ */ new Map();
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2760
|
+
const conceptIdByQName = /* @__PURE__ */ new Map();
|
|
2761
|
+
const conceptIdByQNameHasChildren = /* @__PURE__ */ new Map();
|
|
2762
|
+
section2.concepts.forEach((concept) => indexConceptTree(concept, conceptByQName, conceptById, conceptIdByQName, conceptIdByQNameHasChildren, childrenIndex));
|
|
2763
|
+
childrenByRole.set(section2.roleURI, childrenIndex);
|
|
2764
|
+
conceptIdByQNamePerRole.set(section2.roleURI, conceptIdByQName);
|
|
2765
|
+
totalGroupChildrenByRole.set(section2.roleURI, buildTotalGroupChildrenIndex(section2.concepts, PRESENTATION_TOTAL_GROUP_ACCESSORS));
|
|
2758
2766
|
});
|
|
2759
2767
|
const dimensionQNameToId = /* @__PURE__ */ new Map();
|
|
2760
2768
|
const memberQNameToId = /* @__PURE__ */ new Map();
|
|
@@ -2775,6 +2783,7 @@ function buildFormulaResolutionContext(formData, columns, presentationRoles, hyp
|
|
|
2775
2783
|
periodEndDate,
|
|
2776
2784
|
conceptByQName,
|
|
2777
2785
|
conceptById,
|
|
2786
|
+
conceptIdByQNamePerRole,
|
|
2778
2787
|
childrenByRole,
|
|
2779
2788
|
totalGroupChildrenByRole,
|
|
2780
2789
|
dimensionQNameToId,
|
|
@@ -2867,14 +2876,15 @@ function unionCandidates(lists) {
|
|
|
2867
2876
|
return Array.from(merged.values());
|
|
2868
2877
|
}
|
|
2869
2878
|
function matchFilter(filter2, universe, ctx) {
|
|
2870
|
-
var _a;
|
|
2879
|
+
var _a, _b;
|
|
2871
2880
|
switch (filter2.type) {
|
|
2872
2881
|
case "CONCEPT_NAME": {
|
|
2873
2882
|
const qname = filter2.attributes["concept.qname"];
|
|
2874
|
-
const
|
|
2875
|
-
if (!
|
|
2883
|
+
const infos = qname ? ctx.conceptByQName.get(qname) : void 0;
|
|
2884
|
+
if (!infos || infos.length === 0)
|
|
2876
2885
|
return [];
|
|
2877
|
-
|
|
2886
|
+
const conceptIds = new Set(infos.map((info) => info.conceptId));
|
|
2887
|
+
return universe.filter((candidate) => conceptIds.has(candidate.conceptId));
|
|
2878
2888
|
}
|
|
2879
2889
|
case "CONCEPT_BALANCE": {
|
|
2880
2890
|
const balance = filter2.attributes["balance"];
|
|
@@ -2887,16 +2897,16 @@ function matchFilter(filter2, universe, ctx) {
|
|
|
2887
2897
|
const anchorQName = filter2.attributes["qname"];
|
|
2888
2898
|
const linkrole = filter2.attributes["linkrole"];
|
|
2889
2899
|
const axis = filter2.attributes["axis"];
|
|
2890
|
-
const
|
|
2900
|
+
const anchorConceptId = anchorQName && linkrole ? (_a = ctx.conceptIdByQNamePerRole.get(linkrole)) == null ? void 0 : _a.get(anchorQName) : void 0;
|
|
2891
2901
|
const childrenIndex = linkrole ? ctx.childrenByRole.get(linkrole) : void 0;
|
|
2892
|
-
if (!
|
|
2902
|
+
if (!anchorConceptId || !childrenIndex)
|
|
2893
2903
|
return [];
|
|
2894
2904
|
let relatedConceptIds;
|
|
2895
2905
|
if (axis === "descendant") {
|
|
2896
|
-
relatedConceptIds = collectDescendantConceptIds(childrenIndex,
|
|
2906
|
+
relatedConceptIds = collectDescendantConceptIds(childrenIndex, anchorConceptId);
|
|
2897
2907
|
} else {
|
|
2898
|
-
const nestedChildren = childrenIndex.get(
|
|
2899
|
-
relatedConceptIds = nestedChildren.length > 0 ? nestedChildren : ((
|
|
2908
|
+
const nestedChildren = childrenIndex.get(anchorConceptId) || [];
|
|
2909
|
+
relatedConceptIds = nestedChildren.length > 0 ? nestedChildren : ((_b = ctx.totalGroupChildrenByRole.get(linkrole ?? "")) == null ? void 0 : _b.get(anchorConceptId)) || [];
|
|
2900
2910
|
}
|
|
2901
2911
|
const relatedSet = new Set(relatedConceptIds);
|
|
2902
2912
|
return universe.filter((candidate) => relatedSet.has(candidate.conceptId));
|
|
@@ -2924,6 +2934,8 @@ function matchFilter(filter2, universe, ctx) {
|
|
|
2924
2934
|
}
|
|
2925
2935
|
case "OR_FILTER":
|
|
2926
2936
|
return unionCandidates(filter2.children.map((child) => resolveFilter(child, universe, ctx)));
|
|
2937
|
+
case "ASPECT_COVER":
|
|
2938
|
+
return universe;
|
|
2927
2939
|
default:
|
|
2928
2940
|
return [];
|
|
2929
2941
|
}
|
|
@@ -2976,6 +2988,27 @@ function resolveFormulaVariableUsedFallback(variable, ctx) {
|
|
|
2976
2988
|
return true;
|
|
2977
2989
|
return void 0;
|
|
2978
2990
|
}
|
|
2991
|
+
function collectConceptNameQNames(filters) {
|
|
2992
|
+
const qnames = [];
|
|
2993
|
+
for (const filter2 of filters) {
|
|
2994
|
+
if (filter2.type === "CONCEPT_NAME") {
|
|
2995
|
+
const qname = filter2.attributes["concept.qname"];
|
|
2996
|
+
if (qname)
|
|
2997
|
+
qnames.push(qname);
|
|
2998
|
+
} else if (filter2.type === "OR_FILTER") {
|
|
2999
|
+
qnames.push(...collectConceptNameQNames(filter2.children));
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
return qnames;
|
|
3003
|
+
}
|
|
3004
|
+
function resolveVariableConceptQNames(variable) {
|
|
3005
|
+
return collectConceptNameQNames(variable.filters);
|
|
3006
|
+
}
|
|
3007
|
+
function resolveVariableColumnsForConcept(variable, targetConceptId, ctx) {
|
|
3008
|
+
const universe = ctx.columns.map((column2) => ({ conceptId: targetConceptId, columnId: column2.id }));
|
|
3009
|
+
const resolved = intersectCandidates(variable.filters.map((filter2) => resolveFilter(filter2, universe, ctx)));
|
|
3010
|
+
return resolved.map((candidate) => candidate.columnId);
|
|
3011
|
+
}
|
|
2979
3012
|
function buildAssertionBindings(assertion, ctx) {
|
|
2980
3013
|
const bindings = /* @__PURE__ */ new Map();
|
|
2981
3014
|
for (const variable of assertion.variables) {
|
|
@@ -3074,10 +3107,10 @@ class FormulaValidationService {
|
|
|
3074
3107
|
if (assertion.type === "EXISTENCE_ASSERTION") {
|
|
3075
3108
|
const matchedFactCount = resolveFormulaVariableMatchCount(assertion.variables[0], ctx);
|
|
3076
3109
|
const satisfied2 = evaluateExistenceTest(assertion.test, matchedFactCount);
|
|
3077
|
-
return this._evaluatedResult(assertion, role, satisfied2, language, matchedFactCount);
|
|
3110
|
+
return this._evaluatedResult(assertion, role, ctx, satisfied2, language, matchedFactCount);
|
|
3078
3111
|
}
|
|
3079
3112
|
const satisfied = evaluateFormulaTest(assertion.test, bindings, fallbackUsage);
|
|
3080
|
-
return this._evaluatedResult(assertion, role, satisfied, language);
|
|
3113
|
+
return this._evaluatedResult(assertion, role, ctx, satisfied, language);
|
|
3081
3114
|
} catch (error2) {
|
|
3082
3115
|
if (error2 instanceof FormulaExpressionError) {
|
|
3083
3116
|
console.warn(`[FormulaValidationService] Assertion "${assertion.id}" could not be evaluated (${error2.reason}): ${error2.message}`);
|
|
@@ -3098,7 +3131,8 @@ class FormulaValidationService {
|
|
|
3098
3131
|
skipReason
|
|
3099
3132
|
};
|
|
3100
3133
|
}
|
|
3101
|
-
_evaluatedResult(assertion, role, satisfied, language, matchedFactCount) {
|
|
3134
|
+
_evaluatedResult(assertion, role, ctx, satisfied, language, matchedFactCount) {
|
|
3135
|
+
const message = satisfied ? void 0 : this._pickMessage(assertion, language);
|
|
3102
3136
|
return {
|
|
3103
3137
|
assertionId: assertion.id,
|
|
3104
3138
|
roleURI: role.roleURI,
|
|
@@ -3107,8 +3141,9 @@ class FormulaValidationService {
|
|
|
3107
3141
|
severity: assertion.severity,
|
|
3108
3142
|
satisfied,
|
|
3109
3143
|
skipped: false,
|
|
3110
|
-
message
|
|
3111
|
-
matchedFactCount
|
|
3144
|
+
message,
|
|
3145
|
+
matchedFactCount,
|
|
3146
|
+
conceptLinks: message ? this._buildConceptLinks(assertion, role.roleURI, ctx, message) : void 0
|
|
3112
3147
|
};
|
|
3113
3148
|
}
|
|
3114
3149
|
_pickMessage(assertion, language) {
|
|
@@ -3116,7 +3151,55 @@ class FormulaValidationService {
|
|
|
3116
3151
|
if (messages.length === 0)
|
|
3117
3152
|
return void 0;
|
|
3118
3153
|
const message = messages.find((m) => m.xmlLang === language) ?? messages.find((m) => m.xmlLang === "en") ?? messages[0];
|
|
3119
|
-
return message.text;
|
|
3154
|
+
return this._stripTechnicalDetail(message.text);
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Taxonomy-authored messages append raw XBRL Formula/XPath debug expressions after the
|
|
3158
|
+
* human-readable sentence, delimited by " | {...}" (e.g. `xff:has-fallback-value(...)`,
|
|
3159
|
+
* `varArc_...` variable names) — see the fixture's `*_formula.json` message text. That debug
|
|
3160
|
+
* payload is meaningless to a non-technical filer, so only the sentence preceding it is shown.
|
|
3161
|
+
*/
|
|
3162
|
+
_stripTechnicalDetail(text) {
|
|
3163
|
+
const braceIndex = text.indexOf("{");
|
|
3164
|
+
if (braceIndex === -1)
|
|
3165
|
+
return text.trim();
|
|
3166
|
+
return text.slice(0, braceIndex).replace(/\|\s*$/, "").trim();
|
|
3167
|
+
}
|
|
3168
|
+
/**
|
|
3169
|
+
* JDF-059: resolves every concept the assertion's variables name directly (JDF-045's
|
|
3170
|
+
* `resolveVariableConceptQNames`) to a clickable fact location — but only the ones the
|
|
3171
|
+
* (already-stripped) message text actually mentions by backtick-quoted local name, e.g.
|
|
3172
|
+
* `` `Equity` `` — a concept a variable references internally (say, as a dimensional
|
|
3173
|
+
* exclusion) but the message never calls out by name has nothing for the user to click on, so
|
|
3174
|
+
* it's left out rather than guessed at. Column resolution reuses the assertion's own filters
|
|
3175
|
+
* (`resolveVariableColumnsForConcept`), so the link always points at the exact fact this
|
|
3176
|
+
* specific assertion run cared about — never just "some field for this concept" — including an
|
|
3177
|
+
* empty one for a failed "MUST exist" assertion, so the user can navigate straight to where the
|
|
3178
|
+
* missing value belongs.
|
|
3179
|
+
*/
|
|
3180
|
+
_buildConceptLinks(assertion, roleURI, ctx, message) {
|
|
3181
|
+
var _a, _b, _c;
|
|
3182
|
+
const links = [];
|
|
3183
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
3184
|
+
for (const variable of assertion.variables) {
|
|
3185
|
+
for (const qname of resolveVariableConceptQNames(variable)) {
|
|
3186
|
+
const conceptId = ((_a = ctx.conceptIdByQNamePerRole.get(roleURI)) == null ? void 0 : _a.get(qname)) ?? ((_c = (_b = ctx.conceptByQName.get(qname)) == null ? void 0 : _b[0]) == null ? void 0 : _c.conceptId);
|
|
3187
|
+
if (!conceptId)
|
|
3188
|
+
continue;
|
|
3189
|
+
const label = qname.includes(":") ? qname.split(":").pop() : qname;
|
|
3190
|
+
if (seenLabels.has(label))
|
|
3191
|
+
continue;
|
|
3192
|
+
if (!new RegExp("`" + this._escapeRegExp(label) + "`").test(message))
|
|
3193
|
+
continue;
|
|
3194
|
+
const columns = resolveVariableColumnsForConcept(variable, conceptId, ctx);
|
|
3195
|
+
links.push({ label, conceptQName: qname, conceptId, columnId: columns[0] });
|
|
3196
|
+
seenLabels.add(label);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
return links;
|
|
3200
|
+
}
|
|
3201
|
+
_escapeRegExp(text) {
|
|
3202
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3120
3203
|
}
|
|
3121
3204
|
}
|
|
3122
3205
|
class FactMatcher {
|
|
@@ -10290,6 +10373,35 @@ let JupiterFormulaValidationDialog = class extends LitElement {
|
|
|
10290
10373
|
}
|
|
10291
10374
|
this.requestUpdate();
|
|
10292
10375
|
}
|
|
10376
|
+
// JDF-059: navigates the host form to the exact fact this assertion's variable resolved to
|
|
10377
|
+
// (or its empty target cell, for a "must exist" failure) and closes this dialog, mirroring the
|
|
10378
|
+
// existing "click a validation error, land on its field" flow this feature deliberately reuses
|
|
10379
|
+
// rather than building a second focus mechanism.
|
|
10380
|
+
_handleConceptLinkClick(link) {
|
|
10381
|
+
this.dispatchEvent(new CustomEvent("formula-concept-click", {
|
|
10382
|
+
detail: { conceptId: link.conceptId, conceptQName: link.conceptQName, columnId: link.columnId },
|
|
10383
|
+
bubbles: true,
|
|
10384
|
+
composed: true
|
|
10385
|
+
}));
|
|
10386
|
+
}
|
|
10387
|
+
// Splits a message on its backtick-quoted concept mentions and renders the ones this
|
|
10388
|
+
// assertion resolved a fact location for (`result.conceptLinks`) as click-to-focus buttons;
|
|
10389
|
+
// any other backtick mention (e.g. an enumerated value like `Na`, not a concept) renders as
|
|
10390
|
+
// plain text with the backticks dropped, since there is nothing to link it to.
|
|
10391
|
+
_renderMessage(result) {
|
|
10392
|
+
const message = result.message ?? "";
|
|
10393
|
+
const linkByLabel = new Map((result.conceptLinks ?? []).map((link) => [link.label, link]));
|
|
10394
|
+
const parts = message.split(/(`[^`]+`)/g);
|
|
10395
|
+
return html`${parts.map((part) => {
|
|
10396
|
+
const match = /^`([^`]+)`$/.exec(part);
|
|
10397
|
+
if (!match)
|
|
10398
|
+
return part;
|
|
10399
|
+
const link = linkByLabel.get(match[1]);
|
|
10400
|
+
if (!link)
|
|
10401
|
+
return match[1];
|
|
10402
|
+
return html`<button type="button" class="concept-link" @click="${() => this._handleConceptLinkClick(link)}">${match[1]}</button>`;
|
|
10403
|
+
})}`;
|
|
10404
|
+
}
|
|
10293
10405
|
_groupByRole(failures) {
|
|
10294
10406
|
const grouped = failures.reduce((groups, result) => {
|
|
10295
10407
|
var _a;
|
|
@@ -10338,7 +10450,7 @@ let JupiterFormulaValidationDialog = class extends LitElement {
|
|
|
10338
10450
|
<span class="severity-badge ${result.severity === "ERROR" ? "error" : "warning"}">
|
|
10339
10451
|
${result.severity === "ERROR" ? I18n.t("formulaValidation.severityError") : I18n.t("formulaValidation.severityWarning")}
|
|
10340
10452
|
</span>
|
|
10341
|
-
<span class="assertion-message">${result
|
|
10453
|
+
<span class="assertion-message">${this._renderMessage(result)}</span>
|
|
10342
10454
|
</div>
|
|
10343
10455
|
`)}
|
|
10344
10456
|
</div>
|
|
@@ -10533,6 +10645,23 @@ JupiterFormulaValidationDialog.styles = css`
|
|
|
10533
10645
|
line-height: 1.4;
|
|
10534
10646
|
}
|
|
10535
10647
|
|
|
10648
|
+
.concept-link {
|
|
10649
|
+
font: inherit;
|
|
10650
|
+
font-weight: 600;
|
|
10651
|
+
color: var(--buttonBgColor, var(--jupiter-primary-color, #1976d2));
|
|
10652
|
+
background: none;
|
|
10653
|
+
border: none;
|
|
10654
|
+
padding: 0;
|
|
10655
|
+
margin: 0;
|
|
10656
|
+
cursor: pointer;
|
|
10657
|
+
text-decoration: underline;
|
|
10658
|
+
text-underline-offset: 2px;
|
|
10659
|
+
}
|
|
10660
|
+
|
|
10661
|
+
.concept-link:hover {
|
|
10662
|
+
opacity: 0.8;
|
|
10663
|
+
}
|
|
10664
|
+
|
|
10536
10665
|
.dialog-actions {
|
|
10537
10666
|
display: flex;
|
|
10538
10667
|
gap: 12px;
|
|
@@ -11381,6 +11510,14 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
11381
11510
|
_handleFormulaValidationDialogCancel() {
|
|
11382
11511
|
this._showFormulaValidationDialog = false;
|
|
11383
11512
|
}
|
|
11513
|
+
// JDF-059: a concept name inside a formula-validation message was clicked — close the popup
|
|
11514
|
+
// (mirrors the existing "click a validation error, land on its field" UX from the host-rendered
|
|
11515
|
+
// show-validation-results flow) and jump straight to the exact fact the assertion's own variable
|
|
11516
|
+
// resolution identified, via `columnId` rather than re-deriving it from dimensions.
|
|
11517
|
+
_handleFormulaConceptClick(event) {
|
|
11518
|
+
this._showFormulaValidationDialog = false;
|
|
11519
|
+
this.scrollToConcept(event.detail.conceptQName, void 0, void 0, event.detail.columnId);
|
|
11520
|
+
}
|
|
11384
11521
|
_handleRoleFilterApply(event) {
|
|
11385
11522
|
var _a, _b;
|
|
11386
11523
|
const { selectedRoleIds, periodPreferences } = event.detail;
|
|
@@ -12519,7 +12656,6 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12519
12656
|
return;
|
|
12520
12657
|
}
|
|
12521
12658
|
this._correctLegacyPeriodStartInstantDates();
|
|
12522
|
-
this._runFormulaValidationPreflight();
|
|
12523
12659
|
const submissionData = this._generateSubmissionData();
|
|
12524
12660
|
console.log("📊 Form Submission Data:", JSON.stringify(submissionData, null, 2));
|
|
12525
12661
|
console.log("📊 Submission Data Summary:");
|
|
@@ -12541,6 +12677,16 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12541
12677
|
this._submitDisabled = false;
|
|
12542
12678
|
}, 1e3);
|
|
12543
12679
|
}
|
|
12680
|
+
/**
|
|
12681
|
+
* Handler for the standalone "Quick Validate" button (visible only when `formulaEnabled` is
|
|
12682
|
+
* true). Runs the formula-assertion pre-flight in isolation from the original Validate/submit
|
|
12683
|
+
* button, which no longer triggers formula validation as a side effect.
|
|
12684
|
+
*/
|
|
12685
|
+
_handleQuickValidate() {
|
|
12686
|
+
if (this.mode === "admin")
|
|
12687
|
+
return;
|
|
12688
|
+
this._runFormulaValidationPreflight();
|
|
12689
|
+
}
|
|
12544
12690
|
/**
|
|
12545
12691
|
* JDF-049: runs FormulaValidationService against the live form state and stores the results
|
|
12546
12692
|
* for the results dialog (JDF-050). Graceful no-op — per JDF-048's gating and this ticket's
|
|
@@ -12550,21 +12696,42 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
12550
12696
|
* cost when off" requirement).
|
|
12551
12697
|
*/
|
|
12552
12698
|
_runFormulaValidationPreflight() {
|
|
12553
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
12699
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
12554
12700
|
if (!this.formulaEnabled)
|
|
12555
12701
|
return;
|
|
12556
12702
|
const formulaRoles = (_c = (_b = (_a = this.xbrlInput) == null ? void 0 : _a.formula) == null ? void 0 : _b[0]) == null ? void 0 : _c.roles;
|
|
12557
12703
|
if (!formulaRoles || formulaRoles.length === 0)
|
|
12558
12704
|
return;
|
|
12705
|
+
const scopeSections = ((_e = (_d = this._currentSchema) == null ? void 0 : _d.sections) == null ? void 0 : _e.length) ? this._currentSchema.sections : this._allSections;
|
|
12706
|
+
if (!scopeSections || scopeSections.length === 0)
|
|
12707
|
+
return;
|
|
12708
|
+
const visibleRoleURIs = new Set(scopeSections.map((section2) => {
|
|
12709
|
+
var _a2;
|
|
12710
|
+
return ((_a2 = section2.metadata) == null ? void 0 : _a2.roleURI) || section2.id;
|
|
12711
|
+
}));
|
|
12712
|
+
const scopedFormulaRoles = formulaRoles.filter((role) => visibleRoleURIs.has(role.roleURI));
|
|
12713
|
+
if (scopedFormulaRoles.length === 0)
|
|
12714
|
+
return;
|
|
12715
|
+
const conceptSections = scopeSections.map((section2) => {
|
|
12716
|
+
var _a2;
|
|
12717
|
+
return {
|
|
12718
|
+
roleURI: ((_a2 = section2.metadata) == null ? void 0 : _a2.roleURI) || section2.id,
|
|
12719
|
+
concepts: section2.concepts
|
|
12720
|
+
};
|
|
12721
|
+
});
|
|
12559
12722
|
const ctx = buildFormulaResolutionContext(
|
|
12560
12723
|
this._formData,
|
|
12561
12724
|
this._mergeAllSectionColumns(),
|
|
12562
|
-
|
|
12563
|
-
(
|
|
12725
|
+
conceptSections,
|
|
12726
|
+
(_h = (_g = (_f = this.xbrlInput) == null ? void 0 : _f.hypercubes) == null ? void 0 : _g[0]) == null ? void 0 : _h.roles,
|
|
12564
12727
|
this.periodStartDate,
|
|
12565
12728
|
this.periodEndDate
|
|
12566
12729
|
);
|
|
12567
|
-
const
|
|
12730
|
+
const scopedXbrlInput = {
|
|
12731
|
+
...this.xbrlInput,
|
|
12732
|
+
formula: [{ ...this.xbrlInput.formula[0], roles: scopedFormulaRoles }]
|
|
12733
|
+
};
|
|
12734
|
+
const results = this._formulaValidationService.evaluate(scopedXbrlInput, ctx, this.language);
|
|
12568
12735
|
const summary = this._formulaValidationService.summarize(results);
|
|
12569
12736
|
this._formulaValidationResults = results;
|
|
12570
12737
|
this._formulaValidationSummary = summary;
|
|
@@ -14728,9 +14895,9 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14728
14895
|
return dims.length === 1 && this._normalizeAxisId(col.dimensionData.axisId ?? "") === this._normalizeAxisId(dims[0].axis) && this._normalizeMemberId(col.dimensionData.memberId ?? "") === this._normalizeMemberId(dims[0].member);
|
|
14729
14896
|
})) == null ? void 0 : _a.id;
|
|
14730
14897
|
}
|
|
14731
|
-
async scrollToConcept(conceptName, dimensions, match) {
|
|
14898
|
+
async scrollToConcept(conceptName, dimensions, match, columnId) {
|
|
14732
14899
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
14733
|
-
console.log(`[scrollToConcept] ▶ START conceptName=${conceptName} value=${match == null ? void 0 : match.value} dims=${JSON.stringify(dimensions)}`);
|
|
14900
|
+
console.log(`[scrollToConcept] ▶ START conceptName=${conceptName} value=${match == null ? void 0 : match.value} dims=${JSON.stringify(dimensions)} columnId=${columnId}`);
|
|
14734
14901
|
let targetSection = null;
|
|
14735
14902
|
let targetConcept = null;
|
|
14736
14903
|
const sectionsToSearch = [
|
|
@@ -14744,7 +14911,19 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14744
14911
|
for (const section2 of sectionsToSearch) {
|
|
14745
14912
|
const found = this._findConceptByName(section2.concepts, conceptName);
|
|
14746
14913
|
if (found) {
|
|
14747
|
-
if (
|
|
14914
|
+
if (columnId) {
|
|
14915
|
+
const cols = section2.columns ?? this._columns;
|
|
14916
|
+
const hasColumn = cols.some((c2) => c2.id === columnId);
|
|
14917
|
+
console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" | columnId="${columnId}" present=${hasColumn}`);
|
|
14918
|
+
if (hasColumn) {
|
|
14919
|
+
targetSection = section2;
|
|
14920
|
+
targetConcept = found;
|
|
14921
|
+
break;
|
|
14922
|
+
} else if (!targetSection) {
|
|
14923
|
+
targetSection = section2;
|
|
14924
|
+
targetConcept = found;
|
|
14925
|
+
}
|
|
14926
|
+
} else if (dimensions == null ? void 0 : dimensions.length) {
|
|
14748
14927
|
const cols = section2.columns ?? this._columns;
|
|
14749
14928
|
const colId = this._findColumnByDimensions(cols, dimensions);
|
|
14750
14929
|
console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" | colMatch=${colId ?? "null"} | cols=${cols.map((c2) => c2.id).join(",")}`);
|
|
@@ -14777,7 +14956,9 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
14777
14956
|
let targetColumnId = null;
|
|
14778
14957
|
const hasValueMatch = (match == null ? void 0 : match.value) !== void 0 && (match == null ? void 0 : match.value) !== null;
|
|
14779
14958
|
const targetValue = hasValueMatch ? String(match.value) : null;
|
|
14780
|
-
if (
|
|
14959
|
+
if (columnId) {
|
|
14960
|
+
targetColumnId = columnId;
|
|
14961
|
+
} else if (dimensions == null ? void 0 : dimensions.length) {
|
|
14781
14962
|
targetColumnId = this._findColumnByDimensions(columns, dimensions) ?? null;
|
|
14782
14963
|
} else if (!hasValueMatch) {
|
|
14783
14964
|
targetColumnId = ((_b = columns[0]) == null ? void 0 : _b.id) ?? null;
|
|
@@ -15023,6 +15204,15 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15023
15204
|
</button>
|
|
15024
15205
|
` : ""}
|
|
15025
15206
|
|
|
15207
|
+
${this.formulaEnabled && this.mode !== "admin" ? html`
|
|
15208
|
+
<button
|
|
15209
|
+
class="btn-secondary btn-quick-validate"
|
|
15210
|
+
@click="${this._handleQuickValidate}"
|
|
15211
|
+
?disabled="${this.disabled || this.readonly || this._validationStatus === "inProgress"}"
|
|
15212
|
+
>
|
|
15213
|
+
${I18n.t("form.quickValidate")}
|
|
15214
|
+
</button>
|
|
15215
|
+
` : ""}
|
|
15026
15216
|
|
|
15027
15217
|
<button
|
|
15028
15218
|
class="btn-primary"
|
|
@@ -15060,6 +15250,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
15060
15250
|
.results="${this._formulaValidationResults}"
|
|
15061
15251
|
.summary="${this._formulaValidationSummary}"
|
|
15062
15252
|
@dialog-cancel="${this._handleFormulaValidationDialogCancel}"
|
|
15253
|
+
@formula-concept-click="${this._handleFormulaConceptClick}"
|
|
15063
15254
|
@click="${this._handleFormulaValidationDialogCancel}"
|
|
15064
15255
|
></jupiter-formula-validation-dialog>
|
|
15065
15256
|
` : ""}
|