/** * Function to apply Gift Certificate's credit to cart */ public function apply_smart_coupon_to_cart() { $this->global_wc()->cart->smart_coupon_credit_used = array(); $cart_contains_subscription = false; if (class_exists('WC_Subscriptions_Cart') && WC_Subscriptions_Cart::cart_contains_subscription()) { $cart_contains_subscription = true; } if ($cart_contains_subscription) { $calculation_type = WC_Subscriptions_Cart::get_calculation_type(); if ($calculation_type == 'recurring_total') { return; } } if ($this->global_wc()->cart->applied_coupons) { foreach ($this->global_wc()->cart->applied_coupons as $code) { $smart_coupon = new WC_Coupon($code); if ($smart_coupon->is_valid() && $smart_coupon->discount_type == 'smart_coupon') { $order_total = $this->global_wc()->cart->cart_contents_total + $this->global_wc()->cart->tax_total + $this->global_wc()->cart->shipping_tax_total + $this->global_wc()->cart->shipping_total; if ($this->global_wc()->cart->discount_total != 0 && $this->global_wc()->cart->discount_total + $smart_coupon->amount > $order_total) { $smart_coupon->amount = $order_total - $this->global_wc()->cart->discount_total; } elseif ($smart_coupon->amount > $order_total) { $smart_coupon->amount = $order_total; } $this->global_wc()->cart->discount_total = $this->global_wc()->cart->discount_total + $smart_coupon->amount; if ($cart_contains_subscription) { WC_Subscriptions_Cart::increase_coupon_discount_amount($code, $smart_coupon->amount); } $this->global_wc()->cart->smart_coupon_credit_used[$code] = $smart_coupon->amount; //Code for displaying the price label for the store credit coupons if (empty($this->global_wc()->cart->coupon_discount_amounts)) { $this->global_wc()->cart->coupon_discount_amounts = array(); } $this->global_wc()->cart->coupon_discount_amounts[$code] = $smart_coupon->amount; } } } }
/** * Removes the "set_subscription_prices_for_calculation" filter from the WC Product's woocommerce_get_price hook once * * @since 1.5.10 */ public static function set_prorated_price_for_calculation($price, $product) { if (WC_Subscriptions_Product::is_subscription($product) && self::is_product_prorated($product) && in_array(WC_Subscriptions_Cart::get_calculation_type(), array('combined_total', 'none'))) { $next_payment_date = self::calculate_first_payment_date($product, 'timestamp'); if (self::is_today($next_payment_date)) { return $price; } switch ($product->subscription_period) { case 'week': $days_in_cycle = 7 * $product->subscription_period_interval; break; case 'month': $days_in_cycle = date('t') * $product->subscription_period_interval; break; case 'year': $days_in_cycle = (365 + date('L')) * $product->subscription_period_interval; break; } $days_until_next_payment = ceil(($next_payment_date - gmdate('U')) / (60 * 60 * 24)); if ('combined_total' == WC_Subscriptions_Cart::get_calculation_type()) { $sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee($product); if ($sign_up_fee > 0 && 0 == WC_Subscriptions_Product::get_trial_length($product)) { $price = $sign_up_fee + $days_until_next_payment * (($price - $sign_up_fee) / $days_in_cycle); } } elseif ('none' == WC_Subscriptions_Cart::get_calculation_type()) { $price = $days_until_next_payment * ($price / $days_in_cycle); } } return $price; }
/** * Sets which coupons should be applied for this calculation. * * This function is hooked to "woocommerce_before_calculate_totals" so that WC will calculate a subscription * product's total based on the total of it's price per period and sign up fee (if any). * * @since 1.3.5 */ public static function remove_coupons($cart) { global $woocommerce; $calculation_type = WC_Subscriptions_Cart::get_calculation_type(); // Only hook when totals are being calculated completely (on cart & checkout pages) if ('none' == $calculation_type || !WC_Subscriptions_Cart::cart_contains_subscription() || !is_checkout() && !is_cart() && !defined('WOOCOMMERCE_CHECKOUT') && !defined('WOOCOMMERCE_CART')) { return; } $applied_coupons = $cart->get_applied_coupons(); // If we're calculating a sign-up fee or recurring fee only amount, remove irrelevant coupons if (!empty($applied_coupons)) { // Keep track of which coupons, if any, need to be reapplied immediately $coupons_to_reapply = array(); if (in_array($calculation_type, array('combined_total', 'sign_up_fee_total', 'recurring_total'))) { foreach ($applied_coupons as $coupon_code) { $coupon = new WC_Coupon($coupon_code); if (in_array($coupon->type, array('recurring_fee', 'recurring_percent'))) { // always apply coupons to their specific calculation case if ('recurring_total' == $calculation_type) { $coupons_to_reapply[] = $coupon_code; } elseif ('combined_total' == $calculation_type && !WC_Subscriptions_Cart::cart_contains_free_trial()) { // sometimes apply recurring coupons to initial total $coupons_to_reapply[] = $coupon_code; } else { self::$removed_coupons[] = $coupon_code; } } elseif (in_array($calculation_type, array('combined_total', 'sign_up_fee_total', 'none')) && !in_array($coupon->type, array('recurring_fee', 'recurring_percent'))) { // apply all coupons to the first payment $coupons_to_reapply[] = $coupon_code; } else { self::$removed_coupons[] = $coupon_code; } } // Now remove all coupons (WC only provides a function to remove all coupons) $cart->remove_coupons(); // And re-apply those which relate to this calculation $woocommerce->cart->applied_coupons = $coupons_to_reapply; } } }
/** * Apply sign up fee or recurring fee discount * * @since 1.2 */ public static function apply_subscription_discount($original_price, $cart_item, $cart) { _deprecated_function(__METHOD__, '2.0.10', 'Have moved to filtering on "woocommerce_coupon_get_discount_amount" to return discount amount. See: ' . __CLASS__ . '::get_discount_amount()'); $product_id = $cart_item['data']->is_type(array('subscription_variation')) ? $cart_item['data']->variation_id : $cart_item['data']->id; if (!WC_Subscriptions_Product::is_subscription($product_id)) { return $original_price; } $price = $calculation_price = $original_price; $calculation_type = WC_Subscriptions_Cart::get_calculation_type(); if (!empty($cart->applied_coupons)) { foreach ($cart->applied_coupons as $code) { $coupon = new WC_Coupon($code); // Pre 2.5 is_valid_for_product() does not use wc_get_product_coupon_types() if (WC_Subscriptions::is_woocommerce_pre('2.5')) { $is_valid_for_product = true; } else { $is_valid_for_product = $coupon->is_valid_for_product(wc_get_product($product_id), $cart_item); } if ($coupon->apply_before_tax() && $coupon->is_valid() && $is_valid_for_product) { $apply_recurring_coupon = $apply_recurring_percent_coupon = $apply_initial_coupon = $apply_initial_percent_coupon = false; // Apply recurring fee discounts to recurring total calculations if ('recurring_total' == $calculation_type) { $apply_recurring_coupon = 'recurring_fee' == $coupon->type ? true : false; $apply_recurring_percent_coupon = 'recurring_percent' == $coupon->type ? true : false; } if ('none' == $calculation_type) { // If all items have a free trial we don't need to apply recurring coupons to the initial total if (!WC_Subscriptions_Cart::all_cart_items_have_free_trial()) { if ('recurring_fee' == $coupon->type) { $apply_initial_coupon = true; } if ('recurring_percent' == $coupon->type) { $apply_initial_percent_coupon = true; } } // Apply sign-up discounts to initial total if (!empty($cart_item['data']->subscription_sign_up_fee)) { if ('sign_up_fee' == $coupon->type) { $apply_initial_coupon = true; } if ('sign_up_fee_percent' == $coupon->type) { $apply_initial_percent_coupon = true; } $calculation_price = $cart_item['data']->subscription_sign_up_fee; } } if ($apply_recurring_coupon || $apply_initial_coupon) { $discount_amount = $calculation_price < $coupon->amount ? $calculation_price : $coupon->amount; // Recurring coupons only apply when there is no free trial (carts can have a mix of free trial and non free trial items) if ($apply_initial_coupon && 'recurring_fee' == $coupon->type && !empty($cart_item['data']->subscription_trial_length)) { $discount_amount = 0; } $cart->discount_cart = $cart->discount_cart + $discount_amount * $cart_item['quantity']; $cart = self::increase_coupon_discount_amount($cart, $coupon->code, $discount_amount * $cart_item['quantity']); $price = $price - $discount_amount; } elseif ($apply_recurring_percent_coupon) { $discount_amount = round($calculation_price / 100 * $coupon->amount, WC()->cart->dp); $cart->discount_cart = $cart->discount_cart + $discount_amount * $cart_item['quantity']; $cart = self::increase_coupon_discount_amount($cart, $coupon->code, $discount_amount * $cart_item['quantity']); $price = $price - $discount_amount; } elseif ($apply_initial_percent_coupon) { // Recurring coupons only apply when there is no free trial (carts can have a mix of free trial and non free trial items) if ('recurring_percent' == $coupon->type && empty($cart_item['data']->subscription_trial_length)) { $amount_to_discount = $cart_item['data']->subscription_price; } else { $amount_to_discount = 0; } // Sign up fee coupons only apply to sign up fees if ('sign_up_fee_percent' == $coupon->type) { $amount_to_discount = $cart_item['data']->subscription_sign_up_fee; } $discount_amount = round($amount_to_discount / 100 * $coupon->amount, WC()->cart->dp); $cart->discount_cart = $cart->discount_cart + $discount_amount * $cart_item['quantity']; $cart = self::increase_coupon_discount_amount($cart, $coupon->code, $discount_amount * $cart_item['quantity']); $price = $price - $discount_amount; } } } if ($price < 0) { $price = 0; } } return $price; }
/** * Removes the "set_subscription_prices_for_calculation" filter from the WC Product's woocommerce_get_price hook once * * @since 1.5.10 */ public static function set_prorated_price_for_calculation($price, $product) { if (WC_Subscriptions_Product::is_subscription($product) && self::is_product_prorated($product) && 'none' == WC_Subscriptions_Cart::get_calculation_type()) { $next_payment_date = self::calculate_first_payment_date($product, 'timestamp'); if (self::is_today($next_payment_date)) { return $price; } switch ($product->subscription_period) { case 'week': $days_in_cycle = 7 * $product->subscription_period_interval; break; case 'month': $days_in_cycle = date('t') * $product->subscription_period_interval; break; case 'year': $days_in_cycle = (365 + date('L')) * $product->subscription_period_interval; break; } $days_until_next_payment = ceil(($next_payment_date - gmdate('U')) / (60 * 60 * 24)); $sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee($product); if ($sign_up_fee > 0 && 0 == WC_Subscriptions_Product::get_trial_length($product)) { $price = $sign_up_fee + $days_until_next_payment * (($price - $sign_up_fee) / $days_in_cycle); } else { $price = $days_until_next_payment * ($price / $days_in_cycle); } // Now round the amount to the number of decimals displayed for prices to avoid rounding errors in the total calculations (we don't want to use WC_DISCOUNT_ROUNDING_PRECISION here because it can still lead to rounding errors). For full details, see: https://github.com/Prospress/woocommerce-subscriptions/pull/1134#issuecomment-178395062 $price = round($price, wc_get_price_decimals()); } return $price; }
/** * Returns an array of order items in a TaxCloud friendly format * * array( * [item_key] => array ( * [TIC] => TIC or empty string * [ItemID] => Product ID * [Index] => Item index (can be left blank for our purposes; re-assigned in generate_lookup_data) * [Type] => Item type (Values: cart, fee, shipping) * [Price] => The item price * [Qty] => The quantity of the item being purchased * ) * ) * * @since 4.2 * @return (array) array of items (see above for structure) */ private function get_items_array() { $items = $this->cart->cart_contents; $based_on = WC_WooTax::get_option('tax_based_on'); $final_items = array(); // Add cart items foreach ($items as $item_key => $item) { $product = $item['data']; // Get product TIC $tic_raw = get_post_meta($product->id, 'wootax_tic', true); $tic = $tic_raw == false ? '' : trim($tic_raw); // Get product ID, Qty, and Price $item_id = $item_key; $product_price = $item['line_total'] / $item['quantity']; if ($based_on == 'item-price' || !$based_on) { $qty = $item['quantity']; $price = $product_price; } else { $qty = 1; $price = $product_price * $item['quantity']; } // Add to final items array $final_items[$item_key] = array('Index' => NULL, 'ItemID' => $item_id, 'Price' => $price, 'Qty' => $qty, 'Type' => 'cart'); // Only add TIC if it is set if (!empty($tic)) { $final_items[$item_key]['TIC'] = $tic; } } // Add fees $fees = $this->cart->get_fees(); if (is_array($fees)) { foreach ($fees as $ind => $fee) { // Skip if not taxable (TODO: phase this out too?) if (isset($fee->taxable) && !$fee->taxable) { continue; } // Get fee identifier (fee index since we are in checkout) $fee_id = $fee->id; // Get fee amount $fee->amount $amt = $fee->amount; // Add to final items array $final_items[$fee_id] = array('Index' => NULL, 'ItemID' => $fee_id, 'TIC' => WT_FEE_TIC, 'Qty' => 1, 'Price' => $amt, 'Type' => 'fee'); } } // Add shipping costs $shipping_total = $this->cart->shipping_total; if ($this->is_subscription) { if (!WC_Subscriptions_Cart::charge_shipping_up_front() && (WC_Subscriptions_Cart::get_calculation_type() == 'sign_up_fee_total' || WC_Subscriptions_Cart::get_calculation_type() == 'free_trial_total')) { $shipping_total = 0; } } if ($shipping_total > 0) { // Add a shipping item to the final items array (we assume that only one shipping method is being used per order) $final_items[WT_SHIPPING_ITEM] = array('Index' => NULL, 'ItemID' => WT_SHIPPING_ITEM, 'TIC' => WT_SHIPPING_TIC, 'Qty' => 1, 'Price' => $shipping_total, 'Type' => 'shipping'); } return $final_items; }