Ankit Agarwal
Jun 2, 2026
  29
(0 votes)

Creating Read-Only Calculated Fields in Optimizely Commerce 14

Introduction

In many Optimizely Commerce projects, product information comes from external systems such as ERPs, PIMs, inventory management systems, or custom business services. While editors need visibility into this information, they should not always be allowed to modify it.

Examples include:

  • ERP Product ID

  • Total Inventory Across Warehouses

  • Last Inventory Sync Date

  • Product Popularity Score

  • Margin Percentage

  • Number of Active Variants

A common requirement is to display these values within the Commerce editor interface while preventing accidental edits.

In this blog post, we'll explore how to create a read-only Commerce property in Optimizely Commerce 14 that displays calculated or external data to editors.


The Problem

Consider a product that exists in an ERP system.

Editors manage product content within Optimizely, but the ERP SKU is controlled by the ERP and should never be modified manually.

A typical implementation might look like this:

public virtual string ErpProductId { get; set; }

Unfortunately, this renders an editable textbox in the CMS editor.

This creates several issues:

  • Editors may accidentally overwrite values.

  • Data can become inconsistent with external systems.

  • Additional validation becomes necessary.

A better approach is to display the value as read-only.


Creating a Read-Only Property

First, add the property to your product content type.

[Display(
    Name = "ERP Product ID",
    GroupName = SystemTabNames.Content,
    Order = 100)]
[Editable(false)]
public virtual string ErpProductId { get; set; }

The Editable(false) attribute indicates that the property should not be editable within the editor interface.

However, depending on the editor component being used, additional customization may be required.


Creating a Custom Editor Descriptor

To ensure a consistent read-only experience, create a custom Editor Descriptor.

using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

[EditorDescriptorRegistration(
    TargetType = typeof(string),
    UIHint = "ReadOnlyText")]
public class ReadOnlyTextEditorDescriptor : EditorDescriptor
{
    public override void ModifyMetadata(
        ExtendedMetadata metadata,
        IEnumerable<Attribute> attributes)
    {
        base.ModifyMetadata(metadata, attributes);

        metadata.IsReadOnly = true;
    }
}

This descriptor forces the editor field into read-only mode.


Applying the UI Hint

Now update the product property.

[Display(
    Name = "ERP Product ID",
    Order = 100)]
[UIHint("ReadOnlyText")]
public virtual string ErpProductId { get; set; }

When editors open the product, they can see the value but cannot change it.


Populating the Value Dynamically

In many scenarios, the value should be calculated rather than stored.

For example, displaying total inventory across all warehouses.

Create a service:

public interface IInventorySummaryService
{
    int GetTotalInventory(string sku);
}

Implementation:

public class InventorySummaryService :
    IInventorySummaryService
{
    private readonly IInventoryService _inventoryService;

    public InventorySummaryService(
        IInventoryService inventoryService)
    {
        _inventoryService = inventoryService;
    }

    public int GetTotalInventory(string sku)
    {
        var records =
            _inventoryService.QueryByEntry(
                new CatalogKey(sku));

        return records.Sum(x => x.PurchaseAvailableQuantity);
    }
}

Displaying Calculated Information

A useful pattern is to expose calculated information through a backing property.

[Display(
    Name = "Total Inventory",
    Order = 200)]
[UIHint("ReadOnlyText")]
public virtual string TotalInventoryDisplay
{
    get
    {
        return CalculateInventory().ToString();
    }
}

This allows editors to view business-critical information directly from the product editor.


Additional Use Cases

1. Number of Variants

Display how many variants belong to a product.

public virtual string VariantCount
{
    get
    {
        return GetVariantCount().ToString();
    }
}

2. Last ERP Synchronization

public virtual DateTime LastErpSyncDate
{
    get; set;
}

Displayed as read-only so editors know when data was last refreshed.


3. Product Popularity Score

public virtual decimal PopularityScore
{
    get; set;
}

Generated from analytics and displayed for editorial insight.


4. Margin Percentage

public virtual decimal MarginPercentage
{
    get; set;
}

Calculated from pricing and cost information.


Improving the Editor Experience

For even better usability, consider placing these properties into a dedicated tab.

[Display(
    Name = "ERP Product ID",
    GroupName = "System Information",
    Order = 10)]

Example editor layout:

System Information

  • ERP Product ID

  • Last ERP Sync Date

  • Total Inventory

  • Active Variants

  • Popularity Score

This creates a clean separation between editable content and system-generated information.


Benefits

Implementing read-only properties provides several advantages:

  • Prevents accidental data modification

  • Improves editor confidence

  • Exposes useful business information

  • Reduces validation requirements

  • Keeps external systems as the source of truth

  • Creates a cleaner editing experience


Conclusion

Not every piece of information in Optimizely Commerce should be editable. In many enterprise Commerce implementations, valuable product data originates from external systems and should only be displayed to editors.

By combining content properties, custom editor descriptors, and calculated values, you can create a richer editing experience while ensuring data integrity.

This small customization is easy to implement but can significantly improve the usability and reliability of your Commerce solution.

Jun 02, 2026

Comments

Please login to comment.
Latest blogs
Pushing Content Between Optimizely DXP Environments

We've been working with a client who has been on Optimizely 11 for a long time - self-hosted, on their own server infrastructure, with a content...

Matt Pallatt | Jun 2, 2026

Launching “Learning by Doing – Optimizely OPAL Series” | Episode 01 is Live!

  Introduction With Optimizely OPAL, we’re seeing a shift from "content management" to "intelligent content orchestration". But here’s the reality ...

Ratish | Jun 1, 2026 |

Finding Thomas Part 2 - The Recognition Engine

Remember Thomas? In digital landscape, Thomas is the returning visitor who reads everything, opens every email, converts on nothing. In standard...

Ritu Madan | Jun 1, 2026