BOM Pricing documentation
Oakhammer BOM Pricing for WooCommerce, version 0.2. Questions: hello@oakhammer.app or Plugins → BOM Pricing → Help & feedback.
Overview
BOM Pricing prices WooCommerce products you build from parts: it adds up what the parts cost, turns that cost into a price with your rule, and sets the regular price. When a part's cost changes, every product that uses it is recalculated.
It is for stores that sell assembled or kitted products: custom bikes, gaming PCs, furniture, tool kits, gift boxes, meal kits and bundles of any kind. Without it, a part that gets more expensive quietly eats the margin on every kit that uses it until someone notices.
| Free | Pro | Business | |
|---|---|---|---|
| Bills of materials on simple products and on each variation | Yes | Yes | Yes |
| Lines that name the exact variation (attributes + SKU) | Yes | Yes | Yes |
| Shared parts on variable products; kits inside kits | Yes | Yes | Yes |
| Pricing rules (markup, margin, cost + fixed) with rounding | Yes | Yes | Yes |
| Cost changes recalculate every affected product and flag it | Yes | Yes | Yes |
| Review screen; apply prices one at a time | Yes | Yes | Yes |
| Prices update automatically when a cost changes, with a safety limit | Yes | Yes | |
| Apply all or selected prices at once | Yes | Yes | |
| Change history (costs, kit costs, prices applied, updates held) | Yes | Yes | |
| Import part costs from a CSV by SKU | Yes | Yes | |
| How many more of each kit you can build, and which part runs out first | Yes | ||
| Part usage per day that counts parts used by kit sales | Yes | ||
| Reorder list with lead times and suggested quantities | Yes | ||
| Reduce part stock automatically when a kit sells | Yes |
Pro: $59/year for 1 site, $129/year for 5 sites, $249/year for 25 sites. Business: $99, $219 and $399 a year for the same site counts. Each includes updates and support.
Requirements: WordPress 6.5 or newer (tested to 7.1), WooCommerce 8.0 or newer (tested to 11.1), PHP 7.4 or newer.
Getting started
You can price your first kit in about ten minutes. The example below builds a bike from a wheelset, a saddle, labor and a frame.
- Install and activate the plugin under Plugins → Add New → Upload. WooCommerce must be active.
- Set your default rule under WooCommerce → Settings → Products → BOM Pricing. The out-of-box default is markup 40%, rounded up to .99.
- Enter part costs. Open each part you buy and fill in Unit cost: on the General tab for a simple product, or in each variation's pricing fields. Parts do not have to be for sale; set them to private if customers should not see them. For example: Wheelset $420; Saddle $65; Frame, Large / Blue variation $2,300.
- Build the kit. Open the product you sell, go to the BOM & Pricing tab, click Add component, and search by name or SKU. Add Add labor / other cost lines for anything that is not a product (assembly labor $75, packaging, freight).
- Check the numbers. The box under the lines shows the rolled-up cost, the computed price with its margin, and the current regular price, updating as you type.
- Apply the price. Tick Apply price and click Update. The regular price is set to the computed price.
From then on, WooCommerce → BOM Pricing lists every kit and tells you when one needs a new price.
User guide
Parts and costs
A part is any simple product or any single variation with a Unit cost. Each variation carries its own cost, so a large blue frame and a small black frame are priced separately. A variation with no cost of its own uses its parent product's cost.
A part with no cost counts as $0 and the kit shows a warning, so a missing cost never goes unnoticed.
Building a bill of materials
Open a product and go to BOM & Pricing. Each line is either:
- A component: any simple product or specific variation, searched by name or SKU. The line shows the product name, the variation's attributes (for example "Size: Large, Color: Blue") and its SKU, with an edit link to the part.
- Labor / other cost: a label, quantity and unit cost for things that are not products.
Drag lines by the handle to reorder them. Quantities can be fractional (0.5 m of cable).
Variable products
Variable products are built in two places, so you only enter common parts once:
- Shared lines go on the product's BOM & Pricing tab. Every variation includes them (wheelset, saddle, labor).
- Variation lines go in each variation under Variations → BOM & pricing. Add only what differs (the specific frame).
A variation's cost = its shared lines + its own lines. The variation header shows its cost so you can compare variations without opening each one. The variable product itself is never priced; its variations are.
Kits inside kits
A component can itself have a BOM. Its rolled-up cost is used as the line's unit cost, and the line is tagged sub-assembly. Example: a Two-Bike Pack uses two Large / Blue bikes plus a roof rack. Costs roll up through any depth.
If a kit ends up containing itself (A uses B, B uses A), that line counts as $0 and the kit shows a Circular BOM warning instead of looping.
Pricing rules
| Rule | Price is | Example on $3,535 cost |
|---|---|---|
| Markup % on cost | cost × (1 + markup) | 40% → $4,949.00 |
| Target margin % | cost ÷ (1 − margin) | 30% → $5,050.00 |
| Cost + fixed amount | cost + amount | $900 → $4,435.00 |
| Manual | no price is computed; cost and margin are shown only | — |
Rounding always goes up, so it never gives away margin:
| Rounding | $4,949.00 becomes |
|---|---|
| No rounding | $4,949.00 |
| Up to .99 | $4,949.99 |
| Up to whole number | $4,949.00 |
| Up to next 5 / 10 / 25 | $4,950 / $4,950 / $4,950 |
| Up to next 10, minus .01 | $4,949.99 |
Rules are chosen in this order: the variation's own rule, then the product's rule, then the store default. Set a variation to Same as product to inherit.
Applying prices
BOM Pricing only ever sets the regular price. Sale prices are never changed.
- When you save a product: tick Apply price before clicking Update. On a variable product, this applies every BOM-priced variation. On a single variation, tick the box inside its BOM & pricing section and save the variations.
- From the review screen: click Apply price on any row.
- Pro: automatically, or all at once (see the Pro guide).
To make the box ticked by default, turn on Apply computed price on save in settings.
When a part cost changes
The moment a part's cost is saved, from the editor, a CSV import, the WooCommerce API or WP-CLI, every product that uses it is recalculated, including kits that use it through another kit. Their cost updates immediately; their price does not (unless Pro automatic updates are on).
Each affected product is marked Needs update. A notice on the Dashboard and product screens gives the count, and the WooCommerce → BOM Pricing menu shows a badge.
The review screen (WooCommerce → BOM Pricing)
One row per BOM-priced product or variation:
| Column | Shows |
|---|---|
| Product | Name, variation attributes, SKU, and why it changed ("Saddle cost changed $60.00 → $65.00") |
| Rule | The rule in effect and where it comes from (store default, product, variation) |
| Cost | Rolled-up cost today |
| Computed | What the rule gives from today's cost |
| Current | The regular price customers see (and any sale price) |
| Change | Computed minus current, in money and % |
| Margin | Margin at the current price → margin at the computed price |
| Status | In sync, Needs update, Below cost, No price set, Manual |
The Needs attention view (the default when anything needs attention) lists everything that is not in sync. Search matches name, attributes and SKU.
Products list
The BOM cost column on Products shows each kit's cost, its margin at the current price and its status. For a variable product it shows the cost range across variations and how many need an update. Parts show their unit cost.
Settings
WooCommerce → Settings → Products → BOM Pricing:
| Setting | Default | What it does |
|---|---|---|
| Default pricing rule | Markup % on cost | Used by products that don't set their own |
| Default rule value | 40 | Percent for markup/margin, amount for cost + fixed |
| Default rounding | Up to .99 | Rounding for the default rule |
| Component cost source | BOM Pricing unit cost field | Or WooCommerce's own Cost of Goods field |
| Write rolled-up cost to WooCommerce Cost of Goods | On | Keeps WooCommerce's cost field and its margin reports in step |
| Apply computed price on save | Off | Pre-ticks "Apply price" on the product screen |
Using WooCommerce's Cost of Goods field
If you already keep costs in WooCommerce's built-in Cost of Goods field (turn it on under WooCommerce → Settings → Advanced → Features), set Component cost source to that field. BOM Pricing then reads part costs from it and hides its own Unit cost field. With Write rolled-up cost on, each kit's rolled-up cost is written back to its Cost of Goods field.
Pro guide
Pro is a separate add-on that runs alongside the free plugin; keep both active. It adds four things.
Automatic price updates
Turn on Update prices automatically under WooCommerce → Settings → Products → BOM Pricing. When a part cost changes, every affected product's regular price is set to its new computed price straight away. Two guardrails decide what waits for you instead:
| Setting | Default | Effect |
|---|---|---|
| Hold changes larger than (%) | 15 | A change bigger than this percent of the current price is held for review. 0 = no limit. |
| Only raise prices automatically | Off | When on, price decreases are held for review. |
Held items stay on the review screen as Needs update, and the history records why they were held ("change of 34.6% is over the 15.0% limit"). Automatic updates only react to part cost changes; editing a product's own BOM still uses the Apply price box. Products on the Manual rule are never changed.
Bulk apply
On WooCommerce → BOM Pricing:
- Apply all computed prices that need updating applies every row that is not in sync.
- Or tick rows and choose Apply computed price from Bulk actions.
History
WooCommerce → BOM History logs every change, newest first, with who made it:
| Event | Recorded when |
|---|---|
| Component cost | A part's unit cost changes, with where the change came from (admin, REST, CLI) |
| Kit cost | A kit's rolled-up cost changes, and which part caused it |
| Price applied | A price is set, from save, the review screen, bulk, auto, or CLI, with the cost it was based on |
| Auto-update held | An automatic update was held, and why |
Click any item to see only its history.
Import costs from CSV
On the review screen, click Import costs. Upload a file or paste text with a header row:
sku,cost
FRAME-L-BLU,2300.00
WHEEL-29,420
SADDLE-1,$65.00
- The first column can be
skuorid; column order does not matter. Currency symbols and thousands separators are ignored. - Variations match by their own SKU.
- Preview first: each row shows the item, current and new cost, how many kits it affects, and whether it will update, is unchanged, was not found, or was skipped because the item is priced from its own BOM.
- Import these costs applies the changes. Affected kits are recalculated, exactly as if you had edited each part.
License
Your license comes with your purchase and is activated from the Pro plugin's Account page. It enables updates and support for the number of sites on your plan. Pro and Business use the same add-on; a Business license unlocks the BOM Stock screen.
Business guide: BOM-aware stock
Normal inventory tools see a kit sale as one item leaving the shelf. BOM Stock knows what the kit is made of, so it can tell you how many more you can build, which part will stop you, and what to reorder first. It is included in the Business plan.
Set up
- Turn on Manage stock (Inventory tab) for the parts you buy and enter the quantity on hand. For variable parts, such as frames in several sizes, set stock on each variation.
- Optional: enter each part's Supplier lead time (days) on the same tab. Parts without one use the store default.
- Kits you build to order usually do not manage their own stock. Kits you build ahead and keep on the shelf can.
- Check the settings under WooCommerce → Settings → Products → BOM Pricing → Stock (Business).
How many you can build
Each kit's BOM summary box shows "Can build 14 more from parts on hand (limited by Frame — Size: Large, Color: Blue)". The count is the smallest of (units on hand ÷ units per kit) across every part that tracks stock.
- Sub-assemblies are expanded into their parts, so a Two-Bike Pack needs two frames, two wheelsets and two saddles.
- A sub-assembly that manages its own stock is treated as a part: you built those ahead, so its own stock counts.
- Labor and other non-product lines, and parts that don't track stock, never limit the count.
- Each kit is counted on its own. Kits that share parts draw on the same stock, so building one lowers what's left for the others.
The BOM Stock screen
WooCommerce → BOM Stock has two tables. Switch the usage window between 30, 60 and 90 days at the top; Refresh recounts orders now (results are otherwise cached for an hour).
Reorder first lists every stock-tracked part used in a BOM, most urgent first:
| Column | Meaning |
|---|---|
| On hand | Current stock |
| Used (kits + direct) | Units used in the window: parts inside kits sold, plus the part sold on its own |
| Per day | Used ÷ days in the window |
| Days left | On hand ÷ per day |
| Lead time | The part's supplier lead time |
| Suggested order | Enough for the lead time plus the "Order enough for" days, minus what's on hand |
| Limits | How many kits this part is the limiting part for |
Status is Out of stock, Order now (runs out within the lead time), Order soon (within lead time plus the warning days), OK, or No recent use. Orders that are processing, completed or on hold count; cancelled, failed and refunded quantities don't.
Kits you can build shows each kit's buildable count, the part that runs out first, units sold in the window, and days of parts left at the current sales rate.
Reduce part stock when a kit sells
With Reduce part stock when a kit sells on, whenever WooCommerce reduces stock for an order, each kit's parts are reduced too (order quantity × BOM quantity) and an order note lists every change. If the order is cancelled or its stock is restored, the parts are put back. Each order line is only reduced once. Kits that manage their own stock are skipped, since their parts were used when you built them.
Stock settings
| Setting | Default | Effect |
|---|---|---|
| Usage window (days) | 30 | Orders from this many days set usage per day |
| Default supplier lead time (days) | 14 | For parts without their own lead time |
| Order enough for (days) | 30 | Suggested order covers the lead time plus this many days |
| Warn this many days early | 14 | When "Order soon" starts |
| Reduce part stock when a kit sells | Off | See above |
Troubleshooting & FAQ
| You see | Why | Fix |
|---|---|---|
| "… has no cost entered" | A part has no Unit cost, so it counts as $0 | Enter the part's cost (or its variation's) |
| "… no longer exists" / tag missing | A part was deleted; its line counts as $0 | Remove or replace the line |
| "Circular BOM: … contains this item" | Two kits contain each other | Remove the line that points back up |
| Status Below cost | The regular price is lower than the rolled-up cost | Apply the computed price, or set a higher price |
| Status No price set | The product has a BOM but no regular price yet | Apply price |
| "Sale price … is below cost" | A running sale is under cost; sale prices are never changed for you | Edit or end the sale |
| A variation shows no BOM & pricing cost | It has no lines of its own and the product has no shared lines | Add lines, or leave it priced by hand |
| Component search finds nothing | Search needs 2+ characters; variable parents are not selectable | Search for the specific variation or its SKU |
| Pro didn't update a price | The change was over the hold limit, was a decrease with "only raise" on, or the rule is Manual | See WooCommerce → BOM History for the reason |
Do parts have to be products customers can buy? No. Keep them private or hidden; search includes every status.
Will it change my sale prices? No. Only the regular price is set.
Can another system update costs? Yes. Write the cost through the WooCommerce REST API or WP-CLI and the recalculation runs (see the developer reference).
What happens to my data if I delete the plugin? It is kept, because costs and BOMs are business data. To remove everything, add define( 'OHBP_REMOVE_ALL_DATA', true ); to wp-config.php before deleting the plugin.
Does it send data anywhere? No. Nothing leaves your site unless you submit the Help & feedback form, and then only your message plus, if ticked, version numbers and counts. No customer, order or product data.
How do I get help? Plugins → BOM Pricing → Help & feedback, or email hello@oakhammer.app.
Developer reference
Data
| Where | Key | Holds |
|---|---|---|
Table {prefix}ohbp_bom_lines |
one row per BOM line | assembly_id (product, variable parent for shared lines, or variation), component_id (null for labor/other lines), label, unit_cost (labor/other only), qty, sort_order |
| Post meta | _ohbp_cost |
A part's unit cost (when the cost source is BOM Pricing) |
| Post meta | _ohbp_rolled_cost |
Last computed roll-up for a kit |
| Post meta | _ohbp_rule |
{mode, value, rounding} on a product or variation |
| Post meta | _ohbp_last_push |
{time, price, cost, user, context} of the last applied price |
| Option | ohbp_settings |
Plugin settings |
| Option | ohbp_attention |
Items whose cost changed and need review |
Table {prefix}ohbp_pro_log (Pro) |
one row per event | History |
| Post meta (Business) | _ohbp_lead_days |
A part's supplier lead time |
| Order item meta (Business) | _ohbp_parts_reduced |
Parts reduced for that kit line, used to restore them |
Prices are stored in WooCommerce's normal _regular_price; nothing else about the product changes.
Updating costs from another system
Any write to the cost meta triggers the recalculation, so an ERP, ops tool or script only needs to update the part. Through the WooCommerce REST API:
curl -X PUT https://store.example/wp-json/wc/v3/products/146/variations/149 \
-u ck_xxx:cs_xxx -H 'Content-Type: application/json' \
-d '{"meta_data":[{"key":"_ohbp_cost","value":"2500.00"}]}'
If the store uses WooCommerce's Cost of Goods field as the cost source, write that field instead.
WP-CLI
| Command | Does |
|---|---|
wp ohbp list [--status=drift] [--format=csv] |
Every BOM-priced item with cost, rule, computed and current price, status |
wp ohbp rollup <id> [--format=json] |
Line-by-line cost breakdown and warnings |
wp ohbp set-cost <id> <cost> |
Set a part's cost (runs the recalculation) |
wp ohbp set-bom <id> --lines='<json>' [--rule='<json>'] |
Replace a BOM, e.g. [{"component_id":12,"qty":1},{"label":"Labor","unit_cost":75,"qty":1}] |
wp ohbp push <id>… or wp ohbp push --all [--dry-run] |
Apply computed prices |
wp ohbp buildable [<id>…] [--format=csv] |
Business: how many of each kit can be built, and the limiting part |
wp ohbp stock [--days=30] [--format=csv] |
Business: the reorder list |
Hooks
| Hook | Type | Arguments | Use |
|---|---|---|---|
ohbp_component_cost_changed |
action | $id, $old, $new |
A part's unit cost changed |
ohbp_assembly_cost_changed |
action | $assembly_id, $old, $new, $trigger_id |
A kit's rolled-up cost changed |
ohbp_price_pushed |
action | $id, $old_price, $new_price, $cost, $context |
A price was applied (save, manual, bulk, auto, cli) |
ohbp_pre_push |
filter | null, $id, $price, $cost, $context |
Return a string to block a price change with that reason |
ohbp_computed_price |
filter | $price, $id, $cost, $rule |
Adjust the computed price (e.g. MAP floors) |
ohbp_effective_rule |
filter | $rule, $id |
Override the rule for an item |
ohbp_review_bulk_actions |
filter | $actions |
Add bulk actions to the review screen |
ohbp_review_handle_bulk_action |
action | $action, $ids |
Handle them |
ohbp_settings_fields |
filter | $fields |
Add settings to the BOM Pricing section |
ohbp_send_feedback |
filter | null, $type, $message, $email, $diagnostics |
Deliver Help & feedback messages yourself; return true when sent |
ohbp_support_email |
filter | $email |
Where Help & feedback goes |
ohbp_summary_after |
action | $id, $status |
Add lines to a product's BOM summary box |
ohbp_loaded |
action | none | The plugin is ready; add-ons start here |
Uninstall
Deleting the plugin keeps all data. With OHBP_REMOVE_ALL_DATA set to true in wp-config.php, uninstall drops the BOM table and deletes the plugin's post meta and options.