Enable the extended line numbering feature to support large-scale asset management. This enhancement increases root asset capacity from 9,999 to 9,999,999, ensuring unique identifiers for high-volume accounts and preventing duplicate numbering during assetization.
Required Editions
| Available in: Lightning Experience |
| Available in: Enterprise, Unlimited, Performance, and Developer Edition. |
| User Permissions Needed | |
|---|---|
| Set of permissions required Configuring Extended Asset Line Numbering: | CME Managed Package |
Before you begin, create the provided batch class and include the appropriate namespace where required. After the code is final, run the batch class to begin the process.
Ensure to fulfill these conditions while you execute the script:
- Set UseAssetReferenceIdForParentAndRoot to True.
- Set ExtendedLineNumberModeEnabled to True.
- The value of AssetReferenceId__c is unique for each account.
- Run the script within the context of a single account.
public with sharing class DedupAssetLineNumberBatchJob implements Database.Batchable<SObject>, Database.Stateful {
private Set<String> usedRootLineNumbers = new Set<String>();
private Integer highestNumericRootLineNumber = null;
private Id accountId;
public DedupAssetLineNumberBatchJob(Id accountId) {
this.accountId = accountId;
}
public Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator([SELECT Id, AssetReferenceId__c,NumericRootLineNumber__c, LineNumber__c FROM Asset WHERE AccountId = :accountId AND LineNumber__c != null AND (NOT LineNumber__c LIKE '%.%') ORDER BY NumericRootLineNumber__c desc nulls last]);
}
public void execute(Database.BatchableContext BC, List<SObject> scope) {
dedupAssetLineNumber(scope);
}
public void finish(Database.BatchableContext BC) {
}
private void dedupAssetLineNumber(List<SObject> scope) {
List<Asset> rootAssetsWithDupilcateLineNumbers = new List<Asset>();
for(SObject assetSO : scope) {
Asset asset = (Asset)assetSO;
String rootLineNumber = asset.LineNumber__c;
Integer numericRootLineNumber = Integer.valueOf(asset.NumericRootLineNumber__c);
//initialize the highestNumericRootLineNumber
if(this.highestNumericRootLineNumber == null) {
this.highestNumericRootLineNumber = numericRootLineNumber;
}
//collect the root assets with duplicate line numbers
if(usedRootLineNumbers.contains(rootLineNumber)) {
rootAssetsWithDupilcateLineNumbers.add(asset);
}
usedRootLineNumbers.add(rootLineNumber);
}
this.highestNumericRootLineNumber = renumberAssetsAndChildren(rootAssetsWithDupilcateLineNumbers, this.highestNumericRootLineNumber, usedRootLineNumbers);
}
private Integer renumberAssetsAndChildren(List<Asset> rootAssetsWithDupilcateLineNumbers, Integer highestNumericRootLineNumber, Set<String> usedRootLineNumbers) {
Set<String> assetReferenceIds = new Set<String>();
Map<String, String> rootAssetReferenceIdToNewRootLineNumber = new Map<String, String>();
for(Asset asset : rootAssetsWithDupilcateLineNumbers) {
assetReferenceIds.add(asset.AssetReferenceId__c);
highestNumericRootLineNumber ++;
String newRootLineNumber = padLeft(highestNumericRootLineNumber);
usedRootLineNumbers.add(newRootLineNumber);
rootAssetReferenceIdToNewRootLineNumber.put(asset.AssetReferenceId__c, newRootLineNumber);
}
List<Asset> assetsToRenumber = [SELECT AssetReferenceId__c, RootItemId__c , LineNumber__c FROM Asset WHERE RootItemId__c IN :assetReferenceIds ORDER BY LineNumber__c ASC];
for(Asset assetToRenumber : assetsToRenumber) {
String newRootLineNumber = rootAssetReferenceIdToNewRootLineNumber.get(assetToRenumber.RootItemId__c);
if(assetToRenumber.LineNumber__c.contains('.')) {
List<String> rootLineNumberSections = assetToRenumber.LineNumber__c.split('\\.');
rootLineNumberSections[0] = newRootLineNumber;
assetToRenumber.LineNumber__c = String.join(rootLineNumberSections, '.');
}
else {
assetToRenumber.LineNumber__c = newRootLineNumber;
}
}
update assetsToRenumber;
return highestNumericRootLineNumber;
}
private String padLeft(Integer i) {
String str = String.valueOf(i);
while (str.length() < 7) {
str = '0' + str;
}
return str;
}
}
Also find the Batch Invocation sample for reference:
//replace with account Id
Id accountID = 'someAcountId';
DedupAssetLineNumberBatchJob dedup = new DedupAssetLineNumberBatchJob(accountId);
//lower batchSize if governor limits are reached
Integer batchSize = 200;
Database.executeBAtch(dedup,batchSize);
The steps to enable the extended line numbering feature.
- In Setup, find and select Custom Settings.
- On the custom settings, locate the CPQ Configuration Setup and click Manage.
- Find and select ExtendedLineNumberModeEnabled setting.
- Select the checkbox to enable the seven-digit numbering format.
- Save the changes.
After you enable this setting, you can’t turn it off. This restriction ensures continuous data integrity for your asset records.
Post-Activation Steps
Verify API Integration: Check any external systems that parse line numbers. Make sure that they can process the new seven-digit format.
Test MACD Flows: Perform a "Change" or "Move" operation to verify that the system correctly converts extended asset numbers back to the four-digit format within the cart.
Use Case Scenario
As an out-of-the-box behaviour the Industries CPQ assigns each asset a line number starting at 1 and incrementing sequentially. Once the counter exceeds 9,999 for a given Account, the system either fails to insert the next asset or assigns it a duplicate line number.The duplicate line numbers cause MACD operations to fail as the MACD logic uses the line number to identify the root of an asset bundle.
When ExtendedLineNumberModeEnabled is set to true alongside its companion setting UseAssetReferenceIdForParentAndRoot as true, the system switches to a longer, non-colliding line number format starting from the very first asset. Every asset on the Account retains a unique line number regardless of volume, supporting millions of assets under a single business Account.
This change is permanent and cannot be reversed.
Issue:
A B2B telecommunications provider manages a national retailer with 15,000 store locations, each carrying three subscriptions — roughly 45,000 root assets — all contracted under a single Account. As the asset count crosses 9,999, duplicate line numbers begin to appear, causing downstream operational failures.
Before enabling Extended Asset Line Numbering, evaluate whether assets can be split across regional child Accounts in an Account hierarchy. If each child Account stays below 9,999 root assets and downstream systems can roll up to the parent for consolidated views, no CPQ setting change is needed.
If the customer requires a single billing Account — for contractual or operational reasons — enable ExtendedLineNumberModeEnabled. This expands the root line number from four digits to seven digits, raising the per-Account ceiling to 9,999,999 root assets.
Solution:
All 45,000 root assets coexist on one Account with unique line numbers. New root assets use the seven-digit format immediately. Existing assets migrate progressively through normal checkout or MACD flows, preserving backward compatibility. Downstream MACD, billing, and reporting operations continue without interruption.
ExtendedLineNumberModeEnabled does not include these actions:
-
- Asset-to-Order or Asset-to-Quote MACD performance, locking, or lookup skew.
- How assets are attached to Accounts.
- The foreign keys such as Asset.AccountId, Asset.vlocity_cmt__Billing AccountId__c, or Asset.vlocity_cmt__ServiceAccountId__c are unchanged in spite of being in the same rows having the same parents.
- How the database locks Accounts.
- How MACD orchestration runs.