Add computed list columns
Updated
A model can offer columns for another dictionary's list that are computed, not stored: the price of a price type, the stock in a store. The user adds them in Column setup → Computed. This page is the recipe. The contract, the endpoints, and the worked HTTP example are Computed list columns.
Note
Do not add a stored field for a value that already lives elsewhere. A new price type or a new store must appear as a parameter record, not as a schema change.
How to#
1. The service — implement IListColumnProvider on a
model service (the same pattern as IFiscalCalendar).
Inventory's ItemListColumns is the worked example: Price parameterized
by PriceType, Stock by Store (optional, with an "all stores" caption).
public partial class ItemListColumns : IListColumnProvider
{
private readonly ISqlService _sql;
public ItemListColumns(ISqlService sql) => _sql = sql;
public IReadOnlyList<ListColumnDefinition> ColumnsFor(string objectName)
=> objectName == "Item" ? ItemColumns : [];
public async Task<IReadOnlyDictionary<Guid, object?>> ValuesAsync(
string objectName, string key, Guid? parameter,
IReadOnlyList<Guid> ids, DateTime asOf, CancellationToken ct = default)
=> key switch
{
"Price" => parameter is Guid type
? await PricesAsync(type, ids, asOf) : new Dictionary<Guid, object?>(),
"Stock" => await StockAsync(parameter, ids, asOf),
_ => new Dictionary<Guid, object?>(),
};
}
ColumnsFor returns the catalogue for that object, empty otherwise.
ValuesAsync is called once per column per page — read the whole ids
list in one query (or in chunks of a few hundred). Answer with a dictionary
keyed by row id; a missing id renders empty. Do not throw for "no value".
asOf is a date; the list sends today.
Every implementation is asked, so another model can offer reserves on the
same Item list. The provider is named by its implementation type
(ItemListColumns).
2. Bump modelVersion in the same change.
Warning
Inject platform services in the constructor (
ISqlService,IDictionaryManager<T>). Resolve other model services inside the method withScriptServices.Get<IPricingService>(). A model service in the constructor is not in the DI container: the provider cannot be created, its columns disappear from Computed, and the log hasList column provider ItemListColumns could not be created. Other providers still answer.
What's not there yet#
No filter or sort on a computed column. No date picker (always today). Document journals do not ask providers. Not exported to Excel. Details: Computed list columns.