Skip to content

Commit c970f4d

Browse files
mjoslynclaude
andcommitted
Fix mini-cart per-unit price for bulk-priced lines
Bulk (quantity-break) pricing was applied only on woocommerce_before_calculate_ totals. The mini-cart template renders each line's per-unit price BEFORE its subtotal triggers recalculation, so the per-unit line showed the non-bulk price (e.g. $595) while the subtotal reflected the bulk price ($575 x qty). The main cart page, which calls calculate_totals() up front, was unaffected. Make set_cart_item_price_from_session quantity-aware: restore the price via effective_price_qty() with the stored line quantity so the cart item already carries the bulk per-unit price before anything renders. apply_bulk_cart_pricing still re-applies it on every recalculation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent bdce458 commit c970f4d

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

src/WooHooks.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,14 @@ public function set_cart_item_price_from_session( $session_data, $values, $key )
324324
}
325325
// Recompute for the current pricing context (so switching roles via the
326326
// toolbar updates cart prices on the next load) rather than restoring the
327-
// price snapshotted when the item was added.
328-
$new_price = $this->engine->effective_price( null, $session_data['data'] );
327+
// price snapshotted when the item was added. Quantity-aware so the restored
328+
// per-unit price already reflects a bulk break: templates that read the item
329+
// price BEFORE calculate_totals runs (notably the mini-cart, which renders
330+
// each line's per-unit price before its subtotal triggers recalculation)
331+
// would otherwise show the non-bulk price while the subtotal shows the bulk
332+
// one. apply_bulk_cart_pricing() re-applies this on every recalculation.
333+
$qty = isset( $values['quantity'] ) ? max( 1, (int) $values['quantity'] ) : 1;
334+
$new_price = $this->engine->effective_price_qty( null, $session_data['data'], $qty );
329335
$session_data['data']->set_price( $new_price );
330336
$session_data['new_price'] = $new_price;
331337
return $session_data;

tests/WooHooksTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,30 @@ public function test_bulk_cart_below_threshold_uses_the_per_unit_price() {
113113
$this->assertSame( '70', (string) $this->hooks->get_price( $product->get_price(), $product ) );
114114
}
115115

116+
public function test_session_restore_is_quantity_aware_for_bulk() {
117+
// The mini-cart renders a line's per-unit price BEFORE calculate_totals runs, so
118+
// the session-restored price must already reflect the quantity break — otherwise
119+
// the per-unit line shows the non-bulk price while the subtotal shows the bulk one.
120+
$this->set_meta(
121+
10,
122+
array(
123+
'_pricebook_bulk_pricing' => array(
124+
'dealer' => array( array( 'min_qty' => 10, 'max_qty' => 0, 'price' => '55' ) ),
125+
),
126+
)
127+
);
128+
129+
// 13 units -> the bulk per-unit price (55), not the plain dealer price (70).
130+
$product = new FakeProduct( 10, 'simple' );
131+
$restored = $this->hooks->set_cart_item_price_from_session( array( 'data' => $product ), array( 'quantity' => 13 ), 'key' );
132+
$this->assertSame( '55', (string) $restored['data']->get_price() );
133+
134+
// Below the break -> the plain per-unit price.
135+
$below = new FakeProduct( 10, 'simple' );
136+
$restored = $this->hooks->set_cart_item_price_from_session( array( 'data' => $below ), array( 'quantity' => 5 ), 'key' );
137+
$this->assertSame( '70', (string) $restored['data']->get_price() );
138+
}
139+
116140
/**
117141
* A minimal WC_Cart stand-in exposing the get_cart() shape apply_bulk_cart_pricing
118142
* iterates: a list of items each with a 'data' product and a 'quantity'.

0 commit comments

Comments
 (0)