Skip to content

feat(V2): rewrite XLSX import/export with metadata-driven V2 implementation#1538

Merged
TheLastCicada merged 4 commits into
v2-rc2from
feat/v2-xlsx-import-export
Mar 18, 2026
Merged

feat(V2): rewrite XLSX import/export with metadata-driven V2 implementation#1538
TheLastCicada merged 4 commits into
v2-rc2from
feat/v2-xlsx-import-export

Conversation

@TheLastCicada

@TheLastCicada TheLastCicada commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add missing hasMany associations to ProjectV2 for ValidationV2, VerificationV2, ProjectMethodologyV2, and StakeholderProjectV2 — enables findAll({ include: [...] }) to load all child records for XLS export
  • Create src/utils/v2-xls.js with metadata-driven helpers (buildXlsSchema, createV2Xls, parseV2Xlsx, stageV2XlsRecords) that derive XLS schema at runtime from Sequelize associations — no separate config to maintain
  • Rewrite V2 XLSX importProjectV2.updateFromXLS and UnitV2.updateFromXLS now use the new V2-specific utilities instead of V1's tableDataFromXlsx/collapseTablesData which silently failed on V2 model names
  • Fix V2 XLSX export — controllers now include all child associations when xls=true and use createV2Xls for proper sheet naming
  • Expand integration tests — verify actual staging records (INSERT vs UPDATE), support singular/plural sheet names, and validate Excel export headers

Design decisions

  • V1 code (src/utils/xls.js) is untouched — V1 and V2 have independent XLS implementations
  • Only new model property is static xlsSheetName on ProjectV2 and UnitV2; everything else is derived from existing Sequelize metadata
  • getAssociatedModels() is unchanged — new hasMany associations only affect Sequelize include queries, not staging/commit flow
  • Children are staged independently with their own table names, matching the V2 architecture where each model has its own controller and changelist method

Test plan

  • V2 integration tests pass (1275 passing, 2 pre-existing failures unrelated to this change)
  • Project XLSX import: INSERT new project, UPDATE existing project, singular sheet name accepted
  • Unit XLSX import: INSERT new unit, UPDATE existing unit, singular sheet name accepted
  • Project and Unit XLSX export returns valid .xlsx with correct content-disposition headers
  • Staging records contain correct action (INSERT/UPDATE) and data after import
  • Manual round-trip test: export via GET ?xls=true, re-import the downloaded file via PUT /xlsx

Note

Medium Risk
Changes the V2 XLSX import/export pipeline (sheet naming, association includes, staging upserts), which can affect data staging correctness and exported file structure; impact is limited to V2 endpoints and utilities.

Overview
Reworks V2 XLSX import/export for ProjectV2 and UnitV2 to be metadata-driven. Controllers now export via createV2Xls and, when xls=true, automatically include all HasMany child associations (avoiding duplicate includes when specific columns also request associations).

Rewrites V2 XLSX import to stage records consistently. ProjectV2.updateFromXLS and UnitV2.updateFromXLS switch from V1 parsing/collapsing helpers to new parseV2Xlsx + stageV2XlsRecords, which supports singular/plural sheet names, stages parent and child rows independently with StagingV2.upsert, and allows model-specific row prep (e.g. deriving unitSerialId).

Expands model associations and tests. ProjectV2 adds missing hasMany relationships (e.g. validations/verifications/etc.) and sets xlsSheetName on V2 models; integration tests are updated to assert staging INSERT vs UPDATE, tolerate background audit noise via baselines, and stabilize org-creation polling.

Written by Cursor Bugbot for commit bd5191a. This will update automatically on new commits. Configure here.

…tation

V2 XLSX import was non-functional — the V1 utility functions
(tableDataFromXlsx, collapseTablesData) could not match V2 model names
or foreign keys. V2 export omitted child records because associations
were missing. This commit:

- Add missing hasMany associations to ProjectV2 (validations,
  verifications, projectMethodologies, stakeholderProjects)
- Create src/utils/v2-xls.js with metadata-driven helpers that derive
  schema from Sequelize associations at runtime
- Rewrite ProjectV2/UnitV2 updateFromXLS to use new V2 utilities
- Fix export controllers to include all child associations when xls=true
- Add integration tests that verify actual staging (INSERT/UPDATE),
  singular/plural sheet name support, and Excel export
Comment thread src/controllers/v2/project-v2.controller.js
Comment thread src/utils/v2-xls.js
Comment thread src/models/v2/unit-v2.model.js
@TheLastCicada TheLastCicada changed the base branch from develop to v2-rc2 March 18, 2026 05:36
- Deduplicate Sequelize includes in project controller when xls=true
  and columns also request child associations (avoids duplicate alias
  error)
- Replace naive replace(/s$/, '') singularization with naiveSingular()
  that handles -ies → -y (e.g. projectMethodologies → projectMethodology)
- Restore unitSerialId derivation from block range via prepareXlsRow
  hook on UnitV2, called generically from stageV2XlsRecords
- Fix org creation test to skip PENDING record when polling for the
  finalized V1 organization
- Rewrite audit tests to use baseline-delta counts and future timestamps
  so background sync activity cannot cause false failures
Comment thread src/models/v2/project-v2.model.js
Comment thread src/utils/v2-xls.js
- Restore child record JSON parsing (locations, estimations, ratings,
  coBenefits) and cadTrustProjectId backfilling in
  updateProjectPropertiesV2, which is used by CSV batch upload
- Guard parseV2Xlsx against ragged rows where trailing cells are
  missing, preventing undefined values from overwriting existing
  fields during update staging

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread src/utils/v2-xls.js
Blank trailing rows in XLSX files were parsed as objects with all-null
values, causing stageV2XlsRecords to generate random UUIDs and stage
INSERT records with empty payloads. Add an isEmptyRow guard at the
top of both parent and child staging loops.
@TheLastCicada TheLastCicada merged commit fca2136 into v2-rc2 Mar 18, 2026
24 checks passed
@TheLastCicada TheLastCicada deleted the feat/v2-xlsx-import-export branch March 18, 2026 18:08
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