SfSkills Domains Docs GitHub

Home / architect / cpq-integration-with-erp

cpq-integration-with-erp

Use this skill when designing or troubleshooting an integration between Salesforce CPQ and an ERP system (SAP, Oracle, NetSuite, Microsoft Dynamics). Covers the canonical order transmission trigger, pricing data mastery strategy, amendment delta-quantity handling, and non-blocking inventory checks during quoting. NOT for syncing standard Accounts, Products or Orders with an ERP in an org without CPQ — use architect/sales-cloud-integration-patterns. NOT for moving CPQ Price Rules and quote templates between orgs — use devops/cpq-deployment-administration.

Open the skill folder on GitHub


CPQ Integration with ERP

This skill activates when an architect or practitioner must design, implement, or troubleshoot a data and process integration between Salesforce CPQ and a back-office ERP system. It covers the canonical trigger point for order transmission, pricing data mastery strategy, amendment order delta handling, and non-blocking inventory availability checks during quoting.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

The ERP Trigger Point Is Order Activation, Not Quote Close

The most common integration design error is triggering ERP transmission when a CPQ quote reaches Closed Won or Approved. The correct trigger is Order record activation — a field value change on the Salesforce Order object (e.g., Status = Activated) after the Order has been generated from the approved quote.

Quote close fires while the order is still being generated and validated. ERP systems receiving partial or pre-validated order data produce fulfillment errors that are hard to trace. The Order record, by contrast, carries the full line-item detail required by ERP document types (Sales Order, Purchase Order) and has a stable queryable state.

The MuleSoft Accelerator for SAP uses a platform event or Order status change data capture (CDC) event as the trigger — not a quote-level webhook.

CPQ Quote Lines Are on SBQQ__QuoteLine__c, Not QuoteLineItem

Salesforce CPQ stores quote line detail on the custom object SBQQ__QuoteLine__c. The standard Salesforce object QuoteLineItem is not populated by CPQ. Any ERP integration that queries QuoteLineItem to retrieve CPQ product, quantity, or pricing data will return zero rows.

When the Order is generated from a CPQ quote, CPQ creates OrderItem records (standard object) linked to the Order. ERP integrations should consume OrderItem from the activated Order for order-time data. Quote-time data lives on SBQQ__QuoteLine__c.

Key fields on SBQQ__QuoteLine__c relevant to integration:

Pricing Mastery and Avoiding Price Book Drift

When ERP is the pricing master, two patterns are used:

Synchronous fetch at calculation time: The QCP or a custom Apex price rule calls out to the integration layer to fetch current ERP pricing during quote calculation. This keeps CPQ always aligned with ERP but introduces latency and availability dependency. Callouts from QCP are supported (unlike inventory checks), but must complete within governor limit windows.

Scheduled batch sync: A nightly or hourly batch job reads ERP pricing and upserts CPQ PricebookEntry records. Simpler operationally but introduces a staleness window. Acceptable when ERP prices change weekly or less frequently.

Maintaining a full copy of ERP pricing in CPQ price books without a controlled sync job causes drift within weeks due to discounting, promotions, and manual edits on either side.

Amendment Orders Are Delta Quantities, Not Net-New Totals

When a CPQ subscription is amended (e.g., adding 5 seats to an existing 20-seat deal), CPQ generates an Amendment Order with OrderItem.Quantity = 5 (the delta), not 25 (the new total). ERP systems that interpret every CPQ Order as a net-new order will create a duplicate fulfillment for 5 units instead of updating the existing subscription.

The integration must detect the Order type by checking SBQQ__AmendedContract__c on the originating quote or Order.SBQQ__Contracted__c. When an amendment is detected, the ERP integration must route to an "update existing contract" or "incremental release" transaction rather than a new Sales Order.


Common Patterns

Pattern 1: Order Activation Event to ERP via Platform Events

When to use: The integration middleware (MuleSoft, Boomi) can subscribe to Salesforce platform events or change data capture (CDC). The business requires reliable, at-least-once delivery with retry.

How it works:

  1. A Flow publishes a platform event (Order_Activated__e) when Order.Status transitions to the configured activated value.
  2. MuleSoft (or the middleware) subscribes to the platform event channel.
  3. The middleware queries the activated Order and its OrderItem records.
  4. The middleware maps OrderItem fields to the ERP Sales Order document schema.
  5. For amendments, the middleware checks Order.SBQQ__Contracted__c and routes to the ERP delta-update API.
  6. ERP confirmation (order number, status) is written back to Salesforce via REST or a second platform event as an external reference ID.

Why not the alternative (quote-close trigger): Quote close precedes Order generation. The Order object does not yet exist when the quote closes in some configurations, so the middleware cannot query OrderItem at that moment. Even when the Order exists, it may not yet have all line items written.

Pattern 2: Async Inventory Check via Invocable Action and LWC Button

When to use: Sales reps need to see inventory availability on the CPQ quote screen before submitting. QCP callout restrictions prevent synchronous inventory calls from the calculation engine.

How it works:

  1. A custom LWC component on the CPQ quote page presents a "Check Availability" button.
  2. The button fires an Apex invocable action (outside QCP) that performs the HTTP callout to the ERP inventory API.
  3. Results are stored on custom fields on SBQQ__QuoteLine__c (e.g., Inventory_Status__c, Available_Qty__c).
  4. The CPQ quote page displays the stored values — it does not show live data inline in the QCP.

Why not a QCP callout: QCP runs in a restricted Apex execution context. Direct HTTP callouts from QCP methods are not supported and throw System.CalloutException: Callout not allowed from this context. This is a platform limitation, not a configuration option.

Pattern 3: Scheduled Pricing Sync with Conflict Detection

When to use: ERP pricing changes are infrequent (weekly or monthly), latency tolerance is high, and the integration team cannot support synchronous QCP-time fetches.

How it works:

  1. A scheduled Apex batch job or MuleSoft scheduled flow queries ERP pricing data via API.
  2. The job upserts PricebookEntry records using Product2.ProductCode as the external ID match key.
  3. A conflict detection step compares the incoming ERP price against the current CPQ price before writing. If the delta exceeds a configured threshold (e.g., >5%), the job logs a Price_Sync_Alert__c record for manual review rather than silently overwriting.
  4. The job is idempotent — re-running produces the same result.

Decision Guidance

SituationRecommended ApproachReason
ERP needs order data after quote approvalOrder activation platform event to middleware to ERPEnsures full OrderItem data is available; reliable retry via middleware
ERP is pricing master, prices change dailySynchronous QCP callout to integration layer at calculation timeEliminates staleness window; acceptable if ERP API is fast and available
ERP is pricing master, prices change weeklyScheduled batch sync to PricebookEntrySimpler; reduces ERP API call volume; staleness acceptable
Amendment order detectedRoute to ERP delta-update API, not new Sales OrderCPQ amendment Orders carry delta quantity; net-new ERP order creates duplicates
Inventory check required during quotingAsync invocable action and LWC button, store results on quote lineQCP callout restriction; synchronous inline check is not supported
ERP integration queries QuoteLineItemRedirect queries to SBQQ__QuoteLine__c or activated OrderItemQuoteLineItem is not populated by CPQ
ERP trigger is set to Opportunity Closed WonMove trigger to Order.Status activated valueOrder may not exist at quote-close time

  1. Establish the trigger point and data contract. Confirm with stakeholders that Order activation (not quote close) is the ERP trigger. Document the exact Order.Status value and the OrderItem fields required by the ERP document type (product code, quantity, unit price, currency, ship-to address).
  2. Decide pricing mastery and sync strategy. If ERP is the pricing master, choose synchronous QCP fetch (for daily price changes) or scheduled batch sync (for weekly changes). Document the staleness tolerance. If CPQ is the pricing master, define how ERP pulls prices (read-only API access to Salesforce).
  3. Design amendment detection logic. Before building order transmission, add a branching condition that checks for amendment indicators (SBQQ__AmendedContract__c on the quote, or Order type flags). Define the ERP delta-update API call and its idempotency key.
  4. Build the async inventory check if required. Create the invocable Apex action that calls the ERP inventory API. Add the LWC button component to the CPQ quote page layout. Store results on SBQQ__QuoteLine__c custom fields. Do not attempt to embed this in QCP.
  5. Implement error handling and retry. Define the dead-letter queue or retry strategy in middleware. ERP callouts must have timeout handling. Write ERP-side order numbers back to the Salesforce Order as an external reference ID for deduplication.
  6. Test amendment and renewal scenarios explicitly. Create a CPQ amendment and confirm the integration routes to the delta-update path. Verify the quantity in ERP equals the delta, not the cumulative total.
  7. Review object references and run the checker script. Confirm all SOQL queries use SBQQ__QuoteLine__c for quote-time data and OrderItem for order-time data. Run scripts/check_cpq_integration_with_erp.py --manifest-dir path/to/metadata to catch common misconfigurations.

Review Checklist


Salesforce-Specific Gotchas

  1. QCP context does not permit HTTP callouts — The Quote Calculator Plugin executes in a restricted Apex context. Attempting an HTTP callout (e.g., to an ERP inventory API) from a QCP method throws System.CalloutException: Callout not allowed from this context. Inventory checks must be implemented outside QCP using an invocable action triggered by a button or flow.
  2. QuoteLineItem is empty for CPQ quotes — CPQ bypasses the standard QuoteLineItem object entirely. All CPQ quote line data resides on SBQQ__QuoteLine__c. Integrations built by teams unfamiliar with CPQ frequently query QuoteLineItem via SOQL and get zero rows, then incorrectly assume the quote has no line items.
  3. Amendment Orders carry delta quantity, not cumulative total — A CPQ amendment adding 10 seats to an existing 50-seat subscription generates an Order with OrderItem.Quantity = 10. If the ERP creates a new Sales Order for 10, the ERP subscription record shows 10 seats instead of 60. The integration must detect amendment Orders and route them to an ERP contract-update or incremental-release transaction.
  4. Quote close does not guarantee Order existence — CPQ Order generation can be asynchronous after quote close in some configurations. Triggering ERP transmission on Opportunity.StageName = Closed Won or SBQQ__Quote__c.SBQQ__Status__c = Approved may fire before the Order object has been created, resulting in no Order data to transmit and a silent failure.

Output Artifacts

ArtifactDescription
Integration trigger point decisionDocuments which Order.Status value signals ERP readiness and why
CPQ-to-ERP data mappingMaps SBQQ__QuoteLine__c / OrderItem fields to ERP document fields
Pricing sync architecture decisionRecords chosen sync strategy, staleness tolerance, and conflict-detection rules
Amendment routing designDocuments how amendment vs net-new Orders are distinguished and routed
Async inventory check designDocuments LWC and invocable Apex pattern for non-blocking inventory display


Official Sources Used

Package files