/** * Apply sign up fee or recurring fee discount after tax is calculated * * @since 1.2 * @version 1.3.6 */ public static function apply_subscription_discount_after_tax($coupon, $cart_item, $price) { global $woocommerce; $calculation_type = WC_Subscriptions_Cart::get_calculation_type(); if (sizeof($woocommerce->cart->cart_contents) > 0) { foreach ($woocommerce->cart->cart_contents as $cart_item_key => $cart_item) { if (!WC_Subscriptions_Product::is_subscription($cart_item['product_id'])) { continue; } if (!$coupon->apply_before_tax() && $coupon->is_valid() && self::is_subscription_discountable($cart_item, $coupon)) { $apply_sign_up_coupon = $apply_sign_up_percent_coupon = $apply_recurring_coupon = $apply_recurring_percent_coupon = $apply_initial_coupon = $apply_initial_percent_coupon = false; if ('sign_up_fee_total' == $calculation_type) { $apply_sign_up_coupon = 'sign_up_fee' == $coupon->type ? true : false; $apply_sign_up_percent_coupon = 'sign_up_fee_percent' == $coupon->type ? true : false; } elseif ('recurring_total' == $calculation_type) { $apply_recurring_coupon = 'recurring_fee' == $coupon->type ? true : false; $apply_recurring_percent_coupon = 'recurring_percent' == $coupon->type ? true : false; } if (in_array($calculation_type, array('combined_total', 'none'))) { if (!WC_Subscriptions_Cart::cart_contains_free_trial()) { // Apply recurring discounts to initial total if ('recurring_fee' == $coupon->type) { $apply_initial_coupon = true; } if ('recurring_percent' == $coupon->type) { $apply_initial_percent_coupon = true; } } if (WC_Subscriptions_Cart::get_cart_subscription_sign_up_fee() > 0) { // Apply sign-up discounts to initial total if ('sign_up_fee' == $coupon->type) { $apply_initial_coupon = true; } if ('sign_up_fee_percent' == $coupon->type) { $apply_initial_percent_coupon = true; } } } // Deduct coupon amounts if ($apply_sign_up_coupon || $apply_recurring_coupon || $apply_initial_coupon) { if ($price < $coupon->amount) { $discount_amount = $price; } else { $discount_amount = $coupon->amount; } $woocommerce->cart->discount_total = $woocommerce->cart->discount_total + $discount_amount * $cart_item['quantity']; WC_Subscriptions_Cart::increase_coupon_discount_amount($coupon->code, $discount_amount * $cart_item['quantity']); // Deduct coupon % discounts from relevant total } elseif ($apply_sign_up_percent_coupon || $apply_recurring_percent_coupon) { $woocommerce->cart->discount_total = $woocommerce->cart->discount_total + round($price / 100 * $coupon->amount, $woocommerce->cart->dp); WC_Subscriptions_Cart::increase_coupon_discount_amount($coupon->code, round($price / 100 * $coupon->amount, $woocommerce->cart->dp)); // Deduct coupon % discounts from combined total (we need to calculate percent from base price) } elseif ($apply_initial_percent_coupon) { $product_id = $cart_item['data']->is_type(array('subscription_variation')) ? $cart_item['data']->variation_id : $cart_item['data']->id; // We need to calculate the right amount to discount when the price is the combined sign-up fee and recurring amount if ('combined_total' == $calculation_type && !WC_Subscriptions_Cart::cart_contains_free_trial() && isset($woocommerce->cart->base_sign_up_fees[$product_id]) && $woocommerce->cart->base_sign_up_fees[$product_id] > 0) { $base_total = $woocommerce->cart->base_sign_up_fees[$product_id] + $woocommerce->cart->base_recurring_prices[$product_id]; if ('recurring_percent' == $coupon->type) { $portion_of_total = $woocommerce->cart->base_recurring_prices[$product_id] / $base_total; } if ('sign_up_fee_percent' == $coupon->type) { $portion_of_total = $woocommerce->cart->base_sign_up_fees[$product_id] / $base_total; } $amount_to_discount = WC_Subscriptions_Manager::get_amount_from_proportion($price, $portion_of_total); } else { $amount_to_discount = $price; } $discount_amount = round($amount_to_discount / 100 * $coupon->amount, $woocommerce->cart->dp); $woocommerce->cart->discount_total = $woocommerce->cart->discount_total + $discount_amount; WC_Subscriptions_Cart::increase_coupon_discount_amount($coupon->code, $discount_amount); } } } } }
/** * Version 1.2 introduced a massive change to the order meta data schema. This function goes * through and upgrades the existing data on all orders to the new schema. * * The upgrade process is timeout safe as it keeps a record of the orders upgraded and only * deletes this record once all orders have been upgraded successfully. If operating on a huge * number of orders and the upgrade process times out, only the orders not already upgraded * will be upgraded in future requests that trigger this function. * * @since 1.2 */ private static function upgrade_database_to_1_2() { global $wpdb; set_transient('wc_subscriptions_is_upgrading', 'true', 60 * 2); // Get IDs only and use a direct DB query for efficiency $orders_to_upgrade = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'shop_order' AND post_parent = 0"); $upgraded_orders = get_option('wcs_1_2_upgraded_order_ids', array()); // Transition deprecated subscription status if we aren't in the middle of updating orders if (empty($upgraded_orders)) { $wpdb->query($wpdb->prepare("UPDATE {$wpdb->usermeta} SET meta_value = replace( meta_value, 's:9:\"suspended\"', 's:7:\"on-hold\"' ) WHERE meta_key LIKE %s", '%_' . WC_Subscriptions_Manager::$users_meta_key)); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->usermeta} SET meta_value = replace( meta_value, 's:6:\"failed\"', 's:9:\"cancelled\"' ) WHERE meta_key LIKE %s", '%_' . WC_Subscriptions_Manager::$users_meta_key)); } $orders_to_upgrade = array_diff($orders_to_upgrade, $upgraded_orders); // Upgrade all _sign_up_{field} order meta to new order data format foreach ($orders_to_upgrade as $order_id) { $order = new WC_Order($order_id); // Manually check if a product in an order is a subscription, we can't use WC_Subscriptions_Order::order_contains_subscription( $order ) because it relies on the new data structure $contains_subscription = false; foreach ($order->get_items() as $order_item) { if (WC_Subscriptions_Product::is_subscription(WC_Subscriptions_Order::get_items_product_id($order_item))) { $contains_subscription = true; break; } } if (!$contains_subscription) { continue; } $trial_lengths = WC_Subscriptions_Order::get_meta($order, '_order_subscription_trial_lengths', array()); $trial_length = array_pop($trial_lengths); $has_trial = !empty($trial_length) && $trial_length > 0 ? true : false; $sign_up_fee_total = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_total', 0); // Create recurring_* meta data from existing cart totals $cart_discount = $order->get_cart_discount(); update_post_meta($order_id, '_order_recurring_discount_cart', $cart_discount); $order_discount = $order->get_order_discount(); update_post_meta($order_id, '_order_recurring_discount_total', $order_discount); $order_shipping_tax = get_post_meta($order_id, '_order_shipping_tax', true); update_post_meta($order_id, '_order_recurring_shipping_tax_total', $order_shipping_tax); $order_tax = get_post_meta($order_id, '_order_tax', true); // $order->get_total_tax() includes shipping tax update_post_meta($order_id, '_order_recurring_tax_total', $order_tax); $order_total = $order->get_total(); update_post_meta($order_id, '_order_recurring_total', $order_total); // Set order totals to include sign up fee fields, if there was a sign up fee on the order and a trial period (other wise, the recurring totals are correct) if ($sign_up_fee_total > 0) { // Order totals need to be changed to be equal to sign up fee totals if ($has_trial) { $cart_discount = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0); $order_discount = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_total', 0); $order_tax = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0); $order_total = $sign_up_fee_total; } else { // No trial, sign up fees need to be added to order totals $cart_discount += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0); $order_discount += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_total', 0); $order_tax += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0); $order_total += $sign_up_fee_total; } update_post_meta($order_id, '_order_total', $order_total); update_post_meta($order_id, '_cart_discount', $cart_discount); update_post_meta($order_id, '_order_discount', $order_discount); update_post_meta($order_id, '_order_tax', $order_tax); } // Make sure we get order taxes in WC 1.x format if (false == self::$is_wc_version_2) { $order_taxes = $order->get_taxes(); } else { $order_tax_row = $wpdb->get_row($wpdb->prepare("\n\t\t\t\t\tSELECT * FROM {$wpdb->postmeta}\n\t\t\t\t\tWHERE meta_key = '_order_taxes_old'\n\t\t\t\t\tAND post_id = %s\n\t\t\t\t\t", $order_id)); $order_taxes = (array) maybe_unserialize($order_tax_row->meta_value); } // Set recurring taxes to order taxes, if using WC 2.0, this will be migrated to the new format in @see self::upgrade_to_latest_wc() update_post_meta($order_id, '_order_recurring_taxes', $order_taxes); $sign_up_fee_taxes = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_taxes', array()); // Update order taxes to include sign up fee taxes foreach ($sign_up_fee_taxes as $index => $sign_up_tax) { if ($has_trial && $sign_up_fee_total > 0) { // Order taxes need to be set to the same as the sign up fee taxes if (isset($sign_up_tax['cart_tax']) && $sign_up_tax['cart_tax'] > 0) { $order_taxes[$index]['cart_tax'] = $sign_up_tax['cart_tax']; } } elseif (!$has_trial && $sign_up_fee_total > 0) { // Sign up fee taxes need to be added to order taxes if (isset($sign_up_tax['cart_tax']) && $sign_up_tax['cart_tax'] > 0) { $order_taxes[$index]['cart_tax'] += $sign_up_tax['cart_tax']; } } } if (false == self::$is_wc_version_2) { // Doing it right: updated Subs *before* updating WooCommerce, the WooCommerce updater will take care of data migration update_post_meta($order_id, '_order_taxes', $order_taxes); } else { // Doing it wrong: updated Subs *after* updating WooCommerce, need to store in WC2.0 tax structure $index = 0; $new_order_taxes = $order->get_taxes(); foreach ($new_order_taxes as $item_id => $order_tax) { $index = $index + 1; if (!isset($order_taxes[$index]['label']) || !isset($order_taxes[$index]['cart_tax']) || !isset($order_taxes[$index]['shipping_tax'])) { continue; } // Add line item meta if ($item_id) { woocommerce_update_order_item_meta($item_id, 'compound', absint(isset($order_taxes[$index]['compound']) ? $order_taxes[$index]['compound'] : 0)); woocommerce_update_order_item_meta($item_id, 'tax_amount', woocommerce_clean($order_taxes[$index]['cart_tax'])); woocommerce_update_order_item_meta($item_id, 'shipping_tax_amount', woocommerce_clean($order_taxes[$index]['shipping_tax'])); } } } /* Upgrade each order item to use new Item Meta schema */ $order_subscription_periods = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_periods', array()); $order_subscription_intervals = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_intervals', array()); $order_subscription_lengths = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_lengths', array()); $order_subscription_trial_lengths = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_trial_lengths', array()); $order_items = $order->get_items(); foreach ($order_items as $index => $order_item) { $product_id = WC_Subscriptions_Order::get_items_product_id($order_item); $item_meta = new WC_Order_Item_Meta($order_item['item_meta']); $subscription_interval = isset($order_subscription_intervals[$product_id]) ? $order_subscription_intervals[$product_id] : 1; $subscription_length = isset($order_subscription_lengths[$product_id]) ? $order_subscription_lengths[$product_id] : 0; $subscription_trial_length = isset($order_subscription_trial_lengths[$product_id]) ? $order_subscription_trial_lengths[$product_id] : 0; $subscription_sign_up_fee = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0); if ($sign_up_fee_total > 0) { // Discounted price * Quantity $sign_up_fee_line_total = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0); $sign_up_fee_line_tax = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0); // Base price * Quantity $sign_up_fee_line_subtotal = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0) + WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0); $sign_up_fee_propotion = $sign_up_fee_line_total > 0 ? $sign_up_fee_line_subtotal / $sign_up_fee_line_total : 0; $sign_up_fee_line_subtotal_tax = WC_Subscriptions_Manager::get_amount_from_proportion(WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0), $sign_up_fee_propotion); if ($has_trial) { // Set line item totals equal to sign up fee totals $order_item['line_subtotal'] = $sign_up_fee_line_subtotal; $order_item['line_subtotal_tax'] = $sign_up_fee_line_subtotal_tax; $order_item['line_total'] = $sign_up_fee_line_total; $order_item['line_tax'] = $sign_up_fee_line_tax; } else { // No trial period, sign up fees need to be added to order totals $order_item['line_subtotal'] += $sign_up_fee_line_subtotal; $order_item['line_subtotal_tax'] += $sign_up_fee_line_subtotal_tax; $order_item['line_total'] += $sign_up_fee_line_total; $order_item['line_tax'] += $sign_up_fee_line_tax; } } // Upgrading with WC 1.x if (method_exists($item_meta, 'add')) { $item_meta->add('_subscription_period', $order_subscription_periods[$product_id]); $item_meta->add('_subscription_interval', $subscription_interval); $item_meta->add('_subscription_length', $subscription_length); $item_meta->add('_subscription_trial_length', $subscription_trial_length); $item_meta->add('_subscription_recurring_amount', $order_item['line_subtotal']); // WC_Subscriptions_Product::get_price() would return a price without filters applied $item_meta->add('_subscription_sign_up_fee', $subscription_sign_up_fee); // Set recurring amounts for the item $item_meta->add('_recurring_line_total', $order_item['line_total']); $item_meta->add('_recurring_line_tax', $order_item['line_tax']); $item_meta->add('_recurring_line_subtotal', $order_item['line_subtotal']); $item_meta->add('_recurring_line_subtotal_tax', $order_item['line_subtotal_tax']); $order_item['item_meta'] = $item_meta->meta; $order_items[$index] = $order_item; } else { // Ignoring all advice, upgrading 4 months after version 1.2 was released, and doing it with WC 2.0 installed woocommerce_add_order_item_meta($index, '_subscription_period', $order_subscription_periods[$product_id]); woocommerce_add_order_item_meta($index, '_subscription_interval', $subscription_interval); woocommerce_add_order_item_meta($index, '_subscription_length', $subscription_length); woocommerce_add_order_item_meta($index, '_subscription_trial_length', $subscription_trial_length); woocommerce_add_order_item_meta($index, '_subscription_trial_period', $order_subscription_periods[$product_id]); woocommerce_add_order_item_meta($index, '_subscription_recurring_amount', $order_item['line_subtotal']); woocommerce_add_order_item_meta($index, '_subscription_sign_up_fee', $subscription_sign_up_fee); // Calculated recurring amounts for the item woocommerce_add_order_item_meta($index, '_recurring_line_total', $order_item['line_total']); woocommerce_add_order_item_meta($index, '_recurring_line_tax', $order_item['line_tax']); woocommerce_add_order_item_meta($index, '_recurring_line_subtotal', $order_item['line_subtotal']); woocommerce_add_order_item_meta($index, '_recurring_line_subtotal_tax', $order_item['line_subtotal_tax']); if ($sign_up_fee_total > 0) { // Order totals have changed woocommerce_update_order_item_meta($index, '_line_subtotal', woocommerce_format_decimal($order_item['line_subtotal'])); woocommerce_update_order_item_meta($index, '_line_subtotal_tax', woocommerce_format_decimal($order_item['line_subtotal_tax'])); woocommerce_update_order_item_meta($index, '_line_total', woocommerce_format_decimal($order_item['line_total'])); woocommerce_update_order_item_meta($index, '_line_tax', woocommerce_format_decimal($order_item['line_tax'])); } } } // Save the new meta on the order items for WC 1.x (the API functions already saved the data for WC2.x) if (false == self::$is_wc_version_2) { update_post_meta($order_id, '_order_items', $order_items); } $upgraded_orders[] = $order_id; update_option('wcs_1_2_upgraded_order_ids', $upgraded_orders); } // Remove the lock on upgrading delete_transient('wc_subscriptions_is_upgrading'); }
/** * Uses the a subscription's combined price total calculated by WooCommerce to determine the * total price that should be charged per period. * * @since 1.2 */ public static function calculate_subscription_totals($total) { global $woocommerce; if (!self::cart_contains_subscription()) { return $total; } elseif ('sign_up_fee' == self::$recalculation_type) { // We've requested totals be recalculated with sign up fee only, we need to remove anything shipping related from the sign-up fee totals $total = $total - $woocommerce->cart->shipping_tax_total - $woocommerce->cart->shipping_total; $woocommerce->cart->shipping_taxes = array(); $woocommerce->cart->shipping_tax_total = 0; self::$recalculation_type = 'none'; return $total; } $base_sign_up_fee = self::get_cart_subscription_sign_up_fee(); if ($base_sign_up_fee == 0 || self::$recalculation_type == 'base_recurring_fee') { // Nothing to fudge here, but we still need to keep a record of recurring totals foreach ($woocommerce->cart->get_cart() as $cart_item_key => $values) { $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_total'] = $values['line_total']; $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_tax'] = $values['line_tax']; $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_subtotal'] = $values['line_subtotal']; $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_subtotal_tax'] = $values['line_subtotal_tax']; } $woocommerce->cart->recurring_cart_contents_total = $woocommerce->cart->cart_contents_total; $woocommerce->cart->recurring_discount_cart = $woocommerce->cart->discount_cart; $woocommerce->cart->recurring_discount_total = $woocommerce->cart->discount_total; $woocommerce->cart->recurring_subtotal = $woocommerce->cart->subtotal; $woocommerce->cart->recurring_subtotal_ex_tax = $woocommerce->cart->subtotal_ex_tax; $woocommerce->cart->recurring_taxes = $woocommerce->cart->get_taxes(); $woocommerce->cart->recurring_tax_total = $woocommerce->cart->tax_total; $woocommerce->cart->recurring_total = $total; // after calculating the recurring fee with discount, reset flag if (self::$recalculation_type == 'base_recurring_fee') { self::$recalculation_type = 'none'; return $total; } } else { // The $total = price per period + sign up fee, so lets derive each one individually $sign_up_fee_proportion = $base_sign_up_fee / ($base_sign_up_fee + self::$base_product_price); // self::$base_product_price as set in set_subscription_prices_for_calculation() $recurring_proportion = 1 - $sign_up_fee_proportion; foreach ($woocommerce->cart->get_cart() as $cart_item_key => $values) { $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_total'] = WC_Subscriptions_Manager::get_amount_from_proportion($values['line_total'], $recurring_proportion); $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_tax'] = WC_Subscriptions_Manager::get_amount_from_proportion($values['line_tax'], $recurring_proportion); $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_subtotal'] = WC_Subscriptions_Manager::get_amount_from_proportion($values['line_subtotal'], $recurring_proportion); $woocommerce->cart->recurring_cart_contents[$values['product_id']]['recurring_line_subtotal_tax'] = WC_Subscriptions_Manager::get_amount_from_proportion($values['line_subtotal_tax'], $recurring_proportion); } // Calculate recurring totals, required for totals display correctly on cart and order page $woocommerce->cart->recurring_cart_contents_total = WC_Subscriptions_Manager::get_amount_from_proportion($woocommerce->cart->cart_contents_total, $recurring_proportion); $woocommerce->cart->recurring_discount_cart = WC_Subscriptions_Manager::get_amount_from_proportion($woocommerce->cart->discount_cart, $recurring_proportion); $woocommerce->cart->recurring_discount_total = WC_Subscriptions_Manager::get_amount_from_proportion($woocommerce->cart->discount_total, $recurring_proportion); $woocommerce->cart->recurring_subtotal = WC_Subscriptions_Manager::get_amount_from_proportion($woocommerce->cart->subtotal, $recurring_proportion); $woocommerce->cart->recurring_subtotal_ex_tax = WC_Subscriptions_Manager::get_amount_from_proportion($woocommerce->cart->subtotal_ex_tax, $recurring_proportion); $woocommerce->cart->recurring_taxes = array(); // Add non-shipping taxes foreach ($woocommerce->cart->taxes as $tax_id => $tax_amount) { $woocommerce->cart->recurring_taxes[$tax_id] = WC_Subscriptions_Manager::get_amount_from_proportion($tax_amount, $recurring_proportion); } // And shipping taxes foreach ($woocommerce->cart->shipping_taxes as $tax_id => $tax_amount) { $woocommerce->cart->recurring_taxes[$tax_id] = $tax_amount; } // Shipping only applies to recurring payments // Shipping only applies to recurring amounts, not the sign-up fee, so we'll add it in its entirety to the recurring amount later $total_sans_shipping = $total - $woocommerce->cart->shipping_tax_total - $woocommerce->cart->shipping_total; $total_ex_tax = $total_sans_shipping - $woocommerce->cart->tax_total; $recurring_total_ex_tax = WC_Subscriptions_Manager::get_amount_from_proportion($total_ex_tax, $recurring_proportion) + $woocommerce->cart->shipping_total; $woocommerce->cart->recurring_total = WC_Subscriptions_Manager::get_amount_from_proportion($total_sans_shipping, $recurring_proportion) + $woocommerce->cart->shipping_tax_total + $woocommerce->cart->shipping_total; $woocommerce->cart->recurring_tax_total = $woocommerce->cart->recurring_total - $recurring_total_ex_tax; /** Handle pricing adjustments - Recurring / Sign up fee discounts / trial periods */ // Recurring discount if (WC_Subscriptions_Coupon::cart_contains_recurring_discount()) { // save cart with combined totals $original_cart = clone $woocommerce->cart; // calculate total with sign up fee only first self::$recalculation_type = 'sign_up_fee'; $woocommerce->cart->calculate_totals(); // save cart with just sign up fee $sign_up_fee_cart = clone $woocommerce->cart; // now calculate base recurring fee with discount included self::$recalculation_type = 'base_recurring_fee'; $woocommerce->cart->calculate_totals(); if (self::cart_contains_free_trial()) { // restore sign up fee cart contents & total $woocommerce->cart->cart_contents = $sign_up_fee_cart->cart_contents; $woocommerce->cart->cart_contents_total = $sign_up_fee_cart->cart_contents_total; // restore sign up fee cart sub-totals $woocommerce->cart->subtotal = $sign_up_fee_cart->subtotal; $woocommerce->cart->subtotal_ex_tax = $sign_up_fee_cart->subtotal_ex_tax; // restore sign up fee cart taxes $woocommerce->cart->taxes = $sign_up_fee_cart->taxes; $woocommerce->cart->tax_total = $sign_up_fee_cart->tax_total; // final total is sign up fee cart total only $total = $sign_up_fee_cart->total; // Add sign up fee discounts to cart/total discounts (which already include recurring discounts) $woocommerce->cart->discount_cart = $sign_up_fee_cart->discount_cart > 0 ? $sign_up_fee_cart->discount_cart : 0; $woocommerce->cart->discount_total = $sign_up_fee_cart->discount_total > 0 ? $sign_up_fee_cart->discount_total : 0; } else { // restore combined cart contents & total $woocommerce->cart->cart_contents = $original_cart->cart_contents; $woocommerce->cart->cart_contents_total = $original_cart->cart_contents_total; // restore combined cart sub-totals $woocommerce->cart->subtotal = $original_cart->subtotal; $woocommerce->cart->subtotal_ex_tax = $original_cart->subtotal_ex_tax; // combine and total any taxes on sign up fees to the cart total (which already includes taxes on recurring fees) foreach ($woocommerce->cart->taxes as $tax_key => $tax_amount) { $woocommerce->cart->taxes[$tax_key] += $sign_up_fee_cart->taxes[$tax_key]; } $woocommerce->cart->tax_total += $sign_up_fee_cart->tax_total; // Add sign up fee discounts to cart/total discounts (which already include recurring discounts) $woocommerce->cart->discount_cart += $sign_up_fee_cart->discount_cart > 0 ? $sign_up_fee_cart->discount_cart : 0; $woocommerce->cart->discount_total += $sign_up_fee_cart->discount_total > 0 ? $sign_up_fee_cart->discount_total : 0; // Final total is sign up fee cart total + recurring fee cart total $total = $sign_up_fee_cart->total + $woocommerce->cart->recurring_total; } // Sign up fee discount } elseif (WC_Subscriptions_Coupon::cart_contains_sign_up_discount()) { // save cart with combined totals $original_cart = clone $woocommerce->cart; // calculate totals with sign up fee only first self::$recalculation_type = 'sign_up_fee'; $woocommerce->cart->calculate_totals(); if (self::cart_contains_free_trial()) { // only need sign up total for the initial payment $total = $woocommerce->cart->total; } else { // restore combined cart contents & total $woocommerce->cart->cart_contents = $original_cart->cart_contents; $woocommerce->cart->cart_contents_total = $original_cart->cart_contents_total; // restore combined cart sub-totals $woocommerce->cart->subtotal = $original_cart->subtotal; $woocommerce->cart->subtotal_ex_tax = $original_cart->subtotal_ex_tax; $total = $woocommerce->cart->total + $woocommerce->cart->recurring_total; } // Free trial with no discounts - recalculate all the initial payment amounts just for the sign-up fee } elseif (self::cart_contains_free_trial()) { // only pass sign up fee thru get_price filters self::$recalculation_type = 'sign_up_fee'; $woocommerce->cart->calculate_totals(); // Make the sign up fee only total persist $total = $woocommerce->cart->total; } } return $total; }
/** * Version 1.2 introduced a massive change to the order meta data schema. This function goes * through and upgrades the existing data on all orders to the new schema. * * The upgrade process is timeout safe as it keeps a record of the orders upgraded and only * deletes this record once all orders have been upgraded successfully. If operating on a huge * number of orders and the upgrade process times out, only the orders not already upgraded * will be upgraded in future requests that trigger this function. * * @since 1.2 */ private static function upgrade_database() { global $wpdb; set_transient('wc_subscriptions_is_upgrading', 'true', 60 * 2); // Get IDs only and use a direct DB query for efficiency $orders_to_upgrade = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'shop_order' AND post_parent = 0"); $upgraded_orders = get_option('wcs_1_2_upgraded_order_ids', array()); // Transition deprecated subscription status if we aren't in the middle of updating orders if (empty($upgraded_orders)) { $wpdb->query($wpdb->prepare("UPDATE {$wpdb->usermeta} SET meta_value = replace( meta_value, 's:9:\"suspended\"', 's:7:\"on-hold\"' ) WHERE meta_key LIKE %s", '%_' . WC_Subscriptions_Manager::$users_meta_key)); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->usermeta} SET meta_value = replace( meta_value, 's:6:\"failed\"', 's:9:\"cancelled\"' ) WHERE meta_key LIKE %s", '%_' . WC_Subscriptions_Manager::$users_meta_key)); } $orders_to_upgrade = array_diff($orders_to_upgrade, $upgraded_orders); // Upgrade all _sign_up_{field} order meta to new order data format foreach ($orders_to_upgrade as $order_id) { $order = new WC_Order($order_id); // Manually check if a product in an order is a subscription, we can't use WC_Subscriptions_Order::order_contains_subscription( $order ) because it relies on the new data structure $contains_subscription = false; foreach ($order->get_items() as $order_item) { if (WC_Subscriptions_Product::is_subscription($order_item['id'])) { $contains_subscription = true; break; } } if (!$contains_subscription) { continue; } $trial_lengths = WC_Subscriptions_Order::get_meta($order, '_order_subscription_trial_lengths', array()); $trial_length = array_pop($trial_lengths); $has_trial = !empty($trial_length) && $trial_length > 0 ? true : false; $sign_up_fee_total = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_total', 0); // Create recurring_* meta data from existing cart totals $cart_discount = $order->get_cart_discount(); update_post_meta($order_id, '_order_recurring_discount_cart', $cart_discount); $order_discount = $order->get_order_discount(); update_post_meta($order_id, '_order_recurring_discount_total', $order_discount); $order_shipping_tax = get_post_meta($order_id, '_order_shipping_tax', true); update_post_meta($order_id, '_order_recurring_shipping_tax_total', $order_shipping_tax); $order_tax = get_post_meta($order_id, '_order_tax', true); // $order->get_total_tax() includes shipping tax update_post_meta($order_id, '_order_recurring_tax_total', $order_tax); $order_total = $order->get_total(); update_post_meta($order_id, '_order_recurring_total', $order_total); // Set order totals to include sign up fee fields, if there was a sign up fee on the order and a trial period (other wise, the recurring totals are correct) if ($sign_up_fee_total > 0) { // Order totals need to be changed to be equal to sign up fee totals if ($has_trial) { $cart_discount = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0); $order_discount = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_total', 0); $order_tax = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0); $order_total = $sign_up_fee_total; } else { // No trial, sign up fees need to be added to order totals $cart_discount += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0); $order_discount += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_total', 0); $order_tax += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0); $order_total += $sign_up_fee_total; } update_post_meta($order_id, '_order_total', $order_total); update_post_meta($order_id, '_cart_discount', $cart_discount); update_post_meta($order_id, '_order_discount', $order_discount); update_post_meta($order_id, '_order_tax', $order_tax); } $order_taxes = $order->get_taxes(); // Set recurring taxes to order taxes update_post_meta($order_id, '_order_recurring_taxes', $order_taxes); $sign_up_fee_taxes = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_taxes', array()); // Update order taxes to include sign up fee taxes foreach ($sign_up_fee_taxes as $index => $sign_up_tax) { if ($has_trial && $sign_up_fee_total > 0) { // Order taxes need to be set to the same as the sign up fee taxes if (isset($sign_up_tax['cart_tax']) && $sign_up_tax['cart_tax'] > 0) { $order_taxes[$index]['cart_tax'] = $sign_up_tax['cart_tax']; } } elseif (!$has_trial && $sign_up_fee_total > 0) { // Sign up fee taxes need to be added to order taxes if (isset($sign_up_tax['cart_tax']) && $sign_up_tax['cart_tax'] > 0) { $order_taxes[$index]['cart_tax'] += $sign_up_tax['cart_tax']; } } } update_post_meta($order_id, '_order_taxes', $order_taxes); /* Upgrade each order item to use new Item Meta schema */ $order_subscription_periods = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_periods', array()); $order_subscription_intervals = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_intervals', array()); $order_subscription_lengths = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_lengths', array()); $order_subscription_trial_lengths = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_trial_lengths', array()); $order_items = $order->get_items(); foreach ($order_items as $index => $order_item) { $item_meta = new WC_Order_Item_Meta($order_item['item_meta']); $subscription_interval = isset($order_subscription_intervals[$order_item['id']]) ? $order_subscription_intervals[$order_item['id']] : 1; $subscription_length = isset($order_subscription_lengths[$order_item['id']]) ? $order_subscription_lengths[$order_item['id']] : 0; $subscription_trial_length = isset($order_subscription_trial_lengths[$order_item['id']]) ? $order_subscription_trial_lengths[$order_item['id']] : 0; $item_meta->add('_subscription_period', $order_subscription_periods[$order_item['id']]); $item_meta->add('_subscription_interval', $subscription_interval); $item_meta->add('_subscription_length', $subscription_length); $item_meta->add('_subscription_trial_length', $subscription_trial_length); $item_meta->add('_subscription_recurring_amount', $order_item['line_subtotal']); // WC_Subscriptions_Product::get_price() would return a price without filters applied $item_meta->add('_subscription_sign_up_fee', WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0)); // Set recurring amounts for the item $item_meta->add('_recurring_line_total', $order_item['line_total']); $item_meta->add('_recurring_line_tax', $order_item['line_tax']); $item_meta->add('_recurring_line_subtotal', $order_item['line_subtotal']); $item_meta->add('_recurring_line_subtotal_tax', $order_item['line_subtotal_tax']); if ($sign_up_fee_total > 0) { // Discounted price * Quantity $sign_up_fee_line_total = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0); $sign_up_fee_line_tax = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0); // Base price * Quantity $sign_up_fee_line_subtotal = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0) + WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0); $sign_up_fee_propotion = $sign_up_fee_line_total > 0 ? $sign_up_fee_line_subtotal / $sign_up_fee_line_total : 0; $sign_up_fee_line_subtotal_tax = WC_Subscriptions_Manager::get_amount_from_proportion(WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0), $sign_up_fee_propotion); if ($has_trial) { // Set line item totals equal to sign up fee totals $order_item['line_subtotal'] = $sign_up_fee_line_subtotal; $order_item['line_subtotal_tax'] = $sign_up_fee_line_subtotal_tax; $order_item['line_total'] = $sign_up_fee_line_total; $order_item['line_tax'] = $sign_up_fee_line_tax; } else { // No trial period, sign up fees need to be added to order totals $order_item['line_subtotal'] += $sign_up_fee_line_subtotal; $order_item['line_subtotal_tax'] += $sign_up_fee_line_subtotal_tax; $order_item['line_total'] += $sign_up_fee_line_total; $order_item['line_tax'] += $sign_up_fee_line_tax; } } $order_item['item_meta'] = $item_meta->meta; $order_items[$index] = $order_item; } // Save the new meta on the order items update_post_meta($order_id, '_order_items', $order_items); $upgraded_orders[] = $order_id; update_option('wcs_1_2_upgraded_order_ids', $upgraded_orders); } // Remove the lock on upgrading delete_transient('wc_subscriptions_is_upgrading'); }