Terms Library now authors V3 deal terms

For Dealops V3 orgs, Terms Library edits move from in-place V2 term writes to V3 flowSpec.calculator.dealTerms writes that auto-publish and project back to V2.

Author: @pk675 PR: dealops#6444 Ticket: DEA-7059 Stack: Dealops V3 + V2 projection Files: 16 Delta: +786 / -107 State: Open

What it fixes

V3 orgs could edit terms in the Terms Library, but those edits only touched the V2 pricingFlowSpec. The V3 flowSpec stayed stale.

What it adds

A V3 Terms Library backend: read from calculator.dealTerms, transform the V2 UI Term shape, write through v3Admin.updateFlowSpec, and auto-publish.

What it preserves

Non-V3 orgs keep the legacy term.* path. V2-owned calculator content such as pricing tables, columns, and components remains owned by the Pricing Calculator.

Terms Library client
V3 flowSpec
Publish / sync projection
V2 pricingFlowSpec

1. Why this exists

Before

On DEALOPS_V3 orgs, the Terms Library updated the old V2 term list in place.

  • V3 flowSpec.calculator.dealTerms did not receive the edit.
  • Publish could carry the old V2 calculator step forward, masking the mismatch.
  • Reseed or full regeneration from V3 could wipe the Terms Library edits.
After

For V3 orgs, Terms Library edits become normal V3 flowSpec edits.

  • V3 is authoritative for deal terms.
  • Each create / update / delete / reorder writes a new V3 version.
  • Publish projects those V3 terms into a new V2 pricingFlowSpec version.
Consequence: on first publish after this change, an org whose V2 terms diverged from V3 will have V2 snapped to the V3 term set. The Terms Library reads V3 for V3 orgs, so the UI shows the authoritative set before publishing.

2. What changes

Client hook split V3 term schema expanded Publish projection updated V2 runtime preserved
Terms Library V2 Term UI shape flowSpec calculator.dealTerms[] auto-publish new version pricingFlowSpec calculator term items legacy V2 orgs keep term.* mutations
Client adapter

termFlowSpec.ts maps between V3 DealTermShape and the V2 Term shape used by the existing table and drawer.

Pure transforms: append, replace, remove, reorder.

Backend selector

useTermsBackend picks V3 or V2 behavior from organization.pricingFlowType === 'DEALOPS_V3'.

Same hook surface for create / update / delete / reorder.

Server projection

mergeV3ProductSelectionIntoV2FlowSpec can now project deal-term grid items from the converted V3 doc into the current V2 calculator step.

Only enabled by publish / rollback full-flowSpec sync.

Meaningful files

apps/client/src/api/hooks/terms/termFlowSpec.ts

New adapter and pure transform module for V3 deal terms.

useListDealTerms.ts + usePersistDealTerms.ts

Reads V3 flowSpec and writes read-modify-write updates through updateFlowSpec.

useTermsBackend.ts

Central branch between V3 flowSpec backend and legacy V2 term.* mutations.

pricingFlowSpecMutations.ts

Adds calculator term projection while preserving V2-owned non-term items.

dealops3/flowSpec/types.ts + v2Converter.ts

Extends deal term schema and emits V2-compatible term fields.

pricingFlowSpecMutations.test.ts

New Mocha coverage for projection, final deletion, opt-out behavior, and no-current-V2 behavior.

3. How it works

Read path
  1. useIsV3Org() checks pricingFlowType.
  2. V3 orgs query v3Admin.getFlowSpec.
  3. extractDealTerms() reads flowSpec.calculator.dealTerms ?? [].
  4. dealTermsToV2Terms() adapts to the existing Terms Library UI contract: { terms }.
Write path
  1. Mutation hooks call useTermsBackend().
  2. V3 ops become pure DealTermShape[] → DealTermShape[] transforms.
  3. usePersistDealTerms() fetches the freshest flowSpec.
  4. withDealTerms() replaces only calculator.dealTerms, then updateFlowSpec publishes.
Operation V3 org behavior Legacy V2 org behavior
Create appendDealTerm(term) then updateFlowSpec term.addTerm
Update replaceDealTermById(term); preserves V3-only fields via merge term.updateTerm
Delete removeDealTermById(termId); deleting the final term clears V2 projected term items term.deleteTerm, then renumber surviving terms
Reorder reorderDealTerms(terms); array position is order term.updateMultipleTerms

Projection rules in the V2 calculator step

mergeV3ProductSelectionIntoV2FlowSpec(..., projectDealTerms)
mergeV3ProductSelectionIntoV2FlowSpec(
  currentFlowSpec.pricingFlowSpecData,
  convertedV2FlowSpecData,
  true // full V3 publish / rollback: dealTerms are authoritative
)

4. Schema and conversion details

New V3 term fields

Additive optional fields on dealTermSchema:

status: ON | OFF | ADMIN category orderFormNote

Absent status means ON.

New term types

The V3 enum now accepts Terms Library types that map directly into V2 UI config.

currency checkbox
V2 converter

buildV2Term emits the new fields into the V2 shape.

  • status defaults to ON.
  • category goes to uiConfig.category.
  • orderFormNote stays term-level.
Existing specs without these new optional fields convert byte-identically for the previously modeled fields because defaults preserve old behavior.

5. What it doesn't change

Explicit non-goals

6. Risks / rollback / open questions

Primary migration risk: if a V3 org has V2 terms that intentionally diverged from V3 terms, the first publish after this change will overwrite the V2 calculator term items with the V3 set. That is intended by the PR because V3 becomes authoritative.
Rollback shape
  • Reverting the client backend selector would send Terms Library writes back to the legacy V2 term.* path.
  • Reverting the server projectDealTerms path would stop V3 terms from being projected during publish.
  • Those two pieces should roll back together; otherwise reads and writes can disagree again.
Review focus
  • Confirm useTermsBackend() hook composition is acceptable even though it constructs both V2 and V3 backend objects before selecting one.
  • Confirm projection callers are correct: full V3 publish / rollback pass true; partial or lossy sync leaves it false.
  • Confirm final-term deletion should clear V2 term items for V3 orgs, as covered by the new Mocha test.
Test signal

New server Mocha tests cover term projection into V2, preservation of the V2 pricing table, final-term deletion, opt-out behavior with projectDealTerms = false, and the no-current-V2-row case. Existing publish tests are updated to assert that V3 deal terms now overwrite stale V2 term labels while non-term calculator content remains V2-owned.