Skip to content

Commit 4f0cc24

Browse files
Simplify Free Shipping Currency Conversion (#11094)
1 parent 315a6d6 commit 4f0cc24

File tree

3 files changed

+46
-57
lines changed

3 files changed

+46
-57
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: update
3+
4+
Avoid loading shipping zones when adjusting currencies for free shipping methods.

includes/multi-currency/FrontendPrices.php

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function init_hooks() {
6565
add_filter( 'woocommerce_get_variation_prices_hash', [ $this, 'add_exchange_rate_to_variation_prices_hash' ], 99 );
6666

6767
// Shipping methods hooks.
68-
add_action( 'init', [ $this, 'register_free_shipping_filters' ], 99 );
68+
add_action( 'woocommerce_shipping_zone_shipping_methods', [ $this, 'convert_free_shipping_method_min_amount' ], 99 );
6969
add_filter( 'woocommerce_shipping_method_add_rate_args', [ $this, 'convert_shipping_method_rate_cost' ], 99 );
7070

7171
// Coupon hooks.
@@ -336,42 +336,20 @@ public function get_coupon_min_max_amount( $amount ) {
336336
}
337337

338338
/**
339-
* Returns the free shipping zone settings with converted min_amount.
339+
* Converts the min_amount of free shipping methods.
340340
*
341-
* @param array $data The shipping zone settings.
342-
*
343-
* @return array The shipping zone settings with converted min_amount.
344-
*/
345-
public function get_free_shipping_min_amount( $data ) {
346-
if ( empty( $data['min_amount'] ) ) {
347-
return $data;
348-
}
349-
350-
// Free shipping min amount is treated as products to avoid inconsistencies with charm pricing
351-
// making a method invalid when its min amount is the same as the product's price.
352-
$data['min_amount'] = $this->multi_currency->get_price( $data['min_amount'], 'product' );
353-
return $data;
354-
}
355-
356-
/**
357-
* Register the hooks to set the min amount for free shipping methods.
341+
* @param array $methods The shipping methods.
358342
*/
359-
public function register_free_shipping_filters() {
360-
$shipping_zones = \WC_Shipping_Zones::get_zones();
361-
362-
$default_zone = \WC_Shipping_Zones::get_zone( 0 );
363-
if ( $default_zone ) {
364-
$shipping_zones[] = [ 'shipping_methods' => $default_zone->get_shipping_methods() ];
365-
}
366-
367-
foreach ( $shipping_zones as $shipping_zone ) {
368-
foreach ( $shipping_zone['shipping_methods'] as $shipping_method ) {
369-
if ( 'free_shipping' === $shipping_method->id ) {
370-
$option_name = 'option_woocommerce_' . trim( $shipping_method->id ) . '_' . (int) $shipping_method->instance_id . '_settings';
371-
add_filter( $option_name, [ $this, 'get_free_shipping_min_amount' ], 99 );
372-
}
343+
public function convert_free_shipping_method_min_amount( $methods ) {
344+
foreach ( $methods as $method ) {
345+
// Free shipping min amount is treated as products to avoid inconsistencies with charm pricing
346+
// making a method invalid when its min amount is the same as the product's price.
347+
if ( 'free_shipping' === $method->id && ! empty( $method->min_amount ) ) {
348+
$method->min_amount = $this->multi_currency->get_price( $method->min_amount, 'product' );
373349
}
374350
}
351+
352+
return $methods;
375353
}
376354

377355
/**

tests/unit/multi-currency/test-class-frontend-prices.php

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,47 @@ public function woocommerce_filter_provider() {
8181
[ 'woocommerce_variation_prices', 'get_variation_price_range' ],
8282
[ 'woocommerce_get_variation_prices_hash', 'add_exchange_rate_to_variation_prices_hash' ],
8383
[ 'woocommerce_shipping_method_add_rate_args', 'convert_shipping_method_rate_cost' ],
84-
[ 'init', 'register_free_shipping_filters' ],
84+
[ 'woocommerce_shipping_zone_shipping_methods', 'convert_free_shipping_method_min_amount' ],
8585
[ 'woocommerce_coupon_get_amount', 'get_coupon_amount' ],
8686
[ 'woocommerce_coupon_get_minimum_amount', 'get_coupon_min_max_amount' ],
8787
[ 'woocommerce_coupon_get_maximum_amount', 'get_coupon_min_max_amount' ],
8888
[ 'woocommerce_new_order', 'add_order_meta' ],
8989
];
9090
}
9191

92-
public function test_registers_woocommerce_filters_for_free_shipping_methods() {
93-
// Add a free shipping method to the default zone.
94-
$default_zone_free_method = \WC_Shipping_Zones::get_zone( 0 )->add_shipping_method( 'free_shipping' );
92+
public function test_convert_free_shipping_method_min_amount() {
93+
// Add multiple shipping methods to the default zone.
94+
$default_zone = \WC_Shipping_Zones::get_zone( 0 );
95+
$default_zone->add_shipping_method( 'flat_rate' );
96+
$free_shipping_id = $default_zone->add_shipping_method( 'free_shipping' );
97+
$free_shipping_method = \WC_Shipping_Zones::get_shipping_method( $free_shipping_id );
98+
$this->assertNotNull( $free_shipping_method );
99+
100+
// Set a min_amount to be converted.
101+
$free_shipping_method->instance_settings['min_amount'] = 10.0;
102+
update_option(
103+
$free_shipping_method->get_instance_option_key(),
104+
$free_shipping_method->instance_settings,
105+
'yes'
106+
);
107+
108+
$this->mock_multi_currency
109+
->expects( $this->once() )
110+
->method( 'get_price' )
111+
->with( 10.0, 'product' )
112+
->willReturn( 25.0 );
95113

96-
// Create a new shipping zone and shipping method.
97-
$new_zone = new WC_Shipping_Zone();
98-
$new_zone_free_method = $new_zone->add_shipping_method( 'free_shipping' );
114+
// The filter should be registered when the zone's shipping methods are retrieved.
115+
$methods = $default_zone->get_shipping_methods();
99116

100-
$this->frontend_prices->register_free_shipping_filters();
117+
$this->assertCount( 2, $methods );
101118

102-
$this->assertGreaterThan( 98, has_filter( 'option_woocommerce_free_shipping_' . $default_zone_free_method . '_settings', [ $this->frontend_prices, 'get_free_shipping_min_amount' ] ) );
103-
$this->assertGreaterThan( 98, has_filter( 'option_woocommerce_free_shipping_' . $new_zone_free_method . '_settings', [ $this->frontend_prices, 'get_free_shipping_min_amount' ] ) );
119+
$test_gateway = $methods[ $free_shipping_id ];
120+
$this->assertInstanceOf( \WC_Shipping_Free_Shipping::class, $test_gateway );
121+
$this->assertEquals( 25.0, $test_gateway->min_amount );
122+
123+
// Make sure it doesn't change the gateway's settings directly.
124+
$this->assertEquals( 10.0, $test_gateway->instance_settings['min_amount'] );
104125
}
105126

106127
public function test_get_product_price_returns_empty_price() {
@@ -332,20 +353,6 @@ public function test_get_coupon_min_max_amount_converts_amount_as_product() {
332353
$this->assertSame( 12.5, $this->frontend_prices->get_coupon_min_max_amount( '5.0' ) );
333354
}
334355

335-
public function test_get_free_shipping_min_amount_returns_empty_amount() {
336-
$this->assertSame( [ 'key' => 'value' ], $this->frontend_prices->get_free_shipping_min_amount( [ 'key' => 'value' ] ) );
337-
}
338-
339-
public function test_get_free_shipping_min_amount_returns_zero_amount() {
340-
$this->assertSame( [ 'min_amount' => '0' ], $this->frontend_prices->get_free_shipping_min_amount( [ 'min_amount' => '0' ] ) );
341-
}
342-
343-
public function test_get_free_shipping_min_amount_converts_amount_as_product() {
344-
$this->mock_multi_currency->method( 'get_price' )->with( 5.0, 'product' )->willReturn( 12.5 );
345-
346-
$this->assertSame( [ 'min_amount' => 12.5 ], $this->frontend_prices->get_free_shipping_min_amount( [ 'min_amount' => '5.0' ] ) );
347-
}
348-
349356
public function test_add_order_meta_skips_default_currency() {
350357
$this->mock_multi_currency->method( 'get_default_currency' )->willReturn( new WCPay\MultiCurrency\Currency( $this->localization_service, 'USD' ) );
351358

0 commit comments

Comments
 (0)