$coupon_ids[] */ private $grouped_coupons; /** * @var array [coupon_name] => $coupon_id */ private $single_coupons; private $applied_coupons; private $external_coupons; /** * @var WDP_Cart_Adjustments_Shipping */ private $shipping_adjustments; /** * WDP_Cart_Totals constructor. * * @param $cart WDP_Cart * @param $wc_cart WC_Cart */ public function __construct( $cart, $wc_cart ) { if ( is_null( $wc_cart ) ) { $wc_cart = WC()->cart; } $cart = apply_filters( 'wdp_cart_before_totals', $cart, $wc_cart ); $this->context = $cart->get_context(); $wc_cart->get_customer()->set_is_vat_exempt( $this->context->get_tax_exempt() ); $this->external_coupons = $this->context->get_option('disable_external_coupons') ? array() : $cart->get_external_coupons(); $cart_adjustments = $cart->get_adjustments(); $this->shipping_adjustments = isset( $cart_adjustments['shipping'] ) ? $cart_adjustments['shipping'] : null; $this->coupons = isset( $cart_adjustments['coupons'] ) ? $cart_adjustments['coupons'] : array(); $this->fees = isset( $cart_adjustments['fees'] ) ? $cart_adjustments['fees'] : array(); add_filter( 'woocommerce_coupon_message', array( $this, 'no_coupon_msg' ), 10, 3 ); add_filter( 'woocommerce_coupon_error', array( $this, 'no_coupon_msg' ), 10, 3 ); // Calculate totals to handle fee, coupons and shipping which depends on cart contents(totals) // e.g. coupon with min/max spend $wc_cart->calculate_totals(); $this->install_hooks( $wc_cart ); $wc_cart->calculate_totals(); remove_filter( 'woocommerce_coupon_message', array( $this, 'no_coupon_msg' ), 10 ); remove_filter( 'woocommerce_coupon_error', array( $this, 'no_coupon_msg' ), 10 ); } public function no_coupon_msg( $msg, $msg_code, $wc_coupon ) { return ""; } public function install_hooks( $wc_cart ) { // fee add_action( 'woocommerce_cart_calculate_fees', array( $this, 'woocommerce_cart_calculate_fees' ), 10, 1 ); // coupons add_action( 'woocommerce_cart_calculate_fees', array( $this, 'woocommerce_cart_calculate_coupons' ), 10, 1 ); add_filter( 'woocommerce_get_shop_coupon_data', array( $this, 'woocommerce_get_shop_coupon_data' ), 10, 2 ); $this->apply_coupons_to_wc_cart( $wc_cart ); // delete [Remove] for coupons if ( $this->applied_coupons ) { add_filter( 'woocommerce_cart_totals_coupon_html', array( $this, 'woocommerce_cart_totals_coupon_html' ), 10, 3 ); } // apply shipping add_filter( 'woocommerce_package_rates', array( $this, 'woocommerce_package_rates' ), 10, 2 ); add_filter( 'woocommerce_cart_shipping_method_full_label', array( $this, 'woocommerce_cart_shipping_method_full_label' ), 10, 2 ); // To apply shipping we have to clear stored packages in session to allow 'woocommerce_package_rates' filter run foreach ( WC()->session->get_session_data() as $key => $value ) { if ( preg_match( '/(shipping_for_package_).*/', $key ) ) { unset( WC()->session->$key ); } } } /** * @param WC_Cart $wc_cart */ public function woocommerce_cart_calculate_fees( $wc_cart ) { $applied_fees = array(); $fees_tax = array(); $cart_total = $this->context->is_prices_includes_tax() ? $wc_cart->get_cart_contents_total() + $wc_cart->get_cart_contents_tax() : $wc_cart->get_cart_contents_total(); foreach ( $this->fees as $i => $fee ) { $fee_amount = 0; if ( 'amount' === $fee['type'] ) { $fee_amount = $fee['value']; } elseif ( 'percentage' === $fee['type'] ) { $fee_amount = $cart_total * $fee['value'] / 100; } if ( ! empty( $fee_amount ) ) { if ( $this->context->is_combine_multiple_fees() || empty( $fee['name'] ) ) { $fee_name = $this->context->get_option( 'default_fee_name' ); } else { $fee_name = $fee['name']; } $tax_class = ! empty( $fee['tax_class'] ) ? $fee['tax_class'] : ""; $taxable = (boolean) $tax_class; $fees_tax[ $fee_name ] = array( 'taxable' => $taxable, 'tax_class' => $tax_class, ); if ( ! isset( $applied_fees[ $fee_name ][ $fee['rule_id'] ] ) ) { $applied_fees[ $fee_name ][ $fee['rule_id'] ] = 0; } $applied_fees[ $fee_name ][ $fee['rule_id'] ] += $fee_amount; } } foreach ( $applied_fees as $fee_name => $amount_per_rule ) { $wc_cart->add_fee( $fee_name, array_sum( $amount_per_rule ), $fees_tax[ $fee_name ]['taxable'], $fees_tax[ $fee_name ]['tax_class'] ); } $totals = $wc_cart->get_totals(); $totals['wdp_fees'] = $applied_fees; $wc_cart->set_totals( $totals ); } /** APPLY COUPONS */ /** * @param WC_Cart $wc_cart */ private function apply_coupons_to_wc_cart( $wc_cart ) { $this->grouped_coupons = array(); $this->single_coupons = array(); foreach ( $this->coupons as $coupon_id => $coupon ) { if ( empty( $coupon['value'] ) ) // skip zero coupons? { continue; } if ( 'amount' === $coupon['type'] ) { if ( $this->context->is_combine_multiple_discounts() || empty( $coupon['name'] ) ) { $coupon_name = $this->context->get_option( 'default_discount_name' ); } else { $coupon_name = $coupon['name']; } $coupon_name = strtolower( $coupon_name ); if ( ! isset( $this->grouped_coupons[ $coupon_name ] ) ) { $this->grouped_coupons[ $coupon_name ] = array(); } $this->grouped_coupons[ $coupon_name ][] = $coupon_id; } elseif ( 'percentage' === $coupon['type'] ) { $template = ! empty( $coupon['name'] ) ? $coupon['name'] : $this->context->get_option( 'default_discount_name' ); $template = strtolower( $template ); $count = 1; do { $coupon_name = "{$template} #{$count}"; $count ++; } while ( isset( $this->single_coupons[ $coupon_name ] ) ); $this->single_coupons[ $coupon_name ] = $coupon_id; } elseif ( 'item_adjustments' === $coupon['type'] ) { $this->single_coupons[ $coupon['name'] ] = $coupon_id; } } // remove postfix for single %% discount if ( count( $this->single_coupons ) == 1 ) { $keys = array_keys( $this->single_coupons ); $values = array_values( $this->single_coupons ); $this->single_coupons = array( str_replace( ' #1', '', $keys[0] ) => $values[0] ); } $this->applied_coupons = array(); add_filter( 'woocommerce_coupon_message', '__return_empty_string', 10, 3 ); // temporary disable 'woocommerce_applied_coupon' hook global $wp_filter; if ( isset( $wp_filter['woocommerce_applied_coupon'] ) ) { $stored_actions = $wp_filter['woocommerce_applied_coupon']; unset( $wp_filter['woocommerce_applied_coupon'] ); } else { $stored_actions = array(); } foreach ( array_keys( $this->external_coupons ) as $code ) { /** * @var $coupon WC_Coupon */ $wc_cart->apply_coupon( $code ); } // restore hook if ( ! empty( $stored_actions ) ) { $wp_filter['woocommerce_applied_coupon'] = $stored_actions; } foreach ( array_keys( $this->grouped_coupons ) as $coupon_name ) { $this->applied_coupons[] = $coupon_name; $wc_cart->apply_coupon( $coupon_name ); } foreach ( array_keys( $this->single_coupons ) as $coupon_name ) { $this->applied_coupons[] = $coupon_name; $wc_cart->apply_coupon( $coupon_name ); } remove_filter( 'woocommerce_coupon_message', '__return_empty_string', 10 ); } /** * Trigger an action to add custom fees. * * @param WC_Cart $wc_cart */ public function woocommerce_cart_calculate_coupons( $wc_cart ) { $applied_grouped_coupons = array(); foreach ( $this->grouped_coupons as $coupon_name => $coupon_ids ) { if ( ! isset( $applied_grouped_coupons[ $coupon_name ] ) ) { $applied_grouped_coupons[ $coupon_name ] = array(); } foreach ( $coupon_ids as $coupon_id ) { $coupon = $this->coupons[ $coupon_id ]; $rule_id = isset( $coupon['rule_id'] ) ? $coupon['rule_id'] : null; $amount = $coupon['value']; if ( ! is_null( $rule_id ) ) { if ( ! isset( $applied_grouped_coupons[ $coupon_name ][ $rule_id ] ) ) { $applied_grouped_coupons[ $coupon_name ][ $rule_id ] = 0; } $applied_grouped_coupons[ $coupon_name ][ $rule_id ] += $amount; } } } $applied_single_coupons = array(); foreach ( $this->single_coupons as $coupon_name => $coupon_id ) { $coupon = $this->coupons[ $coupon_id ]; $rule_id = isset( $coupon['rule_id'] ) ? $coupon['rule_id'] : null; if ( ! is_null( $rule_id ) ) { $applied_single_coupons[ $coupon_name ] = $rule_id; } } $applied_coupons = array( 'grouped' => $applied_grouped_coupons, 'single' => $applied_single_coupons, ); $totals = $wc_cart->get_totals(); $totals['wdp_coupons'] = $applied_coupons; $wc_cart->set_totals( $totals ); } /** * This filter allows custom coupon objects to be created on the fly. * * @param mixed $coupon * @param mixed $data Coupon name * * @return array|mixed */ public function woocommerce_get_shop_coupon_data( $coupon, $data ) { if ( isset( $this->grouped_coupons[ $data ] ) ) { $grouped_coupon = array( 'id' => rand( 0, 1000 ), 'discount_type' => 'fixed_cart', 'amount' => 0, ); foreach ( $this->grouped_coupons[ $data ] as $coupon_id ) { if ( ! empty( $this->coupons[ $coupon_id ] ) ) { $grouped_coupon['amount'] += (float) $this->coupons[ $coupon_id ]['value']; } } if ( ! empty( $grouped_coupon['amount'] ) ) { $coupon = $grouped_coupon; } } elseif ( isset( $this->single_coupons[ $data ] ) ) { $coupon_id = $this->single_coupons[ $data ]; if ( isset( $this->coupons[ $coupon_id ] ) ) { $coupon_data = $this->coupons[ $coupon_id ]; $coupon_type = ( 'percentage' === $coupon_data['type'] ? 'percent' : 'fixed_cart' ); $coupon_amount = (float) $coupon_data['value']; if ( ! empty( $coupon_amount ) ) { $coupon = array( 'id' => $coupon_id + 1, 'discount_type' => $coupon_type, 'amount' => $coupon_amount, ); } } } return $coupon; } /** * Hide [Remove] link * * @param string $coupon_html * @param WC_Coupon $coupon * @param string $discount_amount_html * * @return string */ public function woocommerce_cart_totals_coupon_html( $coupon_html, $coupon, $discount_amount_html ) { if ( in_array( $coupon->get_code(), $this->applied_coupons ) ) { $coupon_html = $discount_amount_html; } return $coupon_html; } /** APPLY SHIPPING */ /** * @param WC_Shipping_Rate[] $rates * @param array $package * * @return WC_Shipping_Rate[] */ public function woocommerce_package_rates( $rates, $package ) { if ( is_null( $this->shipping_adjustments ) ) { return $rates; } if ( $this->shipping_adjustments->is_free() ) { foreach ( $rates as &$rate ) { $meta_data = $rate->get_meta_data(); if ( isset( $meta_data['wdp_initial_cost'] ) ) { $cost = $meta_data['wdp_initial_cost']; } else { $cost = $rate->get_cost(); } $rate->add_meta_data( 'wdp_initial_cost', $cost ); $rate->add_meta_data( 'wdp_rule', $this->shipping_adjustments->get_rule_id_applied_free_shipping() ); $rate->add_meta_data( 'wdp_amount', $cost ); $rate->add_meta_data( 'wdp_free_shipping', true ); $rate->set_cost( 0 ); $rate->set_taxes( array() ); // no taxes } } else { $applied_shipping = array(); foreach ( $rates as &$rate ) { $rate_id = $rate->get_id(); $meta_data = $rate->get_meta_data(); if ( isset( $meta_data['wdp_initial_cost'] ) ) { $cost = $meta_data['wdp_initial_cost']; } else { $cost = $rate->get_cost(); } foreach ( $this->shipping_adjustments->get_items() as $id => $item ) { $amount = 0; if ( 'amount' === $item['type'] ) { $amount = $item['value']; } elseif ( 'percentage' === $item['type'] ) { $amount = $cost * $item['value'] / 100; } elseif ( 'fixed' === $item['type'] ) { $amount = $cost - $item['value']; } if ( empty( $amount ) ) { continue; } if ( ! isset( $applied_shipping[ $rate_id ] ) || $amount > $applied_shipping[ $rate_id ]['amount'] ) { $applied_shipping[ $rate_id ] = array( 'adjustment_id' => $id, 'amount' => $amount, ); } } if ( ! empty( $applied_shipping[ $rate_id ] ) ) { $shipping = $this->shipping_adjustments->get_item( $applied_shipping[ $rate_id ]['adjustment_id'] ); if ( is_null( $shipping ) ) { break; } $amount = $applied_shipping[ $rate_id ]['amount']; $rate->add_meta_data( 'wdp_initial_cost', $cost ); $rate->add_meta_data( 'wdp_rule', $shipping['rule_id'] ); $rate->add_meta_data( 'wdp_amount', $amount ); $rate->add_meta_data( 'wdp_free_shipping', false ); $newcost = $cost - $amount; if ( $newcost < 0 ) { $newcost = 0; } $rate->set_cost( $newcost ); // recalc taxes if ( $cost > 0 ) { $perc = $newcost / $cost; $taxes = $rate->get_taxes(); foreach ( $taxes as $k => $v ) { $taxes[ $k ] = $v * $perc; } $rate->set_taxes( $taxes ); } } }//each not free shipping! } return $rates; } /** * @param string $label * @param WC_Shipping_Rate $method * * @return mixed */ public function woocommerce_cart_shipping_method_full_label( $label, $method ) { if ( false !== strpos( $label, 'wdp-amount' ) ) { return $label; } $meta_data = $method->get_meta_data(); if ( ! isset( $meta_data['wdp_initial_cost'] ) ) { return $label; } $initial_cost = $meta_data['wdp_initial_cost']; $initial_cost_html = '' . wc_price( $initial_cost ) . ''; $initial_cost_html = preg_replace( '/\samount/is', 'wdp-amount', $initial_cost_html ); // if ( $method->get_cost() > 0 ) { $label = preg_replace( '/(]*>)/is', $initial_cost_html . ' $1', $label, 1 ); // } else { // $label .= ': ' . $initial_cost_html . ' ' . wc_price( 0 ); // } return $label; } }