Skip to content

bomSubstore

Resolves and formats Bill of Materials (BOM) tables defined in the Mercura back-office. For each completed configuration, the SDK fetches the BOM definition from the API, combines it with the current form values, quantities, and dynamic prices, and returns channel-specific resolved tables ready to render.

BOM resolution is memoized per config and output channel — it only re-computes when the underlying BOM definition, form data, values, or locale change.

Table of contents

Top-level

Per-config (via useBomByConfigId / useBomByCurrentConfigId)


Top-level

bomConfigs

bomConfigs: Record<number, BomStoreItemVariables>

Map from config ID to that config’s raw BOM fetch state.

Each entry is created when configsSubstore.addConfig is called and removed when configsSubstore.removeConfig is called. The BOM definition is fetched automatically via TanStack Query, keyed by formId, language, and country.

Related


getBoms

getBoms: (args: GetBomsArgs) => ResolvedBom[]

Returns resolved BOM data for every completed configuration in the session.

type GetBomsArgs = {
channel: BomOutputChannel;
skipMultiplyConfigQuantity?: boolean;
pricesIncludedText?: string;
};
type ResolvedBom = {
configId: number;
bom: ApiResource<TBomFullResolvedData>;
};
type BomOutputChannel = "checkout" | "pdf" | "excel" | "email";
ChannelPurpose
checkoutReal-time UI in the configurator checkout
pdfHigh-fidelity layout for PDF generation
excelTabular format for spreadsheet export
emailSimplified summary for email output

The channel controls which BOM elements and columns are visible — each element and column in the back-office definition has per-channel hide_on_* flags.

Quantities are multiplied by config.configQuantity by default. Pass skipMultiplyConfigQuantity: true to get per-unit quantities. Use pricesIncludedText to customise the label shown when a price resolves to zero (defaults to the value passed by your UI).

Returns ResolvedBom[] — one entry per config with status === "completed". Each bom field is an ApiResource with data, isLoading, and error.

Related


getBom

getBom: (
configId: number,
args: GetBomsArgs,
) => ApiResource<TBomFullResolvedData>

Resolves the BOM for a single configuration.

Same arguments and behaviour as getBoms, but scoped to one configId. Returns { data: undefined, isLoading: false, error: null } when no BOM entry exists for the given config.

Related

  • getBoms — batch version across all completed configs

Per-config

Access per-config BOM state through the useBomByConfigId or useBomByCurrentConfigId hooks. Both expose a BomStoreItem:

type BomStoreItem = {
bomConfig: ApiResource<TBom>;
getBom: (args: GetBomsArgs) => ApiResource<TBomFullResolvedData>;
};

bomConfig

bomConfig: ApiResource<TBom>

Raw BOM definition fetched from the API for this config’s form. Includes data, isLoading, and error.

The TBom shape contains tables (with columns, groups, and elements) and an optional summary block. Column field values include sku, label, description, sales_price, cost_price, margin, quantity, property, name, discount, and value.

Related


getBom (per config)

getBom: (args: GetBomsArgs) => ApiResource<TBomFullResolvedData>

Resolves this config’s BOM into renderable tables and summary rows.

type TBomFullResolvedData = {
tables: BomTable[];
summary: TBomSummaryResult | null;
};

Each BomTable contains headerColumns, groups, and numColumns. Groups contain typed rows:

Row typeDescription
element-with-optionsA form element with one or more selected options; each option is a sub-row with resolved column values
element-without-optionsA form element without options (e.g. text/number); renders a single row with the element label
textStatic text content defined in the BOM builder
subtotalComputed subtotal row for numeric columns

Each resolved cell includes a formatted value string, a rawValue for calculations, alignment, widthPercentage, and an isMarkdown flag.

The summary block aggregates numeric columns (cost_price, sales_price, margin, discount) and property values across the BOM. Returns null when no summary is configured or all rows are hidden for the channel.

Related


Hooks

HookScopeDescription
useBomGlobalAccess bomSubstore directly (getBoms, getBom, bomConfigs)
useBomByConfigId(id, sel)Per configSelect from a specific config’s BomStoreItem
useBomByCurrentConfigId(sel)Current configSame as above, scoped to configsSubstore.currentConfigId

Example — render BOM tables at checkout:

const getBoms = sdk.useBom((s) => s.getBoms);
const resolved = getBoms({
channel: "checkout",
pricesIncludedText: "included",
});
resolved.map(({ configId, bom }) => {
if (bom.isLoading) return <p key={configId}>Loading BOM…</p>;
if (bom.error) return <p key={configId}>Failed to load BOM</p>;
if (!bom.data) return null;
return bom.data.tables.map((table) => (
<BomTableView key={table.id} table={table} />
));
});