Ankit Agarwal
Jun 2, 2026
  196
(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
Optimizely CMS 13 Upgrade Overview

Wondering how to upgrade from Optimizely CMS 12 to 13? Not sure where to start – you are not alone.  Here is a quick overview... The post Optimizel...

Madhu | Jun 8, 2026 |

Optimizely Developers The Netherlands Meetup 2026

  I'm excited to not only co-host the upcoming  Optimizely Developers (The Netherlands) Meetup on my home turf at iO  in Den Bosch with Ehsan Ahzar...

WilliamP | Jun 5, 2026

Automatic assign of content reference properties on content creation

A long time ago I was tired of configuring setting properties on a settings page when deploying new functionality that needed global settings, so I...

Per Nergård (MVP) | Jun 5, 2026

Optimizely Opal vs AI-Assistant

This page compares strengths, use cases, flexibility, and pricing—highlighting Opal’s breadth vs Epicweb’s depth and how they can complement each...

Luc Gosso (MVP) | Jun 2, 2026 |