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']; $value = $addon['value']; /* * 'woocommerce_addons_add_cart_price_to_value' * * Use this filter to display the price next to each selected add-on option. * By default, add-on prices show up only next to flat fee add-ons. * * @param boolean */ $add_price_to_value = apply_filters( 'woocommerce_addons_add_cart_price_to_value', false, $cart_item ); if ( 0.0 === (float) $addon['price'] ) { $value .= ''; } elseif ( 'percentage_based' === $addon['price_type'] && 0.0 === (float) $price ) { $value .= ''; } elseif ( 'custom_price' === $addon['field_type'] && $addon['price'] ) { if ( class_exists( 'WC_Product_Addons_Helper' ) ) { $addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon['price'], $cart_item['data'] ) ); /* translators: %1$s custom addon price in cart */ $value .= sprintf( _x( ' (%1$s)', 'custom price addon price in cart', 'woocommerce-payments' ), $addon_price ); $addon['display'] = $value; } } elseif ( 'flat_fee' === $addon['price_type'] && $addon['price'] ) { if ( class_exists( 'WC_Product_Addons_Helper' ) ) { $addon_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. $addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value']; } $addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $cart_item['data'] ) ); /* translators: %1$s flat fee addon price in order */ $value .= sprintf( _x( ' (+ %1$s)', 'flat fee addon price in cart', 'woocommerce-payments' ), $addon_price ); } } elseif ( 'quantity_based' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) { if ( class_exists( 'WC_Product_Addons_Helper' ) ) { $addon_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. $addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value']; } $addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $cart_item['data'] ) ); /* translators: %1$s addon price in order */ $value .= sprintf( _x( ' (%1$s)', 'quantity based addon price in cart', 'woocommerce-payments' ), $addon_price ); } } elseif ( 'percentage_based' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) { // 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 ); /* translators: %1$s addon price in order */ $value .= sprintf( _x( ' (%1$s)', 'percentage based addon price in cart', 'woocommerce-payments' ), WC()->cart->get_product_price( $_product ) ); } return [ 'name' => $addon['name'], 'value' => $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' ); $flat_fees = 0; $quantity = $cart_item['quantity']; $price_before_addons = $price; $regular_price_before_addons = $regular_price; $sale_price_before_addons = $sale_price; // 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) ( $price_before_addons * ( $addon_price / 100 ) ); $regular_price += (float) ( $regular_price_before_addons * ( $addon_price / 100 ) ); $sale_price += (float) ( $sale_price_before_addons * ( $addon_price / 100 ) ); break; case 'flat_fee': $flat_fee = $quantity > 0 ? (float) ( $addon_price / $quantity ) : 0; $price += $flat_fee; $regular_price += $flat_fee; $sale_price += $flat_fee; $flat_fees += $flat_fee; 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, 'addons_flat_fees_sum' => $flat_fees, ]; } /** * 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 { $add_price_to_value = apply_filters( 'woocommerce_addons_add_order_price_to_value', false, $item ); $value = $addon['value']; // Pass the timestamp as the add-on value in order to save the timestamp to the DB. if ( isset( $addon['timestamp'] ) ) { $value = $addon['timestamp']; } // If there is an add-on price, add the price of the add-on to the label name. if ( $addon['price'] && $add_price_to_value ) { $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' ); } if ( class_exists( 'WC_Product_Addons_Helper' ) ) { $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' ) ); } if ( 'flat_fee' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) { /* translators: %1$s flat fee addon price in order */ $value .= sprintf( _x( ' (+ %1$s)', 'flat fee addon price in order', 'woocommerce-payments' ), $price ); } elseif ( ( 'quantity_based' === $addon['price_type'] || 'percentage_based' === $addon['price_type'] ) && $addon['price'] && $add_price_to_value ) { /* translators: %1$s addon price in order */ $value .= sprintf( _x( ' (%1$s)', 'addon price in order', 'woocommerce-payments' ), $price ); } elseif ( 'custom_price' === $addon['field_type'] ) { /* translators: %1$s custom addon price in order */ $value = sprintf( _x( ' (%1$s)', 'custom addon price in order', 'woocommerce-payments' ), $price ); } $meta_data['raw_price'] = $this->multi_currency->get_price( $addon['price'], 'product' ); } $meta_data['value'] = $value; return $meta_data; } /** * 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; } }