Oakhammer Software

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.

  1. Install and activate the plugin under Plugins → Add New → Upload. WooCommerce must be active.
  2. Set your default rule under WooCommerce → Settings → Products → BOM Pricing. The out-of-box default is markup 40%, rounded up to .99.
  3. 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.
  4. 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).
  5. 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.
  6. 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 sku or id; 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

  1. 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.
  2. Optional: enter each part's Supplier lead time (days) on the same tab. Parts without one use the store default.
  3. Kits you build to order usually do not manage their own stock. Kits you build ahead and keep on the shelf can.
  4. 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.