Products — Price Lookup Module
The Products app exports a Filtrera module at /apps/products/price-lookup.module.hrc that computes the effective price of one or more products across a set of price lists, plus a price-change history within a configurable time window.
It is the canonical building block for:
- Custom price-lookup ingresses — exposing pricing data to a storefront, marketing system, or BI tool with a request and response shape that fits your project.
- Bulk price exports — computing prices in batches for export to ERP, PIM, or marketing platforms.
- EU Omnibus 30-day-low displays —
lowestPriceover a 30-daywindowis the canonical input for "lowest price in the last 30 days" labels.
Module path
/apps/products/price-lookup.module.hrcExports
lookupPrices
Computes effective prices and price history for a list of products against a list of price lists, scoped to one currency and one time window.
Filtrera type signature
(params: {
productNumbers: [text]
currencyCode: text
priceListKeys: [text]
window: duration
}) => { text -> {
currentPrice: number | nothing
history: [{ at: instant, price: number | nothing }]
lowestPrice: number | nothing
highestPrice: number | nothing
} }Inputs
| Field | Type | Description |
|---|---|---|
productNumbers | [text] | Asset numbers of the products to look up. |
currencyCode | text | ISO 4217 currency code. Only prices in this currency are considered. |
priceListKeys | [text] | Price lists to consider. Only prices on these lists are considered. |
window | duration | How far back to compute history, lowest, and highest. For EU Omnibus, use 30 days. |
Output
A map keyed by productNumber (asset.product.assetNumber) where each entry has:
| Field | Type | Description |
|---|---|---|
currentPrice | number | nothing | The current best price across active price lists, ignoring deactivated prices. nothing if no active price applies. |
history | [{ at: instant, price: number | nothing }] | Chronologically-ordered entries — one per change of the effective best price within window. price is nothing when the effective price was not defined (no active list, or all lists deactivated) at that moment. |
lowestPrice | number | nothing | Lowest effective price observed within window. |
highestPrice | number | nothing | Highest effective price observed within window. |
Window semantics
- The history is computed by replaying activity-log events (
priceChange,priceDeactivation,priceReactivation,priceListActivation,priceListDeactivation— see Activity log event types) from the start of the window forward. - An entry is appended to
historywhenever the effective best price for the product changes — not on every individualpriceChangeevent. Activations and deactivations of price lists or individual prices contribute to this only when they affect the effective price. - Products that exist but have no matching active prices appear in the result with
currentPrice = nothingand an empty or deactivation-onlyhistory. - Products that don't exist do not appear in the result map.
Declaring the dependency
Add this to your app's h_app.yaml:
requires:
modules:
/apps/products/price-lookup.module.hrc:
exports:
lookupPrices:
type: '(params: { productNumbers: [text], currencyCode: text, priceListKeys: [text], window: duration }) => { text -> { currentPrice: number | nothing, history: [{ at: instant, price: number | nothing }], lowestPrice: number | nothing, highestPrice: number | nothing } }'See Declaring App Dependencies for the surrounding context.
Use cases
Custom price-lookup ingress
Expose a project-specific HTTP ingress backed by lookupPrices. The contract — request body, response shape, auth, channel filtering, tax handling — is intentionally up to the implementer because storefront pricing rules vary widely.
import './price-lookup.module.hrc' as priceLookup
param request: {
productNumbers: [text]
currencyCode: text
priceListKeys: [text]
}
let result =
priceLookup.lookupPrices {
productNumbers = request.productNumbers
currencyCode = request.currencyCode
priceListKeys = request.priceListKeys
window = 30 days
}
from {
currencyCode = request.currencyCode
prices =
result entries
select e => {
productNumber = e.key
currentPrice = e.value.currentPrice
lowestIn30Days = e.value.lowestPrice
timeline = e.value.history
}
}Register the component as a public HTTP ingress in your h_app.yaml and your storefront can call it directly.
Bulk price export
Call lookupPrices from a job that emits an export feed for ERP, PIM, or marketing platforms. Batch the input list to keep memory usage bounded and to fit any downstream API limits.
import './price-lookup.module.hrc' as priceLookup
param batch: {
productNumbers: [text]
priceListKeys: [text]
currencyCode: text
}
let prices =
priceLookup.lookupPrices {
productNumbers = batch.productNumbers
currencyCode = batch.currencyCode
priceListKeys = batch.priceListKeys
window = 30 days
}
from
prices entries
select e => {
productNumber = e.key
price = e.value.currentPrice
lowestPrice = e.value.lowestPrice
}EU Omnibus 30-day-low
For storefront price displays that need to show the lowest price in the last 30 days (per the EU Omnibus Directive), pass window = 30 days and surface lowestPrice alongside currentPrice. The history field is available if you want to render a price-over-time chart or audit trail.