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.
Comments