WooCommerceProductAddOns.php
11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
<?php
/**
* Class WooCommerceProductAddOns
*
* @package WCPay\MultiCurrency\Compatibility
*/
namespace WCPay\MultiCurrency\Compatibility;
use WC_Product;
use WCPay\MultiCurrency\MultiCurrency;
use WCPay\MultiCurrency\Utils;
/**
* Class that controls Multi Currency Compatibility with WooCommerce Product Add Ons Plugin.
*/
class WooCommerceProductAddOns extends BaseCompatibility {
const ADDONS_CONVERTED_META_KEY = '_wcpay_multi_currency_addons_converted';
/**
* Init the class.
*
* @return void
*/
protected function init() {
// Add needed actions and filters if Product Add Ons is active.
if ( class_exists( 'WC_Product_Addons' ) ) {
if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) {
add_filter( 'woocommerce_product_addons_option_price_raw', [ $this, 'get_addons_price' ], 50, 2 );
add_filter( 'woocommerce_product_addons_price_raw', [ $this, 'get_addons_price' ], 50, 2 );
add_filter( 'woocommerce_product_addons_params', [ $this, 'product_addons_params' ], 50, 1 );
add_filter( 'woocommerce_product_addons_get_item_data', [ $this, 'get_item_data' ], 50, 3 );
add_filter( 'woocommerce_product_addons_update_product_price', [ $this, 'update_product_price' ], 50, 4 );
add_filter( 'woocommerce_product_addons_order_line_item_meta', [ $this, 'order_line_item_meta' ], 50, 4 );
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );
}
if ( wp_doing_ajax() ) {
add_filter( 'woocommerce_product_addons_ajax_get_product_price_including_tax', [ $this, 'get_product_calculation_price' ], 50, 3 );
add_filter( 'woocommerce_product_addons_ajax_get_product_price_excluding_tax', [ $this, 'get_product_calculation_price' ], 50, 3 );
}
}
}
/**
* Checks to see if the product's price should be converted.
*
* @param bool $return Whether to convert the product's price or not. Default is true.
* @param object $product Product object to test.
*
* @return bool True if it should be converted.
*/
public function should_convert_product_price( bool $return, $product ): bool {
// If it's already false, return it.
if ( ! $return ) {
return $return;
}
// Check for cart items to see if they have already been converted.
if ( 1 === $product->get_meta( self::ADDONS_CONVERTED_META_KEY ) ) {
return false;
}
return $return;
}
/**
* Converts the price of an addon from WooCommerce Products Add-on extension.
*
* @param mixed $price The price to be filtered.
* @param array $type The type of the addon.
* @return mixed The price as a string or float.
*/
public function get_addons_price( $price, $type ) {
if ( 'percentage_based' === $type['price_type'] ) {
// If the addon is a percentage_based type $price is actually a percentage
// and doesn't need any conversion.
return $price;
}
return $this->multi_currency->get_price( $price, 'product' );
}
/**
* Fixes currency formatting issues in Product Add-Ons. PAO gets these values directly from the db options,
* so those values aren't filtered. Luckily, there's a filter.
*
* @param array $params Product Add-Ons global parameters.
*
* @return array Adjust parameters.
*/
public function product_addons_params( array $params ): array {
$params['currency_format_num_decimals'] = wc_get_price_decimals();
$params['currency_format_decimal_sep'] = wc_get_price_decimal_separator();
$params['currency_format_thousand_sep'] = wc_get_price_thousand_separator();
return $params;
}
/**
* Filters the cart item data meta so we can provide the proper name with converted add on price.
*
* @param array $addon_data The addon data we are filtering/replacing.
* @param array $addon The addon being processed.
* @param array $cart_item The cart item being processed.
*
* @return array
*/
public function get_item_data( $addon_data, $addon, $cart_item ): array {
$price = isset( $cart_item['addons_price_before_calc'] ) ? $cart_item['addons_price_before_calc'] : $addon['price'];
$name = $addon['name'];
if ( 0.0 === $addon['price'] ) {
$name .= '';
} elseif ( 'percentage_based' === $addon['price_type'] && 0.0 === $price ) {
$name .= '';
} elseif ( 'custom_price' === $addon['field_type'] ) {
$name .= ' (' . wc_price( $addon['price'] ) . ')';
} elseif ( 'percentage_based' !== $addon['price_type'] && $addon['price'] && apply_filters( 'woocommerce_addons_add_price_to_name', '__return_true' ) ) {
// Get our converted and tax adjusted price to put in the add on name.
$price = $this->multi_currency->get_price( $addon['price'], 'product' );
if ( 'input_multiplier' === $addon['field_type'] ) {
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
$price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
}
$price = \WC_Product_Addons_Helper::get_product_addon_price_for_display( $price, $cart_item['data'] );
$name .= ' (' . wc_price( $price ) . ')';
} else {
// Get the percentage cost in the currency in use, and set the meta data on the product that the value was converted.
$_product = wc_get_product( $cart_item['product_id'] );
$price = $this->multi_currency->get_price( $price, 'product' );
$_product->set_price( $price * ( $addon['price'] / 100 ) );
$_product->update_meta_data( self::ADDONS_CONVERTED_META_KEY, 1 );
$name .= ' (' . WC()->cart->get_product_price( $_product ) . ')';
}
return [
'name' => $name,
'value' => $addon['value'],
'display' => isset( $addon['display'] ) ? $addon['display'] : '',
];
}
/**
* Updates the product price according to converted add on values.
*
* @param array $updated_prices Prices updated by Product Add-Ons (unused).
* @param array $cart_item Cart item meta data.
* @param array $prices Original prices passed to Product Add-Ons for calculations.
*
* @return array
*/
public function update_product_price( $updated_prices, $cart_item, $prices ): array {
$price = $this->multi_currency->get_price( $prices['price'], 'product' );
$regular_price = $this->multi_currency->get_price( $prices['regular_price'], 'product' );
$sale_price = $this->multi_currency->get_price( $prices['sale_price'], 'product' );
$quantity = $cart_item['quantity'];
// TODO: Check compat with Smart Coupons.
// Compatibility with Smart Coupons self declared gift amount purchase.
$credit_called = ! empty( $_POST['credit_called'] ) ? $_POST['credit_called'] : null; // phpcs:ignore
if ( empty( $price ) && ! empty( $credit_called ) ) {
// Variable $_POST['credit_called'] is an array.
if ( isset( $credit_called[ $cart_item['data']->get_id() ] ) ) {
$price = (float) $credit_called[ $cart_item['data']->get_id() ];
$regular_price = $price;
$sale_price = $price;
}
}
if ( empty( $price ) && ! empty( $cart_item['credit_amount'] ) ) {
$price = (float) $cart_item['credit_amount'];
$regular_price = $price;
$sale_price = $price;
}
foreach ( $cart_item['addons'] as $addon ) {
// Percentage based and custom defined addon prices do not get converted, all others do.
if ( 'percentage_based' === $addon['price_type'] || 'custom_price' === $addon['field_type'] ) {
$addon_price = $addon['price'];
} elseif ( 'input_multiplier' === $addon['field_type'] ) {
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
} else {
$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
}
switch ( $addon['price_type'] ) {
case 'percentage_based':
$price += (float) ( $cart_item['data']->get_price( 'view' ) * ( $addon_price / 100 ) );
$regular_price += (float) ( $regular_price * ( $addon_price / 100 ) );
$sale_price += (float) ( $sale_price * ( $addon_price / 100 ) );
break;
case 'flat_fee':
$price += (float) ( $addon_price / $quantity );
$regular_price += (float) ( $addon_price / $quantity );
$sale_price += (float) ( $addon_price / $quantity );
break;
default:
$price += (float) $addon_price;
$regular_price += (float) $addon_price;
$sale_price += (float) $addon_price;
break;
}
}
// Let ourselves know this item has had add ons converted.
$cart_item['data']->update_meta_data( self::ADDONS_CONVERTED_META_KEY, 1 );
return [
'price' => $price,
'regular_price' => $regular_price,
'sale_price' => $sale_price,
];
}
/**
* Filters the meta data for order line items so that we can properly set values in the names.
*
* @param array $meta_data A key/value for the meta data to be inserted for the line item.
* @param array $addon The addon being processed.
* @param \WC_Order_Item_Product $item Order item data.
* @param array $values Order item values.
*
* @return array A key/value for the meta data to be inserted for the line item.
*/
public function order_line_item_meta( array $meta_data, array $addon, \WC_Order_Item_Product $item, array $values ): array {
// If there is an add-on price, add the price of the add-on to the label name.
if ( $addon['price'] && apply_filters( 'woocommerce_addons_add_price_to_name', true ) ) {
$product = $item->get_product();
if ( 'percentage_based' === $addon['price_type'] && 0.0 !== (float) $product->get_price() ) {
// Calculate the percentage price.
$addon_price = $product->get_price() * ( $addon['price'] / 100 );
} elseif ( 'custom_price' === $addon['field_type'] ) {
// Custom prices do not get converted.
$addon_price = $addon['price'];
} elseif ( 'input_multiplier' === $addon['field_type'] ) {
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
} else {
// Convert all others.
$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
}
$price = html_entity_decode(
wp_strip_all_tags( wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $values['data'] ) ) ),
ENT_QUOTES,
get_bloginfo( 'charset' )
);
$addon['name'] .= ' (' . $price . ')';
}
if ( 'custom_price' === $addon['field_type'] ) {
$addon['value'] = $addon['price'];
}
return [
'key' => $addon['name'],
'value' => $addon['value'],
];
}
/**
* Gets the product price during ajax requests from the product page.
*
* @param float $price Price to get converted.
* @param int $quantity Quantity of the product selected.
* @param \WC_Product $product WC_Product related to the price.
*
* @return float Adjusted price.
*/
public function get_product_calculation_price( float $price, int $quantity, \WC_Product $product ): float {
return $this->multi_currency->get_price( $price / $quantity, 'product' ) * $quantity;
}
}