| @@ -15,55 +15,99 @@ | ||
| 15 | 15 | $place, |
| 16 | 16 | PricingRule $pricingRule |
| 17 | 17 | ) { |
| 18 | 18 | |
| 19 | - /** | |
| 20 | - * Clarifying type | |
| 21 | - * | |
| 22 | - * @var woocommerce_wpml $woocommerce_wpml | |
| 23 | - */ global $woocommerce_wpml; | |
| 24 | - | |
| 25 | - if ( $pricingRule->isPercentage() || ! $productPrice || ! $woocommerce_wpml ) { | |
| 19 | + if ( $pricingRule->isPercentage() || ! $productPrice ) { | |
| 26 | 20 | return $productPrice; |
| 27 | 21 | } |
| 28 | - | |
| 29 | - if ( ! $woocommerce_wpml->multi_currency || ! method_exists( $woocommerce_wpml->multi_currency, | |
| 30 | - 'get_client_currency' ) ) { | |
| 31 | - return $productPrice; | |
| 32 | - } | |
| 33 | - | |
| 34 | - $currentCurrency = $woocommerce_wpml->multi_currency->get_client_currency(); | |
| 35 | - | |
| 36 | - if ( wcml_get_woocommerce_currency_option() !== $currentCurrency ) { | |
| 37 | - | |
| 38 | - return $woocommerce_wpml->multi_currency->prices->convert_price_amount( $productPrice, | |
| 39 | - $currentCurrency ); | |
| 40 | - } | |
| 41 | - | |
| 42 | - return $productPrice; | |
| 22 | + | |
| 23 | + return $this->convertPrice( $productPrice ); | |
| 43 | 24 | }, 10, 10 ); |
| 44 | - | |
| 25 | + | |
| 45 | 26 | add_filter( 'tiered_pricing_table/services/regular_pricing/price', function ( $newPrice ) { |
| 46 | - | |
| 27 | + | |
| 47 | 28 | if ( is_null( $newPrice ) ) { |
| 48 | 29 | return $newPrice; |
| 49 | 30 | } |
| 50 | - | |
| 51 | - global $woocommerce_wpml; | |
| 52 | - | |
| 53 | - if ( ! $woocommerce_wpml || ! $woocommerce_wpml->multi_currency || ! method_exists( $woocommerce_wpml->multi_currency, | |
| 54 | - 'get_client_currency' ) ) { | |
| 55 | - return $newPrice; | |
| 56 | - } | |
| 57 | - | |
| 58 | - $currentCurrency = $woocommerce_wpml->multi_currency->get_client_currency(); | |
| 59 | - | |
| 60 | - if ( wcml_get_woocommerce_currency_option() !== $currentCurrency ) { | |
| 61 | - return $woocommerce_wpml->multi_currency->prices->convert_price_amount( $newPrice, $currentCurrency ); | |
| 62 | - } | |
| 63 | - | |
| 64 | - return $newPrice; | |
| 31 | + | |
| 32 | + return $this->convertPrice( $newPrice ); | |
| 65 | 33 | }, 10, 4 ); |
| 34 | + | |
| 35 | + /** | |
| 36 | + * Note: deliberately NO cart-side handlers here. | |
| 37 | + * | |
| 38 | + * Unlike WOOCS/CURCY/WCCS, WCML does not filter "woocommerce_product_get_price" for regular | |
| 39 | + * products — it converts by filtering "get_post_metadata" for the _price meta | |
| 40 | + * (WCML_Multi_Currency_Prices::product_price_filter). A price written to the product object | |
| 41 | + * with set_price() therefore never passes through WCML's conversion. | |
| 42 | + * | |
| 43 | + * So the tiered price must reach the cart already converted, which is exactly what the | |
| 44 | + * "price_by_rules" filter above does (it runs in the "view" context, including for the cart). | |
| 45 | + * Handing the cart a raw "edit" context price instead would leave it unconverted — that was | |
| 46 | + * the 7.1.3 regression where cart prices ignored exchange rates. | |
| 47 | + */ | |
| 48 | + } | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * Whether WPML Multicurrency is active and a non-base currency is currently selected. | |
| 52 | + * | |
| 53 | + * Used to gate the cart-side handlers: when no conversion is happening (multicurrency module off, | |
| 54 | + * or the base currency is selected) the default cart price/subtotal handling is already correct, | |
| 55 | + * and the discount markup in the subtotal should be preserved. | |
| 56 | + * | |
| 57 | + * @return bool | |
| 58 | + */ | |
| 59 | + protected function isConversionNeeded(): bool { | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * Clarifying type | |
| 63 | + * | |
| 64 | + * @var woocommerce_wpml $woocommerce_wpml | |
| 65 | + */ global $woocommerce_wpml; | |
| 66 | + | |
| 67 | + if ( ! $woocommerce_wpml || ! $woocommerce_wpml->multi_currency || ! $woocommerce_wpml->multi_currency->prices ) { | |
| 68 | + return false; | |
| 69 | + } | |
| 70 | + | |
| 71 | + if ( ! method_exists( $woocommerce_wpml->multi_currency, 'get_client_currency' ) ) { | |
| 72 | + return false; | |
| 73 | + } | |
| 74 | + | |
| 75 | + return wcml_get_woocommerce_currency_option() !== $woocommerce_wpml->multi_currency->get_client_currency(); | |
| 76 | + } | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * Convert a price into the currency currently selected by the client. | |
| 80 | + * | |
| 81 | + * Uses WCML's own conversion pipeline (`raw_price_filter`), which applies both the exchange rate | |
| 82 | + * and the rounding rules configured per currency in the WPML Multicurrency settings. | |
| 83 | + * Calling `convert_price_amount()` alone would skip the rounding rules and produce | |
| 84 | + * tier prices inconsistent with the product's regular price. | |
| 85 | + * | |
| 86 | + * @param mixed $price | |
| 87 | + * | |
| 88 | + * @return mixed | |
| 89 | + */ | |
| 90 | + protected function convertPrice( $price ) { | |
| 91 | + | |
| 92 | + if ( ! $this->isConversionNeeded() ) { | |
| 93 | + return $price; | |
| 94 | + } | |
| 95 | + | |
| 96 | + /** | |
| 97 | + * Clarifying type | |
| 98 | + * | |
| 99 | + * @var woocommerce_wpml $woocommerce_wpml | |
| 100 | + */ global $woocommerce_wpml; | |
| 101 | + | |
| 102 | + if ( ! method_exists( $woocommerce_wpml->multi_currency->prices, 'raw_price_filter' ) ) { | |
| 103 | + return $price; | |
| 104 | + } | |
| 105 | + | |
| 106 | + return $woocommerce_wpml->multi_currency->prices->raw_price_filter( | |
| 107 | + $price, | |
| 108 | + $woocommerce_wpml->multi_currency->get_client_currency() | |
| 109 | + ); | |
| 66 | 110 | } |
| 67 | 111 | |
| 68 | 112 | public function getTitle(): string { |
| 69 | 113 | return 'WPML Multicurrency'; |