jupiter-dynamic-forms 1.20.1 → 1.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6154,6 +6154,7 @@ let JupiterFormSection = class extends LitElement {
6154
6154
  this.periodStartDate = "";
6155
6155
  this.periodEndDate = "";
6156
6156
  this.language = "en";
6157
+ this.calculationEnabled = false;
6157
6158
  this._expanded = true;
6158
6159
  this._showAddColumnDialog = false;
6159
6160
  this._sectionPeriodType = "duration";
@@ -6162,7 +6163,7 @@ let JupiterFormSection = class extends LitElement {
6162
6163
  this._boundFieldBlur = (e2) => {
6163
6164
  this._clearHighlights();
6164
6165
  this._focusedConceptId = null;
6165
- if (this.mode === "readonly")
6166
+ if (this.mode === "readonly" || !this.calculationEnabled)
6166
6167
  return;
6167
6168
  const detail = e2.detail;
6168
6169
  if ((detail == null ? void 0 : detail.conceptId) && (detail == null ? void 0 : detail.columnId)) {
@@ -6198,8 +6199,12 @@ let JupiterFormSection = class extends LitElement {
6198
6199
  if (changedProperties.has("isFirstSection") || changedProperties.has("section")) {
6199
6200
  this._checkAndExpandFirstSection();
6200
6201
  }
6201
- if (changedProperties.has("section") && ((_a = this.section) == null ? void 0 : _a.concepts)) {
6202
- this._buildTotalMaps(this.section.concepts);
6202
+ if (changedProperties.has("section") || changedProperties.has("calculationEnabled")) {
6203
+ if (this.calculationEnabled && ((_a = this.section) == null ? void 0 : _a.concepts)) {
6204
+ this._buildTotalMaps(this.section.concepts);
6205
+ } else {
6206
+ this._clearCalculationState();
6207
+ }
6203
6208
  }
6204
6209
  }
6205
6210
  _checkAndExpandFirstSection() {
@@ -6435,6 +6440,21 @@ let JupiterFormSection = class extends LitElement {
6435
6440
  this._totalChildConceptsMap.clear();
6436
6441
  this._traverseTotalGroups(concepts);
6437
6442
  }
6443
+ /**
6444
+ * JDF-041: drops all calculation-warning state — used when the feature is disabled/toggled off.
6445
+ * Clears each existing mismatch through `_clearCalculationMismatch` (rather than resetting the
6446
+ * map directly) so the parent's validation summary, which tracks mismatches via the dispatched
6447
+ * `calculation-mismatch-changed` events, is told about each removal instead of being left stale.
6448
+ */
6449
+ _clearCalculationState() {
6450
+ this._totalChildrenMap.clear();
6451
+ this._memberParentMap.clear();
6452
+ this._totalConceptMap.clear();
6453
+ this._totalChildConceptsMap.clear();
6454
+ for (const key of Array.from(this._calculationMismatches.keys())) {
6455
+ this._clearCalculationMismatch(key);
6456
+ }
6457
+ }
6438
6458
  /**
6439
6459
  * JDF-038: XBRL uses more than one preferredLabel role to mark a "total" row — the plain
6440
6460
  * `.../role/totalLabel` and the sign-flipped `.../role/negatedTotalLabel` (used for subtotals of
@@ -6445,6 +6465,15 @@ let JupiterFormSection = class extends LitElement {
6445
6465
  static _isTotalLabel(preferredLabel) {
6446
6466
  return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("totallabel"));
6447
6467
  }
6468
+ /**
6469
+ * JDF-041: distinguishes a self-contained "deduction block" subtotal (negatedTotalLabel, e.g.
6470
+ * "Total of sum of expenses") from a plain cascading/running total. Only the latter is treated as
6471
+ * additively built on the total before it — a negatedTotalLabel row summarizes just the raw items
6472
+ * directly before it and must not also absorb an unrelated earlier total into its own check.
6473
+ */
6474
+ static _isNegatedTotalLabel(preferredLabel) {
6475
+ return !!(preferredLabel == null ? void 0 : preferredLabel.toLowerCase().includes("negatedtotallabel"));
6476
+ }
6448
6477
  /**
6449
6478
  * A member of a total's sibling/child group can itself be a presentation-only abstract wrapper
6450
6479
  * (e.g. "Group equity" wrapping Equity + Third party share + "Total of group equity") rather than
@@ -6475,6 +6504,47 @@ let JupiterFormSection = class extends LitElement {
6475
6504
  this._memberParentMap.set(memberId, totalConcept.id);
6476
6505
  }
6477
6506
  }
6507
+ /**
6508
+ * JDF-041: computes the JDF-030 sibling-fallback member set for a total at `concepts[i]`, given the
6509
+ * nearest preceding total's index (`lastTotalIdx`, -1 if none) and the raw siblings between them.
6510
+ *
6511
+ * - No preceding total → members are just the raw siblings (unchanged, e.g. the very first total
6512
+ * in the role).
6513
+ * - This total is itself a negatedTotalLabel (a self-contained deduction-block subtotal, e.g.
6514
+ * "Total of sum of expenses") → the preceding total stays a scan boundary only, never a member —
6515
+ * it summarizes just its own raw siblings, same as before JDF-041.
6516
+ * - Otherwise (a plain totalLabel, cascading/running total) with raw siblings before it → the
6517
+ * preceding total is included as a real member alongside them, since this total is built
6518
+ * additively on top of it.
6519
+ * - Otherwise (a plain totalLabel adjacent to the preceding total, zero raw siblings between them):
6520
+ * if that preceding total is itself a negatedTotalLabel subtotal, it doesn't carry the running
6521
+ * total from before it forward (per the rule above) — reach one more hop back to also include the
6522
+ * total the subtotal was deducted from (e.g. "Net operating result" needs both "Gross operating
6523
+ * result" and "Total of sum of expenses", not just the latter). Otherwise, the single preceding
6524
+ * total is the member, as it was for JDF-038's original adjacent-boundary case.
6525
+ */
6526
+ _resolveTotalGroupMembers(concepts, i2, lastTotalIdx, siblings) {
6527
+ if (lastTotalIdx < 0)
6528
+ return siblings;
6529
+ const concept = concepts[i2];
6530
+ if (JupiterFormSection._isNegatedTotalLabel(concept.preferredLabel)) {
6531
+ return siblings.length > 0 ? siblings : [concepts[lastTotalIdx]];
6532
+ }
6533
+ if (siblings.length > 0) {
6534
+ return [concepts[lastTotalIdx], ...siblings];
6535
+ }
6536
+ if (JupiterFormSection._isNegatedTotalLabel(concepts[lastTotalIdx].preferredLabel)) {
6537
+ let grandTotalIdx = -1;
6538
+ for (let k = lastTotalIdx - 1; k >= 0; k--) {
6539
+ if (JupiterFormSection._isTotalLabel(concepts[k].preferredLabel)) {
6540
+ grandTotalIdx = k;
6541
+ break;
6542
+ }
6543
+ }
6544
+ return grandTotalIdx >= 0 ? [concepts[grandTotalIdx], concepts[lastTotalIdx]] : [concepts[lastTotalIdx]];
6545
+ }
6546
+ return [concepts[lastTotalIdx]];
6547
+ }
6478
6548
  _traverseTotalGroups(concepts) {
6479
6549
  var _a, _b;
6480
6550
  for (let i2 = 0; i2 < concepts.length; i2++) {
@@ -6491,10 +6561,9 @@ let JupiterFormSection = class extends LitElement {
6491
6561
  }
6492
6562
  }
6493
6563
  const siblings = concepts.slice(lastTotalIdx + 1, i2);
6494
- if (siblings.length > 0) {
6495
- this._registerTotalGroup(concept, siblings);
6496
- } else if (lastTotalIdx >= 0) {
6497
- this._registerTotalGroup(concept, [concepts[lastTotalIdx]]);
6564
+ const members = this._resolveTotalGroupMembers(concepts, i2, lastTotalIdx, siblings);
6565
+ if (members.length > 0) {
6566
+ this._registerTotalGroup(concept, members);
6498
6567
  }
6499
6568
  }
6500
6569
  }
@@ -6529,7 +6598,7 @@ let JupiterFormSection = class extends LitElement {
6529
6598
  return;
6530
6599
  }
6531
6600
  const children = this._totalChildConceptsMap.get(totalConceptId) || [];
6532
- const { calculatedSum, hasAnyValue, breakdown } = JupiterFormSection._buildCalculationBreakdown(
6601
+ const { calculatedSum, hasAnyValue, breakdown } = this._buildCalculationBreakdown(
6533
6602
  children,
6534
6603
  this.formData,
6535
6604
  columnId,
@@ -6576,19 +6645,46 @@ let JupiterFormSection = class extends LitElement {
6576
6645
  static _resolveWeight(totalBalance, childBalance) {
6577
6646
  return !totalBalance || !childBalance || totalBalance === childBalance ? 1 : -1;
6578
6647
  }
6579
- static _buildCalculationBreakdown(children, formData, columnId, totalBalance) {
6648
+ /**
6649
+ * JDF-041: a group member can itself be a registered cascading total with no entered value (e.g. a
6650
+ * customer who fills in "Operating income" directly without ever populating the "Net operating
6651
+ * result" running-subtotal row it's built on). Treating a blank member as a zero contribution
6652
+ * silently drops whatever it's actually built from and produces a false-positive mismatch. Instead,
6653
+ * look through a blank total member into its own registered members, recursively, so the check
6654
+ * reaches the real entered values wherever they are in the chain. `visited`/`depth` guard against
6655
+ * cycles and unbounded recursion (mirrors `_resolveValueConcept`'s depth cutoff).
6656
+ */
6657
+ _resolveMemberContributions(member, weight, formData, columnId, visited, depth = 0) {
6580
6658
  var _a;
6659
+ const val = parseFloat((_a = formData[member.id]) == null ? void 0 : _a[columnId]);
6660
+ if (!isNaN(val)) {
6661
+ return [{ conceptId: member.id, label: member.label, value: val, balance: member.balance, weight }];
6662
+ }
6663
+ if (depth >= 5 || visited.has(member.id))
6664
+ return [];
6665
+ const nestedMembers = this._totalChildConceptsMap.get(member.id);
6666
+ if (!nestedMembers)
6667
+ return [];
6668
+ const nextVisited = new Set(visited).add(member.id);
6669
+ const contributions = [];
6670
+ for (const nested of nestedMembers) {
6671
+ const nestedWeight = weight * JupiterFormSection._resolveWeight(member.balance, nested.balance);
6672
+ contributions.push(...this._resolveMemberContributions(nested, nestedWeight, formData, columnId, nextVisited, depth + 1));
6673
+ }
6674
+ return contributions;
6675
+ }
6676
+ _buildCalculationBreakdown(children, formData, columnId, totalBalance) {
6581
6677
  let calculatedSum = 0;
6582
6678
  let hasAnyValue = false;
6583
6679
  const breakdown = [];
6584
6680
  for (const child of children) {
6585
- const val = parseFloat((_a = formData[child.id]) == null ? void 0 : _a[columnId]);
6586
- if (isNaN(val))
6587
- continue;
6588
- hasAnyValue = true;
6589
6681
  const weight = JupiterFormSection._resolveWeight(totalBalance, child.balance);
6590
- calculatedSum += weight * val;
6591
- breakdown.push({ conceptId: child.id, label: child.label, value: val, balance: child.balance, weight });
6682
+ const contributions = this._resolveMemberContributions(child, weight, formData, columnId, /* @__PURE__ */ new Set());
6683
+ for (const contribution of contributions) {
6684
+ calculatedSum += contribution.weight * contribution.value;
6685
+ hasAnyValue = true;
6686
+ breakdown.push(contribution);
6687
+ }
6592
6688
  }
6593
6689
  return { calculatedSum, hasAnyValue, breakdown };
6594
6690
  }
@@ -7251,6 +7347,9 @@ __decorateClass$3([
7251
7347
  __decorateClass$3([
7252
7348
  n2({ type: String })
7253
7349
  ], JupiterFormSection.prototype, "language", 2);
7350
+ __decorateClass$3([
7351
+ n2({ type: Boolean })
7352
+ ], JupiterFormSection.prototype, "calculationEnabled", 2);
7254
7353
  __decorateClass$3([
7255
7354
  r()
7256
7355
  ], JupiterFormSection.prototype, "_expanded", 2);
@@ -9292,6 +9391,7 @@ let JupiterDynamicForm = class extends LitElement {
9292
9391
  this.initialData = {};
9293
9392
  this.disabled = false;
9294
9393
  this.readonly = false;
9394
+ this.calculationEnabled = false;
9295
9395
  this.periodStartDate = "2025-01-01";
9296
9396
  this.periodEndDate = "2025-12-31";
9297
9397
  this.language = "en";
@@ -13035,6 +13135,7 @@ let JupiterDynamicForm = class extends LitElement {
13035
13135
  .masterData="${this._effectiveMasterData}"
13036
13136
  .periodStartDate="${this.periodStartDate}"
13037
13137
  .periodEndDate="${this.periodEndDate}"
13138
+ .calculationEnabled="${this.calculationEnabled}"
13038
13139
  @field-change="${this._handleFieldChange}"
13039
13140
  @period-change="${this._handlePeriodChange}"
13040
13141
  @typed-member-change="${this._handleTypedMemberChange}"
@@ -13182,6 +13283,7 @@ let JupiterDynamicForm = class extends LitElement {
13182
13283
  .masterData="${this._effectiveMasterData}"
13183
13284
  .periodStartDate="${this.periodStartDate}"
13184
13285
  .periodEndDate="${this.periodEndDate}"
13286
+ .calculationEnabled="${this.calculationEnabled}"
13185
13287
  @field-change="${this._handleFieldChange}"
13186
13288
  @typed-member-change="${this._handleTypedMemberChange}"
13187
13289
  @add-concept-repeat="${this._handleAddConceptRepeat}"
@@ -14518,6 +14620,9 @@ __decorateClass([
14518
14620
  __decorateClass([
14519
14621
  n2({ type: Boolean })
14520
14622
  ], JupiterDynamicForm.prototype, "readonly", 2);
14623
+ __decorateClass([
14624
+ n2({ type: Boolean, attribute: "calculation-enabled" })
14625
+ ], JupiterDynamicForm.prototype, "calculationEnabled", 2);
14521
14626
  __decorateClass([
14522
14627
  n2({ type: String })
14523
14628
  ], JupiterDynamicForm.prototype, "periodStartDate", 2);