Skip to content

Fix #42: pivot cache triggers Excel repair#74

Open
senoff wants to merge 1 commit into
protobi:masterfrom
senoff:senoff/fix-42-pivot-cache-repair
Open

Fix #42: pivot cache triggers Excel repair#74
senoff wants to merge 1 commit into
protobi:masterfrom
senoff:senoff/fix-42-pivot-cache-repair

Conversation

@senoff
Copy link
Copy Markdown

@senoff senoff commented May 7, 2026

Original Problem

Workbooks with programmatically created pivot tables opened in Excel with a repair dialog: "Repaired Records: PivotTable report from /xl/pivotCache/pivotCacheDefinition1.xml part (PivotTable cache)". This made the feature unusable for production. See #42.

Cause

Two independent bugs in the pivot cache serialization:

  1. recordCount on <pivotCacheDefinition> was set to cacheFields.length + 1 (the number of columns plus one). Excel validates this attribute against the count attribute of <pivotCacheRecords>, which is the number of data rows. A mismatch is one of the triggers for the repair dialog.

  2. makeCacheFields in lib/doc/pivot-table.js enumerated full sharedItems for ALL source fields, including value fields (e.g. a "Counter" column) and unused fields (e.g. a long-text "Description" column). Excel's strict cache validator flags full sharedItems enumeration for non-axial fields, especially when those fields contain high-cardinality or long strings.

Fix

lib/xlsx/xform/pivot-table/pivot-cache-definition-xform.js: compute recordCount = Math.max(0, sourceSheet.getSheetValues().length - 2) (data rows = total rows minus header row, minus the one-based offset of getSheetValues).

lib/doc/pivot-table.js: pass only axial field names ([...rows, ...columns, ...pages]) as fieldNamesWithSharedItems to makeCacheFields. Added inspect() helper that derives type hints (containsNumber, containsString, containsBlank) for non-axial fields without enumerating their values. Non-axial fields now produce {name, sharedItems: null, hints}.

lib/xlsx/xform/pivot-table/cache-field.js: updated the sharedItems === null render path to emit a lightweight <sharedItems .../> with correct type attributes derived from hints (was hard-coded containsNumber="1" containsInteger="1" which was wrong for string/mixed fields).

Files changed

  • lib/doc/pivot-table.jsmakePivotTable (axialFieldNames), makeCacheFields (signature + inspect helper + result loop)
  • lib/xlsx/xform/pivot-table/pivot-cache-definition-xform.jsrecordCount computation
  • lib/xlsx/xform/pivot-table/cache-field.jsconstructor (accepts hints), render (lightweight sharedItems)
  • spec/integration/issues/issue-42-pivot-cache-repair.spec.js — new test (5 cases)

Test Run

5 tests verify: cache definition present, recordCount matches ROW_COUNT (20), Description field has no enumerated items (self-closing sharedItems), Repo/Severity axial fields retain full enumeration, workbook round-trips without error.

5 passing (36ms)

Cross-PR check

lib/doc/pivot-table.js is also touched by PR #57 (formatter sweep, formatting only — no logic). No other open senoff PR touches the pivot cache files.

Excel/soffice verification

soffice is not available in this worker environment. The test verifies the XML structure through JSZip inspection: recordCount matches the data row count and non-axial sharedItems is self-closing. Recommend maintainer open the output file in Excel to confirm the repair dialog no longer appears before merge.

grace-review summary

Run 1 — gpt-5.5: HIGH (non-axial text fields serialized as numeric records). Dismissed: pivot-cache-records-xform.js already branches on Number.isFinite(value) — text values emit <s v="..."/> not <n v="..."/>. gemini: HIGH (incomplete sharedItems attributes). Addressed: added correct containsString/containsSemiMixedTypes/containsNumber attrs for all hint combinations.

Note: committed with --no-verify per AGENTS.md Rule 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant