Thursday, September 10, 2026

A Salesforce API 67 gotcha: updating queried records in user mode

I hit this while testing an agent workflow that runs under a dedicated service user. The model call completed successfully, but saving the command outcome failed with this:

System.DmlException: Operation failed due to fields being inaccessible on
Sobject AgentCommand__c, check errors on Exception or Result!

At first glance, it looked like a missing permission. The service user had edit access to AgentCommand__c, and its permission set granted access to the fields the worker updated. That turned out not to be the issue.

What changed

In API 67, Apex database operations run in user mode by default. That brings object permissions and field-level security into play for ordinary DML. without sharing does not change that; it affects record sharing, not whether DML validates field access.

The failure surfaced in code that looked harmless:

AgentCommand__c command = [
    SELECT Id, AgentRun__c, Status__c, ExpectedRunRevision__c
    FROM AgentCommand__c
    WHERE Id = :commandId
    FOR UPDATE
];

command.Status__c = 'Succeeded';
update command;

The command's AgentRun__c field is a required, non-reparentable master-detail relationship. It has to be supplied on insert, but it is not updateable. It is also not something that can be added as an editable field permission.

Although the code only changed Status__c, the SObject passed to update also contained AgentRun__c because it came from the query. In user mode, Salesforce validates the populated fields on that record. The unchanged master-detail field was enough to fail the update.

How I verified it

I ran a small anonymous Apex reproduction in the affected scratch org:

  1. Query a command including AgentRun__c.
  2. Update that queried instance with AccessLevel.USER_MODE.
  3. Inspect DmlException.getDmlFieldNames(0).
  4. Update a new record containing only Id and Status__c.

The first update failed and reported AgentRun__c. The second update succeeded. That ruled out a missing object permission and identified the relationship field as the problem.

The fix

Don't DML the queried record. Build a sparse update record with only the fields that the operation owns:

AgentCommand__c commandUpdate = new AgentCommand__c(
    Id = command.Id,
    Status__c = command.Status__c,
    Output__c = command.Output__c,
    ErrorCode__c = command.ErrorCode__c,
    ErrorMessage__c = command.ErrorMessage__c,
    LeaseToken__c = command.LeaseToken__c,
    LeaseExpiresAt__c = command.LeaseExpiresAt__c
);
update commandUpdate;

For our lifecycle code, several paths update commands that were loaded by queries: normal completion, timeout handling, lease recovery, and payload retention. I centralized those writes behind a helper:

private static void updateCommands(List<AgentCommand__c> commands) {
    if (commands == null || commands.isEmpty()) {
        return;
    }
    List<SObject> updateable = Security
        .stripInaccessible(AccessType.UPDATABLE, commands, true)
        .getRecords();
    update updateable;
}

Security.stripInaccessible removes fields the execution user cannot update, including the populated master-detail field. It is a useful safety net for a record that originated from a query. Sparse records are still the clearest approach when the set of changed fields is known.

The lesson

Required does not mean updateable. A required master-detail relationship is a good example: it is essential when creating the child record and invalid on a later update if reparenting is disabled.

When an API 67 user-mode update reports an inaccessible field, inspect DmlException.getDmlFieldNames(0) before changing permission sets. The offending field may be a system-managed field that should never be in the update payload in the first place.

Share This:    Facebook Twitter

Tuesday, August 4, 2026

Debugging INSUFFICIENT_ACCESS_OR_READONLY in Apex

We recently ran into this Salesforce error during an update operation:

Update failed. First exception on row 3 with id <recordId>; first error: INSUFFICIENT_ACCESS_OR_READONLY, insufficient access rights on object id: []

At first, the error did not clearly point to the root cause. To debug it, we added a small check before the update statement in our Apex queue class to verify whether the running user had edit access to the records being processed.

Set<Id> recordIds = new Map<Id, CustomObject__c>(recordsToUpdate).keySet();
Set<Id> recordsWithoutEditAccess = new Set<Id>();

for (UserRecordAccess access : [
    SELECT RecordId, HasEditAccess
    FROM UserRecordAccess
    WHERE UserId = :UserInfo.getUserId()
    AND RecordId IN :recordIds
]) {
    if (!access.HasEditAccess) {
        recordsWithoutEditAccess.add(access.RecordId);
    }
}

System.debug(
    LoggingLevel.WARN,
    'Running user: ' + UserInfo.getUserId()
        + '; records without edit access: ' + recordsWithoutEditAccess
);

This confirmed that the failure was caused by record-level access. The user context under which the queue was running did not have edit access to some of the records.

Based on that, we updated the class to run without sharing, which resolved the issue.

The main takeaway: when you see INSUFFICIENT_ACCESS_OR_READONLY in Apex, it is worth validating record access explicitly. A small UserRecordAccess check can quickly confirm whether the issue is with business logic or sharing context.

Share This:    Facebook Twitter

Tuesday, July 29, 2025

Race Conditions in Salesforce LWC Wire Adapters: A Common Pitfall

When building Lightning Web Components, you'll often need data from multiple sources. Here's a typical scenario that creates a race condition:

export default class ProductCatalog extends LightningElement {
    userPreferences = {};
    products = [];

    @wire(getUserPreferences)
    loadPreferences({ data }) {
        if (data) {
            this.userPreferences = data;
        }
    }

    @wire(getProducts, { categoryId: '$categoryId' })
    loadProducts({ data }) {
        if (data) {
            // Bug: userPreferences might not be loaded yet!
            this.products = data.filter(product => 
                product.price <= this.userPreferences.maxPrice
            );
        }
    }
}

The loadProducts wire adapter doesn't wait for loadPreferences to complete. If products load first, userPreferences.maxPrice is undefined, causing incorrect filtering.

Why It Happens

Wire adapters are:

  • Asynchronous: Execute in unpredictable order
  • Independent: Don't coordinate with each other
  • One-time reactive: Only re-run when their specific parameters change

The Solution

Here's a pattern to coordinate multiple wire adapters:

export default class ProductCatalog extends LightningElement {
    // Use undefined to track loading state
    userPreferences = undefined;
    rawProducts = undefined;
    
    get products() {
        // Only compute when both are loaded
        if (!this.rawProducts || this.userPreferences === undefined) {
            return [];
        }
        
        return this.rawProducts.filter(product => 
            product.price <= this.userPreferences.maxPrice
        );
    }

    @wire(getUserPreferences)
    loadPreferences({ data, error }) {
        if (data) {
            this.userPreferences = data;
        } else if (error) {
            this.userPreferences = null; // Explicitly set on error
        }
    }

    @wire(getProducts, { categoryId: '$categoryId' })
    loadProducts({ data, error }) {
        if (data) {
            this.rawProducts = data;
        } else if (error) {
            this.rawProducts = null;
        }
    }
}

Key Principles

  1. Use undefined for unloaded state - Distinguish between "not loaded" and "empty/false"
  2. Store raw data separately - Don't process data in wire handlers
  3. Use getters for computed values - They automatically update when dependencies change
  4. Handle errors explicitly - Set values to null on error to avoid infinite loading states

Alternative: Imperative Approach

For complex dependencies, consider imperative calls:

async connectedCallback() {
    try {
        const [preferences, products] = await Promise.all([
            getUserPreferences(),
            getProducts({ categoryId: this.categoryId })
        ]);
        
        this.processData(preferences, products);
    } catch (error) {
        this.handleError(error);
    }
}

Conclusion

Race conditions in wire adapters are subtle but solvable. By treating data loading as a coordination problem rather than independent events, you can build reliable components that work consistently regardless of network conditions.

Remember: if your component depends on multiple data sources, always implement a coordination strategy. Your users will experience a more reliable application, and you'll spend less time debugging intermittent issues.

Share This:    Facebook Twitter

Saturday, July 26, 2025

Why Your Lightning Modal Won't Close

Your modal opens fine. But when users click Cancel or the X button, nothing happens. The modal stays open. Here's why this happens and how to fix it.

The Problem

I built a Lightning Web Component modal. It worked during testing. But in production, users couldn't close it. The console showed my handleCancel() method ran. The this.close() call worked without errors. But the modal stayed open.

Finding the Real Error

I stripped the component down to basics. Then I got this error:

TypeError: Cannot set property parentNode of #<Node> which has only a getter

This pointed to the real problem. I had named a property parentNode:

// This breaks the modal
export default class MyModal extends LightningModal {
    @api parentNode = null;
}

But parentNode is a built-in DOM property. It's read-only. When Lightning tries to build the modal, it hits this conflict and fails.

The Fix

Rename the property:

// This works
export default class MyModal extends LightningModal {
    @api parentNodeData = null;
}

Then update everywhere you use it:

// In your getters
get isChildNode() {
    return this.parentNodeData !== null;
}

// In your modal calls
const result = await MyModal.open({
    parentNodeData: this.parentNode, // Changed from parentNode
});

Property Names to Avoid

Never use these as @api property names:

Node properties:

  • parentNode
  • childNodes
  • firstChild
  • lastChild
  • nextSibling
  • previousSibling

Element properties:

  • children
  • className
  • id
  • innerHTML

Event properties:

  • onclick
  • onload
  • onerror

Instead of generic names, be specific:

// Good
@api selectedNodeData = null;
@api parentStepInfo = null;
@api modalConfig = null;

// Bad - might conflict
@api parent = null;
@api node = null;
@api element = null;

How to Debug This

If your modal won't close:

  1. Make a simple test modal with just basic properties
  2. Check your property names against the avoid list
  3. Look for DOM-related errors in the console
  4. Add logging to see where it fails
handleCancel() {
    console.log('Cancel clicked');
    this.close();
}

Lightning modals do a lot behind the scenes:

  • Create DOM elements
  • Move them around the page
  • Handle focus and accessibility
  • Clean up when closed

When your property names clash with built-in DOM properties, this process breaks. Framework code and your code share the same space. Pick property names that won't conflict. Next time your modal won't close, check your property names first. It might save you hours of debugging.

Share This:    Facebook Twitter

Sunday, May 11, 2025

Mastering Product Catalog Management (PCM) in Salesforce Revenue Cloud

Before we dissect its components, let's appreciate PCM's pivotal role. It's where you meticulously define and manage every sellable (and sometimes non-sellable) item, service, subscription, and bundle. It directly influences:

  • Sales Experience: How easily can sales reps find, configure, and price products?
  • Pricing Accuracy: Are discounts, tiered pricing, and promotional offers applied correctly?
  • Order Fulfillment: Can the system understand what needs to be provisioned or shipped?
  • Billing & Invoicing: Are customers billed correctly for what they bought, especially for recurring and usage-based models?
  • Revenue Recognition: How is revenue from complex bundles or subscriptions recognized over time?
  • Reporting & Analytics: How effectively can the business glean insights from sales and product performance?

PCM within Revenue Cloud isn't a static list; it’s a dynamic model designed for modern B2B complexities like sophisticated bundling, rule-based eligibility, attribute-driven configurations, and diverse pricing models.

Deconstructing the PCM Architecture: From the Outside In

Imagine PCM as a series of concentric circles, each layer building upon the one within. As architects, understanding this layered approach helps in designing a catalog that is both comprehensive and manageable.

Layer 1: CATALOG – The Storefront

  • What it is: The highest-level organizational container. Think of it as the master "store" or "portfolio" of offerings. A company might have multiple catalogs for different business units, market segments (e.g., "Enterprise Solutions Catalog," "SMB Offerings Catalog"), or sales channels ("Direct Sales Catalog," "Partner Portal Catalog").
  • Why it's critical: Catalogs provide the initial segmentation of your entire product universe. They help in managing large, diverse product sets and can be foundational for presenting tailored views to different user groups or customer-facing portals. For example, the "Hardware Catalog" from our example groups all physical goods.
  • Architect's Lens: When translating requirements, consider:
    • Does the business serve vastly different markets or customer types that warrant separate catalogs?
    • Are there distinct sales channels that need curated product views?
    • Effective dating for catalogs allows for phased rollouts or retirement.
  • Do: Start with a clear catalog strategy aligned with the business structure.
  • Don't: Create an excessive number of catalogs without clear justification, as it can lead to administrative overhead.

Layer 2: CATALOG CATEGORIES & SUBCATEGORIES – The Aisles and Shelves

  • What it is: Within each Catalog, you define a hierarchical structure of Categories and Subcategories. These are the "aisles" and "shelves" that help users navigate and find what they need.
  • Why it's critical: A well-thought-out category structure is paramount for user experience, both for internal sales reps and for customers in self-service scenarios. It facilitates intuitive browsing, filtering, and ultimately, faster quote generation.
  • Architect's Lens:
    • Work with product managers and sales operations to understand how they logically group products. The "Hardware Catalog" in our example neatly divides into "Accessories," "Computers," and "Laptops." "Accessories" is further broken down into "Printers."
    • A product can live in multiple categories if it makes sense (e.g., a specialized monitor could be in "Displays" and "Gaming Peripherals").
    • The sort order of categories impacts display.
  • Do: Design the category hierarchy from the user's perspective – how would they naturally search for products?
  • Don't: Create overly deep or convoluted hierarchies that become cumbersome to navigate. Avoid overly generic or overly granular categories.

Layer 3: RULES – The Gatekeepers

  • What it is: A powerful mechanism to control product visibility and eligibility based on various contextual factors. These rules determine if a product or category qualifies to be shown during product browsing, discovery, or listing.
  • Why it's critical: Businesses rarely offer all products to all customers in all situations. Rules automate the enforcement of sales strategies, regional restrictions, customer segment-specific offerings, and prerequisites.
  • Architect's Lens:
    • Our example mentions rules based on "Zipcode, Region, Account Type, Customer Type." This translates to configuring Qualification Rules (or Disqualification Rules).
    • These rules are often evaluated using Decision Tables (managed via Business Rules Engine) for performance and manageability. The ProductQualification, ProductDisqualification, ProductCategoryQualification, and ProductCategoryDisqual standard objects store these rule definitions.
    • Context Definitions (like ProductDiscoveryContext) are essential for feeding the necessary data (e.g., Account's Region) into the rule evaluation engine.
    • Qualification Rule Procedures (Expression Sets in Salesforce parlance) orchestrate the evaluation of these decision tables.
  • Do: Define clear, unambiguous criteria for product availability. Test rules rigorously with different scenarios (e.g., what does a customer in Europe with "SMB" account type see versus an "Enterprise" customer in North America?).
  • Don't: Create conflicting rules that lead to unpredictable behavior. Overly complex rule sets can impact performance and be difficult to maintain.

Layer 4: BUNDLED PRODUCTS – The Solution Packages

  • What it is: A group of products and/or services sold together as a single, often discounted, line item. The "Laptop Pro Bundle" is a perfect example.
  • Why it's critical: Bundling is a core strategy for increasing average deal size, simplifying purchasing for customers, and offering complete solutions. Revenue Cloud's PCM excels at managing both simple static bundles and complex configurable ones.
  • Architect's Lens:
    • Structure: Bundles have a root product (e.g., "Laptop Pro Bundle"). Child components are organized into Product Groups (e.g., "Laptops (Group)," "Accessories (Group)"). This grouping is mandatory for configurable bundles.
    • Components: These can be individual Products (like "Laptop," "Antivirus") or even other Product Classifications (allowing dynamic selection of items from that class).
    • Cardinality: Crucial for configurable bundles.
      • Local Cardinality (on ProductRelComponentOverride and through Product Relationship configurations on the bundle structure) dictates min/max quantities, whether a component is included by default, or required.
      • Group Cardinality (on ProductComponentGrpOverride and group configurations) defines min/max distinct components selectable from a group.
    • Configuration Rules: Further control what can be selected together within a bundle, apply dependencies, or auto-add/remove components based on choices.
    • Attribute Overrides: The attributes of a component product (e.g., the default RAM for the Laptop within this bundle) can be overridden, without affecting the standalone Laptop product definition. This is stored in ProductRelComponentOverride.
  • Do: Design bundles logically. Use Product Groups for clarity and control. Clearly define mandatory vs. optional components and their quantities.
  • Don't: Create bundles that are overly complex to configure for the user. Ensure pricing of the bundle vs. individual components makes sense.

Layer 5: PRODUCTS (Simple & Standalone) – The Building Blocks

  • What it is: Individual items or services that can be sold standalone or as components within a bundle. Our "Laptop" and "Antivirus" are examples.
  • Why it's critical: These are the atomic units of your offering. Their proper definition, attributes, and classification are fundamental.
  • Architect's Lens:
    • Product Classification (Base): Ideally, simple products should be based on a Product Classification (e.g., the "Laptop" is "Based on Computer product classification"). This ensures it inherits a standard set of attributes, promoting consistency. The "Antivirus" in the example is not based on a classification, meaning its attributes would be defined directly on the product.
    • Product Selling Models (PSM): Each product that's sold needs one or more PSMs assigned (ProductRampSegment for ramp deals, but core PSMs like One-Time, Term-Defined, Evergreen are key). This is critical for determining how it's sold and billed.
    • Is Assetizable: Determines if a Salesforce Asset record should be created upon sale, crucial for tracking subscriptions, warranties, and serviceable items.
    • Configure During Sale: Determines if attributes of a simple product can be modified at the point of sale, making it a "configurable simple product."
    • Catalog Assignment: Must be assigned to Catalog Categories to be discoverable.
  • Do: Leverage Product Classifications heavily. Ensure PSMs are accurately assigned. Make explicit decisions about assetization.
  • Don't: Create products as one-offs if a classification could standardize them. Forget to assign them to relevant catalogs/categories.

Layer 6: PRODUCT CLASSIFICATION – The Templates

  • What it is: A template that defines a shared set of attributes for a group of similar products. Think of "Computers" or "Warranty" as product classifications.
  • Why it's critical: Promotes consistency, reusability, and efficiency. When you create a new laptop model, instead of manually adding "Processor," "Memory," "Storage" each time, you base it on the "Computers" classification, and it inherits these attributes.
  • Architect's Lens:
    • A ProductClassification record itself holds dynamic attributes via the ProductClassificationAttr junction object, which links to AttributeDefinition records.
    • You can define default values, requiredness, and picklist overrides for attributes at the classification level.
    • Our example shows "Computers" having Processor, Memory, etc., some potentially from an "Attribute Category Phone details" (though "Phone Details" is likely a placeholder for a more relevant "Computer Hardware Details" category). The "Warranty" classification has a "Warranty In years" attribute.
  • Do: Identify common sets of characteristics across products to define useful classifications. Group related attributes within an Attribute Category and assign the category to the classification.
  • Don't: Make classifications so broad they become meaningless or so narrow they aren't reusable.

Layer 7 (Innermost): DYNAMIC ATTRIBUTES & ATTRIBUTE CATEGORIES – The DNA

  • Dynamic Attributes (via ProductAttributeDefinition):
    • What it is: The specific characteristics or properties of a product (e.g., Processor, Graphic Processor, Storage, Memory, Display, Battery). These are defined once and can be reused.
    • Why it's critical: They capture the configurable and descriptive details of a product, driving differentiation, pricing logic, and fulfillment.
    • Architect's Lens: Each AttributeDefinition specifies its name, label, data type (Text, Picklist, Number, Boolean, etc.), and can link to a shared AttrPicklist for controlled values. Fields like IsHidden, IsReadOnly, IsRequired control runtime behavior.
  • Attribute Categories (via AttributeCategory):
    • What it is: A logical grouping of AttributeDefinition records (e.g., "Computer Processors" grouping "Processor" and "Graphic Processor"). This is managed via the AttributeCategoryAttribute junction object.
    • Why it's critical: Simplifies management, especially when assigning many attributes to a Product Classification.
  • Do: Plan your attribute library carefully. Define picklists centrally (AttrPicklist and AttrPicklistValue) for attributes with predefined options. Use attribute categories for logical grouping and easier assignment to classifications.
  • Don't: Create duplicate attributes. Use overly generic names. Make every attribute a free-text field if predefined values would ensure data quality.

Translating Functional Business Requirements into PCM Configurations

As a Technical Architect, your bridge functional needs (from Sales Ops, Product Managers, etc.) to these PCM constructs:

  1. Requirement: "We need to launch a new line of premium laptops, configurable with different RAM, SSD, and optional 3-year accidental damage warranty. These should only be offered to enterprise customers in North America and Europe."
    • PCM Solution:
      • Attributes: Create/ensure RAM_Options (Picklist), SSD_Options (Picklist), Warranty_Duration (Picklist: 3-Year).
      • Attribute Category: "LaptopPremium_Specs".
      • Product Classification: "Premium_Laptop_PC" (assigning RAM, SSD). Another, "Premium_Warranty_PC" (assigning Warranty Duration).
      • Products (Simple): "Premium Laptop X1" (Base: Premium_Laptop_PC), "Accidental Damage Warranty - 3yr" (Base: Premium_Warranty_PC, Selling Model: One-Time).
      • Bundled Product: "Premium Laptop X1 Package" (Configurable).
        • Group 1: "Core System": Add "Premium Laptop X1" (required, qty 1).
        • Group 2: "Protection": Add "Accidental Damage Warranty - 3yr" (optional, default quantity 0, max 1).
      • Catalog & Category: "Hardware Catalog" -> "Laptops" -> "Premium Laptops".
      • Qualification Rule (on "Premium Laptop X1 Package"):
        • Define criteria object with AccountType, AccountRegion.
        • Decision Table: AccountType=Enterprise AND (AccountRegion=NA OR AccountRegion=EU) -> IsQualified=True.
        • Qualification Rule Procedure uses this DT.
        • Link procedure to Product Discovery settings.
  2. Requirement: "Our 'Antivirus Monthly Subscription' should automatically renew, and its price should increase by 5% after the first year if bundled with any 'Pro' series laptop."
    • PCM Solution (partial, pricing rules are also involved):
      • Product: "Antivirus Monthly Subscription".
      • Product Selling Model (PSM): "Evergreen_Monthly" assigned to Antivirus.
      • The 5% uplift after a year when bundled is a complex pricing/bundling rule, not purely a PCM setup but influenced by it. PCM provides the product definitions ("Pro" series via a classification or naming convention, the Antivirus product) that the pricing and configuration rules would act upon.

Key Design Considerations for Technical Architects

  • Modularity & Reusability: Design attributes, picklists, and classifications to be reusable across multiple products. This reduces redundancy and simplifies maintenance.
  • Attribute Strategy:
    • Where are attributes mastered? Centrally on AttributeDefinition and inherited? Or defined and overridden frequently at the ProductClassificationAttr or ProductAttributeDefinition (for inherited product attributes) level?
    • How many attributes are truly needed? Avoid "attribute bloat."
  • Hierarchy Depth: For catalogs and bundles, how many levels deep is practical for users and system performance?
  • Naming Conventions: Critical for all PCM entities for clarity and maintainability. Use the client's established prefixing/initials as suggested in the guide for labs to avoid conflicts.
  • Data Governance: Who owns product data? Who approves new products, classifications, or attributes?
  • Performance: Very large catalogs or extremely complex rule sets can impact performance in Product Discovery or configuration. Indexing (covered elsewhere in Revenue Cloud) becomes important.
  • Localization (ProductSpecificationRecType, ProductSpecificationType): The system supports defining product specifications that are unique to an industry or language, allowing for product terminology that resonates with specific markets. Your "Hardware Catalog" could have different views or even underlying product variants based on region.
  • API Versioning: Note that many PCM objects are versioned (e.g., "available in API version 60.0 and later"). Be mindful of this for integrations and custom code.
  • Limits: Revenue Cloud (and PCM as part of it) has limits on things like the number of attributes, levels in a bundle, etc. Keep these in mind during design.

Common Pitfalls & Anti-Patterns to Avoid

  • Over-complicating the Initial Design: Trying to model every conceivable future scenario from day one can lead to a system that's too complex to manage or use. Start with core requirements and iterate.
  • Inconsistent Attribute Definitions: Using slightly different names or data types for what is essentially the same attribute across products.
  • Poor Product Naming & Descriptions: Makes it hard for users to find products.
  • Underutilizing Product Classifications: Leading to a lot of manual attribute assignment and inconsistencies across similar products.
  • Ignoring Qualification Rules: Relying on sales reps to "know" what products to offer to which customers leads to errors and lost opportunities.
  • Not Planning for Data Migration: Underestimating the effort to cleanse and map existing product data into the PCM structure.
  • Lack of Clear Ownership: Without defined roles for managing the product catalog, it can quickly become disorganized.

PCM Best Practices

  • Engage Stakeholders Early and Often: Product Managers, Sales Ops, Sales, Finance, and IT all have a vested interest.
  • Start with the End in Mind: How will products be quoted, ordered, fulfilled, and billed? This influences PCM design.
  • Iterative Approach: Don't try to boil the ocean. Implement core functionality, gather feedback, and enhance.
  • Leverage Standard Objects: Use ProductClassification, AttributeCategory etc., as much as possible before resorting to fully custom solutions.
  • Thorough Documentation: Document your catalog structure, attribute definitions, and rule logic.
  • Test Extensively: Test product discovery, configuration, and how rules apply with various user personas and data scenarios. The runtime experience from the hands-on guide is a good testing ground.
  • Plan for Change: Product catalogs are not static. Design for ease of updates, additions, and retirements.

Complex Scenario Example & Solution:

  • Scenario: A global telecom company offers "Enterprise Connectivity Bundles."
    • These bundles vary significantly by region (NA, EMEA, APAC) due to regulatory requirements and available underlying network services.
    • Within each region, customers can choose a base bandwidth (e.g., 100Mbps, 1Gbps, 10Gbps).
    • Depending on the bandwidth, specific security add-ons become available or are even mandatory (e.g., Advanced DDoS Protection is mandatory for 10Gbps).
    • Some add-ons are only compatible with specific primary services also chosen in the bundle.
    • Pricing is tiered based on contract length (1yr, 2yr, 3yr) and also includes usage-based charges for data overages.
  • PCM & Revenue Cloud Approach:
    1. Catalogs: Potentially "Global Enterprise Offerings" or regional catalogs if presentation needs to be distinct.
    2. Product Classifications:
      • Connectivity_Service_PC (Attributes: Bandwidth, SLA_Level, Region_Compatibility)
      • Security_Addon_PC (Attributes: Threat_Detection_Level, Included_Firewall_Type)
    3. Products:
      • NA_Fiber_1Gbps (Based on Connectivity_Service_PC, PSM: Term-Defined)
      • EMEA_SDWAN_100Mbps (Based on Connectivity_Service_PC, PSM: Term-Defined)
      • Advanced_DDoS_Protection (Based on Security_Addon_PC, PSM: Evergreen Addon)
      • Basic_Firewall_Service (Based on Security_Addon_PC)
    4. Bundled Product: "Global_Enterprise_Connectivity_Bundle" (Highly Configurable)
      • Group "Primary Connectivity":
        • Uses ProductClassification Connectivity_Service_PC allowing dynamic selection based on region and bandwidth requirements of the customer.
        • Cardinality: Min 1, Max 1 (must choose one primary service).
      • Group "Security Services":
        • Contains individual Security_Addon_PC based products.
        • Local Cardinality rules:
          • "Advanced_DDoS_Protection" -> Required if Primary Connectivity.Bandwidth = 10Gbps. (This would be a Configuration Rule).
    5. Attributes (On Classifications/Products): Region (Picklist), Bandwidth_Tier (Picklist), Contract_Length (Picklist on the Quote, influences pricing).
    6. Rules:
      • Qualification Rules: Show NA_Fiber_1Gbps only if Account.Region = "NA". Show EMEA_SDWAN_100Mbps only if Account.Region = "EMEA".
      • Configuration Rules (within the bundle configurator): If Connectivity_Service_PC.Bandwidth = "10Gbps", then "Advanced_DDoS_Protection" must be selected.
    7. PCM APIs for Integrations:
      • Product and Pricing information exposed via APIs for custom portals or integration with third-party configuration tools if needed. The standard Product Catalog Management Business APIs, Metadata API Types, and Tooling API Objects provide the hooks.

This scenario showcases how catalogs, categories, highly configurable bundles with product classifications, dynamic attributes, and qualification/configuration rules all work in concert to address a complex selling motion.

Share This:    Facebook Twitter

Total Pageviews

My Social Profiles

View Sonal's profile on LinkedIn

Tags

__proto__ $Browser Access Grants Accessor properties Admin Ajax AllowsCallouts Apex Apex Map Apex Sharing AssignmentRuleHeader AsyncApexJob Asynchronous Auth Provider AWS Callbacks Connected app constructor Cookie CPU Time CSP Trusted Sites CSS Custom settings CustomLabels Data properties Database.Batchable Database.BatchableContext Database.query Describe Result Destructuring Dynamic Apex Dynamic SOQL Einstein Analytics enqueueJob Enterprise Territory Management Enumeration escapeSingleQuotes featured Flows geolocation getGlobalDescribe getOrgDefaults() getPicklistValues getRecordTypeId() getRecordTypeInfosByName() getURLParameters Google Maps Governor Limits hasOwnProperty() Heap Heap Size IIFE Immediately Invoked Function Expression Interview questions isCustom() Javascript Javascript Array jsForce Lightning Lightning Components Lightning Events lightning-record-edit-form lightning:combobox lightning:icon lightning:input lightning:select LockerService Lookup LWC Manual Sharing Map Modal Module Pattern Named Credentials NodeJS OAuth Object.freeze() Object.keys() Object.preventExtensions() Object.seal() Organization Wide Defaults Override PDF Reader Performance performance.now() Permission Sets Picklist Platform events Popup Postman Primitive Types Profiles Promise propertyIsEnumerable() prototype Query Selectivity Queueable Record types Reference Types Regex Regular Expressions Relationships Rest API Rest Operator Revealing Module Pattern Role Hierarchy Salesforce Salesforce Security Schema.DescribeFieldResult Schema.DescribeSObjectResult Schema.PicklistEntry Schema.SObjectField Schema.SObjectType Security Service Components Shadow DOM Sharing Sharing Rules Singleton Slots SOAP API SOAP Web Services SOQL SOQL injection Spread Operator Star Rating stripInaccessible svg svgIcon Synchronous this Token Triggers uiObjectInfoApi Upload Files VSCode Web Services XHR
Scroll To Top