Compile Time Changes

class DsPart

Constructor Migration: List Price → Base Price + Option Price Sum

The DsPart class constructors have been expanded and split to support the new part pricing system (introduced in 16.5). New constructors now accept basePrice and optionPriceSum separately.

Old constructors that use list price remain available but are marked for future deprecation.

Old Constructors
  • constructor(Snapper, DsPData, double listPrice, …)
  • constructor(Snapper, DsPData, str articleCode, str description, double listPrice, …)
  • These constructors use list price (a single cached price) instead of separating base + options.
  • Still supported in 16.5 for compatibility.
New Constructors (preferred, introduced in 16.5)
  • constructor(Snapper, DsPData, double basePrice, Double optionPriceSum, …)
  • constructor(Snapper, DsPData, str articleCode, str description, double basePrice, Double optionPriceSum, …)
  • These constructors use separate base price and option prices instead of utilizing a single cached list price
  • Preferred in v16.5+
Impact
  • If you use list price constructors today → update to the new basePrice + optionPriceSum pattern
  • See new pricing API documentation in cm.core.part.Part compile-time section for more migration tips

New Pricing API

The following functions were introduced or modified to support the transition to the new part pricing system. Find more documentation on the new pricing system in the compile-time section for cm.core.part.Part.

Added: public Double generateOptionPriceSum() {}
Removed: public double basePrice() {}
Removed: public void performPriceConversions(ProjectInformation projectInfo) {}

Added: extend public Double generateOptionPriceSum()

Old → New behavior

Old:

  • Not applicable.

New:

  • If the Part has an associated pData (DsPData):
    • Delegates to pData.optionPriceSum(specOptions, this)
  • Otherwise:
    • Falls back to superclass implementation (super()).
Impact
  • If you have custom option price logic, override generateOptionPriceSum() in your extended Part class.
  • Invalidate the cache (invalidateOptionPriceSum()) when option state changes to trigger regeneration.

Removed: public double basePrice() {}

The basePrice method was expanded to support the new pricing framework by adding parameters and delegating to the superclass when new pricing is enabled.

Old: public double basePrice() {}
New: public double basePrice(bool includeChildren=false, Space space=null, bool translatePrice=true)
Old → New behavior

Old:

  • Simple signature:
    • public double basePrice()
  • Always derived base price from:
    • Adjustments via PartSpecial (replace or append)
    • _data.basePrice

New:

  • New signature with optional parameters:
    • public double basePrice(bool includeChildren=false, Space space=null, bool translatePrice=true)
  • Behavior changes:
    • If useNewPricing is enabled → delegates directly to super()
    • Otherwise → falls back to legacy logic (_data.basePrice with PartSpecial adjustments)
Impact
  • Functional expansion: method now supports contextual pricing (child parts, space, translation)
  • Under the new pricing system, logic comes from the superclass, not local overrides
  • Legacy behavior retained for backwards compatibility

Removed: public void performPriceConversions(ProjectInformation projectInfo) {}

  • This overidden method was removed as it is a complete duplicate of the super class version (in ProdPart).

New Lead Time Interface

As of 16.5, a new interface for lead time has been added to core Part and overridden in DsPart. It's value is displayed in the new LeadTimeColumn implemented in cm/core/init/leadTimeColumn.cm.

In cm/abstract/dataSymbol/parts.cm

/**
 * Get lead time string.
 * Ex. 20 days, 3 weeks, etc.
 */
public str leadTime() {
	DsProductType product = pData.product();
	if (!product) return super();

	int days;
	for (ref in product.leadTimeProgramRefs()) {
		DsLeadTimeProgramType ltp = pData.leadTimeProgram(ref);
		if (!ltp) continue;
		days = max(days, ltp.days);
	}

	return days > 0 ? spnn(days, ' ', days == 1 ? $day : $days) : "";
}

Other DsPart changes

Removed: public void generateOptAdjustmentSifRows(SpecOption opt, str[] lines) {}

The abstract ProdPart interface for generateOptAdjustmentSifRows has been changed to accept PartOptionItems instead of SpecOptions. As a result, the empty override of this function in DsPart has been removed entirely.

Old: public void generateOptAdjustmentSifRows(SpecOption opt, str[] lines) {}
Changed: public str optSpecialKey(SpecOption opt) {}

The abstract ProdPart interface for optSpecialKey has been changed to accept PartOptionItems instead of SpecOptions.

Old: public str optSpecialKey(SpecOption opt) {}
New: public str optSpecialKey(PartOptionItem  opt) {}
Changed: public PartInfoTree[] infoTrees() {}

When collecting PartInfoTrees on DsParts, the specOptions sequence is no longer filtered by DsSpecOptions. All options within the specOptions sequence will have a tree constructed.

public PartInfoTree[] infoTrees() {
	PartInfoTree[] items();
	PartInfoTree[] parents();
	
Old:	for (DsSpecOption tp in specOptions, index=i) {
New:	for (tp in specOptions, index=i) {
            ...
        }

	return items;
}
Changed: public str flattenableKey() {}

With the addition of the new part attribute description/notes system, DsParts now need to be differentiable/split by their attribute values.

The following change has been made in flattenableKey to account for this:

public str flattenableKey() {
	StrBuf buf(uniqueKey());
	
	...
	
New:	buf << annotationFlattenableKey();

	return buf.retireToS();
}

class DsFreeformPicklistPart

Removed: public double basePrice() {}

The basePrice method was expanded to support the new pricing framework by adding parameters and delegating to the superclass when new pricing is enabled.

Old: public double basePrice() {}
New: public double basePrice(bool includeChildren=false, Space space=null, bool translatePrice=true) {}
Old → New behavior

Old:

  • Simple signature:
    • public double basePrice()
  • Always derived base price from:
    • item.price()
    • Adjustments via PartSpecial (replace or append)

New:

  • New signature with optional parameters:
    • public double basePrice(bool includeChildren=false, Space space=null, bool translatePrice=true)
  • Behavior changes:
    • If useNewPricing is enabled → delegates directly to super()
    • Otherwise → falls back to legacy logic (item.price() with PartSpecial adjustments)

class DsFreeformItem

The following functions were added to support base price for DsFreeformItems.

Added: extend public str currentCurrencyKey(str key) {}
Added: extend public double basePrice() {}
Added: extend public double basePrice=(double value) {}

Added: extend public str currentCurrencyKey(str key) {}

The handling of currency-based pricing was refactored to make currentCurrencyKey more flexible.

Old → New behavior

Old:

  • currentCurrencyKey() directly constructed a key using cDsFPriceKey and the current currency symbol

New:

  • currentCurrencyKey() now delegates to a new overload:
    • currentCurrencyKey(str key) → builds a unique freeform key from the given key and currency symbol.

Added: extend public double basePrice() {} and extend public double basePrice=(double value) {}

Explicit support for base price has been added to DsFreeformItem.

Old → New behavior

Old:

  • Only list price (price) was supported via field data

New:

  • Added explicit basePrice field accessors:
    • basePrice() → retrieves the base price using cDsFBasePriceKey
    • basePrice=(double value) → sets the base price.

Runtime/Behavior Changes

class DsPData

The following functions were introduced or modified to support the transition to the new part pricing system. Find more documentation on the new pricing system in the compile-time section for cm.core.part.Part.

Added: extend public bool useNewPartPricing() {}
Added: extend public Double optionPriceSum(SpecOption[] opts, Part part=null) {}
Added: extend public DsPart createPart(Snapper snapper, PartsEnv env, double basePrice, Double optionPriceSum) {}
Changed: extend public void getParts(Snapper snapper, PartsEnv env) {}

Added: extend public bool useNewPartPricing() {}

Indicates whether the new part pricing system should be used when generating Parts. An opt-in feature flag to the new pricing system.

Behavior
  • If a proxy is present, defers to proxy implementation.
  • Defaults to returning false (need to opt-in to new pricing system)
  • Versioning Notes
    • OLD system (≤16.0): Pricing based on cached list price only.
    • NEW system (≥16.5): Pricing based on cached base price + optionPriceSum.
Impact
  • Acts as the feature flag for determining which pricing path is followed in constructors, getParts, and createPart.
  • Developers should begin migrating logic to use base price + option price sum rather than raw list price.

Added: extend public Double optionPriceSum(SpecOption[] opts, Part part=null) {}

Calculates the total option prices from a collection of SpecOptions. Optionally takes a Part parameter to allow for special pricing scenarios.

Behavior
  • If a proxy is present, delegates calculation to the proxy.
  • Otherwise, iterates through provided options and sums upcharges.
  • Returns the sum wrapped in a Double.
Impact
  • Centralizes logic for calculating option-based pricing.
  • Developers no longer need to manually aggregate upcharges for SpecOptions.
  • Provides proxy support for flexible overrides.

Added: extend public DsPart createPart(Snapper snapper, PartsEnv env, double basePrice, Double optionPriceSum) {}

The createPart method was split into two overloads to support the new pricing framework. The new overload takes basePrice and optionPriceSum, while the old single-parameter version (using list price) has been marked for deprecation.

Old → New behavior

Old:

  • Single method signature:
    • createPart(Snapper snapper, PartsEnv env, double price)
  • Relied on a unified “list price” model.
  • Retained for backward compatibility.
  • Still used when useNewPartPricing is false

New:

  • New primary method:
    • createPart(Snapper snapper, PartsEnv env, double basePrice, Double optionPriceSum)
  • Constructs parts with explicit basePrice and optional optionPriceSum
  • Used when useNewPartPricing is true
Impact
  • Developers should migrate to the new overload using basePrice and optionPriceSum
  • Better alignment with the new pricing system, which separates base price and option prices.

Changed: extend public void getParts(Snapper snapper, PartsEnv env) {}

The getParts method was updated to integrate the new pricing model.

Old → New behavior

Old:

  • Always calculated part price using price(options)
  • Always created parts with createPart(..., price)
  • Cached copies of parts but did not reset option price sum state.
Old:
extend public void getParts(Snapper snapper, PartsEnv env) {
	...

	if (usePartsCache and cachedParts.any) {
		...
	} else if (prd) {
		...	    
	    double price = price(options);
		...
	    DsPart dpart = createPart(snapper, env, price);
	   ...
	}
}

New:

  • Introduces flag:
    • bool newPricing = useNewPartPricing();
    • If true → price comes from basePrice(), and new createPart uses new signature
    • If false → price comes from price(options), and createPart uses legacy signature
  • Cached part copies now call invalidateOptionPriceSum() to reset option-based pricing.
New:
extend public void getParts(Snapper snapper, PartsEnv env) {
	...

	if (usePartsCache and cachedParts.any) {
		for (c in cachedParts) {
			Part cpy = c.copy;
			...
			cpy.invalidateOptionPriceSum();
		}
	} else if (prd) {
		...
		bool newPricing = useNewPartPricing();
		double price = newPricing ? basePrice() : price(options);
		...
		DsPart dpart = newPricing ? createPart(snapper, env, price, null) : createPart(snapper, env, price);
		
		...
	}
}
Impact
  • Pricing flexibility: supports both old list price calculation and new base price + option prices model
  • Cache correctness: avoids stale option price values by invalidating option price on cached Parts
  • Compatibility: existing logic still works when useNewPartPricing() is disabled.

SIF Export

With the addition of the new part attribute description/notes system, DsPData now exports these values during SIF exports in generateConfiguraSifRows(..):

final package str[] generateConfiguraSifRows(DsPart part, Space space) {
	...

		//Attributes
New:	part.generateSifAnnotationRows(env.lines);

	return res;
}

class DsFreeformPData

Changed: extend public void initPricelist() {}

The logic for initializing a pricelistRef was simplified by replacing an early return with direct initialization when the reference is empty.

Old → New behavior

Old:

  • If pricelistRef was already set, the method returned immediately and did nothing.
  • Otherwise, it explicitly set pricelistRef to the current currency symbol before creating a new PricelistType.
Old: 
extend public void initPricelist() {
	if (pricelistRef.any()) return;
	str code = currentCurrencySymbol();
	pricelistRef = code;
	Date currentDate(date());
	dataCatalog.pricelists.put(pricelistRef, PricelistType(pricelistRef, pricelistRef, currentDate));
}

New:

  • If pricelistRef is empty, it is set to the current currency symbol.
  • Regardless of prior state, the method always inserts/updates the pricelistRef entry in dataCatalog.pricelists
New:
extend public void initPricelist() {
	if (pricelistRef.empty()) pricelistRef = currentCurrencySymbol();

	Date currentDate(date());
	dataCatalog.pricelists.put(pricelistRef, PricelistType(pricelistRef, pricelistRef, currentDate));
}

Changed: public DsPart createPart(..) {}

The createPart API was extended to support a new pricing model with separate basePrice and optionPriceSum parameters. The original listPrice version remains for backward compatibility but is now marked for deprecation.

Old → New behavior

Old:

  • Single createPart method signature:
    • public DsPart createPart(Snapper snapper, PartsEnv env, double price)
  • Pricing was passed as a single price (list price)
  • Called in getParts when useNewPartPricing is false

New:

  • New primary createPart method signature:
    • public DsPart createPart(Snapper snapper, PartsEnv env, double basePrice, Double optionPriceSum)
    • Separates base price from option price additions, allowing more flexible and accurate pricing.
  • Called in getParts when useNewPartPricing is true
Impact
  • More transparent pricing calculation with clearer separation of base vs. option-based prices
  • Developers should migrate from using listPrice to passing basePrice and optionPriceSum

Changed: public void getParts(Snapper snapper, PartsEnv env) {}

The getParts method was updated to support the new pricing model which is documented in the compile-time section for cm.core.part.Part. Instead of always creating parts with a single price, it now conditionally uses basePrice when the new pricing system is enabled.

Old → New behavior

Old:

  • Always created parts using:
    • DsPart dpart = createPart(snapper, env, freeformItem.price());
  • Relied solely on list price (price) for part creation.
Old:
public void getParts(Snapper snapper, PartsEnv env) {
	...
	if (usePartsCache and cachedParts.any) {
		...
	} else {
		DsPart dpart = createPart(snapper, env, freeformItem.price());
	    ...
	}
}

New:

  • Introduces conditional logic via useNewPartPricing():
    • If enabled → parts are created using basePrice (and optionPriceSum = null).
    • If disabled → falls back to old price-based method.
  • Caching and spec option handling remain unchanged.
New:
public void getParts(Snapper snapper, PartsEnv env) {
	...
	if (usePartsCache and cachedParts.any) {
		...
	} else {
		DsPart dpart;
		if (useNewPartPricing()) {
			dpart = createPart(snapper, env, freeformItem.basePrice(), null);
		} else {
			dpart = createPart(snapper, env, freeformItem.price());
		}
		...
	}
}
Impact
  • Supports transition to the new pricing system without breaking existing functionality.
  • Developers must be aware that part creation may now depend on either basePrice or price, depending on configuration.

class DsFreeformPicklistPart

Constructor Behavior Change: List Price → Base Price + Option Price Sum

The constructor for parts created from DsFreeformPData was updated to support the new pricing model. It now conditionally passes basePrice and optionPriceSum to the superclass instead of always relying on listPrice.

Old → New behavior

Old:

  • Always invoked superclass with DsFreeformItem price:
    • super(snapper, fpData, fpData.freeformItem.price(), qty);
  • Relied solely on list price for part initialization.

New:

  • Constructor now checks useNewPartPricing():
    • If enabled → calls superclass with basePrice and optionPriceSum=null.
    • If disabled → calls superclass with listPrice as before.
Impact
  • Parts are initialized with either listPrice or basePrice depending on pricing model configuration.
  • Developers relying on constructor parameters should be aware of the new basePrice/optionPriceSum path.

class DsPDataProxy

The following functions were introduced or modified to support the transition to the new part pricing system. Find more documentation on the new pricing system in the compile-time section for cm.core.part.Part.

Added: extend public bool useNewPartPricing(DsPDataProxyEnv dsEnv) {}
Added: extend public Double optionPriceSum(DsPDataProxyEnv dsEnv, SpecOption[] opts, Part part=null) {}
Added: extend public DsPart createPart(DsPDataProxyEnv dsEnv, PartsEnv env, double basePrice, Double optionPriceSum) {}

Added: extend public bool useNewPartPricing() {}

Allows proxy-aware code to check whether the new pricing system is enabled.

Behavior
  • Returns false by default. Need to opt-in to using new pricing system.
Impact
  • Enables consistent feature flag checks for new vs. old pricing when executing through proxies

Added: extend public Double optionPriceSum(DsPDataProxyEnv dsEnv, SpecOption[] opts, Part part=null) {}

A new overload of optionPriceSum was introduced to support execution through a DsPDataProxy, enabling proxy-aware option pricing calculations.

Behavior
  • Temporarily increments blockDataProxy to prevent recursive proxy calls.
  • Delegates the actual calculation to dsEnv.data.optionPriceSum(opts, part).
  • Ensures blockDataProxy is decremented in a finally block for safety.
Impact
  • Required when option price calculation must occur through a proxy environment (DsPDataProxyEnv).
  • Complements the existing non-proxy optionPriceSum(SpecOption[], Part) method.
  • Provides consistency in environments where proxy delegation is active.
  • Developers writing proxy-enabled logic should use this overload when a DsPDataProxy is available to ensure correct delegation.

Added: extend public DsPart createPart(DsPDataProxyEnv dsEnv, PartsEnv env, double basePrice, Double optionPriceSum) {}

The createPart method was split into two overloads to support the new pricing framework. The new overload takes basePrice and optionPriceSum, while the old single-parameter version (using list price) has been marked for deprecation.

Old → New behavior

Old:

  • Single method signature:
    • createPart(DsPDataProxyEnv dsEnv, PartsEnv env, double price)
  • Relied on a unified “list price” model.
  • Retained for backward compatibility.
  • Still used when useNewPartPricing is false

New:

  • New primary method:
    • createPart(DsPDataProxyEnv dsEnv, PartsEnv env, double basePrice, Double optionPriceSum)
  • Returns null by default
  • Used when useNewPartPricing is true
Impact
  • Proxy providers may implement this method to customize createPart behavior in the new pricing system.
  • Better alignment with the new pricing system, which separates base price and option prices.