jupiter-dynamic-forms 1.20.2 → 1.20.3

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.
Files changed (33) hide show
  1. package/dist/core/concept-tree.d.ts.map +1 -1
  2. package/dist/core/dynamic-form.d.ts +41 -0
  3. package/dist/core/dynamic-form.d.ts.map +1 -1
  4. package/dist/core/form-field.d.ts.map +1 -1
  5. package/dist/core/form-section.d.ts +4 -0
  6. package/dist/core/form-section.d.ts.map +1 -1
  7. package/dist/core/formula-validation-dialog.d.ts +30 -0
  8. package/dist/core/formula-validation-dialog.d.ts.map +1 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +349 -75
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +1666 -293
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/schema/types.d.ts +26 -0
  16. package/dist/schema/types.d.ts.map +1 -1
  17. package/dist/schema/xbrl-types.d.ts +67 -0
  18. package/dist/schema/xbrl-types.d.ts.map +1 -1
  19. package/dist/utils/formula-constants.d.ts +7 -0
  20. package/dist/utils/formula-constants.d.ts.map +1 -0
  21. package/dist/utils/formula-expression-evaluator.d.ts +64 -0
  22. package/dist/utils/formula-expression-evaluator.d.ts.map +1 -0
  23. package/dist/utils/formula-precondition-evaluator.d.ts +33 -0
  24. package/dist/utils/formula-precondition-evaluator.d.ts.map +1 -0
  25. package/dist/utils/formula-resolution-context.d.ts +45 -0
  26. package/dist/utils/formula-resolution-context.d.ts.map +1 -0
  27. package/dist/utils/formula-variable-resolver.d.ts +30 -0
  28. package/dist/utils/formula-variable-resolver.d.ts.map +1 -0
  29. package/dist/utils/total-group-resolver.d.ts +68 -0
  30. package/dist/utils/total-group-resolver.d.ts.map +1 -0
  31. package/dist/utils/total-label.d.ts +9 -0
  32. package/dist/utils/total-label.d.ts.map +1 -0
  33. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1817,6 +1817,15 @@ const calculationWarning$1 = {
1817
1817
  added: "Added",
1818
1818
  subtracted: "Subtracted (opposite balance)"
1819
1819
  };
1820
+ const formulaValidation$1 = {
1821
+ dialogTitle: "Formula check results",
1822
+ allPassed: "All formula checks passed",
1823
+ summaryCount: "{{failed}} of {{total}} checks failed",
1824
+ summaryCountBreakdown: "{{warnings}} warnings, {{errors}} errors",
1825
+ severityWarning: "Warning",
1826
+ severityError: "Error",
1827
+ close: "Close"
1828
+ };
1820
1829
  const enTranslations = {
1821
1830
  conceptInfo: conceptInfo$1,
1822
1831
  form: form$1,
@@ -1828,7 +1837,8 @@ const enTranslations = {
1828
1837
  validation: validation$1,
1829
1838
  xbrlValidation: xbrlValidation$1,
1830
1839
  error: error$1,
1831
- calculationWarning: calculationWarning$1
1840
+ calculationWarning: calculationWarning$1,
1841
+ formulaValidation: formulaValidation$1
1832
1842
  };
1833
1843
  const conceptInfo = {
1834
1844
  title: "Conceptinformatie",
@@ -2012,6 +2022,15 @@ const calculationWarning = {
2012
2022
  added: "Opgeteld",
2013
2023
  subtracted: "Afgetrokken (tegengestelde balans)"
2014
2024
  };
2025
+ const formulaValidation = {
2026
+ dialogTitle: "Resultaten formulecontrole",
2027
+ allPassed: "Alle formulecontroles geslaagd",
2028
+ summaryCount: "{{failed}} van {{total}} controles mislukt",
2029
+ summaryCountBreakdown: "{{warnings}} waarschuwingen, {{errors}} fouten",
2030
+ severityWarning: "Waarschuwing",
2031
+ severityError: "Fout",
2032
+ close: "Sluiten"
2033
+ };
2015
2034
  const nlTranslations = {
2016
2035
  conceptInfo,
2017
2036
  form,
@@ -2023,7 +2042,8 @@ const nlTranslations = {
2023
2042
  validation,
2024
2043
  xbrlValidation,
2025
2044
  error,
2026
- calculationWarning
2045
+ calculationWarning,
2046
+ formulaValidation
2027
2047
  };
2028
2048
  const translations = {
2029
2049
  en: enTranslations,
@@ -2102,6 +2122,9 @@ class I18n {
2102
2122
  }
2103
2123
  }
2104
2124
  I18n.currentLanguage = "en";
2125
+ function isTotalLabel$1(preferredLabel) {
2126
+ return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("totallabel"));
2127
+ }
2105
2128
  class DraftStorageService {
2106
2129
  constructor(useSessionStorage = false) {
2107
2130
  this.DRAFT_DATA_KEY = "jupiter-form-draft-data";
@@ -2261,7 +2284,7 @@ class DraftStorageService {
2261
2284
  /**
2262
2285
  * Create metadata snapshot from current form state
2263
2286
  */
2264
- createMetadataSnapshot(periodStartDate, periodEndDate, language, selectedRoleIds, allSections, typedMemberData, periodPreferences, periodData, unitData, reportingLanguage = "en", repeatCounts, decimalsData, roleCompletedStates, hiddenColumns) {
2287
+ createMetadataSnapshot(periodStartDate, periodEndDate, language, selectedRoleIds, allSections, typedMemberData, periodPreferences, periodData, unitData, reportingLanguage = "en", repeatCounts, decimalsData, roleCompletedStates, hiddenColumns, totalManuallyEditedData) {
2265
2288
  return {
2266
2289
  periodStartDate,
2267
2290
  periodEndDate,
@@ -2278,6 +2301,7 @@ class DraftStorageService {
2278
2301
  repeatCounts,
2279
2302
  roleCompletedStates,
2280
2303
  hiddenColumns,
2304
+ totalManuallyEditedData,
2281
2305
  schemaVersion: this.STORAGE_VERSION
2282
2306
  };
2283
2307
  }
@@ -2300,6 +2324,801 @@ class DraftStorageService {
2300
2324
  return { compatible, warnings };
2301
2325
  }
2302
2326
  }
2327
+ const CALCULATION_TOLERANCE = 0.01;
2328
+ class FormulaExpressionError extends Error {
2329
+ constructor(message, test, reason = "unsupported-syntax") {
2330
+ super(message);
2331
+ this.test = test;
2332
+ this.reason = reason;
2333
+ this.name = "FormulaExpressionError";
2334
+ }
2335
+ }
2336
+ const TOKEN_PATTERN = /\s*(?:(\$[A-Za-z_][\w-]*)|(-?\d+(?:\.\d+)?)|([A-Za-z][\w:-]*)|('[^']*')|(\()|(\))|(\+)|(-)|(=))/y;
2337
+ function tokenize(test) {
2338
+ const tokens = [];
2339
+ TOKEN_PATTERN.lastIndex = 0;
2340
+ let index = 0;
2341
+ while (index < test.length) {
2342
+ TOKEN_PATTERN.lastIndex = index;
2343
+ const match = TOKEN_PATTERN.exec(test);
2344
+ if (!match || match.index !== index) {
2345
+ if (/^\s*$/.test(test.slice(index)))
2346
+ break;
2347
+ throw new FormulaExpressionError(`Unrecognized token at position ${index}: "${test.slice(index, index + 10)}..."`, test, "unsupported-syntax");
2348
+ }
2349
+ const [full, variable, number, ident, string, lparen, rparen, plus, minus, equals] = match;
2350
+ if (variable)
2351
+ tokens.push({ type: "VARIABLE", value: variable.slice(1) });
2352
+ else if (number)
2353
+ tokens.push({ type: "NUMBER", value: number });
2354
+ else if (ident)
2355
+ tokens.push({ type: "IDENT", value: ident });
2356
+ else if (string)
2357
+ tokens.push({ type: "STRING", value: string.slice(1, -1) });
2358
+ else if (lparen)
2359
+ tokens.push({ type: "LPAREN", value: lparen });
2360
+ else if (rparen)
2361
+ tokens.push({ type: "RPAREN", value: rparen });
2362
+ else if (plus)
2363
+ tokens.push({ type: "PLUS", value: plus });
2364
+ else if (minus)
2365
+ tokens.push({ type: "MINUS", value: minus });
2366
+ else if (equals)
2367
+ tokens.push({ type: "EQUALS", value: equals });
2368
+ index += full.length;
2369
+ }
2370
+ tokens.push({ type: "EOF", value: "" });
2371
+ return tokens;
2372
+ }
2373
+ class Parser {
2374
+ constructor(tokens, test) {
2375
+ this.tokens = tokens;
2376
+ this.test = test;
2377
+ this.pos = 0;
2378
+ }
2379
+ peek() {
2380
+ return this.tokens[this.pos];
2381
+ }
2382
+ next() {
2383
+ return this.tokens[this.pos++];
2384
+ }
2385
+ expect(type) {
2386
+ const token = this.next();
2387
+ if (token.type !== type) {
2388
+ throw new FormulaExpressionError(
2389
+ `Expected ${type} but got ${token.type} ("${token.value}")`,
2390
+ this.test,
2391
+ "parse-error"
2392
+ );
2393
+ }
2394
+ return token;
2395
+ }
2396
+ parseBoolExpr() {
2397
+ const node = this.parseOrExpr();
2398
+ if (this.peek().type !== "EOF") {
2399
+ throw new FormulaExpressionError(
2400
+ `Unexpected trailing token "${this.peek().value}"`,
2401
+ this.test,
2402
+ "unsupported-syntax"
2403
+ );
2404
+ }
2405
+ return node;
2406
+ }
2407
+ parseOrExpr() {
2408
+ const terms = [this.parseAndExpr()];
2409
+ while (this.peek().type === "IDENT" && this.peek().value === "or") {
2410
+ this.next();
2411
+ terms.push(this.parseAndExpr());
2412
+ }
2413
+ return terms.length === 1 ? terms[0] : { kind: "or", terms };
2414
+ }
2415
+ parseAndExpr() {
2416
+ const terms = [this.parseBoolTerm()];
2417
+ while (this.peek().type === "IDENT" && this.peek().value === "and") {
2418
+ this.next();
2419
+ terms.push(this.parseBoolTerm());
2420
+ }
2421
+ return terms.length === 1 ? terms[0] : { kind: "and", terms };
2422
+ }
2423
+ parseBoolTerm() {
2424
+ const token = this.peek();
2425
+ if (token.type === "IDENT" && token.value === "not") {
2426
+ this.next();
2427
+ this.expect("LPAREN");
2428
+ const arg = this.parseOrExpr();
2429
+ this.expect("RPAREN");
2430
+ return { kind: "not", arg };
2431
+ }
2432
+ if (token.type === "IDENT" && token.value === "empty") {
2433
+ this.next();
2434
+ this.expect("LPAREN");
2435
+ const variable = this.expect("VARIABLE").value;
2436
+ this.expect("RPAREN");
2437
+ return { kind: "empty", variable };
2438
+ }
2439
+ if (token.type === "IDENT" && token.value === "xff:has-fallback-value") {
2440
+ this.next();
2441
+ this.expect("LPAREN");
2442
+ const inner = this.next();
2443
+ if (inner.type !== "IDENT" || inner.value !== "xs:QName") {
2444
+ throw new FormulaExpressionError(
2445
+ `Expected xs:QName(...) inside xff:has-fallback-value(...), got "${inner.value}"`,
2446
+ this.test,
2447
+ "unsupported-syntax"
2448
+ );
2449
+ }
2450
+ this.expect("LPAREN");
2451
+ const variableName = this.expect("STRING").value;
2452
+ this.expect("RPAREN");
2453
+ this.expect("RPAREN");
2454
+ return { kind: "hasFallbackValue", variableName };
2455
+ }
2456
+ const left = this.parseValueExpr();
2457
+ const opToken = this.peek();
2458
+ if (opToken.type === "EQUALS") {
2459
+ this.next();
2460
+ return { kind: "comparison", op: "=", left, right: this.parseValueExpr() };
2461
+ }
2462
+ if (opToken.type === "IDENT" && opToken.value === "eq") {
2463
+ this.next();
2464
+ return { kind: "comparison", op: "eq", left, right: this.parseValueExpr() };
2465
+ }
2466
+ throw new FormulaExpressionError(
2467
+ `Expected a comparison operator ("=" or "eq") after value expression, got "${opToken.value}"`,
2468
+ this.test,
2469
+ "unsupported-syntax"
2470
+ );
2471
+ }
2472
+ parseValueExpr() {
2473
+ const terms = [];
2474
+ let sign = 1;
2475
+ if (this.peek().type === "PLUS") {
2476
+ this.next();
2477
+ } else if (this.peek().type === "MINUS") {
2478
+ this.next();
2479
+ sign = -1;
2480
+ }
2481
+ terms.push({ sign, term: this.parseValueTerm() });
2482
+ while (this.peek().type === "PLUS" || this.peek().type === "MINUS") {
2483
+ const opSign = this.next().type === "PLUS" ? 1 : -1;
2484
+ terms.push({ sign: opSign, term: this.parseValueTerm() });
2485
+ }
2486
+ return terms.length === 1 && terms[0].sign === 1 ? terms[0].term : { kind: "additive", terms };
2487
+ }
2488
+ parseValueTerm() {
2489
+ const token = this.peek();
2490
+ if (token.type === "NUMBER") {
2491
+ this.next();
2492
+ return { kind: "number", value: Number(token.value) };
2493
+ }
2494
+ if (token.type === "VARIABLE") {
2495
+ this.next();
2496
+ return { kind: "variable", name: token.value };
2497
+ }
2498
+ if (token.type === "IDENT" && token.value === "sum") {
2499
+ this.next();
2500
+ this.expect("LPAREN");
2501
+ const variable = this.expect("VARIABLE").value;
2502
+ this.expect("RPAREN");
2503
+ return { kind: "sum", variable };
2504
+ }
2505
+ if (token.type === "LPAREN") {
2506
+ this.next();
2507
+ const inner = this.parseValueExpr();
2508
+ this.expect("RPAREN");
2509
+ return inner;
2510
+ }
2511
+ throw new FormulaExpressionError(
2512
+ `Expected a number, variable, sum(...), or parenthesized expression, got "${token.value}"`,
2513
+ this.test,
2514
+ "unsupported-syntax"
2515
+ );
2516
+ }
2517
+ }
2518
+ function lookup(bindings, name) {
2519
+ return bindings.get(name);
2520
+ }
2521
+ function evalSum(variable, bindings) {
2522
+ const binding = lookup(bindings, variable);
2523
+ if (binding === void 0)
2524
+ return 0;
2525
+ if (Array.isArray(binding))
2526
+ return binding.reduce((total, value) => total + value, 0);
2527
+ return binding;
2528
+ }
2529
+ function evalValue(node, bindings, test) {
2530
+ switch (node.kind) {
2531
+ case "number":
2532
+ return node.value;
2533
+ case "sum":
2534
+ return evalSum(node.variable, bindings);
2535
+ case "variable": {
2536
+ const binding = lookup(bindings, node.name);
2537
+ if (binding === void 0) {
2538
+ throw new FormulaExpressionError(
2539
+ `Variable "$${node.name}" has no bound fact and cannot be evaluated directly (only sum()/empty() tolerate an unbound variable)`,
2540
+ test,
2541
+ "unbound-variable"
2542
+ );
2543
+ }
2544
+ if (Array.isArray(binding)) {
2545
+ throw new FormulaExpressionError(
2546
+ `Variable "$${node.name}" is a sequence and must be wrapped in sum(...) to be used in arithmetic`,
2547
+ test,
2548
+ "unsupported-syntax"
2549
+ );
2550
+ }
2551
+ return binding;
2552
+ }
2553
+ case "additive":
2554
+ return node.terms.reduce((total, { sign, term }) => total + sign * evalValue(term, bindings, test), 0);
2555
+ }
2556
+ }
2557
+ function evalBool(node, bindings, test, fallbackUsage) {
2558
+ switch (node.kind) {
2559
+ case "or":
2560
+ return node.terms.some((term) => evalBool(term, bindings, test, fallbackUsage));
2561
+ case "and":
2562
+ return node.terms.every((term) => evalBool(term, bindings, test, fallbackUsage));
2563
+ case "not":
2564
+ return !evalBool(node.arg, bindings, test, fallbackUsage);
2565
+ case "empty": {
2566
+ const binding = lookup(bindings, node.variable);
2567
+ return binding === void 0 || Array.isArray(binding) && binding.length === 0;
2568
+ }
2569
+ case "hasFallbackValue": {
2570
+ const usedFallback = fallbackUsage.get(node.variableName);
2571
+ if (usedFallback === void 0) {
2572
+ throw new FormulaExpressionError(
2573
+ `xff:has-fallback-value(xs:QName('${node.variableName}')) could not be evaluated — no fallback-usage tracked for this variable (either not one of this assertion's FACT_VARIABLEs, or it matched zero facts with no fallbackValue declared)`,
2574
+ test,
2575
+ "unbound-variable"
2576
+ );
2577
+ }
2578
+ return usedFallback;
2579
+ }
2580
+ case "comparison": {
2581
+ const left = evalValue(node.left, bindings, test);
2582
+ const right = evalValue(node.right, bindings, test);
2583
+ return Math.abs(left - right) < CALCULATION_TOLERANCE;
2584
+ }
2585
+ }
2586
+ }
2587
+ function evaluateFormulaTest(test, bindings, fallbackUsage = /* @__PURE__ */ new Map()) {
2588
+ const tokens = tokenize(test);
2589
+ const ast = new Parser(tokens, test).parseBoolExpr();
2590
+ return evalBool(ast, bindings, test, fallbackUsage);
2591
+ }
2592
+ const EXISTENCE_TEST_PATTERN = /^\.\s*(eq|ne|gt|lt|ge|le)\s+(-?\d+(?:\.\d+)?)$/;
2593
+ function compareExistence(op, count, threshold) {
2594
+ switch (op) {
2595
+ case "eq":
2596
+ return count === threshold;
2597
+ case "ne":
2598
+ return count !== threshold;
2599
+ case "gt":
2600
+ return count > threshold;
2601
+ case "lt":
2602
+ return count < threshold;
2603
+ case "ge":
2604
+ return count >= threshold;
2605
+ case "le":
2606
+ return count <= threshold;
2607
+ }
2608
+ }
2609
+ function evaluateExistenceTest(test, matchedFactCount) {
2610
+ const trimmed = (test ?? "").trim();
2611
+ if (trimmed === "") {
2612
+ return matchedFactCount > 0;
2613
+ }
2614
+ const clauses = trimmed.split(/\bor\b/);
2615
+ return clauses.some((clause) => {
2616
+ const match = EXISTENCE_TEST_PATTERN.exec(clause.trim());
2617
+ if (!match) {
2618
+ throw new FormulaExpressionError(`Unsupported existence-assertion test shape: "${clause.trim()}"`, trimmed, "unsupported-syntax");
2619
+ }
2620
+ const [, op, thresholdText] = match;
2621
+ return compareExistence(op, matchedFactCount, Number(thresholdText));
2622
+ });
2623
+ }
2624
+ function isTotalLabel(preferredLabel) {
2625
+ return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("totallabel"));
2626
+ }
2627
+ function isNegatedTotalLabel(preferredLabel) {
2628
+ return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("negatedtotallabel"));
2629
+ }
2630
+ function resolveValueNode(node, accessors, depth = 0) {
2631
+ if (!accessors.isAbstract(node))
2632
+ return node;
2633
+ if (depth > 5)
2634
+ return null;
2635
+ const children = accessors.getChildren(node);
2636
+ const nestedTotal = children.find((child) => isTotalLabel(accessors.getPreferredLabel(child)));
2637
+ if (!nestedTotal)
2638
+ return null;
2639
+ return resolveValueNode(nestedTotal, accessors, depth + 1);
2640
+ }
2641
+ function resolveTotalGroupMembers(nodes, i2, lastTotalIdx, rawSiblings, accessors) {
2642
+ if (lastTotalIdx < 0)
2643
+ return rawSiblings;
2644
+ const node = nodes[i2];
2645
+ if (isNegatedTotalLabel(accessors.getPreferredLabel(node))) {
2646
+ return rawSiblings.length > 0 ? rawSiblings : [nodes[lastTotalIdx]];
2647
+ }
2648
+ if (rawSiblings.length > 0) {
2649
+ return [nodes[lastTotalIdx], ...rawSiblings];
2650
+ }
2651
+ if (isNegatedTotalLabel(accessors.getPreferredLabel(nodes[lastTotalIdx]))) {
2652
+ let grandTotalIdx = -1;
2653
+ for (let k = lastTotalIdx - 1; k >= 0; k--) {
2654
+ if (isTotalLabel(accessors.getPreferredLabel(nodes[k]))) {
2655
+ grandTotalIdx = k;
2656
+ break;
2657
+ }
2658
+ }
2659
+ return grandTotalIdx >= 0 ? [nodes[grandTotalIdx], nodes[lastTotalIdx]] : [nodes[lastTotalIdx]];
2660
+ }
2661
+ return [nodes[lastTotalIdx]];
2662
+ }
2663
+ function buildTotalGroupChildrenIndex(roots, accessors) {
2664
+ const index = /* @__PURE__ */ new Map();
2665
+ function registerGroup(totalNode, members) {
2666
+ const resolvedIds = [];
2667
+ for (const member of members) {
2668
+ const resolved = resolveValueNode(member, accessors);
2669
+ if (resolved)
2670
+ resolvedIds.push(accessors.getId(resolved));
2671
+ }
2672
+ if (resolvedIds.length === 0)
2673
+ return;
2674
+ index.set(accessors.getId(totalNode), resolvedIds);
2675
+ }
2676
+ function traverse(nodes) {
2677
+ for (let i2 = 0; i2 < nodes.length; i2++) {
2678
+ const node = nodes[i2];
2679
+ if (isTotalLabel(accessors.getPreferredLabel(node))) {
2680
+ const children2 = accessors.getChildren(node);
2681
+ if (children2.length === 0) {
2682
+ let lastTotalIdx = -1;
2683
+ for (let j = i2 - 1; j >= 0; j--) {
2684
+ if (isTotalLabel(accessors.getPreferredLabel(nodes[j]))) {
2685
+ lastTotalIdx = j;
2686
+ break;
2687
+ }
2688
+ }
2689
+ const rawSiblings = nodes.slice(lastTotalIdx + 1, i2);
2690
+ const members = resolveTotalGroupMembers(nodes, i2, lastTotalIdx, rawSiblings, accessors);
2691
+ if (members.length > 0)
2692
+ registerGroup(node, members);
2693
+ }
2694
+ }
2695
+ const children = accessors.getChildren(node);
2696
+ if (children.length > 0)
2697
+ traverse(children);
2698
+ }
2699
+ }
2700
+ traverse(roots);
2701
+ return index;
2702
+ }
2703
+ function isNonEmptyRawValue(rawValue) {
2704
+ return rawValue !== void 0 && rawValue !== null && rawValue !== "";
2705
+ }
2706
+ function collectAllCandidates(formData) {
2707
+ const candidates = [];
2708
+ for (const conceptId of Object.keys(formData)) {
2709
+ const columnValues = formData[conceptId] || {};
2710
+ for (const columnId of Object.keys(columnValues)) {
2711
+ if (isNonEmptyRawValue(columnValues[columnId])) {
2712
+ candidates.push({ conceptId, columnId });
2713
+ }
2714
+ }
2715
+ }
2716
+ return candidates;
2717
+ }
2718
+ function indexConceptTree(concept, conceptByQName, conceptById, childrenIndex) {
2719
+ const info = {
2720
+ conceptId: concept.id,
2721
+ qname: concept.conceptName,
2722
+ balance: concept.balance
2723
+ };
2724
+ if (!conceptById.has(concept.id))
2725
+ conceptById.set(concept.id, info);
2726
+ if (!conceptByQName.has(concept.conceptName))
2727
+ conceptByQName.set(concept.conceptName, info);
2728
+ const children = concept.children || [];
2729
+ childrenIndex.set(
2730
+ concept.id,
2731
+ children.map((child) => child.id)
2732
+ );
2733
+ children.forEach((child) => indexConceptTree(child, conceptByQName, conceptById, childrenIndex));
2734
+ }
2735
+ function indexMemberTree(member, memberQNameToId) {
2736
+ if (!memberQNameToId.has(member.conceptName))
2737
+ memberQNameToId.set(member.conceptName, member.id);
2738
+ (member.children || []).forEach((child) => indexMemberTree(child, memberQNameToId));
2739
+ }
2740
+ const PRESENTATION_TOTAL_GROUP_ACCESSORS = {
2741
+ getId: (concept) => concept.id,
2742
+ getPreferredLabel: (concept) => concept.preferredLabel,
2743
+ isAbstract: (concept) => concept.elementAbstract,
2744
+ getChildren: (concept) => concept.children || []
2745
+ };
2746
+ function buildFormulaResolutionContext(formData, columns, presentationRoles, hypercubeRoles, periodStartDate, periodEndDate) {
2747
+ const conceptByQName = /* @__PURE__ */ new Map();
2748
+ const conceptById = /* @__PURE__ */ new Map();
2749
+ const childrenByRole = /* @__PURE__ */ new Map();
2750
+ const totalGroupChildrenByRole = /* @__PURE__ */ new Map();
2751
+ presentationRoles.forEach((role) => {
2752
+ var _a;
2753
+ const concepts = ((_a = role.presentationLinkbase) == null ? void 0 : _a.concepts) || [];
2754
+ const childrenIndex = /* @__PURE__ */ new Map();
2755
+ concepts.forEach((concept) => indexConceptTree(concept, conceptByQName, conceptById, childrenIndex));
2756
+ childrenByRole.set(role.roleURI, childrenIndex);
2757
+ totalGroupChildrenByRole.set(role.roleURI, buildTotalGroupChildrenIndex(concepts, PRESENTATION_TOTAL_GROUP_ACCESSORS));
2758
+ });
2759
+ const dimensionQNameToId = /* @__PURE__ */ new Map();
2760
+ const memberQNameToId = /* @__PURE__ */ new Map();
2761
+ (hypercubeRoles || []).forEach((role) => {
2762
+ role.items.forEach((item) => {
2763
+ item.dimensions.forEach((dimension) => {
2764
+ if (!dimensionQNameToId.has(dimension.conceptName)) {
2765
+ dimensionQNameToId.set(dimension.conceptName, dimension.id);
2766
+ }
2767
+ (dimension.members || []).forEach((member) => indexMemberTree(member, memberQNameToId));
2768
+ });
2769
+ });
2770
+ });
2771
+ return {
2772
+ formData,
2773
+ columns,
2774
+ periodStartDate,
2775
+ periodEndDate,
2776
+ conceptByQName,
2777
+ conceptById,
2778
+ childrenByRole,
2779
+ totalGroupChildrenByRole,
2780
+ dimensionQNameToId,
2781
+ memberQNameToId,
2782
+ allCandidates: collectAllCandidates(formData)
2783
+ };
2784
+ }
2785
+ function collectDescendantConceptIds(childrenIndex, rootConceptId) {
2786
+ const result = [];
2787
+ const stack = [...childrenIndex.get(rootConceptId) || []];
2788
+ const seen = /* @__PURE__ */ new Set();
2789
+ while (stack.length > 0) {
2790
+ const conceptId = stack.pop();
2791
+ if (seen.has(conceptId))
2792
+ continue;
2793
+ seen.add(conceptId);
2794
+ result.push(conceptId);
2795
+ stack.push(...childrenIndex.get(conceptId) || []);
2796
+ }
2797
+ return result;
2798
+ }
2799
+ function candidateKey(candidate) {
2800
+ return `${candidate.conceptId}::${candidate.columnId}`;
2801
+ }
2802
+ function resolveMatchedCandidates(variable, ctx) {
2803
+ return intersectCandidates(variable.filters.map((filter2) => resolveFilter(filter2, ctx.allCandidates, ctx)));
2804
+ }
2805
+ function getColumn(ctx, columnId) {
2806
+ return ctx.columns.find((column2) => column2.id === columnId);
2807
+ }
2808
+ function columnMatchesExplicitDimension(column2, axisId, memberId) {
2809
+ if (!(column2 == null ? void 0 : column2.dimensionData))
2810
+ return false;
2811
+ const primaryMatch = column2.dimensionData.axisId === axisId && (memberId === void 0 || column2.dimensionData.memberId === memberId);
2812
+ if (primaryMatch)
2813
+ return true;
2814
+ return (column2.dimensionData.combinations || []).some(
2815
+ (combo) => combo.axisId === axisId && (memberId === void 0 || combo.memberId === memberId)
2816
+ );
2817
+ }
2818
+ function columnMatchesTypedDimension(column2, axisId) {
2819
+ if (!(column2 == null ? void 0 : column2.dimensionData))
2820
+ return false;
2821
+ if (column2.dimensionData.typedMemberId !== void 0 && column2.dimensionData.axisId === axisId)
2822
+ return true;
2823
+ return (column2.dimensionData.typedMembers || []).some((typedMember) => typedMember.axisId === axisId);
2824
+ }
2825
+ function paramTokenName(rawDate) {
2826
+ if (!rawDate)
2827
+ return void 0;
2828
+ return rawDate.startsWith("$") ? rawDate.slice(1) : rawDate;
2829
+ }
2830
+ function resolveParamDate(paramToken, ctx) {
2831
+ const isStart = paramToken.includes("StartDate");
2832
+ const isEnd = paramToken.includes("EndDate") || paramToken === "FinancialReportingPeriodCurrentEndDateParam";
2833
+ if (paramToken.startsWith("FinancialReportingPeriodCurrent")) {
2834
+ return isStart ? { start: ctx.periodStartDate } : isEnd ? { end: ctx.periodEndDate } : void 0;
2835
+ }
2836
+ if (paramToken.startsWith("FinancialReportingPeriodPrePrevious") || paramToken.startsWith("FinancialReportingPeriodPrevious")) {
2837
+ const previousYearColumn = findPreviousYearColumn(ctx);
2838
+ if (!previousYearColumn)
2839
+ return void 0;
2840
+ return isStart ? { start: previousYearColumn.periodStartDate } : isEnd ? { end: previousYearColumn.periodEndDate } : void 0;
2841
+ }
2842
+ return void 0;
2843
+ }
2844
+ function findPreviousYearColumn(ctx) {
2845
+ return ctx.columns.find((column2) => column2.periodEndDate !== void 0 && column2.periodEndDate < ctx.periodEndDate);
2846
+ }
2847
+ function columnEffectivePeriod(column2, ctx) {
2848
+ return {
2849
+ start: (column2 == null ? void 0 : column2.periodStartDate) ?? ctx.periodStartDate,
2850
+ end: (column2 == null ? void 0 : column2.periodEndDate) ?? ctx.periodEndDate
2851
+ };
2852
+ }
2853
+ function columnMatchesPeriod(column2, dateAttribute, ctx) {
2854
+ const paramToken = paramTokenName(dateAttribute);
2855
+ if (!paramToken)
2856
+ return false;
2857
+ const resolved = resolveParamDate(paramToken, ctx);
2858
+ if (!resolved)
2859
+ return false;
2860
+ const period = columnEffectivePeriod(column2, ctx);
2861
+ const targetDate = resolved.end ?? resolved.start;
2862
+ return period.end === targetDate;
2863
+ }
2864
+ function unionCandidates(lists) {
2865
+ const merged = /* @__PURE__ */ new Map();
2866
+ lists.forEach((list) => list.forEach((candidate) => merged.set(candidateKey(candidate), candidate)));
2867
+ return Array.from(merged.values());
2868
+ }
2869
+ function matchFilter(filter2, universe, ctx) {
2870
+ var _a;
2871
+ switch (filter2.type) {
2872
+ case "CONCEPT_NAME": {
2873
+ const qname = filter2.attributes["concept.qname"];
2874
+ const info = qname ? ctx.conceptByQName.get(qname) : void 0;
2875
+ if (!info)
2876
+ return [];
2877
+ return universe.filter((candidate) => candidate.conceptId === info.conceptId);
2878
+ }
2879
+ case "CONCEPT_BALANCE": {
2880
+ const balance = filter2.attributes["balance"];
2881
+ return universe.filter((candidate) => {
2882
+ var _a2;
2883
+ return ((_a2 = ctx.conceptById.get(candidate.conceptId)) == null ? void 0 : _a2.balance) === balance;
2884
+ });
2885
+ }
2886
+ case "CONCEPT_RELATION": {
2887
+ const anchorQName = filter2.attributes["qname"];
2888
+ const linkrole = filter2.attributes["linkrole"];
2889
+ const axis = filter2.attributes["axis"];
2890
+ const anchorInfo = anchorQName ? ctx.conceptByQName.get(anchorQName) : void 0;
2891
+ const childrenIndex = linkrole ? ctx.childrenByRole.get(linkrole) : void 0;
2892
+ if (!anchorInfo || !childrenIndex)
2893
+ return [];
2894
+ let relatedConceptIds;
2895
+ if (axis === "descendant") {
2896
+ relatedConceptIds = collectDescendantConceptIds(childrenIndex, anchorInfo.conceptId);
2897
+ } else {
2898
+ const nestedChildren = childrenIndex.get(anchorInfo.conceptId) || [];
2899
+ relatedConceptIds = nestedChildren.length > 0 ? nestedChildren : ((_a = ctx.totalGroupChildrenByRole.get(linkrole ?? "")) == null ? void 0 : _a.get(anchorInfo.conceptId)) || [];
2900
+ }
2901
+ const relatedSet = new Set(relatedConceptIds);
2902
+ return universe.filter((candidate) => relatedSet.has(candidate.conceptId));
2903
+ }
2904
+ case "EXPLICIT_DIMENSION": {
2905
+ const dimensionQName = filter2.attributes["dimension.qname"];
2906
+ const memberQName = filter2.attributes["member.qname"];
2907
+ const axisId = dimensionQName ? ctx.dimensionQNameToId.get(dimensionQName) : void 0;
2908
+ const memberId = memberQName ? ctx.memberQNameToId.get(memberQName) : void 0;
2909
+ if (!axisId)
2910
+ return [];
2911
+ return universe.filter((candidate) => columnMatchesExplicitDimension(getColumn(ctx, candidate.columnId), axisId, memberId));
2912
+ }
2913
+ case "TYPED_DIMENSION": {
2914
+ const dimensionQName = filter2.attributes["dimension.qname"];
2915
+ const axisId = dimensionQName ? ctx.dimensionQNameToId.get(dimensionQName) : void 0;
2916
+ if (!axisId)
2917
+ return [];
2918
+ return universe.filter((candidate) => columnMatchesTypedDimension(getColumn(ctx, candidate.columnId), axisId));
2919
+ }
2920
+ case "PERIOD_INSTANT":
2921
+ case "PERIOD_END": {
2922
+ const dateAttribute = filter2.attributes["date"];
2923
+ return universe.filter((candidate) => columnMatchesPeriod(getColumn(ctx, candidate.columnId), dateAttribute, ctx));
2924
+ }
2925
+ case "OR_FILTER":
2926
+ return unionCandidates(filter2.children.map((child) => resolveFilter(child, universe, ctx)));
2927
+ default:
2928
+ return [];
2929
+ }
2930
+ }
2931
+ function resolveFilter(filter2, universe, ctx) {
2932
+ const matched = matchFilter(filter2, universe, ctx);
2933
+ if (!filter2.complement)
2934
+ return matched;
2935
+ const matchedKeys = new Set(matched.map(candidateKey));
2936
+ return universe.filter((candidate) => !matchedKeys.has(candidateKey(candidate)));
2937
+ }
2938
+ function intersectCandidates(sets) {
2939
+ if (sets.length === 0)
2940
+ return [];
2941
+ return sets.reduce((acc, set) => {
2942
+ const setKeys = new Set(set.map(candidateKey));
2943
+ return acc.filter((candidate) => setKeys.has(candidateKey(candidate)));
2944
+ });
2945
+ }
2946
+ function resolveFormulaVariable(variable, ctx) {
2947
+ const matched = resolveMatchedCandidates(variable, ctx);
2948
+ const values = matched.map((candidate) => {
2949
+ var _a;
2950
+ return parseFloat((_a = ctx.formData[candidate.conceptId]) == null ? void 0 : _a[candidate.columnId]);
2951
+ }).filter((value) => !Number.isNaN(value));
2952
+ if (variable.bindAsSequence) {
2953
+ if (values.length === 0 && variable.fallbackValue === "()")
2954
+ return [];
2955
+ if (values.length === 0)
2956
+ return void 0;
2957
+ return values;
2958
+ }
2959
+ if (values.length === 0)
2960
+ return void 0;
2961
+ if (values.length > 1) {
2962
+ console.warn(
2963
+ `[formula-variable-resolver] Variable "${variable.name}" is scalar (bindAsSequence: false) but matched ${values.length} facts; using the first match by column order. This can legitimately happen when filters under-constrain the candidate set.`
2964
+ );
2965
+ }
2966
+ return values[0];
2967
+ }
2968
+ function resolveFormulaVariableMatchCount(variable, ctx) {
2969
+ return resolveMatchedCandidates(variable, ctx).length;
2970
+ }
2971
+ function resolveFormulaVariableUsedFallback(variable, ctx) {
2972
+ const matchCount = resolveFormulaVariableMatchCount(variable, ctx);
2973
+ if (matchCount > 0)
2974
+ return false;
2975
+ if (variable.fallbackValue !== void 0)
2976
+ return true;
2977
+ return void 0;
2978
+ }
2979
+ function buildAssertionBindings(assertion, ctx) {
2980
+ const bindings = /* @__PURE__ */ new Map();
2981
+ for (const variable of assertion.variables) {
2982
+ bindings.set(variable.name, resolveFormulaVariable(variable, ctx));
2983
+ }
2984
+ return bindings;
2985
+ }
2986
+ function buildAssertionFallbackUsage(assertion, ctx) {
2987
+ var _a;
2988
+ const usesHasFallbackValue = ((_a = assertion.test) == null ? void 0 : _a.includes("has-fallback-value")) || assertion.preconditions.some((precondition) => precondition.test.includes("has-fallback-value"));
2989
+ if (!usesHasFallbackValue)
2990
+ return /* @__PURE__ */ new Map();
2991
+ const fallbackUsage = /* @__PURE__ */ new Map();
2992
+ for (const variable of assertion.variables) {
2993
+ const usedFallback = resolveFormulaVariableUsedFallback(variable, ctx);
2994
+ if (usedFallback !== void 0)
2995
+ fallbackUsage.set(variable.name, usedFallback);
2996
+ }
2997
+ return fallbackUsage;
2998
+ }
2999
+ function evaluateAssertionPreconditions(preconditions, bindings, fallbackUsage = /* @__PURE__ */ new Map()) {
3000
+ if (preconditions.length === 0)
3001
+ return { satisfied: true };
3002
+ for (const precondition of preconditions) {
3003
+ let result;
3004
+ try {
3005
+ result = evaluateFormulaTest(precondition.test, bindings, fallbackUsage);
3006
+ } catch (error2) {
3007
+ if (error2 instanceof FormulaExpressionError) {
3008
+ return { satisfied: false, skipReason: "unresolvable" };
3009
+ }
3010
+ throw error2;
3011
+ }
3012
+ if (!result) {
3013
+ return { satisfied: false, skipReason: "precondition" };
3014
+ }
3015
+ }
3016
+ return { satisfied: true };
3017
+ }
3018
+ class FormulaValidationService {
3019
+ /**
3020
+ * Evaluates every assertion in `xbrlInput.formula[0].roles` (only the first entrypoint,
3021
+ * consistent with how xbrl-form-builder.ts uses presentation/hypercube data) against the
3022
+ * given resolution context. One bad assertion never aborts the other 783 — resolution/
3023
+ * evaluation errors are caught per-assertion and recorded as skipped, not thrown.
3024
+ */
3025
+ evaluate(xbrlInput, ctx, language) {
3026
+ var _a;
3027
+ const formulaData = (_a = xbrlInput.formula) == null ? void 0 : _a[0];
3028
+ if (!formulaData)
3029
+ return [];
3030
+ const results = [];
3031
+ for (const role of formulaData.roles) {
3032
+ for (const assertion of role.assertions) {
3033
+ results.push(this._evaluateAssertion(assertion, role, ctx, language));
3034
+ }
3035
+ }
3036
+ return results;
3037
+ }
3038
+ summarize(results) {
3039
+ const summary = {
3040
+ totalAssertions: results.length,
3041
+ evaluated: 0,
3042
+ skipped: 0,
3043
+ passed: 0,
3044
+ failed: 0,
3045
+ failedWarnings: 0,
3046
+ failedErrors: 0
3047
+ };
3048
+ for (const result of results) {
3049
+ if (result.skipped) {
3050
+ summary.skipped += 1;
3051
+ continue;
3052
+ }
3053
+ summary.evaluated += 1;
3054
+ if (result.satisfied) {
3055
+ summary.passed += 1;
3056
+ } else {
3057
+ summary.failed += 1;
3058
+ if (result.severity === "ERROR")
3059
+ summary.failedErrors += 1;
3060
+ else
3061
+ summary.failedWarnings += 1;
3062
+ }
3063
+ }
3064
+ return summary;
3065
+ }
3066
+ _evaluateAssertion(assertion, role, ctx, language) {
3067
+ try {
3068
+ const bindings = buildAssertionBindings(assertion, ctx);
3069
+ const fallbackUsage = buildAssertionFallbackUsage(assertion, ctx);
3070
+ const precondition = evaluateAssertionPreconditions(assertion.preconditions, bindings, fallbackUsage);
3071
+ if (!precondition.satisfied) {
3072
+ return this._skippedResult(assertion, role, precondition.skipReason ?? "unresolvable");
3073
+ }
3074
+ if (assertion.type === "EXISTENCE_ASSERTION") {
3075
+ const matchedFactCount = resolveFormulaVariableMatchCount(assertion.variables[0], ctx);
3076
+ const satisfied2 = evaluateExistenceTest(assertion.test, matchedFactCount);
3077
+ return this._evaluatedResult(assertion, role, satisfied2, language, matchedFactCount);
3078
+ }
3079
+ const satisfied = evaluateFormulaTest(assertion.test, bindings, fallbackUsage);
3080
+ return this._evaluatedResult(assertion, role, satisfied, language);
3081
+ } catch (error2) {
3082
+ if (error2 instanceof FormulaExpressionError) {
3083
+ console.warn(`[FormulaValidationService] Assertion "${assertion.id}" could not be evaluated (${error2.reason}): ${error2.message}`);
3084
+ return this._skippedResult(assertion, role, "unresolvable");
3085
+ }
3086
+ throw error2;
3087
+ }
3088
+ }
3089
+ _skippedResult(assertion, role, skipReason) {
3090
+ return {
3091
+ assertionId: assertion.id,
3092
+ roleURI: role.roleURI,
3093
+ roleLabel: role.role,
3094
+ type: assertion.type,
3095
+ severity: assertion.severity,
3096
+ satisfied: false,
3097
+ skipped: true,
3098
+ skipReason
3099
+ };
3100
+ }
3101
+ _evaluatedResult(assertion, role, satisfied, language, matchedFactCount) {
3102
+ return {
3103
+ assertionId: assertion.id,
3104
+ roleURI: role.roleURI,
3105
+ roleLabel: role.role,
3106
+ type: assertion.type,
3107
+ severity: assertion.severity,
3108
+ satisfied,
3109
+ skipped: false,
3110
+ message: satisfied ? void 0 : this._pickMessage(assertion, language),
3111
+ matchedFactCount
3112
+ };
3113
+ }
3114
+ _pickMessage(assertion, language) {
3115
+ const messages = assertion.messages;
3116
+ if (messages.length === 0)
3117
+ return void 0;
3118
+ const message = messages.find((m) => m.xmlLang === language) ?? messages.find((m) => m.xmlLang === "en") ?? messages[0];
3119
+ return message.text;
3120
+ }
3121
+ }
2303
3122
  class FactMatcher {
2304
3123
  /**
2305
3124
  * Find a matching fact for a given cell context
@@ -3153,15 +3972,15 @@ function determineInputTypeFromBaseChain(baseTypeChain, datatypes) {
3153
3972
  placeholder: I18n.t("field.enterValue")
3154
3973
  };
3155
3974
  }
3156
- var __defProp$6 = Object.defineProperty;
3157
- var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
3158
- var __decorateClass$6 = (decorators, target, key, kind) => {
3159
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
3975
+ var __defProp$7 = Object.defineProperty;
3976
+ var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
3977
+ var __decorateClass$7 = (decorators, target, key, kind) => {
3978
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
3160
3979
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
3161
3980
  if (decorator = decorators[i2])
3162
3981
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
3163
3982
  if (kind && result)
3164
- __defProp$6(target, key, result);
3983
+ __defProp$7(target, key, result);
3165
3984
  return result;
3166
3985
  };
3167
3986
  let JupiterFormField = class extends LitElement {
@@ -4178,6 +4997,7 @@ let JupiterFormField = class extends LitElement {
4178
4997
  <button
4179
4998
  class="period-icon-btn"
4180
4999
  type="button"
5000
+ tabindex="-1"
4181
5001
  @click="${this._handlePeriodIconClick}"
4182
5002
  title="Edit period"
4183
5003
  >
@@ -4674,99 +5494,99 @@ JupiterFormField.styles = css`
4674
5494
  animation: concept-highlight-pulse 10s ease-out forwards;
4675
5495
  }
4676
5496
  `;
4677
- __decorateClass$6([
5497
+ __decorateClass$7([
4678
5498
  n2({ type: Object })
4679
5499
  ], JupiterFormField.prototype, "field", 2);
4680
- __decorateClass$6([
5500
+ __decorateClass$7([
4681
5501
  n2({ type: String })
4682
5502
  ], JupiterFormField.prototype, "conceptId", 2);
4683
- __decorateClass$6([
5503
+ __decorateClass$7([
4684
5504
  n2({ type: String })
4685
5505
  ], JupiterFormField.prototype, "conceptType", 2);
4686
- __decorateClass$6([
5506
+ __decorateClass$7([
4687
5507
  n2({ type: Array })
4688
5508
  ], JupiterFormField.prototype, "datatypes", 2);
4689
- __decorateClass$6([
5509
+ __decorateClass$7([
4690
5510
  n2({ type: Array })
4691
5511
  ], JupiterFormField.prototype, "defaultUnits", 2);
4692
- __decorateClass$6([
5512
+ __decorateClass$7([
4693
5513
  n2({ type: String })
4694
5514
  ], JupiterFormField.prototype, "columnId", 2);
4695
- __decorateClass$6([
5515
+ __decorateClass$7([
4696
5516
  n2()
4697
5517
  ], JupiterFormField.prototype, "value", 2);
4698
- __decorateClass$6([
5518
+ __decorateClass$7([
4699
5519
  n2({ type: Boolean })
4700
5520
  ], JupiterFormField.prototype, "disabled", 2);
4701
- __decorateClass$6([
5521
+ __decorateClass$7([
4702
5522
  n2({ type: String })
4703
5523
  ], JupiterFormField.prototype, "locale", 2);
4704
- __decorateClass$6([
5524
+ __decorateClass$7([
4705
5525
  n2({ type: Boolean })
4706
5526
  ], JupiterFormField.prototype, "hideLabel", 2);
4707
- __decorateClass$6([
5527
+ __decorateClass$7([
4708
5528
  n2({ type: String })
4709
5529
  ], JupiterFormField.prototype, "mode", 2);
4710
- __decorateClass$6([
5530
+ __decorateClass$7([
4711
5531
  n2({ type: Object })
4712
5532
  ], JupiterFormField.prototype, "masterData", 2);
4713
- __decorateClass$6([
5533
+ __decorateClass$7([
4714
5534
  n2({ type: Array })
4715
5535
  ], JupiterFormField.prototype, "facts", 2);
4716
- __decorateClass$6([
5536
+ __decorateClass$7([
4717
5537
  n2({ type: Object })
4718
5538
  ], JupiterFormField.prototype, "column", 2);
4719
- __decorateClass$6([
5539
+ __decorateClass$7([
4720
5540
  n2({ type: String })
4721
5541
  ], JupiterFormField.prototype, "periodStartDate", 2);
4722
- __decorateClass$6([
5542
+ __decorateClass$7([
4723
5543
  n2({ type: String })
4724
5544
  ], JupiterFormField.prototype, "periodEndDate", 2);
4725
- __decorateClass$6([
5545
+ __decorateClass$7([
4726
5546
  n2({ type: String })
4727
5547
  ], JupiterFormField.prototype, "periodInstantDate", 2);
4728
- __decorateClass$6([
5548
+ __decorateClass$7([
4729
5549
  n2({ type: String })
4730
5550
  ], JupiterFormField.prototype, "unit", 2);
4731
- __decorateClass$6([
5551
+ __decorateClass$7([
4732
5552
  n2({ type: String })
4733
5553
  ], JupiterFormField.prototype, "decimals", 2);
4734
- __decorateClass$6([
5554
+ __decorateClass$7([
4735
5555
  n2({ type: String })
4736
5556
  ], JupiterFormField.prototype, "globalDecimals", 2);
4737
- __decorateClass$6([
5557
+ __decorateClass$7([
4738
5558
  n2({ type: Object })
4739
5559
  ], JupiterFormField.prototype, "typedMemberValues", 2);
4740
- __decorateClass$6([
5560
+ __decorateClass$7([
4741
5561
  r()
4742
5562
  ], JupiterFormField.prototype, "_errors", 2);
4743
- __decorateClass$6([
5563
+ __decorateClass$7([
4744
5564
  r()
4745
5565
  ], JupiterFormField.prototype, "_xbrlErrors", 2);
4746
- __decorateClass$6([
5566
+ __decorateClass$7([
4747
5567
  r()
4748
5568
  ], JupiterFormField.prototype, "_touched", 2);
4749
- __decorateClass$6([
5569
+ __decorateClass$7([
4750
5570
  r()
4751
5571
  ], JupiterFormField.prototype, "_showPeriodPopup", 2);
4752
- __decorateClass$6([
5572
+ __decorateClass$7([
4753
5573
  r()
4754
5574
  ], JupiterFormField.prototype, "_availableUnits", 2);
4755
- __decorateClass$6([
5575
+ __decorateClass$7([
4756
5576
  r()
4757
5577
  ], JupiterFormField.prototype, "_numericDraftValue", 2);
4758
- JupiterFormField = __decorateClass$6([
5578
+ JupiterFormField = __decorateClass$7([
4759
5579
  t$1("jupiter-form-field")
4760
5580
  ], JupiterFormField);
4761
- var __defProp$5 = Object.defineProperty;
4762
- var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
4763
- var __decorateClass$5 = (decorators, target, key, kind) => {
4764
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
5581
+ var __defProp$6 = Object.defineProperty;
5582
+ var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
5583
+ var __decorateClass$6 = (decorators, target, key, kind) => {
5584
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
4765
5585
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
4766
5586
  if (decorator = decorators[i2])
4767
5587
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
4768
5588
  if (kind && result)
4769
- __defProp$5(target, key, result);
5589
+ __defProp$6(target, key, result);
4770
5590
  return result;
4771
5591
  };
4772
5592
  let JupiterConceptTree = class extends LitElement {
@@ -5036,6 +5856,7 @@ let JupiterConceptTree = class extends LitElement {
5036
5856
  const hasChildren = this.concept.children && this.concept.children.length > 0;
5037
5857
  const level = this.concept.level || 0;
5038
5858
  const isAbstract = this.concept.abstract || false;
5859
+ const isTotal = isTotalLabel$1(this.concept.preferredLabel);
5039
5860
  return html`
5040
5861
  <!-- Concept Name Cell (Left Column) -->
5041
5862
  <td class="concept-name-cell ${isAbstract ? "abstract" : hasChildren ? "" : "leaf"} ${this.rowFocused ? "row-focused" : ""}">
@@ -5045,7 +5866,7 @@ let JupiterConceptTree = class extends LitElement {
5045
5866
  @click="${this._toggleExpanded}">
5046
5867
  ${hasChildren ? "▶" : ""}
5047
5868
  </div>
5048
- <div class="concept-label"
5869
+ <div class="concept-label ${isTotal ? "total-concept-label" : ""}"
5049
5870
  @click="${this._toggleExpanded}"
5050
5871
  title="${this.concept.id}${this.concept.description ? " - " + this.concept.description : ""}">
5051
5872
  ${this.concept.label}
@@ -5053,14 +5874,14 @@ let JupiterConceptTree = class extends LitElement {
5053
5874
  ${this.concept.balance ? html`
5054
5875
  <div class="concept-balance ${this.concept.balance}">${this.concept.balance}</div>
5055
5876
  ` : ""}
5056
- <button class="concept-info-btn" type="button" title="${I18n.t("conceptInfo.title")}"
5877
+ <button class="concept-info-btn" type="button" tabindex="-1" title="${I18n.t("conceptInfo.title")}"
5057
5878
  @click="${this._openInfoDialog}">ℹ</button>
5058
5879
  ${this.showAddButton ? html`
5059
- <button class="repeat-btn" type="button" title="Add row"
5880
+ <button class="repeat-btn" type="button" tabindex="-1" title="Add row"
5060
5881
  @click="${this._handleAddRepeat}">+</button>
5061
5882
  ` : ""}
5062
5883
  ${this.showRemoveButton ? html`
5063
- <button class="repeat-btn remove" type="button" title="Remove row"
5884
+ <button class="repeat-btn remove" type="button" tabindex="-1" title="Remove row"
5064
5885
  @click="${this._handleRemoveRepeat}">−</button>
5065
5886
  ` : ""}
5066
5887
  </div>
@@ -5078,11 +5899,12 @@ let JupiterConceptTree = class extends LitElement {
5078
5899
  const storedDecimals = (_d = (_c = this.decimalsData) == null ? void 0 : _c[this.concept.id]) == null ? void 0 : _d[column2.id];
5079
5900
  const calcMismatch = this.mode !== "readonly" ? (_e = this.calculationMismatches) == null ? void 0 : _e.get(`${this.concept.id}__${column2.id}`) : void 0;
5080
5901
  return html`
5081
- <td class="field-cell ${!shouldShowField ? "empty" : ""} ${isAbstract ? "abstract-row" : ""} ${this.highlightType && column2.id === this.highlightColumnId ? "highlight-" + this.highlightType : ""} ${calcMismatch ? "calc-warning" : ""} ${this.rowFocused ? "row-focused" : ""}">
5902
+ <td class="field-cell ${!shouldShowField ? "empty" : ""} ${isAbstract ? "abstract-row" : ""} ${isTotal ? "total-row" : ""} ${this.highlightType && column2.id === this.highlightColumnId ? "highlight-" + this.highlightType : ""} ${calcMismatch ? "calc-warning" : ""} ${this.rowFocused ? "row-focused" : ""}">
5082
5903
  ${calcMismatch ? html`
5083
5904
  <button
5084
5905
  class="calc-warning-badge"
5085
5906
  type="button"
5907
+ tabindex="-1"
5086
5908
  title="${calcMismatch.message}"
5087
5909
  @click="${(e2) => this._openCalculationWarningDialog(e2, calcMismatch)}"
5088
5910
  >!</button>
@@ -5199,6 +6021,10 @@ JupiterConceptTree.styles = css`
5199
6021
  min-width: 0; /* Allows flex item to shrink below content size */
5200
6022
  }
5201
6023
 
6024
+ .concept-label.total-concept-label {
6025
+ font-weight: 700;
6026
+ }
6027
+
5202
6028
  .concept-balance {
5203
6029
  margin-left: 8px;
5204
6030
  font-size: 11px;
@@ -5290,6 +6116,10 @@ JupiterConceptTree.styles = css`
5290
6116
  color: #fff;
5291
6117
  }
5292
6118
 
6119
+ .field-cell.total-row {
6120
+ background: var(--jupiter-total-cell-background, rgba(0, 0, 0, 0.045));
6121
+ }
6122
+
5293
6123
  .field-cell.highlight-total {
5294
6124
  background: rgba(25, 118, 210, 0.15);
5295
6125
  }
@@ -5395,90 +6225,90 @@ JupiterConceptTree.styles = css`
5395
6225
  }
5396
6226
 
5397
6227
  `;
5398
- __decorateClass$5([
6228
+ __decorateClass$6([
5399
6229
  n2({ type: Object })
5400
6230
  ], JupiterConceptTree.prototype, "concept", 2);
5401
- __decorateClass$5([
6231
+ __decorateClass$6([
5402
6232
  n2({ type: Array })
5403
6233
  ], JupiterConceptTree.prototype, "columns", 2);
5404
- __decorateClass$5([
6234
+ __decorateClass$6([
5405
6235
  n2({ type: Object })
5406
6236
  ], JupiterConceptTree.prototype, "formData", 2);
5407
- __decorateClass$5([
6237
+ __decorateClass$6([
5408
6238
  n2({ type: Object })
5409
6239
  ], JupiterConceptTree.prototype, "periodData", 2);
5410
- __decorateClass$5([
6240
+ __decorateClass$6([
5411
6241
  n2({ type: Object })
5412
6242
  ], JupiterConceptTree.prototype, "unitData", 2);
5413
- __decorateClass$5([
6243
+ __decorateClass$6([
5414
6244
  n2({ type: Object })
5415
6245
  ], JupiterConceptTree.prototype, "decimalsData", 2);
5416
- __decorateClass$5([
6246
+ __decorateClass$6([
5417
6247
  n2({ type: String })
5418
6248
  ], JupiterConceptTree.prototype, "globalDecimals", 2);
5419
- __decorateClass$5([
6249
+ __decorateClass$6([
5420
6250
  n2({ type: Array })
5421
6251
  ], JupiterConceptTree.prototype, "defaultUnits", 2);
5422
- __decorateClass$5([
6252
+ __decorateClass$6([
5423
6253
  n2({ type: Boolean })
5424
6254
  ], JupiterConceptTree.prototype, "disabled", 2);
5425
- __decorateClass$5([
6255
+ __decorateClass$6([
5426
6256
  n2({ type: String })
5427
6257
  ], JupiterConceptTree.prototype, "locale", 2);
5428
- __decorateClass$5([
6258
+ __decorateClass$6([
5429
6259
  n2({ type: Set })
5430
6260
  ], JupiterConceptTree.prototype, "expandedConcepts", 2);
5431
- __decorateClass$5([
6261
+ __decorateClass$6([
5432
6262
  n2({ type: Array })
5433
6263
  ], JupiterConceptTree.prototype, "datatypes", 2);
5434
- __decorateClass$5([
6264
+ __decorateClass$6([
5435
6265
  n2({ type: String })
5436
6266
  ], JupiterConceptTree.prototype, "mode", 2);
5437
- __decorateClass$5([
6267
+ __decorateClass$6([
5438
6268
  n2({ type: Object })
5439
6269
  ], JupiterConceptTree.prototype, "masterData", 2);
5440
- __decorateClass$5([
6270
+ __decorateClass$6([
5441
6271
  n2({ type: Array })
5442
6272
  ], JupiterConceptTree.prototype, "facts", 2);
5443
- __decorateClass$5([
6273
+ __decorateClass$6([
5444
6274
  n2({ type: Object })
5445
6275
  ], JupiterConceptTree.prototype, "typedMemberData", 2);
5446
- __decorateClass$5([
6276
+ __decorateClass$6([
5447
6277
  n2({ type: Boolean })
5448
6278
  ], JupiterConceptTree.prototype, "showAddButton", 2);
5449
- __decorateClass$5([
6279
+ __decorateClass$6([
5450
6280
  n2({ type: Boolean })
5451
6281
  ], JupiterConceptTree.prototype, "showRemoveButton", 2);
5452
- __decorateClass$5([
6282
+ __decorateClass$6([
5453
6283
  n2({ type: String })
5454
6284
  ], JupiterConceptTree.prototype, "highlightType", 2);
5455
- __decorateClass$5([
6285
+ __decorateClass$6([
5456
6286
  n2({ type: String })
5457
6287
  ], JupiterConceptTree.prototype, "highlightColumnId", 2);
5458
- __decorateClass$5([
6288
+ __decorateClass$6([
5459
6289
  n2({ type: Object })
5460
6290
  ], JupiterConceptTree.prototype, "calculationMismatches", 2);
5461
- __decorateClass$5([
6291
+ __decorateClass$6([
5462
6292
  n2({ type: String })
5463
6293
  ], JupiterConceptTree.prototype, "language", 2);
5464
- __decorateClass$5([
6294
+ __decorateClass$6([
5465
6295
  n2({ type: Boolean })
5466
6296
  ], JupiterConceptTree.prototype, "rowFocused", 2);
5467
- __decorateClass$5([
6297
+ __decorateClass$6([
5468
6298
  r()
5469
6299
  ], JupiterConceptTree.prototype, "_expanded", 2);
5470
- JupiterConceptTree = __decorateClass$5([
6300
+ JupiterConceptTree = __decorateClass$6([
5471
6301
  t$1("jupiter-concept-tree")
5472
6302
  ], JupiterConceptTree);
5473
- var __defProp$4 = Object.defineProperty;
5474
- var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
5475
- var __decorateClass$4 = (decorators, target, key, kind) => {
5476
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
6303
+ var __defProp$5 = Object.defineProperty;
6304
+ var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
6305
+ var __decorateClass$5 = (decorators, target, key, kind) => {
6306
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
5477
6307
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
5478
6308
  if (decorator = decorators[i2])
5479
6309
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
5480
6310
  if (kind && result)
5481
- __defProp$4(target, key, result);
6311
+ __defProp$5(target, key, result);
5482
6312
  return result;
5483
6313
  };
5484
6314
  let JupiterAddColumnDialog = class extends LitElement {
@@ -6087,48 +6917,48 @@ JupiterAddColumnDialog.styles = css`
6087
6917
  line-height: 1.4;
6088
6918
  }
6089
6919
  `;
6090
- __decorateClass$4([
6920
+ __decorateClass$5([
6091
6921
  n2({ type: String })
6092
6922
  ], JupiterAddColumnDialog.prototype, "periodType", 2);
6093
- __decorateClass$4([
6923
+ __decorateClass$5([
6094
6924
  n2({ type: Boolean })
6095
6925
  ], JupiterAddColumnDialog.prototype, "open", 2);
6096
- __decorateClass$4([
6926
+ __decorateClass$5([
6097
6927
  n2({ type: Array })
6098
6928
  ], JupiterAddColumnDialog.prototype, "availableDimensions", 2);
6099
- __decorateClass$4([
6929
+ __decorateClass$5([
6100
6930
  n2({ type: String })
6101
6931
  ], JupiterAddColumnDialog.prototype, "periodStartDate", 2);
6102
- __decorateClass$4([
6932
+ __decorateClass$5([
6103
6933
  n2({ type: String })
6104
6934
  ], JupiterAddColumnDialog.prototype, "periodEndDate", 2);
6105
- __decorateClass$4([
6935
+ __decorateClass$5([
6106
6936
  r()
6107
6937
  ], JupiterAddColumnDialog.prototype, "_startDate", 2);
6108
- __decorateClass$4([
6938
+ __decorateClass$5([
6109
6939
  r()
6110
6940
  ], JupiterAddColumnDialog.prototype, "_endDate", 2);
6111
- __decorateClass$4([
6941
+ __decorateClass$5([
6112
6942
  r()
6113
6943
  ], JupiterAddColumnDialog.prototype, "_instantDate", 2);
6114
- __decorateClass$4([
6944
+ __decorateClass$5([
6115
6945
  r()
6116
6946
  ], JupiterAddColumnDialog.prototype, "_selectedType", 2);
6117
- __decorateClass$4([
6947
+ __decorateClass$5([
6118
6948
  r()
6119
6949
  ], JupiterAddColumnDialog.prototype, "_selectedDimensions", 2);
6120
- JupiterAddColumnDialog = __decorateClass$4([
6950
+ JupiterAddColumnDialog = __decorateClass$5([
6121
6951
  t$1("jupiter-add-column-dialog")
6122
6952
  ], JupiterAddColumnDialog);
6123
- var __defProp$3 = Object.defineProperty;
6124
- var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
6125
- var __decorateClass$3 = (decorators, target, key, kind) => {
6126
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
6953
+ var __defProp$4 = Object.defineProperty;
6954
+ var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
6955
+ var __decorateClass$4 = (decorators, target, key, kind) => {
6956
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
6127
6957
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
6128
6958
  if (decorator = decorators[i2])
6129
6959
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
6130
6960
  if (kind && result)
6131
- __defProp$3(target, key, result);
6961
+ __defProp$4(target, key, result);
6132
6962
  return result;
6133
6963
  };
6134
6964
  let JupiterFormSection = class extends LitElement {
@@ -6155,6 +6985,7 @@ let JupiterFormSection = class extends LitElement {
6155
6985
  this.periodEndDate = "";
6156
6986
  this.language = "en";
6157
6987
  this.calculationEnabled = false;
6988
+ this.totalManuallyEditedData = {};
6158
6989
  this._expanded = true;
6159
6990
  this._showAddColumnDialog = false;
6160
6991
  this._sectionPeriodType = "duration";
@@ -6179,6 +7010,7 @@ let JupiterFormSection = class extends LitElement {
6179
7010
  this._memberParentMap = /* @__PURE__ */ new Map();
6180
7011
  this._totalConceptMap = /* @__PURE__ */ new Map();
6181
7012
  this._totalChildConceptsMap = /* @__PURE__ */ new Map();
7013
+ this._sessionManuallyEditedTotals = /* @__PURE__ */ new Set();
6182
7014
  }
6183
7015
  connectedCallback() {
6184
7016
  super.connectedCallback();
@@ -6206,6 +7038,9 @@ let JupiterFormSection = class extends LitElement {
6206
7038
  this._clearCalculationState();
6207
7039
  }
6208
7040
  }
7041
+ if (changedProperties.has("totalManuallyEditedData")) {
7042
+ this._sessionManuallyEditedTotals.clear();
7043
+ }
6209
7044
  }
6210
7045
  _checkAndExpandFirstSection() {
6211
7046
  if (this.isFirstSection && this.section && this.section.concepts) {
@@ -6355,6 +7190,15 @@ let JupiterFormSection = class extends LitElement {
6355
7190
  }
6356
7191
  _handleFieldChange(event) {
6357
7192
  event.stopPropagation();
7193
+ const { conceptId, columnId } = event.detail || {};
7194
+ if (conceptId && columnId && this._totalConceptMap.has(conceptId)) {
7195
+ this._sessionManuallyEditedTotals.add(`${conceptId}__${columnId}`);
7196
+ this.dispatchEvent(new CustomEvent("total-manually-edited", {
7197
+ detail: { conceptId, columnId },
7198
+ bubbles: true,
7199
+ composed: true
7200
+ }));
7201
+ }
6358
7202
  this.dispatchEvent(new CustomEvent("field-change", {
6359
7203
  detail: event.detail,
6360
7204
  bubbles: true,
@@ -6383,6 +7227,13 @@ let JupiterFormSection = class extends LitElement {
6383
7227
  }
6384
7228
  return result;
6385
7229
  }
7230
+ _isTotalManuallyEdited(totalConceptId, columnId) {
7231
+ var _a, _b;
7232
+ const key = `${totalConceptId}__${columnId}`;
7233
+ if (this._sessionManuallyEditedTotals.has(key))
7234
+ return true;
7235
+ return ((_b = (_a = this.totalManuallyEditedData) == null ? void 0 : _a[totalConceptId]) == null ? void 0 : _b[columnId]) === true;
7236
+ }
6386
7237
  /**
6387
7238
  * Returns true when every column cell for a concept has no value.
6388
7239
  * Used in readonly mode to hide fully-blank rows.
@@ -6463,7 +7314,7 @@ let JupiterFormSection = class extends LitElement {
6463
7314
  * matching is the single source of truth for "is this row a total" everywhere in this file.
6464
7315
  */
6465
7316
  static _isTotalLabel(preferredLabel) {
6466
- return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("totallabel"));
7317
+ return isTotalLabel$1(preferredLabel);
6467
7318
  }
6468
7319
  /**
6469
7320
  * JDF-041: distinguishes a self-contained "deduction block" subtotal (negatedTotalLabel, e.g.
@@ -6584,10 +7435,29 @@ let JupiterFormSection = class extends LitElement {
6584
7435
  }
6585
7436
  }
6586
7437
  _evaluateTotal(totalConceptId, columnId) {
6587
- var _a;
7438
+ var _a, _b;
6588
7439
  const totalConcept = this._totalConceptMap.get(totalConceptId);
6589
- const totalRaw = (_a = this.formData[totalConceptId]) == null ? void 0 : _a[columnId];
6590
7440
  const key = `${totalConceptId}__${columnId}`;
7441
+ const children = this._totalChildConceptsMap.get(totalConceptId) || [];
7442
+ const { calculatedSum, hasAnyValue, breakdown } = this._buildCalculationBreakdown(
7443
+ children,
7444
+ this.formData,
7445
+ columnId,
7446
+ totalConcept.balance
7447
+ );
7448
+ if (!this._isTotalManuallyEdited(totalConceptId, columnId)) {
7449
+ if (hasAnyValue) {
7450
+ const currentRaw = (_a = this.formData[totalConceptId]) == null ? void 0 : _a[columnId];
7451
+ const currentValue = parseFloat(currentRaw);
7452
+ const isBlank = currentRaw === null || currentRaw === void 0 || currentRaw === "";
7453
+ if (isBlank || isNaN(currentValue) || !JupiterFormSection._checkTotal(currentValue, calculatedSum)) {
7454
+ this._dispatchCalculationTotalAutoPopulated(totalConceptId, columnId, calculatedSum);
7455
+ }
7456
+ }
7457
+ this._clearCalculationMismatch(key);
7458
+ return;
7459
+ }
7460
+ const totalRaw = (_b = this.formData[totalConceptId]) == null ? void 0 : _b[columnId];
6591
7461
  if (totalRaw === null || totalRaw === void 0 || totalRaw === "") {
6592
7462
  this._clearCalculationMismatch(key);
6593
7463
  return;
@@ -6597,13 +7467,6 @@ let JupiterFormSection = class extends LitElement {
6597
7467
  this._clearCalculationMismatch(key);
6598
7468
  return;
6599
7469
  }
6600
- const children = this._totalChildConceptsMap.get(totalConceptId) || [];
6601
- const { calculatedSum, hasAnyValue, breakdown } = this._buildCalculationBreakdown(
6602
- children,
6603
- this.formData,
6604
- columnId,
6605
- totalConcept.balance
6606
- );
6607
7470
  if (!hasAnyValue || JupiterFormSection._checkTotal(totalValue, calculatedSum)) {
6608
7471
  this._clearCalculationMismatch(key);
6609
7472
  return;
@@ -6626,6 +7489,13 @@ let JupiterFormSection = class extends LitElement {
6626
7489
  this._calculationMismatches = next;
6627
7490
  this._dispatchCalculationMismatchChanged(key, detail);
6628
7491
  }
7492
+ _dispatchCalculationTotalAutoPopulated(conceptId, columnId, value) {
7493
+ this.dispatchEvent(new CustomEvent("calculation-total-auto-populated", {
7494
+ detail: { conceptId, columnId, value, sectionId: this.section.id },
7495
+ bubbles: true,
7496
+ composed: true
7497
+ }));
7498
+ }
6629
7499
  _clearCalculationMismatch(key) {
6630
7500
  if (!this._calculationMismatches.has(key))
6631
7501
  return;
@@ -6689,7 +7559,7 @@ let JupiterFormSection = class extends LitElement {
6689
7559
  return { calculatedSum, hasAnyValue, breakdown };
6690
7560
  }
6691
7561
  static _checkTotal(totalValue, calculatedSum) {
6692
- return Math.abs(calculatedSum - totalValue) < 0.01;
7562
+ return Math.abs(calculatedSum - totalValue) < CALCULATION_TOLERANCE;
6693
7563
  }
6694
7564
  static _formatNumber(n3) {
6695
7565
  return Number.isInteger(n3) ? String(n3) : n3.toFixed(2);
@@ -7275,123 +8145,126 @@ JupiterFormSection.styles = css`
7275
8145
  font-style: italic;
7276
8146
  }
7277
8147
  `;
7278
- __decorateClass$3([
8148
+ __decorateClass$4([
7279
8149
  n2({ type: Object })
7280
8150
  ], JupiterFormSection.prototype, "section", 2);
7281
- __decorateClass$3([
8151
+ __decorateClass$4([
7282
8152
  n2({ type: Array })
7283
8153
  ], JupiterFormSection.prototype, "columns", 2);
7284
- __decorateClass$3([
8154
+ __decorateClass$4([
7285
8155
  n2({ type: Array })
7286
8156
  ], JupiterFormSection.prototype, "datatypes", 2);
7287
- __decorateClass$3([
8157
+ __decorateClass$4([
7288
8158
  n2({ type: Object })
7289
8159
  ], JupiterFormSection.prototype, "formData", 2);
7290
- __decorateClass$3([
8160
+ __decorateClass$4([
7291
8161
  n2({ type: Object })
7292
8162
  ], JupiterFormSection.prototype, "periodData", 2);
7293
- __decorateClass$3([
8163
+ __decorateClass$4([
7294
8164
  n2({ type: Object })
7295
8165
  ], JupiterFormSection.prototype, "unitData", 2);
7296
- __decorateClass$3([
8166
+ __decorateClass$4([
7297
8167
  n2({ type: Object })
7298
8168
  ], JupiterFormSection.prototype, "decimalsData", 2);
7299
- __decorateClass$3([
8169
+ __decorateClass$4([
7300
8170
  n2({ type: String })
7301
8171
  ], JupiterFormSection.prototype, "globalDecimals", 2);
7302
- __decorateClass$3([
8172
+ __decorateClass$4([
7303
8173
  n2({ type: Object })
7304
8174
  ], JupiterFormSection.prototype, "typedMemberData", 2);
7305
- __decorateClass$3([
8175
+ __decorateClass$4([
7306
8176
  n2({ type: Object })
7307
8177
  ], JupiterFormSection.prototype, "repeatCounts", 2);
7308
- __decorateClass$3([
8178
+ __decorateClass$4([
7309
8179
  n2({ type: Array })
7310
8180
  ], JupiterFormSection.prototype, "defaultUnits", 2);
7311
- __decorateClass$3([
8181
+ __decorateClass$4([
7312
8182
  n2({ type: Boolean })
7313
8183
  ], JupiterFormSection.prototype, "disabled", 2);
7314
- __decorateClass$3([
8184
+ __decorateClass$4([
7315
8185
  n2({ type: Boolean })
7316
8186
  ], JupiterFormSection.prototype, "collapsible", 2);
7317
- __decorateClass$3([
8187
+ __decorateClass$4([
7318
8188
  n2({ type: String })
7319
8189
  ], JupiterFormSection.prototype, "locale", 2);
7320
- __decorateClass$3([
8190
+ __decorateClass$4([
7321
8191
  n2({ type: Boolean })
7322
8192
  ], JupiterFormSection.prototype, "isFirstSection", 2);
7323
- __decorateClass$3([
8193
+ __decorateClass$4([
7324
8194
  n2({ type: Array })
7325
8195
  ], JupiterFormSection.prototype, "availableDimensions", 2);
7326
- __decorateClass$3([
8196
+ __decorateClass$4([
7327
8197
  n2({ type: Boolean })
7328
8198
  ], JupiterFormSection.prototype, "hideHeader", 2);
7329
- __decorateClass$3([
8199
+ __decorateClass$4([
7330
8200
  n2({ type: String })
7331
8201
  ], JupiterFormSection.prototype, "mode", 2);
7332
- __decorateClass$3([
8202
+ __decorateClass$4([
7333
8203
  n2({ type: Boolean })
7334
8204
  ], JupiterFormSection.prototype, "showFactsOnly", 2);
7335
- __decorateClass$3([
8205
+ __decorateClass$4([
7336
8206
  n2({ type: Object })
7337
8207
  ], JupiterFormSection.prototype, "conceptMatchIds", 2);
7338
- __decorateClass$3([
8208
+ __decorateClass$4([
7339
8209
  n2({ type: Object })
7340
8210
  ], JupiterFormSection.prototype, "masterData", 2);
7341
- __decorateClass$3([
8211
+ __decorateClass$4([
7342
8212
  n2({ type: String })
7343
8213
  ], JupiterFormSection.prototype, "periodStartDate", 2);
7344
- __decorateClass$3([
8214
+ __decorateClass$4([
7345
8215
  n2({ type: String })
7346
8216
  ], JupiterFormSection.prototype, "periodEndDate", 2);
7347
- __decorateClass$3([
8217
+ __decorateClass$4([
7348
8218
  n2({ type: String })
7349
8219
  ], JupiterFormSection.prototype, "language", 2);
7350
- __decorateClass$3([
8220
+ __decorateClass$4([
7351
8221
  n2({ type: Boolean })
7352
8222
  ], JupiterFormSection.prototype, "calculationEnabled", 2);
7353
- __decorateClass$3([
8223
+ __decorateClass$4([
8224
+ n2({ type: Object })
8225
+ ], JupiterFormSection.prototype, "totalManuallyEditedData", 2);
8226
+ __decorateClass$4([
7354
8227
  r()
7355
8228
  ], JupiterFormSection.prototype, "_expanded", 2);
7356
- __decorateClass$3([
8229
+ __decorateClass$4([
7357
8230
  r()
7358
8231
  ], JupiterFormSection.prototype, "_showAddColumnDialog", 2);
7359
- __decorateClass$3([
8232
+ __decorateClass$4([
7360
8233
  r()
7361
8234
  ], JupiterFormSection.prototype, "_sectionPeriodType", 2);
7362
- __decorateClass$3([
8235
+ __decorateClass$4([
7363
8236
  r()
7364
8237
  ], JupiterFormSection.prototype, "_openMenuColumnId", 2);
7365
- __decorateClass$3([
8238
+ __decorateClass$4([
7366
8239
  r()
7367
8240
  ], JupiterFormSection.prototype, "_insertAfterColumnId", 2);
7368
- __decorateClass$3([
8241
+ __decorateClass$4([
7369
8242
  r()
7370
8243
  ], JupiterFormSection.prototype, "_expandedConcepts", 2);
7371
- __decorateClass$3([
8244
+ __decorateClass$4([
7372
8245
  r()
7373
8246
  ], JupiterFormSection.prototype, "_allTreeExpanded", 2);
7374
- __decorateClass$3([
8247
+ __decorateClass$4([
7375
8248
  r()
7376
8249
  ], JupiterFormSection.prototype, "_highlightMap", 2);
7377
- __decorateClass$3([
8250
+ __decorateClass$4([
7378
8251
  r()
7379
8252
  ], JupiterFormSection.prototype, "_focusedConceptId", 2);
7380
- __decorateClass$3([
8253
+ __decorateClass$4([
7381
8254
  r()
7382
8255
  ], JupiterFormSection.prototype, "_calculationMismatches", 2);
7383
- JupiterFormSection = __decorateClass$3([
8256
+ JupiterFormSection = __decorateClass$4([
7384
8257
  t$1("jupiter-form-section")
7385
8258
  ], JupiterFormSection);
7386
- var __defProp$2 = Object.defineProperty;
7387
- var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
7388
- var __decorateClass$2 = (decorators, target, key, kind) => {
7389
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
8259
+ var __defProp$3 = Object.defineProperty;
8260
+ var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
8261
+ var __decorateClass$3 = (decorators, target, key, kind) => {
8262
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
7390
8263
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
7391
8264
  if (decorator = decorators[i2])
7392
8265
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
7393
8266
  if (kind && result)
7394
- __defProp$2(target, key, result);
8267
+ __defProp$3(target, key, result);
7395
8268
  return result;
7396
8269
  };
7397
8270
  let JupiterAdvancedFilter = class extends LitElement {
@@ -7580,27 +8453,27 @@ JupiterAdvancedFilter.styles = css`
7580
8453
  color: var(--primaryTextColor, var(--jupiter-text-primary, #333));
7581
8454
  }
7582
8455
  `;
7583
- __decorateClass$2([
8456
+ __decorateClass$3([
7584
8457
  n2({ type: Boolean })
7585
8458
  ], JupiterAdvancedFilter.prototype, "showFactsOnly", 2);
7586
- __decorateClass$2([
8459
+ __decorateClass$3([
7587
8460
  n2({ type: String })
7588
8461
  ], JupiterAdvancedFilter.prototype, "conceptSearchText", 2);
7589
- __decorateClass$2([
8462
+ __decorateClass$3([
7590
8463
  r()
7591
8464
  ], JupiterAdvancedFilter.prototype, "_localConceptSearchText", 2);
7592
- JupiterAdvancedFilter = __decorateClass$2([
8465
+ JupiterAdvancedFilter = __decorateClass$3([
7593
8466
  t$1("jupiter-advanced-filter")
7594
8467
  ], JupiterAdvancedFilter);
7595
- var __defProp$1 = Object.defineProperty;
7596
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
7597
- var __decorateClass$1 = (decorators, target, key, kind) => {
7598
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
8468
+ var __defProp$2 = Object.defineProperty;
8469
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
8470
+ var __decorateClass$2 = (decorators, target, key, kind) => {
8471
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
7599
8472
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
7600
8473
  if (decorator = decorators[i2])
7601
8474
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
7602
8475
  if (kind && result)
7603
- __defProp$1(target, key, result);
8476
+ __defProp$2(target, key, result);
7604
8477
  return result;
7605
8478
  };
7606
8479
  let _lastViewWasAdvancedFilter = false;
@@ -9191,188 +10064,519 @@ JupiterFilterRolesDialog.styles = css`
9191
10064
  font-size: 14px;
9192
10065
  }
9193
10066
 
9194
- .picklist-search {
9195
- padding: 12px;
9196
- border-bottom: 1px solid var(--jupiter-border-color, #ddd);
10067
+ .picklist-search {
10068
+ padding: 12px;
10069
+ border-bottom: 1px solid var(--jupiter-border-color, #ddd);
10070
+ }
10071
+
10072
+ .picklist-list {
10073
+ flex: 1;
10074
+ overflow-y: auto;
10075
+ padding: 8px;
10076
+ }
10077
+
10078
+ .picklist-item {
10079
+ padding: 10px 12px;
10080
+ margin-bottom: 4px;
10081
+ border: 1px solid var(--jupiter-border-color, #ddd);
10082
+ border-radius: 4px;
10083
+ cursor: pointer;
10084
+ transition: all 0.2s ease;
10085
+ background: var(--jupiter-background, #fff);
10086
+ }
10087
+
10088
+ .picklist-item:hover {
10089
+ background: var(--jupiter-hover-background, #f5f5f5);
10090
+ border-color: var(--jupiter-primary-color, #1976d2);
10091
+ }
10092
+
10093
+ .picklist-item.selected {
10094
+ background: var(--jupiter-primary-color, #1976d2);
10095
+ color: white;
10096
+ border-color: var(--jupiter-primary-color, #1976d2);
10097
+ }
10098
+
10099
+ .picklist-item.dragging {
10100
+ opacity: 0.5;
10101
+ cursor: move;
10102
+ }
10103
+
10104
+ .picklist-item.drag-over {
10105
+ border-top: 3px solid var(--jupiter-primary-color, #1976d2);
10106
+ margin-top: 3px;
10107
+ }
10108
+
10109
+ .picklist-item[draggable="true"] {
10110
+ cursor: move;
10111
+ }
10112
+
10113
+ .picklist-item-label {
10114
+ font-weight: 500;
10115
+ font-size: 14px;
10116
+ margin-bottom: 4px;
10117
+ }
10118
+
10119
+ .picklist-item-value {
10120
+ font-size: 11px;
10121
+ opacity: 0.8;
10122
+ word-break: break-all;
10123
+ }
10124
+
10125
+ .picklist-controls {
10126
+ display: flex;
10127
+ flex-direction: column;
10128
+ justify-content: center;
10129
+ gap: 8px;
10130
+ padding: 0 8px;
10131
+ }
10132
+
10133
+ .picklist-button {
10134
+ background: var(--jupiter-primary-color, #1976d2);
10135
+ color: white;
10136
+ border: none;
10137
+ padding: 8px 16px;
10138
+ border-radius: 4px;
10139
+ cursor: pointer;
10140
+ font-size: 14px;
10141
+ transition: background-color 0.2s ease;
10142
+ }
10143
+
10144
+ .picklist-button:hover:not(:disabled) {
10145
+ background: var(--jupiter-primary-color-dark, #1565c0);
10146
+ }
10147
+
10148
+ .picklist-button:disabled {
10149
+ opacity: 0.4;
10150
+ cursor: not-allowed;
10151
+ }
10152
+
10153
+ .picklist-count {
10154
+ padding: 8px 12px;
10155
+ font-size: 12px;
10156
+ color: var(--jupiter-text-secondary, #666);
10157
+ background: var(--jupiter-background-light, #f8f9fa);
10158
+ border-top: 1px solid var(--jupiter-border-color, #ddd);
10159
+ text-align: center;
10160
+ }
10161
+
10162
+ .btn-text {
10163
+ background: none;
10164
+ border: none;
10165
+ color: var(--buttonBgColor, var(--jupiter-primary-color, #1976d2));
10166
+ padding: 10px 8px;
10167
+ font-size: 14px;
10168
+ font-weight: 500;
10169
+ font-family: inherit;
10170
+ cursor: pointer;
10171
+ margin-right: auto;
10172
+ border-radius: 4px;
10173
+ transition: background-color 0.2s ease;
10174
+ }
10175
+
10176
+ .btn-text:hover {
10177
+ background: var(--menuBgColorLighter, var(--jupiter-hover-background, #f5f5f5));
10178
+ }
10179
+ `;
10180
+ __decorateClass$2([
10181
+ n2({ type: Boolean, reflect: true })
10182
+ ], JupiterFilterRolesDialog.prototype, "open", 2);
10183
+ __decorateClass$2([
10184
+ n2({ type: Array })
10185
+ ], JupiterFilterRolesDialog.prototype, "availableRoles", 2);
10186
+ __decorateClass$2([
10187
+ n2({ type: Array })
10188
+ ], JupiterFilterRolesDialog.prototype, "selectedRoleIds", 2);
10189
+ __decorateClass$2([
10190
+ n2({ type: Object })
10191
+ ], JupiterFilterRolesDialog.prototype, "periodPreferences", 2);
10192
+ __decorateClass$2([
10193
+ n2({ type: String })
10194
+ ], JupiterFilterRolesDialog.prototype, "mode", 2);
10195
+ __decorateClass$2([
10196
+ n2({ type: Object })
10197
+ ], JupiterFilterRolesDialog.prototype, "hypercubeData", 2);
10198
+ __decorateClass$2([
10199
+ n2({ type: Boolean })
10200
+ ], JupiterFilterRolesDialog.prototype, "showFactsOnly", 2);
10201
+ __decorateClass$2([
10202
+ n2({ type: String })
10203
+ ], JupiterFilterRolesDialog.prototype, "conceptSearchText", 2);
10204
+ __decorateClass$2([
10205
+ r()
10206
+ ], JupiterFilterRolesDialog.prototype, "_tempSelectedRoles", 2);
10207
+ __decorateClass$2([
10208
+ r()
10209
+ ], JupiterFilterRolesDialog.prototype, "_searchQuery", 2);
10210
+ __decorateClass$2([
10211
+ r()
10212
+ ], JupiterFilterRolesDialog.prototype, "_filteredRoles", 2);
10213
+ __decorateClass$2([
10214
+ r()
10215
+ ], JupiterFilterRolesDialog.prototype, "_tempPeriodPreferences", 2);
10216
+ __decorateClass$2([
10217
+ r()
10218
+ ], JupiterFilterRolesDialog.prototype, "_selectedAvailableRole", 2);
10219
+ __decorateClass$2([
10220
+ r()
10221
+ ], JupiterFilterRolesDialog.prototype, "_selectedChosenRole", 2);
10222
+ __decorateClass$2([
10223
+ r()
10224
+ ], JupiterFilterRolesDialog.prototype, "_chosenSearchQuery", 2);
10225
+ __decorateClass$2([
10226
+ r()
10227
+ ], JupiterFilterRolesDialog.prototype, "_draggedRoleId", 2);
10228
+ __decorateClass$2([
10229
+ r()
10230
+ ], JupiterFilterRolesDialog.prototype, "_dragOverRoleId", 2);
10231
+ __decorateClass$2([
10232
+ r()
10233
+ ], JupiterFilterRolesDialog.prototype, "_chosenRoleOrder", 2);
10234
+ __decorateClass$2([
10235
+ r()
10236
+ ], JupiterFilterRolesDialog.prototype, "_showAdvancedFilter", 2);
10237
+ __decorateClass$2([
10238
+ r()
10239
+ ], JupiterFilterRolesDialog.prototype, "_showFactsOnly", 2);
10240
+ __decorateClass$2([
10241
+ r()
10242
+ ], JupiterFilterRolesDialog.prototype, "_conceptSearchText", 2);
10243
+ __decorateClass$2([
10244
+ r()
10245
+ ], JupiterFilterRolesDialog.prototype, "_collapsedRoles", 2);
10246
+ JupiterFilterRolesDialog = __decorateClass$2([
10247
+ t$1("jupiter-filter-roles-dialog")
10248
+ ], JupiterFilterRolesDialog);
10249
+ var __defProp$1 = Object.defineProperty;
10250
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
10251
+ var __decorateClass$1 = (decorators, target, key, kind) => {
10252
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
10253
+ for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
10254
+ if (decorator = decorators[i2])
10255
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10256
+ if (kind && result)
10257
+ __defProp$1(target, key, result);
10258
+ return result;
10259
+ };
10260
+ let JupiterFormulaValidationDialog = class extends LitElement {
10261
+ constructor() {
10262
+ super(...arguments);
10263
+ this.results = [];
10264
+ this.open = false;
10265
+ this._collapsedGroups = /* @__PURE__ */ new Set();
10266
+ this._boundHandleKeydown = this._handleKeydown.bind(this);
10267
+ }
10268
+ connectedCallback() {
10269
+ super.connectedCallback();
10270
+ window.addEventListener("keydown", this._boundHandleKeydown);
10271
+ }
10272
+ disconnectedCallback() {
10273
+ window.removeEventListener("keydown", this._boundHandleKeydown);
10274
+ super.disconnectedCallback();
10275
+ }
10276
+ _handleKeydown(event) {
10277
+ if (this.open && event.key === "Escape") {
10278
+ this._handleClose();
10279
+ }
10280
+ }
10281
+ _handleClose() {
10282
+ this.open = false;
10283
+ this.dispatchEvent(new CustomEvent("dialog-cancel", { bubbles: true }));
10284
+ }
10285
+ _toggleGroup(roleLabel) {
10286
+ if (this._collapsedGroups.has(roleLabel)) {
10287
+ this._collapsedGroups.delete(roleLabel);
10288
+ } else {
10289
+ this._collapsedGroups.add(roleLabel);
10290
+ }
10291
+ this.requestUpdate();
10292
+ }
10293
+ _groupByRole(failures) {
10294
+ const grouped = failures.reduce((groups, result) => {
10295
+ var _a;
10296
+ (groups[_a = result.roleLabel] ?? (groups[_a] = [])).push(result);
10297
+ return groups;
10298
+ }, {});
10299
+ return Object.entries(grouped);
10300
+ }
10301
+ render() {
10302
+ if (!this.open)
10303
+ return html``;
10304
+ const failures = this.results.filter((result) => !result.skipped && !result.satisfied);
10305
+ const groups = this._groupByRole(failures);
10306
+ const summary = this.summary;
10307
+ return html`
10308
+ <div class="dialog" @click="${(e2) => e2.stopPropagation()}">
10309
+ <div class="dialog-header">
10310
+ <h2 class="dialog-title">${I18n.t("formulaValidation.dialogTitle")}</h2>
10311
+ ${summary ? html`
10312
+ <p class="dialog-summary">
10313
+ ${I18n.t("formulaValidation.summaryCount", { failed: summary.failed, total: summary.totalAssertions })}
10314
+ ${summary.failed > 0 ? html`— ${I18n.t("formulaValidation.summaryCountBreakdown", { warnings: summary.failedWarnings, errors: summary.failedErrors })}` : ""}
10315
+ </p>
10316
+ ` : ""}
10317
+ </div>
10318
+
10319
+ <div class="dialog-content">
10320
+ ${failures.length === 0 ? html`
10321
+ <div class="all-passed">
10322
+ <div class="all-passed-icon">✓</div>
10323
+ <div class="all-passed-text">${I18n.t("formulaValidation.allPassed")}</div>
10324
+ </div>
10325
+ ` : groups.map(([roleLabel, rows]) => {
10326
+ const collapsed = this._collapsedGroups.has(roleLabel);
10327
+ return html`
10328
+ <div class="role-group">
10329
+ <div class="role-group-header" @click="${() => this._toggleGroup(roleLabel)}">
10330
+ <span class="role-group-title">${roleLabel}</span>
10331
+ <span class="role-group-count">${rows.length}</span>
10332
+ <button class="role-group-toggle" type="button">${collapsed ? "▸" : "▾"}</button>
10333
+ </div>
10334
+ ${collapsed ? "" : html`
10335
+ <div class="role-group-rows">
10336
+ ${rows.map((result) => html`
10337
+ <div class="assertion-row">
10338
+ <span class="severity-badge ${result.severity === "ERROR" ? "error" : "warning"}">
10339
+ ${result.severity === "ERROR" ? I18n.t("formulaValidation.severityError") : I18n.t("formulaValidation.severityWarning")}
10340
+ </span>
10341
+ <span class="assertion-message">${result.message}</span>
10342
+ </div>
10343
+ `)}
10344
+ </div>
10345
+ `}
10346
+ </div>
10347
+ `;
10348
+ })}
10349
+ </div>
10350
+
10351
+ <div class="dialog-actions">
10352
+ <button class="btn btn-primary" @click="${this._handleClose}">
10353
+ ${I18n.t("formulaValidation.close")}
10354
+ </button>
10355
+ </div>
10356
+ </div>
10357
+ `;
10358
+ }
10359
+ };
10360
+ JupiterFormulaValidationDialog.styles = css`
10361
+ :host {
10362
+ position: fixed;
10363
+ top: 0;
10364
+ left: 0;
10365
+ width: 100%;
10366
+ height: 100%;
10367
+ background: rgba(0, 0, 0, 0.5);
10368
+ z-index: 1100;
10369
+ display: flex;
10370
+ align-items: center;
10371
+ justify-content: center;
10372
+ opacity: 0;
10373
+ visibility: hidden;
10374
+ transition: opacity 0.3s ease, visibility 0.3s ease;
10375
+ }
10376
+
10377
+ :host([open]) {
10378
+ opacity: 1;
10379
+ visibility: visible;
10380
+ }
10381
+
10382
+ .dialog {
10383
+ background: var(--bg-color-2, var(--jupiter-card-background, #fff));
10384
+ border-radius: 8px;
10385
+ padding: 24px;
10386
+ min-width: 420px;
10387
+ max-width: 640px;
10388
+ width: 90vw;
10389
+ max-height: 90vh;
10390
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
10391
+ transform: scale(0.9);
10392
+ transition: transform 0.3s ease;
10393
+ display: flex;
10394
+ flex-direction: column;
10395
+ }
10396
+
10397
+ :host([open]) .dialog {
10398
+ transform: scale(1);
10399
+ }
10400
+
10401
+ .dialog-header {
10402
+ margin-bottom: 16px;
10403
+ flex-shrink: 0;
10404
+ }
10405
+
10406
+ .dialog-title {
10407
+ font-size: 20px;
10408
+ font-weight: 600;
10409
+ color: var(--primaryTextColor, var(--jupiter-text-primary, #333));
10410
+ margin: 0 0 8px 0;
10411
+ }
10412
+
10413
+ .dialog-summary {
10414
+ font-size: 14px;
10415
+ color: var(--jupiter-text-secondary, #666);
10416
+ margin: 0;
10417
+ }
10418
+
10419
+ /* JDF-035 dialog-overflow fix: min-height: 0 is required on a flex-column scrollable child,
10420
+ otherwise the browser lets it grow past the dialog's max-height instead of scrolling. */
10421
+ .dialog-content {
10422
+ margin-bottom: 20px;
10423
+ flex: 1;
10424
+ overflow-y: auto;
10425
+ min-height: 0;
10426
+ }
10427
+
10428
+ .all-passed {
10429
+ display: flex;
10430
+ flex-direction: column;
10431
+ align-items: center;
10432
+ gap: 12px;
10433
+ padding: 32px 16px;
10434
+ color: var(--jupiter-text-primary, #333);
10435
+ }
10436
+
10437
+ .all-passed-icon {
10438
+ width: 40px;
10439
+ height: 40px;
10440
+ border-radius: 50%;
10441
+ background: var(--jupiter-success-color, #4caf50);
10442
+ color: #fff;
10443
+ display: flex;
10444
+ align-items: center;
10445
+ justify-content: center;
10446
+ font-size: 22px;
10447
+ font-weight: 700;
10448
+ }
10449
+
10450
+ .all-passed-text {
10451
+ font-size: 15px;
10452
+ font-weight: 500;
9197
10453
  }
9198
10454
 
9199
- .picklist-list {
9200
- flex: 1;
9201
- overflow-y: auto;
9202
- padding: 8px;
10455
+ .role-group {
10456
+ border: 1px solid var(--jupiter-border-color, #ddd);
10457
+ border-radius: 4px;
10458
+ margin-bottom: 12px;
10459
+ overflow: hidden;
9203
10460
  }
9204
10461
 
9205
- .picklist-item {
10462
+ .role-group-header {
10463
+ display: flex;
10464
+ align-items: center;
10465
+ justify-content: space-between;
10466
+ gap: 8px;
9206
10467
  padding: 10px 12px;
9207
- margin-bottom: 4px;
9208
- border: 1px solid var(--jupiter-border-color, #ddd);
9209
- border-radius: 4px;
10468
+ background: var(--jupiter-card-background, #fafafa);
9210
10469
  cursor: pointer;
9211
- transition: all 0.2s ease;
9212
- background: var(--jupiter-background, #fff);
10470
+ user-select: none;
9213
10471
  }
9214
10472
 
9215
- .picklist-item:hover {
9216
- background: var(--jupiter-hover-background, #f5f5f5);
9217
- border-color: var(--jupiter-primary-color, #1976d2);
10473
+ .role-group-title {
10474
+ font-weight: 600;
10475
+ font-size: 14px;
10476
+ color: var(--primaryTextColor, var(--jupiter-text-primary, #333));
9218
10477
  }
9219
10478
 
9220
- .picklist-item.selected {
9221
- background: var(--jupiter-primary-color, #1976d2);
9222
- color: white;
9223
- border-color: var(--jupiter-primary-color, #1976d2);
10479
+ .role-group-count {
10480
+ font-size: 12px;
10481
+ color: var(--jupiter-text-secondary, #666);
9224
10482
  }
9225
10483
 
9226
- .picklist-item.dragging {
9227
- opacity: 0.5;
9228
- cursor: move;
10484
+ .role-group-toggle {
10485
+ background: none;
10486
+ border: none;
10487
+ cursor: pointer;
10488
+ font-size: 12px;
10489
+ color: var(--jupiter-text-secondary, #666);
10490
+ padding: 0 4px;
9229
10491
  }
9230
10492
 
9231
- .picklist-item.drag-over {
9232
- border-top: 3px solid var(--jupiter-primary-color, #1976d2);
9233
- margin-top: 3px;
10493
+ .role-group-rows {
10494
+ padding: 4px 12px 8px 12px;
9234
10495
  }
9235
10496
 
9236
- .picklist-item[draggable="true"] {
9237
- cursor: move;
10497
+ .assertion-row {
10498
+ display: flex;
10499
+ align-items: flex-start;
10500
+ gap: 8px;
10501
+ padding: 8px 0;
10502
+ border-top: 1px solid var(--jupiter-border-color, #eee);
9238
10503
  }
9239
10504
 
9240
- .picklist-item-label {
9241
- font-weight: 500;
9242
- font-size: 14px;
9243
- margin-bottom: 4px;
10505
+ .assertion-row:first-child {
10506
+ border-top: none;
9244
10507
  }
9245
10508
 
9246
- .picklist-item-value {
10509
+ .severity-badge {
10510
+ flex-shrink: 0;
10511
+ display: inline-flex;
10512
+ align-items: center;
10513
+ gap: 4px;
10514
+ padding: 2px 8px;
10515
+ border-radius: 10px;
9247
10516
  font-size: 11px;
9248
- opacity: 0.8;
9249
- word-break: break-all;
9250
- }
9251
-
9252
- .picklist-controls {
9253
- display: flex;
9254
- flex-direction: column;
9255
- justify-content: center;
9256
- gap: 8px;
9257
- padding: 0 8px;
10517
+ font-weight: 700;
10518
+ color: #fff;
10519
+ white-space: nowrap;
9258
10520
  }
9259
10521
 
9260
- .picklist-button {
9261
- background: var(--jupiter-primary-color, #1976d2);
9262
- color: white;
9263
- border: none;
9264
- padding: 8px 16px;
9265
- border-radius: 4px;
9266
- cursor: pointer;
9267
- font-size: 14px;
9268
- transition: background-color 0.2s ease;
10522
+ .severity-badge.warning {
10523
+ background: var(--jupiter-warning-color, #ff9800);
9269
10524
  }
9270
10525
 
9271
- .picklist-button:hover:not(:disabled) {
9272
- background: var(--jupiter-primary-color-dark, #1565c0);
10526
+ .severity-badge.error {
10527
+ background: var(--jupiter-error-color, #d32f2f);
9273
10528
  }
9274
10529
 
9275
- .picklist-button:disabled {
9276
- opacity: 0.4;
9277
- cursor: not-allowed;
10530
+ .assertion-message {
10531
+ font-size: 13px;
10532
+ color: var(--primaryTextColor, var(--jupiter-text-primary, #333));
10533
+ line-height: 1.4;
9278
10534
  }
9279
10535
 
9280
- .picklist-count {
9281
- padding: 8px 12px;
9282
- font-size: 12px;
9283
- color: var(--jupiter-text-secondary, #666);
9284
- background: var(--jupiter-background-light, #f8f9fa);
10536
+ .dialog-actions {
10537
+ display: flex;
10538
+ gap: 12px;
10539
+ justify-content: flex-end;
10540
+ flex-shrink: 0;
9285
10541
  border-top: 1px solid var(--jupiter-border-color, #ddd);
9286
- text-align: center;
10542
+ padding-top: 16px;
10543
+ margin-top: 16px;
9287
10544
  }
9288
10545
 
9289
- .btn-text {
9290
- background: none;
10546
+ .btn {
10547
+ padding: 10px 20px;
9291
10548
  border: none;
9292
- color: var(--buttonBgColor, var(--jupiter-primary-color, #1976d2));
9293
- padding: 10px 8px;
10549
+ border-radius: 4px;
9294
10550
  font-size: 14px;
9295
10551
  font-weight: 500;
9296
- font-family: inherit;
9297
10552
  cursor: pointer;
9298
- margin-right: auto;
9299
- border-radius: 4px;
9300
10553
  transition: background-color 0.2s ease;
9301
10554
  }
9302
10555
 
9303
- .btn-text:hover {
9304
- background: var(--menuBgColorLighter, var(--jupiter-hover-background, #f5f5f5));
10556
+ .btn-primary {
10557
+ background: var(--buttonBgColor, var(--jupiter-primary-color, #1976d2));
10558
+ color: var(--buttonTextColor, white);
10559
+ }
10560
+
10561
+ .btn-primary:hover {
10562
+ opacity: 0.9;
9305
10563
  }
9306
10564
  `;
9307
- __decorateClass$1([
9308
- n2({ type: Boolean, reflect: true })
9309
- ], JupiterFilterRolesDialog.prototype, "open", 2);
9310
- __decorateClass$1([
9311
- n2({ type: Array })
9312
- ], JupiterFilterRolesDialog.prototype, "availableRoles", 2);
9313
10565
  __decorateClass$1([
9314
10566
  n2({ type: Array })
9315
- ], JupiterFilterRolesDialog.prototype, "selectedRoleIds", 2);
9316
- __decorateClass$1([
9317
- n2({ type: Object })
9318
- ], JupiterFilterRolesDialog.prototype, "periodPreferences", 2);
9319
- __decorateClass$1([
9320
- n2({ type: String })
9321
- ], JupiterFilterRolesDialog.prototype, "mode", 2);
10567
+ ], JupiterFormulaValidationDialog.prototype, "results", 2);
9322
10568
  __decorateClass$1([
9323
10569
  n2({ type: Object })
9324
- ], JupiterFilterRolesDialog.prototype, "hypercubeData", 2);
9325
- __decorateClass$1([
9326
- n2({ type: Boolean })
9327
- ], JupiterFilterRolesDialog.prototype, "showFactsOnly", 2);
9328
- __decorateClass$1([
9329
- n2({ type: String })
9330
- ], JupiterFilterRolesDialog.prototype, "conceptSearchText", 2);
9331
- __decorateClass$1([
9332
- r()
9333
- ], JupiterFilterRolesDialog.prototype, "_tempSelectedRoles", 2);
9334
- __decorateClass$1([
9335
- r()
9336
- ], JupiterFilterRolesDialog.prototype, "_searchQuery", 2);
9337
- __decorateClass$1([
9338
- r()
9339
- ], JupiterFilterRolesDialog.prototype, "_filteredRoles", 2);
9340
- __decorateClass$1([
9341
- r()
9342
- ], JupiterFilterRolesDialog.prototype, "_tempPeriodPreferences", 2);
9343
- __decorateClass$1([
9344
- r()
9345
- ], JupiterFilterRolesDialog.prototype, "_selectedAvailableRole", 2);
9346
- __decorateClass$1([
9347
- r()
9348
- ], JupiterFilterRolesDialog.prototype, "_selectedChosenRole", 2);
9349
- __decorateClass$1([
9350
- r()
9351
- ], JupiterFilterRolesDialog.prototype, "_chosenSearchQuery", 2);
9352
- __decorateClass$1([
9353
- r()
9354
- ], JupiterFilterRolesDialog.prototype, "_draggedRoleId", 2);
9355
- __decorateClass$1([
9356
- r()
9357
- ], JupiterFilterRolesDialog.prototype, "_dragOverRoleId", 2);
9358
- __decorateClass$1([
9359
- r()
9360
- ], JupiterFilterRolesDialog.prototype, "_chosenRoleOrder", 2);
9361
- __decorateClass$1([
9362
- r()
9363
- ], JupiterFilterRolesDialog.prototype, "_showAdvancedFilter", 2);
9364
- __decorateClass$1([
9365
- r()
9366
- ], JupiterFilterRolesDialog.prototype, "_showFactsOnly", 2);
10570
+ ], JupiterFormulaValidationDialog.prototype, "summary", 2);
9367
10571
  __decorateClass$1([
9368
- r()
9369
- ], JupiterFilterRolesDialog.prototype, "_conceptSearchText", 2);
10572
+ n2({ type: Boolean, reflect: true })
10573
+ ], JupiterFormulaValidationDialog.prototype, "open", 2);
9370
10574
  __decorateClass$1([
9371
10575
  r()
9372
- ], JupiterFilterRolesDialog.prototype, "_collapsedRoles", 2);
9373
- JupiterFilterRolesDialog = __decorateClass$1([
9374
- t$1("jupiter-filter-roles-dialog")
9375
- ], JupiterFilterRolesDialog);
10576
+ ], JupiterFormulaValidationDialog.prototype, "_collapsedGroups", 2);
10577
+ JupiterFormulaValidationDialog = __decorateClass$1([
10578
+ t$1("jupiter-formula-validation-dialog")
10579
+ ], JupiterFormulaValidationDialog);
9376
10580
  var __defProp = Object.defineProperty;
9377
10581
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9378
10582
  var __decorateClass = (decorators, target, key, kind) => {
@@ -9392,6 +10596,7 @@ let JupiterDynamicForm = class extends LitElement {
9392
10596
  this.disabled = false;
9393
10597
  this.readonly = false;
9394
10598
  this.calculationEnabled = false;
10599
+ this.formulaEnabled = false;
9395
10600
  this.periodStartDate = "2025-01-01";
9396
10601
  this.periodEndDate = "2025-12-31";
9397
10602
  this.language = "en";
@@ -9411,6 +10616,7 @@ let JupiterDynamicForm = class extends LitElement {
9411
10616
  this._unitData = {};
9412
10617
  this._preservedUnitData = {};
9413
10618
  this._decimalsData = {};
10619
+ this._totalManuallyEditedData = {};
9414
10620
  this._effectiveMasterData = void 0;
9415
10621
  this._typedMemberData = {};
9416
10622
  this._preservedTypedMemberData = {};
@@ -9443,6 +10649,9 @@ let JupiterDynamicForm = class extends LitElement {
9443
10649
  this._contextMenuY = 0;
9444
10650
  this._contextMenuRoleId = null;
9445
10651
  this._validationStatus = "idle";
10652
+ this._formulaValidationResults = [];
10653
+ this._showFormulaValidationDialog = false;
10654
+ this._formulaValidationService = new FormulaValidationService();
9446
10655
  this._skipDraftLoading = false;
9447
10656
  this._skipPeriodPreferencesRestore = false;
9448
10657
  this._autoSaveTimer = null;
@@ -9474,6 +10683,24 @@ let JupiterDynamicForm = class extends LitElement {
9474
10683
  this.addEventListener("calculation-mismatch-changed", (e2) => {
9475
10684
  this._handleCalculationMismatchChanged(e2);
9476
10685
  });
10686
+ this.addEventListener("total-manually-edited", (e2) => {
10687
+ const { conceptId, columnId } = e2.detail;
10688
+ const updated = { ...this._totalManuallyEditedData };
10689
+ updated[conceptId] = { ...updated[conceptId], [columnId]: true };
10690
+ this._totalManuallyEditedData = updated;
10691
+ });
10692
+ this.addEventListener("calculation-total-auto-populated", (e2) => {
10693
+ const { conceptId, columnId, value } = e2.detail;
10694
+ const oldValue = this._writeFormDataValue(conceptId, columnId, value);
10695
+ this._touched.add(`${conceptId}-${columnId}`);
10696
+ this._dirty = true;
10697
+ this._validateForm();
10698
+ this.requestUpdate();
10699
+ this.dispatchEvent(new CustomEvent("field-change", {
10700
+ detail: { fieldId: conceptId, conceptId, columnId, value, oldValue, source: "auto-calculated" },
10701
+ bubbles: true
10702
+ }));
10703
+ });
9477
10704
  document.addEventListener("click", () => {
9478
10705
  if (this._showRoleContextMenu) {
9479
10706
  this._showRoleContextMenu = false;
@@ -10148,6 +11375,12 @@ let JupiterDynamicForm = class extends LitElement {
10148
11375
  const roleCount = this._getRoleIdsArray().length;
10149
11376
  console.log(`🚫 Filter dialog cancelled. Current selection: ${roleCount}/${this._allSections.length}`);
10150
11377
  }
11378
+ // JDF-050: closes the formula-validation results dialog opened by JDF-049's pre-flight.
11379
+ // Purely a visibility toggle — submit already proceeded on the click that opened it
11380
+ // (non-blocking, per the corrected JDF-043 Q4 decision), so there is nothing to resume here.
11381
+ _handleFormulaValidationDialogCancel() {
11382
+ this._showFormulaValidationDialog = false;
11383
+ }
10151
11384
  _handleRoleFilterApply(event) {
10152
11385
  var _a, _b;
10153
11386
  const { selectedRoleIds, periodPreferences } = event.detail;
@@ -10175,7 +11408,8 @@ let JupiterDynamicForm = class extends LitElement {
10175
11408
  this._repeatCounts,
10176
11409
  this._decimalsData,
10177
11410
  void 0,
10178
- this._getHiddenColumnsForMetadata()
11411
+ this._getHiddenColumnsForMetadata(),
11412
+ this._totalManuallyEditedData
10179
11413
  );
10180
11414
  this._draftStorageService.saveDraft(currentFormData, currentMetadata);
10181
11415
  console.log("✅ Current form data saved to draft storage with NEW preferences");
@@ -10284,9 +11518,13 @@ let JupiterDynamicForm = class extends LitElement {
10284
11518
  this._calculationWarnings = next;
10285
11519
  this._validateForm();
10286
11520
  }
10287
- _handleFieldChange(event) {
10288
- const { fieldId, conceptId, columnId, value } = event.detail;
10289
- console.log(`📝 Field change: conceptId=${conceptId}, columnId=${columnId}, value=${value}, fieldId=${fieldId}`);
11521
+ /**
11522
+ * JDF-058: single source of truth for writing a value into `_formData` with the immutable-update
11523
+ * pattern Lit reactivity needs shared by a real user keystroke (`_handleFieldChange`) and the
11524
+ * auto-populate-totals `calculation-total-auto-populated` handler, so the update logic isn't
11525
+ * duplicated. Returns the value that was previously stored, for event `oldValue` fields.
11526
+ */
11527
+ _writeFormDataValue(conceptId, columnId, value) {
10290
11528
  const updatedFormData = { ...this._formData };
10291
11529
  if (!updatedFormData[conceptId]) {
10292
11530
  updatedFormData[conceptId] = {};
@@ -10294,6 +11532,12 @@ let JupiterDynamicForm = class extends LitElement {
10294
11532
  const oldValue = updatedFormData[conceptId][columnId];
10295
11533
  updatedFormData[conceptId] = { ...updatedFormData[conceptId], [columnId]: value };
10296
11534
  this._formData = updatedFormData;
11535
+ return oldValue;
11536
+ }
11537
+ _handleFieldChange(event) {
11538
+ const { fieldId, conceptId, columnId, value } = event.detail;
11539
+ console.log(`📝 Field change: conceptId=${conceptId}, columnId=${columnId}, value=${value}, fieldId=${fieldId}`);
11540
+ const oldValue = this._writeFormDataValue(conceptId, columnId, value);
10297
11541
  console.log(`💾 Updated formData[${conceptId}]:`, this._formData[conceptId]);
10298
11542
  this._touched.add(`${conceptId}-${columnId}`);
10299
11543
  this._dirty = true;
@@ -11275,6 +12519,7 @@ let JupiterDynamicForm = class extends LitElement {
11275
12519
  return;
11276
12520
  }
11277
12521
  this._correctLegacyPeriodStartInstantDates();
12522
+ this._runFormulaValidationPreflight();
11278
12523
  const submissionData = this._generateSubmissionData();
11279
12524
  console.log("📊 Form Submission Data:", JSON.stringify(submissionData, null, 2));
11280
12525
  console.log("📊 Submission Data Summary:");
@@ -11296,6 +12541,58 @@ let JupiterDynamicForm = class extends LitElement {
11296
12541
  this._submitDisabled = false;
11297
12542
  }, 1e3);
11298
12543
  }
12544
+ /**
12545
+ * JDF-049: runs FormulaValidationService against the live form state and stores the results
12546
+ * for the results dialog (JDF-050). Graceful no-op — per JDF-048's gating and this ticket's
12547
+ * "no Validate-formulas affordance/effect when formula data is absent" requirement — when
12548
+ * `formulaEnabled` is off or `xbrlInput.formula` has no roles. Never blocks the caller; always
12549
+ * returns before any resolution work is attempted when gated off (JDF-048's "zero evaluation
12550
+ * cost when off" requirement).
12551
+ */
12552
+ _runFormulaValidationPreflight() {
12553
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
12554
+ if (!this.formulaEnabled)
12555
+ return;
12556
+ const formulaRoles = (_c = (_b = (_a = this.xbrlInput) == null ? void 0 : _a.formula) == null ? void 0 : _b[0]) == null ? void 0 : _c.roles;
12557
+ if (!formulaRoles || formulaRoles.length === 0)
12558
+ return;
12559
+ const ctx = buildFormulaResolutionContext(
12560
+ this._formData,
12561
+ this._mergeAllSectionColumns(),
12562
+ ((_f = (_e = (_d = this.xbrlInput) == null ? void 0 : _d.presentation) == null ? void 0 : _e[0]) == null ? void 0 : _f.roles) || [],
12563
+ (_i = (_h = (_g = this.xbrlInput) == null ? void 0 : _g.hypercubes) == null ? void 0 : _h[0]) == null ? void 0 : _i.roles,
12564
+ this.periodStartDate,
12565
+ this.periodEndDate
12566
+ );
12567
+ const results = this._formulaValidationService.evaluate(this.xbrlInput, ctx, this.language);
12568
+ const summary = this._formulaValidationService.summarize(results);
12569
+ this._formulaValidationResults = results;
12570
+ this._formulaValidationSummary = summary;
12571
+ const hasFailures = results.some((result) => !result.skipped && !result.satisfied);
12572
+ if (hasFailures) {
12573
+ this._showFormulaValidationDialog = true;
12574
+ }
12575
+ this.dispatchEvent(new CustomEvent("formula-validation-complete", {
12576
+ detail: { results, summary },
12577
+ bubbles: true,
12578
+ composed: true
12579
+ }));
12580
+ }
12581
+ /**
12582
+ * Formula assertions can reference concepts/dimensions from any disclosure role, but
12583
+ * `FormColumn`s (especially dimension columns) are tracked per-section (`section.columns`),
12584
+ * with `this._columns` as the shared/default fallback (see the pattern at L1398/L2814/L5284).
12585
+ * `_formData` itself is already global (keyed by conceptId/columnId, no section nesting), so a
12586
+ * single merged, deduplicated-by-id column list is the correct match for it.
12587
+ */
12588
+ _mergeAllSectionColumns() {
12589
+ const merged = /* @__PURE__ */ new Map();
12590
+ this._columns.forEach((column2) => merged.set(column2.id, column2));
12591
+ this._allSections.forEach((section2) => {
12592
+ (section2.columns || []).forEach((column2) => merged.set(column2.id, column2));
12593
+ });
12594
+ return Array.from(merged.values());
12595
+ }
11299
12596
  _handleSaveDraft(source = "manual") {
11300
12597
  console.log(`🔵 [Save Draft] Checking for errors...`);
11301
12598
  console.log(`🔵 [Save Draft] _xbrlFormErrors.length: ${this._xbrlFormErrors.length}`);
@@ -11333,7 +12630,8 @@ let JupiterDynamicForm = class extends LitElement {
11333
12630
  this._repeatCounts,
11334
12631
  this._decimalsData,
11335
12632
  roleCompletedStates,
11336
- this._getHiddenColumnsForMetadata()
12633
+ this._getHiddenColumnsForMetadata(),
12634
+ this._totalManuallyEditedData
11337
12635
  );
11338
12636
  const draftPayloadSnapshot = JSON.stringify({
11339
12637
  draftData,
@@ -11475,6 +12773,10 @@ let JupiterDynamicForm = class extends LitElement {
11475
12773
  );
11476
12774
  console.log("🔄 Restored hidden columns for", this._hiddenColumnIds.size, "sections");
11477
12775
  }
12776
+ if (metadata.totalManuallyEditedData) {
12777
+ this._totalManuallyEditedData = metadata.totalManuallyEditedData;
12778
+ console.log("🔄 Restored total manually-edited data:", Object.keys(this._totalManuallyEditedData).length, "concepts");
12779
+ }
11478
12780
  if (metadata.periodPreferences) {
11479
12781
  if (this._skipPeriodPreferencesRestore) {
11480
12782
  console.log("⏭️ Skipping period preferences restoration - using new filter selections");
@@ -11554,6 +12856,7 @@ let JupiterDynamicForm = class extends LitElement {
11554
12856
  });
11555
12857
  this._formData = { ...this._formData, ...restoredFormData };
11556
12858
  console.log(`🔄 Restored ${Object.keys(restoredFormData).length} concepts with data`);
12859
+ this._seedLegacyManuallyEditedTotals(restoredFormData);
11557
12860
  this._initializeGlobalDecimalsFromRoundingData();
11558
12861
  this._periodData = {
11559
12862
  ...this._periodData,
@@ -11687,6 +12990,47 @@ let JupiterDynamicForm = class extends LitElement {
11687
12990
  });
11688
12991
  console.log(`✅ Field population complete: ${populatedCount} populated, ${notFoundCount} not found`);
11689
12992
  }
12993
+ /**
12994
+ * JDF-058: seed the manual-edit dirty flag for any total whose value predates this feature, so
12995
+ * upgrading never silently recomputes a customer's pre-existing, already-filed totals. Walks
12996
+ * `_allSections` (the full pool, including currently-hidden/filtered-out sections — a hidden
12997
+ * total's pre-existing value must stay protected the moment its section becomes visible again),
12998
+ * not just the currently-selected roles. Idempotent: only seeds a cell that has no explicit
12999
+ * `totalManuallyEditedData` entry already (i.e. `metadata.totalManuallyEditedData` didn't cover
13000
+ * it), so re-running on an already-seeded draft is a no-op. Does not touch `restoredFormData`.
13001
+ */
13002
+ _seedLegacyManuallyEditedTotals(restoredFormData) {
13003
+ const seeded = { ...this._totalManuallyEditedData };
13004
+ let changed = false;
13005
+ const walk = (concepts) => {
13006
+ var _a;
13007
+ for (const concept of concepts) {
13008
+ if (isTotalLabel$1(concept.preferredLabel)) {
13009
+ const values = restoredFormData[concept.id];
13010
+ if (values) {
13011
+ for (const columnId of Object.keys(values)) {
13012
+ const value = values[columnId];
13013
+ const isBlank = value === null || value === void 0 || value === "";
13014
+ if (!isBlank && ((_a = seeded[concept.id]) == null ? void 0 : _a[columnId]) === void 0) {
13015
+ seeded[concept.id] = { ...seeded[concept.id] || {}, [columnId]: true };
13016
+ changed = true;
13017
+ }
13018
+ }
13019
+ }
13020
+ }
13021
+ if (concept.children && concept.children.length > 0) {
13022
+ walk(concept.children);
13023
+ }
13024
+ }
13025
+ };
13026
+ for (const section2 of this._allSections) {
13027
+ walk(section2.concepts || []);
13028
+ }
13029
+ if (changed) {
13030
+ this._totalManuallyEditedData = seeded;
13031
+ console.log("🔒 [JDF-058] Seeded manual-edit protection for pre-existing total values in legacy draft");
13032
+ }
13033
+ }
11690
13034
  _restoreCustomColumns(customColumns) {
11691
13035
  const columnsBySection = /* @__PURE__ */ new Map();
11692
13036
  customColumns.forEach((col) => {
@@ -13136,6 +14480,7 @@ let JupiterDynamicForm = class extends LitElement {
13136
14480
  .periodStartDate="${this.periodStartDate}"
13137
14481
  .periodEndDate="${this.periodEndDate}"
13138
14482
  .calculationEnabled="${this.calculationEnabled}"
14483
+ .totalManuallyEditedData="${this._totalManuallyEditedData}"
13139
14484
  @field-change="${this._handleFieldChange}"
13140
14485
  @period-change="${this._handlePeriodChange}"
13141
14486
  @typed-member-change="${this._handleTypedMemberChange}"
@@ -13284,6 +14629,7 @@ let JupiterDynamicForm = class extends LitElement {
13284
14629
  .periodStartDate="${this.periodStartDate}"
13285
14630
  .periodEndDate="${this.periodEndDate}"
13286
14631
  .calculationEnabled="${this.calculationEnabled}"
14632
+ .totalManuallyEditedData="${this._totalManuallyEditedData}"
13287
14633
  @field-change="${this._handleFieldChange}"
13288
14634
  @typed-member-change="${this._handleTypedMemberChange}"
13289
14635
  @add-concept-repeat="${this._handleAddConceptRepeat}"
@@ -13706,7 +15052,18 @@ let JupiterDynamicForm = class extends LitElement {
13706
15052
  @click="${this._handleFilterDialogCancel}"
13707
15053
  ></jupiter-filter-roles-dialog>
13708
15054
  ` : ""}
13709
-
15055
+
15056
+ <!-- Formula Validation Results Dialog (JDF-050) -->
15057
+ ${this._showFormulaValidationDialog ? html`
15058
+ <jupiter-formula-validation-dialog
15059
+ ?open="${this._showFormulaValidationDialog}"
15060
+ .results="${this._formulaValidationResults}"
15061
+ .summary="${this._formulaValidationSummary}"
15062
+ @dialog-cancel="${this._handleFormulaValidationDialogCancel}"
15063
+ @click="${this._handleFormulaValidationDialogCancel}"
15064
+ ></jupiter-formula-validation-dialog>
15065
+ ` : ""}
15066
+
13710
15067
  <!-- Validation Error Popup -->
13711
15068
  ${this._showErrorPopup ? html`
13712
15069
  <div class="error-popup-overlay" @click="${() => {
@@ -14623,6 +15980,9 @@ __decorateClass([
14623
15980
  __decorateClass([
14624
15981
  n2({ type: Boolean, attribute: "calculation-enabled" })
14625
15982
  ], JupiterDynamicForm.prototype, "calculationEnabled", 2);
15983
+ __decorateClass([
15984
+ n2({ type: Boolean, attribute: "formula-enabled" })
15985
+ ], JupiterDynamicForm.prototype, "formulaEnabled", 2);
14626
15986
  __decorateClass([
14627
15987
  n2({ type: String })
14628
15988
  ], JupiterDynamicForm.prototype, "periodStartDate", 2);
@@ -14695,6 +16055,9 @@ __decorateClass([
14695
16055
  __decorateClass([
14696
16056
  r()
14697
16057
  ], JupiterDynamicForm.prototype, "_decimalsData", 2);
16058
+ __decorateClass([
16059
+ r()
16060
+ ], JupiterDynamicForm.prototype, "_totalManuallyEditedData", 2);
14698
16061
  __decorateClass([
14699
16062
  r()
14700
16063
  ], JupiterDynamicForm.prototype, "_effectiveMasterData", 2);
@@ -14794,6 +16157,15 @@ __decorateClass([
14794
16157
  __decorateClass([
14795
16158
  r()
14796
16159
  ], JupiterDynamicForm.prototype, "_validationStatus", 2);
16160
+ __decorateClass([
16161
+ r()
16162
+ ], JupiterDynamicForm.prototype, "_formulaValidationResults", 2);
16163
+ __decorateClass([
16164
+ r()
16165
+ ], JupiterDynamicForm.prototype, "_formulaValidationSummary", 2);
16166
+ __decorateClass([
16167
+ r()
16168
+ ], JupiterDynamicForm.prototype, "_showFormulaValidationDialog", 2);
14797
16169
  JupiterDynamicForm = __decorateClass([
14798
16170
  t$1("jupiter-dynamic-form")
14799
16171
  ], JupiterDynamicForm);
@@ -14808,6 +16180,7 @@ export {
14808
16180
  JupiterFilterRolesDialog,
14809
16181
  JupiterFormField,
14810
16182
  JupiterFormSection,
16183
+ JupiterFormulaValidationDialog,
14811
16184
  TYPE_INPUT_MAP,
14812
16185
  XBRLValidator,
14813
16186
  collectEnumerationsFromChain,