Persist V3 catalog advanced calculation settings
This PR fixes the V3 Product Catalog drawer so AdvancedSection calculation controls round-trip through save, merge, and reopen.
Disable volume ramp, tiered pricing, ramp strategy, blending, proration, and fixed-volume settings now survive save and reopen in V3 attribute-based catalog orgs.
A shared converter at @dealops/types/v2/advancedCalcAttrs defines the V2 form ↔ canonical V3 attribute mapping once.
The server clear list remains allowlisted. New clearable calc keys can be deleted, but arbitrary structural fields still cannot be stripped.
34/34 productDto.test.ts tests pass locally, including regression coverage for write → read round trips.
1. Why this exists
In V3 attribute-based catalog orgs, AdvancedSection calc settings could look saved in the admin drawer but reopen with stale or missing values.
disableVolumeRampdid not reliably persist.- Cleared strategy fields could survive server-side key-wise merge.
- V3
prorationcould be written but read through the older V2 key.
The quote editor depends on these persisted attributes to decide ramp behavior and related calculation affordances.
- Admins in Merge Test Org saw advanced settings revert.
- Quote ramp behavior could be incorrect after catalog edits.
- The bug sat at the V2-shaped form ↔ V3-shaped catalog boundary.
2. What changes
disableVolumeRamp.set, computes clear, hydrates drawer state.Key mapping
| AdvancedSection field | Canonical V3 attribute | Behavior |
|---|---|---|
disableVolumeRamp |
volumeRampEnabled |
Inverted polarity: disabling ramp writes volumeRampEnabled = false. |
disableTieredPricing |
tieredPricingEnabled |
Inverted polarity: disabling tiered pricing writes tieredPricingEnabled = false. |
defaultFlattenRampStrategy |
flattenRampStrategy |
Set when chosen; explicitly cleared when emptied. |
defaultBlendTieredStrategy |
blendTieredStrategy |
Set when chosen; explicitly cleared when emptied. |
prorateFirstAndLastMonth |
proration |
Maps count_fractional_month ↔ fractional_month and count_full_month ↔ full_month. |
fixedVolume |
fixedVolume |
Set when present; explicitly cleared when turned off. |
Meaningful file changes
packages/types/v2/advancedCalcAttrs.tsapps/client/.../ProductCatalog/utils/productDto.tsapps/server/src/trpc/router/v3Admin/catalog.tsclearAttributeKeys allowlist to the three clearable calc attributes.apps/client/.../__tests__/productDto.test.tsapps/server/src/dealops3/__tests__/catalogWrite.test.ts and updateProductSchema.test.tspackages/types/package.json3. How it works
v3CalcAttrsFromAdvanced serializes form values into two buckets:
set: values to write intosystemAttributesclear: V3 keys that must be deleted after merge
advancedFromV3CalcAttrs hydrates drawer values from stored attributes using a caller-provided read(key).
That keeps legacy fallback behavior inside productDto.ts intact.
attributeKeysToClear now appends only the converter’s clearable calc keys.
The server schema must allow the same keys before the mutation accepts them.
Before / after: save semantics
Omitting a cleared value was not enough.
base.systemAttributes = {
flattenRampStrategy: "last",
fixedVolume: 1
}
payload.systemAttributes = {
name: "Widget"
}
merge(base, payload)
// stale flattenRampStrategy and fixedVolume survive
Clearable calc attributes are named explicitly.
payload.clearAttributeKeys = [
"flattenRampStrategy",
"blendTieredStrategy",
"fixedVolume"
]
withClearedAttributes(merged, payload.clearAttributeKeys)
// stale calc attrs are removed
Polarity is preserved
disableVolumeRamp: true
AdvancedSection speaks in disabled toggles.
volumeRampEnabled: false
V3 stores canonical enabled flags, so the converter intentionally inverts booleans.
Hydration path used by the drawer
The new regression test exercises the same chain the drawer uses:
dtoToFamilyAndSku(dto)
→ buildProductSKUFormInitialData(product, productSKU)
→ advanced
This catches mismatches where save writes a V3 key but reopen reads the old V2 key.
4. What it doesn't change
- No new Prisma migration, table, or column.
- No REST route; this stays on the existing V3 admin tRPC update path.
- No broad delete API.
clearAttributeKeysremains a narrow enum, notz.string(). - No change to composite product proration behavior; composite SKUs keep the existing
full_monthtreatment. - No rewrite of the AdvancedSection UI itself.
- No change to unrelated catalog attributes beyond existing
categoryId/productLinecleanup behavior.
5. Risks / rollback / open questions
@dealops/types/v2/advancedCalcAttrs. Consumers must have the package build output available, and the monorepo package export was updated for that.
Revert the PR to restore prior V3 catalog behavior.
No data migration is introduced, so rollback is code-only. Any attributes already saved by the fixed path are ordinary V3 system attributes.
- Passing:
productDto.test.ts34/34 locally. - Added: server tests for schema allowlist and merge-clear behavior.
- Blocked: full client Jest suite by pre-existing
@dealops/types/src/telemetryenvironment issue. - Recommended: manual QA in Merge Test Org before merge.
advancedCalcAttrs.ts, productDto.ts, and v3Admin/catalog.ts.