This commit is contained in:
Prospress Inc
2017-04-11 09:25:24 +02:00
committed by Remco Tolsma
parent a7af434d12
commit 0691aa36da
127 changed files with 49016 additions and 4656 deletions

View File

@@ -15,6 +15,17 @@
} }
/* Subscriptions Admin Page */ /* Subscriptions Admin Page */
.post-type-shop_subscription .tablenav input,
.post-type-shop_subscription .tablenav select {
line-height: 32px;
height: 32px;
}
.post-type-shop_subscription .tablenav .select2-container {
width: 200px !important;
font-size: 14px;
vertical-align: middle;
margin: 1px 6px 4px 1px;
}
.post-type-shop_subscription .widefat .column-status, .post-type-shop_subscription .widefat .column-status,
.post-type-shop_subscription .widefat .column-order_id, .post-type-shop_subscription .widefat .column-order_id,
.post-type-shop_subscription .widefat .column-order_title { .post-type-shop_subscription .widefat .column-order_title {
@@ -201,29 +212,44 @@ a.close-subscriptions-search {
display: none !important; display: none !important;
} }
/* Variation Pricing Fields in WooCommerce 2.3 */ /* Variation Pricing Fields with WooCommerce 3.0+ */
.variable_subscription_pricing_2_3 .wc_input_subscription_price { .wc-metaboxes-wrapper .variable_subscription_trial label,
max-width: 24%; .wc-metaboxes-wrapper .variable_subscription_pricing label,
clear: left; .wc-metaboxes-wrapper .variable_subscription_sync label {
display: block;
}
.wc-metaboxes-wrapper .wc_input_subscription_trial_period,
.wc-metaboxes-wrapper .wc_input_subscription_period,
.wc-metaboxes-wrapper .wc_input_subscription_period_interval,
.wc-metaboxes-wrapper .wc_input_subscription_length,
.wc-metaboxes-wrapper .wc_input_subscription_payment_sync_day,
.wc-metaboxes-wrapper .wc_input_subscription_payment_sync_month {
padding: 4px 6px;
height: 31px;
}
/* Variation Pricing Fields in WooCommerce 2.3+ */
.variable_subscription_pricing_2_3 .wc_input_subscription_price,
.variable_subscription_pricing_2_3 .wc_input_subscription_period_interval {
max-width: 33%;
float: left; float: left;
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_period_interval { .variable_subscription_pricing_2_3 .wc_input_subscription_price {
max-width: 41%; clear: left;
float: left;
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_period { .variable_subscription_pricing_2_3 .wc_input_subscription_period {
max-width: 32%; max-width: 34%;
float: right; float: right;
} }
.variable_subscription_pricing_2_3.variable_subscription_trial p.form-row { .variable_subscription_pricing_2_3.variable_subscription_trial p.form-row {
margin-bottom: 0; margin-bottom: 0;
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_trial_period { .variable_subscription_pricing_2_3 .wc_input_subscription_trial_period,
max-width: 47%;
float: right;
}
.variable_subscription_pricing_2_3 .wc_input_subscription_trial_length { .variable_subscription_pricing_2_3 .wc_input_subscription_trial_length {
max-width: 48%; max-width: 50%;
}
.variable_subscription_pricing_2_3 .wc_input_subscription_trial_period {
float: right;
} }
.variable_subscription_pricing_2_3 .variable_subscription_length, .variable_subscription_pricing_2_3 .variable_subscription_length,
.variable_subscription_pricing_2_3 .variable_subscription_trial_sign_up { .variable_subscription_pricing_2_3 .variable_subscription_trial_sign_up {
@@ -270,6 +296,7 @@ a.close-subscriptions-search {
/* Add/Edit Subscription Screen */ /* Add/Edit Subscription Screen */
#woocommerce-subscription-data .handlediv, #woocommerce-subscription-data .handlediv,
#woocommerce-subscription-data h2.hndle,
#woocommerce-subscription-data h3.hndle { #woocommerce-subscription-data h3.hndle {
display: none; display: none;
} }

View File

@@ -1,5 +1,41 @@
*** WooCommerce Subscriptions Changelog *** *** WooCommerce Subscriptions Changelog ***
2017.04.07 - version 2.2.3
* Fix: WooCommerce 3.0: Improve backward compatibility of get_date( 'start' ) calls for subscriptions without a date created set by WooCommerce 3.0 (which happens when manually adding a new subscriptions). PR#2047
* Fix: WooCommerce 3.0: Prevent saving subscriptions with a 0 start date/date created by making sure we set a default date created value when manually adding a new subscription. PR#2048
* Fix: WooCommerce 3.0: Remove the correct 3.0 core function responsible for sending order emails to avoid sending both WooCommerce order emails and Subscriptions order emails. PR#2045
* Fix: WooCommerce 3.0: Make sure synchronised product meta data is set correctly on subscriptino line items by using WooCommerce 3.0 compatible hook. PR#2049
* Fix: Make sure filters are applied when getting customer subscriptions with REST API endpoints by using WC_API_Subscriptions::get_subscriptions() instead of a custom query to get customer subscriptions. PR#1806
* Fix: Display correct variable subscription product "From" prices and grouped subscription products prices to match tax settings. PR#2054
* Fix: Don't attempt to save shipping and billing fields which aren't set when submitting the Edit Subscription screen to avoid notices. PR#2053
* Fix: Don't use method return value in write context in REST API to avoid errors on certain versions of PHP. PR#2051
2017.04.04 - version 2.2.2
* Fix: WooCommerce 3.0: Fix using $this when in a static context on newer versions of PHP affecting Edit Subscription and Edit Order screens. PR#2043
* Fix: WooCommerce 3.0: Grant new product download permissions to all subscriptions because WooCommerce no longer does it for orders (and subscriptions are a custom order type). PR#2039
* Fix: WooCommerce 3.0: Display subscription related System Status fields in the System Status page on 3.0 which removed old hooks. PR#2039
* Fix: WooCommerce 3.0: Fix updating payment method and elsewhere via the Edit Subscription administration screen. PR#2037
* Fix: WooCommerce 3.0: Store correct prorated price for switches against the order line item so it can be used for future switches. PR#2032
2017.04.04 - version 2.2.1
* Fix: WooCommerce 3.0: Add subscription shipping line item tax the same way WooCommerce 3.0 does it. PR#2023
* Fix: WooCommerce 3.0: Fix division by zero error when manually paying for renewal with offsite payment methods like PayPal. PR#2036
* Fix: Send customer renewal invoice emails for manual renewals and when creating a pending renewal. PR#2035
* Fix: Replace use of undefined variable within WC_Subscription::get_failed_payment_count(). PR#2025
2017.04.03 - version 2.2.0
* WooCommerce 3.0 Compatibility Release
* Tweak: add new getters for subscription related product meta data to conform to WooCommerce 3.0's new CRUD system and also prepare for subscription related product meta data to be moved to schemes that can be applied to any product instead of only subscription product types
* Tweak: add new subscription data store to abstract data storage of subscription data from the subscription class, matching WooCommerce 3.0's new approach to data abstraction.
* Tweak: add new subscription getters and setters for all subscription data to conform to WooCommerce 3.0's new CRUD architecture
* Tweak: add new subscription date APIs to work with dates as date/time objects and with nomclementure that aligns with WooCommerce 3.0
* Tweak: deprecate subscription last payment date APIs because they actual returned the last order date, forcing us to also update the last order date whenever a payment was processed. Two new APIs can now be used: last order date and last order date paid.
* Tweak: improve variable subscription product sync performance by using cache. Also align it with the approach WooCommerce takes.
* Tweak: update Select2 to version 4 to work with the version used by WooCommerce 3.0.
* Tweak: show recurring fees in the recurring totals section on Cart & Checkout pages if 3rd party extensions, like Checkout Add Ons, are adding fees to recurring carts. (PR#2005)
* Fix: don't show Subscription Data metabox title div on WooCommerce > Edit Subscription administration screen with WordPress 4.4. (PR#1998)
* Fix: remove option to order the WooCommerce > Subscriptions administration screen by Status, because this option is not supported. (PR#1996)
2017.03.06 - version 2.1.4 2017.03.06 - version 2.1.4
* Tweak: Lengthen PayPal IPN lock to 5 days to prevent creating duplicate renewal orders when a fatal error occurs in processing the IPN. Prevoiusly the lock was set to 5 minutes, meaning on the 2nd IPN would be ignored. Given fatal errors will rarely be fixed within 5 minutes, Subscriptions will now ignore 5 days worth of IPN retries.(PR#1838 * Tweak: Lengthen PayPal IPN lock to 5 days to prevent creating duplicate renewal orders when a fatal error occurs in processing the IPN. Prevoiusly the lock was set to 5 minutes, meaning on the 2nd IPN would be ignored. Given fatal errors will rarely be fixed within 5 minutes, Subscriptions will now ignore 5 days worth of IPN retries.(PR#1838
* Tweak: Log exceptions caught while updating subscription statuses to make it easier to diagnose issues with 3rd party code and hosting environment that occur during status transitions. PR#1842 * Tweak: Log exceptions caught while updating subscription statuses to make it easier to diagnose issues with 3rd party code and hosting environment that occur during status transitions. PR#1842

View File

@@ -100,8 +100,7 @@ abstract class WCS_Hook_Deprecator {
* @since 2.0 * @since 2.0
*/ */
protected static function get_order( $subscription ) { protected static function get_order( $subscription ) {
$order = isset( $subscription->order->id ) ? $subscription->order : $subscription; return ( false == $subscription->get_parent_id() ) ? $subscription : $subscription->get_parent();
return $order;
} }
/** /**
@@ -113,7 +112,7 @@ abstract class WCS_Hook_Deprecator {
* @since 2.0 * @since 2.0
*/ */
protected static function get_order_id( $subscription ) { protected static function get_order_id( $subscription ) {
return isset( $subscription->order->id ) ? $subscription->order->id : $subscription->id; return ( false == $subscription->get_parent_id() ) ? $subscription->get_id() : $subscription->get_parent_id();
} }
/** /**

View File

@@ -28,6 +28,14 @@ abstract class WCS_Scheduler {
public function set_date_types_to_schedule() { public function set_date_types_to_schedule() {
$this->date_types_to_schedule = apply_filters( 'woocommerce_subscriptions_date_types_to_schedule', array_keys( wcs_get_subscription_date_types() ) ); $this->date_types_to_schedule = apply_filters( 'woocommerce_subscriptions_date_types_to_schedule', array_keys( wcs_get_subscription_date_types() ) );
if ( isset( $this->date_types_to_schedule['start'] ) ) {
unset( $this->date_types_to_schedule['start'] );
}
if ( isset( $this->date_types_to_schedule['last_payment'] ) ) {
unset( $this->date_types_to_schedule['last_payment'] );
}
} }
protected function get_date_types_to_schedule() { protected function get_date_types_to_schedule() {
@@ -38,7 +46,7 @@ abstract class WCS_Scheduler {
* When a subscription's date is updated, maybe schedule an event * When a subscription's date is updated, maybe schedule an event
* *
* @param object $subscription An instance of a WC_Subscription object * @param object $subscription An instance of a WC_Subscription object
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'end', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'end', 'end_of_prepaid_term' or a custom date type
* @param string $datetime A MySQL formatted date/time string in the GMT/UTC timezone. * @param string $datetime A MySQL formatted date/time string in the GMT/UTC timezone.
*/ */
abstract public function update_date( $subscription, $date_type, $datetime ); abstract public function update_date( $subscription, $date_type, $datetime );
@@ -47,7 +55,7 @@ abstract class WCS_Scheduler {
* When a subscription's date is deleted, clear it from the scheduler * When a subscription's date is deleted, clear it from the scheduler
* *
* @param object $subscription An instance of a WC_Subscription object * @param object $subscription An instance of a WC_Subscription object
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'end', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'end', 'end_of_prepaid_term' or a custom date type
*/ */
abstract public function delete_date( $subscription, $date_type ); abstract public function delete_date( $subscription, $date_type );

View File

@@ -41,6 +41,14 @@ class WC_Subscriptions_Admin {
*/ */
private static $found_related_orders = false; private static $found_related_orders = false;
/**
* Is meta boxes saved once?
*
* @var boolean
* @since 2.2.0
*/
private static $saved_product_meta = false;
/** /**
* Bootstraps the class and hooks required actions & filters. * Bootstraps the class and hooks required actions & filters.
* *
@@ -104,7 +112,7 @@ class WC_Subscriptions_Admin {
add_filter( 'set-screen-option', __CLASS__ . '::set_manage_subscriptions_screen_option', 10, 3 ); add_filter( 'set-screen-option', __CLASS__ . '::set_manage_subscriptions_screen_option', 10, 3 );
add_filter( 'woocommerce_debug_posting', __CLASS__ . '::add_system_status_items' ); add_filter( 'woocommerce_system_status_report', __CLASS__ . '::render_system_status_items' );
add_filter( 'woocommerce_payment_gateways_setting_columns', __CLASS__ . '::payment_gateways_rewewal_column' ); add_filter( 'woocommerce_payment_gateways_setting_columns', __CLASS__ . '::payment_gateways_rewewal_column' );
@@ -304,11 +312,6 @@ class WC_Subscriptions_Admin {
public static function variable_subscription_pricing_fields( $loop, $variation_data, $variation ) { public static function variable_subscription_pricing_fields( $loop, $variation_data, $variation ) {
global $thepostid; global $thepostid;
// Set month as the default billing period
if ( ! $subscription_period = get_post_meta( $variation->ID, '_subscription_period', true ) ) {
$subscription_period = 'month';
}
// When called via Ajax // When called via Ajax
if ( ! function_exists( 'woocommerce_wp_text_input' ) ) { if ( ! function_exists( 'woocommerce_wp_text_input' ) ) {
require_once( WC()->plugin_path() . '/admin/post-types/writepanels/writepanels-init.php' ); require_once( WC()->plugin_path() . '/admin/post-types/writepanels/writepanels-init.php' );
@@ -318,6 +321,13 @@ class WC_Subscriptions_Admin {
$thepostid = $variation->post_parent; $thepostid = $variation->post_parent;
} }
$variation_product = wc_get_product( $variation );
$billing_period = WC_Subscriptions_Product::get_period( $variation_product );
if ( empty( $billing_period ) ) {
$billing_period = 'month';
}
include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/admin/html-variation-price.php' ); include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/admin/html-variation-price.php' );
wp_nonce_field( 'wcs_subscription_variations', '_wcsnonce_save_variations', false ); wp_nonce_field( 'wcs_subscription_variations', '_wcsnonce_save_variations', false );
@@ -354,7 +364,7 @@ class WC_Subscriptions_Admin {
*/ */
public static function save_subscription_meta( $post_id ) { public static function save_subscription_meta( $post_id ) {
if ( empty( $_POST['_wcsnonce'] ) || ! wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_subscription_meta' ) || ! isset( $_POST['product-type'] ) || ! in_array( $_POST['product-type'], apply_filters( 'woocommerce_subscription_product_types', array( WC_Subscriptions::$name ) ) ) ) { if ( self::$saved_product_meta || ( empty( $_POST['_wcsnonce'] ) || ! wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_subscription_meta' ) || ! isset( $_POST['product-type'] ) || ! in_array( $_POST['product-type'], apply_filters( 'woocommerce_subscription_product_types', array( WC_Subscriptions::$name ) ) ) ) ) {
return; return;
} }
@@ -420,6 +430,8 @@ class WC_Subscriptions_Admin {
} }
} }
// To prevent running this function on multiple save_post triggered events per update. Similar to WC_Admin_Meta_Boxes:$saved_meta_boxes implementation.
self::$saved_product_meta = true;
} }
/** /**
@@ -431,7 +443,7 @@ class WC_Subscriptions_Admin {
*/ */
public static function save_variable_subscription_meta( $post_id ) { public static function save_variable_subscription_meta( $post_id ) {
if ( empty( $_POST['_wcsnonce'] ) || ! wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_subscription_meta' ) || ! isset( $_POST['product-type'] ) || ! in_array( $_POST['product-type'], apply_filters( 'woocommerce_subscription_variable_product_types', array( 'variable-subscription' ) ) ) ) { if ( self::$saved_product_meta || ( empty( $_POST['_wcsnonce'] ) || ! wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_subscription_meta' ) || ! isset( $_POST['product-type'] ) || ! in_array( $_POST['product-type'], apply_filters( 'woocommerce_subscription_variable_product_types', array( 'variable-subscription' ) ) ) ) ) {
return; return;
} }
@@ -441,6 +453,8 @@ class WC_Subscriptions_Admin {
update_post_meta( $post_id, '_subscription_one_time_shipping', stripslashes( isset( $_REQUEST['_subscription_one_time_shipping'] ) ? 'yes' : 'no' ) ); update_post_meta( $post_id, '_subscription_one_time_shipping', stripslashes( isset( $_REQUEST['_subscription_one_time_shipping'] ) ? 'yes' : 'no' ) );
// To prevent running this function on multiple save_post triggered events per update. Similar to WC_Admin_Meta_Boxes:$saved_meta_boxes implementation.
self::$saved_product_meta = true;
} }
/** /**
@@ -458,8 +472,8 @@ class WC_Subscriptions_Admin {
$price_changed = false; $price_changed = false;
$old_regular_price = $product->regular_price; $old_regular_price = $product->get_regular_price();
$old_sale_price = $product->sale_price; $old_sale_price = $product->get_sale_price();
if ( ! empty( $_REQUEST['change_regular_price'] ) ) { if ( ! empty( $_REQUEST['change_regular_price'] ) ) {
@@ -490,9 +504,8 @@ class WC_Subscriptions_Admin {
if ( isset( $new_price ) && $new_price != $old_regular_price ) { if ( isset( $new_price ) && $new_price != $old_regular_price ) {
$price_changed = true; $price_changed = true;
update_post_meta( $product->id, '_regular_price', $new_price ); wcs_set_objects_property( $product, 'regular_price', $new_price );
update_post_meta( $product->id, '_subscription_price', $new_price ); wcs_set_objects_property( $product, 'subscription_price', $new_price );
$product->regular_price = $new_price;
} }
} }
@@ -524,33 +537,31 @@ class WC_Subscriptions_Admin {
case 4 : case 4 :
if ( strstr( $sale_price, '%' ) ) { if ( strstr( $sale_price, '%' ) ) {
$percent = str_replace( '%', '', $sale_price ) / 100; $percent = str_replace( '%', '', $sale_price ) / 100;
$new_price = $product->regular_price - ( $product->regular_price * $percent ); $new_price = $product->get_regular_price() - ( $product->get_regular_price() * $percent );
} else { } else {
$new_price = $product->regular_price - $sale_price; $new_price = $product->get_regular_price() - $sale_price;
} }
break; break;
} }
if ( isset( $new_price ) && $new_price != $old_sale_price ) { if ( isset( $new_price ) && $new_price != $old_sale_price ) {
$price_changed = true; $price_changed = true;
update_post_meta( $product->id, '_sale_price', $new_price ); wcs_set_objects_property( $product, 'sale_price', $new_price );
$product->sale_price = $new_price;
} }
} }
if ( $price_changed ) { if ( $price_changed ) {
update_post_meta( $product->id, '_sale_price_dates_from', '' ); wcs_set_objects_property( $product, 'sale_price_dates_from', '' );
update_post_meta( $product->id, '_sale_price_dates_to', '' ); wcs_set_objects_property( $product, 'sale_price_dates_to', '' );
if ( $product->regular_price < $product->sale_price ) { if ( $product->get_regular_price() < $product->get_sale_price() ) {
$product->sale_price = ''; wcs_set_objects_property( $product, 'sale_price', '' );
update_post_meta( $product->id, '_sale_price', '' );
} }
if ( $product->sale_price ) { if ( $product->get_sale_price() ) {
update_post_meta( $product->id, '_price', $product->sale_price ); wcs_set_objects_property( $product, 'price', $product->get_sale_price() );
} else { } else {
update_post_meta( $product->id, '_price', $product->regular_price ); wcs_set_objects_property( $product, 'price', $product->get_regular_price() );
} }
} }
} }
@@ -628,9 +639,12 @@ class WC_Subscriptions_Admin {
} }
// Now that all the variation's meta is saved, sync the min variation price // Now that all the variation's meta is saved, sync the min variation price
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$variable_subscription = wc_get_product( $post_id ); $variable_subscription = wc_get_product( $post_id );
$variable_subscription->variable_product_sync(); $variable_subscription->variable_product_sync();
} else {
WC_Product_Variable::sync( $post_id );
}
} }
/** /**
@@ -652,10 +666,9 @@ class WC_Subscriptions_Admin {
public static function check_customer_is_set( $old_status, $new_status, $subscription ) { public static function check_customer_is_set( $old_status, $new_status, $subscription ) {
global $post; global $post;
if ( is_admin() && 'active' == $new_status && ! empty( $post ) && 'shop_subscription' === $post->post_type ) { if ( is_admin() && 'active' == $new_status && isset( $_POST['woocommerce_meta_nonce'] ) && wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) && isset( $_POST['customer_user'] ) && ! empty( $post ) && 'shop_subscription' === $post->post_type ) {
$customer_user = ( isset( $_POST['customer_user'] ) ) ? sanitize_text_field( $_POST['customer_user'] ) : ''; // csrf in core wp save post function $user = new WP_User( absint( $_POST['customer_user'] ) );
$user = new WP_User( $customer_user );
if ( 0 === $user->ID ) { if ( 0 === $user->ID ) {
throw new Exception( sprintf( __( 'Unable to change subscription status to "%s". Please assign a customer to the subscription to activate it.', 'woocommerce-subscriptions' ), $new_status ) ); throw new Exception( sprintf( __( 'Unable to change subscription status to "%s". Please assign a customer to the subscription to activate it.', 'woocommerce-subscriptions' ), $new_status ) );
@@ -1375,6 +1388,30 @@ class WC_Subscriptions_Admin {
return $list; return $list;
} }
/**
* Renders the Subscription information in the WC status page
*/
public static function render_system_status_items() {
$debug_data = array();
$is_wcs_debug = defined( 'WCS_DEBUG' ) ? WCS_DEBUG : false;
$debug_data['wcs_debug'] = array(
'name' => _x( 'WCS_DEBUG', 'label that indicates whether debugging is turned on for the plugin', 'woocommerce-subscriptions' ),
'note' => ( $is_wcs_debug ) ? __( 'Yes', 'woocommerce-subscriptions' ) : __( 'No', 'woocommerce-subscriptions' ),
'success' => $is_wcs_debug ? 0 : 1,
);
$debug_data['wcs_staging'] = array(
'name' => _x( 'Subscriptions Mode', 'Live or Staging, Label on WooCommerce -> System Status page', 'woocommerce-subscriptions' ),
'note' => '<strong>' . ( ( WC_Subscriptions::is_duplicate_site() ) ? _x( 'Staging', 'refers to staging site', 'woocommerce-subscriptions' ) : _x( 'Live', 'refers to live site', 'woocommerce-subscriptions' ) ) . '</strong>',
'success' => ( WC_Subscriptions::is_duplicate_site() ) ? 0 : 1,
);
$debug_data = apply_filters( 'wcs_system_status', $debug_data );
include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/admin/status.php' );
}
/** /**
* Adds Subscriptions specific details to the WooCommerce System Status report. * Adds Subscriptions specific details to the WooCommerce System Status report.
* *
@@ -1382,6 +1419,7 @@ class WC_Subscriptions_Admin {
* @return array * @return array
*/ */
public static function add_system_status_items( $debug_data ) { public static function add_system_status_items( $debug_data ) {
_deprecated_function( __METHOD__, '2.2.2', __CLASS__ . '::render_system_status_items()' );
$is_wcs_debug = defined( 'WCS_DEBUG' ) ? WCS_DEBUG : false; $is_wcs_debug = defined( 'WCS_DEBUG' ) ? WCS_DEBUG : false;

View File

@@ -51,7 +51,7 @@ class WCS_Admin_Meta_Boxes {
* Add WC Meta boxes * Add WC Meta boxes
*/ */
public function add_meta_boxes() { public function add_meta_boxes() {
global $current_screen, $post_ID; global $post_ID;
add_meta_box( 'woocommerce-subscription-data', _x( 'Subscription Data', 'meta box title', 'woocommerce-subscriptions' ), 'WCS_Meta_Box_Subscription_Data::output', 'shop_subscription', 'normal', 'high' ); add_meta_box( 'woocommerce-subscription-data', _x( 'Subscription Data', 'meta box title', 'woocommerce-subscriptions' ), 'WCS_Meta_Box_Subscription_Data::output', 'shop_subscription', 'normal', 'high' );
@@ -110,7 +110,7 @@ class WCS_Admin_Meta_Boxes {
'i18n_trial_end_next_notice' => __( 'Please enter a date before the next payment.', 'woocommerce-subscriptions' ), 'i18n_trial_end_next_notice' => __( 'Please enter a date before the next payment.', 'woocommerce-subscriptions' ),
'i18n_end_date_notice' => __( 'Please enter a date after the next payment.', 'woocommerce-subscriptions' ), 'i18n_end_date_notice' => __( 'Please enter a date after the next payment.', 'woocommerce-subscriptions' ),
'process_renewal_action_warning' => __( "Are you sure you want to process a renewal?\n\nThis will charge the customer and email them the renewal order (if emails are enabled).", 'woocommerce-subscriptions' ), 'process_renewal_action_warning' => __( "Are you sure you want to process a renewal?\n\nThis will charge the customer and email them the renewal order (if emails are enabled).", 'woocommerce-subscriptions' ),
'payment_method' => wcs_get_subscription( $post )->payment_method, 'payment_method' => wcs_get_subscription( $post )->get_payment_method(),
'search_customers_nonce' => wp_create_nonce( 'search-customers' ), 'search_customers_nonce' => wp_create_nonce( 'search-customers' ),
) ) ); ) ) );
} else if ( 'shop_order' == $screen->id ) { } else if ( 'shop_order' == $screen->id ) {
@@ -156,7 +156,7 @@ class WCS_Admin_Meta_Boxes {
* @since 2.0 * @since 2.0
*/ */
public static function process_renewal_action_request( $subscription ) { public static function process_renewal_action_request( $subscription ) {
do_action( 'woocommerce_scheduled_subscription_payment', $subscription->id ); do_action( 'woocommerce_scheduled_subscription_payment', $subscription->get_id() );
$subscription->add_order_note( __( 'Process renewal order action requested by admin.', 'woocommerce-subscriptions' ), false, true ); $subscription->add_order_note( __( 'Process renewal order action requested by admin.', 'woocommerce-subscriptions' ), false, true );
} }
@@ -173,7 +173,7 @@ class WCS_Admin_Meta_Boxes {
$renewal_order = wcs_create_renewal_order( $subscription ); $renewal_order = wcs_create_renewal_order( $subscription );
if ( ! $subscription->is_manual() ) { if ( ! $subscription->is_manual() ) {
$renewal_order->set_payment_method( $subscription->payment_gateway ); $renewal_order->set_payment_method( wc_get_payment_gateway_by_order( $subscription ) ); // We need to pass the payment gateway instance to be compatible with WC < 3.0, only WC 3.0+ supports passing the string name
} }
$subscription->add_order_note( __( 'Create pending renewal order requested by admin action.', 'woocommerce-subscriptions' ), false, true ); $subscription->add_order_note( __( 'Create pending renewal order requested by admin action.', 'woocommerce-subscriptions' ), false, true );
@@ -207,7 +207,7 @@ class WCS_Admin_Meta_Boxes {
// init payment gateways // init payment gateways
WC()->payment_gateways(); WC()->payment_gateways();
do_action( 'woocommerce_scheduled_subscription_payment_' . $order->payment_method, $order->get_total(), $order ); do_action( 'woocommerce_scheduled_subscription_payment_' . wcs_get_objects_property( $order, 'payment_method' ), $order->get_total(), $order );
} }
} }
@@ -229,7 +229,8 @@ class WCS_Admin_Meta_Boxes {
$can_be_retried = false; $can_be_retried = false;
if ( wcs_order_contains_renewal( $order ) && $order->needs_payment() && ! empty( $order->payment_method ) ) { if ( wcs_order_contains_renewal( $order ) && $order->needs_payment() && '' != wcs_get_objects_property( $order, 'payment_method' ) ) {
$supports_date_changes = false;
$order_payment_gateway = wc_get_payment_gateway_by_order( $order ); $order_payment_gateway = wc_get_payment_gateway_by_order( $order );
$order_payment_gateway_supports = ( isset( $order_payment_gateway->id ) ) ? has_action( 'woocommerce_scheduled_subscription_payment_' . $order_payment_gateway->id ) : false; $order_payment_gateway_supports = ( isset( $order_payment_gateway->id ) ) ? has_action( 'woocommerce_scheduled_subscription_payment_' . $order_payment_gateway->id ) : false;

View File

@@ -159,7 +159,12 @@ class WCS_Admin_Post_Types {
// Let's create a temporary table, drop the previous one, because otherwise this query is hella slow // Let's create a temporary table, drop the previous one, because otherwise this query is hella slow
$wpdb->query( "DROP TEMPORARY TABLE IF EXISTS {$table_name}" ); $wpdb->query( "DROP TEMPORARY TABLE IF EXISTS {$table_name}" );
$wpdb->query( "CREATE TEMPORARY TABLE {$table_name} (id INT, INDEX USING BTREE (id), last_payment DATETIME) AS SELECT pm.meta_value as id, MAX( p.post_date_gmt ) as last_payment FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '_subscription_renewal' GROUP BY pm.meta_value" ); $wpdb->query(
"CREATE TEMPORARY TABLE {$table_name} (id INT, INDEX USING BTREE (id), last_payment DATETIME) AS
SELECT pm.meta_value as id, MAX( p.post_date_gmt ) as last_payment FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '_subscription_renewal'
GROUP BY pm.meta_value" );
// Magic ends here // Magic ends here
$pieces['join'] .= "LEFT JOIN {$table_name} lp $pieces['join'] .= "LEFT JOIN {$table_name} lp
@@ -189,9 +194,15 @@ class WCS_Admin_Post_Types {
$product_string = wc_get_product( $product_id )->get_formatted_name(); $product_string = wc_get_product( $product_id )->get_formatted_name();
} }
?> WCS_Select2::render( array(
<input type="hidden" class="wc-product-search" name="_wcs_product" data-placeholder="<?php esc_attr_e( 'Search for a product&hellip;', 'woocommerce-subscriptions' ); ?>" data-action="woocommerce_json_search_products_and_variations" data-selected="<?php echo esc_attr( strip_tags( $product_string ) ); ?>" value="<?php echo esc_attr( $product_id ); ?>" data-allow_clear="true" /> 'class' => 'wc-product-search',
<?php 'name' => '_wcs_product',
'placeholder' => esc_attr__( 'Search for a product&hellip;', 'woocommerce-subscriptions' ),
'action' => 'woocommerce_json_search_products_and_variations',
'selected' => strip_tags( $product_string ),
'value' => $product_id,
'allow_clear' => 'true',
) );
} }
/** /**
@@ -401,7 +412,7 @@ class WCS_Admin_Post_Types {
'start_date' => __( 'Start Date', 'woocommerce-subscriptions' ), 'start_date' => __( 'Start Date', 'woocommerce-subscriptions' ),
'trial_end_date' => __( 'Trial End', 'woocommerce-subscriptions' ), 'trial_end_date' => __( 'Trial End', 'woocommerce-subscriptions' ),
'next_payment_date' => __( 'Next Payment', 'woocommerce-subscriptions' ), 'next_payment_date' => __( 'Next Payment', 'woocommerce-subscriptions' ),
'last_payment_date' => __( 'Last Payment', 'woocommerce-subscriptions' ), 'last_payment_date' => __( 'Last Order Date', 'woocommerce-subscriptions' ), // Keep deprecated 'last_payment_date' key for backward compatibility
'end_date' => __( 'End Date', 'woocommerce-subscriptions' ), 'end_date' => __( 'End Date', 'woocommerce-subscriptions' ),
'orders' => _x( 'Orders', 'number of orders linked to a subscription', 'woocommerce-subscriptions' ), 'orders' => _x( 'Orders', 'number of orders linked to a subscription', 'woocommerce-subscriptions' ),
); );
@@ -416,7 +427,7 @@ class WCS_Admin_Post_Types {
public function render_shop_subscription_columns( $column ) { public function render_shop_subscription_columns( $column ) {
global $post, $the_subscription, $wp_list_table; global $post, $the_subscription, $wp_list_table;
if ( empty( $the_subscription ) || $the_subscription->id != $post->ID ) { if ( empty( $the_subscription ) || $the_subscription->get_id() != $post->ID ) {
$the_subscription = wcs_get_subscription( $post->ID ); $the_subscription = wcs_get_subscription( $post->ID );
} }
@@ -433,7 +444,7 @@ class WCS_Admin_Post_Types {
$action_url = add_query_arg( $action_url = add_query_arg(
array( array(
'post' => $the_subscription->id, 'post' => $the_subscription->get_id(),
'_wpnonce' => wp_create_nonce( 'bulk-posts' ), '_wpnonce' => wp_create_nonce( 'bulk-posts' ),
) )
); );
@@ -502,14 +513,14 @@ class WCS_Admin_Post_Types {
$customer_tip .= _x( 'Billing:', 'meaning billing address', 'woocommerce-subscriptions' ) . ' ' . esc_html( $address ); $customer_tip .= _x( 'Billing:', 'meaning billing address', 'woocommerce-subscriptions' ) . ' ' . esc_html( $address );
} }
if ( $the_subscription->billing_email ) { if ( $the_subscription->get_billing_email() ) {
// translators: placeholder is customer's billing email // translators: placeholder is customer's billing email
$customer_tip .= '<br/><br/>' . sprintf( __( 'Email: %s', 'woocommerce-subscriptions' ), esc_attr( $the_subscription->billing_email ) ); $customer_tip .= '<br/><br/>' . sprintf( __( 'Email: %s', 'woocommerce-subscriptions' ), esc_attr( $the_subscription->get_billing_email() ) );
} }
if ( $the_subscription->billing_phone ) { if ( $the_subscription->get_billing_phone() ) {
// translators: placeholder is customer's billing phone number // translators: placeholder is customer's billing phone number
$customer_tip .= '<br/><br/>' . sprintf( __( 'Tel: %s', 'woocommerce-subscriptions' ), esc_html( $the_subscription->billing_phone ) ); $customer_tip .= '<br/><br/>' . sprintf( __( 'Tel: %s', 'woocommerce-subscriptions' ), esc_html( $the_subscription->get_billing_phone() ) );
} }
if ( ! empty( $customer_tip ) ) { if ( ! empty( $customer_tip ) ) {
@@ -523,8 +534,8 @@ class WCS_Admin_Post_Types {
$username = '<a href="user-edit.php?user_id=' . absint( $user_info->ID ) . '">'; $username = '<a href="user-edit.php?user_id=' . absint( $user_info->ID ) . '">';
if ( $the_subscription->billing_first_name || $the_subscription->billing_last_name ) { if ( $the_subscription->get_billing_first_name() || $the_subscription->get_billing_last_name() ) {
$username .= esc_html( ucfirst( $the_subscription->billing_first_name ) . ' ' . ucfirst( $the_subscription->billing_last_name ) ); $username .= esc_html( ucfirst( $the_subscription->get_billing_first_name() ) . ' ' . ucfirst( $the_subscription->get_billing_last_name() ) );
} elseif ( $user_info->first_name || $user_info->last_name ) { } elseif ( $user_info->first_name || $user_info->last_name ) {
$username .= esc_html( ucfirst( $user_info->first_name ) . ' ' . ucfirst( $user_info->last_name ) ); $username .= esc_html( ucfirst( $user_info->first_name ) . ' ' . ucfirst( $user_info->last_name ) );
} else { } else {
@@ -533,8 +544,8 @@ class WCS_Admin_Post_Types {
$username .= '</a>'; $username .= '</a>';
} elseif ( $the_subscription->billing_first_name || $the_subscription->billing_last_name ) { } elseif ( $the_subscription->get_billing_first_name() || $the_subscription->get_billing_last_name() ) {
$username = trim( $the_subscription->billing_first_name . ' ' . $the_subscription->billing_last_name ); $username = trim( $the_subscription->get_billing_first_name() . ' ' . $the_subscription->get_billing_last_name() );
} }
// translators: $1: is opening link, $2: is subscription order number, $3: is closing link tag, $4: is user's name // translators: $1: is opening link, $2: is subscription order number, $3: is closing link tag, $4: is user's name
$column_content = sprintf( _x( '%1$s#%2$s%3$s for %4$s', 'Subscription title on admin table. (e.g.: #211 for John Doe)', 'woocommerce-subscriptions' ), '<a href="' . esc_url( admin_url( 'post.php?post=' . absint( $post->ID ) . '&action=edit' ) ) . '">', '<strong>' . esc_attr( $the_subscription->get_order_number() ) . '</strong>', '</a>', $username ); $column_content = sprintf( _x( '%1$s#%2$s%3$s for %4$s', 'Subscription title on admin table. (e.g.: #211 for John Doe)', 'woocommerce-subscriptions' ), '<a href="' . esc_url( admin_url( 'post.php?post=' . absint( $post->ID ) . '&action=edit' ) ) . '">', '<strong>' . esc_attr( $the_subscription->get_order_number() ) . '</strong>', '</a>', $username );
@@ -570,7 +581,7 @@ class WCS_Admin_Post_Types {
$item_name = sprintf( '%s &times; %s', absint( $item_quantity ), $item_name ); $item_name = sprintf( '%s &times; %s', absint( $item_quantity ), $item_name );
} }
if ( $_product ) { if ( $_product ) {
$item_name = sprintf( '<a href="%s">%s</a>', get_edit_post_link( $_product->id ), $item_name ); $item_name = sprintf( '<a href="%s">%s</a>', get_edit_post_link( ( $_product->is_type( 'variation' ) ) ? wcs_get_objects_property( $_product, 'parent_id' ) : $_product->get_id() ), $item_name );
} }
$column_content .= '<div class="order-item">'; $column_content .= '<div class="order-item">';
@@ -606,7 +617,7 @@ class WCS_Admin_Post_Types {
$item_name = esc_html( $item_name ); $item_name = esc_html( $item_name );
if ( $_product ) { if ( $_product ) {
$item_name = sprintf( '<a href="%s">%s</a>', get_edit_post_link( $_product->id ), $item_name ); $item_name = sprintf( '<a href="%s">%s</a>', get_edit_post_link( $_product->get_id() ), $item_name );
} }
echo wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) ); echo wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) );
@@ -636,10 +647,13 @@ class WCS_Admin_Post_Types {
case 'next_payment_date': case 'next_payment_date':
case 'last_payment_date': case 'last_payment_date':
case 'end_date': case 'end_date':
if ( 0 == $the_subscription->get_time( $column, 'gmt' ) ) { $date_type_map = array( 'start_date' => 'date_created', 'last_payment_date' => 'last_order_date_created' );
$date_type = array_key_exists( $column, $date_type_map ) ? $date_type_map[ $column ] : $column;
if ( 0 == $the_subscription->get_time( $date_type, 'gmt' ) ) {
$column_content .= '-'; $column_content .= '-';
} else { } else {
$column_content .= sprintf( '<time class="%s" title="%s">%s</time>', esc_attr( $column ), esc_attr( date( __( 'Y/m/d g:i:s A', 'woocommerce-subscriptions' ) , $the_subscription->get_time( $column, 'site' ) ) ), esc_html( $the_subscription->get_date_to_display( $column ) ) ); $column_content .= sprintf( '<time class="%s" title="%s">%s</time>', esc_attr( $column ), esc_attr( date( __( 'Y/m/d g:i:s A', 'woocommerce-subscriptions' ) , $the_subscription->get_time( $date_type, 'site' ) ) ), esc_html( $the_subscription->get_date_to_display( $date_type ) ) );
if ( 'next_payment_date' == $column && $the_subscription->payment_method_supports( 'gateway_scheduled_payments' ) && ! $the_subscription->is_manual() && $the_subscription->has_status( 'active' ) ) { if ( 'next_payment_date' == $column && $the_subscription->payment_method_supports( 'gateway_scheduled_payments' ) && ! $the_subscription->is_manual() && $the_subscription->has_status( 'active' ) ) {
$column_content .= '<div class="woocommerce-help-tip" data-tip="' . esc_attr__( 'This date should be treated as an estimate only. The payment gateway for this subscription controls when payments are processed.', 'woocommerce-subscriptions' ) . '"></div>'; $column_content .= '<div class="woocommerce-help-tip" data-tip="' . esc_attr__( 'This date should be treated as an estimate only. The payment gateway for this subscription controls when payments are processed.', 'woocommerce-subscriptions' ) . '"></div>';
@@ -667,7 +681,6 @@ class WCS_Admin_Post_Types {
public function shop_subscription_sortable_columns( $columns ) { public function shop_subscription_sortable_columns( $columns ) {
$sortable_columns = array( $sortable_columns = array(
'status' => 'post_status',
'order_title' => 'ID', 'order_title' => 'ID',
'recurring_total' => 'order_total', 'recurring_total' => 'order_total',
'start_date' => 'date', 'start_date' => 'date',
@@ -941,11 +954,9 @@ class WCS_Admin_Post_Types {
* @return string the link string * @return string the link string
*/ */
public function get_related_orders_link( $the_subscription ) { public function get_related_orders_link( $the_subscription ) {
$order_id = isset( $the_subscription->order->id ) ? $the_subscription->order->id : 0;
return sprintf( return sprintf(
'<a href="%s">%s</a>', '<a href="%s">%s</a>',
admin_url( 'edit.php?post_status=all&post_type=shop_order&_subscription_related_orders=' . absint( $the_subscription->id ) ), admin_url( 'edit.php?post_status=all&post_type=shop_order&_subscription_related_orders=' . absint( $the_subscription->get_id() ) ),
count( $the_subscription->get_related_orders() ) count( $the_subscription->get_related_orders() )
); );
} }

View File

@@ -26,7 +26,7 @@ class WCS_Meta_Box_Related_Orders {
if ( wcs_is_subscription( $post->ID ) ) { if ( wcs_is_subscription( $post->ID ) ) {
$subscription = wcs_get_subscription( $post->ID ); $subscription = wcs_get_subscription( $post->ID );
$order = ( false == $subscription->order ) ? $subscription : $subscription->order; $order = ( false == $subscription->get_parent_id() ) ? $subscription : $subscription->get_parent();
} else { } else {
$order = wc_get_order( $post->ID ); $order = wc_get_order( $post->ID );
} }
@@ -58,7 +58,7 @@ class WCS_Meta_Box_Related_Orders {
// First, display all the subscriptions // First, display all the subscriptions
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
$subscription->relationship = __( 'Subscription', 'woocommerce-subscriptions' ); wcs_set_objects_property( $subscription, 'relationship', __( 'Subscription', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[] = $subscription; $orders[] = $subscription;
} }
@@ -79,7 +79,7 @@ class WCS_Meta_Box_Related_Orders {
foreach ( $resubscribed_subscriptions as $subscription ) { foreach ( $resubscribed_subscriptions as $subscription ) {
$subscription = wcs_get_subscription( $subscription ); $subscription = wcs_get_subscription( $subscription );
$subscription->relationship = _x( 'Resubscribed Subscription', 'relation to order', 'woocommerce-subscriptions' ); wcs_set_objects_property( $subscription, 'relationship', _x( 'Resubscribed Subscription', 'relation to order', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[] = $subscription; $orders[] = $subscription;
} }
} else if ( wcs_order_contains_subscription( $post->ID, array( 'resubscribe' ) ) ) { } else if ( wcs_order_contains_subscription( $post->ID, array( 'resubscribe' ) ) ) {
@@ -87,16 +87,17 @@ class WCS_Meta_Box_Related_Orders {
} }
foreach ( $initial_subscriptions as $subscription ) { foreach ( $initial_subscriptions as $subscription ) {
$subscription->relationship = _x( 'Initial Subscription', 'relation to order', 'woocommerce-subscriptions' ); wcs_set_objects_property( $subscription, 'relationship', _x( 'Initial Subscription', 'relation to order', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[] = $subscription; $orders[] = $subscription;
} }
// Now, if we're on a single subscription or renewal order's page, display the parent orders // Now, if we're on a single subscription or renewal order's page, display the parent orders
if ( 1 == count( $subscriptions ) ) { if ( 1 == count( $subscriptions ) ) {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
if ( false !== $subscription->order ) { if ( $subscription->get_parent_id() ) {
$subscription->order->relationship = _x( 'Parent Order', 'relation to order', 'woocommerce-subscriptions' ); $order = $subscription->get_parent();
$orders[] = $subscription->order; wcs_set_objects_property( $order, 'relationship', _x( 'Parent Order', 'relation to order', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[] = $order;
} }
} }
} }
@@ -105,7 +106,7 @@ class WCS_Meta_Box_Related_Orders {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
foreach ( $subscription->get_related_orders( 'all', 'renewal' ) as $order ) { foreach ( $subscription->get_related_orders( 'all', 'renewal' ) as $order ) {
$order->relationship = _x( 'Renewal Order', 'relation to order', 'woocommerce-subscriptions' ); wcs_set_objects_property( $order, 'relationship', _x( 'Renewal Order', 'relation to order', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[] = $order; $orders[] = $order;
} }
} }
@@ -113,7 +114,8 @@ class WCS_Meta_Box_Related_Orders {
$orders = apply_filters( 'woocommerce_subscriptions_admin_related_orders_to_display', $orders, $subscriptions, $post ); $orders = apply_filters( 'woocommerce_subscriptions_admin_related_orders_to_display', $orders, $subscriptions, $post );
foreach ( $orders as $order ) { foreach ( $orders as $order ) {
if ( $order->id == $post->ID ) {
if ( wcs_get_objects_property( $order, 'id' ) == $post->ID ) {
continue; continue;
} }
include( 'views/html-related-orders-row.php' ); include( 'views/html-related-orders-row.php' );

View File

@@ -25,7 +25,7 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
public static function output( $post ) { public static function output( $post ) {
global $the_subscription; global $the_subscription;
if ( ! is_object( $the_subscription ) || $the_subscription->id !== $post->ID ) { if ( ! is_object( $the_subscription ) || $the_subscription->get_id() !== $post->ID ) {
$the_subscription = wc_get_order( $post->ID ); $the_subscription = wc_get_order( $post->ID );
} }
@@ -39,7 +39,7 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
#post-body-content, #titlediv, #major-publishing-actions, #minor-publishing-actions, #visibility, #submitdiv { display:none } #post-body-content, #titlediv, #major-publishing-actions, #minor-publishing-actions, #visibility, #submitdiv { display:none }
</style> </style>
<div class="panel-wrap woocommerce"> <div class="panel-wrap woocommerce">
<input name="post_title" type="hidden" value="<?php echo empty( $post->post_title ) ? esc_attr( get_post_type_object( $subscription->post->post_type )->labels->singular_name ) : esc_attr( $post->post_title ); ?>" /> <input name="post_title" type="hidden" value="<?php echo empty( $post->post_title ) ? esc_attr( get_post_type_object( $post->post_type )->labels->singular_name ) : esc_attr( $post->post_title ); ?>" />
<input name="post_status" type="hidden" value="<?php echo esc_attr( 'wc-' . $subscription->get_status() ); ?>" /> <input name="post_status" type="hidden" value="<?php echo esc_attr( 'wc-' . $subscription->get_status() ); ?>" />
<div id="order_data" class="panel"> <div id="order_data" class="panel">
@@ -52,11 +52,11 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
<p class="form-field form-field-wide wc-customer-user"> <p class="form-field form-field-wide wc-customer-user">
<label for="customer_user"><?php esc_html_e( 'Customer:', 'woocommerce-subscriptions' ) ?> <?php <label for="customer_user"><?php esc_html_e( 'Customer:', 'woocommerce-subscriptions' ) ?> <?php
if ( ! empty( $subscription->customer_user ) ) { if ( $subscription->get_user_id() ) {
$args = array( $args = array(
'post_status' => 'all', 'post_status' => 'all',
'post_type' => 'shop_subscription', 'post_type' => 'shop_subscription',
'_customer_user' => absint( $subscription->customer_user ), '_customer_user' => absint( $subscription->get_user_id() ),
); );
printf( '<a href="%s">%s &rarr;</a>', printf( '<a href="%s">%s &rarr;</a>',
esc_url( add_query_arg( $args, admin_url( 'edit.php' ) ) ), esc_url( add_query_arg( $args, admin_url( 'edit.php' ) ) ),
@@ -67,13 +67,20 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
<?php <?php
$user_string = ''; $user_string = '';
$user_id = ''; $user_id = '';
if ( ! empty( $subscription->customer_user ) && ( false !== get_userdata( $subscription->customer_user ) ) ) { if ( $subscription->get_user_id() && ( false !== get_userdata( $subscription->get_user_id() ) ) ) {
$user_id = absint( $subscription->customer_user ); $user_id = absint( $subscription->get_user_id() );
$user = get_user_by( 'id', $user_id ); $user = get_user_by( 'id', $user_id );
$user_string = esc_html( $user->display_name ) . ' (#' . absint( $user->ID ) . ' &ndash; ' . esc_html( $user->user_email ); $user_string = esc_html( $user->display_name ) . ' (#' . absint( $user->ID ) . ' &ndash; ' . esc_html( $user->user_email ) . ')';
} }
WCS_Select2::render( array(
'class' => 'wc-customer-search',
'name' => 'customer_user',
'id' => 'customer_user',
'placeholder' => esc_attr__( 'Search for a customer&hellip;', 'woocommerce-subscriptions' ),
'selected' => $user_string,
'value' => $user_id,
) );
?> ?>
<input type="hidden" class="wc-customer-search" id="customer_user" name="customer_user" data-placeholder="<?php esc_attr_e( 'Search for a customer&hellip;', 'woocommerce-subscriptions' ); ?>" data-selected="<?php echo esc_attr( $user_string ); ?>" value="<?php echo esc_attr( $user_id ); ?>" />
</p> </p>
<p class="form-field form-field-wide"> <p class="form-field form-field-wide">
@@ -112,18 +119,18 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
continue; continue;
} }
$field_name = 'billing_' . $key; $function_name = 'get_billing_' . $key;
if ( $subscription->$field_name ) { if ( $subscription->$function_name() ) {
echo '<p><strong>' . esc_html( $field['label'] ) . ':</strong> ' . wp_kses_post( make_clickable( esc_html( $subscription->$field_name ) ) ) . '</p>'; echo '<p><strong>' . esc_html( $field['label'] ) . ':</strong> ' . wp_kses_post( make_clickable( esc_html( $subscription->$function_name() ) ) ) . '</p>';
} }
} }
echo '<p' . ( ! empty( $subscription->payment_method ) ? ' class="' . esc_attr( $subscription->payment_method ) . '"' : '' ) . '><strong>' . esc_html__( 'Payment Method', 'woocommerce-subscriptions' ) . ':</strong>' . wp_kses_post( nl2br( $subscription->get_payment_method_to_display() ) ); echo '<p' . ( ( '' != $subscription->get_payment_method() ) ? ' class="' . esc_attr( $subscription->get_payment_method() ) . '"' : '' ) . '><strong>' . esc_html__( 'Payment Method', 'woocommerce-subscriptions' ) . ':</strong>' . wp_kses_post( nl2br( $subscription->get_payment_method_to_display() ) );
// Display help tip // Display help tip
if ( ! empty( $subscription->payment_method ) && ! $subscription->is_manual() ) { if ( '' != $subscription->get_payment_method() && ! $subscription->is_manual() ) {
echo wcs_help_tip( sprintf( _x( 'Gateway ID: [%s]', 'The gateway ID displayed on the Edit Subscriptions screen when editing payment method.', 'woocommerce-subscriptions' ), $subscription->payment_gateway->id ) ); echo wcs_help_tip( sprintf( _x( 'Gateway ID: [%s]', 'The gateway ID displayed on the Edit Subscriptions screen when editing payment method.', 'woocommerce-subscriptions' ), $subscription->get_payment_method() ) );
} }
echo '</p>'; echo '</p>';
@@ -236,7 +243,7 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
/** /**
* Save meta box data * Save meta box data
*/ */
public static function save( $post_id, $post ) { public static function save( $post_id, $post = '' ) {
global $wpdb; global $wpdb;
if ( 'shop_subscription' != $post->post_type || empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) ) { if ( 'shop_subscription' != $post->post_type || empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) ) {
@@ -253,12 +260,22 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
if ( self::$billing_fields ) { if ( self::$billing_fields ) {
foreach ( self::$billing_fields as $key => $field ) { foreach ( self::$billing_fields as $key => $field ) {
if ( ! isset( $_POST[ '_billing_' . $key ] ) ) {
continue;
}
update_post_meta( $post_id, '_billing_' . $key, wc_clean( $_POST[ '_billing_' . $key ] ) ); update_post_meta( $post_id, '_billing_' . $key, wc_clean( $_POST[ '_billing_' . $key ] ) );
} }
} }
if ( self::$shipping_fields ) { if ( self::$shipping_fields ) {
foreach ( self::$shipping_fields as $key => $field ) { foreach ( self::$shipping_fields as $key => $field ) {
if ( ! isset( $_POST[ '_shipping_' . $key ] ) ) {
continue;
}
update_post_meta( $post_id, '_shipping_' . $key, wc_clean( $_POST[ '_shipping_' . $key ] ) ); update_post_meta( $post_id, '_shipping_' . $key, wc_clean( $_POST[ '_shipping_' . $key ] ) );
} }
} }

View File

@@ -49,19 +49,20 @@ class WCS_Meta_Box_Schedule {
$dates = array(); $dates = array();
foreach ( wcs_get_subscription_date_types() as $date_key => $date_label ) { foreach ( wcs_get_subscription_date_types() as $date_type => $date_label ) {
$date_key = wcs_normalise_date_type_key( $date_type );
if ( 'last_payment' == $date_key ) { if ( 'last_order_date_created' == $date_key ) {
continue; continue;
} }
$utc_timestamp_key = $date_key . '_timestamp_utc'; $utc_timestamp_key = $date_type . '_timestamp_utc';
// A subscription needs a start date, even if it wasn't set // A subscription needs a created date, even if it wasn't set or is empty
if ( isset( $_POST[ $utc_timestamp_key ] ) ) { if ( 'date_created' === $date_key && empty( $_POST[ $utc_timestamp_key ] ) ) {
$datetime = $_POST[ $utc_timestamp_key ];
} elseif ( 'start' === $date_key ) {
$datetime = current_time( 'timestamp', true ); $datetime = current_time( 'timestamp', true );
} elseif ( isset( $_POST[ $utc_timestamp_key ] ) ) {
$datetime = $_POST[ $utc_timestamp_key ];
} else { // No date to set } else { // No date to set
continue; continue;
} }
@@ -76,6 +77,8 @@ class WCS_Meta_Box_Schedule {
} catch ( Exception $e ) { } catch ( Exception $e ) {
wcs_add_admin_notice( $e->getMessage(), 'error' ); wcs_add_admin_notice( $e->getMessage(), 'error' );
} }
$subscription->save();
} }
} }
} }

View File

@@ -8,28 +8,32 @@
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly exit; // Exit if accessed directly
} }
// WC 3.0+ compatibility
$order_post = wcs_get_objects_property( $order, 'post' );
?> ?>
<tr> <tr>
<td> <td>
<a href="<?php echo esc_url( get_edit_post_link( $order->id ) ); ?>"> <a href="<?php echo esc_url( get_edit_post_link( wcs_get_objects_property( $order, 'id' ) ) ); ?>">
<?php echo sprintf( esc_html_x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), esc_html( $order->get_order_number() ) ); ?> <?php echo sprintf( esc_html_x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), esc_html( $order->get_order_number() ) ); ?>
</a> </a>
</td> </td>
<td> <td>
<?php echo esc_html( $order->relationship ); ?> <?php echo esc_html( wcs_get_objects_property( $order, 'relationship' ) ); ?>
</td> </td>
<td> <td>
<?php <?php
$timestamp_gmt = wcs_date_to_time( $order->post->post_date_gmt ); $timestamp_gmt = wcs_get_objects_property( $order, 'date_created' )->getTimestamp();
if ( $timestamp_gmt > 0 ) { if ( $timestamp_gmt > 0 ) {
// translators: php date format // translators: php date format
$t_time = get_the_time( _x( 'Y/m/d g:i:s A', 'post date', 'woocommerce-subscriptions' ), $order->post ); $t_time = get_the_time( _x( 'Y/m/d g:i:s A', 'post date', 'woocommerce-subscriptions' ), $order_post );
$date_to_display = wcs_get_human_time_diff( $timestamp_gmt ); $date_to_display = wcs_get_human_time_diff( $timestamp_gmt );
} else { } else {
$t_time = $date_to_display = __( 'Unpublished', 'woocommerce-subscriptions' ); $t_time = $date_to_display = __( 'Unpublished', 'woocommerce-subscriptions' );
} ?> } ?>
<abbr title="<?php echo esc_attr( $t_time ); ?>"> <abbr title="<?php echo esc_attr( $t_time ); ?>">
<?php echo esc_html( apply_filters( 'post_date_column_time', $date_to_display, $order->post ) ); ?> <?php echo esc_html( apply_filters( 'post_date_column_time', $date_to_display, $order_post ) ); ?>
</abbr> </abbr>
</td> </td>
<td> <td>

View File

@@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
'id' => '_billing_interval', 'id' => '_billing_interval',
'class' => 'billing_interval', 'class' => 'billing_interval',
'label' => __( 'Recurring:', 'woocommerce-subscriptions' ), 'label' => __( 'Recurring:', 'woocommerce-subscriptions' ),
'value' => empty( $the_subscription->billing_interval ) ? 1 : $the_subscription->billing_interval, 'value' => $the_subscription->get_billing_interval(),
'options' => wcs_get_subscription_period_interval_strings(), 'options' => wcs_get_subscription_period_interval_strings(),
) )
); );
@@ -30,7 +30,7 @@ if ( ! defined( 'ABSPATH' ) ) {
'id' => '_billing_period', 'id' => '_billing_period',
'class' => 'billing_period', 'class' => 'billing_period',
'label' => __( 'Billing Period', 'woocommerce-subscriptions' ), 'label' => __( 'Billing Period', 'woocommerce-subscriptions' ),
'value' => empty( $the_subscription->billing_period ) ? 'month' : $the_subscription->billing_period, 'value' => $the_subscription->get_billing_period(),
'options' => wcs_get_subscription_period_strings(), 'options' => wcs_get_subscription_period_strings(),
) )
); );
@@ -39,21 +39,22 @@ if ( ! defined( 'ABSPATH' ) ) {
</div> </div>
<?php else : ?> <?php else : ?>
<strong><?php esc_html_e( 'Recurring:', 'woocommerce-subscriptions' ); ?></strong> <strong><?php esc_html_e( 'Recurring:', 'woocommerce-subscriptions' ); ?></strong>
<?php printf( '%s %s', esc_html( wcs_get_subscription_period_interval_strings( $the_subscription->billing_interval ) ), esc_html( wcs_get_subscription_period_strings( 1, $the_subscription->billing_period ) ) ); ?> <?php printf( '%s %s', esc_html( wcs_get_subscription_period_interval_strings( $the_subscription->get_billing_interval() ) ), esc_html( wcs_get_subscription_period_strings( 1, $the_subscription->get_billing_period() ) ) ); ?>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php foreach ( wcs_get_subscription_date_types() as $date_key => $date_label ) : ?> <?php foreach ( wcs_get_subscription_date_types() as $date_key => $date_label ) : ?>
<?php $internal_date_key = wcs_normalise_date_type_key( $date_key ) ?>
<?php if ( false === wcs_display_date_type( $date_key, $the_subscription ) ) : ?> <?php if ( false === wcs_display_date_type( $date_key, $the_subscription ) ) : ?>
<?php continue; ?> <?php continue; ?>
<?php endif;?> <?php endif;?>
<div id="subscription-<?php echo esc_attr( $date_key ); ?>-date" class="date-fields"> <div id="subscription-<?php echo esc_attr( $date_key ); ?>-date" class="date-fields">
<strong><?php echo esc_html( $date_label ); ?>:</strong> <strong><?php echo esc_html( $date_label ); ?>:</strong>
<input type="hidden" name="<?php echo esc_attr( $date_key ); ?>_timestamp_utc" id="<?php echo esc_attr( $date_key ); ?>_timestamp_utc" value="<?php echo esc_attr( $the_subscription->get_time( $date_key, 'gmt' ) ); ?>"/> <input type="hidden" name="<?php echo esc_attr( $date_key ); ?>_timestamp_utc" id="<?php echo esc_attr( $date_key ); ?>_timestamp_utc" value="<?php echo esc_attr( $the_subscription->get_time( $internal_date_key, 'gmt' ) ); ?>"/>
<?php if ( $the_subscription->can_date_be_updated( $date_key ) ) : ?> <?php if ( $the_subscription->can_date_be_updated( $internal_date_key ) ) : ?>
<?php echo wp_kses( wcs_date_input( $the_subscription->get_time( $date_key, 'site' ), array( 'name_attr' => $date_key ) ), array( 'input' => array( 'type' => array(), 'class' => array(), 'placeholder' => array(), 'name' => array(), 'id' => array(), 'maxlength' => array(), 'size' => array(), 'value' => array(), 'patten' => array() ), 'div' => array( 'class' => array() ), 'span' => array(), 'br' => array() ) ); ?> <?php echo wp_kses( wcs_date_input( $the_subscription->get_time( $internal_date_key, 'site' ), array( 'name_attr' => $date_key ) ), array( 'input' => array( 'type' => array(), 'class' => array(), 'placeholder' => array(), 'name' => array(), 'id' => array(), 'maxlength' => array(), 'size' => array(), 'value' => array(), 'patten' => array() ), 'div' => array( 'class' => array() ), 'span' => array(), 'br' => array() ) ); ?>
<?php else : ?> <?php else : ?>
<?php echo esc_html( $the_subscription->get_date_to_display( $date_key ) ); ?> <?php echo esc_html( $the_subscription->get_date_to_display( $internal_date_key ) ); ?>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>

View File

@@ -46,10 +46,10 @@ class WCS_Report_Cache_Manager {
'woocommerce_subscription_status_active' => array( 'woocommerce_subscription_status_active' => array(
1 => 'WC_Report_Upcoming_Recurring_Revenue', 1 => 'WC_Report_Upcoming_Recurring_Revenue',
), ),
'woocommerce_order_add_product' => array( 'woocommerce_new_order_item' => array(
3 => 'WC_Report_Subscription_By_Product', 3 => 'WC_Report_Subscription_By_Product',
), ),
'woocommerce_order_edit_product' => array( 'woocommerce_update_order_item' => array(
3 => 'WC_Report_Subscription_By_Product', 3 => 'WC_Report_Subscription_By_Product',
), ),
); );
@@ -77,6 +77,20 @@ class WCS_Report_Cache_Manager {
*/ */
public function __construct() { public function __construct() {
// Use the old hooks
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$hooks = array(
'woocommerce_order_add_product' => 'woocommerce_new_order_item',
'woocommerce_order_edit_product' => 'woocommerce_update_order_item',
);
foreach ( $hooks as $old_hook => $new_hook ) {
$this->update_events_and_classes[ $old_hook ] = $this->update_events_and_classes[ $new_hook ];
unset( $this->update_events_and_classes[ $new_hook ] ); // New hooks aren't called, so no need to attach to them
}
}
add_action( $this->cron_hook, array( $this, 'update_cache' ), 10, 1 ); add_action( $this->cron_hook, array( $this, 'update_cache' ), 10, 1 );
foreach ( $this->update_events_and_classes as $event_hook => $report_classes ) { foreach ( $this->update_events_and_classes as $event_hook => $report_classes ) {

View File

@@ -17,7 +17,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @package WooCommerce_Subscriptions/API * @package WooCommerce_Subscriptions/API
* @extends WC_REST_Order_Notes_Controller * @extends WC_REST_Order_Notes_Controller
*/ */
class WC_REST_Subscription_Notes_Controller extends WC_REST_Order_Notes_Controller { class WC_REST_Subscription_Notes_Controller extends WC_REST_Order_Notes_V1_Controller {
/** /**
* Route base. * Route base.

View File

@@ -18,7 +18,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @package WooCommerce_Subscriptions/API * @package WooCommerce_Subscriptions/API
* @extends WC_REST_Orders_Controller * @extends WC_REST_Orders_Controller
*/ */
class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller { class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
/** /**
* Route base. * Route base.
@@ -41,8 +41,6 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
add_filter( 'woocommerce_rest_prepare_shop_subscription', array( $this, 'filter_get_subscription_response' ), 10, 3 ); add_filter( 'woocommerce_rest_prepare_shop_subscription', array( $this, 'filter_get_subscription_response' ), 10, 3 );
add_filter( 'woocommerce_rest_shop_subscription_query', array( $this, 'query_args' ), 10, 2 ); add_filter( 'woocommerce_rest_shop_subscription_query', array( $this, 'query_args' ), 10, 2 );
add_filter( 'woocommerce_rest_pre_insert_shop_subscription', array( $this, 'prepare_subscription_args' ), 10, 2 );
} }
/** /**
@@ -83,12 +81,15 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
if ( ! empty( $post->post_type ) && ! empty( $post->ID ) && 'shop_subscription' == $post->post_type ) { if ( ! empty( $post->post_type ) && ! empty( $post->ID ) && 'shop_subscription' == $post->post_type ) {
$subscription = wcs_get_subscription( $post->ID ); $subscription = wcs_get_subscription( $post->ID );
$response->data['billing_period'] = $subscription->billing_period; $response->data['billing_period'] = $subscription->get_billing_period();
$response->data['billing_interval'] = $subscription->billing_interval; $response->data['billing_interval'] = $subscription->get_billing_interval();
$response->data['start_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'start' ) );
$response->data['trial_end_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'trial_end' ) ); foreach ( array( 'start', 'trial_end', 'next_payment', 'end' ) as $date_type ) {
$response->data['next_payment_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'next_payment' ) ); $date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type;
$response->data['end_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'end_date' ) ); $date = $subscription->get_date( $date_type_key );
$response->data[ $date_type . '_date'] = ( ! empty( $date ) ) ? wc_rest_prepare_date_response( $date ) : '';
}
} }
return $response; return $response;
@@ -103,17 +104,50 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
* @param WP_REST_Request $request * @param WP_REST_Request $request
*/ */
protected function create_order( $request ) { protected function create_order( $request ) {
$post_id = parent::create_order( $request ); try {
if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] && false === get_user_by( 'id', $request['customer_id'] ) ) {
if ( is_wp_error( $post_id ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id',__( 'Customer ID is invalid.', 'woocommerce-subscriptions' ), 400 );
return $post_id;
} }
// If the start date is not set in the request, set its default to now
if ( ! isset( $request['start_date'] ) ) {
$request['start_date'] = gmdate( 'Y-m-d H:i:s' );
}
// prepare all subscription data from the request
$subscription = $this->prepare_item_for_database( $request );
$subscription->set_created_via( 'rest-api' );
$subscription->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$subscription->calculate_totals();
// allow the order total to be overriden (i.e. if you want to have a subscription with no order items but a flat $10.00 recurring payment )
if ( isset( $request['order_total'] ) ) { if ( isset( $request['order_total'] ) ) {
update_post_meta( $post_id, '_order_total', wc_format_decimal( $request['order_total'], get_option( 'woocommerce_price_num_decimals' ) ) ); $subscription->set_total( wc_format_decimal( $request['order_total'], get_option( 'woocommerce_price_num_decimals' ) ) );
} }
return $post_id; $subscription->save();
// Store the post meta on the subscription after it's saved, this is to avoid compat. issue with the filters in WC_Subscriptions::set_payment_method_meta() expecting the $subscription to have an ID (therefore it needs to be called after the WC_Subscription has been saved)
$payment_data = ( ! empty( $request['payment_details'] ) ) ? $request['payment_details'] : array();
if ( empty( $payment_data['payment_details']['method_id'] ) && ! empty( $request['payment_method'] ) ) {
$payment_data['method_id'] = $request['payment_method'];
}
$this->update_payment_method( $subscription, $payment_data );
// Handle set paid.
if ( true === $request['set_paid'] ) {
$subscription->payment_complete( $request['transaction_id'] );
} else {
$subscription->save(); // $subscription->payment_complete() calls $subscription->update_status() which saves the subscription, so we only need to save it if not calling that
}
return $subscription->get_id();
} catch ( WC_Data_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() );
} catch ( WC_REST_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
} }
/** /**
@@ -124,28 +158,47 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
* @param WP_REST_Request $request * @param WP_REST_Request $request
* @param WP_POST $post * @param WP_POST $post
*/ */
protected function update_order( $request, $post ) { protected function update_order( $request ) {
try { try {
$post_id = parent::update_order( $request, $post ); $subscription = $this->prepare_item_for_database( $request );
if ( is_wp_error( $post_id ) ) { // If any line items have changed, recalculate subscription totals.
return $post_id; if ( isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) {
$subscription->calculate_totals();
} }
$subscription = wcs_get_subscription( $post_id ); // allow the order total to be overriden (i.e. if you want to have a subscription with no order items but a flat $10.00 recurring payment )
$this->update_schedule( $subscription, $request ); if ( isset( $request['order_total'] ) ) {
$subscription->set_total( wc_format_decimal( $request['order_total'], get_option( 'woocommerce_price_num_decimals' ) ) );
if ( empty( $request['payment_details']['method_id'] ) && ! empty( $request['payment_method'] ) ) {
$request['payment_details']['method_id'] = $request['payment_method'];
} }
$this->update_payment_method( $subscription, $request['payment_details'], true ); $subscription->save();
return $post_id; // Update the post meta on the subscription after it's saved, this is to avoid compat. issue with the filters in WC_Subscriptions::set_payment_method_meta() expecting the $subscription to have an ID (therefore it needs to be called after the WC_Subscription has been saved)
$payment_data = ( ! empty( $request['payment_details'] ) ) ? $request['payment_details'] : array();
$existing_payment_method_id = $subscription->get_payment_method();
if ( empty( $payment_data['method_id'] ) && isset( $request['payment_method'] ) ) {
$payment_data['method_id'] = $request['payment_method'];
} elseif ( ! empty( $existing_payment_method_id ) ) {
$payment_data['method_id'] = $existing_payment_method_id;
}
if ( isset( $payment_data['method_id'] ) ) {
$this->update_payment_method( $subscription, $payment_data, true );
}
// Handle set paid.
if ( $subscription->needs_payment() && true === $request['set_paid'] ) {
$subscription->payment_complete();
}
return $subscription->get_id();
} catch ( WC_Data_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() );
} catch ( WC_REST_Exception $e ) { } catch ( WC_REST_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
} catch ( Exception $e ) {
return new WP_Error( 'woocommerce_rest_cannot_update_subscription', $e->getMessage(), array( 'status' => 400 ) );
} }
} }
@@ -218,64 +271,6 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
return $subscription_statuses; return $subscription_statuses;
} }
/**
* Create WC_Subscription object.
*
* @since 2.1
* @param array $args subscription args.
* @return WC_Subscription
*/
protected function create_base_order( $args ) {
$subscription = wcs_create_subscription( $args );
if ( is_wp_error( $subscription ) ) {
throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_subscription', sprintf( __( 'Cannot create subscription: %s.', 'woocommerce-subscriptions' ), implode( ', ', $subscription->get_error_messages() ) ), 400 );
}
$this->update_schedule( $subscription, $args );
if ( empty( $args['payment_details']['method_id'] ) && ! empty( $args['payment_method'] ) ) {
$args['payment_details']['method_id'] = $args['payment_method'];
}
$this->update_payment_method( $subscription, $args['payment_details'] );
return $subscription;
}
/**
* Update or set the subscription schedule with the request data
*
* @since 2.1
* @param WC_Subscription $subscription
* @param array $data
*/
public function update_schedule( $subscription, $data ) {
if ( isset( $data['billing_interval'] ) ) {
update_post_meta( $subscription->id, '_billing_interval', absint( $data['billing_interval'] ) );
}
if ( ! empty( $data['billing_period'] ) ) {
update_post_meta( $subscription->id, '_billing_period', $data['billing_period'] );
}
try {
$dates_to_update = array();
foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) {
if ( isset( $data[ $date_type . '_date' ] ) ) {
$dates_to_update[ $date_type ] = $data[ $date_type . '_date' ];
}
}
if ( ! empty( $dates_to_update ) ) {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
}
/** /**
* Validate and update payment method on a subscription * Validate and update payment method on a subscription
* *
@@ -285,9 +280,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
* @param bool $updating * @param bool $updating
*/ */
public function update_payment_method( $subscription, $data, $updating = false ) { public function update_payment_method( $subscription, $data, $updating = false ) {
$payment_gateways = WC()->payment_gateways->get_available_payment_gateways(); $payment_method = ( ! empty( $data['method_id'] ) ) ? $data['method_id'] : '';
$payment_method = ( ! empty( $data['method_id'] ) ) ? $data['method_id'] : 'manual';
$payment_gateway = ( ! empty( $payment_gateways[ $payment_method ] ) ) ? $payment_gateways[ $payment_method ] : '';
try { try {
if ( $updating && ! array_key_exists( $payment_method, WCS_Change_Payment_Method_Admin::get_valid_payment_methods( $subscription ) ) ) { if ( $updating && ! array_key_exists( $payment_method, WCS_Change_Payment_Method_Admin::get_valid_payment_methods( $subscription ) ) ) {
@@ -296,8 +289,8 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
$payment_method_meta = apply_filters( 'woocommerce_subscription_payment_meta', array(), $subscription ); $payment_method_meta = apply_filters( 'woocommerce_subscription_payment_meta', array(), $subscription );
if ( ! empty( $payment_gateway ) && isset( $payment_method_meta[ $payment_gateway->id ] ) ) { if ( isset( $payment_method_meta[ $payment_method ] ) ) {
$payment_method_meta = $payment_method_meta[ $payment_gateway->id ]; $payment_method_meta = $payment_method_meta[ $payment_method ];
if ( ! empty( $payment_method_meta ) ) { if ( ! empty( $payment_method_meta ) ) {
@@ -316,18 +309,92 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
} }
} }
if ( empty( $subscription->payment_gateway ) ) { $subscription->set_payment_method( $payment_method, $payment_method_meta );
$subscription->payment_gateway = $payment_gateway;
}
$subscription->set_payment_method( $payment_gateway, $payment_method_meta );
} catch ( Exception $e ) { } catch ( Exception $e ) {
$subscription->set_payment_method();
$subscription->save();
// translators: 1$: gateway id, 2$: error message // translators: 1$: gateway id, 2$: error message
throw new WC_REST_Exception( 'woocommerce_rest_invalid_payment_data', sprintf( __( 'Subscription payment method could not be set to %1$s with error message: %2$s', 'woocommerce-subscriptions' ), $payment_method, $e->getMessage() ), 400 ); throw new WC_REST_Exception( 'woocommerce_rest_invalid_payment_data', sprintf( __( 'Subscription payment method could not be set to %1$s with error message: %2$s', 'woocommerce-subscriptions' ), $payment_method, $e->getMessage() ), 400 );
} }
} }
/**
* Prepare a single subscription for create.
*
* @param WP_REST_Request $request Request object.
* @return WP_Error|WC_Subscription $data Object.
*/
protected function prepare_item_for_database( $request ) {
$id = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
$subscription = new WC_Subscription( $id );
$schema = $this->get_item_schema();
$data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) );
$dates_to_update = array();
// Handle all writable props
foreach ( $data_keys as $key ) {
$value = $request[ $key ];
if ( ! is_null( $value ) ) {
switch ( $key ) {
case 'billing' :
case 'shipping' :
$this->update_address( $subscription, $value, $key );
break;
case 'line_items' :
case 'shipping_lines' :
case 'fee_lines' :
case 'coupon_lines' :
if ( is_array( $value ) ) {
foreach ( $value as $item ) {
if ( is_array( $item ) ) {
if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) {
$subscription->remove_item( $item['id'] );
} else {
$this->set_item( $subscription, $key, $item );
}
}
}
}
break;
case 'start_date' :
case 'trial_end_date' :
case 'next_payment_date' :
case 'end_date' :
$date_type_key = ( 'start_date' === $key ) ? 'date_created' : $key;
$dates_to_update[ $date_type_key ] = $value;
break;
default :
if ( is_callable( array( $subscription, "set_{$key}" ) ) ) {
$subscription->{"set_{$key}"}( $value );
}
break;
}
}
}
try {
if ( ! empty( $dates_to_update ) ) {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
/**
* Filter the data for the insert.
*
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
* prepared for the response.
*
* @param WC_Subscription $subscription The subscription object.
* @param WP_REST_Request $request Request object.
*/
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $subscription, $request );
}
/** /**
* Adds additional item schema information for subscription requests * Adds additional item schema information for subscription requests
* *
@@ -350,7 +417,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
), ),
'payment_details' => array( 'payment_details' => array(
'description' => __( 'Subscription payment details.', 'woocommerce-subscriptions' ), 'description' => __( 'Subscription payment details.', 'woocommerce-subscriptions' ),
'type' => 'array', 'type' => 'object',
'context' => array( 'edit' ), 'context' => array( 'edit' ),
'properties' => array( 'properties' => array(
'method_id' => array( 'method_id' => array(
@@ -365,7 +432,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
'type' => 'date-time', 'type' => 'date-time',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
), ),
'trial_date' => array( 'trial_end_date' => array(
'description' => __( "The subscription's trial date", 'woocommerce-subscriptions' ), 'description' => __( "The subscription's trial date", 'woocommerce-subscriptions' ),
'type' => 'date-time', 'type' => 'date-time',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
@@ -386,21 +453,32 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
return $schema; return $schema;
} }
/**
* Deprecated functions
*/
/** /**
* Prepare subscription data for create. * Prepare subscription data for create.
* *
* Now that we override WC_REST_Orders_V1_Controller::prepare_item_for_database() function,
* we no longer need to prepare these args
*
* @since 2.1 * @since 2.1
* @param stdClass $data * @param stdClass $data
* @param WP_REST_Request $request Request object. * @param WP_REST_Request $request Request object.
* @return stdClass * @return stdClass
* @deprecated 2.2
*/ */
public function prepare_subscription_args( $data, $request ) { public function prepare_subscription_args( $data, $request ) {
wcs_deprecated_function( __METHOD__, '2.2' );
$data->billing_interval = $request['billing_interval']; $data->billing_interval = $request['billing_interval'];
$data->billing_period = $request['billing_period']; $data->billing_period = $request['billing_period'];
foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) { foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) {
if ( ! empty( $request[ $date_type . '_date' ] ) ) { if ( ! empty( $request[ $date_type . '_date' ] ) ) {
$data->{$date_type . '_date'} = $request[ $date_type . '_date' ]; $date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type . '_date';
$data->{$date_type_key} = $request[ $date_type . '_date' ];
} }
} }
@@ -409,4 +487,42 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
return $data; return $data;
} }
/**
* Update or set the subscription schedule with the request data.
*
*
* @since 2.1
* @param WC_Subscription $subscription
* @param array $data
* @deprecated 2.2
*/
public function update_schedule( $subscription, $data ) {
wcs_deprecated_function( __METHOD__, '2.2', 'WC_REST_Subscriptions_Controller::prepare_item_for_database() now prepares the billing interval/period and dates' );
if ( isset( $data['billing_interval'] ) ) {
$subscription->set_billing_interval( absint( $data['billing_interval'] ) );
}
if ( ! empty( $data['billing_period'] ) ) {
$subscription->set_billing_period( $data['billing_period'] );
}
try {
$dates_to_update = array();
foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) {
if ( isset( $data[ $date_type . '_date' ] ) ) {
$date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type;
$dates_to_update[ $date_type_key ] = $data[ $date_type . '_date' ];
}
}
if ( ! empty( $dates_to_update ) ) {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
}
} }

View File

@@ -42,8 +42,6 @@ class WC_API_Subscriptions_Customers extends WC_API_Customers {
* @return array * @return array
*/ */
public function register_routes( $routes ) { public function register_routes( $routes ) {
$routes = parent::register_routes( $routes );
# GET /customers/<id>/subscriptions # GET /customers/<id>/subscriptions
$routes[ $this->base . '/(?P<id>\d+)/subscriptions' ] = array( $routes[ $this->base . '/(?P<id>\d+)/subscriptions' ] = array(
array( array( $this, 'get_customer_subscriptions' ), WC_API_SERVER::READABLE ), array( array( $this, 'get_customer_subscriptions' ), WC_API_SERVER::READABLE ),
@@ -59,7 +57,7 @@ class WC_API_Subscriptions_Customers extends WC_API_Customers {
* @param $id int * @param $id int
* @param $fields array * @param $fields array
*/ */
public function get_customer_subscriptions( $id, $fields = null ) { public function get_customer_subscriptions( $id, $fields = null, $filter = array() ) {
global $wpdb; global $wpdb;
// check the customer id given is a valid customer in the store. We're able to leech off WC-API for this. // check the customer id given is a valid customer in the store. We're able to leech off WC-API for this.
@@ -68,22 +66,14 @@ class WC_API_Subscriptions_Customers extends WC_API_Customers {
if ( is_wp_error( $id ) ) { if ( is_wp_error( $id ) ) {
return $id; return $id;
} }
$subscription_ids = array();
$filter['customer_id'] = $id;
$subscriptions = WC()->api->WC_API_Subscriptions->get_subscriptions( $fields, $filter, null, -1 );
$subscription_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID, post_date_gmt if ( ! empty( $subscriptions['subscriptions'] ) && is_array( $subscriptions['subscriptions'] ) ) {
FROM {$wpdb->posts} AS posts foreach ( $subscriptions['subscriptions'] as $subscription ) {
LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id $subscription_ids[] = $subscription['id'];
WHERE meta.meta_key = '_customer_user' }
AND meta.meta_value = '%d'
AND posts.post_type = 'shop_subscription'
AND posts.post_status IN ( '" . implode( "','", array_keys( wcs_get_subscription_statuses() ) ) . "' )
GROUP BY posts.ID
ORDER BY posts.post_date_gmt DESC
", $id ) );
$subscriptions = array();
foreach ( $subscription_ids as $subscription_id ) {
$subscriptions[] = WC()->api->WC_API_Subscriptions->get_subscription( $subscription_id, $fields );
} }
return array( 'customer_subscriptions' => apply_filters( 'wc_subscriptions_api_customer_subscriptions', $subscriptions, $id, $fields, $subscription_ids, $this->server ) ); return array( 'customer_subscriptions' => apply_filters( 'wc_subscriptions_api_customer_subscriptions', $subscriptions, $id, $fields, $subscription_ids, $this->server ) );

View File

@@ -196,17 +196,18 @@ class WC_API_Subscriptions extends WC_API_Orders {
// allow order total to be manually set, especially for those cases where there's no line items added to the subscription // allow order total to be manually set, especially for those cases where there's no line items added to the subscription
if ( isset( $data['order_total'] ) ) { if ( isset( $data['order_total'] ) ) {
update_post_meta( $subscription->id, '_order_total', wc_format_decimal( $data['order_total'], get_option( 'woocommerce_price_num_decimals' ) ) ); $subscription->set_total( wc_format_decimal( $data['order_total'], get_option( 'woocommerce_price_num_decimals' ) ) );
} }
if ( isset( $data['payment_details'] ) && is_array( $data['payment_details'] ) ) { if ( isset( $data['payment_details'] ) && is_array( $data['payment_details'] ) ) {
$this->update_payment_method( $subscription, $data['payment_details'], false ); $this->update_payment_method( $subscription, $data['payment_details'], false );
} }
do_action( 'wcs_api_subscription_created', $subscription->id, $this ); $subscription->save();
return array( 'creating_subscription' => $this->get_subscription( $subscription->id ) ); do_action( 'wcs_api_subscription_created', $subscription->get_id(), $this );
return array( 'creating_subscription' => $this->get_subscription( $subscription->get_id() ) );
} catch ( WC_API_Exception $e ) { } catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
@@ -215,7 +216,7 @@ class WC_API_Subscriptions extends WC_API_Orders {
// show the subscription in response if it was still created but errored. // show the subscription in response if it was still created but errored.
if ( ! empty( $subscription ) && ! is_wp_error( $subscription ) ) { if ( ! empty( $subscription ) && ! is_wp_error( $subscription ) ) {
$response['creating_subscription'] = $this->get_subscription( $subscription->id ); $response['creating_subscription'] = $this->get_subscription( $subscription->get_id() );
} }
return $response; return $response;
@@ -251,7 +252,7 @@ class WC_API_Subscriptions extends WC_API_Orders {
} }
if ( ! empty( $data['order_id'] ) ) { if ( ! empty( $data['order_id'] ) ) {
wp_update_post( array( 'ID' => $subscription_id, 'post_parent' => $data['order_id'] ) ); $subscription->set_parent_id( $data['order_id'] );
} }
// set $data['order'] = $data['subscription'] so that edit_order can read in the request // set $data['order'] = $data['subscription'] so that edit_order can read in the request
@@ -269,11 +270,13 @@ class WC_API_Subscriptions extends WC_API_Orders {
$this->update_schedule( $subscription, $data ); $this->update_schedule( $subscription, $data );
$subscription->save();
do_action( 'wcs_api_subscription_updated', $subscription_id, $data, $this ); do_action( 'wcs_api_subscription_updated', $subscription_id, $data, $this );
return $this->get_subscription( $subscription_id ); return $this->get_subscription( $subscription_id );
} catch ( WC_API_Excpetion $e ) { } catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
} catch ( Exception $e ) { } catch ( Exception $e ) {
@@ -327,8 +330,8 @@ class WC_API_Subscriptions extends WC_API_Orders {
} }
} }
if ( empty( $subscription->payment_gateway ) ) { if ( '' == $subscription->get_payment_method() ) {
$subscription->payment_gateway = $payment_gateway; $subscription->set_payment_method( $payment_gateway );
} }
$subscription->set_payment_method( $payment_gateway, $payment_method_meta ); $subscription->set_payment_method( $payment_gateway, $payment_method_meta );
@@ -377,8 +380,7 @@ class WC_API_Subscriptions extends WC_API_Orders {
throw new WC_API_Exception( 'wcs_api_invalid_subscription_meta', __( 'Invalid subscription billing interval given. Must be an integer greater than 0.', 'woocommerce-subscriptions' ), 400 ); throw new WC_API_Exception( 'wcs_api_invalid_subscription_meta', __( 'Invalid subscription billing interval given. Must be an integer greater than 0.', 'woocommerce-subscriptions' ), 400 );
} }
update_post_meta( $subscription->id, '_billing_interval', $interval ); $subscription->set_billing_interval( $interval );
} }
if ( ! empty( $data['billing_period'] ) ) { if ( ! empty( $data['billing_period'] ) ) {
@@ -389,7 +391,7 @@ class WC_API_Subscriptions extends WC_API_Orders {
throw new WC_API_Exception( 'wcs_api_invalid_subscription_meta', __( 'Invalid subscription billing period given.', 'woocommerce-subscriptions' ), 400 ); throw new WC_API_Exception( 'wcs_api_invalid_subscription_meta', __( 'Invalid subscription billing period given.', 'woocommerce-subscriptions' ), 400 );
} }
update_post_meta( $subscription->id, '_billing_period', $period ); $subscription->set_billing_period( $period );
} }
$dates_to_update = array(); $dates_to_update = array();
@@ -397,7 +399,8 @@ class WC_API_Subscriptions extends WC_API_Orders {
foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) { foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) {
if ( isset( $data[ $date_type . '_date' ] ) ) { if ( isset( $data[ $date_type . '_date' ] ) ) {
$dates_to_update[ $date_type ] = $data[ $date_type . '_date' ]; $date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type;
$dates_to_update[ $date_type_key ] = $data[ $date_type . '_date' ];
} }
} }
@@ -456,16 +459,16 @@ class WC_API_Subscriptions extends WC_API_Orders {
} }
$subscription_data['billing_schedule'] = array( $subscription_data['billing_schedule'] = array(
'period' => $subscription->billing_period, 'period' => $subscription->get_billing_period(),
'interval' => $subscription->billing_interval, 'interval' => $subscription->get_billing_interval(),
'start_at' => $this->get_formatted_datetime( $subscription, 'start' ), 'start_at' => $this->get_formatted_datetime( $subscription, 'date_created' ),
'trial_end_at' => $this->get_formatted_datetime( $subscription, 'trial_end' ), 'trial_end_at' => $this->get_formatted_datetime( $subscription, 'trial_end' ),
'next_payment_at' => $this->get_formatted_datetime( $subscription, 'next_payment' ), 'next_payment_at' => $this->get_formatted_datetime( $subscription, 'next_payment' ),
'end_at' => $this->get_formatted_datetime( $subscription, 'end' ), 'end_at' => $this->get_formatted_datetime( $subscription, 'end' ),
); );
if ( ! empty( $subscription->order ) ) { if ( $subscription->get_parent_id() ) {
$subscription_data['parent_order_id'] = $subscription->order->id; $subscription_data['parent_order_id'] = $subscription->get_parent_id();
} else { } else {
$subscription_data['parent_order_id'] = array(); $subscription_data['parent_order_id'] = array();
} }

View File

@@ -0,0 +1,36 @@
<?php
/**
* REST API subscription notes controller
*
* Handles requests to the /subscription/<id>/notes endpoint.
*
* @author Prospress
* @since 2.1
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* REST API Subscription Notes controller class.
*
* @package WooCommerce_Subscriptions/API
* @extends WC_REST_Order_Notes_Controller
*/
class WC_REST_Subscription_Notes_Controller extends WC_REST_Order_Notes_Controller {
/**
* Route base.
*
* @var string
*/
protected $rest_base = 'subscriptions/(?P<order_id>[\d]+)/notes';
/**
* Post type.
*
* @var string
*/
protected $post_type = 'shop_subscription';
}

View File

@@ -0,0 +1,419 @@
<?php
/**
* REST API Subscriptions controller
*
* Handles requests to the /subscription endpoint.
*
* @author Prospress
* @since 2.1
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* REST API Subscriptions controller class.
*
* @package WooCommerce_Subscriptions/API
* @extends WC_REST_Orders_Controller
*/
class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
/**
* Route base.
*
* @var string
*/
protected $rest_base = 'subscriptions';
/**
* Post type.
*
* @var string
*/
protected $post_type = 'shop_subscription';
/**
* Initialize subscription actions and filters
*/
public function __construct() {
add_filter( 'woocommerce_rest_prepare_shop_subscription', array( $this, 'filter_get_subscription_response' ), 10, 3 );
add_filter( 'woocommerce_rest_shop_subscription_query', array( $this, 'query_args' ), 10, 2 );
add_filter( 'woocommerce_rest_pre_insert_shop_subscription', array( $this, 'prepare_subscription_args' ), 10, 2 );
}
/**
* Register the routes for subscriptions.
*/
public function register_routes() {
parent::register_routes();
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/orders', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_subscription_orders' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => $this->get_collection_params(),
),
'schema' => array( $this, 'get_public_item_schema' ),
) );
register_rest_route( $this->namespace, '/' . $this->rest_base . '/statuses', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_statuses' ),
),
'schema' => array( $this, 'get_public_item_schema' ),
) );
}
/**
* Filter WC_REST_Orders_Controller::get_item response for subscription post types
*
* @since 2.1
* @param WP_REST_Response $response
* @param WP_POST $post
* @param WP_REST_Request $request
*/
public function filter_get_subscription_response( $response, $post, $request ) {
if ( ! empty( $post->post_type ) && ! empty( $post->ID ) && 'shop_subscription' == $post->post_type ) {
$subscription = wcs_get_subscription( $post->ID );
$response->data['billing_period'] = $subscription->get_billing_period();
$response->data['billing_interval'] = $subscription->get_billing_interval();
$response->data['start_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'date_created' ) );
$response->data['trial_end_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'trial_end' ) );
$response->data['next_payment_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'next_payment' ) );
$response->data['end_date'] = wc_rest_prepare_date_response( $subscription->get_date( 'end_date' ) );
}
return $response;
}
/**
* Sets the order_total value on the subscription after WC_REST_Orders_Controller::create_order
* calls calculate_totals(). This allows store admins to create a recurring payment via the api
* without needing to attach a product to the subscription.
*
* @since 2.1
* @param WP_REST_Request $request
*/
protected function create_order( $request ) {
$post_id = parent::create_order( $request );
if ( is_wp_error( $post_id ) ) {
return $post_id;
}
if ( isset( $request['order_total'] ) ) {
update_post_meta( $post_id, '_order_total', wc_format_decimal( $request['order_total'], get_option( 'woocommerce_price_num_decimals' ) ) );
}
return $post_id;
}
/**
* Overrides WC_REST_Orders_Controller::update_order to update subscription specific meta
* calls parent::update_order to update the rest.
*
* @since 2.1
* @param WP_REST_Request $request
* @param WP_POST $post
*/
protected function update_order( $request, $post ) {
try {
$post_id = parent::update_order( $request, $post );
if ( is_wp_error( $post_id ) ) {
return $post_id;
}
$subscription = wcs_get_subscription( $post_id );
$this->update_schedule( $subscription, $request );
$payment_data = ( ! empty( $request['payment_details'] ) ) ? $request['payment_details'] : array();
$existing_payment_method_id = $subscription->get_payment_method();
if ( empty( $payment_data['method_id'] ) && isset( $request['payment_method'] ) ) {
$payment_data['method_id'] = $request['payment_method'];
} elseif ( ! empty( $existing_payment_method_id ) ) {
$payment_data['method_id'] = $existing_payment_method_id;
}
if ( isset( $payment_data['method_id'] ) ) {
$this->update_payment_method( $subscription, $payment_data, true );
}
return $post_id;
} catch ( WC_REST_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
} catch ( Exception $e ) {
return new WP_Error( 'woocommerce_rest_cannot_update_subscription', $e->getMessage(), array( 'status' => 400 ) );
}
}
/**
* Get subscription orders
*
* @since 2.1
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response $response
*/
public function get_subscription_orders( $request ) {
$id = (int) $request['id'];
if ( empty( $id ) || ! wcs_is_subscription( $id ) ) {
return new WP_Error( 'woocommerce_rest_invalid_shop_subscription_id', __( 'Invalid subscription id.', 'woocommerce-subscriptions' ), array( 'status' => 404 ) );
}
$this->post_type = 'shop_order';
$subscription = wcs_get_subscription( $id );
$subscription_orders = $subscription->get_related_orders();
$orders = array();
foreach ( $subscription_orders as $order_id ) {
$post = get_post( $order_id );
if ( ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) {
continue;
}
$response = $this->prepare_item_for_response( $post, $request );
foreach ( array( 'parent', 'renewal', 'switch' ) as $order_type ) {
if ( wcs_order_contains_subscription( $order_id, $order_type ) ) {
$response->data['order_type'] = $order_type . '_order';
break;
}
}
$orders[] = $this->prepare_response_for_collection( $response );
}
$response = rest_ensure_response( $orders );
$response->header( 'X-WP-Total', count( $orders ) );
$response->header( 'X-WP-TotalPages', 1 );
return apply_filters( 'wcs_rest_subscription_orders_response', $response, $request );
}
/**
* Get subscription statuses
*
* @since 2.1
*/
public function get_statuses() {
return rest_ensure_response( wcs_get_subscription_statuses() );
}
/**
* Overrides WC_REST_Orders_Controller::get_order_statuses() so that subscription statuses are
* validated correctly in WC_REST_Orders_Controller::get_collection_params()
*
* @since 2.1
*/
protected function get_order_statuses() {
$subscription_statuses = array();
foreach ( array_keys( wcs_get_subscription_statuses() ) as $status ) {
$subscription_statuses[] = str_replace( 'wc-', '', $status );
}
return $subscription_statuses;
}
/**
* Create WC_Subscription object.
*
* @since 2.1
* @param array $args subscription args.
* @return WC_Subscription
*/
protected function create_base_order( $args ) {
$subscription = wcs_create_subscription( $args );
if ( is_wp_error( $subscription ) ) {
throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_subscription', sprintf( __( 'Cannot create subscription: %s.', 'woocommerce-subscriptions' ), implode( ', ', $subscription->get_error_messages() ) ), 400 );
}
$this->update_schedule( $subscription, $args );
if ( empty( $args['payment_details']['method_id'] ) && ! empty( $args['payment_method'] ) ) {
$args['payment_details']['method_id'] = $args['payment_method'];
}
$this->update_payment_method( $subscription, $args['payment_details'] );
return $subscription;
}
/**
* Update or set the subscription schedule with the request data
*
* @since 2.1
* @param WC_Subscription $subscription
* @param array $data
*/
public function update_schedule( $subscription, $data ) {
if ( isset( $data['billing_interval'] ) ) {
update_post_meta( $subscription->get_id(), '_billing_interval', absint( $data['billing_interval'] ) );
}
if ( ! empty( $data['billing_period'] ) ) {
update_post_meta( $subscription->get_id(), '_billing_period', $data['billing_period'] );
}
try {
$dates_to_update = array();
foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) {
if ( isset( $data[ $date_type . '_date' ] ) ) {
$date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type;
$dates_to_update[ $date_type_key ] = $data[ $date_type . '_date' ];
}
}
if ( ! empty( $dates_to_update ) ) {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
}
/**
* Validate and update payment method on a subscription
*
* @since 2.1
* @param WC_Subscription $subscription
* @param array $data
* @param bool $updating
*/
public function update_payment_method( $subscription, $data, $updating = false ) {
$payment_gateways = WC()->payment_gateways->get_available_payment_gateways();
$payment_method = ( ! empty( $data['method_id'] ) ) ? $data['method_id'] : 'manual';
$payment_gateway = ( ! empty( $payment_gateways[ $payment_method ] ) ) ? $payment_gateways[ $payment_method ] : '';
try {
if ( $updating && ! array_key_exists( $payment_method, WCS_Change_Payment_Method_Admin::get_valid_payment_methods( $subscription ) ) ) {
throw new Exception( __( 'Gateway does not support admin changing the payment method on a Subscription.', 'woocommerce-subscriptions' ) );
}
$payment_method_meta = apply_filters( 'woocommerce_subscription_payment_meta', array(), $subscription );
if ( ! empty( $payment_gateway ) && isset( $payment_method_meta[ $payment_gateway->id ] ) ) {
$payment_method_meta = $payment_method_meta[ $payment_gateway->id ];
if ( ! empty( $payment_method_meta ) ) {
foreach ( $payment_method_meta as $meta_table => &$meta ) {
if ( ! is_array( $meta ) ) {
continue;
}
foreach ( $meta as $meta_key => &$meta_data ) {
if ( isset( $data[ $meta_table ][ $meta_key ] ) ) {
$meta_data['value'] = $data[ $meta_table ][ $meta_key ];
}
}
}
}
}
$subscription->set_payment_method( $payment_gateway, $payment_method_meta );
} catch ( Exception $e ) {
$subscription->set_payment_method();
// translators: 1$: gateway id, 2$: error message
throw new WC_REST_Exception( 'woocommerce_rest_invalid_payment_data', sprintf( __( 'Subscription payment method could not be set to %1$s with error message: %2$s', 'woocommerce-subscriptions' ), $payment_method, $e->getMessage() ), 400 );
}
}
/**
* Adds additional item schema information for subscription requests
*
* @since 2.1
*/
public function get_item_schema() {
$schema = parent::get_item_schema();
$subscriptions_schema = array(
'billing_interval' => array(
'description' => __( 'The number of billing periods between subscription renewals.', 'woocommerce-subscriptions' ),
'type' => 'integer',
'context' => array( 'view', 'edit' ),
),
'billing_period' => array(
'description' => __( 'Billing period for the subscription.', 'woocommerce-subscriptions' ),
'type' => 'string',
'enum' => array_keys( wcs_get_subscription_period_strings() ),
'context' => array( 'view', 'edit' ),
),
'payment_details' => array(
'description' => __( 'Subscription payment details.', 'woocommerce-subscriptions' ),
'type' => 'object',
'context' => array( 'edit' ),
'properties' => array(
'method_id' => array(
'description' => __( 'Payment gateway ID.', 'woocommerce-subscriptions' ),
'type' => 'string',
'context' => array( 'edit' ),
),
),
),
'start_date' => array(
'description' => __( "The subscription's start date.", 'woocommerce-subscriptions' ),
'type' => 'date-time',
'context' => array( 'view', 'edit' ),
),
'trial_date' => array(
'description' => __( "The subscription's trial date", 'woocommerce-subscriptions' ),
'type' => 'date-time',
'context' => array( 'view', 'edit' ),
),
'next_payment_date' => array(
'description' => __( "The subscription's next payment date.", 'woocommerce-subscriptions' ),
'type' => 'date-time',
'context' => array( 'view', 'edit' ),
),
'end_date' => array(
'description' => __( "The subscription's end date.", 'woocommerce-subscriptions' ),
'type' => 'date-time',
'context' => array( 'view', 'edit' ),
),
);
$schema['properties'] += $subscriptions_schema;
return $schema;
}
/**
* Prepare subscription data for create.
*
* @since 2.1
* @param stdClass $data
* @param WP_REST_Request $request Request object.
* @return stdClass
*/
public function prepare_subscription_args( $data, $request ) {
$data->billing_interval = $request['billing_interval'];
$data->billing_period = $request['billing_period'];
foreach ( array( 'start', 'trial_end', 'end', 'next_payment' ) as $date_type ) {
if ( ! empty( $request[ $date_type . '_date' ] ) ) {
$date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type . '_date';
$data->{$date_type_key} = $request[ $date_type . '_date' ];
}
}
$data->payment_details = ! empty( $request['payment_details'] ) ? $request['payment_details'] : '';
$data->payment_method = ! empty( $request['payment_method'] ) ? $request['payment_method'] : '';
return $data;
}
}

View File

@@ -0,0 +1,7 @@
<?php
class WC_Order_Item_Pending_Switch extends WC_Order_Item_Product {
public function get_type() {
return 'line_item_pending_switch';
}
}

View File

@@ -17,7 +17,10 @@ if ( ! defined( 'ABSPATH' ) ) {
class WC_Product_Subscription_Variation extends WC_Product_Variation { class WC_Product_Subscription_Variation extends WC_Product_Variation {
var $product_type; /**
* A way to access the old array property.
*/
protected $subscription_variation_level_meta_data;
/** /**
* Create a simple subscription product object. * Create a simple subscription product object.
@@ -25,26 +28,46 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
* @access public * @access public
* @param mixed $product * @param mixed $product
*/ */
public function __construct( $product, $args = array() ) { public function __construct( $product = 0 ) {
parent::__construct( $product, $args = array() ); parent::__construct( $product );
$this->parent_product_type = $this->product_type; $this->subscription_variation_level_meta_data = new WCS_Array_Property_Post_Meta_Black_Magic( $this->get_id() );
}
$this->product_type = 'subscription_variation'; /**
* Magic __get method for backwards compatibility. Map legacy vars to WC_Subscriptions_Product getters.
*
* @param string $key Key name.
* @return mixed
*/
public function __get( $key ) {
$this->subscription_variation_level_meta_data = array( if ( 'subscription_variation_level_meta_data' === $key ) {
'subscription_price' => 0,
'subscription_period' => '',
'subscription_period_interval' => 'day',
'subscription_length' => 0,
'subscription_trial_length' => 0,
'subscription_trial_period' => 'day',
'subscription_sign_up_fee' => 0,
'subscription_payment_sync_date' => 0,
);
$this->variation_level_meta_data = array_merge( $this->variation_level_meta_data, $this->subscription_variation_level_meta_data ); wcs_deprecated_argument( __CLASS__ . '::$' . $key, '2.2.0', 'Product properties should not be accessed directly with WooCommerce 3.0+. Use the getter in WC_Subscriptions_Product instead.' );
$value = $this->subscription_variation_level_meta_data; // Behold, the horror that is the magic of WCS_Array_Property_Post_Meta_Black_Magic
} else {
$value = wcs_product_deprecated_property_handler( $key, $this );
// No matching property found in wcs_product_deprecated_property_handler()
if ( is_null( $value ) ) {
$value = parent::__get( $key );
}
}
return $value;
}
/**
* Get internal type.
*
* @return string
*/
public function get_type() {
return 'subscription_variation';
} }
/** /**
@@ -90,6 +113,45 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
return apply_filters( 'woocommerce_product_single_add_to_cart_text', self::add_to_cart_text(), $this ); return apply_filters( 'woocommerce_product_single_add_to_cart_text', self::add_to_cart_text(), $this );
} }
/**
* Checks if the variable product this variation belongs to is purchasable.
*
* @access public
* @return bool
*/
public function is_purchasable() {
$purchasable = WCS_Limiter::is_purchasable( wc_get_product( $this->get_parent_id() )->is_purchasable(), $this );
return apply_filters( 'woocommerce_subscription_variation_is_purchasable', $purchasable, $this );
}
/**
* Checks the product type to see if it is either this product's type or the parent's
* product type.
*
* @access public
* @param mixed $type Array or string of types
* @return bool
*/
public function is_type( $type ) {
if ( 'variation' == $type || ( is_array( $type ) && in_array( 'variation', $type ) ) ) {
return true;
} else {
return parent::is_type( $type );
}
}
/* Deprecated Functions */
/**
* Return the sign-up fee for this product
*
* @return string
*/
public function get_sign_up_fee() {
wcs_deprecated_function( __METHOD__, '2.2.0', 'WC_Subscriptions_Product::get_sign_up_fee( $this )' );
return WC_Subscriptions_Product::get_sign_up_fee( $this );
}
/** /**
* Returns the sign up fee (including tax) by filtering the products price used in * Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty ) * @see WC_Product::get_price_including_tax( $qty )
@@ -97,10 +159,11 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
* @return string * @return string
*/ */
public function get_sign_up_fee_including_tax( $qty = 1, $price = '' ) { public function get_sign_up_fee_including_tax( $qty = 1, $price = '' ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_including_tax( $product, array( "qty" => $qty, "price" => $price ) )' );
add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 ); add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
$sign_up_fee_including_tax = parent::get_price_including_tax( $qty, $price ); $sign_up_fee_including_tax = parent::get_price_including_tax( $qty );
remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 ); remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
@@ -114,54 +177,14 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
* @return string * @return string
*/ */
public function get_sign_up_fee_excluding_tax( $qty = 1, $price = '' ) { public function get_sign_up_fee_excluding_tax( $qty = 1, $price = '' ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_excluding_tax( $product, array( "qty" => $qty, "price" => $price ) )' );
add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 ); add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
$sign_up_fee_excluding_tax = parent::get_price_excluding_tax( $qty, $price ); $sign_up_fee_excluding_tax = parent::get_price_excluding_tax( $qty );
remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 ); remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
return $sign_up_fee_excluding_tax; return $sign_up_fee_excluding_tax;
} }
/**
* Return the sign-up fee for this product
*
* @return string
*/
public function get_sign_up_fee() {
return WC_Subscriptions_Product::get_sign_up_fee( $this );
}
/**
* Checks if the variable product this variation belongs to is purchasable.
*
* @access public
* @return bool
*/
function is_purchasable() {
$purchasable = WCS_Limiter::is_purchasable( $this->parent->is_purchasable(), $this );
return apply_filters( 'woocommerce_subscription_variation_is_purchasable', $purchasable, $this );
}
/**
* Checks the product type to see if it is either this product's type or the parent's
* product type.
*
* @access public
* @param mixed $type Array or string of types
* @return bool
*/
public function is_type( $type ) {
if ( $this->product_type == $type || ( is_array( $type ) && in_array( $this->product_type, $type ) ) ) {
return true;
} elseif ( $this->parent_product_type == $type || ( is_array( $type ) && in_array( $this->parent_product_type, $type ) ) ) {
return true;
} else {
return false;
}
}
} }

View File

@@ -17,66 +17,13 @@ if ( ! defined( 'ABSPATH' ) ) {
class WC_Product_Subscription extends WC_Product_Simple { class WC_Product_Subscription extends WC_Product_Simple {
var $subscription_price;
var $subscription_period;
var $subscription_period_interval;
var $subscription_length;
var $subscription_trial_length;
var $subscription_trial_period;
var $subscription_sign_up_fee;
/** /**
* Create a simple subscription product object. * Get internal type.
* *
* @access public * @return string
* @param mixed $product
*/ */
public function __construct( $product ) { public function get_type() {
parent::__construct( $product ); return 'subscription';
$this->product_type = 'subscription';
// Load all meta fields
$this->product_custom_fields = get_post_meta( $this->id );
// Convert selected subscription meta fields for easy access
if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) {
$this->subscription_price = $this->product_custom_fields['_subscription_price'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) {
$this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) {
$this->subscription_period = $this->product_custom_fields['_subscription_period'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) {
$this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) {
$this->subscription_length = $this->product_custom_fields['_subscription_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) {
$this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) {
$this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0];
}
$this->subscription_payment_sync_date = ( ! isset( $this->product_custom_fields['_subscription_payment_sync_date'][0] ) ) ? 0 : maybe_unserialize( $this->product_custom_fields['_subscription_payment_sync_date'][0] );
$this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0];
} }
/** /**
@@ -86,12 +33,15 @@ class WC_Product_Subscription extends WC_Product_Simple {
* @return mixed * @return mixed
*/ */
public function __get( $key ) { public function __get( $key ) {
if ( 'limit_subscriptions' === $key ) {
_deprecated_argument( 'WC_Product_Subscription->limit_subscriptions', '2.1', 'Use wcs_get_product_limitation directly' ); $value = wcs_product_deprecated_property_handler( $key, $this );
return wcs_get_product_limitation( $this );
} else { // No matching property found in wcs_product_deprecated_property_handler()
return parent::__get( $key ); if ( is_null( $value ) ) {
$value = parent::__get( $key );
} }
return $value;
} }
/** /**
@@ -137,49 +87,6 @@ class WC_Product_Subscription extends WC_Product_Simple {
return apply_filters( 'woocommerce_product_single_add_to_cart_text', self::add_to_cart_text(), $this ); return apply_filters( 'woocommerce_product_single_add_to_cart_text', self::add_to_cart_text(), $this );
} }
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_including_tax( $qty = 1 ) {
add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
$sign_up_fee_including_tax = parent::get_price_including_tax( $qty );
remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
return $sign_up_fee_including_tax;
}
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_excluding_tax( $qty = 1 ) {
add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
$sign_up_fee_excluding_tax = parent::get_price_excluding_tax( $qty );
remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
return $sign_up_fee_excluding_tax;
}
/**
* Return the sign-up fee for this product
*
* @return string
*/
public function get_sign_up_fee() {
return WC_Subscriptions_Product::get_sign_up_fee( $this );
}
/** /**
* Checks if the store manager has requested the current product be limited to one purchase * Checks if the store manager has requested the current product be limited to one purchase
* per customer, and if so, checks whether the customer already has an active subscription to * per customer, and if so, checks whether the customer already has an active subscription to
@@ -193,4 +100,38 @@ class WC_Product_Subscription extends WC_Product_Simple {
return apply_filters( 'woocommerce_subscription_is_purchasable', $purchasable, $this ); return apply_filters( 'woocommerce_subscription_is_purchasable', $purchasable, $this );
} }
/* Deprecated Functions */
/**
* Return the sign-up fee for this product
*
* @return string
*/
public function get_sign_up_fee() {
wcs_deprecated_function( __METHOD__, '2.2.0', 'WC_Subscriptions_Product::get_sign_up_fee( $this )' );
return WC_Subscriptions_Product::get_sign_up_fee( $this );
}
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_including_tax( $qty = 1 ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_including_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' );
return wcs_get_price_including_tax( $this, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $this ) ) );
}
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_excluding_tax( $qty = 1 ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_excluding_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' );
return wcs_get_price_excluding_tax( $this, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $this ) ) );
}
} }

View File

@@ -16,69 +16,17 @@ if ( ! defined( 'ABSPATH' ) ) {
class WC_Product_Variable_Subscription extends WC_Product_Variable { class WC_Product_Variable_Subscription extends WC_Product_Variable {
var $subscription_price; private $min_max_variation_data = array();
var $subscription_period; private $sorted_variation_prices = array();
var $max_variation_period;
var $subscription_period_interval;
var $max_variation_period_interval;
var $product_type;
/** /**
* Create a simple subscription product object. * Get internal type.
* *
* @access public * @return string
* @param mixed $product
*/ */
public function __construct( $product ) { public function get_type() {
return 'variable-subscription';
parent::__construct( $product );
$this->parent_product_type = $this->product_type;
$this->product_type = 'variable-subscription';
// Load all meta fields
$this->product_custom_fields = get_post_meta( $this->id );
// Convert selected subscription meta fields for easy access
if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) {
$this->subscription_price = $this->product_custom_fields['_subscription_price'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) {
$this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) {
$this->subscription_period = $this->product_custom_fields['_subscription_period'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) {
$this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) {
$this->subscription_length = $this->product_custom_fields['_subscription_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) {
$this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) {
$this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0];
}
$this->subscription_payment_sync_date = 0;
$this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0];
add_filter( 'woocommerce_add_to_cart_handler', array( &$this, 'add_to_cart_handler' ), 10, 2 );
} }
/** /**
@@ -88,14 +36,21 @@ class WC_Product_Variable_Subscription extends WC_Product_Variable {
* @return mixed * @return mixed
*/ */
public function __get( $key ) { public function __get( $key ) {
if ( 'limit_subscriptions' === $key ) {
_deprecated_argument( 'WC_Product_Subscription->limit_subscriptions', '2.1', 'Use wcs_get_product_limitation directly' ); if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
return wcs_get_product_limitation( $this ); $value = parent::__get( $key );
} else { } else {
return parent::__get( $key ); $value = wcs_product_deprecated_property_handler( $key, $this );
// No matching property found in wcs_product_deprecated_property_handler()
if ( is_null( $value ) ) {
$value = parent::__get( $key );
} }
} }
return $value;
}
/** /**
* Get the add to cart button text for the single page * Get the add to cart button text for the single page
* *
@@ -113,352 +68,6 @@ class WC_Product_Variable_Subscription extends WC_Product_Variable {
return apply_filters( 'woocommerce_product_single_add_to_cart_text', $text, $this ); return apply_filters( 'woocommerce_product_single_add_to_cart_text', $text, $this );
} }
/**
* Sync variable product prices with the childs lowest/highest prices.
*
* @access public
* @return void
*/
public function variable_product_sync( $product_id = '' ) {
parent::variable_product_sync();
$children = get_posts( array(
'post_parent' => $this->id,
'posts_per_page' => -1,
'post_type' => 'product_variation',
'fields' => 'ids',
'post_status' => 'publish',
) );
$lowest_initial_amount = $highest_initial_amount = $lowest_price = $highest_price = '';
$shortest_initial_period = $longest_initial_period = $shortest_trial_period = $longest_trial_period = $shortest_trial_length = $longest_trial_length = '';
$longest_initial_interval = $shortest_initial_interval = $variable_subscription_period = $variable_subscription_period_interval = '';
$lowest_regular_price = $highest_regular_price = $lowest_sale_price = $highest_sale_price = $max_subscription_period = $max_subscription_period_interval = '';
$variable_subscription_sign_up_fee = $variable_subscription_trial_period = $variable_subscription_trial_length = $variable_subscription_length = $variable_subscription_sign_up_fee = $variable_subscription_trial_period = $variable_subscription_trial_length = $variable_subscription_length = '';
$min_variation_id = $max_variation_id = null;
if ( $children ) {
foreach ( $children as $child ) {
$is_max = $is_min = false;
// WC has already determined the correct price which accounts for sale price
$child_price = get_post_meta( $child, '_price', true );
$child_billing_period = get_post_meta( $child, '_subscription_period', true );
$child_billing_interval = get_post_meta( $child, '_subscription_period_interval', true );
$child_sign_up_fee = get_post_meta( $child, '_subscription_sign_up_fee', true );
$child_free_trial_length = get_post_meta( $child, '_subscription_trial_length', true );
$child_free_trial_period = get_post_meta( $child, '_subscription_trial_period', true );
if ( '' === $child_price && '' === $child_sign_up_fee ) {
continue;
}
$child_price = ( '' === $child_price ) ? 0 : $child_price;
$child_sign_up_fee = ( '' === $child_sign_up_fee ) ? 0 : $child_sign_up_fee;
// Take taxes into a account to avoid displaying a variation with a lower price pre-tax, but higher price post-tax, in the case of variations having different tax classes (see: https://github.com/Prospress/woocommerce-subscriptions/pull/1602#issuecomment-241330131)
if ( $variation = $this->get_child( $child ) ) {
if ( $child_price > 0 ) {
$child_price = ( 'incl' == get_option( 'woocommerce_tax_display_shop' ) ) ? $variation->get_price_including_tax( 1, $child_price ) : $variation->get_price_excluding_tax( 1, $child_price );
}
if ( $child_sign_up_fee > 0 ) {
$child_sign_up_fee = ( 'incl' == get_option( 'woocommerce_tax_display_shop' ) ) ? $variation->get_sign_up_fee_including_tax( 1, $child_sign_up_fee ) : $variation->get_sign_up_fee_excluding_tax( 1, $child_sign_up_fee );
}
}
$has_free_trial = ( '' !== $child_free_trial_length && $child_free_trial_length > 0 ) ? true : false;
// Determine some recurring price flags
$is_lowest_price = ( $child_price < $lowest_price || '' === $lowest_price ) ? true : false;
$is_longest_period = ( WC_Subscriptions::get_longest_period( $variable_subscription_period, $child_billing_period ) === $child_billing_period ) ? true : false;
$is_longest_interval = ( $child_billing_interval >= $variable_subscription_period_interval || '' === $variable_subscription_period_interval ) ? true : false;
// Find the amount the subscriber will have to pay up-front
if ( $has_free_trial ) {
$initial_amount = $child_sign_up_fee;
$initial_period = $child_free_trial_period;
$initial_interval = $child_free_trial_length;
} else {
$initial_amount = $child_price + $child_sign_up_fee;
$initial_period = $child_billing_period;
$initial_interval = $child_billing_interval;
}
// We have a free trial & no sign-up fee, so need to choose the longest free trial (and maybe the shortest)
if ( $has_free_trial && 0 == $child_sign_up_fee ) {
// First variation
if ( '' === $longest_trial_period ) {
$is_min = true;
// If two variations have the same free trial, choose the variation with the lowest recurring price for the longest period
} elseif ( $variable_subscription_trial_period === $child_free_trial_period && $child_free_trial_length === $variable_subscription_trial_length ) {
// If the variation has the lowest recurring price, it's the cheapest
if ( $is_lowest_price ) {
$is_min = true;
// When current variation's free trial is the same as the lowest, it's the cheaper if it has a longer billing schedule
} elseif ( $child_price === $lowest_price ) {
if ( $is_longest_period && $is_longest_interval ) {
$is_min = true;
// Longest with a new billing period
} elseif ( $is_longest_period && $child_billing_period !== $variable_subscription_trial_period ) {
$is_min = true;
}
}
// Otherwise the cheapest variation is the one with the longer trial
} elseif ( $variable_subscription_trial_period === $child_free_trial_period ) {
$is_min = ( $child_free_trial_length > $variable_subscription_trial_length ) ? true : false;
// Otherwise just a longer trial period (that isn't equal to the longest period)
} elseif ( WC_Subscriptions::get_longest_period( $longest_trial_period, $child_free_trial_period ) === $child_free_trial_period ) {
$is_min = true;
}
if ( $is_min ) {
$longest_trial_period = $child_free_trial_period;
$longest_trial_length = $child_free_trial_length;
}
// If the current cheapest variation is also free, then the shortest trial period is the most expensive
if ( 0 == $lowest_price || '' === $lowest_price ) {
if ( '' === $shortest_trial_period ) {
$is_max = true;
// Need to check trial length
} elseif ( $shortest_trial_period === $child_free_trial_period ) {
$is_max = ( $child_free_trial_length < $shortest_trial_length ) ? true : false;
// Need to find shortest period
} elseif ( WC_Subscriptions::get_shortest_period( $shortest_trial_period, $child_free_trial_period ) === $child_free_trial_period ) {
$is_max = true;
}
if ( $is_max ) {
$shortest_trial_period = $child_free_trial_period;
$shortest_trial_length = $child_free_trial_length;
}
}
} else {
$longest_initial_period = WC_Subscriptions::get_longest_period( $longest_initial_period, $initial_period );
$shortest_initial_period = WC_Subscriptions::get_shortest_period( $shortest_initial_period, $initial_period );
$is_lowest_initial_amount = ( $initial_amount < $lowest_initial_amount || '' === $lowest_initial_amount ) ? true : false;
$is_longest_initial_period = ( $initial_period === $longest_initial_period ) ? true : false;
$is_longest_initial_interval = ( $initial_interval >= $longest_initial_interval || '' === $longest_initial_interval ) ? true : false;
$is_highest_initial = ( $initial_amount > $highest_initial_amount || '' === $highest_initial_amount ) ? true : false;
$is_shortest_period = ( $initial_period === $shortest_initial_period || '' === $shortest_initial_period ) ? true : false;
$is_shortest_interval = ( $initial_interval < $shortest_initial_interval || '' === $shortest_initial_interval ) ? true : false;
// If we're not dealing with the lowest initial access amount, then ignore this variation
if ( ! $is_lowest_initial_amount && $initial_amount !== $lowest_initial_amount ) {
continue;
}
// If the variation has the lowest price, it's the cheapest
if ( $is_lowest_initial_amount ) {
$is_min = true;
// When current variation's price is the same as the lowest, it's the cheapest only if it has a longer billing schedule
} elseif ( $initial_amount === $lowest_initial_amount ) {
// We need to check the recurring schedule when the sign-up fee & free trial periods are equal
if ( $has_free_trial && $initial_period == $longest_initial_period && $initial_interval == $longest_initial_interval ) {
// If the variation has the lowest recurring price, it's the cheapest
if ( $is_lowest_price ) {
$is_min = true;
// When current variation's price is the same as the lowest, it's the cheapest only if it has a longer billing schedule
} elseif ( $child_price === $lowest_price ) {
if ( $is_longest_period && $is_longest_interval ) {
$is_min = true;
// Longest with a new billing period
} elseif ( $is_longest_period && $child_billing_period !== $variable_subscription_period ) {
$is_min = true;
}
}
// Longest initial term is the cheapest
} elseif ( $is_longest_initial_period && $is_longest_initial_interval ) {
$is_min = true;
// Longest with a new billing period
} elseif ( $is_longest_initial_period && $initial_period !== $variable_subscription_period ) {
$is_min = true;
}
}
// If we have the highest price for the shortest period, we might have the maximum variation
if ( $is_highest_initial && $is_shortest_period && $is_shortest_interval ) {
$is_max = true;
// But only if its for the shortest billing period
} elseif ( $child_price === $highest_price ) {
if ( $is_shortest_period && $is_shortest_interval ) {
$is_max = true;
} elseif ( $is_shortest_period ) {
$is_max = true;
}
}
}
// If it's the min subscription terms
if ( $is_min ) {
$min_variation_id = $child;
$lowest_price = $child_price;
$lowest_regular_price = get_post_meta( $child, '_regular_price', true );
$lowest_sale_price = get_post_meta( $child, '_sale_price', true );
$lowest_regular_price = ( '' === $lowest_regular_price ) ? 0 : $lowest_regular_price;
$lowest_sale_price = ( '' === $lowest_sale_price ) ? 0 : $lowest_sale_price;
$lowest_initial_amount = $initial_amount;
$longest_initial_period = $initial_period;
$longest_initial_interval = $initial_interval;
$variable_subscription_sign_up_fee = $child_sign_up_fee;
$variable_subscription_period = $child_billing_period;
$variable_subscription_period_interval = $child_billing_interval;
$variable_subscription_trial_length = $child_free_trial_length;
$variable_subscription_trial_period = $child_free_trial_period;
$variable_subscription_length = get_post_meta( $child, '_subscription_length', true );
}
if ( $is_max ) {
$max_variation_id = $child;
$highest_price = $child_price;
$highest_regular_price = get_post_meta( $child, '_regular_price', true );
$highest_sale_price = get_post_meta( $child, '_sale_price', true );
$highest_initial_amount = $initial_amount;
$highest_regular_price = ( '' === $highest_regular_price ) ? 0 : $highest_regular_price;
$highest_sale_price = ( '' === $highest_sale_price ) ? 0 : $highest_sale_price;
$max_subscription_period = $child_billing_period;
$max_subscription_period_interval = $child_billing_interval;
}
}
update_post_meta( $this->id, '_min_price_variation_id', $min_variation_id );
update_post_meta( $this->id, '_max_price_variation_id', $max_variation_id );
update_post_meta( $this->id, '_price', $lowest_price );
update_post_meta( $this->id, '_min_variation_price', $lowest_price );
update_post_meta( $this->id, '_max_variation_price', $highest_price );
update_post_meta( $this->id, '_min_variation_regular_price', $lowest_regular_price );
update_post_meta( $this->id, '_max_variation_regular_price', $highest_regular_price );
update_post_meta( $this->id, '_min_variation_sale_price', $lowest_sale_price );
update_post_meta( $this->id, '_max_variation_sale_price', $highest_sale_price );
update_post_meta( $this->id, '_min_variation_period', $variable_subscription_period );
update_post_meta( $this->id, '_max_variation_period', $variable_subscription_period_interval );
update_post_meta( $this->id, '_min_variation_period_interval', $max_subscription_period );
update_post_meta( $this->id, '_max_variation_period_interval', $max_subscription_period_interval );
update_post_meta( $this->id, '_subscription_price', $lowest_price );
update_post_meta( $this->id, '_subscription_sign_up_fee', $variable_subscription_sign_up_fee );
update_post_meta( $this->id, '_subscription_period', $variable_subscription_period );
update_post_meta( $this->id, '_subscription_period_interval', $variable_subscription_period_interval );
update_post_meta( $this->id, '_subscription_trial_period', $variable_subscription_trial_period );
update_post_meta( $this->id, '_subscription_trial_length', $variable_subscription_trial_length );
update_post_meta( $this->id, '_subscription_length', $variable_subscription_length );
$this->subscription_price = $lowest_price;
$this->subscription_sign_up_fee = $variable_subscription_sign_up_fee;
$this->subscription_period = $variable_subscription_period;
$this->subscription_period_interval = $variable_subscription_period_interval;
$this->subscription_trial_period = $variable_subscription_trial_period;
$this->subscription_trial_length = $variable_subscription_trial_length;
$this->subscription_length = $variable_subscription_length;
if ( function_exists( 'wc_delete_product_transients' ) ) {
wc_delete_product_transients( $this->id );
} else {
WC()->clear_product_transients( $this->id );
}
} else { // No variations yet
$this->subscription_price = '';
$this->subscription_sign_up_fee = '';
$this->subscription_period = 'day';
$this->subscription_period_interval = 1;
$this->subscription_trial_period = 'day';
$this->subscription_trial_length = 1;
$this->subscription_length = 0;
}
}
/**
* Get the min or max variation (active) price.
*
* This is a copy of WooCommerce < 2.4's get_variation_price() method, because 2.4.0 introduced a new
* transient caching system which assumes asort() on prices yields correct results for min/max prices
* (which it does for prices alone, but that's not the full story for subscription prices). Unfortunately,
* the new caching system is also hard to hook into so we'll just use the old system instead as the
* @see self::variable_product_sync() uses the old method also.
*
* @param string $min_or_max - min or max
* @param boolean $display Whether the value is going to be displayed
* @return string
*/
public function get_variation_price( $min_or_max = 'min', $display = false ) {
$variation_id = get_post_meta( $this->id, '_' . $min_or_max . '_price_variation_id', true );
if ( $display ) {
if ( $variation = $this->get_child( $variation_id ) ) {
$price = ( 'incl' == get_option( 'woocommerce_tax_display_shop' ) ) ? $variation->get_price_including_tax() : $variation->get_price_excluding_tax();
} else {
$price = '';
}
} else {
$price = get_post_meta( $variation_id, '_price', true );
}
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display );
}
/** /**
* Returns the price in html format. * Returns the price in html format.
* *
@@ -468,128 +77,20 @@ class WC_Product_Variable_Subscription extends WC_Product_Variable {
*/ */
public function get_price_html( $price = '' ) { public function get_price_html( $price = '' ) {
$price = parent::get_price_html( $price ); $prices = $this->get_variation_prices( true );
if ( ! isset( $this->subscription_period ) || ! isset( $this->subscription_period_interval ) || ! isset( $this->max_variation_period ) || ! isset( $this->max_variation_period_interval ) ) { if ( empty( $prices['price'] ) ) {
$this->variable_product_sync(); return apply_filters( 'woocommerce_variable_empty_price_html', '', $this );
} }
// Only create the subscription price string when a price has been set
if ( $this->subscription_price !== '' || $this->subscription_sign_up_fee !== '' ) {
$price = '';
if ( $this->is_on_sale() && isset( $this->min_variation_price ) && $this->min_variation_regular_price !== $this->get_price() ) {
if ( ! $this->min_variation_price || $this->min_variation_price !== $this->max_variation_price ) {
$price .= $this->get_price_html_from_text();
}
$variation_id = get_post_meta( $this->id, '_min_price_variation_id', true );
$variation = $this->get_child( $variation_id );
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' ); $tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
$sale_price = 'incl' == $tax_display_mode ? $variation->get_price_including_tax( 1, $variation->get_sale_price() ) : $variation->get_price_excluding_tax( 1, $variation->get_sale_price() ); $price = WC_Subscriptions_Product::get_price( $this );
$regular_price = 'incl' == $tax_display_mode ? $variation->get_price_including_tax( 1, $variation->get_regular_price() ) : $variation->get_price_excluding_tax( 1, $variation->get_regular_price() ); $price = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $this, array( 'price' => $price ) ) : wcs_get_price_excluding_tax( $this, array( 'price' => $price ) );
$price = apply_filters( 'woocommerce_variable_price_html', wcs_get_price_html_from_text( $this ) . wc_price( $price ) . $this->get_price_suffix(), $this );
$price .= $this->get_price_html_from_to( $regular_price, $sale_price );
} else {
if ( $this->min_variation_price !== $this->max_variation_price ) {
$price .= $this->get_price_html_from_text();
}
$price .= wc_price( $this->get_variation_price( 'min', true ) );
}
// Make sure the price contains "From:" when billing schedule differs between variations
if ( false === strpos( $price, $this->get_price_html_from_text() ) ) {
if ( $this->subscription_period !== $this->max_variation_period ) {
$price = $this->get_price_html_from_text() . $price;
} elseif ( $this->subscription_period_interval !== $this->max_variation_period_interval ) {
$price = $this->get_price_html_from_text() . $price;
}
}
$price .= $this->get_price_suffix();
$price = WC_Subscriptions_Product::get_price_string( $this, array( 'price' => $price ) ); $price = WC_Subscriptions_Product::get_price_string( $this, array( 'price' => $price ) );
}
return apply_filters( 'woocommerce_variable_subscription_price_html', $price, $this ); return apply_filters( 'woocommerce_variable_subscription_price_html', apply_filters( 'woocommerce_get_price_html', $price, $this ), $this );
}
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_including_tax( $qty = 1 ) {
add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
$sign_up_fee_including_tax = parent::get_price_including_tax( $qty );
remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
return $sign_up_fee_including_tax;
}
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_excluding_tax( $qty = 1 ) {
add_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
$sign_up_fee_excluding_tax = parent::get_price_excluding_tax( $qty );
remove_filter( 'woocommerce_get_price', array( &$this, 'get_sign_up_fee' ), 100, 0 );
return $sign_up_fee_excluding_tax;
}
/**
* Return the sign-up fee for this product
*
* @return string
*/
public function get_sign_up_fee() {
return WC_Subscriptions_Product::get_sign_up_fee( $this );
}
/**
* get_child function.
*
* @access public
* @param mixed $child_id
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
*/
public function get_child( $child_id ) {
return wc_get_product( $child_id, array(
'product_type' => 'Subscription_Variation',
'parent_id' => $this->id,
'parent' => $this,
) );
}
/**
*
* @param string $product_type A string representation of a product type
*/
public function add_to_cart_handler( $handler, $product ) {
if ( 'variable-subscription' === $handler ) {
$handler = 'variable';
}
return $handler;
} }
/** /**
@@ -615,12 +116,114 @@ class WC_Product_Variable_Subscription extends WC_Product_Variable {
* @return bool * @return bool
*/ */
public function is_type( $type ) { public function is_type( $type ) {
if ( $this->product_type == $type || ( is_array( $type ) && in_array( $this->product_type, $type ) ) ) { if ( 'variable' == $type || ( is_array( $type ) && in_array( 'variable', $type ) ) ) {
return true;
} elseif ( $this->parent_product_type == $type || ( is_array( $type ) && in_array( $this->parent_product_type, $type ) ) ) {
return true; return true;
} else { } else {
return false; return parent::is_type( $type );
} }
} }
/**
* Sort an associative array of $variation_id => $price pairs in order of min and max prices.
*
* @param array $prices Associative array of $variation_id => $price pairs
* @return array
*/
protected function sort_variation_prices( $prices ) {
// If we don't have any prices, there's nothing to sort.
if ( empty( $prices ) ) {
return $prices;
}
$prices_hash = md5( json_encode( $prices ) );
if ( empty( $this->sorted_variation_prices[ $prices_hash ] ) ) {
$child_variation_ids = array_keys( $prices );
$variation_hash = md5( json_encode( $child_variation_ids ) );
if ( empty( $this->min_max_variation_data[ $variation_hash ] ) ) {
$this->min_max_variation_data[ $variation_hash ] = wcs_get_min_max_variation_data( $this, $child_variation_ids );
}
$min_variation_id = $this->min_max_variation_data[ $variation_hash ]['min']['variation_id'];
$max_variation_id = $this->min_max_variation_data[ $variation_hash ]['max']['variation_id'];
// Reorder the variable price arrays to reflect the min and max values so that WooCommerce will find them in the correct order
$min_price = $prices[ $min_variation_id ];
$max_price = $prices[ $max_variation_id ];
unset( $prices[ $min_variation_id ] );
unset( $prices[ $max_variation_id ] );
// Prepend the minimum variation and append the maximum variation
$prices = array( $min_variation_id => $min_price ) + $prices;
$prices += array( $max_variation_id => $max_price );
$this->sorted_variation_prices[ $prices_hash ] = $prices;
}
return $this->sorted_variation_prices[ $prices_hash ];
}
/* Deprecated Functions */
/**
* Return the sign-up fee for this product
*
* @return string
*/
public function get_sign_up_fee() {
wcs_deprecated_function( __METHOD__, '2.2.0', 'WC_Subscriptions_Product::get_sign_up_fee( $this )' );
return WC_Subscriptions_Product::get_sign_up_fee( $this );
}
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_including_tax( $qty = 1 ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_including_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' );
return wcs_get_price_including_tax( $this, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $this ) ) );
}
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @return string
*/
public function get_sign_up_fee_excluding_tax( $qty = 1 ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_excluding_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' );
return wcs_get_price_excluding_tax( $this, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $this ) ) );
}
/**
*
* @param string $product_type A string representation of a product type
*/
public function add_to_cart_handler( $handler, $product ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'WC_Subscriptions_Cart::add_to_cart_handler( $handler, $product )' );
return WC_Subscriptions_Cart::add_to_cart_handler( $handler, $product );
}
/**
* Sync variable product prices with the childs lowest/highest prices.
*
* @access public
* @return void
*/
public function variable_product_sync( $product_id = '' ) {
wcs_deprecated_function( __METHOD__, '2.2,0', 'WC_Subscriptions_Product::variable_subscription_product_sync( $this )' );
if ( empty( $product_id ) ) {
$product_id = $this->get_id();
}
// Sync prices with children
self::sync( $product_id );
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ class WC_Subscriptions_Addresses {
if ( $subscription->needs_shipping_address() && $subscription->has_status( array( 'active', 'on-hold' ) ) ) { if ( $subscription->needs_shipping_address() && $subscription->has_status( array( 'active', 'on-hold' ) ) ) {
$actions['change_address'] = array( $actions['change_address'] = array(
'url' => add_query_arg( array( 'subscription' => $subscription->id ), wc_get_endpoint_url( 'edit-address', 'shipping' ) ), 'url' => add_query_arg( array( 'subscription' => $subscription->get_id() ), wc_get_endpoint_url( 'edit-address', 'shipping' ) ),
'name' => __( 'Change Address', 'woocommerce-subscriptions' ), 'name' => __( 'Change Address', 'woocommerce-subscriptions' ),
); );
} }
@@ -154,7 +154,8 @@ class WC_Subscriptions_Addresses {
$subscription = wcs_get_subscription( absint( $_GET['subscription'] ) ); $subscription = wcs_get_subscription( absint( $_GET['subscription'] ) );
foreach ( array_keys( $address ) as $key ) { foreach ( array_keys( $address ) as $key ) {
$address[ $key ]['value'] = $subscription->$key; $function_name = 'get_' . $key;
$address[ $key ]['value'] = $subscription->$function_name();
} }
} }

View File

@@ -120,6 +120,8 @@ class WC_Subscriptions_Cart {
// WooCommerce determines if free shipping is available using the WC->cart total and coupons, we need to recalculate its availability when obtaining shipping methods for a recurring cart // WooCommerce determines if free shipping is available using the WC->cart total and coupons, we need to recalculate its availability when obtaining shipping methods for a recurring cart
add_filter( 'woocommerce_shipping_free_shipping_is_available', __CLASS__ . '::maybe_recalculate_shipping_method_availability', 10, 2 ); add_filter( 'woocommerce_shipping_free_shipping_is_available', __CLASS__ . '::maybe_recalculate_shipping_method_availability', 10, 2 );
add_filter( 'woocommerce_add_to_cart_handler', __CLASS__ . '::add_to_cart_handler', 10, 2 );
} }
/** /**
@@ -140,7 +142,12 @@ class WC_Subscriptions_Cart {
} }
// Set which price should be used for calculation // Set which price should be used for calculation
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
add_filter( 'woocommerce_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100, 2 ); add_filter( 'woocommerce_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100, 2 );
} else {
add_filter( 'woocommerce_product_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100, 2 );
add_filter( 'woocommerce_product_variation_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100, 2 );
}
} }
/** /**
@@ -150,7 +157,34 @@ class WC_Subscriptions_Cart {
* @since 1.2 * @since 1.2
*/ */
public static function remove_calculation_price_filter() { public static function remove_calculation_price_filter() {
remove_filter( 'woocommerce_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100, 2 ); if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
remove_filter( 'woocommerce_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100 );
} else {
remove_filter( 'woocommerce_product_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100 );
remove_filter( 'woocommerce_product_variation_get_price', __CLASS__ . '::set_subscription_prices_for_calculation', 100 );
}
}
/**
* Use WC core add-to-cart handlers for subscription products.
*
* @param string $handler The name of the handler to use when adding product to the cart
* @param WC_Product $product
*/
public static function add_to_cart_handler( $handler, $product ) {
if ( WC_Subscriptions_Product::is_subscription( $product ) ) {
switch ( $handler ) {
case 'variable-subscription' :
$handler = 'variable';
break;
case 'subscription' :
$handler = 'simple';
break;
}
}
return $handler;
} }
/** /**
@@ -542,8 +576,7 @@ class WC_Subscriptions_Cart {
$all_items_have_free_trial = false; $all_items_have_free_trial = false;
break; break;
} else { } else {
$trial_length = ( isset( $cart_item['data']->subscription_trial_length ) ) ? $cart_item['data']->subscription_trial_length : WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ); if ( 0 == WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) ) {
if ( 0 == $trial_length ) {
$all_items_have_free_trial = false; $all_items_have_free_trial = false;
break; break;
} }
@@ -569,7 +602,7 @@ class WC_Subscriptions_Cart {
if ( self::cart_contains_subscription() ) { if ( self::cart_contains_subscription() ) {
foreach ( WC()->cart->cart_contents as $cart_item_key => $values ) { foreach ( WC()->cart->cart_contents as $cart_item_key => $values ) {
$_product = $values['data']; $_product = $values['data'];
if ( WC_Subscriptions_Product::is_subscription( $_product ) && $_product->needs_shipping() && 'yes' !== $_product->subscription_one_time_shipping ) { if ( WC_Subscriptions_Product::is_subscription( $_product ) && $_product->needs_shipping() && false === WC_Subscriptions_Product::needs_one_time_shipping( $_product ) ) {
$cart_contains_subscriptions_needing_shipping = true; $cart_contains_subscriptions_needing_shipping = true;
} }
} }
@@ -590,8 +623,7 @@ class WC_Subscriptions_Cart {
if ( 'none' == self::$calculation_type ) { if ( 'none' == self::$calculation_type ) {
foreach ( $packages as $index => $package ) { foreach ( $packages as $index => $package ) {
foreach ( $package['contents'] as $cart_item_key => $cart_item ) { foreach ( $package['contents'] as $cart_item_key => $cart_item ) {
$trial_length = ( isset( $cart_item['data']->subscription_trial_length ) ) ? $cart_item['data']->subscription_trial_length : WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ); if ( WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) {
if ( $trial_length > 0 ) {
unset( $packages[ $index ]['contents'][ $cart_item_key ] ); unset( $packages[ $index ]['contents'][ $cart_item_key ] );
} }
} }
@@ -603,7 +635,7 @@ class WC_Subscriptions_Cart {
} elseif ( 'recurring_total' == self::$calculation_type ) { } elseif ( 'recurring_total' == self::$calculation_type ) {
foreach ( $packages as $index => $package ) { foreach ( $packages as $index => $package ) {
foreach ( $package['contents'] as $cart_item_key => $cart_item ) { foreach ( $package['contents'] as $cart_item_key => $cart_item ) {
if ( isset( $cart_item['data']->subscription_one_time_shipping ) && 'yes' == $cart_item['data']->subscription_one_time_shipping ) { if ( WC_Subscriptions_Product::needs_one_time_shipping( $cart_item['data'] ) ) {
$packages[ $index ]['contents_cost'] -= $cart_item['line_total']; $packages[ $index ]['contents_cost'] -= $cart_item['line_total'];
unset( $packages[ $index ]['contents'][ $cart_item_key ] ); unset( $packages[ $index ]['contents'][ $cart_item_key ] );
} }
@@ -633,15 +665,22 @@ class WC_Subscriptions_Cart {
if ( WC_Subscriptions_Product::is_subscription( $product ) && ! wcs_cart_contains_renewal() ) { if ( WC_Subscriptions_Product::is_subscription( $product ) && ! wcs_cart_contains_renewal() ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$product_price_filter = 'woocommerce_get_price';
} else {
$product_price_filter = is_a( $product, 'WC_Product_Variation' ) ? 'woocommerce_product_variation_get_price' : 'woocommerce_product_get_price';
}
// Avoid infinite loop // Avoid infinite loop
remove_filter( 'woocommerce_cart_product_subtotal', __CLASS__ . '::get_formatted_product_subtotal', 11, 4 ); remove_filter( 'woocommerce_cart_product_subtotal', __CLASS__ . '::get_formatted_product_subtotal', 11, 4 );
add_filter( 'woocommerce_get_price', 'WC_Subscriptions_Product::get_sign_up_fee_filter', 100, 2 ); add_filter( $product_price_filter, 'WC_Subscriptions_Product::get_sign_up_fee_filter', 100, 2 );
// And get the appropriate sign up fee string // And get the appropriate sign up fee string
$sign_up_fee_string = $cart->get_product_subtotal( $product, $quantity ); $sign_up_fee_string = $cart->get_product_subtotal( $product, $quantity );
remove_filter( 'woocommerce_get_price', 'WC_Subscriptions_Product::get_sign_up_fee_filter', 100, 2 ); remove_filter( $product_price_filter, 'WC_Subscriptions_Product::get_sign_up_fee_filter', 100, 2 );
add_filter( 'woocommerce_cart_product_subtotal', __CLASS__ . '::get_formatted_product_subtotal', 11, 4 ); add_filter( 'woocommerce_cart_product_subtotal', __CLASS__ . '::get_formatted_product_subtotal', 11, 4 );
@@ -701,10 +740,7 @@ class WC_Subscriptions_Cart {
if ( self::cart_contains_subscription() ) { if ( self::cart_contains_subscription() ) {
foreach ( WC()->cart->cart_contents as $cart_item ) { foreach ( WC()->cart->cart_contents as $cart_item ) {
if ( isset( $cart_item['data']->subscription_trial_length ) && $cart_item['data']->subscription_trial_length > 0 ) { if ( WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) {
$cart_contains_free_trial = true;
break;
} elseif ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) && WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) {
$cart_contains_free_trial = true; $cart_contains_free_trial = true;
break; break;
} }
@@ -758,13 +794,9 @@ class WC_Subscriptions_Cart {
continue; continue;
} }
if ( isset( $cart_item['data']->subscription_sign_up_fee ) ) {
$sign_up_fee += $cart_item['data']->subscription_sign_up_fee;
} elseif ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$sign_up_fee += WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] ); $sign_up_fee += WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] );
} }
} }
}
return apply_filters( 'woocommerce_subscriptions_cart_sign_up_fee', $sign_up_fee ); return apply_filters( 'woocommerce_subscriptions_cart_sign_up_fee', $sign_up_fee );
} }
@@ -1411,10 +1443,7 @@ class WC_Subscriptions_Cart {
if ( self::cart_contains_subscription() ) { if ( self::cart_contains_subscription() ) {
foreach ( WC()->cart->cart_contents as $cart_item ) { foreach ( WC()->cart->cart_contents as $cart_item ) {
if ( isset( $cart_item['data']->subscription_period ) ) { if ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$period = $cart_item['data']->subscription_period;
break;
} elseif ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$period = WC_Subscriptions_Product::get_period( $cart_item['data'] ); $period = WC_Subscriptions_Product::get_period( $cart_item['data'] );
break; break;
} }
@@ -1460,10 +1489,7 @@ class WC_Subscriptions_Cart {
if ( self::cart_contains_subscription() ) { if ( self::cart_contains_subscription() ) {
foreach ( WC()->cart->cart_contents as $cart_item ) { foreach ( WC()->cart->cart_contents as $cart_item ) {
if ( isset( $cart_item['data']->subscription_length ) ) { if ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$length = $cart_item['data']->subscription_length;
break;
} elseif ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$length = WC_Subscriptions_Product::get_length( $cart_item['data'] ); $length = WC_Subscriptions_Product::get_length( $cart_item['data'] );
break; break;
} }
@@ -1488,10 +1514,7 @@ class WC_Subscriptions_Cart {
if ( self::cart_contains_subscription() ) { if ( self::cart_contains_subscription() ) {
foreach ( WC()->cart->cart_contents as $cart_item ) { foreach ( WC()->cart->cart_contents as $cart_item ) {
if ( isset( $cart_item['data']->subscription_trial_length ) ) { if ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$trial_length = $cart_item['data']->subscription_trial_length;
break;
} elseif ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$trial_length = WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ); $trial_length = WC_Subscriptions_Product::get_trial_length( $cart_item['data'] );
break; break;
} }
@@ -1517,10 +1540,7 @@ class WC_Subscriptions_Cart {
// Get the original trial period // Get the original trial period
if ( self::cart_contains_subscription() ) { if ( self::cart_contains_subscription() ) {
foreach ( WC()->cart->cart_contents as $cart_item ) { foreach ( WC()->cart->cart_contents as $cart_item ) {
if ( isset( $cart_item['data']->subscription_trial_period ) ) { if ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$trial_period = $cart_item['data']->subscription_trial_period;
break;
} elseif ( WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
$trial_period = WC_Subscriptions_Product::get_trial_period( $cart_item['data'] ); $trial_period = WC_Subscriptions_Product::get_trial_period( $cart_item['data'] );
break; break;
} }

View File

@@ -28,6 +28,9 @@ class WC_Subscriptions_Change_Payment_Gateway {
// Maybe allow for a recurring payment method to be changed // Maybe allow for a recurring payment method to be changed
add_action( 'plugins_loaded', __CLASS__ . '::set_change_payment_method_flag' ); add_action( 'plugins_loaded', __CLASS__ . '::set_change_payment_method_flag' );
// Attach hooks which depend on WooCommerce constants
add_action( 'woocommerce_loaded', __CLASS__ . '::attach_dependant_hooks' );
// Keep a record of any messages or errors that should be displayed // Keep a record of any messages or errors that should be displayed
add_action( 'before_woocommerce_pay', __CLASS__ . '::store_pay_shortcode_mesages', 100 ); add_action( 'before_woocommerce_pay', __CLASS__ . '::store_pay_shortcode_mesages', 100 );
@@ -46,7 +49,6 @@ class WC_Subscriptions_Change_Payment_Gateway {
// If we're changing the payment method, we want to make sure a number of totals return $0 (to prevent payments being processed now) // If we're changing the payment method, we want to make sure a number of totals return $0 (to prevent payments being processed now)
add_filter( 'woocommerce_subscriptions_total_initial_payment', __CLASS__ . '::maybe_zero_total', 11, 2 ); add_filter( 'woocommerce_subscriptions_total_initial_payment', __CLASS__ . '::maybe_zero_total', 11, 2 );
add_filter( 'woocommerce_subscriptions_sign_up_fee', __CLASS__ . '::maybe_zero_total', 11, 2 ); add_filter( 'woocommerce_subscriptions_sign_up_fee', __CLASS__ . '::maybe_zero_total', 11, 2 );
add_filter( 'woocommerce_order_amount_total', __CLASS__ . '::maybe_zero_total', 11, 2 );
// Redirect to My Account page after changing payment method // Redirect to My Account page after changing payment method
add_filter( 'woocommerce_get_return_url', __CLASS__ . '::get_return_url', 11 ); add_filter( 'woocommerce_get_return_url', __CLASS__ . '::get_return_url', 11 );
@@ -64,6 +66,26 @@ class WC_Subscriptions_Change_Payment_Gateway {
add_filter( 'woocommerce_subscription_needs_payment', __CLASS__ . '::maybe_override_needs_payment', 10, 1 ); add_filter( 'woocommerce_subscription_needs_payment', __CLASS__ . '::maybe_override_needs_payment', 10, 1 );
} }
/**
* Attach WooCommerce version dependent hooks
*
* @since 2.2.0
*/
public static function attach_dependant_hooks() {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
// If we're changing the payment method, we want to make sure a number of totals return $0 (to prevent payments being processed now)
add_filter( 'woocommerce_order_amount_total', __CLASS__ . '::maybe_zero_total', 11, 2 );
} else {
// If we're changing the payment method, we want to make sure a number of totals return $0 (to prevent payments being processed now)
add_filter( 'woocommerce_order_get_total', __CLASS__ . '::maybe_zero_total', 11, 2 );
}
}
/** /**
* Set a flag to indicate that the current request is for changing payment. Better than requiring other extensions * Set a flag to indicate that the current request is for changing payment. Better than requiring other extensions
* to check the $_GET global as it allows for the flag to be overridden. * to check the $_GET global as it allows for the flag to be overridden.
@@ -115,7 +137,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
$subscription_key = isset( $_GET['key'] ) ? wc_clean( $_GET['key'] ) : ''; $subscription_key = isset( $_GET['key'] ) ? wc_clean( $_GET['key'] ) : '';
$subscription = wcs_get_subscription( absint( $wp->query_vars['order-pay'] ) ); $subscription = wcs_get_subscription( absint( $wp->query_vars['order-pay'] ) );
if ( $subscription->id == absint( $wp->query_vars['order-pay'] ) && $subscription->order_key == $subscription_key ) { if ( $subscription->get_id() == absint( $wp->query_vars['order-pay'] ) && $subscription->get_order_key() == $subscription_key ) {
?> ?>
<div class="woocommerce"> <div class="woocommerce">
@@ -138,7 +160,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
echo wp_kses_post( sprintf( esc_html__( 'Total: %s', 'woocommerce-subscriptions' ), '<strong>' . $subscription->get_formatted_order_total() . '</strong>' ) ); echo wp_kses_post( sprintf( esc_html__( 'Total: %s', 'woocommerce-subscriptions' ), '<strong>' . $subscription->get_formatted_order_total() . '</strong>' ) );
?> ?>
</li> </li>
<?php if ( $subscription->payment_method_title ) : ?> <?php if ( $subscription->get_payment_method_title() ) : ?>
<li class="method"> <li class="method">
<?php <?php
// translators: placeholder is the display name of the payment method // translators: placeholder is the display name of the payment method
@@ -148,7 +170,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
<?php endif; ?> <?php endif; ?>
</ul> </ul>
<?php do_action( 'woocommerce_receipt_' . $subscription->payment_method, $subscription->id ); ?> <?php do_action( 'woocommerce_receipt_' . $subscription->get_payment_method(), $subscription->get_id() ); ?>
<div class="clear"></div> <div class="clear"></div>
<?php <?php
@@ -192,7 +214,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
WC_Subscriptions::add_notice( __( 'Invalid Subscription.', 'woocommerce-subscriptions' ), 'error' ); WC_Subscriptions::add_notice( __( 'Invalid Subscription.', 'woocommerce-subscriptions' ), 'error' );
} elseif ( ! current_user_can( 'edit_shop_subscription_payment_method', $subscription->id ) ) { } elseif ( ! current_user_can( 'edit_shop_subscription_payment_method', $subscription->get_id() ) ) {
WC_Subscriptions::add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' ); WC_Subscriptions::add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' );
@@ -213,17 +235,24 @@ class WC_Subscriptions_Change_Payment_Gateway {
WC_Subscriptions::add_notice( sprintf( __( 'Choose a new payment method.%s', 'woocommerce-subscriptions' ), $next_payment_string ), 'notice' ); WC_Subscriptions::add_notice( sprintf( __( 'Choose a new payment method.%s', 'woocommerce-subscriptions' ), $next_payment_string ), 'notice' );
WC_Subscriptions::print_notices(); WC_Subscriptions::print_notices();
if ( $subscription->order_key == $_GET['key'] ) { if ( $subscription->get_order_key() == $_GET['key'] ) {
$subscription_billing_country = $subscription->get_billing_country();
$subscription_billing_state = $subscription->get_billing_state();
$subscription_billing_postcode = $subscription->get_billing_postcode();
// Set customer location to order location // Set customer location to order location
if ( $subscription->billing_country ) { if ( $subscription_billing_country ) {
WC()->customer->set_country( $subscription->billing_country ); $setter = is_callable( array( WC()->customer, 'set_billing_country' ) ) ? 'set_billing_country' : 'set_country';
WC()->customer->$setter( $subscription_billing_country );
} }
if ( $subscription->billing_state ) { if ( $subscription_billing_state ) {
WC()->customer->set_state( $subscription->billing_state ); $setter = is_callable( array( WC()->customer, 'set_billing_state' ) ) ? 'set_billing_state' : 'set_state';
WC()->customer->$setter( $subscription_billing_state );
} }
if ( $subscription->billing_postcode ) { if ( $subscription_billing_postcode ) {
WC()->customer->set_postcode( $subscription->billing_postcode ); $setter = is_callable( array( WC()->customer, 'set_billing_postcode' ) ) ? 'set_billing_postcode' : 'set_postcode';
WC()->customer->$setter( $subscription_billing_postcode );
} }
wc_get_template( 'checkout/form-change-payment-method.php', array( 'subscription' => $subscription ), '', plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/' ); wc_get_template( 'checkout/form-change-payment-method.php', array( 'subscription' => $subscription ), '', plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/' );
@@ -255,7 +284,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
if ( $subscription->can_be_updated_to( 'new-payment-method' ) ) { if ( $subscription->can_be_updated_to( 'new-payment-method' ) ) {
$actions['change_payment_method'] = array( $actions['change_payment_method'] = array(
'url' => wp_nonce_url( add_query_arg( array( 'change_payment_method' => $subscription->id ), $subscription->get_checkout_payment_url() ) ), 'url' => wp_nonce_url( add_query_arg( array( 'change_payment_method' => $subscription->get_id() ), $subscription->get_checkout_payment_url() ) ),
'name' => _x( 'Change Payment', 'label on button, imperative', 'woocommerce-subscriptions' ), 'name' => _x( 'Change Payment', 'label on button, imperative', 'woocommerce-subscriptions' ),
); );
@@ -283,20 +312,29 @@ class WC_Subscriptions_Change_Payment_Gateway {
ob_start(); ob_start();
if ( $subscription->order_key == $_GET['key'] ) { if ( $subscription->get_order_key() == $_GET['key'] ) {
$subscription_billing_country = $subscription->get_billing_country();
$subscription_billing_state = $subscription->get_billing_state();
$subscription_billing_postcode = $subscription->get_billing_postcode();
$subscription_billing_city = $subscription->get_billing_postcode();
// Set customer location to order location // Set customer location to order location
if ( $subscription->billing_country ) { if ( $subscription_billing_country ) {
WC()->customer->set_country( $subscription->billing_country ); $setter = is_callable( array( WC()->customer, 'set_billing_country' ) ) ? 'set_billing_country' : 'set_country';
WC()->customer->$setter( $subscription_billing_country );
} }
if ( $subscription->billing_state ) { if ( $subscription_billing_state ) {
WC()->customer->set_state( $subscription->billing_state ); $setter = is_callable( array( WC()->customer, 'set_billing_state' ) ) ? 'set_billing_state' : 'set_state';
WC()->customer->$setter( $subscription_billing_state );
} }
if ( $subscription->billing_postcode ) { if ( $subscription_billing_postcode ) {
WC()->customer->set_postcode( $subscription->billing_postcode ); $setter = is_callable( array( WC()->customer, 'set_billing_postcode' ) ) ? 'set_billing_postcode' : 'set_postcode';
WC()->customer->$setter( $subscription_billing_postcode );
} }
if ( $subscription->billing_city ) { if ( $subscription_billing_city ) {
WC()->customer->set_city( $subscription->billing_city ); $setter = is_callable( array( WC()->customer, 'set_billing_city' ) ) ? 'set_billing_city' : 'set_city';
WC()->customer->$setter( $subscription_billing_city );
} }
// Update payment method // Update payment method
@@ -315,7 +353,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
// Process payment for the new method (with a $0 order total) // Process payment for the new method (with a $0 order total)
if ( wc_notice_count( 'error' ) == 0 ) { if ( wc_notice_count( 'error' ) == 0 ) {
$result = $available_gateways[ $new_payment_method ]->process_payment( $subscription->id ); $result = $available_gateways[ $new_payment_method ]->process_payment( $subscription->get_id() );
if ( 'success' == $result['result'] && wc_get_page_permalink( 'myaccount' ) == $result['redirect'] ) { if ( 'success' == $result['result'] && wc_get_page_permalink( 'myaccount' ) == $result['redirect'] ) {
$result['redirect'] = $subscription->get_view_order_url(); $result['redirect'] = $subscription->get_view_order_url();
@@ -342,8 +380,8 @@ class WC_Subscriptions_Change_Payment_Gateway {
*/ */
public static function update_payment_method( $subscription, $new_payment_method ) { public static function update_payment_method( $subscription, $new_payment_method ) {
$old_payment_method = $subscription->payment_method; $old_payment_method = $subscription->get_payment_method();
$old_payment_method_title = $subscription->payment_method_title; $old_payment_method_title = $subscription->get_payment_method_title();
$available_gateways = WC()->payment_gateways->get_available_payment_gateways(); // Also inits all payment gateways to make sure that hooks are attached correctly $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); // Also inits all payment gateways to make sure that hooks are attached correctly
do_action( 'woocommerce_subscriptions_pre_update_payment_method', $subscription, $new_payment_method, $old_payment_method ); do_action( 'woocommerce_subscriptions_pre_update_payment_method', $subscription, $new_payment_method, $old_payment_method );
@@ -352,8 +390,8 @@ class WC_Subscriptions_Change_Payment_Gateway {
WC_Subscriptions_Payment_Gateways::trigger_gateway_status_updated_hook( $subscription, 'cancelled' ); WC_Subscriptions_Payment_Gateways::trigger_gateway_status_updated_hook( $subscription, 'cancelled' );
// Update meta // Update meta
update_post_meta( $subscription->id, '_old_payment_method', $old_payment_method ); update_post_meta( $subscription->get_id(), '_old_payment_method', $old_payment_method );
update_post_meta( $subscription->id, '_payment_method', $new_payment_method ); update_post_meta( $subscription->get_id(), '_payment_method', $new_payment_method );
if ( isset( $available_gateways[ $new_payment_method ] ) ) { if ( isset( $available_gateways[ $new_payment_method ] ) ) {
$new_payment_method_title = $available_gateways[ $new_payment_method ]->get_title(); $new_payment_method_title = $available_gateways[ $new_payment_method ]->get_title();
@@ -361,8 +399,8 @@ class WC_Subscriptions_Change_Payment_Gateway {
$new_payment_method_title = ''; $new_payment_method_title = '';
} }
update_post_meta( $subscription->id, '_old_payment_method_title', $old_payment_method_title ); update_post_meta( $subscription->get_id(), '_old_payment_method_title', $old_payment_method_title );
update_post_meta( $subscription->id, '_payment_method_title', $new_payment_method_title ); update_post_meta( $subscription->get_id(), '_payment_method_title', $new_payment_method_title );
if ( empty( $old_payment_method_title ) ) { if ( empty( $old_payment_method_title ) ) {
$old_payment_method_title = $old_payment_method; $old_payment_method_title = $old_payment_method;
@@ -408,7 +446,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
public static function maybe_zero_total( $total, $subscription ) { public static function maybe_zero_total( $total, $subscription ) {
global $wp; global $wp;
if ( ! empty( $_POST['_wcsnonce'] ) && wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_change_payment_method' ) && isset( $_POST['woocommerce_change_payment'] ) && $subscription->order_key == $_GET['key'] && $subscription->id == absint( $_POST['woocommerce_change_payment'] ) ) { if ( ! empty( $_POST['_wcsnonce'] ) && wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_change_payment_method' ) && isset( $_POST['woocommerce_change_payment'] ) && $subscription->get_order_key() == $_GET['key'] && $subscription->get_id() == absint( $_POST['woocommerce_change_payment'] ) ) {
$total = 0; $total = 0;
} elseif ( ! self::$is_request_to_change_payment && isset( $wp->query_vars['order-pay'] ) && wcs_is_subscription( absint( $wp->query_vars['order-pay'] ) ) ) { } elseif ( ! self::$is_request_to_change_payment && isset( $wp->query_vars['order-pay'] ) && wcs_is_subscription( absint( $wp->query_vars['order-pay'] ) ) ) {
// if the request to pay for the order belongs to a subscription but there's no GET params for changing payment method, the receipt page is being used to collect credit card details so we still need to $0 the total // if the request to pay for the order belongs to a subscription but there's no GET params for changing payment method, the receipt page is being used to collect credit card details so we still need to $0 the total
@@ -449,7 +487,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
if ( ! empty( $_POST['_wcsnonce'] ) && wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_change_payment_method' ) && isset( $_POST['payment_method'] ) ) { if ( ! empty( $_POST['_wcsnonce'] ) && wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_change_payment_method' ) && isset( $_POST['payment_method'] ) ) {
$new_payment_method = wc_clean( $_POST['payment_method'] ); $new_payment_method = wc_clean( $_POST['payment_method'] );
} else { } else {
$new_payment_method = $renewal_order->payment_method; $new_payment_method = wcs_get_objects_property( $renewal_order, 'payment_method' );
} }
self::update_payment_method( $subscription, $new_payment_method ); self::update_payment_method( $subscription, $new_payment_method );

View File

@@ -40,6 +40,9 @@ class WC_Subscriptions_Checkout {
// Force registration during checkout process // Force registration during checkout process
add_action( 'woocommerce_before_checkout_process', __CLASS__ . '::force_registration_during_checkout', 10 ); add_action( 'woocommerce_before_checkout_process', __CLASS__ . '::force_registration_during_checkout', 10 );
// When a line item is added to a subscription on checkout, ensure the backorder data added by WC is removed
add_action( 'woocommerce_checkout_create_order_line_item', __CLASS__ . '::remove_backorder_meta_from_subscription_line_item', 10, 4 );
} }
/** /**
@@ -60,12 +63,12 @@ class WC_Subscriptions_Checkout {
$subscriptions = array(); $subscriptions = array();
// First clear out any subscriptions created for a failed payment to give us a clean slate for creating new subscriptions // First clear out any subscriptions created for a failed payment to give us a clean slate for creating new subscriptions
$subscriptions = wcs_get_subscriptions_for_order( $order->id, array( 'order_type' => 'parent' ) ); $subscriptions = wcs_get_subscriptions_for_order( wcs_get_objects_property( $order, 'id' ), array( 'order_type' => 'parent' ) );
if ( ! empty( $subscriptions ) ) { if ( ! empty( $subscriptions ) ) {
remove_action( 'before_delete_post', 'WC_Subscriptions_Manager::maybe_cancel_subscription' ); remove_action( 'before_delete_post', 'WC_Subscriptions_Manager::maybe_cancel_subscription' );
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
wp_delete_post( $subscription->id ); wp_delete_post( $subscription->get_id() );
} }
add_action( 'before_delete_post', 'WC_Subscriptions_Manager::maybe_cancel_subscription' ); add_action( 'before_delete_post', 'WC_Subscriptions_Manager::maybe_cancel_subscription' );
} }
@@ -75,7 +78,7 @@ class WC_Subscriptions_Checkout {
// Create new subscriptions for each group of subscription products in the cart (that is not a renewal) // Create new subscriptions for each group of subscription products in the cart (that is not a renewal)
foreach ( WC()->cart->recurring_carts as $recurring_cart ) { foreach ( WC()->cart->recurring_carts as $recurring_cart ) {
$subscription = self::create_subscription( $order, $recurring_cart ); // Exceptions are caught by WooCommerce $subscription = self::create_subscription( $order, $recurring_cart, $posted_data ); // Exceptions are caught by WooCommerce
if ( is_wp_error( $subscription ) ) { if ( is_wp_error( $subscription ) ) {
throw new Exception( $subscription->get_error_message() ); throw new Exception( $subscription->get_error_message() );
@@ -97,7 +100,7 @@ class WC_Subscriptions_Checkout {
* @param WC_Cart $cart * @param WC_Cart $cart
* @since 2.0 * @since 2.0
*/ */
public static function create_subscription( $order, $cart ) { public static function create_subscription( $order, $cart, $posted_data ) {
global $wpdb; global $wpdb;
try { try {
@@ -108,16 +111,13 @@ class WC_Subscriptions_Checkout {
$variation_id = wcs_cart_pluck( $cart, 'variation_id' ); $variation_id = wcs_cart_pluck( $cart, 'variation_id' );
$product_id = empty( $variation_id ) ? wcs_cart_pluck( $cart, 'product_id' ) : $variation_id; $product_id = empty( $variation_id ) ? wcs_cart_pluck( $cart, 'product_id' ) : $variation_id;
// We need to use the $order->order_date value because the post_date_gmt isn't always set
$order_date_gmt = get_gmt_from_date( $order->order_date );
$subscription = wcs_create_subscription( array( $subscription = wcs_create_subscription( array(
'start_date' => $cart->start_date, 'start_date' => $cart->start_date,
'order_id' => $order->id, 'order_id' => wcs_get_objects_property( $order, 'id' ),
'customer_id' => $order->get_user_id(), 'customer_id' => $order->get_user_id(),
'billing_period' => wcs_cart_pluck( $cart, 'subscription_period' ), 'billing_period' => wcs_cart_pluck( $cart, 'subscription_period' ),
'billing_interval' => wcs_cart_pluck( $cart, 'subscription_period_interval' ), 'billing_interval' => wcs_cart_pluck( $cart, 'subscription_period_interval' ),
'customer_note' => $order->customer_note, 'customer_note' => wcs_get_objects_property( $order, 'customer_note' ),
) ); ) );
if ( is_wp_error( $subscription ) ) { if ( is_wp_error( $subscription ) ) {
@@ -135,30 +135,38 @@ class WC_Subscriptions_Checkout {
// Store trial period for PayPal // Store trial period for PayPal
if ( wcs_cart_pluck( $cart, 'subscription_trial_length' ) > 0 ) { if ( wcs_cart_pluck( $cart, 'subscription_trial_length' ) > 0 ) {
update_post_meta( $subscription->id, '_trial_period', wcs_cart_pluck( $cart, 'subscription_trial_period' ) ); $subscription->set_trial_period( wcs_cart_pluck( $cart, 'subscription_trial_period' ) );
} }
// Set the payment method on the subscription // Set the payment method on the subscription
$available_gateways = WC()->payment_gateways->get_available_payment_gateways(); $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$order_payment_method = wcs_get_objects_property( $order, 'payment_method' );
if ( $cart->needs_payment() && isset( $available_gateways[ $order->payment_method ] ) ) { if ( $cart->needs_payment() && isset( $available_gateways[ $order_payment_method ] ) ) {
$subscription->set_payment_method( $available_gateways[ $order->payment_method ] ); $subscription->set_payment_method( $available_gateways[ $order_payment_method ] );
} }
if ( ! $cart->needs_payment() || 'yes' == get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) ) { if ( ! $cart->needs_payment() || 'yes' == get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) ) {
$subscription->update_manual( 'true' ); $subscription->set_requires_manual_renewal( true );
} elseif ( ! isset( $available_gateways[ $order->payment_method ] ) || ! $available_gateways[ $order->payment_method ]->supports( 'subscriptions' ) ) { } elseif ( ! isset( $available_gateways[ $order_payment_method ] ) || ! $available_gateways[ $order_payment_method ]->supports( 'subscriptions' ) ) {
$subscription->update_manual( 'true' ); $subscription->set_requires_manual_renewal( true );
} }
wcs_copy_order_meta( $order, $subscription, 'subscription' ); wcs_copy_order_meta( $order, $subscription, 'subscription' );
// Store the line items // Store the line items
if ( is_callable( array( WC()->checkout, 'create_order_line_items' ) ) ) {
WC()->checkout->create_order_line_items( $subscription, $cart );
} else {
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$item_id = self::add_cart_item( $subscription, $cart_item, $cart_item_key ); $item_id = self::add_cart_item( $subscription, $cart_item, $cart_item_key );
} }
}
// Store fees (although no fees recur by default, extensions may add them) // Store fees (although no fees recur by default, extensions may add them)
if ( is_callable( array( WC()->checkout, 'create_order_fee_lines' ) ) ) {
WC()->checkout->create_order_fee_lines( $subscription, $cart );
} else {
foreach ( $cart->get_fees() as $fee_key => $fee ) { foreach ( $cart->get_fees() as $fee_key => $fee ) {
$item_id = $subscription->add_fee( $fee ); $item_id = $subscription->add_fee( $fee );
@@ -168,35 +176,50 @@ class WC_Subscriptions_Checkout {
} }
// Allow plugins to add order item meta to fees // Allow plugins to add order item meta to fees
do_action( 'woocommerce_add_order_fee_meta', $order->id, $item_id, $fee, $fee_key ); do_action( 'woocommerce_add_order_fee_meta', $subscription->get_id(), $item_id, $fee, $fee_key );
}
} }
self::add_shipping( $subscription, $cart ); self::add_shipping( $subscription, $cart );
// Store tax rows // Store tax rows
if ( is_callable( array( WC()->checkout, 'create_order_tax_lines' ) ) ) {
WC()->checkout->create_order_tax_lines( $subscription, $cart );
} else {
foreach ( array_keys( $cart->taxes + $cart->shipping_taxes ) as $tax_rate_id ) { foreach ( array_keys( $cart->taxes + $cart->shipping_taxes ) as $tax_rate_id ) {
if ( $tax_rate_id && ! $subscription->add_tax( $tax_rate_id, $cart->get_tax_amount( $tax_rate_id ), $cart->get_shipping_tax_amount( $tax_rate_id ) ) && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) { if ( $tax_rate_id && ! $subscription->add_tax( $tax_rate_id, $cart->get_tax_amount( $tax_rate_id ), $cart->get_shipping_tax_amount( $tax_rate_id ) ) && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) {
// translators: placeholder is an internal error number // translators: placeholder is an internal error number
throw new Exception( sprintf( __( 'Error %d: Unable to add tax to subscription. Please try again.', 'woocommerce-subscriptions' ), 405 ) ); throw new Exception( sprintf( __( 'Error %d: Unable to add tax to subscription. Please try again.', 'woocommerce-subscriptions' ), 405 ) );
} }
} }
}
// Store coupons // Store coupons
if ( is_callable( array( WC()->checkout, 'create_order_coupon_lines' ) ) ) {
WC()->checkout->create_order_coupon_lines( $subscription, $cart );
} else {
foreach ( $cart->get_coupons() as $code => $coupon ) { foreach ( $cart->get_coupons() as $code => $coupon ) {
if ( ! $subscription->add_coupon( $code, $cart->get_coupon_discount_amount( $code ), $cart->get_coupon_discount_tax_amount( $code ) ) ) { if ( ! $subscription->add_coupon( $code, $cart->get_coupon_discount_amount( $code ), $cart->get_coupon_discount_tax_amount( $code ) ) ) {
// translators: placeholder is an internal error number // translators: placeholder is an internal error number
throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-subscriptions' ), 406 ) ); throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-subscriptions' ), 406 ) );
} }
} }
}
// Set the recurring totals on the subscription // Set the recurring totals on the subscription
$subscription->set_total( $cart->shipping_total, 'shipping' ); $subscription->set_shipping_total( $cart->shipping_total );
$subscription->set_total( $cart->get_cart_discount_total(), 'cart_discount' ); $subscription->set_discount_total( $cart->get_cart_discount_total() );
$subscription->set_total( $cart->get_cart_discount_tax_total(), 'cart_discount_tax' ); $subscription->set_discount_tax( $cart->get_cart_discount_tax_total() );
$subscription->set_total( $cart->tax_total, 'tax' ); $subscription->set_cart_tax( $cart->tax_total );
$subscription->set_total( $cart->shipping_tax_total, 'shipping_tax' ); $subscription->set_shipping_tax( $cart->shipping_tax_total );
$subscription->set_total( $cart->total ); $subscription->set_total( $cart->total );
// Hook to adjust subscriptions before saving with WC 3.0+ (matches WC 3.0's new 'woocommerce_checkout_create_order' hook)
do_action( 'woocommerce_checkout_create_subscription', $subscription, $posted_data );
// Save the subscription if using WC 3.0 & CRUD
$subscription->save();
// If we got here, the subscription was created without problems // If we got here, the subscription was created without problems
$wpdb->query( 'COMMIT' ); $wpdb->query( 'COMMIT' );
@@ -238,27 +261,71 @@ class WC_Subscriptions_Checkout {
if ( isset( $package['rates'][ $shipping_method_id ] ) ) { if ( isset( $package['rates'][ $shipping_method_id ] ) ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$item_id = $subscription->add_shipping( $package['rates'][ $shipping_method_id ] ); $item_id = $subscription->add_shipping( $package['rates'][ $shipping_method_id ] );
if ( ! $item_id ) { // Allows plugins to add order item meta to shipping
throw new Exception( __( 'Error: Unable to create subscription. Please try again.', 'woocommerce-subscriptions' ) ); do_action( 'woocommerce_add_shipping_order_item', $subscription->get_id(), $item_id, $package_key );
do_action( 'woocommerce_subscriptions_add_recurring_shipping_order_item', $subscription->get_id(), $item_id, $package_key );
} else { // WC 3.0+
$shipping_rate = $package['rates'][ $shipping_method_id ];
$item = new WC_Order_Item_Shipping();
$item->legacy_package_key = $package_key; // @deprecated For legacy actions.
$item->set_props( array(
'method_title' => $shipping_rate->label,
'method_id' => $shipping_rate->id,
'total' => wc_format_decimal( $shipping_rate->cost ),
'taxes' => array( 'total' => $shipping_rate->taxes ),
'order_id' => $subscription->get_id(),
) );
foreach ( $shipping_rate->get_meta_data() as $key => $value ) {
$item->add_meta_data( $key, $value, true );
} }
// Allows plugins to add order item meta to shipping $subscription->add_item( $item );
do_action( 'woocommerce_add_shipping_order_item', $subscription->id, $item_id, $package_key );
do_action( 'woocommerce_subscriptions_add_recurring_shipping_order_item', $subscription->id, $item_id, $package_key ); $item->save(); // We need the item ID for old hooks, this can be removed once support for WC < 3.0 is dropped
wc_do_deprecated_action( 'woocommerce_subscriptions_add_recurring_shipping_order_item', array( $subscription->get_id(), $item->get_id(), $package_key ), '2.2.0', 'CRUD and woocommerce_checkout_create_subscription_shipping_item action instead' );
do_action( 'woocommerce_checkout_create_order_shipping_item', $item, $package_key, $package ); // WC 3.0+ will also trigger the deprecated 'woocommerce_add_shipping_order_item' hook
do_action( 'woocommerce_checkout_create_subscription_shipping_item', $item, $package_key, $package );
}
} }
} }
WC_Subscriptions_Cart::set_calculation_type( 'none' ); WC_Subscriptions_Cart::set_calculation_type( 'none' );
} }
/**
* Remove the Backordered meta data from subscription line items added on the checkout.
*
* @param WC_Order_Item_Product $order_item
* @param string $cart_item_key The hash used to identify the item in the cart
* @param array $cart_item The cart item's data.
* @param WC_Order|WC_Subscription $subscription The order or subscription object to which the line item relates
* @since 2.2.0
*/
public static function remove_backorder_meta_from_subscription_line_item( $item, $cart_item_key, $cart_item, $subscription ) {
if ( wcs_is_subscription( $subscription ) ) {
$item->delete_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce-subscriptions' ) ) );
}
}
/** /**
* Add a cart item to a subscription. * Add a cart item to a subscription.
* *
* @since 2.0 * @since 2.0
*/ */
public static function add_cart_item( $subscription, $cart_item, $cart_item_key ) { public static function add_cart_item( $subscription, $cart_item, $cart_item_key ) {
if ( ! WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
_deprecated_function( __METHOD__, '2.2.0', 'WC_Checkout::create_order_line_items( $subscription, $cart )' );
}
$item_id = $subscription->add_product( $item_id = $subscription->add_product(
$cart_item['data'], $cart_item['data'],
$cart_item['quantity'], $cart_item['quantity'],
@@ -286,9 +353,11 @@ class WC_Subscriptions_Checkout {
} }
// Allow plugins to add order item meta // Allow plugins to add order item meta
do_action( 'woocommerce_add_order_item_meta', $item_id, $cart_item, $cart_item_key ); if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
do_action( 'woocommerce_add_subscription_item_meta', $item_id, $cart_item, $cart_item_key ); do_action( 'woocommerce_add_subscription_item_meta', $item_id, $cart_item, $cart_item_key );
} else {
wc_do_deprecated_action( 'woocommerce_add_subscription_item_meta', array( $item_id, $cart_item, $cart_item_key ), '3.0', 'CRUD and woocommerce_checkout_create_order_line_item action instead' );
}
return $item_id; return $item_id;
} }

View File

@@ -43,6 +43,11 @@ class WC_Subscriptions_Coupon {
// Add our recurring product coupon types to the list of coupon types that apply to individual products // Add our recurring product coupon types to the list of coupon types that apply to individual products
add_filter( 'woocommerce_product_coupon_types', __CLASS__ . '::filter_product_coupon_types', 10, 1 ); add_filter( 'woocommerce_product_coupon_types', __CLASS__ . '::filter_product_coupon_types', 10, 1 );
if ( ! is_admin() ) {
// WC 3.0 only sets a coupon type if it is a pre-defined supported type, so we need to temporarily add our pseudo types. We don't want to add these on admin pages.
add_filter( 'woocommerce_coupon_discount_types', __CLASS__ . '::add_pseudo_coupon_types' );
}
} }
/** /**
@@ -70,19 +75,19 @@ class WC_Subscriptions_Coupon {
*/ */
public static function get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $coupon ) { public static function get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
$coupon_type = wcs_get_coupon_property( $coupon, 'type' );
// Only deal with subscriptions coupon types // Only deal with subscriptions coupon types
if ( ! in_array( $coupon->type, array( 'recurring_fee', 'recurring_percent', 'sign_up_fee', 'sign_up_fee_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) { if ( ! in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent', 'sign_up_fee', 'sign_up_fee_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) {
return $discount; return $discount;
} }
$product_id = ( $cart_item['data']->is_type( array( 'subscription_variation' ) ) ) ? $cart_item['data']->variation_id : $cart_item['data']->id;
// If not a subscription product return the default discount // If not a subscription product return the default discount
if ( ! wcs_cart_contains_renewal() && ! WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) { if ( ! wcs_cart_contains_renewal() && ! WC_Subscriptions_Product::is_subscription( $cart_item['data'] ) ) {
return $discount; return $discount;
} }
// But if cart contains a renewal, we need to handle both subscription products and manually added non-susbscription products that could be part of a subscription // But if cart contains a renewal, we need to handle both subscription products and manually added non-susbscription products that could be part of a subscription
if ( wcs_cart_contains_renewal() && ! self::is_subsbcription_renewal_line_item( $product_id, $cart_item ) ) { if ( wcs_cart_contains_renewal() && ! self::is_subsbcription_renewal_line_item( $cart_item['data'], $cart_item ) ) {
return $discount; return $discount;
} }
@@ -100,8 +105,8 @@ class WC_Subscriptions_Coupon {
// Check if we're applying any recurring discounts to recurring total calculations // Check if we're applying any recurring discounts to recurring total calculations
if ( 'recurring_total' == $calculation_type ) { if ( 'recurring_total' == $calculation_type ) {
$apply_recurring_coupon = ( 'recurring_fee' == $coupon->type ) ? true : false; $apply_recurring_coupon = ( 'recurring_fee' == $coupon_type ) ? true : false;
$apply_recurring_percent_coupon = ( 'recurring_percent' == $coupon->type ) ? true : false; $apply_recurring_percent_coupon = ( 'recurring_percent' == $coupon_type ) ? true : false;
} }
// Check if we're applying any initial discounts // Check if we're applying any initial discounts
@@ -110,42 +115,42 @@ class WC_Subscriptions_Coupon {
// If all items have a free trial we don't need to apply recurring coupons to the initial total // 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 ( ! WC_Subscriptions_Cart::all_cart_items_have_free_trial() ) {
if ( 'recurring_fee' == $coupon->type ) { if ( 'recurring_fee' == $coupon_type ) {
$apply_initial_coupon = true; $apply_initial_coupon = true;
} }
if ( 'recurring_percent' == $coupon->type ) { if ( 'recurring_percent' == $coupon_type ) {
$apply_initial_percent_coupon = true; $apply_initial_percent_coupon = true;
} }
} }
// Apply sign-up discounts // Apply sign-up discounts
if ( ! empty( $cart_item['data']->subscription_sign_up_fee ) ) { if ( WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] ) > 0 ) {
if ( 'sign_up_fee' == $coupon->type ) { if ( 'sign_up_fee' == $coupon_type ) {
$apply_initial_coupon = true; $apply_initial_coupon = true;
} }
if ( 'sign_up_fee_percent' == $coupon->type ) { if ( 'sign_up_fee_percent' == $coupon_type ) {
$apply_initial_percent_coupon = true; $apply_initial_percent_coupon = true;
} }
// Only Sign up fee coupons apply to sign up fees, adjust the discounting_amount accordingly // Only Sign up fee coupons apply to sign up fees, adjust the discounting_amount accordingly
if ( in_array( $coupon->type, array( 'sign_up_fee', 'sign_up_fee_percent' ) ) ) { if ( in_array( $coupon_type, array( 'sign_up_fee', 'sign_up_fee_percent' ) ) ) {
$discounting_amount = $cart_item['data']->subscription_sign_up_fee; $discounting_amount = WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] );
} else { } else {
$discounting_amount -= $cart_item['data']->subscription_sign_up_fee; $discounting_amount -= WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] );
} }
} }
// Apply renewal discounts // Apply renewal discounts
if ( 'renewal_fee' == $coupon->type ) { if ( 'renewal_fee' == $coupon_type ) {
$apply_recurring_coupon = true; $apply_recurring_coupon = true;
} }
if ( 'renewal_percent' == $coupon->type ) { if ( 'renewal_percent' == $coupon_type ) {
$apply_recurring_percent_coupon = true; $apply_recurring_percent_coupon = true;
} }
if ( 'renewal_cart' == $coupon->type ) { if ( 'renewal_cart' == $coupon_type ) {
$apply_renewal_cart_coupon = true; $apply_renewal_cart_coupon = true;
} }
} }
@@ -154,25 +159,25 @@ class WC_Subscriptions_Coupon {
if ( $apply_recurring_coupon || $apply_initial_coupon ) { if ( $apply_recurring_coupon || $apply_initial_coupon ) {
// Recurring coupons only apply when there is no free trial (carts can have a mix of free trial and non free trial items) // 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 ) ) { if ( $apply_initial_coupon && 'recurring_fee' == $coupon_type && WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) {
$discounting_amount = 0; $discounting_amount = 0;
} }
$discount_amount = min( $coupon->coupon_amount, $discounting_amount ); $discount_amount = min( wcs_get_coupon_property( $coupon, 'amount' ), $discounting_amount );
$discount_amount = $single ? $discount_amount : $discount_amount * $cart_item_qty; $discount_amount = $single ? $discount_amount : $discount_amount * $cart_item_qty;
} elseif ( $apply_recurring_percent_coupon ) { } elseif ( $apply_recurring_percent_coupon ) {
$discount_amount = ( $discounting_amount / 100 ) * $coupon->amount; $discount_amount = ( $discounting_amount / 100 ) * wcs_get_coupon_property( $coupon, 'amount' );
} elseif ( $apply_initial_percent_coupon ) { } 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) // 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 ) ) { if ( 'recurring_percent' == $coupon_type && WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) {
$discounting_amount = 0; $discounting_amount = 0;
} }
$discount_amount = ( $discounting_amount / 100 ) * $coupon->amount; $discount_amount = ( $discounting_amount / 100 ) * wcs_get_coupon_property( $coupon, 'amount' );
} elseif ( $apply_renewal_cart_coupon ) { } elseif ( $apply_renewal_cart_coupon ) {
@@ -182,9 +187,9 @@ class WC_Subscriptions_Coupon {
* *
* BUT... we also need the subtotal to exclude non renewal products, so user the renewal subtotal * BUT... we also need the subtotal to exclude non renewal products, so user the renewal subtotal
*/ */
$discount_percent = ( $discounting_amount * $cart_item['quantity'] ) / self::get_renewal_subtotal( $coupon->code ); $discount_percent = ( $discounting_amount * $cart_item['quantity'] ) / self::get_renewal_subtotal( wcs_get_coupon_property( $coupon, 'code' ) );
$discount_amount = ( $coupon->amount * $discount_percent ) / $cart_item_qty; $discount_amount = ( wcs_get_coupon_property( $coupon, 'amount' ) * $discount_percent ) / $cart_item_qty;
} }
// Round - consistent with WC approach // Round - consistent with WC approach
@@ -217,8 +222,9 @@ class WC_Subscriptions_Coupon {
foreach ( WC()->cart->applied_coupons as $code ) { foreach ( WC()->cart->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code ); $coupon = new WC_Coupon( $code );
$cart_coupon_type = wcs_get_coupon_property( $coupon, 'type' );
if ( 'any' == $coupon_type || $coupon_type == $coupon->type || ( 'core' == $coupon_type && in_array( $coupon->type, $core_coupons ) ) ) { if ( 'any' == $coupon_type || $coupon_type == $cart_coupon_type || ( 'core' == $coupon_type && in_array( $cart_coupon_type, $core_coupons ) ) ) {
$contains_discount = true; $contains_discount = true;
break; break;
} }
@@ -240,9 +246,10 @@ class WC_Subscriptions_Coupon {
} }
self::$coupon_error = ''; self::$coupon_error = '';
$coupon_type = wcs_get_coupon_property( $coupon, 'type' );
// ignore non-subscription coupons // ignore non-subscription coupons
if ( ! in_array( $coupon->type, array( 'recurring_fee', 'sign_up_fee', 'recurring_percent', 'sign_up_fee_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) { if ( ! in_array( $coupon_type, array( 'recurring_fee', 'sign_up_fee', 'recurring_percent', 'sign_up_fee_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) {
// but make sure there is actually something for the coupon to be applied to (i.e. not a free trial) // but make sure there is actually something for the coupon to be applied to (i.e. not a free trial)
if ( ( wcs_cart_contains_renewal() || WC_Subscriptions_Cart::cart_contains_subscription() ) && 0 == WC()->cart->subtotal ) { if ( ( wcs_cart_contains_renewal() || WC_Subscriptions_Cart::cart_contains_subscription() ) && 0 == WC()->cart->subtotal ) {
@@ -251,7 +258,7 @@ class WC_Subscriptions_Coupon {
} else { } else {
// prevent subscription coupons from being applied to renewal payments // prevent subscription coupons from being applied to renewal payments
if ( wcs_cart_contains_renewal() && ! in_array( $coupon->type, array( 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) { if ( wcs_cart_contains_renewal() && ! in_array( $coupon_type, array( 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) {
self::$coupon_error = __( 'Sorry, this coupon is only valid for new subscriptions.', 'woocommerce-subscriptions' ); self::$coupon_error = __( 'Sorry, this coupon is only valid for new subscriptions.', 'woocommerce-subscriptions' );
} }
@@ -261,13 +268,13 @@ class WC_Subscriptions_Coupon {
} }
// prevent subscription renewal coupons from being applied to non renewal payments // prevent subscription renewal coupons from being applied to non renewal payments
if ( ! wcs_cart_contains_renewal() && in_array( $coupon->type, array( 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) { if ( ! wcs_cart_contains_renewal() && in_array( $coupon_type, array( 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) {
// translators: 1$: coupon code that is being removed // translators: 1$: coupon code that is being removed
self::$coupon_error = sprintf( __( 'Sorry, the "%1$s" coupon is only valid for renewals.', 'woocommerce-subscriptions' ), $coupon->code ); self::$coupon_error = sprintf( __( 'Sorry, the "%1$s" coupon is only valid for renewals.', 'woocommerce-subscriptions' ), wcs_get_coupon_property( $coupon, 'code' ) );
} }
// prevent sign up fee coupons from being applied to subscriptions without a sign up fee // prevent sign up fee coupons from being applied to subscriptions without a sign up fee
if ( 0 == WC_Subscriptions_Cart::get_cart_subscription_sign_up_fee() && in_array( $coupon->type, array( 'sign_up_fee', 'sign_up_fee_percent' ) ) ) { if ( 0 == WC_Subscriptions_Cart::get_cart_subscription_sign_up_fee() && in_array( $coupon_type, array( 'sign_up_fee', 'sign_up_fee_percent' ) ) ) {
self::$coupon_error = __( 'Sorry, this coupon is only valid for subscription products with a sign-up fee.', 'woocommerce-subscriptions' ); self::$coupon_error = __( 'Sorry, this coupon is only valid for subscription products with a sign-up fee.', 'woocommerce-subscriptions' );
} }
} }
@@ -307,16 +314,16 @@ class WC_Subscriptions_Coupon {
$this_item_is_discounted = false; $this_item_is_discounted = false;
// Specific products get the discount // Specific products get the discount
if ( sizeof( $coupon->product_ids ) > 0 ) { if ( sizeof( $coupon_product_ids = wcs_get_coupon_property( $coupon, 'product_ids' ) ) > 0 ) {
if ( in_array( wcs_get_canonical_product_id( $cart_item ), $coupon->product_ids ) || in_array( $cart_item['data']->get_parent(), $coupon->product_ids ) ) { if ( in_array( wcs_get_canonical_product_id( $cart_item ), $coupon_product_ids ) || in_array( $cart_item['data']->get_parent(), $coupon_product_ids ) ) {
$this_item_is_discounted = true; $this_item_is_discounted = true;
} }
// Category discounts // Category discounts
} elseif ( sizeof( $coupon->product_categories ) > 0 ) { } elseif ( sizeof( $coupon_product_categories = wcs_get_coupon_property( $coupon, 'product_categories' ) ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->product_categories ) ) > 0 ) { if ( sizeof( array_intersect( $product_cats, $coupon_product_categories ) ) > 0 ) {
$this_item_is_discounted = true; $this_item_is_discounted = true;
} }
} else { } else {
@@ -327,15 +334,15 @@ class WC_Subscriptions_Coupon {
} }
// Specific product ID's excluded from the discount // Specific product ID's excluded from the discount
if ( sizeof( $coupon->exclude_product_ids ) > 0 ) { if ( sizeof( $coupon_excluded_product_ids = wcs_get_coupon_property( $coupon, 'exclude_product_ids' ) ) > 0 ) {
if ( in_array( wcs_get_canonical_product_id( $cart_item ), $coupon->exclude_product_ids ) || in_array( $cart_item['data']->get_parent(), $coupon->exclude_product_ids ) ) { if ( in_array( wcs_get_canonical_product_id( $cart_item ), $coupon_excluded_product_ids ) || in_array( $cart_item['data']->get_parent(), $coupon_excluded_product_ids ) ) {
$this_item_is_discounted = false; $this_item_is_discounted = false;
} }
} }
// Specific categories excluded from the discount // Specific categories excluded from the discount
if ( sizeof( $coupon->exclude_product_categories ) > 0 ) { if ( sizeof( $coupon_excluded_product_categories = wcs_get_coupon_property( $coupon, 'exclude_product_categories' ) ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->exclude_product_categories ) ) > 0 ) { if ( sizeof( array_intersect( $product_cats, $coupon_excluded_product_categories ) ) > 0 ) {
$this_item_is_discounted = false; $this_item_is_discounted = false;
} }
} }
@@ -372,8 +379,9 @@ class WC_Subscriptions_Coupon {
foreach ( $applied_coupons as $coupon_code ) { foreach ( $applied_coupons as $coupon_code ) {
$coupon = new WC_Coupon( $coupon_code ); $coupon = new WC_Coupon( $coupon_code );
$coupon_type = wcs_get_coupon_property( $coupon, 'type' );
if ( in_array( $coupon->type, array( 'recurring_fee', 'recurring_percent' ) ) ) { // always apply coupons to their specific calculation case if ( in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent' ) ) ) { // always apply coupons to their specific calculation case
if ( 'recurring_total' == $calculation_type ) { if ( 'recurring_total' == $calculation_type ) {
$coupons_to_reapply[] = $coupon_code; $coupons_to_reapply[] = $coupon_code;
} elseif ( 'none' == $calculation_type && ! WC_Subscriptions_Cart::all_cart_items_have_free_trial() ) { // sometimes apply recurring coupons to initial total } elseif ( 'none' == $calculation_type && ! WC_Subscriptions_Cart::all_cart_items_have_free_trial() ) { // sometimes apply recurring coupons to initial total
@@ -381,7 +389,7 @@ class WC_Subscriptions_Coupon {
} else { } else {
self::$removed_coupons[] = $coupon_code; self::$removed_coupons[] = $coupon_code;
} }
} elseif ( ( 'none' == $calculation_type ) && ! in_array( $coupon->type, array( 'recurring_fee', 'recurring_percent' ) ) ) { // apply all coupons to the first payment } elseif ( ( 'none' == $calculation_type ) && ! in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent' ) ) ) { // apply all coupons to the first payment
$coupons_to_reapply[] = $coupon_code; $coupons_to_reapply[] = $coupon_code;
} else { } else {
self::$removed_coupons[] = $coupon_code; self::$removed_coupons[] = $coupon_code;
@@ -437,7 +445,7 @@ class WC_Subscriptions_Coupon {
foreach ( $coupons as $coupon ) { foreach ( $coupons as $coupon ) {
if ( $coupon->code == $code ) { if ( wcs_get_coupon_property( $coupon, 'code' ) == $code ) {
if ( $subscription = wcs_get_subscription( $subscription_id ) ) { if ( $subscription = wcs_get_subscription( $subscription_id ) ) {
$subtotal = $subscription->get_subtotal(); $subtotal = $subscription->get_subtotal();
@@ -453,7 +461,7 @@ class WC_Subscriptions_Coupon {
/** /**
* Check if a product is a renewal order line item (rather than a "susbscription") - to pick up non-subsbcription products added a subscription manually * Check if a product is a renewal order line item (rather than a "susbscription") - to pick up non-subsbcription products added a subscription manually
* *
* @param int $product_id * @param int|WC_Product $product_id
* @param array $cart_item * @param array $cart_item
* @param WC_Cart $cart The WooCommerce cart object. * @param WC_Cart $cart The WooCommerce cart object.
* @return boolean whether a product is a renewal order line item * @return boolean whether a product is a renewal order line item
@@ -464,16 +472,13 @@ class WC_Subscriptions_Coupon {
$is_subscription_line_item = false; $is_subscription_line_item = false;
if ( is_object( $product_id ) ) { if ( is_object( $product_id ) ) {
$product = $product_id; $product_id = $product_id->get_id();
$product_id = $product->id;
} elseif ( is_numeric( $product_id ) ) {
$product = wc_get_product( $product_id );
} }
if ( ! empty( $cart_item['subscription_renewal'] ) ) { if ( ! empty( $cart_item['subscription_renewal'] ) ) {
if ( $subscription = wcs_get_subscription( $cart_item['subscription_renewal']['subscription_id'] ) ) { if ( $subscription = wcs_get_subscription( $cart_item['subscription_renewal']['subscription_id'] ) ) {
foreach ( $subscription->get_items() as $item ) { foreach ( $subscription->get_items() as $item ) {
$item_product_id = ( $item['variation_id'] ) ? $item['variation_id'] : $item['product_id']; $item_product_id = wcs_get_canonical_product_id( $item );
if ( ! empty( $item_product_id ) && $item_product_id == $product_id ) { if ( ! empty( $item_product_id ) && $item_product_id == $product_id ) {
$is_subscription_line_item = true; $is_subscription_line_item = true;
} }
@@ -484,6 +489,24 @@ class WC_Subscriptions_Coupon {
return apply_filters( 'woocommerce_is_subscription_renewal_line_item', $is_subscription_line_item, $product_id, $cart_item ); return apply_filters( 'woocommerce_is_subscription_renewal_line_item', $is_subscription_line_item, $product_id, $cart_item );
} }
/**
* Add our pseudo renewal coupon types to the list of supported types.
*
* @param array $coupon_types
* @return array supported coupon types
* @since 2.2
*/
public static function add_pseudo_coupon_types( $coupon_types ) {
return array_merge(
$coupon_types,
array(
'renewal_percent' => __( 'Renewal % discount', 'woocommerce-subscriptions' ),
'renewal_fee' => __( 'Renewal product discount', 'woocommerce-subscriptions' ),
'renewal_cart' => __( 'Renewal cart discount', 'woocommerce-subscriptions' ),
)
);
}
/* Deprecated */ /* Deprecated */
/** /**
@@ -494,9 +517,7 @@ class WC_Subscriptions_Coupon {
public static function apply_subscription_discount( $original_price, $cart_item, $cart ) { 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()' ); _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( $cart_item['data'] ) ) {
if ( ! WC_Subscriptions_Product::is_subscription( $product_id ) ) {
return $original_price; return $original_price;
} }
@@ -506,15 +527,17 @@ class WC_Subscriptions_Coupon {
if ( ! empty( $cart->applied_coupons ) ) { if ( ! empty( $cart->applied_coupons ) ) {
foreach ( $cart->applied_coupons as $code ) { foreach ( $cart->applied_coupons as $coupon_code ) {
$coupon = new WC_Coupon( $code ); $coupon = new WC_Coupon( $coupon_code );
$coupon_type = wcs_get_coupon_property( $coupon, 'type' );
$coupon_amount = wcs_get_coupon_property( $coupon, 'amount' );
// Pre 2.5 is_valid_for_product() does not use wc_get_product_coupon_types() // Pre 2.5 is_valid_for_product() does not use wc_get_product_coupon_types()
if ( WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) { if ( WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
$is_valid_for_product = true; $is_valid_for_product = true;
} else { } else {
$is_valid_for_product = $coupon->is_valid_for_product( wc_get_product( $product_id ), $cart_item ); $is_valid_for_product = $coupon->is_valid_for_product( $cart_item['data'], $cart_item );
} }
if ( $coupon->apply_before_tax() && $coupon->is_valid() && $is_valid_for_product ) { if ( $coupon->apply_before_tax() && $coupon->is_valid() && $is_valid_for_product ) {
@@ -523,8 +546,8 @@ class WC_Subscriptions_Coupon {
// Apply recurring fee discounts to recurring total calculations // Apply recurring fee discounts to recurring total calculations
if ( 'recurring_total' == $calculation_type ) { if ( 'recurring_total' == $calculation_type ) {
$apply_recurring_coupon = ( 'recurring_fee' == $coupon->type ) ? true : false; $apply_recurring_coupon = ( 'recurring_fee' == $coupon_type ) ? true : false;
$apply_recurring_percent_coupon = ( 'recurring_percent' == $coupon->type ) ? true : false; $apply_recurring_percent_coupon = ( 'recurring_percent' == $coupon_type ) ? true : false;
} }
if ( 'none' == $calculation_type ) { if ( 'none' == $calculation_type ) {
@@ -532,71 +555,71 @@ class WC_Subscriptions_Coupon {
// If all items have a free trial we don't need to apply recurring coupons to the initial total // 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 ( ! WC_Subscriptions_Cart::all_cart_items_have_free_trial() ) {
if ( 'recurring_fee' == $coupon->type ) { if ( 'recurring_fee' == $coupon_type ) {
$apply_initial_coupon = true; $apply_initial_coupon = true;
} }
if ( 'recurring_percent' == $coupon->type ) { if ( 'recurring_percent' == $coupon_type ) {
$apply_initial_percent_coupon = true; $apply_initial_percent_coupon = true;
} }
} }
// Apply sign-up discounts to initial total // Apply sign-up discounts to initial total
if ( ! empty( $cart_item['data']->subscription_sign_up_fee ) ) { if ( WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] ) > 0 ) {
if ( 'sign_up_fee' == $coupon->type ) { if ( 'sign_up_fee' == $coupon_type ) {
$apply_initial_coupon = true; $apply_initial_coupon = true;
} }
if ( 'sign_up_fee_percent' == $coupon->type ) { if ( 'sign_up_fee_percent' == $coupon_type ) {
$apply_initial_percent_coupon = true; $apply_initial_percent_coupon = true;
} }
$calculation_price = $cart_item['data']->subscription_sign_up_fee; $calculation_price = WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] );
} }
} }
if ( $apply_recurring_coupon || $apply_initial_coupon ) { if ( $apply_recurring_coupon || $apply_initial_coupon ) {
$discount_amount = ( $calculation_price < $coupon->amount ) ? $calculation_price : $coupon->amount; $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) // 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 ) ) { if ( $apply_initial_coupon && 'recurring_fee' == $coupon_type && WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) {
$discount_amount = 0; $discount_amount = 0;
} }
$cart->discount_cart = $cart->discount_cart + ( $discount_amount * $cart_item['quantity'] ); $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'] ); $cart = self::increase_coupon_discount_amount( $cart, $coupon_code, $discount_amount * $cart_item['quantity'] );
$price = $price - $discount_amount; $price = $price - $discount_amount;
} elseif ( $apply_recurring_percent_coupon ) { } elseif ( $apply_recurring_percent_coupon ) {
$discount_amount = round( ( $calculation_price / 100 ) * $coupon->amount, WC()->cart->dp ); $discount_amount = round( ( $calculation_price / 100 ) * $coupon_amount, WC()->cart->dp );
$cart->discount_cart = $cart->discount_cart + ( $discount_amount * $cart_item['quantity'] ); $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'] ); $cart = self::increase_coupon_discount_amount( $cart, $coupon_code, $discount_amount * $cart_item['quantity'] );
$price = $price - $discount_amount; $price = $price - $discount_amount;
} elseif ( $apply_initial_percent_coupon ) { } 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) // 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 ) ) { if ( 'recurring_percent' == $coupon_type && 0 == WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) ) {
$amount_to_discount = $cart_item['data']->subscription_price; $amount_to_discount = WC_Subscriptions_Product::get_price( $cart_item['data'] );
} else { } else {
$amount_to_discount = 0; $amount_to_discount = 0;
} }
// Sign up fee coupons only apply to sign up fees // Sign up fee coupons only apply to sign up fees
if ( 'sign_up_fee_percent' == $coupon->type ) { if ( 'sign_up_fee_percent' == $coupon_type ) {
$amount_to_discount = $cart_item['data']->subscription_sign_up_fee; $amount_to_discount = WC_Subscriptions_Product::get_sign_up_fee( $cart_item['data'] );
} }
$discount_amount = round( ( $amount_to_discount / 100 ) * $coupon->amount, WC()->cart->dp ); $discount_amount = round( ( $amount_to_discount / 100 ) * $coupon_amount, WC()->cart->dp );
$cart->discount_cart = $cart->discount_cart + $discount_amount * $cart_item['quantity']; $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'] ); $cart = self::increase_coupon_discount_amount( $cart, $coupon_code, $discount_amount * $cart_item['quantity'] );
$price = $price - $discount_amount; $price = $price - $discount_amount;
} }

View File

@@ -104,7 +104,7 @@ class WC_Subscriptions_Email {
public static function send_cancelled_email( $subscription ) { public static function send_cancelled_email( $subscription ) {
WC()->mailer(); WC()->mailer();
if ( $subscription->has_status( array( 'pending-cancel', 'cancelled' ) ) && 'true' !== get_post_meta( $subscription->id, '_cancelled_email_sent', true ) ) { if ( $subscription->has_status( array( 'pending-cancel', 'cancelled' ) ) && 'true' !== get_post_meta( $subscription->get_id(), '_cancelled_email_sent', true ) ) {
do_action( 'cancelled_subscription_notification', $subscription ); do_action( 'cancelled_subscription_notification', $subscription );
} }
} }
@@ -157,7 +157,7 @@ class WC_Subscriptions_Email {
*/ */
public static function maybe_remove_woocommerce_email( $order_id ) { public static function maybe_remove_woocommerce_email( $order_id ) {
if ( wcs_order_contains_renewal( $order_id ) || wcs_order_contains_switch( $order_id ) ) { if ( wcs_order_contains_renewal( $order_id ) || wcs_order_contains_switch( $order_id ) ) {
remove_action( current_filter(), array( 'WC_Emails', 'send_transactional_email' ) ); self::detach_woocommerce_transactional_email();
} }
} }
@@ -170,7 +170,7 @@ class WC_Subscriptions_Email {
*/ */
public static function maybe_reattach_woocommerce_email( $order_id ) { public static function maybe_reattach_woocommerce_email( $order_id ) {
if ( wcs_order_contains_renewal( $order_id ) || wcs_order_contains_switch( $order_id ) ) { if ( wcs_order_contains_renewal( $order_id ) || wcs_order_contains_switch( $order_id ) ) {
add_action( current_filter(), array( 'WC_Emails', 'send_transactional_email' ), 10, 10 ); self::attach_woocommerce_transactional_email();
} }
} }
@@ -185,7 +185,7 @@ class WC_Subscriptions_Email {
public static function renewal_order_emails_available( $available_emails ) { public static function renewal_order_emails_available( $available_emails ) {
global $theorder; global $theorder;
if ( wcs_order_contains_renewal( $theorder->id ) ) { if ( wcs_order_contains_renewal( wcs_get_objects_property( $theorder, 'id' ) ) ) {
$available_emails = array( $available_emails = array(
'new_renewal_order', 'new_renewal_order',
'customer_processing_renewal_order', 'customer_processing_renewal_order',
@@ -252,7 +252,11 @@ class WC_Subscriptions_Email {
add_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback ); add_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback );
add_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback ); add_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback );
if ( function_exists( 'wc_get_email_order_items' ) ) { // WC 3.0+
$items_table = wc_get_email_order_items( $order );
} else {
$items_table = $order->email_order_items_table( $args ); $items_table = $order->email_order_items_table( $args );
}
remove_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback ); remove_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback );
remove_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback ); remove_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback );
@@ -300,6 +304,45 @@ class WC_Subscriptions_Email {
); );
} }
/**
* Detach WC transactional emails from a specific hook.
*
* @param string Optional. The action hook or filter to detach WC core's transactional emails from. Defaults to the current filter.
* @param int Optional. The priority the function runs on. Default 10.
* @since 2.2.3
*/
public static function detach_woocommerce_transactional_email( $hook = '', $priority = 10 ) {
if ( '' === $hook ) {
$hook = current_filter();
}
// Emails might be queued or sent immediately so we need to remove both
remove_action( $hook, array( 'WC_Emails', 'queue_transactional_email' ), $priority );
remove_action( $hook, array( 'WC_Emails', 'send_transactional_email' ), $priority );
}
/**
* Attach WC transactional emails to a specific hook.
*
* @param string Optional. The action hook or filter to attach WC core's transactional emails to. Defaults to the current filter.
* @param int Optional. The priority the function should run on. Default 10.
* @param int Optional. The number of arguments the function accepts. Default 10.
* @since 2.2.3
*/
public static function attach_woocommerce_transactional_email( $hook = '', $priority = 10, $accepted_args = 10 ) {
if ( '' === $hook ) {
$hook = current_filter();
}
if ( false === WC_Subscriptions::is_woocommerce_pre( '3.0' ) && apply_filters( 'woocommerce_defer_transactional_emails', true ) ) {
add_action( $hook, array( 'WC_Emails', 'queue_transactional_email' ), $priority, $accepted_args );
} else {
add_action( $hook, array( 'WC_Emails', 'send_transactional_email' ), $priority, $accepted_args );
}
}
/** /**
* Init the mailer and call the notifications for the current filter. * Init the mailer and call the notifications for the current filter.
* *

View File

@@ -88,7 +88,7 @@ class WC_Subscriptions_Manager {
} }
// If the subscription is using manual payments, the gateway isn't active or it manages scheduled payments // If the subscription is using manual payments, the gateway isn't active or it manages scheduled payments
if ( 0 == $subscription->get_total() || $subscription->is_manual() || empty( $subscription->payment_method ) || ! $subscription->payment_method_supports( 'gateway_scheduled_payments' ) ) { if ( 0 == $subscription->get_total() || $subscription->is_manual() || '' != $subscription->get_payment_method() || ! $subscription->payment_method_supports( 'gateway_scheduled_payments' ) ) {
// Always put the subscription on hold in case something goes wrong while trying to process renewal // Always put the subscription on hold in case something goes wrong while trying to process renewal
$subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) ); $subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
@@ -114,9 +114,13 @@ class WC_Subscriptions_Manager {
} else { } else {
if ( $subscription->is_manual() ) { if ( $subscription->is_manual() ) {
do_action( 'woocommerce_generated_manual_renewal_order', $renewal_order->id ); do_action( 'woocommerce_generated_manual_renewal_order', wcs_get_objects_property( $renewal_order, 'id' ) );
} else { } else {
$renewal_order->set_payment_method( $subscription->payment_gateway ); $renewal_order->set_payment_method( wc_get_payment_gateway_by_order( $subscription ) ); // We need to pass the payment gateway instance to be compatible with WC < 3.0, only WC 3.0+ supports passing the string name
if ( is_callable( array( $renewal_order, 'save' ) ) ) { // WC 3.0+ We need to save the payment method.
$renewal_order->save();
}
} }
} }
} }
@@ -426,7 +430,7 @@ class WC_Subscriptions_Manager {
} }
$args = wp_parse_args( $args, array( $args = wp_parse_args( $args, array(
'start_date' => get_gmt_from_date( $order->order_date ), 'start_date' => wcs_get_datetime_utc_string( wcs_get_objects_property( $order, 'date_created' ) ), // get_date_created() can return null, but if it does, we have an error anyway
'expiry_date' => '', 'expiry_date' => '',
) ); ) );
@@ -439,7 +443,7 @@ class WC_Subscriptions_Manager {
$product = wc_get_product( $product_id ); $product = wc_get_product( $product_id );
// Check if there is already a subscription for this product and order // Check if there is already a subscription for this product and order
$subscriptions = wcs_get_subscriptions( array( 'order_id' => $order->id, 'product_id' => $product_id ) ); $subscriptions = wcs_get_subscriptions( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'product_id' => $product_id ) );
if ( ! empty( $subscriptions ) ) { if ( ! empty( $subscriptions ) ) {
@@ -447,7 +451,7 @@ class WC_Subscriptions_Manager {
// Make sure the subscription is pending and start date is set correctly // Make sure the subscription is pending and start date is set correctly
wp_update_post( array( wp_update_post( array(
'ID' => $subscription->id, 'ID' => $subscription->get_id(),
'post_status' => 'wc-' . apply_filters( 'woocommerce_default_subscription_status', 'pending' ), 'post_status' => 'wc-' . apply_filters( 'woocommerce_default_subscription_status', 'pending' ),
'post_date' => get_date_from_gmt( $args['start_date'] ), 'post_date' => get_date_from_gmt( $args['start_date'] ),
) ); ) );
@@ -456,11 +460,11 @@ class WC_Subscriptions_Manager {
$subscription = wcs_create_subscription( array( $subscription = wcs_create_subscription( array(
'start_date' => get_date_from_gmt( $args['start_date'] ), 'start_date' => get_date_from_gmt( $args['start_date'] ),
'order_id' => $order->id, 'order_id' => wcs_get_objects_property( $order, 'id' ),
'customer_id' => $order->get_user_id(), 'customer_id' => $order->get_user_id(),
'billing_period' => $billing_period, 'billing_period' => $billing_period,
'billing_interval' => $billing_interval, 'billing_interval' => $billing_interval,
'customer_note' => $order->customer_note, 'customer_note' => wcs_get_objects_property( $order, 'customer_note' ),
) ); ) );
if ( is_wp_error( $subscription ) ) { if ( is_wp_error( $subscription ) ) {
@@ -488,8 +492,13 @@ class WC_Subscriptions_Manager {
} }
// Make sure some of the meta is copied form the order rather than the store's defaults // Make sure some of the meta is copied form the order rather than the store's defaults
update_post_meta( $subscription->id, '_order_currency', $order->order_currency ); if ( wcs_get_objects_property( $order, 'prices_include_tax' ) ) {
update_post_meta( $subscription->id, '_prices_include_tax', $order->prices_include_tax ); $prices_include_tax = 'yes';
} else {
$prices_include_tax = 'no';
}
update_post_meta( $subscription->get_id(), '_order_currency', wcs_get_objects_property( $order, 'currency' ) );
update_post_meta( $subscription->get_id(), '_prices_include_tax', $prices_include_tax );
// Adding a new subscription so set the expiry date/time from the order date // Adding a new subscription so set the expiry date/time from the order date
if ( ! empty( $args['expiry_date'] ) ) { if ( ! empty( $args['expiry_date'] ) ) {
@@ -562,7 +571,7 @@ class WC_Subscriptions_Manager {
_deprecated_argument( __METHOD__, '1.2', 'The "suspend" status value is deprecated. Use "on-hold"' ); _deprecated_argument( __METHOD__, '1.2', 'The "suspend" status value is deprecated. Use "on-hold"' );
} }
foreach ( wcs_get_subscriptions_for_order( $order->id, array( 'order_type' => 'parent' ) ) as $subscription_id => $subscription ) { foreach ( wcs_get_subscriptions_for_order( wcs_get_objects_property( $order, 'id' ), array( 'order_type' => 'parent' ) ) as $subscription_id => $subscription ) {
switch ( $status ) { switch ( $status ) {
case 'cancelled' : case 'cancelled' :
@@ -575,7 +584,7 @@ class WC_Subscriptions_Manager {
break; break;
case 'failed' : case 'failed' :
_deprecated_argument( __METHOD__, '2.0', 'The "failed" status value is deprecated.' ); _deprecated_argument( __METHOD__, '2.0', 'The "failed" status value is deprecated.' );
self::failed_subscription_signup( $order->user_id, $subscription_id ); self::failed_subscription_signup( $order->get_user_id(), $subscription_id );
break; break;
case 'pending' : case 'pending' :
_deprecated_argument( __METHOD__, '2.0', 'The "pending" status value is deprecated.' ); _deprecated_argument( __METHOD__, '2.0', 'The "pending" status value is deprecated.' );
@@ -612,7 +621,7 @@ class WC_Subscriptions_Manager {
$subscription = wcs_get_subscription_from_key( $subscription_key ); $subscription = wcs_get_subscription_from_key( $subscription_key );
if ( isset( $new_subscription_details['status'] ) && 'deleted' == $new_subscription_details['status'] ) { if ( isset( $new_subscription_details['status'] ) && 'deleted' == $new_subscription_details['status'] ) {
wp_delete_post( $subscription->id ); wp_delete_post( $subscription->get_id() );
} else { } else {
// There is no direct analog for this in WC_Subscription, so we need to call the deprecated method // There is no direct analog for this in WC_Subscription, so we need to call the deprecated method
self::update_subscription( $subscription_key, $new_subscription_details ); self::update_subscription( $subscription_key, $new_subscription_details );
@@ -647,14 +656,14 @@ class WC_Subscriptions_Manager {
if ( isset( $new_subscription_details['status'] ) && 'deleted' == $new_subscription_details['status'] ) { if ( isset( $new_subscription_details['status'] ) && 'deleted' == $new_subscription_details['status'] ) {
wp_delete_post( $subscription->id ); wp_delete_post( $subscription->get_id() );
} else { } else {
foreach ( $new_subscription_details as $meta_key => $meta_value ) { foreach ( $new_subscription_details as $meta_key => $meta_value ) {
switch ( $meta_key ) { switch ( $meta_key ) {
case 'start_date' : case 'start_date' :
$subscription->update_dates( array( 'start' => $meta_value ) ); $subscription->update_dates( array( 'date_created' => $meta_value ) );
break; break;
case 'trial_expiry_date' : case 'trial_expiry_date' :
$subscription->update_dates( array( 'trial_end' => $meta_value ) ); $subscription->update_dates( array( 'trial_end' => $meta_value ) );
@@ -669,7 +678,7 @@ class WC_Subscriptions_Manager {
_deprecated_argument( __METHOD__, '2.0', 'The "completed_payments" meta value is deprecated. Create a renewal order with completed payment instead.' ); _deprecated_argument( __METHOD__, '2.0', 'The "completed_payments" meta value is deprecated. Create a renewal order with completed payment instead.' );
break; break;
case 'suspension_count' : case 'suspension_count' :
$subscription->update_suspension_count( $subscription->suspension_count + 1 ); $subscription->set_suspension_count( $subscription->get_suspension_count() + 1 );
break; break;
} }
} }
@@ -738,7 +747,7 @@ class WC_Subscriptions_Manager {
public static function clear_users_subscriptions_from_order( $order ) { public static function clear_users_subscriptions_from_order( $order ) {
foreach ( wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'parent' ) ) as $subscription_id => $subscription ) { foreach ( wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'parent' ) ) as $subscription_id => $subscription ) {
wp_delete_post( $subscription->id ); wp_delete_post( $subscription->get_id() );
} }
do_action( 'cleared_users_subscriptions_from_order', $order ); do_action( 'cleared_users_subscriptions_from_order', $order );
@@ -757,7 +766,7 @@ class WC_Subscriptions_Manager {
// delete subscription // delete subscription
foreach ( wcs_get_subscriptions_for_order( $post_id, array( 'order_type' => 'parent' ) ) as $subscription ) { foreach ( wcs_get_subscriptions_for_order( $post_id, array( 'order_type' => 'parent' ) ) as $subscription ) {
wp_trash_post( $subscription->id ); wp_trash_post( $subscription->get_id() );
} }
} }
} }
@@ -828,7 +837,7 @@ class WC_Subscriptions_Manager {
if ( ! empty( $subscriptions ) ) { if ( ! empty( $subscriptions ) ) {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
wp_delete_post( $subscription->id ); wp_delete_post( $subscription->get_id() );
} }
} }
} }
@@ -1253,7 +1262,7 @@ class WC_Subscriptions_Manager {
public static function get_last_payment_date( $subscription_key, $user_id = '', $type = 'mysql' ) { public static function get_last_payment_date( $subscription_key, $user_id = '', $type = 'mysql' ) {
_deprecated_function( __METHOD__, '2.0', 'WC_Subscription::get_date( "last_payment" )' ); _deprecated_function( __METHOD__, '2.0', 'WC_Subscription::get_date( "last_payment" )' );
$subscription = wcs_get_subscription_from_key( $subscription_key ); $subscription = wcs_get_subscription_from_key( $subscription_key );
$last_payment_date = ( 'mysql' == $type ) ? $subscription->get_date( 'last_payment' ) : $subscription->get_time( 'last_payment' ); $last_payment_date = ( 'mysql' == $type ) ? $subscription->get_date( 'last_order_date_created' ) : $subscription->get_time( 'last_order_date_created' );
return apply_filters( 'woocommerce_subscription_last_payment_date', $last_payment_date, $subscription_key, $user_id, $type ); return apply_filters( 'woocommerce_subscription_last_payment_date', $last_payment_date, $subscription_key, $user_id, $type );
} }
@@ -1537,7 +1546,7 @@ class WC_Subscriptions_Manager {
$order = new WC_Order( $order ); $order = new WC_Order( $order );
} }
update_user_meta( $order->user_id, 'paying_customer', 1 ); update_user_meta( $order->get_user_id(), 'paying_customer', 1 );
} }
/** /**
@@ -1557,8 +1566,8 @@ class WC_Subscriptions_Manager {
$order = new WC_Order( $order ); $order = new WC_Order( $order );
} }
if ( $order->user_id > 0 ) { if ( $order->get_user_id() > 0 ) {
update_user_meta( $order->user_id, 'paying_customer', 0 ); update_user_meta( $order->get_user_id(), 'paying_customer', 0 );
} }
} }
@@ -1822,7 +1831,7 @@ class WC_Subscriptions_Manager {
} }
// If the subscription is using manual payments, the gateway isn't active or it manages scheduled payments // If the subscription is using manual payments, the gateway isn't active or it manages scheduled payments
if ( 0 == $subscription->get_total() || $subscription->is_manual() || empty( $subscription->payment_method ) || ! $subscription->payment_method_supports( 'gateway_scheduled_payments' ) ) { if ( 0 == $subscription->get_total() || $subscription->is_manual() || '' != $subscription->get_payment_method() || ! $subscription->payment_method_supports( 'gateway_scheduled_payments' ) ) {
$subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) ); $subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
} }
} }
@@ -2091,7 +2100,7 @@ class WC_Subscriptions_Manager {
// Log failure on order // Log failure on order
// translators: placeholder is subscription ID // translators: placeholder is subscription ID
$subscription->order->add_order_note( sprintf( __( 'Failed sign-up for subscription %s.', 'woocommerce-subscriptions' ), $subscription->id ) ); $subscription->get_parent()->add_order_note( sprintf( __( 'Failed sign-up for subscription %s.', 'woocommerce-subscriptions' ), $subscription->get_id() ) );
do_action( 'subscription_sign_up_failed', $user_id, $subscription_key ); do_action( 'subscription_sign_up_failed', $user_id, $subscription_key );
} }
@@ -2127,7 +2136,7 @@ class WC_Subscriptions_Manager {
$subscription->update_status( 'cancelled' ); $subscription->update_status( 'cancelled' );
} }
wp_trash_post( $subscription->id, true ); wp_trash_post( $subscription->get_id(), true );
do_action( 'subscription_trashed', $user_id, $subscription_key ); do_action( 'subscription_trashed', $user_id, $subscription_key );
} }
@@ -2160,7 +2169,7 @@ class WC_Subscriptions_Manager {
$subscription->update_status( 'cancelled' ); $subscription->update_status( 'cancelled' );
} }
wp_delete_post( $subscription->id, true ); wp_delete_post( $subscription->get_id(), true );
do_action( 'subscription_deleted', $user_id, $subscription_key, $subscription, $item ); do_action( 'subscription_deleted', $user_id, $subscription_key, $subscription, $item );
} }

View File

@@ -238,7 +238,7 @@ class WC_Subscriptions_Order {
$item['name'] = $item['order_item_name']; $item['name'] = $item['order_item_name'];
$item['type'] = $item['order_item_type']; $item['type'] = $item['order_item_type'];
$item['item_meta'] = $order->get_item_meta( $item['order_item_id'] ); $item['item_meta'] = wc_get_order_item_meta( $item['order_item_id'], '' );
// Put meta into item array // Put meta into item array
if ( is_array( $item['item_meta'] ) ) { if ( is_array( $item['item_meta'] ) ) {
@@ -345,7 +345,7 @@ class WC_Subscriptions_Order {
} elseif ( is_array( $order->order_custom_fields ) && isset( $order->order_custom_fields[ '_' . $meta_key ][0] ) && $order->order_custom_fields[ '_' . $meta_key ][0] ) { // < WC 2.1+ } elseif ( is_array( $order->order_custom_fields ) && isset( $order->order_custom_fields[ '_' . $meta_key ][0] ) && $order->order_custom_fields[ '_' . $meta_key ][0] ) { // < WC 2.1+
$meta_value = maybe_unserialize( $order->order_custom_fields[ '_' . $meta_key ][0] ); $meta_value = maybe_unserialize( $order->order_custom_fields[ '_' . $meta_key ][0] );
} else { } else {
$meta_value = get_post_meta( $order->id, '_' . $meta_key, true ); $meta_value = get_post_meta( wcs_get_objects_property( $order, 'id' ), '_' . $meta_key, true );
if ( empty( $meta_value ) ) { if ( empty( $meta_value ) ) {
$meta_value = $default; $meta_value = $default;
@@ -482,12 +482,12 @@ class WC_Subscriptions_Order {
// Do we need to activate a subscription? // Do we need to activate a subscription?
if ( $order_completed && ! $subscription->has_status( wcs_get_subscription_ended_statuses() ) && ! $subscription->has_status( 'active' ) ) { if ( $order_completed && ! $subscription->has_status( wcs_get_subscription_ended_statuses() ) && ! $subscription->has_status( 'active' ) ) {
$new_start_date_offset = current_time( 'timestamp', true ) - $subscription->get_time( 'start' ); $new_start_date_offset = current_time( 'timestamp', true ) - $subscription->get_time( 'date_created' );
// if the payment has been processed more than an hour after the order was first created, let's update the dates on the subscription to account for that, because it may have even been processed days after it was first placed // if the payment has been processed more than an hour after the order was first created, let's update the dates on the subscription to account for that, because it may have even been processed days after it was first placed
if ( $new_start_date_offset > HOUR_IN_SECONDS ) { if ( $new_start_date_offset > HOUR_IN_SECONDS ) {
$dates = array( 'start' => current_time( 'mysql', true ) ); $dates = array( 'date_created' => current_time( 'mysql', true ) );
if ( WC_Subscriptions_Synchroniser::subscription_contains_synced_product( $subscription ) ) { if ( WC_Subscriptions_Synchroniser::subscription_contains_synced_product( $subscription ) ) {
@@ -495,15 +495,15 @@ class WC_Subscriptions_Order {
$next_payment = $subscription->get_time( 'next_payment' ); $next_payment = $subscription->get_time( 'next_payment' );
// if either there is a free trial date or a next payment date that falls before now, we need to recalculate all the sync'd dates // if either there is a free trial date or a next payment date that falls before now, we need to recalculate all the sync'd dates
if ( ( $trial_end > 0 && $trial_end < wcs_date_to_time( $dates['start'] ) ) || ( $next_payment > 0 && $next_payment < wcs_date_to_time( $dates['start'] ) ) ) { if ( ( $trial_end > 0 && $trial_end < wcs_date_to_time( $dates['date_created'] ) ) || ( $next_payment > 0 && $next_payment < wcs_date_to_time( $dates['date_created'] ) ) ) {
foreach ( $subscription->get_items() as $item ) { foreach ( $subscription->get_items() as $item ) {
$product_id = wcs_get_canonical_product_id( $item ); $product_id = wcs_get_canonical_product_id( $item );
if ( WC_Subscriptions_Synchroniser::is_product_synced( $product_id ) ) { if ( WC_Subscriptions_Synchroniser::is_product_synced( $product_id ) ) {
$dates['trial_end'] = WC_Subscriptions_Product::get_trial_expiration_date( $product_id, $dates['start'] ); $dates['trial_end'] = WC_Subscriptions_Product::get_trial_expiration_date( $product_id, $dates['date_created'] );
$dates['next_payment'] = WC_Subscriptions_Synchroniser::calculate_first_payment_date( $product_id, 'mysql', $dates['start'] ); $dates['next_payment'] = WC_Subscriptions_Synchroniser::calculate_first_payment_date( $product_id, 'mysql', $dates['date_created'] );
$dates['end'] = WC_Subscriptions_Product::get_expiration_date( $product_id, $dates['start'] ); $dates['end'] = WC_Subscriptions_Product::get_expiration_date( $product_id, $dates['date_created'] );
break; break;
} }
} }
@@ -624,7 +624,7 @@ class WC_Subscriptions_Order {
*/ */
public static function order_needs_payment( $needs_payment, $order, $valid_order_statuses ) { public static function order_needs_payment( $needs_payment, $order, $valid_order_statuses ) {
if ( wcs_order_contains_subscription( $order ) && in_array( $order->status, $valid_order_statuses ) && 0 == $order->get_total() && false === $needs_payment && self::get_recurring_total( $order ) > 0 && 'yes' !== get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) ) { if ( false === $needs_payment && 0 == $order->get_total() && in_array( $order->get_status(), $valid_order_statuses ) && wcs_order_contains_subscription( $order ) && self::get_recurring_total( $order ) > 0 && 'yes' !== get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) ) {
$needs_payment = true; $needs_payment = true;
} }
@@ -658,22 +658,6 @@ class WC_Subscriptions_Order {
} }
} }
/**
* Wrapper around @see WC_Order::get_order_currency() for versions of WooCommerce prior to 2.1.
*
* @since version 1.4.9
*/
public static function get_order_currency( $order ) {
if ( method_exists( $order, 'get_order_currency' ) ) {
$order_currency = $order->get_order_currency();
} else {
$order_currency = get_woocommerce_currency();
}
return $order_currency;
}
/** /**
* Add admin dropdown for order types to Woocommerce -> Orders screen * Add admin dropdown for order types to Woocommerce -> Orders screen
* *
@@ -946,14 +930,14 @@ class WC_Subscriptions_Order {
if ( wcs_order_contains_subscription( $order, array( 'parent', 'renewal' ) ) ) { if ( wcs_order_contains_subscription( $order, array( 'parent', 'renewal' ) ) ) {
$subscriptions = wcs_get_subscriptions_for_order( $order->id, array( 'order_type' => array( 'parent', 'renewal' ) ) ); $subscriptions = wcs_get_subscriptions_for_order( wcs_get_objects_property( $order, 'id' ), array( 'order_type' => array( 'parent', 'renewal' ) ) );
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
$latest_order = $subscription->get_last_order(); $latest_order = $subscription->get_last_order();
if ( $order->id == $latest_order && $subscription->has_status( 'pending-cancel' ) && $subscription->can_be_updated_to( 'cancelled' ) ) { if ( wcs_get_objects_property( $order, 'id' ) == $latest_order && $subscription->has_status( 'pending-cancel' ) && $subscription->can_be_updated_to( 'cancelled' ) ) {
// translators: $1: opening link tag, $2: order number, $3: closing link tag // translators: $1: opening link tag, $2: order number, $3: closing link tag
$subscription->update_status( 'cancelled', wp_kses( sprintf( __( 'Subscription cancelled for refunded order %1$s#%2$s%3$s.', 'woocommerce-subscriptions' ), sprintf( '<a href="%s">', esc_url( wcs_get_edit_post_link( $order->id ) ) ), $order->get_order_number(), '</a>' ), array( 'a' => array( 'href' => true ) ) ) ); $subscription->update_status( 'cancelled', wp_kses( sprintf( __( 'Subscription cancelled for refunded order %1$s#%2$s%3$s.', 'woocommerce-subscriptions' ), sprintf( '<a href="%s">', esc_url( wcs_get_edit_post_link( wcs_get_objects_property( $order, 'id' ) ) ) ), $order->get_order_number(), '</a>' ), array( 'a' => array( 'href' => true ) ) ) );
} }
} }
} }
@@ -1032,7 +1016,11 @@ class WC_Subscriptions_Order {
* @since 2.1.3 * @since 2.1.3
*/ */
public static function maybe_autocomplete_order( $new_order_status, $order_id ) { public static function maybe_autocomplete_order( $new_order_status, $order_id ) {
// Guard against infinite loops in WC 3.0+ where woocommerce_payment_complete_order_status is called while instantiating WC_Order objects
remove_filter( 'woocommerce_payment_complete_order_status', __METHOD__, 10 );
$order = wc_get_order( $order_id ); $order = wc_get_order( $order_id );
add_filter( 'woocommerce_payment_complete_order_status', __METHOD__, 10, 2 );
if ( 'processing' == $new_order_status && $order->get_total() == 0 && wcs_order_contains_subscription( $order ) ) { if ( 'processing' == $new_order_status && $order->get_total() == 0 && wcs_order_contains_subscription( $order ) ) {
@@ -1350,22 +1338,24 @@ class WC_Subscriptions_Order {
public static function get_recurring_discount_total( $order, $product_id = '' ) { public static function get_recurring_discount_total( $order, $product_id = '' ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different discounts, so use the subscription object' ); _deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different discounts, so use the subscription object' );
$ex_tax = ( $order->tax_display_cart === 'excl' && $order->display_totals_ex_tax ) ? true : false; $ex_tax = ( 'excl' === get_option( 'woocommerce_tax_display_cart' ) && wcs_get_objects_property( $order, 'display_totals_ex_tax' ) ) ? true : false;
$recurring_discount_cart = (double) self::get_recurring_discount_cart( $order ); $recurring_discount_cart = (double) self::get_recurring_discount_cart( $order );
$recurring_discount_cart_tax = (double) self::get_recurring_discount_cart_tax( $order ); $recurring_discount_cart_tax = (double) self::get_recurring_discount_cart_tax( $order );
$recurring_discount_total = 0; $recurring_discount_total = 0;
if ( ! $order->order_version || version_compare( $order->order_version, '2.3.7', '<' ) ) { $order_version = wcs_get_objects_property( $order, 'version' );
if ( '' === $order_version || version_compare( $order_version, '2.3.7', '<' ) ) {
// Backwards compatible total calculation - totals were not stored consistently in old versions. // Backwards compatible total calculation - totals were not stored consistently in old versions.
if ( $ex_tax ) { if ( $ex_tax ) {
if ( $order->prices_include_tax ) { if ( wcs_get_objects_property( $order, 'prices_include_tax' ) ) {
$recurring_discount_total = $recurring_discount_cart - $recurring_discount_cart_tax; $recurring_discount_total = $recurring_discount_cart - $recurring_discount_cart_tax;
} else { } else {
$recurring_discount_total = $recurring_discount_cart; $recurring_discount_total = $recurring_discount_cart;
} }
} else { } else {
if ( $order->prices_include_tax ) { if ( wcs_get_objects_property( $order, 'prices_include_tax' ) ) {
$recurring_discount_total = $recurring_discount_cart; $recurring_discount_total = $recurring_discount_cart;
} else { } else {
$recurring_discount_total = $recurring_discount_cart + $recurring_discount_cart_tax; $recurring_discount_total = $recurring_discount_cart + $recurring_discount_cart_tax;
@@ -1629,13 +1619,13 @@ class WC_Subscriptions_Order {
// Find the billing period discount for all recurring items // Find the billing period discount for all recurring items
if ( empty( $product_id ) ) { if ( empty( $product_id ) ) {
$billing_period = $subscription->billing_period; $billing_period = $subscription->get_billing_period();
break; break;
} else { } else {
// We want the billing period for a specific item (so we need to find if this subscription contains that item) // We want the billing period for a specific item (so we need to find if this subscription contains that item)
foreach ( $subscription->get_items() as $line_item ) { foreach ( $subscription->get_items() as $line_item ) {
if ( wcs_get_canonical_product_id( $line_item ) == $product_id ) { if ( wcs_get_canonical_product_id( $line_item ) == $product_id ) {
$billing_period = $subscription->billing_period; $billing_period = $subscription->get_billing_period();
break 2; break 2;
} }
} }
@@ -1664,13 +1654,13 @@ class WC_Subscriptions_Order {
// Find the billing interval for all recurring items // Find the billing interval for all recurring items
if ( empty( $product_id ) ) { if ( empty( $product_id ) ) {
$billing_interval = $subscription->billing_interval; $billing_interval = $subscription->get_billing_interval();
break; break;
} else { } else {
// We want the billing interval for a specific item (so we need to find if this subscription contains that item) // We want the billing interval for a specific item (so we need to find if this subscription contains that item)
foreach ( $subscription->get_items() as $line_item ) { foreach ( $subscription->get_items() as $line_item ) {
if ( wcs_get_canonical_product_id( $line_item ) == $product_id ) { if ( wcs_get_canonical_product_id( $line_item ) == $product_id ) {
$billing_interval = $subscription->billing_interval; $billing_interval = $subscription->get_billing_interval();
break 2; break 2;
} }
} }
@@ -1802,11 +1792,11 @@ class WC_Subscriptions_Order {
} }
if ( $subscription = self::get_matching_subscription( $order, $product_id ) ) { if ( $subscription = self::get_matching_subscription( $order, $product_id ) ) {
$last_payment_date = $subscription->get_date( 'last_payment' ); $last_payment_date = $subscription->get_date( 'last_order_date_created' );
} elseif ( isset( $order->paid_date ) ) { } elseif ( null !== ( $last_payment_date = wcs_get_objects_property( $order, 'date_paid' ) ) ) {
$last_payment_date = get_gmt_from_date( $order->paid_date ); $last_payment_date = $last_payment_date->date( 'Y-m-d H:i:s' );
} else { } else {
$last_payment_date = $order->post->post_date; $last_payment_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $order, 'date_created' ) ); // get_date_created() can return null, but if it does, we have an error anyway
} }
return $last_payment_date; return $last_payment_date;
@@ -2024,11 +2014,22 @@ class WC_Subscriptions_Order {
// No payments have been recorded yet // No payments have been recorded yet
if ( 0 == $subscription->get_completed_payment_count() ) { if ( 0 == $subscription->get_completed_payment_count() ) {
$subscription->update_dates( array( 'start' => current_time( 'mysql', true ) ) ); $subscription->update_dates( array( 'date_created' => current_time( 'mysql', true ) ) );
$subscription->payment_complete(); $subscription->payment_complete();
} }
} }
} }
} }
/**
* Wrapper around @see WC_Order::get_order_currency() for versions of WooCommerce prior to 2.1.
*
* @since version 1.4.9
* @deprecated 2.2.0
*/
public static function get_order_currency( $order ) {
_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_objects_property( $order, "currency" ) or $order->get_currency()' );
return wcs_get_objects_property( $order, 'currency' );
}
} }
WC_Subscriptions_Order::init(); WC_Subscriptions_Order::init();

View File

@@ -45,6 +45,9 @@ class WC_Subscriptions_Product {
// Make sure a subscriptions price is included in subscription variations when required // Make sure a subscriptions price is included in subscription variations when required
add_filter( 'woocommerce_available_variation', __CLASS__ . '::maybe_set_variations_price_html', 10, 3 ); add_filter( 'woocommerce_available_variation', __CLASS__ . '::maybe_set_variations_price_html', 10, 3 );
// Sync variable product min/max prices with WC 3.0
add_action( 'woocommerce_variable_product_sync_data', __CLASS__ . '::variable_subscription_product_sync', 10 );
// Prevent users from deleting subscription products - it causes too many problems with WooCommerce and other plugins // Prevent users from deleting subscription products - it causes too many problems with WooCommerce and other plugins
add_filter( 'user_has_cap', __CLASS__ . '::user_can_not_delete_subscription', 10, 3 ); add_filter( 'user_has_cap', __CLASS__ . '::user_can_not_delete_subscription', 10, 3 );
@@ -71,23 +74,6 @@ class WC_Subscriptions_Product {
add_action( 'wp_ajax_wcs_update_one_time_shipping', __CLASS__ . '::maybe_update_one_time_shipping_on_variation_edits' ); add_action( 'wp_ajax_wcs_update_one_time_shipping', __CLASS__ . '::maybe_update_one_time_shipping_on_variation_edits' );
} }
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
*
* @return string
*/
public static function get_sign_up_fee_including_tax( $product, $qty = 1 ) {
add_filter( 'woocommerce_get_price', __CLASS__ . '::get_sign_up_fee_filter', 100, 2 );
$sign_up_fee_including_tax = $product->get_price_including_tax( $qty );
remove_filter( 'woocommerce_get_price', __CLASS__ . '::get_sign_up_fee_filter', 100, 2 );
return $sign_up_fee_including_tax;
}
/** /**
* Returns the raw sign up fee value (ignoring tax) by filtering the products price. * Returns the raw sign up fee value (ignoring tax) by filtering the products price.
* *
@@ -98,23 +84,6 @@ class WC_Subscriptions_Product {
return self::get_sign_up_fee( $product ); return self::get_sign_up_fee( $product );
} }
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @return string
*/
public static function get_sign_up_fee_excluding_tax( $product, $qty = 1 ) {
add_filter( 'woocommerce_get_price', __CLASS__ . '::get_sign_up_fee_filter', 100, 2 );
$sign_up_fee_excluding_tax = $product->get_price_excluding_tax( $qty );
remove_filter( 'woocommerce_get_price', __CLASS__ . '::get_sign_up_fee_filter', 100, 2 );
return $sign_up_fee_excluding_tax;
}
/** /**
* Override the WooCommerce "Add to Cart" text with "Sign Up Now". * Override the WooCommerce "Add to Cart" text with "Sign Up Now".
* *
@@ -134,25 +103,20 @@ class WC_Subscriptions_Product {
* Checks a given product to determine if it is a subscription. * Checks a given product to determine if it is a subscription.
* When the received arg is a product object, make sure it is passed into the filter intact in order to retain any properties added on the fly. * When the received arg is a product object, make sure it is passed into the filter intact in order to retain any properties added on the fly.
* *
* @param int|WC_Product $product_id Either a product object or product's post ID. * @param int|WC_Product $product Either a product object or product's post ID.
* @since 1.0 * @since 1.0
*/ */
public static function is_subscription( $product_id ) { public static function is_subscription( $product ) {
$is_subscription = false; $is_subscription = false;
if ( is_object( $product_id ) ) { $product = self::maybe_get_product_instance( $product );
$product = $product_id;
$product_id = $product->id;
} elseif ( is_numeric( $product_id ) ) {
$product = wc_get_product( $product_id );
}
if ( is_object( $product ) && $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' ) ) ) { if ( is_object( $product ) && $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' ) ) ) {
$is_subscription = true; $is_subscription = true;
} }
return apply_filters( 'woocommerce_is_subscription', $is_subscription, $product_id, $product ); return apply_filters( 'woocommerce_is_subscription', $is_subscription, $product->get_id(), $product );
} }
/** /**
@@ -174,8 +138,9 @@ class WC_Subscriptions_Product {
$child_product = wc_get_product( $child_product_id ); $child_product = wc_get_product( $child_product_id );
$child_price = $child_product->get_price(); $tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
$sign_up_fee = $child_product->get_sign_up_fee(); $child_price = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $child_product, array( 'price' => $child_product->get_price() ) ) : wcs_get_price_excluding_tax( $child_product, array( 'price' => $child_product->get_price() ) );
$sign_up_fee = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $child_product, array( 'price' => self::get_sign_up_fee( $child_product ) ) ) : wcs_get_price_excluding_tax( $child_product, array( 'price' => self::get_sign_up_fee( $child_product ) ) );
$has_trial = ( self::get_trial_length( $child_product ) > 0 ) ? true : false; $has_trial = ( self::get_trial_length( $child_product ) > 0 ) ? true : false;
// Make sure we have the *real* price (i.e. total initial payment) // Make sure we have the *real* price (i.e. total initial payment)
@@ -209,7 +174,7 @@ class WC_Subscriptions_Product {
} }
if ( sizeof( $child_prices ) > 1 ) { if ( sizeof( $child_prices ) > 1 ) {
$price .= $grouped_product->get_price_html_from_text(); $price .= wcs_get_price_html_from_text( $grouped_product );
} }
$price .= wc_price( $min_price ); $price .= wc_price( $min_price );
@@ -247,9 +212,7 @@ class WC_Subscriptions_Product {
public static function get_price_string( $product, $include = array() ) { public static function get_price_string( $product, $include = array() ) {
global $wp_locale; global $wp_locale;
if ( ! is_object( $product ) ) { $product = self::maybe_get_product_instance( $product );
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) ) { if ( ! self::is_subscription( $product ) ) {
return; return;
@@ -284,22 +247,22 @@ class WC_Subscriptions_Product {
if ( isset( $include['price'] ) ) { if ( isset( $include['price'] ) ) {
$price = $include['price']; $price = $include['price'];
} else { } else {
$price = $product->get_price_excluding_tax( 1, $include['price'] ); $price = wcs_get_price_excluding_tax( $product, array( 'price' => $include['price'] ) );
} }
if ( true === $include['sign_up_fee'] ) { if ( true === $include['sign_up_fee'] ) {
$sign_up_fee = self::get_sign_up_fee_excluding_tax( $product ); $sign_up_fee = wcs_get_price_excluding_tax( $product, array( 'price' => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) );
} }
} else { // Add Tax } else { // Add Tax
if ( isset( $include['price'] ) ) { if ( isset( $include['price'] ) ) {
$price = $include['price']; $price = $include['price'];
} else { } else {
$price = $product->get_price_including_tax(); $price = wcs_get_price_including_tax( $product );
} }
if ( true === $include['sign_up_fee'] ) { if ( true === $include['sign_up_fee'] ) {
$sign_up_fee = self::get_sign_up_fee_including_tax( $product ); $sign_up_fee = wcs_get_price_including_tax( $product, array( 'price' => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) );
} }
} }
} else { } else {
@@ -421,18 +384,43 @@ class WC_Subscriptions_Product {
* @since 1.0 * @since 1.0
*/ */
public static function get_price( $product ) { public static function get_price( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_price', self::get_meta_data( $product, 'subscription_price', 0 ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
} }
if ( ! self::is_subscription( $product ) || ( ! isset( $product->subscription_price ) && empty( $product->product_custom_fields['_subscription_price'][0] ) ) ) { /**
$subscription_price = ''; * Returns the sale price per period for a product if it is a subscription.
*
* @param mixed $product A WC_Product object or product ID
* @return float
* @since 2.2.0
*/
public static function get_regular_price( $product, $context = 'view' ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$regular_price = $product->regular_price;
} else { } else {
$subscription_price = isset( $product->subscription_price ) ? $product->subscription_price : $product->product_custom_fields['_subscription_price'][0]; $regular_price = $product->get_regular_price( $context );
} }
return apply_filters( 'woocommerce_subscriptions_product_price', $subscription_price, $product ); return apply_filters( 'woocommerce_subscriptions_product_regular_price', $regular_price, $product );
}
/**
* Returns the regular price per period for a product if it is a subscription.
*
* @param mixed $product A WC_Product object or product ID
* @return float
* @since 2.2.0
*/
public static function get_sale_price( $product, $context = 'view' ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$sale_price = $product->sale_price;
} else {
$sale_price = $product->get_sale_price( $context );
}
return apply_filters( 'woocommerce_subscriptions_product_sale_price', $sale_price, $product );
} }
/** /**
@@ -443,18 +431,7 @@ class WC_Subscriptions_Product {
* @since 1.0 * @since 1.0
*/ */
public static function get_period( $product ) { public static function get_period( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_period', self::get_meta_data( $product, 'subscription_period', '' ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) || ( ! isset( $product->subscription_period ) && empty( $product->product_custom_fields['_subscription_period'][0] ) ) ) {
$subscription_period = '';
} else {
$subscription_period = isset( $product->subscription_period ) ? $product->subscription_period : $product->product_custom_fields['_subscription_period'][0];
}
return apply_filters( 'woocommerce_subscriptions_product_period', $subscription_period, $product );
} }
/** /**
@@ -465,18 +442,7 @@ class WC_Subscriptions_Product {
* @since 1.0 * @since 1.0
*/ */
public static function get_interval( $product ) { public static function get_interval( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_period_interval', self::get_meta_data( $product, 'subscription_period_interval', 0 ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) || ( ! isset( $product->subscription_period_interval ) && empty( $product->product_custom_fields['_subscription_period_interval'][0] ) ) ) {
$subscription_period_interval = 1;
} else {
$subscription_period_interval = isset( $product->subscription_period_interval ) ? $product->subscription_period_interval : $product->product_custom_fields['_subscription_period_interval'][0];
}
return apply_filters( 'woocommerce_subscriptions_product_period_interval', $subscription_period_interval, $product );
} }
/** /**
@@ -487,18 +453,7 @@ class WC_Subscriptions_Product {
* @since 1.0 * @since 1.0
*/ */
public static function get_length( $product ) { public static function get_length( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_length', self::get_meta_data( $product, 'subscription_length', 0 ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) || ( ! isset( $product->subscription_length ) && empty( $product->product_custom_fields['_subscription_length'][0] ) ) ) {
$subscription_length = 0;
} else {
$subscription_length = isset( $product->subscription_length ) ? $product->subscription_length : $product->product_custom_fields['_subscription_length'][0];
}
return apply_filters( 'woocommerce_subscriptions_product_length', $subscription_length, $product );
} }
/** /**
@@ -509,18 +464,7 @@ class WC_Subscriptions_Product {
* @since 1.0 * @since 1.0
*/ */
public static function get_trial_length( $product ) { public static function get_trial_length( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_trial_length', self::get_meta_data( $product, 'subscription_trial_length', 0 ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) || ( ! isset( $product->subscription_trial_length ) && empty( $product->product_custom_fields['_subscription_trial_length'][0] ) ) ) {
$subscription_trial_length = 0;
} else {
$subscription_trial_length = isset( $product->subscription_trial_length ) ? $product->subscription_trial_length : $product->product_custom_fields['_subscription_trial_length'][0];
}
return apply_filters( 'woocommerce_subscriptions_product_trial_length', $subscription_trial_length, $product );
} }
/** /**
@@ -531,20 +475,7 @@ class WC_Subscriptions_Product {
* @since 1.2 * @since 1.2
*/ */
public static function get_trial_period( $product ) { public static function get_trial_period( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_trial_period', self::get_meta_data( $product, 'subscription_trial_period', '' ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) ) {
$subscription_trial_period = '';
} elseif ( ! isset( $product->subscription_trial_period ) && empty( $product->product_custom_fields['_subscription_trial_period'][0] ) ) { // Backward compatibility
$subscription_trial_period = self::get_period( $product );
} else {
$subscription_trial_period = isset( $product->subscription_trial_period ) ? $product->subscription_trial_period : $product->product_custom_fields['_subscription_trial_period'][0];
}
return apply_filters( 'woocommerce_subscriptions_product_trial_period', $subscription_trial_period, $product );
} }
/** /**
@@ -555,18 +486,7 @@ class WC_Subscriptions_Product {
* @since 1.0 * @since 1.0
*/ */
public static function get_sign_up_fee( $product ) { public static function get_sign_up_fee( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_sign_up_fee', self::get_meta_data( $product, 'subscription_sign_up_fee', 0 ), self::maybe_get_product_instance( $product ) );
if ( ! is_object( $product ) ) {
$product = WC_Subscriptions::get_product( $product );
}
if ( ! self::is_subscription( $product ) || ( ! isset( $product->subscription_sign_up_fee ) && empty( $product->product_custom_fields['_subscription_sign_up_fee'][0] ) ) ) {
$subscription_sign_up_fee = 0;
} else {
$subscription_sign_up_fee = isset( $product->subscription_sign_up_fee ) ? $product->subscription_sign_up_fee : $product->product_custom_fields['_subscription_sign_up_fee'][0];
}
return apply_filters( 'woocommerce_subscriptions_product_sign_up_fee', $subscription_sign_up_fee, $product );
} }
/** /**
@@ -735,7 +655,7 @@ class WC_Subscriptions_Product {
*/ */
public static function maybe_set_variations_price_html( $variation_details, $variable_product, $variation ) { public static function maybe_set_variations_price_html( $variation_details, $variable_product, $variation ) {
if ( 'variable-subscription' == $variable_product->product_type && empty( $variation_details['price_html'] ) ) { if ( $variable_product->is_type( 'variable-subscription' ) && empty( $variation_details['price_html'] ) ) {
$variation_details['price_html'] = '<span class="price">' . $variation->get_price_html() . '</span>'; $variation_details['price_html'] = '<span class="price">' . $variation->get_price_html() . '</span>';
} }
@@ -759,9 +679,9 @@ class WC_Subscriptions_Product {
$post_id = $args[2]; $post_id = $args[2];
$product = wc_get_product( $post_id ); $product = wc_get_product( $post_id );
if ( false !== $product && 'trash' == $product->post->post_status && $product->is_type( array( 'subscription', 'variable-subscription', 'subscription_variation' ) ) ) { if ( false !== $product && 'trash' == wcs_get_objects_property( $product, 'post_status' ) && $product->is_type( array( 'subscription', 'variable-subscription', 'subscription_variation' ) ) ) {
$product_id = ( $product->is_type( 'subscription_variation' ) ) ? $product->post->ID : $post_id; $product_id = ( $product->is_type( 'subscription_variation' ) ) ? $product->get_parent_id() : $post_id;
$subscription_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->prefix}woocommerce_order_itemmeta` WHERE `meta_key` = '_product_id' AND `meta_value` = %d", $product_id ) ); $subscription_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->prefix}woocommerce_order_itemmeta` WHERE `meta_key` = '_product_id' AND `meta_value` = %d", $product_id ) );
@@ -820,6 +740,17 @@ class WC_Subscriptions_Product {
return $actions; return $actions;
} }
/**
* Check whether a product has one-time shipping only.
*
* @param mixed $product A WC_Product object or product ID
* @return bool True if the product requires only one time shipping, false otherwise.
* @since 2.2.0
*/
public static function needs_one_time_shipping( $product ) {
return apply_filters( 'woocommerce_subscriptions_product_needs_one_time_shipping', 'yes' === self::get_meta_data( $product, 'subscription_one_time_shipping', 'no' ), self::maybe_get_product_instance( $product ) );
}
/** /**
* Hooked to the @see 'wp_scheduled_delete' WP-Cron scheduled task to rename the '_wp_trash_meta_time' meta value * Hooked to the @see 'wp_scheduled_delete' WP-Cron scheduled task to rename the '_wp_trash_meta_time' meta value
* as '_wc_trash_meta_time'. This is the flag used by WordPress to determine which posts should be automatically * as '_wc_trash_meta_time'. This is the flag used by WordPress to determine which posts should be automatically
@@ -965,14 +896,14 @@ class WC_Subscriptions_Product {
continue; continue;
} }
$variable_product = wc_get_product( $variation_id ); $variation_product = wc_get_product( $variation_id );
if ( $variable_product->subscription_trial_length > 0 ) { if ( WC_Subscriptions_Product::get_trial_length( $variation_product ) ) {
$is_synced_or_has_trial = true; $is_synced_or_has_trial = true;
break; break;
} }
if ( WC_Subscriptions_Synchroniser::is_syncing_enabled() && ( ( ! is_array( $variable_product->subscription_payment_sync_date ) && $variable_product->subscription_payment_sync_date > 0 ) || ( is_array( $variable_product->subscription_payment_sync_date ) && $variable_product->subscription_payment_sync_date['day'] > 0 ) ) ) { if ( WC_Subscriptions_Synchroniser::is_product_synced( $variation_product ) ) {
$is_synced_or_has_trial = true; $is_synced_or_has_trial = true;
break; break;
} }
@@ -1008,6 +939,102 @@ class WC_Subscriptions_Product {
wp_send_json( array( 'one_time_shipping' => $subscription_one_time_shipping ) ); wp_send_json( array( 'one_time_shipping' => $subscription_one_time_shipping ) );
} }
/**
* Wrapper to check whether we have a product ID or product and if we have the former, return the later.
*
* @param mixed $product A WC_Product object or product ID
* @return WC_Product
* @since 2.2.0
*/
private static function maybe_get_product_instance( $product ) {
if ( ! is_object( $product ) || ! is_a( $product, 'WC_Product' ) ) {
$product = wc_get_product( $product );
}
return $product;
}
/**
* Get a piece of subscription related meta data for a product in a version compatible way.
*
* @param mixed $product A WC_Product object or product ID
* @param string $meta_key The string key for the meta data
* @return float The value of the sign-up fee, or 0 if the product is not a subscription or the subscription has no sign-up fee
* @since 2.2.0
*/
public static function get_meta_data( $product, $meta_key, $default_value ) {
$product = self::maybe_get_product_instance( $product );
$meta_value = $default_value;
if ( self::is_subscription( $product ) ) {
if ( is_callable( array( $product, 'meta_exists' ) ) ) { // WC 3.0
$prefixed_key = wcs_maybe_prefix_key( $meta_key );
// Only set the meta value when the object has a meta value to workaround ambiguous default return values
if ( $product->meta_exists( $prefixed_key ) ) {
$meta_value = $product->get_meta( $prefixed_key, true );
}
} elseif ( isset( $product->{$meta_key} ) ) { // WC < 3.0
$meta_value = $product->{$meta_key};
}
}
return $meta_value;
}
/**
* sync variable product min/max prices with WC 3.0
*
* @param WC_Product_Variable $product
* @since 2.2.0
*/
public static function variable_subscription_product_sync( $product ) {
if ( self::is_subscription( $product ) ) {
$child_variation_ids = $product->get_visible_children();
if ( $child_variation_ids ) {
$min_max_data = wcs_get_min_max_variation_data( $product, $child_variation_ids );
$product->add_meta_data( '_min_price_variation_id', $min_max_data['min']['variation_id'], true );
$product->add_meta_data( '_max_price_variation_id', $min_max_data['max']['variation_id'], true );
$product->add_meta_data( '_min_variation_price', $min_max_data['min']['price'], true );
$product->add_meta_data( '_max_variation_price', $min_max_data['max']['price'], true );
$product->add_meta_data( '_min_variation_regular_price', $min_max_data['min']['regular_price'], true );
$product->add_meta_data( '_max_variation_regular_price', $min_max_data['max']['regular_price'], true );
$product->add_meta_data( '_min_variation_sale_price', $min_max_data['min']['sale_price'], true );
$product->add_meta_data( '_max_variation_sale_price', $min_max_data['max']['sale_price'], true );
$product->add_meta_data( '_min_variation_period', $min_max_data['min']['period'], true );
$product->add_meta_data( '_max_variation_period', $min_max_data['max']['period'], true );
$product->add_meta_data( '_min_variation_period_interval', $min_max_data['min']['interval'], true );
$product->add_meta_data( '_max_variation_period_interval', $min_max_data['max']['interval'], true );
$product->add_meta_data( '_subscription_price', $min_max_data['min']['price'], true );
$product->add_meta_data( '_subscription_period', $min_max_data['min']['period'], true );
$product->add_meta_data( '_subscription_period_interval', $min_max_data['min']['interval'], true );
$product->add_meta_data( '_subscription_sign_up_fee', $min_max_data['subscription']['signup-fee'], true );
$product->add_meta_data( '_subscription_trial_period', $min_max_data['subscription']['trial_period'], true );
$product->add_meta_data( '_subscription_trial_length', $min_max_data['subscription']['trial_length'], true );
$product->add_meta_data( '_subscription_length', $min_max_data['subscription']['length'], true );
}
}
return $product;
}
/************************
* Deprecated Functions *
************************/
/** /**
* If a product is being marked as not purchasable because it is limited and the customer has a subscription, * If a product is being marked as not purchasable because it is limited and the customer has a subscription,
* but the current request is to resubscribe to the subscription, then mark it as purchasable. * but the current request is to resubscribe to the subscription, then mark it as purchasable.
@@ -1045,7 +1072,7 @@ class WC_Subscriptions_Product {
if ( $item['product_id'] == $product_id || $item['variation_id'] == $product_id ) { if ( $item['product_id'] == $product_id || $item['variation_id'] == $product_id ) {
$subscriptions = wcs_get_subscriptions( array( $subscriptions = wcs_get_subscriptions( array(
'order_id' => $order->id, 'order_id' => wcs_get_objects_property( $order, 'id' ),
'product_id' => $product_id, 'product_id' => $product_id,
) ); ) );
@@ -1065,6 +1092,28 @@ class WC_Subscriptions_Product {
return self::$order_awaiting_payment_for_product[ $product_id ]; return self::$order_awaiting_payment_for_product[ $product_id ];
} }
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
*
* @return string
*/
public static function get_sign_up_fee_including_tax( $product, $qty = 1 ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_including_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' );
return wcs_get_price_including_tax( $product, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) );
}
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @return string
*/
public static function get_sign_up_fee_excluding_tax( $product, $qty = 1 ) {
wcs_deprecated_function( __METHOD__, '2.2.0', 'wcs_get_price_excluding_tax( $product, array( "qty" => $qty, "price" => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) )' );
return wcs_get_price_excluding_tax( $product, array( 'qty' => $qty, 'price' => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) );
}
} }
WC_Subscriptions_Product::init(); WC_Subscriptions_Product::init();

View File

@@ -84,6 +84,8 @@ class WC_Subscriptions_Renewal_Order {
$order_needed_payment = in_array( $orders_old_status, apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'on-hold', 'failed' ), $order ) ); $order_needed_payment = in_array( $orders_old_status, apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'on-hold', 'failed' ), $order ) );
if ( $order_completed && $order_needed_payment ) { if ( $order_completed && $order_needed_payment ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$update_post_data = array( $update_post_data = array(
'ID' => $order_id, 'ID' => $order_id,
'post_date' => current_time( 'mysql', 0 ), 'post_date' => current_time( 'mysql', 0 ),
@@ -92,6 +94,11 @@ class WC_Subscriptions_Renewal_Order {
wp_update_post( $update_post_data ); wp_update_post( $update_post_data );
update_post_meta( $order_id, '_paid_date', current_time( 'mysql', true ) ); update_post_meta( $order_id, '_paid_date', current_time( 'mysql', true ) );
} else {
// In WC 3.0, only the paid date prop represents the paid date, the post date isn't used anymore, also the paid date is stored and referenced as a timestamp in site timezone, not a MySQL string
$order->set_date_paid( current_time( 'timestamp', 0 ) );
$order->save();
}
} }
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
@@ -138,7 +145,7 @@ class WC_Subscriptions_Renewal_Order {
$order_number = sprintf( _x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), $renewal_order->get_order_number() ); $order_number = sprintf( _x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), $renewal_order->get_order_number() );
// translators: placeholder is order ID // translators: placeholder is order ID
$subscription->add_order_note( sprintf( __( 'Order %s created to record renewal.', 'woocommerce-subscriptions' ), sprintf( '<a href="%s">%s</a> ', esc_url( wcs_get_edit_post_link( $renewal_order->id ) ), $order_number ) ) ); $subscription->add_order_note( sprintf( __( 'Order %s created to record renewal.', 'woocommerce-subscriptions' ), sprintf( '<a href="%s">%s</a> ', esc_url( wcs_get_edit_post_link( wcs_get_objects_property( $renewal_order, 'id' ) ) ), $order_number ) ) );
} }
return $renewal_order; return $renewal_order;
@@ -184,8 +191,14 @@ class WC_Subscriptions_Renewal_Order {
); );
foreach ( $order_items as $order_item_id => $item ) { foreach ( $order_items as $order_item_id => $item ) {
if ( is_callable( array( $item, 'delete_meta_data' ) ) ) { // WC 3.0+
foreach( $switched_order_item_keys as $switch_meta_key => $value ) {
$item->delete_meta_data( $switch_meta_key );
}
} else { // WC 2.6
$order_items[ $order_item_id ]['item_meta'] = array_diff_key( $item['item_meta'], $switched_order_item_keys ); $order_items[ $order_item_id ]['item_meta'] = array_diff_key( $item['item_meta'], $switched_order_item_keys );
} }
}
return $order_items; return $order_items;
} }
@@ -210,7 +223,7 @@ class WC_Subscriptions_Renewal_Order {
$subscription = wcs_get_subscription_from_key( $subscription_key ); $subscription = wcs_get_subscription_from_key( $subscription_key );
$renewal_order = wcs_create_renewal_order( $subscription ); $renewal_order = wcs_create_renewal_order( $subscription );
$renewal_order->payment_complete(); $renewal_order->payment_complete();
return $renewal_order->id; return wcs_get_objects_property( $renewal_order, 'id' );
} }
/** /**
@@ -228,7 +241,7 @@ class WC_Subscriptions_Renewal_Order {
_deprecated_function( __METHOD__, '2.0', 'wcs_create_renewal_order( WC_Subscription $subscription )' ); _deprecated_function( __METHOD__, '2.0', 'wcs_create_renewal_order( WC_Subscription $subscription )' );
$renewal_order = wcs_create_renewal_order( wcs_get_subscription_from_key( $subscription_key ) ); $renewal_order = wcs_create_renewal_order( wcs_get_subscription_from_key( $subscription_key ) );
$renewal_order->update_status( 'failed' ); $renewal_order->update_status( 'failed' );
return $renewal_order->id; return wcs_get_objects_property( $renewal_order, 'id' );
} }
/** /**
@@ -246,7 +259,7 @@ class WC_Subscriptions_Renewal_Order {
*/ */
public static function maybe_generate_manual_renewal_order( $user_id, $subscription_key ) { public static function maybe_generate_manual_renewal_order( $user_id, $subscription_key ) {
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::maybe_create_manual_renewal_order( WC_Subscription $subscription )' ); _deprecated_function( __METHOD__, '2.0', __CLASS__ . '::maybe_create_manual_renewal_order( WC_Subscription $subscription )' );
self::maybe_create_manual_renewal_order( wcs_get_subscription_from_key( $subscription_key ) )->id; self::maybe_create_manual_renewal_order( wcs_get_subscription_from_key( $subscription_key ) );
} }
/** /**
@@ -264,7 +277,7 @@ class WC_Subscriptions_Renewal_Order {
$parent_order = self::get_parent_order( $renewal_order ); $parent_order = self::get_parent_order( $renewal_order );
return ( null === $parent_order ) ? null : $parent_order->id; return ( null === $parent_order ) ? null : wcs_get_objects_property( $parent_order, 'id' );
} }
/** /**
@@ -287,10 +300,10 @@ class WC_Subscriptions_Renewal_Order {
$subscriptions = wcs_get_subscriptions_for_renewal_order( $renewal_order ); $subscriptions = wcs_get_subscriptions_for_renewal_order( $renewal_order );
$subscription = array_pop( $subscriptions ); $subscription = array_pop( $subscriptions );
if ( false === $subscription->order ) { // There is no original order if ( false == $subscription->get_parent_id() ) { // There is no original order
$parent_order = null; $parent_order = null;
} else { } else {
$parent_order = $subscription->order; $parent_order = $subscription->get_parent();
} }
return apply_filters( 'woocommerce_subscriptions_parent_order', $parent_order, $renewal_order ); return apply_filters( 'woocommerce_subscriptions_parent_order', $parent_order, $renewal_order );
@@ -316,7 +329,7 @@ class WC_Subscriptions_Renewal_Order {
$renewal_order_count = count( $all_orders ); $renewal_order_count = count( $all_orders );
// Don't include the initial order (if any) // Don't include the initial order (if any)
if ( false !== $subscription->order ) { if ( $subscription->get_parent_id() ) {
$renewal_order_count -= 1; $renewal_order_count -= 1;
} }
} else { } else {
@@ -426,7 +439,7 @@ class WC_Subscriptions_Renewal_Order {
$new_order = wcs_create_renewal_order( $subscription ); $new_order = wcs_create_renewal_order( $subscription );
} }
return $new_order->id; return wcs_get_objects_property( $new_order, 'id' );
} }
/** /**

View File

@@ -17,6 +17,9 @@ class WC_Subscriptions_Switcher {
*/ */
public static function init() { public static function init() {
// Attach hooks which depend on WooCommerce constants
add_action( 'woocommerce_loaded', __CLASS__ . '::attach_dependant_hooks' );
// Check if the current request is for switching a subscription and if so, start he switching process // Check if the current request is for switching a subscription and if so, start he switching process
add_filter( 'template_redirect', __CLASS__ . '::subscription_switch_handler', 100 ); add_filter( 'template_redirect', __CLASS__ . '::subscription_switch_handler', 100 );
@@ -36,12 +39,6 @@ class WC_Subscriptions_Switcher {
// When creating an order, add meta if it's for switching a subscription // When creating an order, add meta if it's for switching a subscription
add_action( 'woocommerce_checkout_update_order_meta', __CLASS__ . '::add_order_meta', 10, 2 ); add_action( 'woocommerce_checkout_update_order_meta', __CLASS__ . '::add_order_meta', 10, 2 );
// For order items created as part of a switch, keep a record of the prorated amounts
add_action( 'woocommerce_add_order_item_meta', __CLASS__ . '::add_order_item_meta', 10, 3 );
// For subscription items created as part of a switch, keep a record of the relationship between the items
add_action( 'woocommerce_add_subscription_item_meta', __CLASS__ . '::set_subscription_item_meta', 50, 3 );
// Add a renewal orders section to the Related Orders meta box // Add a renewal orders section to the Related Orders meta box
add_action( 'woocommerce_subscriptions_related_orders_meta_box_rows', __CLASS__ . '::switch_order_meta_box_rows', 10 ); add_action( 'woocommerce_subscriptions_related_orders_meta_box_rows', __CLASS__ . '::switch_order_meta_box_rows', 10 );
@@ -125,6 +122,28 @@ class WC_Subscriptions_Switcher {
add_action( 'woocommerce_review_order_after_shipping', __CLASS__ . '::maybe_unset_free_trial' ); add_action( 'woocommerce_review_order_after_shipping', __CLASS__ . '::maybe_unset_free_trial' );
} }
/**
* Attach WooCommerce version dependent hooks
*
* @since 2.2.0
*/
public static function attach_dependant_hooks() {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
// For order items created as part of a switch, keep a record of the prorated amounts
add_action( 'woocommerce_add_order_item_meta', __CLASS__ . '::add_order_item_meta', 10, 3 );
// For subscription items created as part of a switch, keep a record of the relationship between the items
add_action( 'woocommerce_add_subscription_item_meta', __CLASS__ . '::set_subscription_item_meta', 50, 3 );
} else {
// For order items created as part of a switch, keep a record of the prorated amounts
add_action( 'woocommerce_checkout_create_order_line_item', __CLASS__ . '::add_line_item_meta', 10, 4 );
}
}
/** /**
* Handles the subscription upgrade/downgrade process. * Handles the subscription upgrade/downgrade process.
* *
@@ -206,7 +225,7 @@ class WC_Subscriptions_Switcher {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
foreach ( $limited_switchable_products as $product ) { foreach ( $limited_switchable_products as $product ) {
if ( ! $subscription->has_product( $product->id ) ) { if ( ! $subscription->has_product( $product->get_id() ) ) {
continue; continue;
} }
@@ -233,7 +252,7 @@ class WC_Subscriptions_Switcher {
// Get the matching item // Get the matching item
foreach ( $subscription->get_items() as $line_item_id => $line_item ) { foreach ( $subscription->get_items() as $line_item_id => $line_item ) {
if ( $line_item['product_id'] == $product->id || $line_item['variation_id'] == $product->id ) { if ( $line_item['product_id'] == $product->get_id() || $line_item['variation_id'] == $product->get_id() ) {
$item_id = $line_item_id; $item_id = $line_item_id;
$item = $line_item; $item = $line_item;
break; break;
@@ -403,7 +422,7 @@ class WC_Subscriptions_Switcher {
*/ */
public static function print_switch_link( $item_id, $item, $subscription ) { public static function print_switch_link( $item_id, $item, $subscription ) {
if ( 'shop_subscription' !== $subscription->order_type || ! self::can_item_be_switched_by_user( $item, $subscription ) ) { if ( wcs_is_order( $subscription ) || 'shop_subscription' !== $subscription->get_type() || ! self::can_item_be_switched_by_user( $item, $subscription ) ) {
return; return;
} }
@@ -431,10 +450,10 @@ class WC_Subscriptions_Switcher {
$additional_query_args = array(); $additional_query_args = array();
// Grouped product // Grouped product
if ( 0 !== $product->post->post_parent ) { if ( wcs_get_objects_property( $product, 'parent_id' ) ) {
$switch_url = get_permalink( $product->post->post_parent ); $switch_url = get_permalink( wcs_get_objects_property( $product, 'parent_id' ) );
} else { } else {
$switch_url = get_permalink( $product->id ); $switch_url = get_permalink( $product->get_id() );
if ( ! empty( $_GET ) && is_product() ) { if ( ! empty( $_GET ) && is_product() ) {
$product_variations = $product->get_variation_attributes(); $product_variations = $product->get_variation_attributes();
@@ -442,7 +461,7 @@ class WC_Subscriptions_Switcher {
} }
} }
$switch_url = self::add_switch_query_args( $subscription->id, $item_id, $switch_url, $additional_query_args ); $switch_url = self::add_switch_query_args( $subscription->get_id(), $item_id, $switch_url, $additional_query_args );
return apply_filters( 'woocommerce_subscriptions_switch_url', $switch_url, $item_id, $item, $subscription ); return apply_filters( 'woocommerce_subscriptions_switch_url', $switch_url, $item_id, $item, $subscription );
} }
@@ -487,7 +506,7 @@ class WC_Subscriptions_Switcher {
$is_product_switchable = false; $is_product_switchable = false;
} }
if ( $subscription->has_status( 'active' ) && 0 !== $subscription->get_date( 'last_payment' ) ) { if ( $subscription->has_status( 'active' ) && 0 !== $subscription->get_date( 'last_order_date_created' ) ) {
$is_subscription_switchable = true; $is_subscription_switchable = true;
} else { } else {
$is_subscription_switchable = false; $is_subscription_switchable = false;
@@ -524,7 +543,7 @@ class WC_Subscriptions_Switcher {
$item_can_be_switched = false; $item_can_be_switched = false;
if ( user_can( $user_id, 'switch_shop_subscription', $subscription->id ) && self::can_item_be_switched( $item, $subscription ) ) { if ( user_can( $user_id, 'switch_shop_subscription', $subscription->get_id() ) && self::can_item_be_switched( $item, $subscription ) ) {
$item_can_be_switched = true; $item_can_be_switched = true;
} }
@@ -541,14 +560,17 @@ class WC_Subscriptions_Switcher {
*/ */
public static function add_order_meta( $order_id, $posted ) { public static function add_order_meta( $order_id, $posted ) {
$order = wc_get_order( $order_id );
// delete all the existing subscription switch links before adding new ones // delete all the existing subscription switch links before adding new ones
delete_post_meta( $order_id, '_subscription_switch' ); wcs_delete_objects_property( $order, 'subscription_switch' );
$switches = self::cart_contains_switches(); $switches = self::cart_contains_switches();
if ( false !== $switches ) { if ( false !== $switches ) {
foreach ( $switches as $switch_details ) { foreach ( $switches as $switch_details ) {
add_post_meta( $order_id, '_subscription_switch', $switch_details['subscription_id'] ); wcs_set_objects_property( $order, 'subscription_switch', $switch_details['subscription_id'] );
} }
} }
} }
@@ -557,16 +579,23 @@ class WC_Subscriptions_Switcher {
* To prorate sign-up fee and recurring amounts correctly when the customer switches a subscription multiple times, keep a record of the * To prorate sign-up fee and recurring amounts correctly when the customer switches a subscription multiple times, keep a record of the
* amount for each on the order item. * amount for each on the order item.
* *
* @param int $order_item_id The ID of a WC_Order_Item object.
* @param array $cart_item The cart item's data.
* @param string $cart_item_key The hash used to identify the item in the cart
* @since 2.0 * @since 2.0
*/ */
public static function add_order_item_meta( $order_item_id, $cart_item, $cart_item_key ) { public static function add_order_item_meta( $order_item_id, $cart_item, $cart_item_key ) {
if ( false === WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
_deprecated_function( __METHOD__, '2.2.0 and WooCommerce 3.0.0', __CLASS__ . '::add_order_line_item_meta( $order_item, $cart_item_key, $cart_item )' );
}
if ( isset( $cart_item['subscription_switch'] ) ) { if ( isset( $cart_item['subscription_switch'] ) ) {
if ( $switches = self::cart_contains_switches() ) { if ( $switches = self::cart_contains_switches() ) {
foreach ( $switches as $switch_item_key => $switch_details ) { foreach ( $switches as $switch_item_key => $switch_details ) {
if ( $cart_item_key == $switch_item_key ) { if ( $cart_item_key == $switch_item_key ) {
wc_add_order_item_meta( $order_item_id, '_switched_subscription_sign_up_fee_prorated', ( isset( WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee_prorated ) ? WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee_prorated : 0 ), true ); wc_add_order_item_meta( $order_item_id, '_switched_subscription_sign_up_fee_prorated', wcs_get_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_sign_up_fee_prorated', 'single', 0 ), true );
wc_add_order_item_meta( $order_item_id, '_switched_subscription_price_prorated', ( isset( WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_price_prorated ) ? WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_price_prorated : 0 ), true ); wc_add_order_item_meta( $order_item_id, '_switched_subscription_price_prorated', wcs_get_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_price_prorated', 'single', 0 ), true );
} }
} }
} }
@@ -582,14 +611,59 @@ class WC_Subscriptions_Switcher {
} }
} }
/**
* Store switch related data on the line item on the subscription and switch order.
*
* For subscriptions: items on a new billing schedule are left to be added as new subscriptions, but we also want
* to keep a record of them being a switch, so we do that here.
*
* For orders: to prorate sign-up fee and recurring amounts correctly when the customer switches a subscription
* multiple times, keep a record of the amount for each on the order item.
*
* Attached to WC 3.0+ hooks and uses WC 3.0 methods.
*
* @param WC_Order_Item_Product $order_item
* @param string $cart_item_key The hash used to identify the item in the cart
* @param array $cart_item The cart item's data.
* @param WC_Order $order The order or subscription object to which the line item relates
* @since 2.2.0
*/
public static function add_line_item_meta( $order_item, $cart_item_key, $cart_item, $order ) {
if ( isset( $cart_item['subscription_switch'] ) ) {
if ( $switches = self::cart_contains_switches() ) {
foreach ( $switches as $switch_item_key => $switch_details ) {
if ( $cart_item_key == $switch_item_key ) {
if ( wcs_is_subscription( $order ) ) {
$order_item->add_meta_data( '_switched_subscription_item_id', $switch_details['item_id'] );
} else {
$sign_up_fee_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->get_meta( 'subscription_sign_up_fee_prorated', true );
$price_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->get_meta( 'subscription_price_prorated', true );
$order_item->add_meta_data( '_switched_subscription_sign_up_fee_prorated', empty( $sign_up_fee_prorated ) ? 0 : $sign_up_fee_prorated );
$order_item->add_meta_data( '_switched_subscription_price_prorated', empty( $price_prorated ) ? 0 : $price_prorated );
}
}
}
}
}
}
/** /**
* Subscription items on a new billing schedule are left to be added as new subscriptions, but we also * Subscription items on a new billing schedule are left to be added as new subscriptions, but we also
* want to keep a record of them being a switch, so we do that here. * want to keep a record of them being a switch, so we do that here.
* *
* @param int $order_item_id The ID of a WC_Order_Item object.
* @param array $cart_item The cart item's data.
* @param string $cart_item_key The hash used to identify the item in the cart
* @since 2.0 * @since 2.0
*/ */
public static function set_subscription_item_meta( $item_id, $cart_item, $cart_item_key ) { public static function set_subscription_item_meta( $item_id, $cart_item, $cart_item_key ) {
if ( ! WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
_deprecated_function( __METHOD__, '2.2.0', __CLASS__ . '::add_subscription_line_item_meta( $order_item, $cart_item_key, $cart_item )' );
}
if ( isset( $cart_item['subscription_switch'] ) ) { if ( isset( $cart_item['subscription_switch'] ) ) {
if ( $switches = self::cart_contains_switches() ) { if ( $switches = self::cart_contains_switches() ) {
foreach ( $switches as $switch_item_key => $switch_details ) { foreach ( $switches as $switch_item_key => $switch_details ) {
@@ -643,7 +717,7 @@ class WC_Subscriptions_Switcher {
$next_payment_timestamp = $subscription->get_time( 'end' ); $next_payment_timestamp = $subscription->get_time( 'end' );
} }
if ( $cart_item['data']->subscription_period != $subscription->billing_period || $cart_item['data']->subscription_period_interval != $subscription->billing_interval ) { if ( WC_Subscriptions_Product::get_period( $cart_item['data'] ) != $subscription->get_billing_period() || WC_Subscriptions_Product::get_interval( $cart_item['data'] ) != $subscription->get_billing_interval() ) {
$is_different_billing_schedule = true; $is_different_billing_schedule = true;
} else { } else {
$is_different_billing_schedule = false; $is_different_billing_schedule = false;
@@ -651,7 +725,7 @@ class WC_Subscriptions_Switcher {
// If we haven't calculated a first payment date, fall back to the recurring cart's next payment date // If we haven't calculated a first payment date, fall back to the recurring cart's next payment date
if ( 0 == $cart_item['subscription_switch']['first_payment_timestamp'] ) { if ( 0 == $cart_item['subscription_switch']['first_payment_timestamp'] ) {
$cart_item['subscription_switch']['first_payment_timestamp'] = strtotime( $recurring_cart->next_payment_date ); $cart_item['subscription_switch']['first_payment_timestamp'] = wcs_date_to_time( $recurring_cart->next_payment_date );
} }
if ( 0 !== $cart_item['subscription_switch']['first_payment_timestamp'] && $next_payment_timestamp !== $cart_item['subscription_switch']['first_payment_timestamp'] ) { if ( 0 !== $cart_item['subscription_switch']['first_payment_timestamp'] && $next_payment_timestamp !== $cart_item['subscription_switch']['first_payment_timestamp'] ) {
@@ -681,8 +755,45 @@ class WC_Subscriptions_Switcher {
if ( $is_single_item_subscription || ( false === $is_different_billing_schedule && false === $is_different_payment_date && false === $is_different_length ) ) { if ( $is_single_item_subscription || ( false === $is_different_billing_schedule && false === $is_different_payment_date && false === $is_different_length ) ) {
// Add the new item // Add the new item
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$item_id = WC_Subscriptions_Checkout::add_cart_item( $subscription, $cart_item, $cart_item_key ); $item_id = WC_Subscriptions_Checkout::add_cart_item( $subscription, $cart_item, $cart_item_key );
wc_update_order_item( $item_id, array( 'order_item_type' => 'line_item_pending_switch' ) ); wc_update_order_item( $item_id, array( 'order_item_type' => 'line_item_pending_switch' ) );
} else {
$item = new WC_Order_Item_Pending_Switch;
$item->legacy_values = $cart_item['data']; // @deprecated For legacy actions.
$item->legacy_cart_item_key = $cart_item_key; // @deprecated For legacy actions.
$item->set_props( array(
'quantity' => $cart_item['quantity'],
'variation' => $cart_item['variation'],
'subtotal' => $cart_item['line_subtotal'],
'total' => $cart_item['line_total'],
'subtotal_tax' => $cart_item['line_subtotal_tax'],
'total_tax' => $cart_item['line_tax'],
'taxes' => $cart_item['line_tax_data'],
) );
if ( ! empty( $cart_item['data'] ) ) {
$product = $cart_item['data'];
$item->set_props( array(
'name' => $product->get_name(),
'tax_class' => $product->get_tax_class(),
'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(),
'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0,
) );
}
if ( WC_Subscriptions_Product::get_trial_length( wcs_get_canonical_product_id( $cart_item ) ) > 0 ) {
$item->add_meta_data( '_has_trial', 'true' );
}
do_action( 'woocommerce_checkout_create_order_line_item', $item, $cart_item_key, $cart_item, $subscription );
$subscription->add_item( $item );
// The subscription is not saved automatically, we need to call 'save' becaused we added an item
$subscription->save();
$item_id = $item->get_id();
}
$switched_item_data['add_line_item'] = $item_id; $switched_item_data['add_line_item'] = $item_id;
@@ -695,19 +806,19 @@ class WC_Subscriptions_Switcher {
} }
} }
$switch_order_data[ $subscription->id ]['switches'][ $cart_item['subscription_switch']['order_line_item_id'] ] = $switched_item_data; $switch_order_data[ $subscription->get_id() ]['switches'][ $cart_item['subscription_switch']['order_line_item_id'] ] = $switched_item_data;
// If the old subscription has just one item, we can safely update its billing schedule // If the old subscription has just one item, we can safely update its billing schedule
if ( $is_single_item_subscription ) { if ( $is_single_item_subscription ) {
if ( $is_different_billing_schedule ) { if ( $is_different_billing_schedule ) {
$switch_order_data[ $subscription->id ]['billing_schedule']['_billing_period'] = $cart_item['data']->subscription_period; $switch_order_data[ $subscription->get_id() ]['billing_schedule']['_billing_period'] = WC_Subscriptions_Product::get_period( $cart_item['data'] );
$switch_order_data[ $subscription->id ]['billing_schedule']['_billing_interval'] = absint( $cart_item['data']->subscription_period_interval ); $switch_order_data[ $subscription->get_id() ]['billing_schedule']['_billing_interval'] = absint( WC_Subscriptions_Product::get_interval( $cart_item['data'] ) );
} }
$updated_dates = array(); $updated_dates = array();
if ( '1' == $cart_item['data']->subscription_length || ( 0 != $recurring_cart->end_date && gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ) >= $recurring_cart->end_date ) ) { if ( '1' == WC_Subscriptions_Product::get_length( $cart_item['data'] ) || ( 0 != $recurring_cart->end_date && gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ) >= $recurring_cart->end_date ) ) {
// Delete the next payment date. // Delete the next payment date.
$updated_dates['next_payment'] = 0; $updated_dates['next_payment'] = 0;
} else if ( $is_different_payment_date ) { } else if ( $is_different_payment_date ) {
@@ -720,7 +831,7 @@ class WC_Subscriptions_Switcher {
if ( ! empty( $updated_dates ) ) { if ( ! empty( $updated_dates ) ) {
$subscription->validate_date_updates( $updated_dates ); $subscription->validate_date_updates( $updated_dates );
$switch_order_data[ $subscription->id ]['dates']['update'] = $updated_dates; $switch_order_data[ $subscription->get_id() ]['dates']['update'] = $updated_dates;
} }
} }
@@ -730,10 +841,16 @@ class WC_Subscriptions_Switcher {
$new_shipping_line_items = array(); $new_shipping_line_items = array();
// Keep a record of the subscription shipping total. Adding shipping methods will cause a new shipping total to be set, we'll need to set it back after. // Keep a record of the subscription shipping total. Adding shipping methods will cause a new shipping total to be set, we'll need to set it back after.
$subscription_shipping_total = $subscription->order_shipping; $subscription_shipping_total = $subscription->get_total_shipping();
WC_Subscriptions_Checkout::add_shipping( $subscription, $recurring_cart ); WC_Subscriptions_Checkout::add_shipping( $subscription, $recurring_cart );
if ( ! WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
// We must save the subscription, we need the Shipping method saved
// otherwise the ID is bogus (new:1) and we need it.
$subscription->save();
}
// Set all new shipping methods to shipping_pending_switch line items // Set all new shipping methods to shipping_pending_switch line items
foreach ( $subscription->get_shipping_methods() as $shipping_line_item_id => $shipping_meta ) { foreach ( $subscription->get_shipping_methods() as $shipping_line_item_id => $shipping_meta ) {
@@ -743,9 +860,13 @@ class WC_Subscriptions_Switcher {
} }
} }
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$subscription->set_total( $subscription_shipping_total, 'shipping' ); $subscription->set_total( $subscription_shipping_total, 'shipping' );
} else {
$subscription->set_shipping_total( $subscription_shipping_total );
}
$switch_order_data[ $subscription->id ]['shipping_line_items'] = $new_shipping_line_items; $switch_order_data[ $subscription->get_id() ]['shipping_line_items'] = $new_shipping_line_items;
} }
} }
@@ -755,13 +876,13 @@ class WC_Subscriptions_Switcher {
$switch_orders = wcs_get_switch_orders_for_subscription( $subscription_id ); $switch_orders = wcs_get_switch_orders_for_subscription( $subscription_id );
foreach ( $switch_orders as $switch_order_id => $switch_order ) { foreach ( $switch_orders as $switch_order_id => $switch_order ) {
if ( $order->id !== $switch_order_id && in_array( $switch_order->get_status(), apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed', 'on-hold' ), $switch_order ) ) ) { if ( wcs_get_objects_property( $order, 'id' ) !== $switch_order_id && in_array( $switch_order->get_status(), apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed', 'on-hold' ), $switch_order ) ) ) {
$switch_order->update_status( 'cancelled', sprintf( __( 'Switch order cancelled due to a new switch order being created #%s.', 'woocommerce-subscriptions' ), $order->get_order_number() ) ); $switch_order->update_status( 'cancelled', sprintf( __( 'Switch order cancelled due to a new switch order being created #%s.', 'woocommerce-subscriptions' ), $order->get_order_number() ) );
} }
} }
} }
update_post_meta( $order_id, '_subscription_switch_data', $switch_order_data ); wcs_set_objects_property( $order, 'subscription_switch_data', $switch_order_data );
} catch ( Exception $e ) { } catch ( Exception $e ) {
// There was an error updating the subscription, roll back and delete pending order for switch // There was an error updating the subscription, roll back and delete pending order for switch
@@ -786,12 +907,12 @@ class WC_Subscriptions_Switcher {
} }
// Then zero the order_shipping total so we have a clean slate to add to // Then zero the order_shipping total so we have a clean slate to add to
$subscription->order_shipping = 0; $subscription->set_total_shipping( 0 );
WC_Subscriptions_Checkout::add_shipping( $subscription, $recurring_cart ); WC_Subscriptions_Checkout::add_shipping( $subscription, $recurring_cart );
// Now update subscription object order_shipping to reflect updated values so it doesn't stay 0 // Now update subscription object order_shipping to reflect updated values so it doesn't stay 0
$subscription->order_shipping = get_post_meta( $subscription->id, '_order_shipping', true ); $subscription->order_shipping = get_post_meta( $subscription->get_id(), '_order_shipping', true );
} }
/** /**
@@ -842,25 +963,27 @@ class WC_Subscriptions_Switcher {
// Select the orders which switched item/s from this subscription // Select the orders which switched item/s from this subscription
$orders = wcs_get_switch_orders_for_subscription( $post->ID ); $orders = wcs_get_switch_orders_for_subscription( $post->ID );
foreach ( $orders as $order ) { foreach ( $orders as $order_id => $order ) {
$orders[ $order->id ]->relationship = __( 'Switch Order', 'woocommerce-subscriptions' ); wcs_set_objects_property( $order, 'relationship', __( 'Switch Order', 'woocommerce-subscriptions' ), 'set_prop_only' );
} }
// Select the subscriptions which had item/s switched to this subscription by its parent order // Select the subscriptions which had item/s switched to this subscription by its parent order
if ( ! empty( $post->post_parent ) ) { if ( ! empty( $post->post_parent ) ) {
$switched_ids = get_post_meta( $post->post_parent, '_subscription_switch' ); $switched_ids = wcs_get_objects_property( wc_get_order( $post->post_parent ), 'subscription_switch', 'multiple' );
} }
// On the Edit Order screen, show any subscriptions with items switched by this order // On the Edit Order screen, show any subscriptions with items switched by this order
} else { } else {
$switched_ids = get_post_meta( $post->ID, '_subscription_switch', false ); $switched_ids = wcs_get_objects_property( wc_get_order( $post->ID ), 'subscription_switch', 'multiple' );
} }
if ( is_array( $switched_ids ) ) {
foreach ( $switched_ids as $subscription_id ) { foreach ( $switched_ids as $subscription_id ) {
$subscription = wcs_get_subscription( $subscription_id ); $subscription = wcs_get_subscription( $subscription_id );
$subscription->relationship = __( 'Switched Subscription', 'woocommerce-subscriptions' ); wcs_set_objects_property( $subscription, 'relationship', __( 'Switched Subscription', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[ $subscription_id ] = $subscription; $orders[ $subscription_id ] = $subscription;
} }
}
foreach ( $orders as $order ) { foreach ( $orders as $order ) {
include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'includes/admin/meta-boxes/views/html-related-orders-row.php' ); include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'includes/admin/meta-boxes/views/html-related-orders-row.php' );
@@ -908,7 +1031,7 @@ class WC_Subscriptions_Switcher {
*/ */
public static function cart_contains_switch_for_product( $product ) { public static function cart_contains_switch_for_product( $product ) {
$product_id = ( is_object( $product ) ) ? $product->id : $product; $product_id = ( is_object( $product ) ) ? $product->get_id() : $product;
$switch_items = self::cart_contains_switches(); $switch_items = self::cart_contains_switches();
$switch_product_ids = array(); $switch_product_ids = array();
@@ -920,10 +1043,12 @@ class WC_Subscriptions_Switcher {
$switch_product = wc_get_product( wcs_get_order_items_product_id( $switch_item_details['item_id'] ) ); $switch_product = wc_get_product( wcs_get_order_items_product_id( $switch_item_details['item_id'] ) );
// If the switch is for a grouped product, we need to check the other products grouped with this one // If the switch is for a grouped product, we need to check the other products grouped with this one
if ( 0 !== $product->post->post_parent ) { if ( wcs_get_objects_property( $product, 'parent_id' ) ) {
$switch_product_ids = array_unique( array_merge( $switch_product_ids, wc_get_product( $product->post->post_parent )->get_children() ) ); $switch_product_ids = array_unique( array_merge( $switch_product_ids, wc_get_product( wcs_get_objects_property( $product, 'parent_id' ) )->get_children() ) );
} elseif ( $switch_product->is_type( 'subscription_variation' ) ) {
$switch_product_ids[] = $switch_product->get_parent_id();
} else { } else {
$switch_product_ids[] = $switch_product->id; $switch_product_ids[] = $switch_product->get_id();
} }
} }
} }
@@ -1031,7 +1156,7 @@ class WC_Subscriptions_Switcher {
$subscription = wcs_get_subscription( $_GET['switch-subscription'] ); $subscription = wcs_get_subscription( $_GET['switch-subscription'] );
// Requesting a switch for someone elses subscription // Requesting a switch for someone elses subscription
if ( ! current_user_can( 'switch_shop_subscription', $subscription->id ) ) { if ( ! current_user_can( 'switch_shop_subscription', $subscription->get_id() ) ) {
WC_Subscriptions::add_notice( __( 'You can not switch this subscription. It appears you do not own the subscription.', 'woocommerce-subscriptions' ), 'error' ); WC_Subscriptions::add_notice( __( 'You can not switch this subscription. It appears you do not own the subscription.', 'woocommerce-subscriptions' ), 'error' );
WC()->cart->empty_cart( true ); WC()->cart->empty_cart( true );
wp_redirect( get_permalink( $subscription['product_id'] ) ); wp_redirect( get_permalink( $subscription['product_id'] ) );
@@ -1043,7 +1168,7 @@ class WC_Subscriptions_Switcher {
// Else it's a valid switch // Else it's a valid switch
$product = wc_get_product( $item['product_id'] ); $product = wc_get_product( $item['product_id'] );
$child_products = ( 0 !== $product->post->post_parent ) ? wc_get_product( $product->post->post_parent )->get_children() : array(); $child_products = ( wcs_get_objects_property( $product, 'parent_id' ) ) ? wc_get_product( wcs_get_objects_property( $product, 'parent_id' ) )->get_children() : array();
if ( $product_id != $item['product_id'] && ! in_array( $item['product_id'], $child_products ) ) { if ( $product_id != $item['product_id'] && ! in_array( $item['product_id'], $child_products ) ) {
return $cart_item_data; return $cart_item_data;
@@ -1057,7 +1182,7 @@ class WC_Subscriptions_Switcher {
} }
$cart_item_data['subscription_switch'] = array( $cart_item_data['subscription_switch'] = array(
'subscription_id' => $subscription->id, 'subscription_id' => $subscription->get_id(),
'item_id' => absint( $_GET['item'] ), 'item_id' => absint( $_GET['item'] ),
'next_payment_timestamp' => $next_payment_timestamp, 'next_payment_timestamp' => $next_payment_timestamp,
'upgraded_or_downgraded' => '', 'upgraded_or_downgraded' => '',
@@ -1107,7 +1232,7 @@ class WC_Subscriptions_Switcher {
// Default tax inclusive or exclusive to the value set on the subscription. This is for backwards compatibility // Default tax inclusive or exclusive to the value set on the subscription. This is for backwards compatibility
if ( empty( $tax_inclusive_or_exclusive ) ) { if ( empty( $tax_inclusive_or_exclusive ) ) {
$tax_inclusive_or_exclusive = ( 'yes' == $subscription->prices_include_tax ) ? 'inclusive_of_tax' : 'exclusive_of_tax'; $tax_inclusive_or_exclusive = ( $subscription->get_prices_include_tax() ) ? 'inclusive_of_tax' : 'exclusive_of_tax';
} }
foreach ( $switched_line_items as $switched_line_item_id => $switched_line_item ) { foreach ( $switched_line_items as $switched_line_item_id => $switched_line_item ) {
@@ -1118,7 +1243,7 @@ class WC_Subscriptions_Switcher {
} }
// Now add any sign-up fees paid in switch orders // Now add any sign-up fees paid in switch orders
foreach ( wcs_get_switch_orders_for_subscription( $subscription->id ) as $order ) { foreach ( wcs_get_switch_orders_for_subscription( $subscription->get_id() ) as $order ) {
foreach ( $order->get_items() as $order_item_id => $order_item ) { foreach ( $order->get_items() as $order_item_id => $order_item ) {
if ( wcs_get_canonical_product_id( $line_item ) == wcs_get_canonical_product_id( $order_item ) ) { if ( wcs_get_canonical_product_id( $line_item ) == wcs_get_canonical_product_id( $order_item ) ) {
@@ -1135,7 +1260,7 @@ class WC_Subscriptions_Switcher {
$order_total = $order_item['line_total']; $order_total = $order_item['line_total'];
if ( 'inclusive_of_tax' == $tax_inclusive_or_exclusive && 'yes' == $order->prices_include_tax ) { if ( 'inclusive_of_tax' == $tax_inclusive_or_exclusive && wcs_get_objects_property( $order, 'prices_include_tax' ) ) {
$order_total += $order_item['line_tax']; $order_total += $order_item['line_tax'];
} }
@@ -1184,13 +1309,18 @@ class WC_Subscriptions_Switcher {
// Set when the first payment and end date for the new subscription should occur // Set when the first payment and end date for the new subscription should occur
WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['first_payment_timestamp'] = $cart_item['subscription_switch']['next_payment_timestamp']; WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['first_payment_timestamp'] = $cart_item['subscription_switch']['next_payment_timestamp'];
WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['end_timestamp'] = $end_timestamp = wcs_date_to_time( WC_Subscriptions_Product::get_expiration_date( $product_id, $subscription->get_date( 'last_payment' ) ) ); WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['end_timestamp'] = $end_timestamp = wcs_date_to_time( WC_Subscriptions_Product::get_expiration_date( $product_id, $subscription->get_date( 'last_order_date_created' ) ) );
// Add any extra sign up fees required to switch to the new subscription // Add any extra sign up fees required to switch to the new subscription
if ( 'yes' == $apportion_sign_up_fee ) { if ( 'yes' == $apportion_sign_up_fee ) {
// With WC 3.0, make sure we get a fresh copy of the product's meta to avoid prorating an already prorated sign-up fee
if ( is_callable( array( $product, 'read_meta_data' ) ) ) {
$product->read_meta_data( true );
}
// Because product add-ons etc. don't apply to sign-up fees, it's safe to use the product's sign-up fee value rather than the cart item's // Because product add-ons etc. don't apply to sign-up fees, it's safe to use the product's sign-up fee value rather than the cart item's
$sign_up_fee_due = $product->subscription_sign_up_fee; $sign_up_fee_due = WC_Subscriptions_Product::get_sign_up_fee( $product );
$sign_up_fee_paid = $subscription->get_items_sign_up_fee( $existing_item, 'inclusive_of_tax' ); $sign_up_fee_paid = $subscription->get_items_sign_up_fee( $existing_item, 'inclusive_of_tax' );
// Make sure total prorated sign-up fee is prorated across total amount of sign-up fee so that customer doesn't get extra discounts // Make sure total prorated sign-up fee is prorated across total amount of sign-up fee so that customer doesn't get extra discounts
@@ -1198,26 +1328,26 @@ class WC_Subscriptions_Switcher {
$sign_up_fee_paid = ( $sign_up_fee_paid * $existing_item['qty'] ) / $cart_item['quantity']; $sign_up_fee_paid = ( $sign_up_fee_paid * $existing_item['qty'] ) / $cart_item['quantity'];
} }
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee = max( $sign_up_fee_due - $sign_up_fee_paid, 0 ); wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_sign_up_fee', max( $sign_up_fee_due - $sign_up_fee_paid, 0 ), 'set_prop_only' );
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_sign_up_fee_prorated', WC_Subscriptions_Product::get_sign_up_fee( WC()->cart->cart_contents[ $cart_item_key ]['data'] ), 'set_prop_only' );
} elseif ( 'no' == $apportion_sign_up_fee ) { // $0 the initial sign-up fee } elseif ( 'no' == $apportion_sign_up_fee ) { // $0 the initial sign-up fee
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee = 0; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_sign_up_fee', 0, 'set_prop_only' );
} }
// Get the current subscription's last payment date // Get the current subscription's last payment date
$last_payment_timestamp = $subscription->get_time( 'last_payment' ); $last_order_time_created = $subscription->get_time( 'last_order_date_created' );
$days_since_last_payment = floor( ( gmdate( 'U' ) - $last_payment_timestamp ) / ( 60 * 60 * 24 ) ); $days_since_last_payment = floor( ( gmdate( 'U' ) - $last_order_time_created ) / ( 60 * 60 * 24 ) );
// Get the current subscription's next payment date // Get the current subscription's next payment date
$next_payment_timestamp = $cart_item['subscription_switch']['next_payment_timestamp']; $next_payment_timestamp = $cart_item['subscription_switch']['next_payment_timestamp'];
$days_until_next_payment = ceil( ( $next_payment_timestamp - gmdate( 'U' ) ) / ( 60 * 60 * 24 ) ); $days_until_next_payment = ceil( ( $next_payment_timestamp - gmdate( 'U' ) ) / ( 60 * 60 * 24 ) );
// If the subscription contains a synced product and the next payment is actually the first payment, determine the days in the "old" cycle from the subscription object // If the subscription contains a synced product and the next payment is actually the first payment, determine the days in the "old" cycle from the subscription object
if ( WC_Subscriptions_Synchroniser::subscription_contains_synced_product( $subscription->id ) && WC_Subscriptions_Synchroniser::calculate_first_payment_date( $product, 'timestamp', $subscription->get_date( 'start' ) ) == $next_payment_timestamp ) { if ( WC_Subscriptions_Synchroniser::subscription_contains_synced_product( $subscription->get_id() ) && WC_Subscriptions_Synchroniser::calculate_first_payment_date( $product, 'timestamp', $subscription->get_date( 'date_created' ) ) == $next_payment_timestamp ) {
$days_in_old_cycle = wcs_get_days_in_cycle( $subscription->billing_period, $subscription->billing_interval ); $days_in_old_cycle = wcs_get_days_in_cycle( $subscription->get_billing_period(), $subscription->get_billing_interval() );
} else { } else {
// Find the number of days between the two // Find the number of days between the two
$days_in_old_cycle = $days_until_next_payment + $days_since_last_payment; $days_in_old_cycle = $days_until_next_payment + $days_since_last_payment;
@@ -1226,7 +1356,7 @@ class WC_Subscriptions_Switcher {
// Find the actual recurring amount charged for the old subscription (we need to use the '_recurring_line_total' meta here rather than '_subscription_recurring_amount' because we want the recurring amount to include extra from extensions, like Product Add-ons etc.) // Find the actual recurring amount charged for the old subscription (we need to use the '_recurring_line_total' meta here rather than '_subscription_recurring_amount' because we want the recurring amount to include extra from extensions, like Product Add-ons etc.)
$old_recurring_total = $existing_item['line_total']; $old_recurring_total = $existing_item['line_total'];
if ( 'yes' == $subscription->prices_include_tax || true === $subscription->prices_include_tax ) { // WC_Abstract_Order::$prices_include_tax can be set to true in __construct() or to 'yes' in populate() if ( $subscription->get_prices_include_tax() ) {
$old_recurring_total += $existing_item['line_tax']; $old_recurring_total += $existing_item['line_tax'];
} }
@@ -1235,18 +1365,18 @@ class WC_Subscriptions_Switcher {
// Find the price per day for the new subscription's recurring total // Find the price per day for the new subscription's recurring total
// If the subscription uses the same billing interval & cycle as the old subscription, // If the subscription uses the same billing interval & cycle as the old subscription,
if ( $item_data->subscription_period == $subscription->billing_period && $item_data->subscription_period_interval == $subscription->billing_interval ) { if ( WC_Subscriptions_Product::get_period( $item_data ) == $subscription->get_billing_period() && WC_Subscriptions_Product::get_interval( $item_data ) == $subscription->get_billing_interval() ) {
$days_in_new_cycle = $days_in_old_cycle; // Use $days_in_old_cycle to make sure they're consistent $days_in_new_cycle = $days_in_old_cycle; // Use $days_in_old_cycle to make sure they're consistent
} else { } else {
// We need to figure out the price per day for the new subscription based on its billing schedule // We need to figure out the price per day for the new subscription based on its billing schedule
$days_in_new_cycle = wcs_get_days_in_cycle( $item_data->subscription_period, $item_data->subscription_period_interval ); $days_in_new_cycle = wcs_get_days_in_cycle( WC_Subscriptions_Product::get_period( $item_data ), WC_Subscriptions_Product::get_interval( $item_data ) );
} }
// We need to use the cart items price to ensure we include extras added by extensions like Product Add-ons // We need to use the cart items price to ensure we include extras added by extensions like Product Add-ons
$new_price_per_day = ( $item_data->price * $cart_item['quantity'] ) / $days_in_new_cycle; $new_price_per_day = ( WC_Subscriptions_Product::get_price( $item_data ) * $cart_item['quantity'] ) / $days_in_new_cycle;
if ( $old_price_per_day < $new_price_per_day ) { if ( $old_price_per_day < $new_price_per_day ) {
@@ -1279,7 +1409,7 @@ class WC_Subscriptions_Switcher {
// If the total amount the customer has paid entitles her to more days at the new price than she has received, there is no gap payment, just shorten the pre-paid term the appropriate number of days // If the total amount the customer has paid entitles her to more days at the new price than she has received, there is no gap payment, just shorten the pre-paid term the appropriate number of days
if ( $days_since_last_payment < $pre_paid_days ) { if ( $days_since_last_payment < $pre_paid_days ) {
WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['first_payment_timestamp'] = $last_payment_timestamp + ( $pre_paid_days * 60 * 60 * 24 ); WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['first_payment_timestamp'] = $last_order_time_created + ( $pre_paid_days * 60 * 60 * 24 );
// If the total amount the customer has paid entitles her to the same or less days at the new price then start the new subscription from today // If the total amount the customer has paid entitles her to the same or less days at the new price then start the new subscription from today
} else { } else {
@@ -1292,7 +1422,7 @@ class WC_Subscriptions_Switcher {
$extra_to_pay = $days_until_next_payment * ( $new_price_per_day - $old_price_per_day ); $extra_to_pay = $days_until_next_payment * ( $new_price_per_day - $old_price_per_day );
// when calculating a subscription with one length (no more next payment date and the end date may have been pushed back) we need to pay for those extra days at the new price per day between the old next payment date and new end date // when calculating a subscription with one length (no more next payment date and the end date may have been pushed back) we need to pay for those extra days at the new price per day between the old next payment date and new end date
if ( 1 == $item_data->subscription_length ) { if ( 1 == WC_Subscriptions_Product::get_length( $item_data ) ) {
$days_to_new_end = floor( ( $end_timestamp - $next_payment_timestamp ) / ( 60 * 60 * 24 ) ); $days_to_new_end = floor( ( $end_timestamp - $next_payment_timestamp ) / ( 60 * 60 * 24 ) );
if ( $days_to_new_end > 0 ) { if ( $days_to_new_end > 0 ) {
@@ -1304,10 +1434,10 @@ class WC_Subscriptions_Switcher {
$extra_to_pay = $extra_to_pay / $cart_item['quantity']; $extra_to_pay = $extra_to_pay / $cart_item['quantity'];
// Keep a record of the two separate amounts so we store these and calculate future switch amounts correctly // Keep a record of the two separate amounts so we store these and calculate future switch amounts correctly
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee; $existing_sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee( WC()->cart->cart_contents[ $cart_item_key ]['data'] );
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_price_prorated = round( $extra_to_pay, wc_get_price_decimals() ); wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_sign_up_fee_prorated', $existing_sign_up_fee, 'set_prop_only' );
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_sign_up_fee += round( $extra_to_pay, wc_get_price_decimals() ); wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_price_prorated', round( $extra_to_pay, wc_get_price_decimals() ), 'set_prop_only' );
wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_sign_up_fee', round( $existing_sign_up_fee + $extra_to_pay, wc_get_price_decimals() ), 'set_prop_only' );
} }
// If the customer is downgrading, set the next payment date and maybe extend it if downgrades are prorated // If the customer is downgrading, set the next payment date and maybe extend it if downgrades are prorated
@@ -1349,7 +1479,7 @@ class WC_Subscriptions_Switcher {
$length_remaining = $base_length; $length_remaining = $base_length;
} }
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_length = $length_remaining; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_length', $length_remaining, 'set_prop_only' );
} }
} }
} }
@@ -1364,7 +1494,7 @@ class WC_Subscriptions_Switcher {
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
if ( isset( $cart_item['subscription_switch']['first_payment_timestamp'] ) && 0 != $cart_item['subscription_switch']['first_payment_timestamp'] ) { if ( isset( $cart_item['subscription_switch']['first_payment_timestamp'] ) && 0 != $cart_item['subscription_switch']['first_payment_timestamp'] ) {
$first_renewal_date = ( '1' != $cart_item['data']->subscription_length ) ? gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ) : 0; $first_renewal_date = ( '1' != WC_Subscriptions_Product::get_length( $cart_item['data'] ) ) ? gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ) : 0;
} }
} }
@@ -1386,19 +1516,19 @@ class WC_Subscriptions_Switcher {
$end_timestamp = WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['end_timestamp']; $end_timestamp = WC()->cart->cart_contents[ $cart_item_key ]['subscription_switch']['end_timestamp'];
// if the subscription is length 1 and prorated, we want to use the prorated the next payment date as the end date // if the subscription is length 1 and prorated, we want to use the prorated the next payment date as the end date
if ( 1 == $cart_item['data']->subscription_length && 0 !== $next_payment_time && isset( $cart_item['subscription_switch']['recurring_payment_prorated'] ) ) { if ( 1 == WC_Subscriptions_Product::get_length( $cart_item['data'] ) && 0 !== $next_payment_time && isset( $cart_item['subscription_switch']['recurring_payment_prorated'] ) ) {
$end_date = gmdate( 'Y-m-d H:i:s', $next_payment_time ); $end_date = gmdate( 'Y-m-d H:i:s', $next_payment_time );
// if the subscription is more than 1 (and not 0) and we have a next payment date (prorated or not) we want to calculate the new end date from that // if the subscription is more than 1 (and not 0) and we have a next payment date (prorated or not) we want to calculate the new end date from that
} elseif ( 0 !== $next_payment_time && $cart_item['data']->subscription_length > 1 ) { } elseif ( 0 !== $next_payment_time && WC_Subscriptions_Product::get_length( $cart_item['data'] ) > 1 ) {
// remove trial period on the switched subscription when calculating the new end date // remove trial period on the switched subscription when calculating the new end date
$trial_length = $cart_item['data']->subscription_trial_length; $trial_length = wcs_get_objects_property( $cart_item['data'], 'subscription_trial_length' );
$cart_item['data']->subscription_trial_length = 0; wcs_set_objects_property( $cart_item['data'], 'subscription_trial_length', 0, 'set_prop_only' );
$end_date = WC_Subscriptions_Product::get_expiration_date( $cart_item['data'], gmdate( 'Y-m-d H:i:s', $next_payment_time ) ); $end_date = WC_Subscriptions_Product::get_expiration_date( $cart_item['data'], gmdate( 'Y-m-d H:i:s', $next_payment_time ) );
// add back the trial length if it has been spoofed // add back the trial length if it has been spoofed
$cart_item['data']->subscription_trial_length = $trial_length; wcs_set_objects_property( $cart_item['data'], 'subscription_trial_length', $trial_length, 'set_prop_only' );
// elseif fallback to using the end date set on the cart item // elseif fallback to using the end date set on the cart item
} elseif ( ! empty( $end_timestamp ) ) { } elseif ( ! empty( $end_timestamp ) ) {
@@ -1509,10 +1639,10 @@ class WC_Subscriptions_Switcher {
public static function process_subscription_switches( $order_id, $order_old_status, $order_new_status ) { public static function process_subscription_switches( $order_id, $order_old_status, $order_new_status ) {
global $wpdb; global $wpdb;
$switch_processed = get_post_meta( $order_id, '_completed_subscription_switch', true );
$order = wc_get_order( $order_id ); $order = wc_get_order( $order_id );
$switch_processed = wcs_get_objects_property( $order, 'completed_subscription_switch' );
if ( ! wcs_order_contains_switch( $order_id ) || 'true' == $switch_processed ) { if ( ! wcs_order_contains_switch( $order ) || 'true' == $switch_processed ) {
return; return;
} }
@@ -1525,7 +1655,7 @@ class WC_Subscriptions_Switcher {
self::complete_subscription_switches( $order ); self::complete_subscription_switches( $order );
update_post_meta( $order_id, '_completed_subscription_switch', 'true' ); wcs_set_objects_property( $order, 'completed_subscription_switch', 'true' );
$wpdb->query( 'COMMIT' ); $wpdb->query( 'COMMIT' );
@@ -1551,7 +1681,7 @@ class WC_Subscriptions_Switcher {
$switch_setting = get_option( WC_Subscriptions_Admin::$option_prefix . '_allow_switching', 'no' ); $switch_setting = get_option( WC_Subscriptions_Admin::$option_prefix . '_allow_switching', 'no' );
// does the current switch setting allow switching for variable or variable_grouped // does the current switch setting allow switching for variable or variable_grouped
if ( 'variable_grouped' == $switch_setting || ( $product->is_type( array( 'variable-subscription', 'subscription_variation' ) ) && 'variable' == $switch_setting ) || ( 'grouped' == $switch_setting && ( $product->is_type( 'grouped' ) || 0 !== $product->post->post_parent ) ) ) { if ( 'variable_grouped' == $switch_setting || ( $product->is_type( array( 'variable-subscription', 'subscription_variation' ) ) && 'variable' == $switch_setting ) || ( 'grouped' == $switch_setting && ( $product->is_type( 'grouped' ) || wcs_get_objects_property( $product, 'parent_id' ) ) ) ) {
$allow_switching = true; $allow_switching = true;
} }
@@ -1618,7 +1748,7 @@ class WC_Subscriptions_Switcher {
if ( in_array( $order_type, array( 'all', 'switch' ) ) ) { if ( in_array( $order_type, array( 'all', 'switch' ) ) ) {
$switch_orders = wcs_get_switch_orders_for_subscription( $subscription->id ); $switch_orders = wcs_get_switch_orders_for_subscription( $subscription->get_id() );
if ( 'all' == $return_fields ) { if ( 'all' == $return_fields ) {
$related_orders += $switch_orders; $related_orders += $switch_orders;
@@ -1710,7 +1840,7 @@ class WC_Subscriptions_Switcher {
} }
$product_id = wcs_get_canonical_product_id( $old_item ); $product_id = wcs_get_canonical_product_id( $old_item );
WCS_Download_Handler::revoke_downloadable_file_permission( $product_id, $subscription->id, $subscription->customer_user ); WCS_Download_Handler::revoke_downloadable_file_permission( $product_id, $subscription->get_id(), $subscription->get_user_id() );
} }
@@ -1726,7 +1856,7 @@ class WC_Subscriptions_Switcher {
public static function complete_subscription_switches( $order ) { public static function complete_subscription_switches( $order ) {
// Get the switch meta // Get the switch meta
$switch_order_data = get_post_meta( $order->id, '_subscription_switch_data', true ); $switch_order_data = wcs_get_objects_property( $order, 'subscription_switch_data' );
// if we don't have an switch data, there is nothing to do here. Switch orders created prior to v2.1 won't have any data to process. // if we don't have an switch data, there is nothing to do here. Switch orders created prior to v2.1 won't have any data to process.
if ( empty( $switch_order_data ) || ! is_array( $switch_order_data ) ) { if ( empty( $switch_order_data ) || ! is_array( $switch_order_data ) ) {
@@ -1752,7 +1882,6 @@ class WC_Subscriptions_Switcher {
// If we are adding a line item to an existing subscription // If we are adding a line item to an existing subscription
if ( isset( $switched_item_data['add_line_item'] ) ) { if ( isset( $switched_item_data['add_line_item'] ) ) {
wc_update_order_item( $switched_item_data['add_line_item'], array( 'order_item_type' => 'line_item' ) ); wc_update_order_item( $switched_item_data['add_line_item'], array( 'order_item_type' => 'line_item' ) );
do_action( 'woocommerce_subscription_item_switched', $order, $subscription, $switched_item_data['add_line_item'], $switched_item_data['remove_line_item'] ); do_action( 'woocommerce_subscription_item_switched', $order, $subscription, $switched_item_data['add_line_item'], $switched_item_data['remove_line_item'] );
} }
@@ -1774,17 +1903,25 @@ class WC_Subscriptions_Switcher {
wc_update_order_item( $switched_item_data['remove_line_item'], array( 'order_item_type' => 'line_item_switched' ) ); wc_update_order_item( $switched_item_data['remove_line_item'], array( 'order_item_type' => 'line_item_switched' ) );
// translators: 1$: old item, 2$: new item when switching // translators: 1$: old item, 2$: new item when switching
$subscription->add_order_note( sprintf( _x( 'Customer switched from: %1$s to %2$s.', 'used in order notes', 'woocommerce-subscriptions' ), $old_item_name, $new_item_name ) ); $add_note = sprintf( _x( 'Customer switched from: %1$s to %2$s.', 'used in order notes', 'woocommerce-subscriptions' ), $old_item_name, $new_item_name );
} }
} }
} }
} }
if ( ! empty( $add_note ) ) {
$subscription->add_order_note( $add_note );
}
if ( ! empty( $switch_data['billing_schedule'] ) ) { if ( ! empty( $switch_data['billing_schedule'] ) ) {
// Update the billing schedule // Update the billing schedule
foreach ( $switch_data['billing_schedule'] as $meta_key => $value ) { if ( ! empty( $switch_data['billing_schedule']['_billing_period'] ) ) {
update_post_meta( $subscription_id, $meta_key, $value ); $subscription->set_billing_period( $switch_data['billing_schedule']['_billing_period'] );
}
if ( ! empty( $switch_data['billing_schedule']['_billing_interval'] ) ) {
$subscription->set_billing_interval( $switch_data['billing_schedule']['_billing_interval'] );
} }
} }
@@ -1798,7 +1935,7 @@ class WC_Subscriptions_Switcher {
} }
if ( ! empty( $switch_data['dates']['update'] ) ) { if ( ! empty( $switch_data['dates']['update'] ) ) {
$subscription->update_dates( $switch_order_data[ $subscription->id ]['dates']['update'] ); $subscription->update_dates( $switch_order_data[ $subscription->get_id() ]['dates']['update'] );
} }
} }
@@ -1821,7 +1958,11 @@ class WC_Subscriptions_Switcher {
// Update the subscription address // Update the subscription address
self::maybe_update_subscription_address( $order, $subscription ); self::maybe_update_subscription_address( $order, $subscription );
$subscription->calculate_totals(); // Save every change
$subscription->save();
// We just changed above the type of some items related to this subscription, so we need to reload it to get the newest items
wcs_get_subscription( $subscription->get_id() )->calculate_totals();
} }
} }
@@ -1917,21 +2058,23 @@ class WC_Subscriptions_Switcher {
return; return;
} }
foreach ( wcs_get_subscriptions_for_switch_order( $order->id ) as $subscription ) { foreach ( wcs_get_subscriptions_for_switch_order( $order ) as $subscription ) {
if ( false === $subscription->is_manual() ) { if ( false === $subscription->is_manual() ) {
continue; continue;
} }
if ( $subscription->payment_method !== $order->payment_method ) { if ( $subscription->get_payment_method() !== wcs_get_objects_property( $order, 'payment_method' ) ) {
// Set the new payment method on the subscription // Set the new payment method on the subscription
$available_gateways = WC()->payment_gateways->get_available_payment_gateways(); $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$payment_method = isset( $available_gateways[ $order->payment_method ] ) ? $available_gateways[ $order->payment_method ] : false; $order_payment_method = wcs_get_objects_property( $order, 'payment_method' );
$payment_method = '' != $order_payment_method && isset( $available_gateways[ $order_payment_method ] ) ? $available_gateways[ $order_payment_method ] : false;
if ( $payment_method && $payment_method->supports( 'subscriptions' ) ) { if ( $payment_method && $payment_method->supports( 'subscriptions' ) ) {
$subscription->set_payment_method( $payment_method ); $subscription->set_payment_method( $payment_method );
$subscription->update_manual( false ); $subscription->set_requires_manual_renewal( false );
$subscription->save();
} }
} }
} }
@@ -2001,7 +2144,7 @@ class WC_Subscriptions_Switcher {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( isset( $cart_item['subscription_switch']['first_payment_timestamp'] ) && 0 != $cart_item['subscription_switch']['first_payment_timestamp'] ) { if ( isset( $cart_item['subscription_switch']['first_payment_timestamp'] ) && 0 != $cart_item['subscription_switch']['first_payment_timestamp'] ) {
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length = 1; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_trial_length', 1, 'set_prop_only' );
} }
} }
@@ -2017,7 +2160,7 @@ class WC_Subscriptions_Switcher {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( isset( $cart_item['subscription_switch']['first_payment_timestamp'] ) && 0 != $cart_item['subscription_switch']['first_payment_timestamp'] ) { if ( isset( $cart_item['subscription_switch']['first_payment_timestamp'] ) && 0 != $cart_item['subscription_switch']['first_payment_timestamp'] ) {
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length = 0; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_trial_length', 0, 'set_prop_only' );
} }
} }
return $total; return $total;
@@ -2094,7 +2237,7 @@ class WC_Subscriptions_Switcher {
// Add the new shipping line item // Add the new shipping line item
foreach ( $shipping_methods as $shipping_line_item ) { foreach ( $shipping_methods as $shipping_line_item ) {
$item_id = wc_add_order_item( $subscription->id, array( $item_id = wc_add_order_item( $subscription->get_id(), array(
'order_item_name' => $shipping_line_item['name'], 'order_item_name' => $shipping_line_item['name'],
'order_item_type' => 'shipping', 'order_item_type' => 'shipping',
) ); ) );
@@ -2258,7 +2401,7 @@ class WC_Subscriptions_Switcher {
*/ */
public static function calculate_first_payment_date( $next_payment_date, $order, $product_id, $type ) { public static function calculate_first_payment_date( $next_payment_date, $order, $product_id, $type ) {
_deprecated_function( __METHOD__, '2.0' ); _deprecated_function( __METHOD__, '2.0' );
return self::get_first_payment_date( $next_payment_date, WC_Subscriptions_Manager::get_subscription_key( $order->id, $product_id ), $order->user_id, $type ); return self::get_first_payment_date( $next_payment_date, WC_Subscriptions_Manager::get_subscription_key( wcs_get_objects_property( $order, 'id' ), $product_id ), $order->get_user_id(), $type );
} }
/** /**
@@ -2275,9 +2418,9 @@ class WC_Subscriptions_Switcher {
$subscription = wcs_get_subscription_from_key( $subscription_key ); $subscription = wcs_get_subscription_from_key( $subscription_key );
if ( $subscription->has_status( 'active' ) && ! empty( $subscription->order ) && wcs_order_contains_switch( $subscription->order->id ) && 1 >= $subscription->get_completed_payment_count() ) { if ( $subscription->has_status( 'active' ) && $subscription->get_parent_id() && wcs_order_contains_switch( $subscription->get_parent_id() ) && 1 >= $subscription->get_completed_payment_count() ) {
$first_payment_timestamp = get_post_meta( $subscription->order->id, '_switched_subscription_first_payment_timestamp', true ); $first_payment_timestamp = get_post_meta( $subscription->get_parent_id(), '_switched_subscription_first_payment_timestamp', true );
if ( 0 != $first_payment_timestamp ) { if ( 0 != $first_payment_timestamp ) {
$next_payment_date = ( 'mysql' == $type ) ? gmdate( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp; $next_payment_date = ( 'mysql' == $type ) ? gmdate( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp;
@@ -2348,5 +2491,34 @@ class WC_Subscriptions_Switcher {
_deprecated_function( __METHOD__, '2.0', 'wcs_order_contains_switch( $order_id )' ); _deprecated_function( __METHOD__, '2.0', 'wcs_order_contains_switch( $order_id )' );
return wcs_order_contains_switch( $order_id ); return wcs_order_contains_switch( $order_id );
} }
/**
* Store the order line item id so it can be retrieved when we're processing the switch on checkout
*
* @param int $order_id
* @param array $checkout_posted_data
* @since 2.2.0
*/
public static function set_switch_order_item_id( $order_id, $posted_checkout_data ) {
_deprecated_function( __METHOD__, '2.2.1', 'WCS_Cart_Switch::set_cart_item_order_item_id()' );
$order = wc_get_order( $order_id );
foreach ( $order->get_items( 'line_item' ) as $order_item_id => $order_item ) {
$cart_item_key = $order_item->get_meta( '_switched_cart_item_key' );
if ( ! empty( $cart_item_key ) ) {
foreach ( WC()->cart->recurring_carts as $recurring_cart_key => $recurring_cart ) {
// If this cart item belongs to this recurring cart
if ( in_array( $cart_item_key, array_keys( $recurring_cart->cart_contents ) ) && isset( WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ]['subscription_switch'] ) ) {
WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ]['subscription_switch']['order_line_item_id'] = $order_item_id;
wc_add_order_item_meta( WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ]['subscription_switch']['item_id'], '_switched_subscription_new_item_id', $order_item_id, true );
}
}
}
}
}
} }
WC_Subscriptions_Switcher::init(); WC_Subscriptions_Switcher::init();

View File

@@ -96,7 +96,12 @@ class WC_Subscriptions_Synchroniser {
// When adding an item to a subscription, check if it is for a synced product to make sure the sync meta is set on the subscription. We can't attach to just the 'woocommerce_new_order_item' here because the '_product_id' and '_variation_id' meta are not set before it fires // When adding an item to a subscription, check if it is for a synced product to make sure the sync meta is set on the subscription. We can't attach to just the 'woocommerce_new_order_item' here because the '_product_id' and '_variation_id' meta are not set before it fires
add_action( 'woocommerce_ajax_add_order_item_meta', __CLASS__ . '::ajax_maybe_add_meta_for_item', 10, 2 ); add_action( 'woocommerce_ajax_add_order_item_meta', __CLASS__ . '::ajax_maybe_add_meta_for_item', 10, 2 );
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
add_action( 'woocommerce_order_add_product', __CLASS__ . '::maybe_add_meta_for_new_product', 10, 3 ); add_action( 'woocommerce_order_add_product', __CLASS__ . '::maybe_add_meta_for_new_product', 10, 3 );
} else {
add_action( 'woocommerce_new_order_item', __CLASS__ . '::maybe_add_meta_for_new_line_item', 10, 3 );
}
// Make sure the sign-up fee for a synchronised subscription is correct // Make sure the sign-up fee for a synchronised subscription is correct
add_filter( 'woocommerce_subscriptions_sign_up_fee', __CLASS__ . '::get_synced_sign_up_fee', 1, 3 ); add_filter( 'woocommerce_subscriptions_sign_up_fee', __CLASS__ . '::get_synced_sign_up_fee', 1, 3 );
@@ -259,14 +264,16 @@ class WC_Subscriptions_Synchroniser {
if ( self::is_syncing_enabled() ) { if ( self::is_syncing_enabled() ) {
// Set month as the default billing period // Set month as the default billing period
if ( ! $subscription_period = get_post_meta( $variation->ID, '_subscription_period', true ) ) { $subscription_period = WC_Subscriptions_Product::get_period( $variation );
if ( empty( $subscription_period ) ) {
$subscription_period = 'month'; $subscription_period = 'month';
} }
$display_week_month_select = ( ! in_array( $subscription_period, array( 'month', 'week' ) ) ) ? 'display: none;' : ''; $display_week_month_select = ( ! in_array( $subscription_period, array( 'month', 'week' ) ) ) ? 'display: none;' : '';
$display_annual_select = ( 'year' != $subscription_period ) ? 'display: none;' : ''; $display_annual_select = ( 'year' != $subscription_period ) ? 'display: none;' : '';
$payment_day = self::get_products_payment_day( $variation->ID ); $payment_day = self::get_products_payment_day( $variation );
// An annual sync date is already set in the form: array( 'day' => 'nn', 'month' => 'nn' ), create a MySQL string from those values (year and time are irrelvent as they are ignored) // An annual sync date is already set in the form: array( 'day' => 'nn', 'month' => 'nn' ), create a MySQL string from those values (year and time are irrelvent as they are ignored)
if ( is_array( $payment_day ) ) { if ( is_array( $payment_day ) ) {
@@ -400,7 +407,7 @@ class WC_Subscriptions_Synchroniser {
$product = wc_get_product( $product ); $product = wc_get_product( $product );
} }
if ( ! is_object( $product ) || ! self::is_syncing_enabled() || 'day' == $product->subscription_period || ! WC_Subscriptions_Product::is_subscription( $product ) ) { if ( ! is_object( $product ) || ! self::is_syncing_enabled() || 'day' == WC_Subscriptions_Product::get_period( $product ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) {
return false; return false;
} }
@@ -441,15 +448,11 @@ class WC_Subscriptions_Synchroniser {
if ( ! self::is_syncing_enabled() ) { if ( ! self::is_syncing_enabled() ) {
$payment_date = 0; $payment_date = 0;
} elseif ( ! is_object( $product ) ) {
$payment_date = get_post_meta( $product, self::$post_meta_key, true );
} elseif ( isset( $product->subscription_payment_sync_date ) ) {
$payment_date = $product->subscription_payment_sync_date;
} else { } else {
$payment_date = 0; $payment_date = WC_Subscriptions_Product::get_meta_data( $product, 'subscription_payment_sync_date', 0 );
} }
return $payment_date; return apply_filters( 'woocommerce_subscriptions_product_sync_date', $payment_date, $product );
} }
/** /**
@@ -654,7 +657,7 @@ class WC_Subscriptions_Synchroniser {
$first_payment_date = ''; $first_payment_date = '';
if ( self::is_product_synced( $product ) ) { if ( self::is_product_synced( $product ) ) {
$first_payment_timestamp = self::calculate_first_payment_date( $product->id, 'timestamp' ); $first_payment_timestamp = self::calculate_first_payment_date( $product->get_id(), 'timestamp' );
if ( 0 != $first_payment_timestamp ) { if ( 0 != $first_payment_timestamp ) {
@@ -713,7 +716,9 @@ class WC_Subscriptions_Synchroniser {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( self::is_product_synced( $cart_item['data'] ) && ! self::is_product_prorated( $cart_item['data'] ) && ! self::is_today( self::calculate_first_payment_date( $cart_item['data'], 'timestamp' ) ) ) { if ( self::is_product_synced( $cart_item['data'] ) && ! self::is_product_prorated( $cart_item['data'] ) && ! self::is_today( self::calculate_first_payment_date( $cart_item['data'], 'timestamp' ) ) ) {
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length = ( WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length > 1 ) ? WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length : 1; $current_trial_length = WC_Subscriptions_Product::get_trial_length( WC()->cart->cart_contents[ $cart_item_key ]['data'] );
$new_trial_length = ( $current_trial_length > 1 ) ? $current_trial_length : 1;
wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_trial_length', $new_trial_length, 'set_prop_only' );
} }
} }
@@ -729,7 +734,7 @@ class WC_Subscriptions_Synchroniser {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( self::is_product_synced( $cart_item['data'] ) ) { if ( self::is_product_synced( $cart_item['data'] ) ) {
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length = WC_Subscriptions_Product::get_trial_length( wcs_get_canonical_product_id( $cart_item ) ); wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_trial_length', WC_Subscriptions_Product::get_trial_length( wcs_get_canonical_product_id( $cart_item ) ), 'set_prop_only' );
} }
} }
return $total; return $total;
@@ -748,7 +753,7 @@ class WC_Subscriptions_Synchroniser {
if ( self::is_syncing_enabled() && ! empty( $cart ) && ! wcs_cart_contains_renewal() ) { if ( self::is_syncing_enabled() && ! empty( $cart ) && ! wcs_cart_contains_renewal() ) {
foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) { foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) {
if ( ( ! is_array( $cart_item['data']->subscription_payment_sync_date ) && $cart_item['data']->subscription_payment_sync_date > 0 ) || ( is_array( $cart_item['data']->subscription_payment_sync_date ) && $cart_item['data']->subscription_payment_sync_date['day'] > 0 ) ) { if ( self::is_product_synced( $cart_item['data'] ) ) {
$contains_synced = $cart_item; $contains_synced = $cart_item;
break; break;
} }
@@ -781,7 +786,7 @@ class WC_Subscriptions_Synchroniser {
if ( $trial_expiration_date > 0 && self::is_product_synced( $product_id ) ) { if ( $trial_expiration_date > 0 && self::is_product_synced( $product_id ) ) {
$trial_expiration_timestamp = strtotime( $trial_expiration_date ); $trial_expiration_timestamp = wcs_date_to_time( $trial_expiration_date );
remove_filter( 'woocommerce_subscriptions_product_trial_expiration_date', __METHOD__ ); // avoid infinite loop remove_filter( 'woocommerce_subscriptions_product_trial_expiration_date', __METHOD__ ); // avoid infinite loop
$first_payment_timestamp = self::calculate_first_payment_date( $product_id, 'timestamp' ); $first_payment_timestamp = self::calculate_first_payment_date( $product_id, 'timestamp' );
add_filter( 'woocommerce_subscriptions_product_trial_expiration_date', __METHOD__, 10, 2 ); // avoid infinite loop add_filter( 'woocommerce_subscriptions_product_trial_expiration_date', __METHOD__, 10, 2 ); // avoid infinite loop
@@ -842,7 +847,7 @@ class WC_Subscriptions_Synchroniser {
*/ */
public static function get_synced_sign_up_fee( $sign_up_fee, $subscription, $product_id ) { public static function get_synced_sign_up_fee( $sign_up_fee, $subscription, $product_id ) {
if ( wcs_is_subscription( $subscription ) && self::subscription_contains_synced_product( $subscription ) && count( wcs_get_line_items_with_a_trial( $subscription->id ) ) < 0 ) { if ( wcs_is_subscription( $subscription ) && self::subscription_contains_synced_product( $subscription ) && count( wcs_get_line_items_with_a_trial( $subscription->get_id() ) ) < 0 ) {
$sign_up_fee = max( $subscription->get_total_initial_payment() - $subscription->get_total(), 0 ); $sign_up_fee = max( $subscription->get_total_initial_payment() - $subscription->get_total(), 0 );
} }
@@ -864,15 +869,15 @@ class WC_Subscriptions_Synchroniser {
return $price; return $price;
} }
switch ( $product->subscription_period ) { switch ( WC_Subscriptions_Product::get_period( $product ) ) {
case 'week' : case 'week' :
$days_in_cycle = 7 * $product->subscription_period_interval; $days_in_cycle = 7 * WC_Subscriptions_Product::get_interval( $product );
break; break;
case 'month' : case 'month' :
$days_in_cycle = gmdate( 't' ) * $product->subscription_period_interval; $days_in_cycle = gmdate( 't' ) * WC_Subscriptions_Product::get_interval( $product );
break; break;
case 'year' : case 'year' :
$days_in_cycle = ( 365 + gmdate( 'L' ) ) * $product->subscription_period_interval; $days_in_cycle = ( 365 + gmdate( 'L' ) ) * WC_Subscriptions_Product::get_interval( $product );
break; break;
} }
@@ -963,7 +968,7 @@ class WC_Subscriptions_Synchroniser {
$product_id = wcs_get_canonical_product_id( $item ); $product_id = wcs_get_canonical_product_id( $item );
if ( self::is_product_synced( $product_id ) ) { if ( self::is_product_synced( $product_id ) ) {
update_post_meta( $subscription->id, '_contains_synced_subscription', 'true' ); update_post_meta( $subscription->get_id(), '_contains_synced_subscription', 'true' );
break; break;
} }
} }
@@ -1012,7 +1017,7 @@ class WC_Subscriptions_Synchroniser {
public static function subscription_contains_synced_product( $subscription_id ) { public static function subscription_contains_synced_product( $subscription_id ) {
if ( is_object( $subscription_id ) ) { if ( is_object( $subscription_id ) ) {
$subscription_id = $subscription_id->id; $subscription_id = $subscription_id->get_id();
} }
return ( 'true' == get_post_meta( $subscription_id, '_contains_synced_subscription', true ) ) ? true : false; return ( 'true' == get_post_meta( $subscription_id, '_contains_synced_subscription', true ) ) ? true : false;
@@ -1033,6 +1038,23 @@ class WC_Subscriptions_Synchroniser {
return $cart_key; return $cart_key;
} }
/**
* When adding a product line item to an order/subscription via the WC_Abstract_Order::add_product() method, check if we should be setting
* the sync meta on the subscription.
*
* Attached to WC 3.0+ hooks and uses WC 3.0 methods.
*
* @param int The new line item id
* @param WC_Order_Item
* @param int The post ID of a WC_Subscription
* @since 2.2.3
*/
public static function maybe_add_meta_for_new_line_item( $item_id, $item, $subscription_id ) {
if ( is_callable( array( $item, 'get_product_id' ) ) && self::is_product_synced( $item->get_product_id() ) ) {
self::maybe_add_subscription_meta( $subscription_id );
}
}
/* Deprecated Functions */ /* Deprecated Functions */
/** /**
@@ -1059,7 +1081,7 @@ class WC_Subscriptions_Synchroniser {
$cart_item = self::cart_contains_synced_subscription(); $cart_item = self::cart_contains_synced_subscription();
if ( false !== $cart_item && isset( $cart_item['data']->subscription_period ) && ( 'year' != $cart_item['data']->subscription_period || $cart_item['data']->subscription_trial_length > 0 ) ) { if ( false !== $cart_item && '' !== WC_Subscriptions_Product::get_period( $cart_item['data'] ) && ( 'year' != WC_Subscriptions_Product::get_period( $cart_item['data'] ) || WC_Subscriptions_Product::get_trial_length( $cart_item['data'] ) > 0 ) ) {
$first_payment_date = self::get_products_first_payment_date( $cart_item['data'] ); $first_payment_date = self::get_products_first_payment_date( $cart_item['data'] );
@@ -1154,17 +1176,16 @@ class WC_Subscriptions_Synchroniser {
* @deprecated 2.0 * @deprecated 2.0
*/ */
public static function get_first_payment_date( $first_payment_date, $order, $product_id, $type ) { public static function get_first_payment_date( $first_payment_date, $order, $product_id, $type ) {
_deprecated_function( __METHOD__, '2.0' ); _deprecated_function( __METHOD__, '2.0' );
$subscription = wcs_get_subscription_from_key( $order . '_' . $product_id ); $subscription = wcs_get_subscription_from_key( $order . '_' . $product_id );
if ( self::order_contains_synced_subscription( $order->id ) && 1 >= $subscription->get_completed_payment_count() ) { if ( self::order_contains_synced_subscription( wcs_get_objects_property( $order, 'id' ) ) && 1 >= $subscription->get_completed_payment_count() ) {
// Don't prematurely set the first payment date when manually adding a subscription from the admin // Don't prematurely set the first payment date when manually adding a subscription from the admin
if ( ! is_admin() || 'active' == $subscription->get_status() ) { if ( ! is_admin() || 'active' == $subscription->get_status() ) {
$first_payment_timestamp = self::calculate_first_payment_date( $product_id, 'timestamp', $order->order_date ); $first_payment_timestamp = self::calculate_first_payment_date( $product_id, 'timestamp', wcs_get_datetime_utc_string( wcs_get_objects_property( $order, 'date_created' ) ) );
if ( 0 != $first_payment_timestamp ) { if ( 0 != $first_payment_timestamp ) {
$first_payment_date = ( 'mysql' == $type ) ? gmdate( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp; $first_payment_date = ( 'mysql' == $type ) ? gmdate( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp;
@@ -1210,7 +1231,7 @@ class WC_Subscriptions_Synchroniser {
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::subscription_contains_synced_product()' ); _deprecated_function( __METHOD__, '2.0', __CLASS__ . '::subscription_contains_synced_product()' );
if ( is_object( $order_id ) ) { if ( is_object( $order_id ) ) {
$order_id = $order_id->id; $order_id = wcs_get_objects_property( $order_id, 'id' );
} }
return ( 'true' == get_post_meta( $order_id, '_order_contains_synced_subscription', true ) ) ? true : false; return ( 'true' == get_post_meta( $order_id, '_order_contains_synced_subscription', true ) ) ? true : false;
@@ -1262,7 +1283,7 @@ class WC_Subscriptions_Synchroniser {
public static function get_sign_up_fee( $sign_up_fee, $order, $product_id, $non_subscription_total ) { public static function get_sign_up_fee( $sign_up_fee, $order, $product_id, $non_subscription_total ) {
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::get_synced_sign_up_fee' ); _deprecated_function( __METHOD__, '2.0', __CLASS__ . '::get_synced_sign_up_fee' );
if ( 'shop_order' == get_post_type( $order ) && self::order_contains_synced_subscription( $order->id ) && WC_Subscriptions_Order::get_subscription_trial_length( $order ) < 1 ) { if ( 'shop_order' == get_post_type( $order ) && self::order_contains_synced_subscription( wcs_get_objects_property( $order, 'id' ) ) && WC_Subscriptions_Order::get_subscription_trial_length( $order ) < 1 ) {
$sign_up_fee = max( WC_Subscriptions_Order::get_total_initial_payment( $order ) - $non_subscription_total, 0 ); $sign_up_fee = max( WC_Subscriptions_Order::get_total_initial_payment( $order ) - $non_subscription_total, 0 );
} }

View File

@@ -22,7 +22,7 @@ class WCS_Action_Scheduler extends WCS_Scheduler {
* Maybe set a schedule action if the new date is in the future * Maybe set a schedule action if the new date is in the future
* *
* @param object $subscription An instance of a WC_Subscription object * @param object $subscription An instance of a WC_Subscription object
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'payment_retry', 'last_payment', 'end', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'payment_retry', 'end', 'end_of_prepaid_term' or a custom date type
* @param string $datetime A MySQL formated date/time string in the GMT/UTC timezone. * @param string $datetime A MySQL formated date/time string in the GMT/UTC timezone.
*/ */
public function update_date( $subscription, $date_type, $datetime ) { public function update_date( $subscription, $date_type, $datetime ) {
@@ -55,7 +55,7 @@ class WCS_Action_Scheduler extends WCS_Scheduler {
* Delete a date from the action scheduler queue * Delete a date from the action scheduler queue
* *
* @param object $subscription An instance of a WC_Subscription object * @param object $subscription An instance of a WC_Subscription object
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'end', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'end', 'end_of_prepaid_term' or a custom date type
*/ */
public function delete_date( $subscription, $date_type ) { public function delete_date( $subscription, $date_type ) {
$this->update_date( $subscription, $date_type, 0 ); $this->update_date( $subscription, $date_type, 0 );
@@ -65,7 +65,7 @@ class WCS_Action_Scheduler extends WCS_Scheduler {
* When a subscription's status is updated, maybe schedule an event * When a subscription's status is updated, maybe schedule an event
* *
* @param object $subscription An instance of a WC_Subscription object * @param object $subscription An instance of a WC_Subscription object
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'end', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'end', 'end_of_prepaid_term' or a custom date type
* @param string $datetime A MySQL formated date/time string in the GMT/UTC timezone. * @param string $datetime A MySQL formated date/time string in the GMT/UTC timezone.
*/ */
public function update_status( $subscription, $new_status, $old_status ) { public function update_status( $subscription, $new_status, $old_status ) {
@@ -126,7 +126,7 @@ class WCS_Action_Scheduler extends WCS_Scheduler {
* Get the hook to use in the action scheduler for the date type * Get the hook to use in the action scheduler for the date type
* *
* @param object $subscription An instance of WC_Subscription to get the hook for * @param object $subscription An instance of WC_Subscription to get the hook for
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'expiration', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'expiration', 'end_of_prepaid_term' or a custom date type
*/ */
protected function get_scheduled_action_hook( $subscription, $date_type ) { protected function get_scheduled_action_hook( $subscription, $date_type ) {
@@ -158,7 +158,7 @@ class WCS_Action_Scheduler extends WCS_Scheduler {
/** /**
* Get the args to set on the scheduled action. * Get the args to set on the scheduled action.
* *
* @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'expiration', 'end_of_prepaid_term' or a custom date type * @param string $date_type Can be 'trial_end', 'next_payment', 'expiration', 'end_of_prepaid_term' or a custom date type
* @param object $subscription An instance of WC_Subscription to get the hook for * @param object $subscription An instance of WC_Subscription to get the hook for
* @return array Array of name => value pairs stored against the scheduled action. * @return array Array of name => value pairs stored against the scheduled action.
*/ */
@@ -170,7 +170,7 @@ class WCS_Action_Scheduler extends WCS_Scheduler {
$action_args = array( 'order_id' => $last_order_id ); $action_args = array( 'order_id' => $last_order_id );
} else { } else {
$action_args = array( 'subscription_id' => $subscription->id ); $action_args = array( 'subscription_id' => $subscription->get_id() );
} }
return apply_filters( 'woocommerce_subscriptions_scheduled_action_args', $action_args, $date_type, $subscription ); return apply_filters( 'woocommerce_subscriptions_scheduled_action_args', $action_args, $date_type, $subscription );

View File

@@ -54,8 +54,13 @@ class WCS_API {
return; return;
} }
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
require_once( 'api/legacy/class-wc-rest-subscriptions-controller.php' );
require_once( 'api/legacy/class-wc-rest-subscription-notes-controller.php' );
} else {
require_once( 'api/class-wc-rest-subscriptions-controller.php' ); require_once( 'api/class-wc-rest-subscriptions-controller.php' );
require_once( 'api/class-wc-rest-subscription-notes-controller.php' ); require_once( 'api/class-wc-rest-subscription-notes-controller.php' );
}
foreach ( array( 'WC_REST_Subscriptions_Controller', 'WC_REST_Subscription_Notes_Controller' ) as $api_class ) { foreach ( array( 'WC_REST_Subscriptions_Controller', 'WC_REST_Subscription_Notes_Controller' ) as $api_class ) {
$controller = new $api_class(); $controller = new $api_class();

View File

@@ -97,9 +97,9 @@ class WCS_Cache_Manager_TLC extends WCS_Cache_Manager {
} }
$subscription = array_shift( $subscription ); $subscription = array_shift( $subscription );
$this->log( 'Got subscription, calling wcs_clear_related_order_cache for ' . $subscription->id ); $this->log( 'Got subscription, calling wcs_clear_related_order_cache for ' . $subscription->get_id() );
$this->wcs_clear_related_order_cache( $subscription->id ); $this->wcs_clear_related_order_cache( $subscription->get_id() );
} }
} }
@@ -158,7 +158,7 @@ class WCS_Cache_Manager_TLC extends WCS_Cache_Manager {
// if it's not a Subscription, we don't deal with it // if it's not a Subscription, we don't deal with it
if ( is_object( $id ) && $id instanceof WC_Subscription ) { if ( is_object( $id ) && $id instanceof WC_Subscription ) {
$id = $id->id; $id = $id->get_id();
} elseif ( is_numeric( $id ) ) { } elseif ( is_numeric( $id ) ) {
$id = absint( $id ); $id = absint( $id );
} else { } else {

View File

@@ -75,9 +75,9 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
return; return;
} }
foreach ( wcs_get_subscriptions_for_order( $post_id, array( 'order_type' => 'any' ) ) as $linked_subscription ) { foreach ( wcs_get_subscriptions_for_order( $post_id, array( 'order_type' => 'any' ) ) as $subscription ) {
$this->log( 'Calling purge delete on ' . current_filter() . ' for ' . $linked_subscription->id ); $this->log( 'Calling purge delete on ' . current_filter() . ' for ' . $subscription->get_id() );
$this->clear_related_order_cache( $linked_subscription ); $this->clear_related_order_cache( $subscription );
} }
} }
@@ -108,7 +108,7 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
// if it's not a Subscription, we don't deal with it // if it's not a Subscription, we don't deal with it
if ( is_object( $subscription_id ) && $subscription_id instanceof WC_Subscription ) { if ( is_object( $subscription_id ) && $subscription_id instanceof WC_Subscription ) {
$subscription_id = $subscription_id->id; $subscription_id = $subscription_id->get_id();
} elseif ( is_numeric( $subscription_id ) ) { } elseif ( is_numeric( $subscription_id ) ) {
$subscription_id = absint( $subscription_id ); $subscription_id = absint( $subscription_id );
} else { } else {
@@ -169,7 +169,7 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
$this->log( 'No subscriptions for this ID: ' . $post_id ); $this->log( 'No subscriptions for this ID: ' . $post_id );
} else { } else {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
$this->log( 'Got subscription, calling clear_related_order_cache for ' . $subscription->id ); $this->log( 'Got subscription, calling clear_related_order_cache for ' . $subscription->get_id() );
$this->clear_related_order_cache( $subscription ); $this->clear_related_order_cache( $subscription );
} }
} }

View File

@@ -39,7 +39,7 @@ class WCS_Cart_Initial_Payment extends WCS_Cart_Renewal {
$order_id = ( isset( $wp->query_vars['order-pay'] ) ) ? $wp->query_vars['order-pay'] : absint( $_GET['order_id'] ); $order_id = ( isset( $wp->query_vars['order-pay'] ) ) ? $wp->query_vars['order-pay'] : absint( $_GET['order_id'] );
$order = wc_get_order( $wp->query_vars['order-pay'] ); $order = wc_get_order( $wp->query_vars['order-pay'] );
if ( $order->order_key == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_subscription( $order, 'parent' ) && ! wcs_order_contains_subscription( $order, 'resubscribe' ) ) { if ( wcs_get_objects_property( $order, 'order_key' ) == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_subscription( $order, 'parent' ) && ! wcs_order_contains_subscription( $order, 'resubscribe' ) ) {
if ( ! is_user_logged_in() ) { if ( ! is_user_logged_in() ) {

View File

@@ -26,6 +26,9 @@ class WCS_Cart_Renewal {
$this->setup_hooks(); $this->setup_hooks();
// Attach hooks which depend on WooCommerce constants
add_action( 'woocommerce_loaded', array( &$this, 'attach_dependant_hooks' ), 10 );
// Set URL parameter for manual subscription renewals // Set URL parameter for manual subscription renewals
add_filter( 'woocommerce_get_checkout_payment_url', array( &$this, 'get_checkout_payment_url' ), 10, 2 ); add_filter( 'woocommerce_get_checkout_payment_url', array( &$this, 'get_checkout_payment_url' ), 10, 2 );
@@ -44,15 +47,37 @@ class WCS_Cart_Renewal {
// When a user is prevented from paying for a failed/pending renewal order because they aren't logged in, redirect them back after login // When a user is prevented from paying for a failed/pending renewal order because they aren't logged in, redirect them back after login
add_filter( 'woocommerce_login_redirect', array( &$this, 'maybe_redirect_after_login' ), 10 , 1 ); add_filter( 'woocommerce_login_redirect', array( &$this, 'maybe_redirect_after_login' ), 10 , 1 );
// When a renewal order's line items are being updated, update the line item IDs stored in cart data.
add_action( 'woocommerce_add_order_item_meta', array( &$this, 'update_line_item_cart_data' ), 10, 3 );
// Once we have finished updating the renewal order on checkout, update the session cart so the cart changes are honoured. // Once we have finished updating the renewal order on checkout, update the session cart so the cart changes are honoured.
add_action( 'woocommerce_checkout_order_processed', array( &$this, 'update_session_cart_after_updating_renewal_order' ), 10 ); add_action( 'woocommerce_checkout_order_processed', array( &$this, 'update_session_cart_after_updating_renewal_order' ), 10 );
add_filter( 'wc_dynamic_pricing_apply_cart_item_adjustment', array( &$this, 'prevent_compounding_dynamic_discounts' ), 10, 2 ); add_filter( 'wc_dynamic_pricing_apply_cart_item_adjustment', array( &$this, 'prevent_compounding_dynamic_discounts' ), 10, 2 );
} }
/**
* Attach WooCommerce version dependent hooks
*
* @since 2.2.0
*/
public function attach_dependant_hooks() {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
// When a renewal order's line items are being updated, update the line item IDs stored in cart data.
add_action( 'woocommerce_add_order_item_meta', array( &$this, 'update_line_item_cart_data' ), 10, 3 );
} else {
// For order items created as part of a renewal, keep a record of the cart item key so that we can match it later once the order item has been saved and has an ID
add_action( 'woocommerce_checkout_create_order_line_item', array( &$this, 'add_line_item_meta' ), 10, 3 );
// After order meta is saved, get the order line item ID for the renewal so we can update it later
add_action( 'woocommerce_checkout_update_order_meta', array( &$this, 'set_order_item_id' ), 10, 2 );
// Don't display cart item key meta stored above on the Edit Order screen
add_action( 'woocommerce_hidden_order_itemmeta', array( &$this, 'hidden_order_itemmeta' ), 10 );
}
}
/** /**
* Bootstraps the class and hooks required actions & filters. * Bootstraps the class and hooks required actions & filters.
* *
@@ -111,7 +136,7 @@ class WCS_Cart_Renewal {
$order_id = ( isset( $wp->query_vars['order-pay'] ) ) ? $wp->query_vars['order-pay'] : absint( $_GET['order_id'] ); $order_id = ( isset( $wp->query_vars['order-pay'] ) ) ? $wp->query_vars['order-pay'] : absint( $_GET['order_id'] );
$order = wc_get_order( $wp->query_vars['order-pay'] ); $order = wc_get_order( $wp->query_vars['order-pay'] );
if ( $order->order_key == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_renewal( $order ) ) { if ( wcs_get_objects_property( $order, 'order_key' ) == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_renewal( $order ) ) {
// If a user isn't logged in, allow them to login first and then redirect back // If a user isn't logged in, allow them to login first and then redirect back
if ( ! is_user_logged_in() ) { if ( ! is_user_logged_in() ) {
@@ -142,7 +167,7 @@ class WCS_Cart_Renewal {
// Add the existing subscription items to the cart // Add the existing subscription items to the cart
$this->setup_cart( $order, array( $this->setup_cart( $order, array(
'subscription_id' => $subscription->id, 'subscription_id' => $subscription->get_id(),
'renewal_order_id' => $order_id, 'renewal_order_id' => $order_id,
) ); ) );
@@ -244,6 +269,7 @@ class WCS_Cart_Renewal {
if ( wcs_is_subscription( $subscription ) ) { if ( wcs_is_subscription( $subscription ) ) {
$used_coupons = $subscription->get_used_coupons(); $used_coupons = $subscription->get_used_coupons();
$subscription_discount = wcs_get_objects_property( $subscription, 'cart_discount' );
// Add any used coupon discounts to the cart (as best we can) using our pseudo renewal coupons // Add any used coupon discounts to the cart (as best we can) using our pseudo renewal coupons
if ( ! empty( $used_coupons ) ) { if ( ! empty( $used_coupons ) ) {
@@ -253,33 +279,33 @@ class WCS_Cart_Renewal {
foreach ( $coupon_items as $coupon_item ) { foreach ( $coupon_items as $coupon_item ) {
$coupon = new WC_Coupon( $coupon_item['name'] ); $coupon = new WC_Coupon( $coupon_item['name'] );
$coupon_type = wcs_get_coupon_property( $coupon, 'type' );
$coupon_code = ''; $coupon_code = '';
// If the coupon still exists we can use the existing/available coupon properties // If the coupon still exists we can use the existing/available coupon properties
if ( true === $coupon->exists ) { if ( true === wcs_get_coupon_property( $coupon, 'exists' ) ) {
// But we only want to handle recurring coupons that have been applied to the subscription // But we only want to handle recurring coupons that have been applied to the subscription
if ( in_array( $coupon->type, array( 'recurring_percent', 'recurring_fee' ) ) ) { if ( in_array( $coupon_type, array( 'recurring_percent', 'recurring_fee' ) ) ) {
// Set the coupon type to be a renewal equivalent for correct validation and calculations // Set the coupon type to be a renewal equivalent for correct validation and calculations
if ( 'recurring_percent' == $coupon->type ) { if ( 'recurring_percent' == $coupon_type ) {
$coupon->type = 'renewal_percent'; wcs_set_coupon_property( $coupon, 'type', 'renewal_percent' );
} elseif ( 'recurring_fee' == $coupon->type ) { } elseif ( 'recurring_fee' == $coupon_type ) {
$coupon->type = 'renewal_fee'; wcs_set_coupon_property( $coupon, 'type', 'renewal_fee' );
} }
// Adjust coupon code to reflect that it is being applied to a renewal // Adjust coupon code to reflect that it is being applied to a renewal
$coupon_code = $coupon->code; $coupon_code = wcs_get_coupon_property( $coupon, 'code' );
} }
} else { } else {
// If the coupon doesn't exist we can only really apply the discount amount we know about - so we'll apply a cart style pseudo coupon and then set the amount // If the coupon doesn't exist we can only really apply the discount amount we know about - so we'll apply a cart style pseudo coupon and then set the amount
$coupon->type = 'renewal_cart'; wcs_set_coupon_property( $coupon, 'type', 'renewal_cart' );
$coupon->amount = $coupon_item['item_meta']['discount_amount']['0']; wcs_set_coupon_property( $coupon, 'amount', $coupon_item['item_meta']['discount_amount']['0'] );
// Adjust coupon code to reflect that it is being applied to a renewal // Adjust coupon code to reflect that it is being applied to a renewal
$coupon_code = $coupon->code; $coupon_code = wcs_get_coupon_property( $coupon, 'code' );
} }
// Now that we have a coupon we know we want to apply // Now that we have a coupon we know we want to apply
@@ -287,11 +313,11 @@ class WCS_Cart_Renewal {
// Set renewal order products as the product ids on the coupon // Set renewal order products as the product ids on the coupon
if ( ! WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) { if ( ! WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
$coupon->product_ids = $this->get_products( $subscription ); wcs_set_coupon_property( $coupon, 'product_ids', $this->get_products( $subscription ) );
} }
// Store the coupon info for later // Store the coupon info for later
$this->store_coupon( $subscription->id, $coupon ); $this->store_coupon( $subscription->get_id(), $coupon );
// Add the coupon to the cart - the actually coupon values / data are grabbed when needed later // Add the coupon to the cart - the actually coupon values / data are grabbed when needed later
if ( WC()->cart && ! WC()->cart->has_discount( $coupon_code ) ) { if ( WC()->cart && ! WC()->cart->has_discount( $coupon_code ) ) {
@@ -300,21 +326,22 @@ class WCS_Cart_Renewal {
} }
} }
// If there are no coupons but there is still a discount (i.e. it might have been manually added), we need to account for that as well // If there are no coupons but there is still a discount (i.e. it might have been manually added), we need to account for that as well
} elseif ( ! empty( $subscription->cart_discount ) ) { } elseif ( ! empty( $subscription_discount ) ) {
$coupon = new WC_Coupon( 'discount_renewal' ); $coupon = new WC_Coupon( 'discount_renewal' );
// Apply our cart style pseudo coupon and the set the amount // Apply our cart style pseudo coupon and the set the amount
$coupon->type = 'renewal_cart'; wcs_set_coupon_property( $coupon, 'type', 'renewal_cart' );
$coupon->amount = $subscription->cart_discount;
wcs_set_coupon_property( $coupon, 'amount', $subscription_discount );
// Set renewal order products as the product ids on the coupon // Set renewal order products as the product ids on the coupon
if ( ! WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) { if ( ! WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
$coupon->product_ids = $this->get_products( $subscription ); wcs_set_coupon_property( $coupon, 'product_ids', $this->get_products( $subscription ) );
} }
// Store the coupon info for later // Store the coupon info for later
$this->store_coupon( $subscription->id, $coupon ); $this->store_coupon( $subscription->get_id(), $coupon );
// Add the coupon to the cart // Add the coupon to the cart
if ( WC()->cart && ! WC()->cart->has_discount( 'discount_renewal' ) ) { if ( WC()->cart && ! WC()->cart->has_discount( 'discount_renewal' ) ) {
@@ -390,21 +417,22 @@ class WCS_Cart_Renewal {
if ( wc_prices_include_tax() ) { if ( wc_prices_include_tax() ) {
if ( apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) { if ( apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
$base_tax_rates = WC_Tax::get_base_tax_rates( $_product->tax_class ); $base_tax_rates = WC_Tax::get_base_tax_rates( wcs_get_objects_property( $_product, 'tax_class' ) );
} else { } else {
$base_tax_rates = WC_Tax::get_rates( $_product->tax_class ); $base_tax_rates = WC_Tax::get_rates( wcs_get_objects_property( $_product, 'tax_class' ) );
} }
$base_taxes_on_item = WC_Tax::calc_tax( $price, $base_tax_rates, false, false ); $base_taxes_on_item = WC_Tax::calc_tax( $price, $base_tax_rates, false, false );
$price += array_sum( $base_taxes_on_item ); $price += array_sum( $base_taxes_on_item );
} }
$_product->price = $price / $item_to_renew['qty']; $_product->set_price( $price / $item_to_renew['qty'] );
// Don't carry over any sign up fee // Don't carry over any sign up fee
$_product->subscription_sign_up_fee = 0; wcs_set_objects_property( $_product, 'subscription_sign_up_fee', 0, 'set_prop_only' );
$_product->post->post_title = apply_filters( 'woocommerce_subscriptions_renewal_product_title', $_product->get_title(), $_product ); // Allow plugins to add additional strings to the product name for renewals
wcs_set_objects_property( $_product, 'name', apply_filters( 'woocommerce_subscriptions_renewal_product_title', $_product->get_title(), $_product ), 'set_prop_only' );
// Make sure the same quantity is renewed // Make sure the same quantity is renewed
$cart_item_session_data['quantity'] = $item_to_renew['qty']; $cart_item_session_data['quantity'] = $item_to_renew['qty'];
@@ -424,12 +452,16 @@ class WCS_Cart_Renewal {
public function checkout_get_value( $value, $key ) { public function checkout_get_value( $value, $key ) {
// Only hook in after WC()->checkout() has been initialised // Only hook in after WC()->checkout() has been initialised
if ( did_action( 'woocommerce_checkout_init' ) > 0 ) { if ( $this->cart_contains() && did_action( 'woocommerce_checkout_init' ) > 0 ) {
// Guard against the fake WC_Checkout singleton, see https://github.com/woocommerce/woocommerce-subscriptions/issues/427#issuecomment-260763250 // Guard against the fake WC_Checkout singleton, see https://github.com/woocommerce/woocommerce-subscriptions/issues/427#issuecomment-260763250
remove_filter( 'woocommerce_checkout_get_value', array( &$this, 'checkout_get_value' ), 10, 2 ); remove_filter( 'woocommerce_checkout_get_value', array( &$this, 'checkout_get_value' ), 10, 2 );
if ( is_callable( array( WC()->checkout(), 'get_checkout_fields' ) ) ) { // WC 3.0+
$address_fields = array_merge( WC()->checkout()->get_checkout_fields( 'billing' ), WC()->checkout()->get_checkout_fields( 'shipping' ) );
} else {
$address_fields = array_merge( WC()->checkout()->checkout_fields['billing'], WC()->checkout()->checkout_fields['shipping'] ); $address_fields = array_merge( WC()->checkout()->checkout_fields['billing'], WC()->checkout()->checkout_fields['shipping'] );
}
add_filter( 'woocommerce_checkout_get_value', array( &$this, 'checkout_get_value' ), 10, 2 ); add_filter( 'woocommerce_checkout_get_value', array( &$this, 'checkout_get_value' ), 10, 2 );
@@ -438,8 +470,8 @@ class WCS_Cart_Renewal {
// Get the most specific order object, which will be the renewal order for renewals, initial order for initial payments, or a subscription for switches/resubscribes // Get the most specific order object, which will be the renewal order for renewals, initial order for initial payments, or a subscription for switches/resubscribes
$order = $this->get_order( $item ); $order = $this->get_order( $item );
if ( isset( $order->$key ) ) { if ( ( $order_value = wcs_get_objects_property( $order, $key ) ) ) {
$value = $order->$key; $value = $order_value;
} }
} }
} }
@@ -582,13 +614,18 @@ class WCS_Cart_Renewal {
*/ */
public function maybe_preserve_order_status( $order_status ) { public function maybe_preserve_order_status( $order_status ) {
if ( null !== WC()->session ) { if ( null !== WC()->session && 'failed' !== $order_status ) {
$order_id = absint( WC()->session->order_awaiting_payment ); $order_id = absint( WC()->session->order_awaiting_payment );
// Guard against infinite loops in WC 3.0+ where default order staus is set in WC_Abstract_Order::__construct()
remove_filter( 'woocommerce_default_order_status', array( &$this, __FUNCTION__ ), 10 );
if ( $order_id > 0 && ( $order = wc_get_order( $order_id ) ) && wcs_order_contains_renewal( $order ) && $order->has_status( 'failed' ) ) { if ( $order_id > 0 && ( $order = wc_get_order( $order_id ) ) && wcs_order_contains_renewal( $order ) && $order->has_status( 'failed' ) ) {
$order_status = 'failed'; $order_status = 'failed';
} }
add_filter( 'woocommerce_default_order_status', array( &$this, __FUNCTION__ ) );
} }
return $order_status; return $order_status;
@@ -710,28 +747,59 @@ class WCS_Cart_Renewal {
foreach ( $coupons as $coupon ) { foreach ( $coupons as $coupon ) {
// Tweak the coupon data for renewal coupons // Tweak the coupon data for renewal coupons
if ( $code == $coupon->code ) { if ( wcs_get_coupon_property( $coupon, 'code' ) == $code ) {
$data = array( $data = array(
'discount_type' => $coupon->type, 'id' => true,
'coupon_amount' => $coupon->amount, 'discount_type' => wcs_get_coupon_property( $coupon, 'type' ),
'individual_use' => ( $coupon->individual_use ) ? $coupon->individual_use : 'no', 'amount' => wcs_get_coupon_property( $coupon, 'amount' ),
'product_ids' => ( $coupon->product_ids ) ? $coupon->product_ids : array(), 'individual_use' => ( $individual_use = wcs_get_coupon_property( $coupon, 'individual_use' ) ) ? $individual_use : false,
'exclude_product_ids' => ( $coupon->exclude_product_ids ) ? $coupon->exclude_product_ids : array(), 'product_ids' => ( $product_ids = wcs_get_coupon_property( $coupon, 'product_ids' ) ) ? $product_ids : array(),
'excluded_product_ids' => ( $excluded_product_ids = wcs_get_coupon_property( $coupon, 'exclude_product_ids' ) ) ? $excluded_product_ids : array(),
'usage_limit' => '', 'usage_limit' => '',
'usage_count' => '', 'usage_count' => '',
'expiry_date' => '', 'date_expires' => '',
'free_shipping' => ( $coupon->free_shipping ) ? $coupon->free_shipping : '', 'free_shipping' => ( $free_shipping = wcs_get_coupon_property( $coupon, 'free_shipping' ) ) ? $free_shipping : false,
'product_categories' => ( $coupon->product_categories ) ? $coupon->product_categories : array(), 'product_categories' => ( $product_categories = wcs_get_coupon_property( $coupon, 'product_categories' ) ) ? $product_categories : array(),
'exclude_product_categories' => ( $coupon->exclude_product_categories ) ? $coupon->exclude_product_categories : array(), 'excluded_product_categories' => ( $excluded_product_categories = wcs_get_coupon_property( $coupon, 'exclude_product_categories' ) ) ? $excluded_product_categories : array(),
'exclude_sale_items' => ( $coupon->exclude_sale_items ) ? $coupon->exclude_sale_items : 'no', 'exclude_sale_items' => ( $exclude_sale_items = wcs_get_coupon_property( $coupon, 'exclude_sale_items' ) ) ? $exclude_sale_items : false,
'minimum_amount' => ( $coupon->minimum_amount ) ? $coupon->minimum_amount : '', 'minimum_amount' => ( $minimum_amount = wcs_get_coupon_property( $coupon, 'minimum_amount' ) ) ? $minimum_amount : '',
'maximum_amount' => ( $coupon->maximum_amount ) ? $coupon->maximum_amount : '', 'maximum_amount' => ( $maximum_amount = wcs_get_coupon_property( $coupon, 'maximum_amount' ) ) ? $maximum_amount : '',
'customer_email' => ( $coupon->customer_email ) ? $coupon->customer_email : array(), 'customer_email' => ( $customer_email = wcs_get_coupon_property( $coupon, 'customer_email' ) ) ? $customer_email : array(),
); );
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
// Pre 3.0 we don't need to pass the id.
unset( $data['id'] );
// Some keys have changed between WC 2.6.x and WC 3.0. This array holds those changes in a 2.6 => 3.0 format.
$property_changes = array(
'coupon_amount' => 'amount',
'exclude_product_ids' => 'excluded_product_ids',
'expiry_date' => 'date_expires',
'exclude_product_categories' => 'excluded_product_categories',
'customer_email' => 'email_restrictions',
);
foreach ( $data as $key => $value ) {
// Switch the 3.0 key out for the 2.6 equivalent
if ( in_array( $key, $property_changes ) ) {
$data[ array_search( $key, $property_changes ) ] = $value;
unset( $data[ $key ] );
}
// Some coupon properties have changed from accepting 'no' and 'yes' to true and false args. We need to change them into the correct format
if ( is_bool( $value ) && in_array( $key, array( 'individual_use', 'free_shipping', 'exclude_sale_items' ) ) ) {
$data[ $key ] = ( true == $value ) ? 'yes' : 'no';
} }
} }
} }
}
}
}
return $data; return $data;
} }
@@ -794,7 +862,7 @@ class WCS_Cart_Renewal {
if ( ! empty( $renewal_coupons ) ) { if ( ! empty( $renewal_coupons ) ) {
foreach ( $renewal_coupons as $subscription_id => $coupons ) { foreach ( $renewal_coupons as $subscription_id => $coupons ) {
foreach ( $coupons as $coupon ) { foreach ( $coupons as $coupon ) {
WC()->cart->remove_coupons( $coupon->code ); WC()->cart->remove_coupons( wcs_get_coupon_property( $coupon, 'code' ) );
} }
} }
} }
@@ -868,7 +936,8 @@ class WCS_Cart_Renewal {
* @since 2.0.14 * @since 2.0.14
*/ */
protected function set_cart_hash( $order_id ) { protected function set_cart_hash( $order_id ) {
update_post_meta( $order_id, '_cart_hash', md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total ) ); $order = wc_get_order( $order_id );
wcs_set_objects_property( $order, 'cart_hash', md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total ) );
} }
/** /**
@@ -907,24 +976,12 @@ class WCS_Cart_Renewal {
return $redirect; return $redirect;
} }
/**
* After updating renewal order line items, update the values stored in cart item data
* which would now reference old line item IDs.
*
* @since 2.1.3
*/
public function update_line_item_cart_data( $item_id, $cart_item_data, $cart_item_key ) {
if ( isset( $cart_item_data[ $this->cart_item_key ] ) ) {
// Update the line_item_id to the new corresponding item_id
WC()->cart->cart_contents[ $cart_item_key ][ $this->cart_item_key ]['line_item_id'] = $item_id;
}
}
/** /**
* Force an update to the session cart after updating renewal order line items. * Force an update to the session cart after updating renewal order line items.
* This is required so that changes made by @see WCS_Cart_Renewal->update_line_item_cart_data() *
* are also reflected in the session cart. * This is required so that changes made by @see WCS_Cart_Renewal->add_line_item_meta() (or @see
* WCS_Cart_Renewal->update_line_item_cart_data() for WC < 3.0), are also reflected
* in the session cart.
* *
* @since 2.1.3 * @since 2.1.3
*/ */
@@ -954,6 +1011,76 @@ class WCS_Cart_Renewal {
return $adjust_price; return $adjust_price;
} }
/**
* For order items created as part of a renewal, keep a record of the cart item key so that we can match it
* later in @see this->set_order_item_id() once the order item has been saved and has an ID.
*
* Attached to WC 3.0+ hooks and uses WC 3.0 methods.
*
* @param WC_Order_Item_Product $order_item
* @param string $cart_item_key The hash used to identify the item in the cart
* @param array $cart_item The cart item's data.
* @since 2.2.0
*/
public function add_line_item_meta( $order_item, $cart_item_key, $cart_item ) {
if ( isset( $cart_item[ $this->cart_item_key ] ) ) {
// Store the cart item key on the line item so that we can link it later on to the order line item ID
$order_item->add_meta_data( '_cart_item_key_' . $this->cart_item_key, $cart_item_key );
}
}
/**
* After order meta is saved, get the order line item ID for this renewal and keep a record of it in
* the cart so we can update it later.
*
* @param int $order_id
* @param array $checkout_posted_data
* @since 2.2.1
*/
public function set_order_item_id( $order_id, $posted_checkout_data ) {
$order = wc_get_order( $order_id );
foreach ( $order->get_items( 'line_item' ) as $order_item_id => $order_item ) {
$cart_item_key = $order_item->get_meta( '_cart_item_key_' . $this->cart_item_key );
if ( ! empty( $cart_item_key ) ) {
// Update the line_item_id to the new corresponding item_id
$this->set_cart_item_order_item_id( $cart_item_key, $order_item_id );
}
}
}
/**
* After updating renewal order line items, update the values stored in cart item data
* which would now reference old line item IDs.
*
* Used when WC 3.0 or newer is active. When prior versions are active,
* @see WCS_Cart_Renewal->update_line_item_cart_data()
*
* @param string $cart_item_key
* @param int $order_item_id
* @since 2.2.1
*/
protected function set_cart_item_order_item_id( $cart_item_key, $order_item_id ) {
WC()->cart->cart_contents[ $cart_item_key ][ $this->cart_item_key ]['line_item_id'] = $order_item_id;
}
/**
* Do not display cart item key order item meta keys unless Subscriptions is in debug mode.
*
* @since 2.2.1
*/
public function hidden_order_itemmeta( $hidden_meta_keys ) {
if ( apply_filters( 'woocommerce_subscriptions_hide_itemmeta', ! defined( 'WCS_DEBUG' ) || true !== WCS_DEBUG ) ) {
$hidden_meta_keys[] = '_cart_item_key_' . $this->cart_item_key;
}
return $hidden_meta_keys;
}
/* Deprecated */ /* Deprecated */
/** /**
@@ -985,5 +1112,38 @@ class WCS_Cart_Renewal {
public function maybe_add_subscription_fees( $cart ) { public function maybe_add_subscription_fees( $cart ) {
_deprecated_function( __METHOD__, '2.0.13', __CLASS__ .'::maybe_add_fees()' ); _deprecated_function( __METHOD__, '2.0.13', __CLASS__ .'::maybe_add_fees()' );
} }
/**
* After updating renewal order line items, update the values stored in cart item data
* which would now reference old line item IDs.
*
* @since 2.1.3
*/
public function update_line_item_cart_data( $item_id, $cart_item_data, $cart_item_key ) {
if ( false === WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
_deprecated_function( __METHOD__, '2.2.0 and WooCommerce 3.0', __CLASS__ . '::add_line_item_meta( $order_item, $cart_item_key, $cart_item )' );
}
if ( isset( $cart_item_data[ $this->cart_item_key ] ) ) {
// Update the line_item_id to the new corresponding item_id
WC()->cart->cart_contents[ $cart_item_key ][ $this->cart_item_key ]['line_item_id'] = $item_id;
}
}
/**
* After updating renewal order line items, update the values stored in cart item data
* which would now reference old line item IDs.
*
* Used when WC 3.0 or newer is active. When prior versions are active,
* @see WCS_Cart_Renewal->update_line_item_cart_data()
*
* @deprecated 2.2.1
* @since 2.2.0
*/
public function update_order_item_data_in_cart( $order_item, $cart_item_key, $cart_item ) {
_deprecated_function( __METHOD__, '2.2.1', __CLASS__ . '::add_line_item_meta( $order_item, $cart_item_key, $cart_item )' );
$this->add_line_item_meta( $order_item, $cart_item_key, $cart_item );
}
} }
new WCS_Cart_Renewal(); new WCS_Cart_Renewal();

View File

@@ -65,7 +65,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
$subscription = wcs_get_subscription( $_GET['resubscribe'] ); $subscription = wcs_get_subscription( $_GET['resubscribe'] );
$redirect_to = get_permalink( wc_get_page_id( 'myaccount' ) ); $redirect_to = get_permalink( wc_get_page_id( 'myaccount' ) );
if ( wp_verify_nonce( $_GET['_wpnonce'], $subscription->id ) === false ) { if ( wp_verify_nonce( $_GET['_wpnonce'], $subscription->get_id() ) === false ) {
wc_add_notice( __( 'There was an error with your request to resubscribe. Please try again.', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'There was an error with your request to resubscribe. Please try again.', 'woocommerce-subscriptions' ), 'error' );
@@ -73,7 +73,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
wc_add_notice( __( 'That subscription does not exist. Has it been deleted?', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'That subscription does not exist. Has it been deleted?', 'woocommerce-subscriptions' ), 'error' );
} elseif ( ! current_user_can( 'subscribe_again', $subscription->id ) ) { } elseif ( ! current_user_can( 'subscribe_again', $subscription->get_id() ) ) {
wc_add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' );
@@ -84,7 +84,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
} else { } else {
$this->setup_cart( $subscription, array( $this->setup_cart( $subscription, array(
'subscription_id' => $subscription->id, 'subscription_id' => $subscription->get_id(),
) ); ) );
if ( WC()->cart->get_cart_contents_count() != 0 ) { if ( WC()->cart->get_cart_contents_count() != 0 ) {
@@ -103,7 +103,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
$order = wc_get_order( $wp->query_vars['order-pay'] ); $order = wc_get_order( $wp->query_vars['order-pay'] );
$order_key = $_GET['key']; $order_key = $_GET['key'];
if ( $order->order_key == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_resubscribe( $order ) ) { if ( wcs_get_objects_property( $order, 'order_key' ) == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_resubscribe( $order ) ) {
if ( ! is_user_logged_in() ) { if ( ! is_user_logged_in() ) {
@@ -121,9 +121,9 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
$subscriptions = wcs_get_subscriptions_for_resubscribe_order( $order ); $subscriptions = wcs_get_subscriptions_for_resubscribe_order( $order );
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
if ( current_user_can( 'subscribe_again', $subscription->id ) ) { if ( current_user_can( 'subscribe_again', $subscription->get_id() ) ) {
$this->setup_cart( $subscription, array( $this->setup_cart( $subscription, array(
'subscription_id' => $subscription->id, 'subscription_id' => $subscription->get_id(),
) ); ) );
} else { } else {
wc_add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' );
@@ -150,8 +150,9 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
$cart_item = $this->cart_contains( $recurring_cart ); $cart_item = $this->cart_contains( $recurring_cart );
if ( false !== $cart_item ) { if ( false !== $cart_item ) {
update_post_meta( $order->id, '_subscription_resubscribe', $cart_item[ $this->cart_item_key ]['subscription_id'], true ); wcs_set_objects_property( $order, 'subscription_resubscribe', $cart_item[ $this->cart_item_key ]['subscription_id'] );
update_post_meta( $new_subscription->id, '_subscription_resubscribe', $cart_item[ $this->cart_item_key ]['subscription_id'], true ); $new_subscription->update_meta_data( '_subscription_resubscribe', $cart_item[ $this->cart_item_key ]['subscription_id'] );
$new_subscription->save();
} }
} }
@@ -171,11 +172,11 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
if ( $subscription ) { if ( $subscription ) {
// Make sure the original subscription terms perisist // Make sure the original subscription terms perisist
$_product = $cart_item_session_data['data']; $_product = $cart_item_session_data['data'];
$_product->subscription_period = $subscription->billing_period; wcs_set_objects_property( $_product, 'subscription_period', $subscription->get_billing_period(), 'set_prop_only' );
$_product->subscription_period_interval = $subscription->billing_interval; wcs_set_objects_property( $_product, 'subscription_period_interval', $subscription->get_billing_interval(), 'set_prop_only' );
// And don't give another free trial period // And don't give another free trial period
$_product->subscription_trial_length = 0; wcs_set_objects_property( $_product, 'subscription_trial_length', 0, 'set_prop_only' );
} }
} }
@@ -254,7 +255,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$subscription = $this->get_order( $cart_item ); $subscription = $this->get_order( $cart_item );
if ( false !== $subscription && $subscription->has_status( 'pending-cancel' ) ) { if ( false !== $subscription && $subscription->has_status( 'pending-cancel' ) ) {
$first_renewal_date = ( '1' != $cart_item['data']->subscription_length ) ? $subscription->get_date( 'end' ) : 0; $first_renewal_date = ( '1' != WC_Subscriptions_Product::get_length( $cart_item['data'] ) ) ? $subscription->get_date( 'end' ) : 0;
break; break;
} }
} }
@@ -271,7 +272,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$subscription = $this->get_order( $cart_item ); $subscription = $this->get_order( $cart_item );
if ( false !== $subscription && $subscription->has_status( 'pending-cancel' ) ) { if ( false !== $subscription && $subscription->has_status( 'pending-cancel' ) ) {
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length = 1; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_trial_length', 1, 'set_prop_only' );
break; break;
} }
} }
@@ -289,7 +290,7 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$subscription = $this->get_order( $cart_item ); $subscription = $this->get_order( $cart_item );
if ( false !== $subscription && $subscription->has_status( 'pending-cancel' ) ) { if ( false !== $subscription && $subscription->has_status( 'pending-cancel' ) ) {
WC()->cart->cart_contents[ $cart_item_key ]['data']->subscription_trial_length = 0; wcs_set_objects_property( WC()->cart->cart_contents[ $cart_item_key ]['data'], 'subscription_trial_length', 0, 'set_prop_only' );
break; break;
} }
} }

View File

@@ -2,11 +2,13 @@
/** /**
* Subscriptions switching cart * Subscriptions switching cart
* *
*
* @author Prospress * @author Prospress
* @since 2.1 * @since 2.1
*/ */
class WCS_Cart_Switch extends WCS_Cart_Renewal{ class WCS_Cart_Switch extends WCS_Cart_Renewal {
/* The flag used to indicate if a cart item is a renewal */
public $cart_item_key = 'subscription_switch';
/** /**
* Initialise class hooks & filters when the file is loaded * Initialise class hooks & filters when the file is loaded
@@ -15,6 +17,9 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal{
*/ */
public function __construct() { public function __construct() {
// Attach hooks which depend on WooCommerce constants
add_action( 'woocommerce_loaded', array( &$this, 'attach_dependant_hooks' ), 10 );
// Set checkout payment URL parameter for subscription switch orders // Set checkout payment URL parameter for subscription switch orders
add_filter( 'woocommerce_get_checkout_payment_url', array( &$this, 'get_checkout_payment_url' ), 10, 2 ); add_filter( 'woocommerce_get_checkout_payment_url', array( &$this, 'get_checkout_payment_url' ), 10, 2 );
@@ -30,7 +35,7 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal{
public function get_checkout_payment_url( $pay_url, $order ) { public function get_checkout_payment_url( $pay_url, $order ) {
if ( wcs_order_contains_switch( $order ) ) { if ( wcs_order_contains_switch( $order ) ) {
$switch_order_data = get_post_meta( $order->id, '_subscription_switch_data', true ); $switch_order_data = wcs_get_objects_property( $order, 'subscription_switch_data' );
if ( ! empty( $switch_order_data ) ) { if ( ! empty( $switch_order_data ) ) {
$pay_url = add_query_arg( array( $pay_url = add_query_arg( array(
@@ -51,7 +56,6 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal{
* @since 2.1 * @since 2.1
*/ */
public function maybe_setup_cart() { public function maybe_setup_cart() {
global $wp; global $wp;
if ( isset( $_GET['pay_for_order'] ) && isset( $_GET['key'] ) && isset( $wp->query_vars['order-pay'] ) && isset( $_GET['subscription_switch'] ) ) { if ( isset( $_GET['pay_for_order'] ) && isset( $_GET['key'] ) && isset( $wp->query_vars['order-pay'] ) && isset( $_GET['subscription_switch'] ) ) {
@@ -61,10 +65,10 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal{
$order_id = ( isset( $wp->query_vars['order-pay'] ) ) ? $wp->query_vars['order-pay'] : absint( $_GET['order_id'] ); $order_id = ( isset( $wp->query_vars['order-pay'] ) ) ? $wp->query_vars['order-pay'] : absint( $_GET['order_id'] );
$order = wc_get_order( $wp->query_vars['order-pay'] ); $order = wc_get_order( $wp->query_vars['order-pay'] );
if ( $order->order_key == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_switch( $order ) ) { if ( wcs_get_objects_property( $order, 'order_key' ) == $order_key && $order->has_status( array( 'pending', 'failed' ) ) && wcs_order_contains_switch( $order ) ) {
WC()->cart->empty_cart( true ); WC()->cart->empty_cart( true );
$switch_order_data = get_post_meta( $order_id, '_subscription_switch_data', true ); $switch_order_data = wcs_get_objects_property( $order, 'subscription_switch_data' );
foreach ( $order->get_items() as $item_id => $line_item ) { foreach ( $order->get_items() as $item_id => $line_item ) {
@@ -88,28 +92,28 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal{
$order_item = wcs_get_order_item( $item_id, $order ); $order_item = wcs_get_order_item( $item_id, $order );
$product = WC_Subscriptions::get_product( wcs_get_canonical_product_id( $order_item ) ); $product = WC_Subscriptions::get_product( wcs_get_canonical_product_id( $order_item ) );
$product_id = $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id();
$order_product_data = array( $order_product_data = array(
'_qty' => 0, '_qty' => (int) $line_item['qty'],
'_variation_id' => '', '_variation_id' => (int) $line_item['variation_id'],
); );
$variations = array(); $variations = array();
foreach ( $order_item['item_meta'] as $meta_key => $meta_value ) { foreach ( $order_item['item_meta'] as $meta_key => $meta_value ) {
$meta_value = is_array( $meta_value ) ? $meta_value[0] : $meta_value; // In WC 3.0 the meta values are no longer arrays
if ( taxonomy_is_product_attribute( $meta_key ) || meta_is_product_attribute( $meta_key, $meta_value[0], $product->id ) ) { if ( taxonomy_is_product_attribute( $meta_key ) || meta_is_product_attribute( $meta_key, $meta_value, $product_id ) ) {
$variations[ $meta_key ] = $meta_value[0]; $variations[ $meta_key ] = $meta_value;
$_POST[ 'attribute_' . $meta_key ] = $meta_value[0]; $_POST[ 'attribute_' . $meta_key ] = $meta_value;
} else if ( array_key_exists( $meta_key, $order_product_data ) ) {
$order_product_data[ $meta_key ] = (int) $meta_value[0];
} }
} }
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product->id, $order_product_data['_qty'], $order_product_data['_variation_id'] ); $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $order_product_data['_qty'], $order_product_data['_variation_id'] );
if ( $passed_validation ) { if ( $passed_validation ) {
$cart_item_key = WC()->cart->add_to_cart( $product->id, $order_product_data['_qty'], $order_product_data['_variation_id'], $variations, array() ); $cart_item_key = WC()->cart->add_to_cart( $product_id, $order_product_data['_qty'], $order_product_data['_variation_id'], $variations, array() );
} }
} }
} }
@@ -121,5 +125,26 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal{
exit; exit;
} }
} }
/**
* Store the order line item id so it can be retrieved when we're processing the switch on checkout.
*
* @param string $cart_item_key
* @param int $order_item_id
* @since 2.2.1
*/
protected function set_cart_item_order_item_id( $cart_item_key, $order_item_id ) {
foreach ( WC()->cart->recurring_carts as $recurring_cart_key => $recurring_cart ) {
// If this cart item belongs to this recurring cart
if ( in_array( $cart_item_key, array_keys( $recurring_cart->cart_contents ) ) && isset( WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ][ $this->cart_item_key ] ) ) {
WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ][ $this->cart_item_key ]['order_line_item_id'] = $order_item_id;
wc_add_order_item_meta( WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ][ $this->cart_item_key ]['item_id'], '_switched_subscription_new_item_id', $order_item_id, true );
}
}
}
} }
new WCS_Cart_Switch(); new WCS_Cart_Switch();

View File

@@ -19,7 +19,7 @@ class WCS_Change_Payment_Method_Admin {
*/ */
public static function display_fields( $subscription ) { public static function display_fields( $subscription ) {
$payment_method = ! empty( $subscription->payment_method ) ? $subscription->payment_method : ''; $payment_method = $subscription->get_payment_method();
$valid_payment_methods = self::get_valid_payment_methods( $subscription ); $valid_payment_methods = self::get_valid_payment_methods( $subscription );
if ( ! $subscription->is_manual() && ! isset( $valid_payment_methods[ $payment_method ] ) ) { if ( ! $subscription->is_manual() && ! isset( $valid_payment_methods[ $payment_method ] ) ) {
@@ -129,7 +129,7 @@ class WCS_Change_Payment_Method_Admin {
$payment_gateway = ( 'manual' != $payment_method ) ? $payment_gateways[ $payment_method ] : ''; $payment_gateway = ( 'manual' != $payment_method ) ? $payment_gateways[ $payment_method ] : '';
if ( ! $subscription->is_manual() && property_exists( $subscription->payment_gateway, 'id' ) && ( '' == $payment_gateway || ( $subscription->payment_gateway->id != $payment_gateway->id ) ) ) { if ( ! $subscription->is_manual() && ( '' == $payment_gateway || $subscription->get_payment_method() != $payment_gateway->id ) ) {
// Before updating to a new payment gateway make sure the subscription status is updated with the current gateway // Before updating to a new payment gateway make sure the subscription status is updated with the current gateway
$gateway_status = apply_filters( 'wcs_gateway_status_payment_changed', 'cancelled', $subscription, $payment_gateway ); $gateway_status = apply_filters( 'wcs_gateway_status_payment_changed', 'cancelled', $subscription, $payment_gateway );
@@ -137,6 +137,7 @@ class WCS_Change_Payment_Method_Admin {
} }
$subscription->set_payment_method( $payment_gateway, $payment_method_meta ); $subscription->set_payment_method( $payment_gateway, $payment_method_meta );
$subscription->save();
} }
/** /**
@@ -158,7 +159,7 @@ class WCS_Change_Payment_Method_Admin {
foreach ( $available_gateways as $gateway_id => $gateway ) { foreach ( $available_gateways as $gateway_id => $gateway ) {
if ( $gateway->supports( 'subscription_payment_method_change_admin' ) && 'no' == get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) || ( ! $subscription->is_manual() && $gateway_id == $subscription->payment_method ) ) { if ( $gateway->supports( 'subscription_payment_method_change_admin' ) && 'no' == get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) || ( ! $subscription->is_manual() && $gateway_id == $subscription->get_payment_method() ) ) {
$valid_gateways[ $gateway_id ] = $gateway->get_title(); $valid_gateways[ $gateway_id ] = $gateway->get_title();
} }

View File

@@ -50,7 +50,7 @@ class WCS_Download_Handler {
*/ */
public static function maybe_revoke_immediate_access( $grant_access, $download_id, $product_id, $order ) { public static function maybe_revoke_immediate_access( $grant_access, $download_id, $product_id, $order ) {
if ( 'yes' == get_option( WC_Subscriptions_Admin::$option_prefix . '_drip_downloadable_content_on_renewal', 'no' ) && ( wcs_is_subscription( $order->id ) || wcs_order_contains_subscription( $order, 'any' ) ) ) { if ( 'yes' == get_option( WC_Subscriptions_Admin::$option_prefix . '_drip_downloadable_content_on_renewal', 'no' ) && ( wcs_is_subscription( wcs_get_objects_property( $order, 'id' ) ) || wcs_order_contains_subscription( $order, 'any' ) ) ) {
$grant_access = false; $grant_access = false;
} }
return $grant_access; return $grant_access;
@@ -79,20 +79,20 @@ class WCS_Download_Handler {
$_product = $subscription->get_product_from_item( $item ); $_product = $subscription->get_product_from_item( $item );
if ( $_product && $_product->exists() && $_product->is_downloadable() ) { if ( $_product && $_product->exists() && $_product->is_downloadable() ) {
$downloads = $_product->get_files(); $downloads = wcs_get_objects_property( $_product, 'downloads' );
$product_id = wcs_get_canonical_product_id( $item ); $product_id = wcs_get_canonical_product_id( $item );
foreach ( array_keys( $downloads ) as $download_id ) { foreach ( array_keys( $downloads ) as $download_id ) {
// grant access on subscription if it does not already exist // grant access on subscription if it does not already exist
if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT download_id FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE `order_id` = %d AND `product_id` = %d AND `download_id` = '%s'", $subscription->id, $product_id, $download_id ) ) ) { if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT download_id FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE `order_id` = %d AND `product_id` = %d AND `download_id` = '%s'", $subscription->get_id(), $product_id, $download_id ) ) ) {
wc_downloadable_file_permission( $download_id, $product_id, $subscription, $item['qty'] ); wc_downloadable_file_permission( $download_id, $product_id, $subscription, $item['qty'] );
} }
self::revoke_downloadable_file_permission( $product_id, $order_id, $order->user_id ); self::revoke_downloadable_file_permission( $product_id, $order_id, $order->get_user_id() );
} }
} }
} }
} }
update_post_meta( $subscription->id, '_download_permissions_granted', 1 ); update_post_meta( $subscription->get_id(), '_download_permissions_granted', 1 );
} }
} }
@@ -147,10 +147,14 @@ class WCS_Download_Handler {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
foreach ( $subscription->get_items() as $subscription_item ) { foreach ( $subscription->get_items() as $subscription_item ) {
if ( wcs_get_canonical_product_id( $subscription_item ) === $product_id ) { if ( wcs_get_canonical_product_id( $subscription_item ) === $product_id ) {
if ( is_callable( array( $subscription_item, 'get_item_downloads' ) ) ) { // WC 3.0+
$files = $subscription_item->get_item_downloads( $subscription_item );
} else { // WC < 3.0
$files = $subscription->get_item_downloads( $subscription_item ); $files = $subscription->get_item_downloads( $subscription_item );
} }
} }
} }
}
return $files; return $files;
} }
@@ -195,7 +199,7 @@ class WCS_Download_Handler {
/** /**
* Grant downloadable file access to any newly added files on any existing subscriptions * Grant downloadable file access to any newly added files on any existing subscriptions
* which don't have existing permissions. * which don't have existing permissions pre WC3.0 and all subscriptions post WC3.0.
* *
* @param int $product_id * @param int $product_id
* @param int $variation_id * @param int $variation_id
@@ -207,7 +211,7 @@ class WCS_Download_Handler {
$product_id = ( $variation_id ) ? $variation_id : $product_id; $product_id = ( $variation_id ) ? $variation_id : $product_id;
$product = wc_get_product( $product_id ); $product = wc_get_product( $product_id );
$existing_download_ids = array_keys( (array) $product->get_files() ); $existing_download_ids = array_keys( (array) wcs_get_objects_property( $product, 'downloads' ) );
$downloadable_ids = array_keys( (array) $downloadable_files ); $downloadable_ids = array_keys( (array) $downloadable_files );
$new_download_ids = array_filter( array_diff( $downloadable_ids, $existing_download_ids ) ); $new_download_ids = array_filter( array_diff( $downloadable_ids, $existing_download_ids ) );
@@ -218,8 +222,8 @@ class WCS_Download_Handler {
foreach ( $subscriptions as $subscription_id ) { foreach ( $subscriptions as $subscription_id ) {
// only grant permissions to subscriptions which have no permissions for this product // Grant permissions to subscriptions which have no permissions for this product, pre WC3.0, or all subscriptions, post WC3.0, as WC doesn't grant them retrospectively anymore.
if ( ! in_array( $subscription_id, $existing_permissions ) ) { if ( ! in_array( $subscription_id, $existing_permissions ) || false === WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$subscription = wcs_get_subscription( $subscription_id ); $subscription = wcs_get_subscription( $subscription_id );
foreach ( $new_download_ids as $download_id ) { foreach ( $new_download_ids as $download_id ) {

View File

@@ -75,9 +75,11 @@ class WCS_Limiter {
} }
break; break;
case 'subscription_variation' : case 'subscription_variation' :
if ( 'no' != wcs_get_product_limitation( $product->parent ) && ! empty( WC()->cart->cart_contents ) && ! wcs_is_order_received_page() && ! wcs_is_paypal_api_page() ) { $variable_product = wc_get_product( $product->get_parent_id() );
if ( 'no' != wcs_get_product_limitation( $variable_product ) && ! empty( WC()->cart->cart_contents ) && ! wcs_is_order_received_page() && ! wcs_is_paypal_api_page() ) {
foreach ( WC()->cart->cart_contents as $cart_item ) { foreach ( WC()->cart->cart_contents as $cart_item ) {
if ( $product->id == $cart_item['data']->id && $product->variation_id != $cart_item['data']->variation_id ) { // If the variable product is limited, it can't be purchased if its the same variation
if ( $product->get_parent_id() == wcs_get_objects_property( $cart_item['data'], 'parent_id' ) && $product->get_id() != $cart_item['data']->get_id() ) {
$purchasable = false; $purchasable = false;
break; break;
} }
@@ -98,21 +100,21 @@ class WCS_Limiter {
public static function is_purchasable_product( $is_purchasable, $product ) { public static function is_purchasable_product( $is_purchasable, $product ) {
//Set up cache //Set up cache
if ( ! isset( self::$is_purchasable_cache[ $product->id ] ) ) { if ( ! isset( self::$is_purchasable_cache[ $product->get_id() ] ) ) {
self::$is_purchasable_cache[ $product->id ] = array(); self::$is_purchasable_cache[ $product->get_id() ] = array();
} }
if ( ! isset( self::$is_purchasable_cache[ $product->id ]['standard'] ) ) { if ( ! isset( self::$is_purchasable_cache[ $product->get_id() ]['standard'] ) ) {
self::$is_purchasable_cache[ $product->id ]['standard'] = $is_purchasable; self::$is_purchasable_cache[ $product->get_id() ]['standard'] = $is_purchasable;
if ( WC_Subscriptions_Product::is_subscription( $product->id ) && 'no' != wcs_get_product_limitation( $product ) && ! wcs_is_order_received_page() && ! wcs_is_paypal_api_page() ) { if ( WC_Subscriptions_Product::is_subscription( $product->get_id() ) && 'no' != wcs_get_product_limitation( $product ) && ! wcs_is_order_received_page() && ! wcs_is_paypal_api_page() ) {
if ( wcs_is_product_limited_for_user( $product ) && ! self::order_awaiting_payment_for_product( $product->id ) ) { if ( wcs_is_product_limited_for_user( $product ) && ! self::order_awaiting_payment_for_product( $product->get_id() ) ) {
self::$is_purchasable_cache[ $product->id ]['standard'] = false; self::$is_purchasable_cache[ $product->get_id() ]['standard'] = false;
} }
} }
} }
return self::$is_purchasable_cache[ $product->id ]['standard']; return self::$is_purchasable_cache[ $product->get_id() ]['standard'];
} }
@@ -132,7 +134,7 @@ class WCS_Limiter {
if ( ! isset( self::$is_purchasable_cache[ $product_key ]['switch'] ) ) { if ( ! isset( self::$is_purchasable_cache[ $product_key ]['switch'] ) ) {
if ( false === $is_purchasable && wcs_is_product_switchable_type( $product ) && WC_Subscriptions_Product::is_subscription( $product->id ) && 'no' != wcs_get_product_limitation( $product ) && is_user_logged_in() && wcs_user_has_subscription( 0, $product->id, wcs_get_product_limitation( $product ) ) ) { if ( false === $is_purchasable && wcs_is_product_switchable_type( $product ) && WC_Subscriptions_Product::is_subscription( $product->get_id() ) && 'no' != wcs_get_product_limitation( $product ) && is_user_logged_in() && wcs_user_has_subscription( 0, $product->get_id(), wcs_get_product_limitation( $product ) ) ) {
//Adding to cart //Adding to cart
if ( isset( $_GET['switch-subscription'] ) ) { if ( isset( $_GET['switch-subscription'] ) ) {
@@ -146,7 +148,7 @@ class WCS_Limiter {
} elseif ( isset( WC()->session->cart ) ) { } elseif ( isset( WC()->session->cart ) ) {
foreach ( WC()->session->cart as $cart_item_key => $cart_item ) { foreach ( WC()->session->cart as $cart_item_key => $cart_item ) {
if ( $product->id == $cart_item['product_id'] && isset( $cart_item['subscription_switch'] ) ) { if ( $product->get_id() == $cart_item['product_id'] && isset( $cart_item['subscription_switch'] ) ) {
$is_purchasable = true; $is_purchasable = true;
break; break;
} }
@@ -172,7 +174,7 @@ class WCS_Limiter {
$subscription_id = ( isset( $_GET['resubscribe'] ) ) ? absint( $_GET['resubscribe'] ) : $resubscribe_cart_item['subscription_resubscribe']['subscription_id']; $subscription_id = ( isset( $_GET['resubscribe'] ) ) ? absint( $_GET['resubscribe'] ) : $resubscribe_cart_item['subscription_resubscribe']['subscription_id'];
$subscription = wcs_get_subscription( $subscription_id ); $subscription = wcs_get_subscription( $subscription_id );
if ( false != $subscription && $subscription->has_product( $product->id ) && wcs_can_user_resubscribe_to( $subscription ) ) { if ( false != $subscription && $subscription->has_product( $product->get_id() ) && wcs_can_user_resubscribe_to( $subscription ) ) {
$is_purchasable = true; $is_purchasable = true;
} }
@@ -183,7 +185,7 @@ class WCS_Limiter {
// Restoring cart from session, so need to check the cart in the session (wcs_cart_contains_renewal() only checks the cart) // Restoring cart from session, so need to check the cart in the session (wcs_cart_contains_renewal() only checks the cart)
} elseif ( WC()->session->cart ) { } elseif ( WC()->session->cart ) {
foreach ( WC()->session->cart as $cart_item_key => $cart_item ) { foreach ( WC()->session->cart as $cart_item_key => $cart_item ) {
if ( $product->id == $cart_item['product_id'] && ( isset( $cart_item['subscription_renewal'] ) || isset( $cart_item['subscription_resubscribe'] ) ) ) { if ( $product->get_id() == $cart_item['product_id'] && ( isset( $cart_item['subscription_renewal'] ) || isset( $cart_item['subscription_resubscribe'] ) ) ) {
$is_purchasable = true; $is_purchasable = true;
break; break;
} }
@@ -216,7 +218,7 @@ class WCS_Limiter {
if ( $item['product_id'] == $product_id || $item['variation_id'] == $product_id ) { if ( $item['product_id'] == $product_id || $item['variation_id'] == $product_id ) {
$subscriptions = wcs_get_subscriptions( array( $subscriptions = wcs_get_subscriptions( array(
'order_id' => $order->id, 'order_id' => wcs_get_objects_property( $order, 'id' ),
'product_id' => $product_id, 'product_id' => $product_id,
) ); ) );

View File

@@ -76,7 +76,7 @@ class WCS_Remove_Item {
// handle undo request // handle undo request
$removed_item = WC()->session->get( 'removed_subscription_items', array() ); $removed_item = WC()->session->get( 'removed_subscription_items', array() );
if ( ! empty( $removed_item[ $item_id ] ) && $subscription->id == $removed_item[ $item_id ] ) { if ( ! empty( $removed_item[ $item_id ] ) && $subscription->get_id() == $removed_item[ $item_id ] ) {
// restore the item // restore the item
wc_update_order_item( $item_id, array( 'order_item_type' => 'line_item' ) ); wc_update_order_item( $item_id, array( 'order_item_type' => 'line_item' ) );
@@ -85,6 +85,7 @@ class WCS_Remove_Item {
WC()->session->set( 'removed_subscription_items', $removed_item ); WC()->session->set( 'removed_subscription_items', $removed_item );
// restore download permissions for this item // restore download permissions for this item
$subscription = wcs_get_subscription( $subscription->get_id() );
$line_items = $subscription->get_items(); $line_items = $subscription->get_items();
$line_item = $line_items[ $item_id ]; $line_item = $line_items[ $item_id ];
$_product = $subscription->get_product_from_item( $line_item ); $_product = $subscription->get_product_from_item( $line_item );
@@ -92,7 +93,7 @@ class WCS_Remove_Item {
if ( $_product && $_product->exists() && $_product->is_downloadable() ) { if ( $_product && $_product->exists() && $_product->is_downloadable() ) {
$downloads = $_product->get_files(); $downloads = wcs_get_objects_property( $_product, 'downloads' );
foreach ( array_keys( $downloads ) as $download_id ) { foreach ( array_keys( $downloads ) as $download_id ) {
wc_downloadable_file_permission( $download_id, $product_id, $subscription, $line_item['qty'] ); wc_downloadable_file_permission( $download_id, $product_id, $subscription, $line_item['qty'] );
@@ -108,14 +109,14 @@ class WCS_Remove_Item {
} else { } else {
// handle remove item requests // handle remove item requests
WC()->session->set( 'removed_subscription_items', array( $item_id => $subscription->id ) ); WC()->session->set( 'removed_subscription_items', array( $item_id => $subscription->get_id() ) );
// remove download access for the item // remove download access for the item
$line_items = $subscription->get_items(); $line_items = $subscription->get_items();
$line_item = $line_items[ $item_id ]; $line_item = $line_items[ $item_id ];
$product_id = wcs_get_canonical_product_id( $line_item ); $product_id = wcs_get_canonical_product_id( $line_item );
WCS_Download_Handler::revoke_downloadable_file_permission( $product_id, $subscription->id, $subscription->get_user_id() ); WCS_Download_Handler::revoke_downloadable_file_permission( $product_id, $subscription->get_id(), $subscription->get_user_id() );
// remove the line item from subscription but preserve its data in the DB // remove the line item from subscription but preserve its data in the DB
wc_update_order_item( $item_id, array( 'order_item_type' => 'line_item_removed' ) ); wc_update_order_item( $item_id, array( 'order_item_type' => 'line_item_removed' ) );
@@ -124,10 +125,15 @@ class WCS_Remove_Item {
$subscription->add_order_note( sprintf( _x( 'Customer removed "%1$s" (Product ID: #%2$d) via the My Account page.', 'used in order note', 'woocommerce-subscriptions' ), wcs_get_line_item_name( $line_item ), $product_id ) ); $subscription->add_order_note( sprintf( _x( 'Customer removed "%1$s" (Product ID: #%2$d) via the My Account page.', 'used in order note', 'woocommerce-subscriptions' ), wcs_get_line_item_name( $line_item ), $product_id ) );
// translators: placeholders are 1$: item name, and, 2$: opening and, 3$: closing link tags // translators: placeholders are 1$: item name, and, 2$: opening and, 3$: closing link tags
wc_add_notice( sprintf( __( 'You have successfully removed "%1$s" from your subscription. %2$sUndo?%3$s', 'woocommerce-subscriptions' ), $line_item['name'], '<a href="' . esc_url( self::get_undo_remove_url( $subscription->id, $item_id, $subscription->get_view_order_url() ) ) . '" >', '</a>' ) ); wc_add_notice( sprintf( __( 'You have successfully removed "%1$s" from your subscription. %2$sUndo?%3$s', 'woocommerce-subscriptions' ), $line_item['name'], '<a href="' . esc_url( self::get_undo_remove_url( $subscription->get_id(), $item_id, $subscription->get_view_order_url() ) ) . '" >', '</a>' ) );
} }
} }
/**
* In WooCommerce 3.0 the subscription object and its items override the database with their current content,
* so we lost the changes we just did with `wc_update_order_item`. Re-reading the object fixes this problem.
*/
$subscription = wcs_get_subscription( $subscription->get_id() );
$subscription->calculate_totals(); $subscription->calculate_totals();
wp_safe_redirect( $subscription->get_view_order_url() ); wp_safe_redirect( $subscription->get_view_order_url() );
exit; exit;
@@ -155,7 +161,7 @@ class WCS_Remove_Item {
wc_add_notice( __( 'Security error. Please contact us if you need assistance.', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'Security error. Please contact us if you need assistance.', 'woocommerce-subscriptions' ), 'error' );
} elseif ( ! current_user_can( 'edit_shop_subscription_line_items', $subscription->id ) ) { } elseif ( ! current_user_can( 'edit_shop_subscription_line_items', $subscription->get_id() ) ) {
wc_add_notice( __( 'You cannot modify a subscription that does not belong to you.', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'You cannot modify a subscription that does not belong to you.', 'woocommerce-subscriptions' ), 'error' );

View File

@@ -134,7 +134,7 @@ class WCS_Retry_Manager {
if ( $subscription->get_date( 'payment_retry' ) > 0 ) { if ( $subscription->get_date( 'payment_retry' ) > 0 ) {
$last_order = $subscription->get_last_order( 'all' ); $last_order = $subscription->get_last_order( 'all' );
$last_retry = ( $last_order ) ? self::store()->get_last_retry_for_order( $last_order->id ) : null; $last_retry = ( $last_order ) ? self::store()->get_last_retry_for_order( wcs_get_objects_property( $last_order, 'id' ) ) : null;
if ( null !== $last_retry && 'cancelled' !== $last_retry->get_status() && null !== ( $last_retry_rule = $last_retry->get_rule() ) ) { if ( null !== $last_retry && 'cancelled' !== $last_retry->get_status() && null !== ( $last_retry_rule = $last_retry->get_rule() ) ) {
@@ -206,17 +206,17 @@ class WCS_Retry_Manager {
return; return;
} }
$retry_count = self::store()->get_retry_count_for_order( $last_order->id ); $retry_count = self::store()->get_retry_count_for_order( wcs_get_objects_property( $last_order, 'id' ) );
if ( self::rules()->has_rule( $retry_count, $last_order->id ) ) { if ( self::rules()->has_rule( $retry_count, wcs_get_objects_property( $last_order, 'id' ) ) ) {
$retry_rule = self::rules()->get_rule( $retry_count, $last_order->id ); $retry_rule = self::rules()->get_rule( $retry_count, wcs_get_objects_property( $last_order, 'id' ) );
do_action( 'woocommerce_subscriptions_before_apply_retry_rule', $retry_rule, $last_order, $subscription ); do_action( 'woocommerce_subscriptions_before_apply_retry_rule', $retry_rule, $last_order, $subscription );
$retry_id = self::store()->save( new WCS_Retry( array( $retry_id = self::store()->save( new WCS_Retry( array(
'status' => 'pending', 'status' => 'pending',
'order_id' => $last_order->id, 'order_id' => wcs_get_objects_property( $last_order, 'id' ),
'date_gmt' => gmdate( 'Y-m-d H:i:s', gmdate( 'U' ) + $retry_rule->get_retry_interval() ), 'date_gmt' => gmdate( 'Y-m-d H:i:s', gmdate( 'U' ) + $retry_rule->get_retry_interval() ),
'rule_raw' => $retry_rule->get_raw_data(), 'rule_raw' => $retry_rule->get_raw_data(),
) ) ); ) ) );
@@ -257,7 +257,7 @@ class WCS_Retry_Manager {
} }
$subscriptions = wcs_get_subscriptions_for_renewal_order( $last_order ); $subscriptions = wcs_get_subscriptions_for_renewal_order( $last_order );
$last_retry = self::store()->get_last_retry_for_order( $last_order->id ); $last_retry = self::store()->get_last_retry_for_order( wcs_get_objects_property( $last_order, 'id' ) );
// we only need to retry the payment if we have applied a retry rule for the order and it still needs payment // we only need to retry the payment if we have applied a retry rule for the order and it still needs payment
if ( null !== $last_retry && 'pending' === $last_retry->get_status() ) { if ( null !== $last_retry && 'pending' === $last_retry->get_status() ) {
@@ -302,7 +302,7 @@ class WCS_Retry_Manager {
WC_Subscriptions_Payment_Gateways::trigger_gateway_renewal_payment_hook( $last_order ); WC_Subscriptions_Payment_Gateways::trigger_gateway_renewal_payment_hook( $last_order );
// Now that we've attempted to process the payment, refresh the order // Now that we've attempted to process the payment, refresh the order
$last_order = wc_get_order( $last_order->id ); $last_order = wc_get_order( wcs_get_objects_property( $last_order, 'id' ) );
// if the order still needs payment, payment failed // if the order still needs payment, payment failed
if ( $last_order->needs_payment() ) { if ( $last_order->needs_payment() ) {

View File

@@ -0,0 +1,123 @@
<?php
/**
* Simple class to generate the HTML for a Select2 element in a WC version compatible way.
*
* @since 2.2
* @category Class
* @author Prospress
* @package WooCommerce Subscriptions
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WCS_Select2 {
protected $default_attributes = array(
'type' => 'hidden',
'placeholder' => '',
'class' => '',
);
protected $attributes = array();
/**
* Constructor.
*
* @param array $attributes The attributes that make up the Select2 element
* @since 2.2
*/
public function __construct( array $attributes ) {
$this->attributes = array_merge( $this->default_attributes, $attributes );
}
/**
* Render a select2 element given an array of attributes.
*
* @param array $attributes Select2 attributes
* @since 2.2
*/
public static function render( array $attributes ) {
$select2 = new self( $attributes );
$select2->print_html();
}
/**
* Get a property name.
*
* @param string $property
* @return string class, name, id or data-$property;
* @since 2.2
*/
protected function get_property_name( $property ) {
$data_properties = WC_Subscriptions::is_woocommerce_pre( '3.0' ) ? array( 'placeholder', 'selected', 'allow_clear' ) : array( 'placeholder', 'allow_clear' );
return in_array( $property, $data_properties ) ? 'data-' . $property : $property;
}
/**
* Returns a list of properties/values (HTML) from an array. All the values
* are escaped.
*
* @param $attributes List of HTML attributes with values
* @return string
* @since 2.2
*/
protected function attributes_to_html( array $attributes ) {
$html = array();
foreach ( $attributes as $property => $value ) {
if ( ! is_scalar( $value ) ) {
$value = wcs_json_encode( $value );
}
$html[] = $this->get_property_name( $property ) . '="' . esc_attr( $value, 'woocommerce-subscriptions' ) . '"';
}
return implode( ' ', $html );
}
/**
* Prints the HTML to show the Select2 field.
*
* @since 2.2
*/
public function print_html() {
$allowed_attributes = array_map( array( $this, 'get_property_name' ), array_keys( $this->attributes ) );
$allowed_attributes = array_fill_keys( $allowed_attributes, array() );
echo wp_kses( $this->get_html(), array( 'input' => $allowed_attributes, 'select' => $allowed_attributes, 'option' => $allowed_attributes ) );
}
/**
* Returns the HTML needed to show the Select2 field
*
* @return string
* @since 2.2
*/
public function get_html() {
$html = "\n<!--select2 -->\n";
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$html .= '<input ';
$html .= $this->attributes_to_html( $this->attributes );
$html .= '/>';
} else {
$attributes = $this->attributes;
$selected_value = isset( $attributes['selected'] ) ? $attributes['selected'] : '';
$attributes['selected'] = 'selected';
$option_attributes = array_intersect_key( $attributes, array_flip( array( 'value', 'selected' ) ) );
$select_attributes = array_diff_key( $attributes, $option_attributes );
$html .= '<select ' . $this->attributes_to_html( $select_attributes ) . '>';
$html .= '<option ' . $this->attributes_to_html( $option_attributes ) . '>' . $selected_value . '</option>';
$html .= '</select>';
}
$html .= "\n<!--/select2 -->\n";
return $html;
}
}

View File

@@ -95,11 +95,11 @@ class WCS_User_Change_Status_Handler {
WC_Subscriptions::add_notice( __( 'That subscription does not exist. Please contact us if you need assistance.', 'woocommerce-subscriptions' ), 'error' ); WC_Subscriptions::add_notice( __( 'That subscription does not exist. Please contact us if you need assistance.', 'woocommerce-subscriptions' ), 'error' );
return false; return false;
} elseif ( ! empty( $wpnonce ) && wp_verify_nonce( $wpnonce, $subscription->id . $subscription->get_status() ) === false ) { } elseif ( ! empty( $wpnonce ) && wp_verify_nonce( $wpnonce, $subscription->get_id() . $subscription->get_status() ) === false ) {
WC_Subscriptions::add_notice( __( 'Security error. Please contact us if you need assistance.', 'woocommerce-subscriptions' ), 'error' ); WC_Subscriptions::add_notice( __( 'Security error. Please contact us if you need assistance.', 'woocommerce-subscriptions' ), 'error' );
return false; return false;
} elseif ( ! user_can( $user_id, 'edit_shop_subscription_status', $subscription->id ) ) { } elseif ( ! user_can( $user_id, 'edit_shop_subscription_status', $subscription->get_id() ) ) {
WC_Subscriptions::add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' ); WC_Subscriptions::add_notice( __( 'That doesn\'t appear to be one of your subscriptions.', 'woocommerce-subscriptions' ), 'error' );
return false; return false;

View File

@@ -104,16 +104,31 @@ class WCS_Webhooks {
public static function create_payload( $payload, $resource, $resource_id, $id ) { public static function create_payload( $payload, $resource, $resource_id, $id ) {
if ( 'subscription' == $resource && empty( $payload ) && wcs_is_subscription( $resource_id ) ) { if ( 'subscription' == $resource && empty( $payload ) && wcs_is_subscription( $resource_id ) ) {
$webhook = new WC_Webhook( $id ); $webhook = new WC_Webhook( $id );
$event = $webhook->get_event(); $event = $webhook->get_event();
$current_user = get_current_user_id(); $current_user = get_current_user_id();
wp_set_current_user( $webhook->get_user_id() ); wp_set_current_user( $webhook->get_user_id() );
WC()->api->WC_API_Subscriptions->register_routes( array() ); $webhook_api_version = ( method_exists( $webhook, 'get_api_version' ) ) ? $webhook->get_api_version() : 'legacy_v3';
switch ( $webhook_api_version ) {
case 'legacy_v3':
WC()->api->WC_API_Subscriptions->register_routes( array() );
$payload = WC()->api->WC_API_Subscriptions->get_subscription( $resource_id ); $payload = WC()->api->WC_API_Subscriptions->get_subscription( $resource_id );
break;
case 'wp_api_v1':
case 'wp_api_v2':
require_once( 'api/class-wc-rest-subscriptions-controller.php' );
$request = new WP_REST_Request( 'GET' );
$controller = new WC_REST_Subscriptions_Controller;
$request->set_param( 'id', $resource_id );
$result = $controller->get_item( $request );
$payload = isset( $result->data ) ? $result->data : array();
break;
}
wp_set_current_user( $current_user ); wp_set_current_user( $current_user );
} }
@@ -154,7 +169,7 @@ class WCS_Webhooks {
* @since 2.0 * @since 2.0
*/ */
public static function add_subscription_created_callback( $subscription ) { public static function add_subscription_created_callback( $subscription ) {
do_action( 'wcs_webhook_subscription_created', $subscription->id ); do_action( 'wcs_webhook_subscription_created', $subscription->get_id() );
} }
/** /**
@@ -163,7 +178,7 @@ class WCS_Webhooks {
* @since 2.0 * @since 2.0
*/ */
public static function add_subscription_updated_callback( $subscription ) { public static function add_subscription_updated_callback( $subscription ) {
do_action( 'wcs_webhook_subscription_updated', $subscription->id ); do_action( 'wcs_webhook_subscription_updated', $subscription->get_id() );
} }
/** /**

View File

@@ -0,0 +1,262 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Subscription Data Store: Stored in CPT.
*
* Extends WC_Order_Data_Store_CPT to make sure subscription related meta data is read/updated.
*
* @version 2.2.0
* @category Class
* @author Prospress
*/
class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements WC_Object_Data_Store_Interface, WC_Order_Data_Store_Interface {
/**
* Define subscription specific data which augments the meta of an order.
*
* The meta keys here determine the prop data that needs to be manually set. We can't use
* the $internal_meta_keys property from WC_Order_Data_Store_CPT because we want its value
* too, so instead we create our own and merge it into $internal_meta_keys in __construct.
*
* @since 2.2.0
* @var array
*/
protected $subscription_internal_meta_keys = array(
'_schedule_trial_end',
'_schedule_next_payment',
'_schedule_cancelled',
'_schedule_end',
'_schedule_payment_retry',
'_subscription_switch_data',
);
/**
* Array of subscription specific data which augments the meta of an order in the form meta_key => prop_key
*
* Used to read/update props on the subscription.
*
* @since 2.2.0
* @var array
*/
protected $subscription_meta_keys_to_props = array(
'_billing_period' => 'billing_period',
'_billing_interval' => 'billing_interval',
'_suspension_count' => 'suspension_count',
'_cancelled_email_sent' => 'cancelled_email_sent',
'_requires_manual_renewal' => 'requires_manual_renewal',
'_trial_period' => 'trial_period',
'_schedule_trial_end' => 'schedule_trial_end',
'_schedule_next_payment' => 'schedule_next_payment',
'_schedule_cancelled' => 'schedule_cancelled',
'_schedule_end' => 'schedule_end',
'_schedule_payment_retry' => 'schedule_payment_retry',
'_subscription_switch_data' => 'switch_data',
);
/**
* Constructor.
*/
public function __construct() {
// Exclude the subscription related meta data we set and manage manually from the objects "meta" data
$this->internal_meta_keys = array_merge( $this->internal_meta_keys, $this->subscription_internal_meta_keys );
}
/**
* Create a new subscription in the database.
*
* @param WC_Subscription $subscription
* @since 2.2.0
*/
public function create( &$subscription ) {
parent::create( $subscription );
do_action( 'woocommerce_new_subscription', $subscription->get_id() );
}
/**
* Read subscription data.
*
* @param WC_Subscription $subscription
* @param object $post_object
* @since 2.2.0
*/
protected function read_order_data( &$subscription, $post_object ) {
// Set all order meta data, as well as data defined by WC_Subscription::$extra_keys which has corresponding setter methods
parent::read_order_data( $subscription, $post_object );
$props_to_set = $dates_to_set = array();
foreach ( $this->subscription_meta_keys_to_props as $meta_key => $prop_key ) {
if ( 0 === strpos( $prop_key, 'schedule' ) || in_array( $meta_key, $this->subscription_internal_meta_keys ) ) {
$meta_value = get_post_meta( $subscription->get_id(), $meta_key, true );
// Dates are set via update_dates() to make sure relationships between dates are validated
if ( 0 === strpos( $prop_key, 'schedule' ) ) {
$date_type = str_replace( 'schedule_', '', $prop_key );
$dates_to_set[ $date_type ] = ( false == $meta_value ) ? 0 : $meta_value;
} else {
$props_to_set[ $prop_key ] = $meta_value;
}
}
}
$subscription->update_dates( $dates_to_set );
$subscription->set_props( $props_to_set );
}
/**
* Update subscription in the database.
*
* @param WC_Subscription $subscription
* @since 2.2.0
*/
public function update( &$subscription ) {
parent::update( $subscription );
do_action( 'woocommerce_update_subscription', $subscription->get_id() );
}
/**
* Update post meta for a subscription based on it's settings in the WC_Subscription class.
*
* @param WC_Subscription $subscription
* @since 2.2.0
*/
protected function update_post_meta( &$subscription ) {
$updated_props = array();
foreach ( $this->get_props_to_update( $subscription, $this->subscription_meta_keys_to_props ) as $meta_key => $prop ) {
$meta_value = ( 'schedule_' == substr( $prop, 0, 9 ) ) ? $subscription->get_date( $prop ) : $subscription->{"get_$prop"}( 'edit' );
// Store as a string of the boolean for backward compatibility (yep, it's gross)
if ( 'requires_manual_renewal' === $prop ) {
$meta_value = $meta_value ? 'true' : 'false';
}
update_post_meta( $subscription->get_id(), $meta_key, $meta_value );
$updated_props[] = $prop;
}
do_action( 'woocommerce_subscription_object_updated_props', $subscription, $updated_props );
parent::update_post_meta( $subscription );
}
/**
* Get amount refunded for all related orders.
*
* @param WC_Subscription $subscription
* @return string
* @since 2.2.0
*/
public function get_total_refunded( $subscription ) {
$total = 0;
foreach ( $subscription->get_related_orders( 'all' ) as $order ) {
$total += parent::get_total_refunded( $order );
}
return $total;
}
/**
* Get the total tax refunded for all related orders.
*
* @param WC_Subscription $subscription
* @return float
* @since 2.2.0
*/
public function get_total_tax_refunded( $subscription ) {
$total = 0;
foreach ( $subscription->get_related_orders() as $order ) {
$total += parent::get_total_tax_refunded( $order );
}
return abs( $total );
}
/**
* Get the total shipping refunded for all related orders.
*
* @param WC_Subscription $subscription
* @return float
* @since 2.2.0
*/
public function get_total_shipping_refunded( $subscription ) {
$total = 0;
foreach ( $subscription->get_related_orders( 'all' ) as $order ) {
$total += parent::get_total_shipping_refunded( $order );
}
return abs( $total );
}
/**
* Return count of subscriptions with type.
*
* @param string $type
* @return int
* @since 2.2.0
*/
public function get_order_count( $status ) {
global $wpdb;
return absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM {$wpdb->posts} WHERE post_type = 'shop_subscription' AND post_status = %s", $status ) ) );
}
/**
* Get all subscriptions matching the passed in args.
*
* @see wc_get_orders()
* @param array $args
* @return array of orders
* @since 2.2.0
*/
public function get_orders( $args = array() ) {
$parent_args = $args = wp_parse_args( $args, array(
'type' => 'shop_subscription',
'return' => 'objects',
) );
// We only want IDs from the parent method
$parent_args['return'] = 'ids';
$subscriptions = parent::get_orders( $parent_args );
if ( $args['paginate'] ) {
if ( 'objects' === $args['return'] ) {
$return = array_map( 'wcs_get_subscription', $subscriptions->orders );
} else {
$return = $subscriptions->orders;
}
return (object) array(
'orders' => $return,
'total' => $subscriptions->total,
'max_num_pages' => $subscriptions->max_num_pages,
);
} else {
if ( 'objects' === $args['return'] ) {
$return = array_map( 'wcs_get_subscription', $subscriptions );
} else {
$return = $subscriptions;
}
return $return;
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Handle WC3.0 deprecated filters.
*
* When triggering a new WC 3.0+ filter which has a deprecated equivalent from WooCommerce 2.6.x, check if the old
* filter had any callbacks attached to it, and if so, log a notice and trigger the old action.
* This class extends @see WC_Deprecated_Filter_Hooks which handles most of the attaching and triggering of old hooks.
*
* @package WooCommerce Subscriptions
* @category Class
* @author Prospress
* @since 2.2.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles deprecation notices and triggering of legacy filter hooks when WC 3.0+ subscription filters are triggered.
*
* @since 2.2.0
*/
class WCS_Deprecated_Filter_Hooks extends WC_Deprecated_Filter_Hooks {
/**
* Array of deprecated hooks we need to handle in the format array( new_hook => old_hook )
*
* @var array
*/
protected $deprecated_hooks = array(
'woocommerce_subscription_get_currency' => 'woocommerce_get_currency',
'woocommerce_subscription_get_discount_total' => 'woocommerce_order_amount_discount_total',
'woocommerce_subscription_get_discount_tax' => 'woocommerce_order_amount_discount_tax',
'woocommerce_subscription_get_shipping_total' => 'woocommerce_order_amount_shipping_total',
'woocommerce_subscription_get_shipping_tax' => 'woocommerce_order_amount_shipping_tax',
'woocommerce_subscription_get_cart_tax' => 'woocommerce_order_amount_cart_tax',
'woocommerce_subscription_get_total' => 'woocommerce_order_amount_total',
'woocommerce_subscription_get_total_tax' => 'woocommerce_order_amount_total_tax',
'woocommerce_subscription_get_total_discount' => 'woocommerce_order_amount_total_discount',
'woocommerce_subscription_get_subtotal' => 'woocommerce_order_amount_subtotal',
'woocommerce_subscription_get_tax_totals' => 'woocommerce_order_tax_totals',
);
/**
* Display a deprecated notice for old hooks.
*
* @param string $old_hook
* @param string $new_hook
* @since 2.2.0
*/
protected function display_notice( $old_hook, $new_hook ) {
wcs_deprecated_function( sprintf( 'The "%s" hook uses out of date data structures and', esc_html( $old_hook ) ), '2.2.0', esc_html( $new_hook ) . ' to filter subscription properties' );
}
}
new WCS_Deprecated_Filter_Hooks();

View File

@@ -132,7 +132,7 @@ class WCS_Filter_Deprecator extends WCS_Hook_Deprecator {
// Old arg spec: $next_payment_date, $order, $product_id, $type, $from_date, $from_date_arg // Old arg spec: $next_payment_date, $order, $product_id, $type, $from_date, $from_date_arg
case 'woocommerce_subscriptions_calculated_next_payment_date' : case 'woocommerce_subscriptions_calculated_next_payment_date' :
$subscription = $new_callback_args[1]; $subscription = $new_callback_args[1];
$last_payment = $subscription->get_date( 'last_payment' ); $last_payment = $subscription->get_date( 'last_order_date_created' );
$return_value = apply_filters( $old_hook, $return_value, self::get_order( $subscription ), self::get_product_id( $subscription ), 'mysql', $last_payment, $last_payment ); $return_value = apply_filters( $old_hook, $return_value, self::get_order( $subscription ), self::get_product_id( $subscription ), 'mysql', $last_payment, $last_payment );
break; break;
@@ -168,10 +168,10 @@ class WCS_Filter_Deprecator extends WCS_Hook_Deprecator {
// Old arg spec: $order_items, $original_order_id, $renewal_order_id, $product_id, $new_order_role // Old arg spec: $order_items, $original_order_id, $renewal_order_id, $product_id, $new_order_role
if ( 'woocommerce_subscriptions_renewal_order_items' == $old_hook ) { if ( 'woocommerce_subscriptions_renewal_order_items' == $old_hook ) {
$return_value = apply_filters( $old_hook, $return_value, $original_id, $renewal_order->id, self::get_product_id( $subscription ), $order_role ); $return_value = apply_filters( $old_hook, $return_value, $original_id, wcs_get_objects_property( $renewal_order, 'id' ), self::get_product_id( $subscription ), $order_role );
} else { } else {
// Old arg spec: $order_meta_query, $original_order_id, $renewal_order_id, $new_order_role // Old arg spec: $order_meta_query, $original_order_id, $renewal_order_id, $new_order_role
$return_value = apply_filters( $old_hook, $return_value, $original_id, $renewal_order->id, $order_role ); $return_value = apply_filters( $old_hook, $return_value, $original_id, wcs_get_objects_property( $renewal_order, 'id' ), $order_role );
} }
break; break;
@@ -202,7 +202,7 @@ class WCS_Filter_Deprecator extends WCS_Hook_Deprecator {
$renewal_order_id = apply_filters( $old_hook, $return_value->id, self::get_order( $subscription ), self::get_product_id( $subscription ), $order_role ); $renewal_order_id = apply_filters( $old_hook, $return_value->id, self::get_order( $subscription ), self::get_product_id( $subscription ), $order_role );
// Only change the return value if a new filter was returned by the hook // Only change the return value if a new filter was returned by the hook
if ( $renewal_order_id !== $renewal_order->id ) { if ( wcs_get_objects_property( $renewal_order, 'id' ) !== $renewal_order_id ) {
$return_value = wc_get_order( $renewal_order_id ); $return_value = wc_get_order( $renewal_order_id );
} }
break; break;
@@ -326,7 +326,7 @@ class WCS_Filter_Deprecator extends WCS_Hook_Deprecator {
$args->subscription = wcs_get_subscription_in_deprecated_structure( $subscription ); $args->subscription = wcs_get_subscription_in_deprecated_structure( $subscription );
$args->user_id = $subscription->get_user_id(); $args->user_id = $subscription->get_user_id();
$args->order = self::get_order( $subscription ); $args->order = self::get_order( $subscription );
$args->payment_gateway = $subscription->payment_method; $args->payment_gateway = $subscription->get_payment_method();
$args->order_uses_manual_payments = $subscription->is_manual(); $args->order_uses_manual_payments = $subscription->is_manual();
$return_value = apply_filters( $old_hook, $return_value, $args ); $return_value = apply_filters( $old_hook, $return_value, $args );
break; break;

View File

@@ -64,7 +64,7 @@ class WCS_Email_Cancelled_Subscription extends WC_Email {
return; return;
} }
update_post_meta( $subscription->id, '_cancelled_email_sent', 'true' ); update_post_meta( $subscription->get_id(), '_cancelled_email_sent', 'true' );
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() ); $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
} }

View File

@@ -55,24 +55,24 @@ class WCS_Email_Completed_Renewal_Order extends WC_Email_Customer_Completed_Orde
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order_id ) { function trigger( $order_id, $order = null ) {
if ( $order_id ) { if ( $order_id ) {
$this->object = new WC_Order( $order_id ); $this->object = new WC_Order( $order_id );
$this->recipient = $this->object->billing_email; $this->recipient = wcs_get_objects_property( $this->object, 'billing_email' );
$order_date_index = array_search( '{order_date}', $this->find ); $order_date_index = array_search( '{order_date}', $this->find );
if ( false === $order_date_index ) { if ( false === $order_date_index ) {
$this->find[] = '{order_date}'; $this->find['order_date'] = '{order_date}';
$this->replace[] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace['order_date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} else { } else {
$this->replace[ $order_date_index ] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace[ $order_date_index ] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} }
$order_number_index = array_search( '{order_number}', $this->find ); $order_number_index = array_search( '{order_number}', $this->find );
if ( false === $order_number_index ) { if ( false === $order_number_index ) {
$this->find[] = '{order_number}'; $this->find['order_number'] = '{order_number}';
$this->replace[] = $this->object->get_order_number(); $this->replace['order_number'] = $this->object->get_order_number();
} else { } else {
$this->replace[ $order_number_index ] = $this->object->get_order_number(); $this->replace[ $order_number_index ] = $this->object->get_order_number();
} }

View File

@@ -54,24 +54,24 @@ class WCS_Email_Completed_Switch_Order extends WC_Email_Customer_Completed_Order
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order_id ) { function trigger( $order_id, $order = null ) {
if ( $order_id ) { if ( $order_id ) {
$this->object = new WC_Order( $order_id ); $this->object = new WC_Order( $order_id );
$this->recipient = $this->object->billing_email; $this->recipient = wcs_get_objects_property( $this->object, 'billing_email' );
$order_date_index = array_search( '{order_date}', $this->find ); $order_date_index = array_search( '{order_date}', $this->find );
if ( false === $order_date_index ) { if ( false === $order_date_index ) {
$this->find[] = '{order_date}'; $this->find['order_date'] = '{order_date}';
$this->replace[] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace['order_date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} else { } else {
$this->replace[ $order_date_index ] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace[ $order_date_index ] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} }
$order_number_index = array_search( '{order_number}', $this->find ); $order_number_index = array_search( '{order_number}', $this->find );
if ( false === $order_number_index ) { if ( false === $order_number_index ) {
$this->find[] = '{order_number}'; $this->find['order_number'] = '{order_number}';
$this->replace[] = $this->object->get_order_number(); $this->replace['order_number'] = $this->object->get_order_number();
} else { } else {
$this->replace[ $order_number_index ] = $this->object->get_order_number(); $this->replace[ $order_number_index ] = $this->object->get_order_number();
} }

View File

@@ -49,19 +49,19 @@ class WCS_Email_Customer_Payment_Retry extends WCS_Email_Customer_Renewal_Invoic
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order ) { function trigger( $order_id, $order = null ) {
$this->retry = WCS_Retry_Manager::store()->get_last_retry_for_order( $order->id ); $this->retry = WCS_Retry_Manager::store()->get_last_retry_for_order( $order_id );
$retry_time_index = array_search( '{retry_time}', $this->find ); $retry_time_index = array_search( '{retry_time}', $this->find );
if ( false === $retry_time_index ) { if ( false === $retry_time_index ) {
$this->find[] = '{retry_time}'; $this->find['retry_time'] = '{retry_time}';
$this->replace[] = strtolower( wcs_get_human_time_diff( $this->retry->get_time() ) ); $this->replace['retry_time'] = strtolower( wcs_get_human_time_diff( $this->retry->get_time() ) );
} else { } else {
$this->replace[ $retry_time_index ] = strtolower( wcs_get_human_time_diff( $this->retry->get_time() ) ); $this->replace[ $retry_time_index ] = strtolower( wcs_get_human_time_diff( $this->retry->get_time() ) );
} }
parent::trigger( $order ); parent::trigger( $order_id, $order );
} }
/** /**

View File

@@ -49,24 +49,24 @@ class WCS_Email_Processing_Renewal_Order extends WC_Email_Customer_Processing_Or
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order_id ) { function trigger( $order_id, $order = null ) {
if ( $order_id ) { if ( $order_id ) {
$this->object = new WC_Order( $order_id ); $this->object = new WC_Order( $order_id );
$this->recipient = $this->object->billing_email; $this->recipient = wcs_get_objects_property( $this->object, 'billing_email' );
$order_date_index = array_search( '{order_date}', $this->find ); $order_date_index = array_search( '{order_date}', $this->find );
if ( false === $order_date_index ) { if ( false === $order_date_index ) {
$this->find[] = '{order_date}'; $this->find['order_date'] = '{order_date}';
$this->replace[] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace['order_date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} else { } else {
$this->replace[ $order_date_index ] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace[ $order_date_index ] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} }
$order_number_index = array_search( '{order_number}', $this->find ); $order_number_index = array_search( '{order_number}', $this->find );
if ( false === $order_number_index ) { if ( false === $order_number_index ) {
$this->find[] = '{order_number}'; $this->find['order_number'] = '{order_number}';
$this->replace[] = $this->object->get_order_number(); $this->replace['order_number'] = $this->object->get_order_number();
} else { } else {
$this->replace[ $order_number_index ] = $this->object->get_order_number(); $this->replace[ $order_number_index ] = $this->object->get_order_number();
} }

View File

@@ -56,28 +56,28 @@ class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order ) { function trigger( $order_id, $order = null ) {
if ( ! is_object( $order ) ) { if ( $order_id && ! is_a( $order, 'WC_Order' ) ) {
$order = new WC_Order( absint( $order ) ); $order = wc_get_order( $order_id );
} }
if ( $order ) { if ( is_a( $order, 'WC_Order' ) ) {
$this->object = $order; $this->object = $order;
$this->recipient = $this->object->billing_email; $this->recipient = wcs_get_objects_property( $this->object, 'billing_email' );
$order_date_index = array_search( '{order_date}', $this->find ); $order_date_index = array_search( '{order_date}', $this->find );
if ( false === $order_date_index ) { if ( false === $order_date_index ) {
$this->find[] = '{order_date}'; $this->find['order_date'] = '{order_date}';
$this->replace[] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace['order_date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} else { } else {
$this->replace[ $order_date_index ] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace[ $order_date_index ] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} }
$order_number_index = array_search( '{order_number}', $this->find ); $order_number_index = array_search( '{order_number}', $this->find );
if ( false === $order_number_index ) { if ( false === $order_number_index ) {
$this->find[] = '{order_number}'; $this->find['order_number'] = '{order_number}';
$this->replace[] = $this->object->get_order_number(); $this->replace['order_number'] = $this->object->get_order_number();
} else { } else {
$this->replace[ $order_number_index ] = $this->object->get_order_number(); $this->replace[ $order_number_index ] = $this->object->get_order_number();
} }

View File

@@ -57,23 +57,23 @@ class WCS_Email_New_Renewal_Order extends WC_Email_New_Order {
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order_id ) { function trigger( $order_id, $order = null ) {
if ( $order_id ) { if ( $order_id ) {
$this->object = new WC_Order( $order_id ); $this->object = new WC_Order( $order_id );
$order_date_index = array_search( '{order_date}', $this->find ); $order_date_index = array_search( '{order_date}', $this->find );
if ( false === $order_date_index ) { if ( false === $order_date_index ) {
$this->find[] = '{order_date}'; $this->find['order-date'] = '{order_date}';
$this->replace[] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace['order-date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} else { } else {
$this->replace[ $order_date_index ] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace[ $order_date_index ] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} }
$order_number_index = array_search( '{order_number}', $this->find ); $order_number_index = array_search( '{order_number}', $this->find );
if ( false === $order_number_index ) { if ( false === $order_number_index ) {
$this->find[] = '{order_number}'; $this->find['order-number'] = '{order_number}';
$this->replace[] = $this->object->get_order_number(); $this->replace['order-number'] = $this->object->get_order_number();
} else { } else {
$this->replace[ $order_number_index ] = $this->object->get_order_number(); $this->replace[ $order_number_index ] = $this->object->get_order_number();
} }

View File

@@ -52,23 +52,23 @@ class WCS_Email_New_Switch_Order extends WC_Email_New_Order {
* @access public * @access public
* @return void * @return void
*/ */
function trigger( $order_id ) { function trigger( $order_id, $order = null ) {
if ( $order_id ) { if ( $order_id ) {
$this->object = new WC_Order( $order_id ); $this->object = new WC_Order( $order_id );
$order_date_index = array_search( '{order_date}', $this->find ); $order_date_index = array_search( '{order_date}', $this->find );
if ( false === $order_date_index ) { if ( false === $order_date_index ) {
$this->find[] = '{order_date}'; $this->find['order_date'] = '{order_date}';
$this->replace[] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace['order_date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} else { } else {
$this->replace[ $order_date_index ] = date_i18n( wc_date_format(), wcs_date_to_time( $this->object->order_date ) ); $this->replace[ $order_date_index ] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
} }
$order_number_index = array_search( '{order_number}', $this->find ); $order_number_index = array_search( '{order_number}', $this->find );
if ( false === $order_number_index ) { if ( false === $order_number_index ) {
$this->find[] = '{order_number}'; $this->find['order_number'] = '{order_number}';
$this->replace[] = $this->object->get_order_number(); $this->replace['order_number'] = $this->object->get_order_number();
} else { } else {
$this->replace[ $order_number_index ] = $this->object->get_order_number(); $this->replace[ $order_number_index ] = $this->object->get_order_number();
} }

View File

@@ -45,13 +45,13 @@ class WCS_Email_Payment_Retry extends WC_Email_Failed_Order {
* *
* @param int $order_id * @param int $order_id
*/ */
public function trigger( $order ) { public function trigger( $order_id, $order = null ) {
$this->object = $order; $this->object = $order;
$this->retry = WCS_Retry_Manager::store()->get_last_retry_for_order( $order->id );; $this->retry = WCS_Retry_Manager::store()->get_last_retry_for_order( wcs_get_objects_property( $order, 'id' ) );;
$this->find['order-date'] = '{order_date}'; $this->find['order-date'] = '{order_date}';
$this->find['order-number'] = '{order_number}'; $this->find['order-number'] = '{order_number}';
$this->find['retry-time'] = '{retry_time}'; $this->find['retry-time'] = '{retry_time}';
$this->replace['order-date'] = date_i18n( wc_date_format(), strtotime( $this->object->order_date ) ); $this->replace['order-date'] = wcs_format_datetime( wcs_get_objects_property( $this->object, 'date_created' ) );
$this->replace['order-number'] = $this->object->get_order_number(); $this->replace['order-number'] = $this->object->get_order_number();
$this->replace['retry-time'] = strtolower( wcs_get_human_time_diff( $this->retry->get_time() ) ); $this->replace['retry-time'] = strtolower( wcs_get_human_time_diff( $this->retry->get_time() ) );

View File

@@ -161,7 +161,7 @@ class WC_Subscriptions_Payment_Gateways {
break; break;
} }
do_action( $hook_prefix . $subscription->payment_method, $subscription ); do_action( $hook_prefix . $subscription->get_payment_method(), $subscription );
} }
/** /**
@@ -170,12 +170,15 @@ class WC_Subscriptions_Payment_Gateways {
* @since 2.1.0 * @since 2.1.0
*/ */
public static function trigger_gateway_renewal_payment_hook( $renewal_order ) { public static function trigger_gateway_renewal_payment_hook( $renewal_order ) {
if ( ! empty( $renewal_order ) && $renewal_order->get_total() > 0 && ! empty( $renewal_order->payment_method ) ) {
$renewal_order_payment_method = wcs_get_objects_property( $renewal_order, 'payment_method' );
if ( ! empty( $renewal_order ) && $renewal_order->get_total() > 0 && ! empty( $renewal_order_payment_method ) ) {
// Make sure gateways are setup // Make sure gateways are setup
WC()->payment_gateways(); WC()->payment_gateways();
do_action( 'woocommerce_scheduled_subscription_payment_' . $renewal_order->payment_method, $renewal_order->get_total(), $renewal_order ); do_action( 'woocommerce_scheduled_subscription_payment_' . $renewal_order_payment_method, $renewal_order->get_total(), $renewal_order );
} }
} }

View File

@@ -234,7 +234,7 @@ class WCS_PayPal {
foreach ( wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'any' ) ) as $subscription ) { foreach ( wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'any' ) ) as $subscription ) {
$subscription->set_payment_method( $payment_method ); $subscription->set_payment_method( $payment_method );
wcs_set_paypal_id( $subscription, $billing_agreement_response->get_billing_agreement_id() ); wcs_set_paypal_id( $subscription, $billing_agreement_response->get_billing_agreement_id() ); // Also saves the subscription
} }
if ( ! wcs_is_subscription( $order ) ) { if ( ! wcs_is_subscription( $order ) ) {
@@ -330,7 +330,7 @@ class WCS_PayPal {
public static function process_subscription_payment( $amount, $order ) { public static function process_subscription_payment( $amount, $order ) {
// If the subscription is using reference transactions, we can process the payment ourselves // If the subscription is using reference transactions, we can process the payment ourselves
$paypal_profile_id = wcs_get_paypal_id( $order->id ); $paypal_profile_id = wcs_get_paypal_id( wcs_get_objects_property( $order, 'id' ) );
if ( wcs_is_paypal_profile_a( $paypal_profile_id, 'billing_agreement' ) ) { if ( wcs_is_paypal_profile_a( $paypal_profile_id, 'billing_agreement' ) ) {
@@ -414,7 +414,7 @@ class WCS_PayPal {
); );
foreach ( $post_meta_keys as $post_meta_key ) { foreach ( $post_meta_keys as $post_meta_key ) {
delete_post_meta( $resubscribe_order->id, $post_meta_key ); delete_post_meta( wcs_get_objects_property( $resubscribe_order, 'id' ), $post_meta_key );
} }
return $resubscribe_order; return $resubscribe_order;
@@ -433,9 +433,9 @@ class WCS_PayPal {
global $post; global $post;
$subscription = wcs_get_subscription( $post ); $subscription = wcs_get_subscription( $post );
if ( 'paypal' === $subscription->payment_method ) { if ( 'paypal' === $subscription->get_payment_method() ) {
$paypal_profile_id = wcs_get_paypal_id( $subscription->id ); $paypal_profile_id = wcs_get_paypal_id( $subscription->get_id() );
$is_paypal_standard = ! wcs_is_paypal_profile_a( $paypal_profile_id, 'billing_agreement' ); $is_paypal_standard = ! wcs_is_paypal_profile_a( $paypal_profile_id, 'billing_agreement' );
if ( $is_paypal_standard ) { if ( $is_paypal_standard ) {

View File

@@ -244,7 +244,7 @@ class WCS_PayPal_Admin {
* @param WC_Subscription $subscription * @param WC_Subscription $subscription
*/ */
public static function profile_link( $subscription ) { public static function profile_link( $subscription ) {
if ( wcs_is_subscription( $subscription ) && 'paypal' == $subscription->payment_method ) { if ( wcs_is_subscription( $subscription ) && 'paypal' == $subscription->get_payment_method() ) {
$paypal_profile_id = wcs_get_paypal_id( $subscription ); $paypal_profile_id = wcs_get_paypal_id( $subscription );

View File

@@ -41,7 +41,7 @@ class WCS_PayPal_Change_Payment_Method_Admin {
* @since 2.0 * @since 2.0
*/ */
public static function add_payment_meta_details( $payment_meta, $subscription ) { public static function add_payment_meta_details( $payment_meta, $subscription ) {
$subscription_id = get_post_meta( $subscription->id, '_paypal_subscription_id', true ); $subscription_id = get_post_meta( $subscription->get_id(), '_paypal_subscription_id', true );
if ( wcs_is_paypal_profile_a( $subscription_id, 'billing_agreement' ) || empty( $subscription_id ) ) { if ( wcs_is_paypal_profile_a( $subscription_id, 'billing_agreement' ) || empty( $subscription_id ) ) {
$label = 'PayPal Billing Agreement ID'; $label = 'PayPal Billing Agreement ID';
@@ -76,8 +76,8 @@ class WCS_PayPal_Change_Payment_Method_Admin {
public static function validate_payment_meta( $payment_meta, $subscription ) { public static function validate_payment_meta( $payment_meta, $subscription ) {
if ( empty( $payment_meta['post_meta']['_paypal_subscription_id']['value'] ) ) { if ( empty( $payment_meta['post_meta']['_paypal_subscription_id']['value'] ) ) {
throw new Exception( 'A valid PayPal Billing Agreement ID value is required.' ); throw new Exception( 'A valid PayPal Billing Agreement ID value is required.' );
} elseif ( $subscription->paypal_subscription_id !== $payment_meta['post_meta']['_paypal_subscription_id']['value'] && 0 !== strpos( $payment_meta['post_meta']['_paypal_subscription_id']['value'], 'B-' ) ) { } elseif ( 0 !== strpos( $payment_meta['post_meta']['_paypal_subscription_id']['value'], 'B-' ) && wcs_get_paypal_id( $subscription ) !== $payment_meta['post_meta']['_paypal_subscription_id']['value'] ) {
throw new Exception( 'Invalid Billing Agreemend ID. A valid PayPal Billing Agreement ID must begin with "B-".' ); throw new Exception( 'Invalid Billing Agreement ID. A valid PayPal Billing Agreement ID must begin with "B-".' );
} }
} }

View File

@@ -206,7 +206,7 @@ class WCS_PayPal_Reference_Transaction_API_Request {
'return_fraud_filters' => 1, 'return_fraud_filters' => 1,
'notify_url' => WC()->api_request_url( 'WC_Gateway_Paypal' ), 'notify_url' => WC()->api_request_url( 'WC_Gateway_Paypal' ),
'invoice_number' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ), 'invoice_number' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ),
'custom' => wcs_json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) ), 'custom' => wcs_json_encode( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ) ) ),
); );
$args = wp_parse_args( $args, $defaults ); $args = wp_parse_args( $args, $defaults );
@@ -310,24 +310,24 @@ class WCS_PayPal_Reference_Transaction_API_Request {
if ( $use_deprecated_params ) { if ( $use_deprecated_params ) {
$this->add_parameters( array( $this->add_parameters( array(
'AMT' => $total_amount, 'AMT' => $total_amount,
'CURRENCYCODE' => $order->get_order_currency(), 'CURRENCYCODE' => wcs_get_objects_property( $order, 'currency' ),
'ITEMAMT' => $this->round( $order_subtotal + $order->get_cart_tax() ), 'ITEMAMT' => $this->round( $order_subtotal + $order->get_cart_tax() ),
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ), 'SHIPPINGAMT' => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ),
'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ), 'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ),
'PAYMENTACTION' => $type, 'PAYMENTACTION' => $type,
'PAYMENTREQUESTID' => $order->id, 'PAYMENTREQUESTID' => wcs_get_objects_property( $order, 'id' ),
'CUSTOM' => json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) ), 'CUSTOM' => json_encode( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ) ) ),
) ); ) );
} else { } else {
$this->add_payment_parameters( array( $this->add_payment_parameters( array(
'AMT' => $total_amount, 'AMT' => $total_amount,
'CURRENCYCODE' => $order->get_order_currency(), 'CURRENCYCODE' => wcs_get_objects_property( $order, 'currency' ),
'ITEMAMT' => $this->round( $order_subtotal + $order->get_cart_tax() ), 'ITEMAMT' => $this->round( $order_subtotal + $order->get_cart_tax() ),
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ), 'SHIPPINGAMT' => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ),
'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ), 'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ),
'PAYMENTACTION' => $type, 'PAYMENTACTION' => $type,
'PAYMENTREQUESTID' => $order->id, 'PAYMENTREQUESTID' => wcs_get_objects_property( $order, 'id' ),
'CUSTOM' => json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) ), 'CUSTOM' => json_encode( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ) ) ),
) ); ) );
} }
} else { } else {
@@ -348,26 +348,26 @@ class WCS_PayPal_Reference_Transaction_API_Request {
if ( $use_deprecated_params ) { if ( $use_deprecated_params ) {
$this->add_parameters( array( $this->add_parameters( array(
'AMT' => $total_amount, 'AMT' => $total_amount,
'CURRENCYCODE' => $order->get_order_currency(), 'CURRENCYCODE' => wcs_get_objects_property( $order, 'currency' ),
'ITEMAMT' => $this->round( $order_subtotal ), 'ITEMAMT' => $this->round( $order_subtotal ),
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() ), 'SHIPPINGAMT' => $this->round( $order->get_total_shipping() ),
'TAXAMT' => $this->round( $order->get_total_tax() ), 'TAXAMT' => $this->round( $order->get_total_tax() ),
'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ), 'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ),
'PAYMENTACTION' => $type, 'PAYMENTACTION' => $type,
'PAYMENTREQUESTID' => $order->id, 'PAYMENTREQUESTID' => wcs_get_objects_property( $order, 'id' ),
'CUSTOM' => json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) ), 'CUSTOM' => json_encode( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ) ) ),
) ); ) );
} else { } else {
$this->add_payment_parameters( array( $this->add_payment_parameters( array(
'AMT' => $total_amount, 'AMT' => $total_amount,
'CURRENCYCODE' => $order->get_order_currency(), 'CURRENCYCODE' => wcs_get_objects_property( $order, 'currency' ),
'ITEMAMT' => $this->round( $order_subtotal ), 'ITEMAMT' => $this->round( $order_subtotal ),
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() ), 'SHIPPINGAMT' => $this->round( $order->get_total_shipping() ),
'TAXAMT' => $this->round( $order->get_total_tax() ), 'TAXAMT' => $this->round( $order->get_total_tax() ),
'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ), 'INVNUM' => WCS_PayPal::get_option( 'invoice_prefix' ) . wcs_str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) ),
'PAYMENTACTION' => $type, 'PAYMENTACTION' => $type,
'PAYMENTREQUESTID' => $order->id, 'PAYMENTREQUESTID' => wcs_get_objects_property( $order, 'id' ),
'CUSTOM' => json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) ), 'CUSTOM' => json_encode( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ) ) ),
) ); ) );
} }
@@ -484,27 +484,32 @@ class WCS_PayPal_Reference_Transaction_API_Request {
*/ */
private function get_item_description( $item, $product ) { private function get_item_description( $item, $product ) {
if ( empty( $item['item_meta'] ) ) { if ( ! empty( WC()->cart ) && is_array( $item ) && empty( $item['item_meta'] ) ) {
// cart item // cart item
$item_desc = WC()->cart->get_item_data( $item, true ); $item_desc = WC()->cart->get_item_data( $item, true );
$item_desc = str_replace( "\n", ', ', rtrim( $item_desc ) ); $item_desc = str_replace( "\n", ', ', rtrim( $item_desc ) );
} else { } else {
// order item // order item
$item_meta = new WC_Order_Item_Meta( $item );
$item_meta = $item_meta->get_formatted();
if ( ! empty( $item_meta ) ) {
$item_desc = array(); $item_desc = array();
foreach ( $item_meta as $meta ) { if ( is_callable( array( $item, 'get_formatted_meta_data' ) ) ) { // WC 3.0+
foreach ( $item->get_formatted_meta_data() as $meta ) {
$item_desc[] = sprintf( '%s: %s', $meta->display_key, $meta->display_value );
}
} else { // WC < 3.0
$item_meta = new WC_Order_Item_Meta( $item );
foreach ( $item_meta->get_formatted() as $meta ) {
$item_desc[] = sprintf( '%s: %s', $meta['label'], $meta['value'] ); $item_desc[] = sprintf( '%s: %s', $meta['label'], $meta['value'] );
} }
}
if ( ! empty( $item_desc ) ) {
$item_desc = implode( ', ', $item_desc ); $item_desc = implode( ', ', $item_desc );
@@ -525,7 +530,7 @@ class WCS_PayPal_Reference_Transaction_API_Request {
* @since 2.0 * @since 2.0
*/ */
public function to_string() { public function to_string() {
return http_build_query( $this->get_parameters() ); return http_build_query( $this->get_parameters(), '', '&' );
} }
/** /**
@@ -630,11 +635,7 @@ class WCS_PayPal_Reference_Transaction_API_Request {
*/ */
private function skip_line_items( $order = null ) { private function skip_line_items( $order = null ) {
if ( isset( $order->prices_include_tax ) ) { $skip_line_items = wcs_get_objects_property( $order, 'prices_include_tax' );
$skip_line_items = $order->prices_include_tax;
} else {
$skip_line_items = wc_prices_include_tax();
}
/** /**
* Filter whether line items should be skipped or not * Filter whether line items should be skipped or not

View File

@@ -64,7 +64,7 @@ class WCS_PayPal_Reference_Transaction_IPN_Handler extends WCS_PayPal_Standard_I
$transaction_details['payment_status'] = 'completed'; $transaction_details['payment_status'] = 'completed';
} }
WC_Gateway_Paypal::log( 'Found order #' . $order->id ); WC_Gateway_Paypal::log( 'Found order #' . wcs_get_objects_property( $order, 'id' ) );
WC_Gateway_Paypal::log( 'Payment status: ' . $transaction_details['payment_status'] ); WC_Gateway_Paypal::log( 'Payment status: ' . $transaction_details['payment_status'] );
if ( method_exists( $this, 'payment_status_' . $transaction_details['payment_status'] ) ) { if ( method_exists( $this, 'payment_status_' . $transaction_details['payment_status'] ) ) {

View File

@@ -84,7 +84,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
remove_filter( 'woocommerce_subscription_payment_gateway_supports', 'WCS_PayPal_Supports::add_feature_support_for_subscription', 10 ); remove_filter( 'woocommerce_subscription_payment_gateway_supports', 'WCS_PayPal_Supports::add_feature_support_for_subscription', 10 );
// We have an invalid $subscription, probably because invoice_prefix has changed since the subscription was first created, so get the subscription by order key // We have an invalid $subscription, probably because invoice_prefix has changed since the subscription was first created, so get the subscription by order key
if ( ! isset( $subscription->id ) ) { if ( ! is_callable( array( $subscription, 'get_id' ) ) ) {
$subscription = wcs_get_subscription( wc_get_order_id_by_order_key( $subscription_key ) ); $subscription = wcs_get_subscription( wc_get_order_id_by_order_key( $subscription_key ) );
} }
@@ -98,7 +98,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
exit; exit;
} }
if ( $subscription->order_key != $subscription_key ) { if ( $subscription->get_order_key() != $subscription_key ) {
WC_Gateway_Paypal::log( 'Subscription IPN Error: Subscription Key does not match invoice.' ); WC_Gateway_Paypal::log( 'Subscription IPN Error: Subscription Key does not match invoice.' );
exit; exit;
} }
@@ -106,7 +106,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
if ( isset( $transaction_details['ipn_track_id'] ) ) { if ( isset( $transaction_details['ipn_track_id'] ) ) {
// Make sure the IPN request has not already been handled // Make sure the IPN request has not already been handled
$handled_ipn_requests = get_post_meta( $subscription->id, '_paypal_ipn_tracking_ids', true ); $handled_ipn_requests = get_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', true );
if ( empty( $handled_ipn_requests ) ) { if ( empty( $handled_ipn_requests ) ) {
$handled_ipn_requests = array(); $handled_ipn_requests = array();
@@ -140,7 +140,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
if ( isset( $transaction_details['txn_id'] ) ) { if ( isset( $transaction_details['txn_id'] ) ) {
// Make sure the IPN request has not already been handled // Make sure the IPN request has not already been handled
$handled_transactions = get_post_meta( $subscription->id, '_paypal_transaction_ids', true ); $handled_transactions = get_post_meta( $subscription->get_id(), '_paypal_transaction_ids', true );
if ( empty( $handled_transactions ) ) { if ( empty( $handled_transactions ) ) {
$handled_transactions = array(); $handled_transactions = array();
@@ -171,7 +171,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
$renewal_order = wc_get_order( substr( $transaction_details['invoice'], strrpos( $transaction_details['invoice'], '-' ) + 1 ) ); $renewal_order = wc_get_order( substr( $transaction_details['invoice'], strrpos( $transaction_details['invoice'], '-' ) + 1 ) );
// check if the failed signup has been previously recorded // check if the failed signup has been previously recorded
if ( $renewal_order->id != get_post_meta( $subscription->id, '_paypal_failed_sign_up_recorded', true ) ) { if ( wcs_get_objects_property( $renewal_order, 'id' ) != get_post_meta( $subscription->get_id(), '_paypal_failed_sign_up_recorded', true ) ) {
$is_renewal_sign_up_after_failure = true; $is_renewal_sign_up_after_failure = true;
} }
} }
@@ -184,7 +184,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
} }
// Ignore IPN messages when the payment method isn't PayPal // Ignore IPN messages when the payment method isn't PayPal
if ( 'paypal' != $subscription->payment_method ) { if ( 'paypal' != $subscription->get_payment_method() ) {
// The 'recurring_payment_suspended' transaction is actually an Express Checkout transaction type, but PayPal also send it for PayPal Standard Subscriptions suspended by admins at PayPal, so we need to handle it *if* the subscription has PayPal as the payment method, or leave it if the subscription is using a different payment method (because it might be using PayPal Express Checkout or PayPal Digital Goods) // The 'recurring_payment_suspended' transaction is actually an Express Checkout transaction type, but PayPal also send it for PayPal Standard Subscriptions suspended by admins at PayPal, so we need to handle it *if* the subscription has PayPal as the payment method, or leave it if the subscription is using a different payment method (because it might be using PayPal Express Checkout or PayPal Digital Goods)
if ( 'recurring_payment_suspended' == $transaction_details['txn_type'] ) { if ( 'recurring_payment_suspended' == $transaction_details['txn_type'] ) {
@@ -206,8 +206,8 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
$existing_profile_id = wcs_get_paypal_id( $subscription ); $existing_profile_id = wcs_get_paypal_id( $subscription );
if ( empty( $existing_profile_id ) || $existing_profile_id !== $transaction_details['subscr_id'] ) { if ( empty( $existing_profile_id ) || $existing_profile_id !== $transaction_details['subscr_id'] ) {
update_post_meta( $subscription->id, '_old_paypal_subscriber_id', $existing_profile_id ); update_post_meta( $subscription->get_id(), '_old_paypal_subscriber_id', $existing_profile_id );
update_post_meta( $subscription->id, '_old_payment_method', $subscription->payment_method ); update_post_meta( $subscription->get_id(), '_old_payment_method', $subscription->get_payment_method() );
} }
} }
@@ -232,13 +232,13 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
// Store PayPal Details on Subscription and Order // Store PayPal Details on Subscription and Order
$this->save_paypal_meta_data( $subscription, $transaction_details ); $this->save_paypal_meta_data( $subscription, $transaction_details );
$this->save_paypal_meta_data( $subscription->order, $transaction_details ); $this->save_paypal_meta_data( $subscription->get_parent(), $transaction_details );
// When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription
if ( ! $is_payment_change && ! $is_renewal_sign_up_after_failure && 0 == $subscription->order->get_total() ) { if ( ! $is_payment_change && ! $is_renewal_sign_up_after_failure && 0 == $subscription->get_parent()->get_total() ) {
// Safe to assume the subscription has an order here because otherwise we wouldn't get a 'subscr_signup' IPN // Safe to assume the subscription has an order here because otherwise we wouldn't get a 'subscr_signup' IPN
$subscription->order->payment_complete(); // No 'txn_id' value for 'subscr_signup' IPN messages $subscription->get_parent()->payment_complete(); // No 'txn_id' value for 'subscr_signup' IPN messages
update_post_meta( $subscription->id, '_paypal_first_ipn_ignored_for_pdt', 'true' ); update_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', 'true' );
} }
// Payment completed // Payment completed
@@ -248,8 +248,8 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
WC_Subscriptions_Change_Payment_Gateway::update_payment_method( $subscription, 'paypal' ); WC_Subscriptions_Change_Payment_Gateway::update_payment_method( $subscription, 'paypal' );
// We need to cancel the subscription now that the method has been changed successfully // We need to cancel the subscription now that the method has been changed successfully
if ( 'paypal' == get_post_meta( $subscription->id, '_old_payment_method', true ) ) { if ( 'paypal' == get_post_meta( $subscription->get_id(), '_old_payment_method', true ) ) {
self::cancel_subscription( $subscription, get_post_meta( $subscription->id, '_old_paypal_subscriber_id', true ) ); self::cancel_subscription( $subscription, get_post_meta( $subscription->get_id(), '_old_paypal_subscriber_id', true ) );
} }
$this->add_order_note( _x( 'IPN subscription payment method changed to PayPal.', 'when it is a payment change, and there is a subscr_signup message, this will be a confirmation message that PayPal accepted it being the new payment method', 'woocommerce-subscriptions' ), $subscription, $transaction_details ); $this->add_order_note( _x( 'IPN subscription payment method changed to PayPal.', 'when it is a payment change, and there is a subscr_signup message, this will be a confirmation message that PayPal accepted it being the new payment method', 'woocommerce-subscriptions' ), $subscription, $transaction_details );
@@ -261,9 +261,9 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
} }
if ( $is_payment_change ) { if ( $is_payment_change ) {
WC_Gateway_Paypal::log( 'IPN subscription payment method changed for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription payment method changed for subscription ' . $subscription->get_id() );
} else { } else {
WC_Gateway_Paypal::log( 'IPN subscription sign up completed for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription sign up completed for subscription ' . $subscription->get_id() );
} }
break; break;
@@ -301,37 +301,37 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
// Subscription Payment completed // Subscription Payment completed
$this->add_order_note( __( 'IPN subscription payment completed.', 'woocommerce-subscriptions' ), $subscription, $transaction_details ); $this->add_order_note( __( 'IPN subscription payment completed.', 'woocommerce-subscriptions' ), $subscription, $transaction_details );
WC_Gateway_Paypal::log( 'IPN subscription payment completed for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription payment completed for subscription ' . $subscription->get_id() );
// First payment on order, process payment & activate subscription // First payment on order, process payment & activate subscription
if ( $is_first_payment ) { if ( $is_first_payment ) {
$subscription->order->payment_complete( $transaction_details['txn_id'] ); $subscription->get_parent()->payment_complete( $transaction_details['txn_id'] );
// Store PayPal Details on Order // Store PayPal Details on Order
$this->save_paypal_meta_data( $subscription->order, $transaction_details ); $this->save_paypal_meta_data( $subscription->get_parent(), $transaction_details );
// IPN got here first or PDT will never arrive. Normally PDT would have arrived, so the first IPN would not be the first payment. In case the the first payment is an IPN, we need to make sure to not ignore the second one // IPN got here first or PDT will never arrive. Normally PDT would have arrived, so the first IPN would not be the first payment. In case the the first payment is an IPN, we need to make sure to not ignore the second one
update_post_meta( $subscription->id, '_paypal_first_ipn_ignored_for_pdt', 'true' ); update_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', 'true' );
// Ignore the first IPN message if the PDT should have handled it (if it didn't handle it, it will have been dealt with as first payment), but set a flag to make sure we only ignore it once // Ignore the first IPN message if the PDT should have handled it (if it didn't handle it, it will have been dealt with as first payment), but set a flag to make sure we only ignore it once
} elseif ( $subscription->get_completed_payment_count() == 1 && '' !== WCS_PayPal::get_option( 'identity_token' ) && 'true' != get_post_meta( $subscription->id, '_paypal_first_ipn_ignored_for_pdt', true ) && false === $is_renewal_sign_up_after_failure ) { } elseif ( $subscription->get_completed_payment_count() == 1 && '' !== WCS_PayPal::get_option( 'identity_token' ) && 'true' != get_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', true ) && false === $is_renewal_sign_up_after_failure ) {
WC_Gateway_Paypal::log( 'IPN subscription payment ignored for subscription ' . $subscription->id . ' due to PDT previously handling the payment.' ); WC_Gateway_Paypal::log( 'IPN subscription payment ignored for subscription ' . $subscription->get_id() . ' due to PDT previously handling the payment.' );
update_post_meta( $subscription->id, '_paypal_first_ipn_ignored_for_pdt', 'true' ); update_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', 'true' );
// Process the payment if the subscription is active // Process the payment if the subscription is active
} elseif ( ! $subscription->has_status( array( 'cancelled', 'expired', 'switched', 'trash' ) ) ) { } elseif ( ! $subscription->has_status( array( 'cancelled', 'expired', 'switched', 'trash' ) ) ) {
if ( true === $is_renewal_sign_up_after_failure && is_object( $renewal_order ) ) { if ( true === $is_renewal_sign_up_after_failure && is_object( $renewal_order ) ) {
update_post_meta( $subscription->id, '_paypal_failed_sign_up_recorded', $renewal_order->id ); update_post_meta( $subscription->get_id(), '_paypal_failed_sign_up_recorded', wcs_get_objects_property( $renewal_order, 'id' ) );
// We need to cancel the old subscription now that the method has been changed successfully // We need to cancel the old subscription now that the method has been changed successfully
if ( 'paypal' == get_post_meta( $subscription->id, '_old_payment_method', true ) ) { if ( 'paypal' == get_post_meta( $subscription->get_id(), '_old_payment_method', true ) ) {
$profile_id = get_post_meta( $subscription->id, '_old_paypal_subscriber_id', true ); $profile_id = get_post_meta( $subscription->get_id(), '_old_paypal_subscriber_id', true );
// Make sure we don't cancel the current profile // Make sure we don't cancel the current profile
if ( $profile_id !== $transaction_details['subscr_id'] ) { if ( $profile_id !== $transaction_details['subscr_id'] ) {
@@ -349,23 +349,23 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
if ( $subscription->get_time( 'trial_end' ) > gmdate( 'U' ) ) { if ( $subscription->get_time( 'trial_end' ) > gmdate( 'U' ) ) {
$update_dates['trial_end'] = gmdate( 'Y-m-d H:i:s', gmdate( 'U' ) - 1 ); $update_dates['trial_end'] = gmdate( 'Y-m-d H:i:s', gmdate( 'U' ) - 1 );
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: trial_end is in futute (date: %s) setting to %s.', $subscription->id, $subscription->get_date( 'trial_end' ), $update_dates['trial_end'] ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: trial_end is in futute (date: %s) setting to %s.', $subscription->get_id(), $subscription->get_date( 'trial_end' ), $update_dates['trial_end'] ) );
} else { } else {
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: trial_end is in past (date: %s).', $subscription->id, $subscription->get_date( 'trial_end' ) ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: trial_end is in past (date: %s).', $subscription->get_id(), $subscription->get_date( 'trial_end' ) ) );
} }
if ( $subscription->get_time( 'next_payment' ) > gmdate( 'U' ) ) { if ( $subscription->get_time( 'next_payment' ) > gmdate( 'U' ) ) {
$update_dates['next_payment'] = gmdate( 'Y-m-d H:i:s', gmdate( 'U' ) - 1 ); $update_dates['next_payment'] = gmdate( 'Y-m-d H:i:s', gmdate( 'U' ) - 1 );
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: next_payment is in future (date: %s) setting to %s.', $subscription->id, $subscription->get_date( 'next_payment' ), $update_dates['next_payment'] ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: next_payment is in future (date: %s) setting to %s.', $subscription->get_id(), $subscription->get_date( 'next_payment' ), $update_dates['next_payment'] ) );
} else { } else {
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: next_payment is in past (date: %s).', $subscription->id, $subscription->get_date( 'next_payment' ) ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment for subscription %d: next_payment is in past (date: %s).', $subscription->get_id(), $subscription->get_date( 'next_payment' ) ) );
} }
if ( ! empty( $update_dates ) ) { if ( ! empty( $update_dates ) ) {
$subscription->update_dates( $update_dates ); $subscription->update_dates( $update_dates );
} }
} catch ( Exception $e ) { } catch ( Exception $e ) {
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment exception subscription %d: %s.', $subscription->id, $e->getMessage() ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment exception subscription %d: %s.', $subscription->get_id(), $e->getMessage() ) );
} }
remove_action( 'woocommerce_subscription_activated_paypal', 'WCS_PayPal_Status_Manager::reactivate_subscription' ); remove_action( 'woocommerce_subscription_activated_paypal', 'WCS_PayPal_Status_Manager::reactivate_subscription' );
@@ -373,7 +373,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
try { try {
$renewal_order->payment_complete( $transaction_details['txn_id'] ); $renewal_order->payment_complete( $transaction_details['txn_id'] );
} catch ( Exception $e ) { } catch ( Exception $e ) {
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment exception calling $renewal_order->payment_complete() for subscription %d: %s.', $subscription->id, $e->getMessage() ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment exception calling $renewal_order->payment_complete() for subscription %d: %s.', $subscription->get_id(), $e->getMessage() ) );
} }
$this->add_order_note( __( 'IPN subscription payment completed.', 'woocommerce-subscriptions' ), $renewal_order, $transaction_details ); $this->add_order_note( __( 'IPN subscription payment completed.', 'woocommerce-subscriptions' ), $renewal_order, $transaction_details );
@@ -390,7 +390,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
if ( ! $is_first_payment ) { if ( ! $is_first_payment ) {
update_post_meta( $renewal_order->id, '_transaction_id', $transaction_details['txn_id'] ); wcs_set_objects_property( $renewal_order, 'transaction_id', $transaction_details['txn_id'] );
if ( 'failed' == strtolower( $transaction_details['payment_status'] ) ) { if ( 'failed' == strtolower( $transaction_details['payment_status'] ) ) {
$subscription->payment_failed(); $subscription->payment_failed();
@@ -403,10 +403,10 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
} }
} }
WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment %s for subscription %d ', $transaction_details['payment_status'], $subscription->id ) ); WC_Gateway_Paypal::log( sprintf( 'IPN subscription payment %s for subscription %d ', $transaction_details['payment_status'], $subscription->get_id() ) );
} else { } else {
WC_Gateway_Paypal::log( 'IPN subscription payment notification received for subscription ' . $subscription->id . ' with status ' . $transaction_details['payment_status'] ); WC_Gateway_Paypal::log( 'IPN subscription payment notification received for subscription ' . $subscription->get_id() . ' with status ' . $transaction_details['payment_status'] );
} }
@@ -429,11 +429,11 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
add_action( 'woocommerce_subscription_on-hold_paypal', 'WCS_PayPal_Status_Manager::suspend_subscription' ); add_action( 'woocommerce_subscription_on-hold_paypal', 'WCS_PayPal_Status_Manager::suspend_subscription' );
WC_Gateway_Paypal::log( 'IPN subscription suspended for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription suspended for subscription ' . $subscription->get_id() );
} else { } else {
WC_Gateway_Paypal::log( sprintf( 'IPN "recurring_payment_suspended" ignored for subscription %d. Subscription already %s.', $subscription->id, $subscription->get_status() ) ); WC_Gateway_Paypal::log( sprintf( 'IPN "recurring_payment_suspended" ignored for subscription %d. Subscription already %s.', $subscription->get_id(), $subscription->get_status() ) );
} }
@@ -444,13 +444,13 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
// Make sure the subscription hasn't been linked to a new payment method // Make sure the subscription hasn't been linked to a new payment method
if ( wcs_get_paypal_id( $subscription ) != $transaction_details['subscr_id'] ) { if ( wcs_get_paypal_id( $subscription ) != $transaction_details['subscr_id'] ) {
WC_Gateway_Paypal::log( 'IPN subscription cancellation request ignored - new PayPal Profile ID linked to this subscription, for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription cancellation request ignored - new PayPal Profile ID linked to this subscription, for subscription ' . $subscription->get_id() );
} else { } else {
$subscription->cancel_order( __( 'IPN subscription cancelled.', 'woocommerce-subscriptions' ) ); $subscription->cancel_order( __( 'IPN subscription cancelled.', 'woocommerce-subscriptions' ) );
WC_Gateway_Paypal::log( 'IPN subscription cancelled for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription cancelled for subscription ' . $subscription->get_id() );
} }
@@ -458,7 +458,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
case 'subscr_eot': // Subscription ended, either due to failed payments or expiration case 'subscr_eot': // Subscription ended, either due to failed payments or expiration
WC_Gateway_Paypal::log( 'IPN EOT request ignored for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN EOT request ignored for subscription ' . $subscription->get_id() );
break; break;
case 'subscr_failed': // Subscription sign up failed case 'subscr_failed': // Subscription sign up failed
@@ -476,7 +476,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
$this->add_order_note( $ipn_failure_note, $renewal_order, $transaction_details ); $this->add_order_note( $ipn_failure_note, $renewal_order, $transaction_details );
} }
WC_Gateway_Paypal::log( 'IPN subscription payment failure for subscription ' . $subscription->id ); WC_Gateway_Paypal::log( 'IPN subscription payment failure for subscription ' . $subscription->get_id() );
// Subscription Payment completed // Subscription Payment completed
$this->add_order_note( $ipn_failure_note, $subscription, $transaction_details ); $this->add_order_note( $ipn_failure_note, $subscription, $transaction_details );
@@ -493,12 +493,12 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
// Store the transaction IDs to avoid handling requests duplicated by PayPal // Store the transaction IDs to avoid handling requests duplicated by PayPal
if ( isset( $transaction_details['ipn_track_id'] ) ) { if ( isset( $transaction_details['ipn_track_id'] ) ) {
$handled_ipn_requests[] = $ipn_id; $handled_ipn_requests[] = $ipn_id;
update_post_meta( $subscription->id, '_paypal_ipn_tracking_ids', $handled_ipn_requests ); update_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', $handled_ipn_requests );
} }
if ( isset( $transaction_details['txn_id'] ) ) { if ( isset( $transaction_details['txn_id'] ) ) {
$handled_transactions[] = $transaction_id; $handled_transactions[] = $transaction_id;
update_post_meta( $subscription->id, '_paypal_transaction_ids', $handled_transactions ); update_post_meta( $subscription->get_id(), '_paypal_transaction_ids', $handled_transactions );
} }
// And delete the transient that's preventing other IPN's being processed // And delete the transient that's preventing other IPN's being processed
@@ -507,7 +507,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
} }
// Log completion // Log completion
$log_message = 'IPN subscription request processed for ' . $subscription->id; $log_message = 'IPN subscription request processed for ' . $subscription->get_id();
if ( isset( $ipn_id ) && ! empty( $ipn_id ) ) { if ( isset( $ipn_id ) && ! empty( $ipn_id ) ) {
$log_message .= sprintf( ' (%s)', $ipn_id ); $log_message .= sprintf( ' (%s)', $ipn_id );
@@ -592,8 +592,8 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
if ( ! empty( $subscriptions ) ) { if ( ! empty( $subscriptions ) ) {
$subscription = array_pop( $subscriptions ); $subscription = array_pop( $subscriptions );
$order_id = $subscription->id; $order_id = $subscription->get_id();
$order_key = $subscription->order_key; $order_key = $subscription->get_order_key();
} }
} }
} elseif ( preg_match( '/^a:2:{/', $args['custom'] ) && ! preg_match( '/[CO]:\+?[0-9]+:"/', $args['custom'] ) && ( $order_details = maybe_unserialize( $args['custom'] ) ) ) { // WC 2.0 - WC 2.3.11, only allow serialized data in the expected format, do not allow objects or anything nasty to sneak in } elseif ( preg_match( '/^a:2:{/', $args['custom'] ) && ! preg_match( '/[CO]:\+?[0-9]+:"/', $args['custom'] ) && ( $order_details = maybe_unserialize( $args['custom'] ) ) ) { // WC 2.0 - WC 2.3.11, only allow serialized data in the expected format, do not allow objects or anything nasty to sneak in
@@ -608,8 +608,8 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
if ( ! empty( $subscriptions ) ) { if ( ! empty( $subscriptions ) ) {
$subscription = array_pop( $subscriptions ); $subscription = array_pop( $subscriptions );
$order_id = $subscription->id; $order_id = $subscription->get_id();
$order_key = $subscription->order_key; $order_key = $subscription->get_order_key();
} }
} }
} else { // WC 1.6.5 - WC 2.0 or invalid data } else { // WC 1.6.5 - WC 2.0 or invalid data
@@ -641,7 +641,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
return; return;
} }
$current_profile_id = wcs_get_paypal_id( $subscription->id ); $current_profile_id = wcs_get_paypal_id( $subscription->get_id() );
// Update the subscription using the old profile ID // Update the subscription using the old profile ID
wcs_set_paypal_id( $subscription, $old_paypal_subscriber_id ); wcs_set_paypal_id( $subscription, $old_paypal_subscriber_id );

View File

@@ -33,9 +33,9 @@ class WCS_PayPal_Standard_Request {
// Payment method changes act on the subscription not the original order // Payment method changes act on the subscription not the original order
if ( $is_payment_change ) { if ( $is_payment_change ) {
$subscriptions = array( wcs_get_subscription( $order->id ) ); $subscriptions = array( wcs_get_subscription( wcs_get_objects_property( $order, 'id' ) ) );
$subscription = array_pop( $subscriptions ); $subscription = array_pop( $subscriptions );
$order = $subscription->order; $order = $subscription->get_parent();
// We need the subscription's total // We need the subscription's total
remove_filter( 'woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 ); remove_filter( 'woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 );
@@ -43,7 +43,7 @@ class WCS_PayPal_Standard_Request {
} else { } else {
// Otherwise the order is the $order // Otherwise the order is the $order
if ( $cart_item = wcs_cart_contains_failed_renewal_order_payment() || false !== WC_Subscriptions_Renewal_Order::get_failed_order_replaced_by( $order->id ) ) { if ( $cart_item = wcs_cart_contains_failed_renewal_order_payment() || false !== WC_Subscriptions_Renewal_Order::get_failed_order_replaced_by( wcs_get_objects_property( $order, 'id' ) ) ) {
$subscriptions = wcs_get_subscriptions_for_renewal_order( $order ); $subscriptions = wcs_get_subscriptions_for_renewal_order( $order );
$order_contains_failed_renewal = true; $order_contains_failed_renewal = true;
} else { } else {
@@ -60,7 +60,7 @@ class WCS_PayPal_Standard_Request {
$paypal_args['cmd'] = '_xclick-subscriptions'; $paypal_args['cmd'] = '_xclick-subscriptions';
// Store the subscription ID in the args sent to PayPal so we can access them later // Store the subscription ID in the args sent to PayPal so we can access them later
$paypal_args['custom'] = wcs_json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key, 'subscription_id' => $subscription->id, 'subscription_key' => $subscription->order_key ) ); $paypal_args['custom'] = wcs_json_encode( array( 'order_id' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ), 'subscription_id' => $subscription->get_id(), 'subscription_key' => $subscription->get_order_key() ) );
foreach ( $subscription->get_items() as $item ) { foreach ( $subscription->get_items() as $item ) {
if ( $item['qty'] > 1 ) { if ( $item['qty'] > 1 ) {
@@ -74,8 +74,8 @@ class WCS_PayPal_Standard_Request {
$paypal_args['item_name'] = wcs_get_paypal_item_name( sprintf( _x( 'Subscription %1$s (Order %2$s) - %3$s', 'item name sent to paypal', 'woocommerce-subscriptions' ), $subscription->get_order_number(), $order->get_order_number(), implode( ', ', $item_names ) ) ); $paypal_args['item_name'] = wcs_get_paypal_item_name( sprintf( _x( 'Subscription %1$s (Order %2$s) - %3$s', 'item name sent to paypal', 'woocommerce-subscriptions' ), $subscription->get_order_number(), $order->get_order_number(), implode( ', ', $item_names ) ) );
$unconverted_periods = array( $unconverted_periods = array(
'billing_period' => $subscription->billing_period, 'billing_period' => $subscription->get_billing_period(),
'trial_period' => $subscription->trial_period, 'trial_period' => $subscription->get_trial_period(),
); );
$converted_periods = array(); $converted_periods = array();
@@ -100,12 +100,12 @@ class WCS_PayPal_Standard_Request {
} }
$price_per_period = $subscription->get_total(); $price_per_period = $subscription->get_total();
$subscription_interval = $subscription->billing_interval; $subscription_interval = $subscription->get_billing_interval();
$start_timestamp = $subscription->get_time( 'start' ); $start_timestamp = $subscription->get_time( 'date_created' );
$trial_end_timestamp = $subscription->get_time( 'trial_end' ); $trial_end_timestamp = $subscription->get_time( 'trial_end' );
$next_payment_timestamp = $subscription->get_time( 'next_payment' ); $next_payment_timestamp = $subscription->get_time( 'next_payment' );
$is_synced_subscription = WC_Subscriptions_Synchroniser::subscription_contains_synced_product( $subscription->id ); $is_synced_subscription = WC_Subscriptions_Synchroniser::subscription_contains_synced_product( $subscription->get_id() );
if ( $is_synced_subscription ) { if ( $is_synced_subscription ) {
$length_from_timestamp = $next_payment_timestamp; $length_from_timestamp = $next_payment_timestamp;
@@ -115,7 +115,7 @@ class WCS_PayPal_Standard_Request {
$length_from_timestamp = $start_timestamp; $length_from_timestamp = $start_timestamp;
} }
$subscription_length = wcs_estimate_periods_between( $length_from_timestamp, $subscription->get_time( 'end' ), $subscription->billing_period ); $subscription_length = wcs_estimate_periods_between( $length_from_timestamp, $subscription->get_time( 'end' ), $subscription->get_billing_period() );
$subscription_installments = $subscription_length / $subscription_interval; $subscription_installments = $subscription_length / $subscription_interval;
@@ -128,24 +128,24 @@ class WCS_PayPal_Standard_Request {
$suffix = '-wcscpm-' . wp_create_nonce(); $suffix = '-wcscpm-' . wp_create_nonce();
} else { } else {
// Failed renewal order, append a descriptor and renewal order's ID // Failed renewal order, append a descriptor and renewal order's ID
$suffix = '-wcsfrp-' . $order->id; $suffix = '-wcsfrp-' . wcs_get_objects_property( $order, 'id' );
} }
$parent_order = $subscription->get_parent();
// Change the 'invoice' and the 'custom' values to be for the original order (if there is one) // Change the 'invoice' and the 'custom' values to be for the original order (if there is one)
if ( false === $subscription->order ) { if ( false === $parent_order ) {
// No original order so we need to use the subscriptions values instead // No original order so we need to use the subscriptions values instead
$order_number = ltrim( $subscription->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) . '-subscription'; $order_number = ltrim( $subscription->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ) . '-subscription';
$order_id_key = array( 'order_id' => $subscription->id, 'order_key' => $subscription->order_key ); $order_id_key = array( 'order_id' => $subscription->get_id(), 'order_key' => $subscription->get_order_key() );
} else { } else {
$order_number = ltrim( $subscription->order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) ); $order_number = ltrim( $parent_order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions' ) );
$order_id_key = array( 'order_id' => $subscription->order->id, 'order_key' => $subscription->order->order_key ); $order_id_key = array( 'order_id' => wcs_get_objects_property( $parent_order, 'id' ), 'order_key' => wcs_get_objects_property( $parent_order, 'order_key' ) );
} }
$order_details = ( false !== $subscription->order ) ? $subscription->order : $subscription;
// Set the invoice details to the original order's invoice but also append a special string and this renewal orders ID so that we can match it up as a failed renewal order payment later // Set the invoice details to the original order's invoice but also append a special string and this renewal orders ID so that we can match it up as a failed renewal order payment later
$paypal_args['invoice'] = WCS_PayPal::get_option( 'invoice_prefix' ) . $order_number . $suffix; $paypal_args['invoice'] = WCS_PayPal::get_option( 'invoice_prefix' ) . $order_number . $suffix;
$paypal_args['custom'] = wcs_json_encode( array_merge( $order_id_key, array( 'subscription_id' => $subscription->id, 'subscription_key' => $subscription->order_key ) ) ); $paypal_args['custom'] = wcs_json_encode( array_merge( $order_id_key, array( 'subscription_id' => $subscription->get_id(), 'subscription_key' => $subscription->get_order_key() ) ) );
} }
@@ -182,7 +182,7 @@ class WCS_PayPal_Standard_Request {
} }
} else { } else {
$subscription_trial_length = wcs_estimate_periods_between( $start_timestamp, $trial_end_timestamp, $subscription->trial_period ); $subscription_trial_length = wcs_estimate_periods_between( $start_timestamp, $trial_end_timestamp, $subscription->get_trial_period() );
} }

View File

@@ -56,9 +56,9 @@ class WCS_PayPal_Standard_Switcher {
*/ */
public static function can_item_be_switched( $item_can_be_switch, $item, $subscription ) { public static function can_item_be_switched( $item_can_be_switch, $item, $subscription ) {
if ( false === $item_can_be_switch && 'paypal' === $subscription->payment_method && WCS_PayPal::are_reference_transactions_enabled() ) { if ( false === $item_can_be_switch && 'paypal' === $subscription->get_payment_method() && WCS_PayPal::are_reference_transactions_enabled() ) {
$is_billing_agreement = wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->id ), 'billing_agreement' ); $is_billing_agreement = wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->get_id() ), 'billing_agreement' );
if ( 'line_item' == $item['type'] && wcs_is_product_switchable_type( $item['product_id'] ) ) { if ( 'line_item' == $item['type'] && wcs_is_product_switchable_type( $item['product_id'] ) ) {
$is_product_switchable = true; $is_product_switchable = true;
@@ -66,7 +66,7 @@ class WCS_PayPal_Standard_Switcher {
$is_product_switchable = false; $is_product_switchable = false;
} }
if ( $subscription->has_status( 'active' ) && 0 !== $subscription->get_date( 'last_payment' ) ) { if ( $subscription->has_status( 'active' ) && 0 !== $subscription->get_date( 'last_order_date_created' ) ) {
$is_subscription_switchable = true; $is_subscription_switchable = true;
} else { } else {
$is_subscription_switchable = false; $is_subscription_switchable = false;
@@ -99,7 +99,7 @@ class WCS_PayPal_Standard_Switcher {
$subscription = wcs_get_subscription( $cart_switch_details['subscription_id'] ); $subscription = wcs_get_subscription( $cart_switch_details['subscription_id'] );
if ( 'paypal' === $subscription->payment_method && ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->id ), 'billing_agreement' ) ) { if ( 'paypal' === $subscription->get_payment_method() && ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->get_id() ), 'billing_agreement' ) ) {
$needs_payment = true; $needs_payment = true;
break; break;
} }
@@ -127,13 +127,16 @@ class WCS_PayPal_Standard_Switcher {
foreach ( wcs_get_subscriptions_for_switch_order( $order_id ) as $subscription ) { foreach ( wcs_get_subscriptions_for_switch_order( $order_id ) as $subscription ) {
if ( 'paypal' === $subscription->payment_method && $subscription->payment_method !== $order->payment_method && false === wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->id ), 'billing_agreement' ) ) { $order_payment_method = wcs_get_objects_property( $order, 'payment_method' );
if ( 'paypal' === $subscription->get_payment_method() && $subscription->get_payment_method() !== $order_payment_method && false === wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->get_id() ), 'billing_agreement' ) ) {
// Set the new payment method on the subscription // Set the new payment method on the subscription
$available_gateways = WC()->payment_gateways->get_available_payment_gateways(); $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
if ( isset( $available_gateways[ $order->payment_method ] ) ) { if ( isset( $available_gateways[ $order_payment_method ] ) ) {
$subscription->set_payment_method( $available_gateways[ $order->payment_method ] ); $subscription->set_payment_method( $available_gateways[ $order_payment_method ] );
$subscription->save();
} }
} }
} }
@@ -158,9 +161,9 @@ class WCS_PayPal_Standard_Switcher {
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
if ( 'paypal' === $subscription->payment_method ) { if ( 'paypal' === $subscription->get_payment_method() ) {
$paypal_id = wcs_get_paypal_id( $subscription->id ); $paypal_id = wcs_get_paypal_id( $subscription->get_id() );
if ( ! wcs_is_paypal_profile_a( $paypal_id, 'billing_agreement' ) ) { if ( ! wcs_is_paypal_profile_a( $paypal_id, 'billing_agreement' ) ) {
update_post_meta( $order_id, '_old_payment_method', 'paypal_standard' ); update_post_meta( $order_id, '_old_payment_method', 'paypal_standard' );
@@ -179,29 +182,29 @@ class WCS_PayPal_Standard_Switcher {
*/ */
public static function cancel_paypal_standard_after_switch( $order ) { public static function cancel_paypal_standard_after_switch( $order ) {
if ( 'paypal_standard' == get_post_meta( $order->id, '_old_payment_method', true ) ) { if ( 'paypal_standard' == get_post_meta( wcs_get_objects_property( $order, 'id' ), '_old_payment_method', true ) ) {
$old_profile_id = get_post_meta( $order->id, '_old_paypal_subscription_id', true ); $old_profile_id = get_post_meta( wcs_get_objects_property( $order, 'id' ), '_old_paypal_subscription_id', true );
if ( ! empty( $old_profile_id ) ) { if ( ! empty( $old_profile_id ) ) {
$subscriptions = wcs_get_subscriptions_for_order( $order->id, array( 'order_type' => 'switch' ) ); $subscriptions = wcs_get_subscriptions_for_order( wcs_get_objects_property( $order, 'id' ), array( 'order_type' => 'switch' ) );
foreach ( $subscriptions as $subscription ) { foreach ( $subscriptions as $subscription ) {
if ( ! wcs_is_paypal_profile_a( $old_profile_id, 'billing_agreement' ) ) { if ( ! wcs_is_paypal_profile_a( $old_profile_id, 'billing_agreement' ) ) {
$new_payment_method = $subscription->payment_method; $new_payment_method = $subscription->get_payment_method();
$new_profile_id = get_post_meta( $subscription->id, '_paypal_subscription_id', true ); // grab the current paypal subscription id in case it's a billing agreement $new_profile_id = get_post_meta( $subscription->get_id(), '_paypal_subscription_id', true ); // grab the current paypal subscription id in case it's a billing agreement
update_post_meta( $subscription->id, '_payment_method', 'paypal' ); update_post_meta( $subscription->get_id(), '_payment_method', 'paypal' );
update_post_meta( $subscription->id, '_paypal_subscription_id', $old_profile_id ); update_post_meta( $subscription->get_id(), '_paypal_subscription_id', $old_profile_id );
WCS_PayPal_Status_Manager::suspend_subscription( $subscription ); WCS_PayPal_Status_Manager::suspend_subscription( $subscription );
// restore payment meta to the new data // restore payment meta to the new data
update_post_meta( $subscription->id, '_payment_method', $new_payment_method ); update_post_meta( $subscription->get_id(), '_payment_method', $new_payment_method );
update_post_meta( $subscription->id, '_paypal_subscription_id', $new_profile_id ); update_post_meta( $subscription->get_id(), '_paypal_subscription_id', $new_profile_id );
} }
} }
} }

View File

@@ -38,7 +38,7 @@ class WCS_PayPal_Status_Manager extends WCS_PayPal {
* @since 2.0 * @since 2.0
*/ */
public static function cancel_subscription( $subscription ) { public static function cancel_subscription( $subscription ) {
if ( ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->id ), 'billing_agreement' ) && self::update_subscription_status( $subscription, 'Cancel' ) ) { if ( ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->get_id() ), 'billing_agreement' ) && self::update_subscription_status( $subscription, 'Cancel' ) ) {
$subscription->add_order_note( __( 'Subscription cancelled with PayPal', 'woocommerce-subscriptions' ) ); $subscription->add_order_note( __( 'Subscription cancelled with PayPal', 'woocommerce-subscriptions' ) );
} }
} }
@@ -49,7 +49,7 @@ class WCS_PayPal_Status_Manager extends WCS_PayPal {
* @since 2.0 * @since 2.0
*/ */
public static function suspend_subscription( $subscription ) { public static function suspend_subscription( $subscription ) {
if ( ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->id ), 'billing_agreement' ) && self::update_subscription_status( $subscription, 'Suspend' ) ) { if ( ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->get_id() ), 'billing_agreement' ) && self::update_subscription_status( $subscription, 'Suspend' ) ) {
$subscription->add_order_note( __( 'Subscription suspended with PayPal', 'woocommerce-subscriptions' ) ); $subscription->add_order_note( __( 'Subscription suspended with PayPal', 'woocommerce-subscriptions' ) );
} }
} }
@@ -62,7 +62,7 @@ class WCS_PayPal_Status_Manager extends WCS_PayPal {
* @since 2.0 * @since 2.0
*/ */
public static function reactivate_subscription( $subscription ) { public static function reactivate_subscription( $subscription ) {
if ( ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->id ), 'billing_agreement' ) && self::update_subscription_status( $subscription, 'Reactivate' ) ) { if ( ! wcs_is_paypal_profile_a( wcs_get_paypal_id( $subscription->get_id() ), 'billing_agreement' ) && self::update_subscription_status( $subscription, 'Reactivate' ) ) {
$subscription->add_order_note( __( 'Subscription reactivated with PayPal', 'woocommerce-subscriptions' ) ); $subscription->add_order_note( __( 'Subscription reactivated with PayPal', 'woocommerce-subscriptions' ) );
} }
} }
@@ -77,7 +77,7 @@ class WCS_PayPal_Status_Manager extends WCS_PayPal {
*/ */
public static function update_subscription_status( $subscription, $new_status ) { public static function update_subscription_status( $subscription, $new_status ) {
$profile_id = wcs_get_paypal_id( $subscription->id ); $profile_id = wcs_get_paypal_id( $subscription->get_id() );
if ( wcs_is_paypal_profile_a( $profile_id, 'billing_agreement' ) ) { if ( wcs_is_paypal_profile_a( $profile_id, 'billing_agreement' ) ) {
@@ -129,7 +129,7 @@ class WCS_PayPal_Status_Manager extends WCS_PayPal {
* @since 2.0 * @since 2.0
*/ */
public static function suspend_subscription_on_payment_changed( $status, $subscription ) { public static function suspend_subscription_on_payment_changed( $status, $subscription ) {
return ( 'paypal' == $subscription->payment_gateway->id ) ? 'on-hold' : $status; return ( 'paypal' == $subscription->get_payment_method() ) ? 'on-hold' : $status;
} }
} }

View File

@@ -78,9 +78,9 @@ class WCS_PayPal_Supports {
*/ */
public static function add_feature_support_for_subscription( $is_supported, $feature, $subscription ) { public static function add_feature_support_for_subscription( $is_supported, $feature, $subscription ) {
if ( 'paypal' === $subscription->payment_method && WCS_PayPal::are_credentials_set() ) { if ( 'paypal' === $subscription->get_payment_method() && WCS_PayPal::are_credentials_set() ) {
$paypal_profile_id = wcs_get_paypal_id( $subscription->id ); $paypal_profile_id = wcs_get_paypal_id( $subscription->get_id() );
$is_billing_agreement = wcs_is_paypal_profile_a( $paypal_profile_id, 'billing_agreement' ); $is_billing_agreement = wcs_is_paypal_profile_a( $paypal_profile_id, 'billing_agreement' );
if ( 'gateway_scheduled_payments' === $feature && $is_billing_agreement ) { if ( 'gateway_scheduled_payments' === $feature && $is_billing_agreement ) {

View File

@@ -20,13 +20,13 @@ if ( ! defined( 'ABSPATH' ) ) {
* @param int The ID of a WC_Order or WC_Subscription object * @param int The ID of a WC_Order or WC_Subscription object
* @since 2.0 * @since 2.0
*/ */
function wcs_get_paypal_id( $order_id ) { function wcs_get_paypal_id( $order ) {
if ( is_object( $order_id ) ) { if ( ! is_object( $order ) ) {
$order_id = $order_id->id; $order = wc_get_order( $order );
} }
return get_post_meta( $order_id, '_paypal_subscription_id', true ); return wcs_get_objects_property( $order, '_paypal_subscription_id' );
} }
/** /**
@@ -48,7 +48,7 @@ function wcs_set_paypal_id( $order, $paypal_subscription_id ) {
} }
} }
return update_post_meta( $order->id, '_paypal_subscription_id', $paypal_subscription_id ); wcs_set_objects_property( $order, 'paypal_subscription_id', $paypal_subscription_id );
} }
/** /**

View File

@@ -0,0 +1,79 @@
<?php
/**
* Simple Subscription Product Legacy Class
*
* Extends WC_Product_Subscription to provide compatibility methods when running WooCommerce < 3.0.
*
* @class WC_Product_Subscription_Legacy
* @package WooCommerce Subscriptions
* @category Class
* @since 2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Product_Subscription_Legacy extends WC_Product_Subscription {
var $subscription_price;
var $subscription_period;
var $subscription_period_interval;
var $subscription_length;
var $subscription_trial_length;
var $subscription_trial_period;
var $subscription_sign_up_fee;
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product ) {
parent::__construct( $product );
$this->product_type = 'subscription';
// Load all meta fields
$this->product_custom_fields = get_post_meta( $this->id );
// Convert selected subscription meta fields for easy access
if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) {
$this->subscription_price = $this->product_custom_fields['_subscription_price'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) {
$this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) {
$this->subscription_period = $this->product_custom_fields['_subscription_period'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) {
$this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) {
$this->subscription_length = $this->product_custom_fields['_subscription_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) {
$this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) {
$this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0];
}
$this->subscription_payment_sync_date = ( ! isset( $this->product_custom_fields['_subscription_payment_sync_date'][0] ) ) ? 0 : maybe_unserialize( $this->product_custom_fields['_subscription_payment_sync_date'][0] );
$this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0];
$this->subscription_limit = ( ! isset( $this->product_custom_fields['_subscription_limit'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_limit'][0];
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* Subscription Product Variation Legacy Class
*
* Extends WC_Product_Subscription_Variation to provide compatibility methods when running WooCommerce < 3.0.
*
* @class WC_Product_Subscription_Variation_Legacy
* @package WooCommerce Subscriptions
* @category Class
* @since 2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Product_Subscription_Variation_Legacy extends WC_Product_Subscription_Variation {
/**
* Set default array value for WC 3.0's data property.
* @var array
*/
protected $data = array();
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product, $args = array() ) {
parent::__construct( $product, $args = array() );
$this->parent_product_type = $this->product_type;
$this->product_type = 'subscription_variation';
$this->subscription_variation_level_meta_data = array(
'subscription_price' => 0,
'subscription_period' => '',
'subscription_period_interval' => 'day',
'subscription_length' => 0,
'subscription_trial_length' => 0,
'subscription_trial_period' => 'day',
'subscription_sign_up_fee' => 0,
'subscription_payment_sync_date' => 0,
);
$this->variation_level_meta_data = array_merge( $this->variation_level_meta_data, $this->subscription_variation_level_meta_data );
}
/* Copied from WC 2.6 WC_Product_Variation */
/**
* __isset function.
*
* @param mixed $key
* @return bool
*/
public function __isset( $key ) {
if ( in_array( $key, array( 'variation_data', 'variation_has_stock' ) ) ) {
return true;
} elseif ( in_array( $key, array_keys( $this->variation_level_meta_data ) ) ) {
return metadata_exists( 'post', $this->variation_id, '_' . $key );
} elseif ( in_array( $key, array_keys( $this->variation_inherited_meta_data ) ) ) {
return metadata_exists( 'post', $this->variation_id, '_' . $key ) || metadata_exists( 'post', $this->id, '_' . $key );
} else {
return metadata_exists( 'post', $this->id, '_' . $key );
}
}
/**
* Get method returns variation meta data if set, otherwise in most cases the data from the parent.
*
* We need to use the WC_Product_Variation's __get() method, not the one in WC_Product_Subscription_Variation,
* which handles deprecation notices.
*
* @param string $key
* @return mixed
*/
public function __get( $key ) {
return WC_Product_Variation::__get( $key );
}
/**
* Provide a WC 3.0 method for variations.
*
* WC < 3.0 products have a get_parent() method, but this is not equivalent to the get_parent_id() method
* introduced in WC 3.0, because it derives the parent from $this->post->post_parent, but for variations,
* $this->post refers to the parent variable object's post, so $this->post->post_parent will be 0 under
* normal circumstances. Becuase of that, we can rely on wcs_get_objects_property( $this, 'parent_id' )
* and define this get_parent_id() method for variations even when WC 3.0 is not active.
*
* @param string $key
* @return mixed
*/
public function get_parent_id() {
return $this->id; // When WC < 3.0 is active, the ID property is the parent variable product's ID
}
}

View File

@@ -0,0 +1,381 @@
<?php
/**
* Variable Subscription Product Legacy Class
*
* Extends WC_Product_Variable_Subscription to provide compatibility methods when running WooCommerce < 3.0.
*
* @class WC_Product_Variable_Subscription_Legacy
* @package WooCommerce Subscriptions
* @category Class
* @since 2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Product_Variable_Subscription_Legacy extends WC_Product_Variable_Subscription {
var $subscription_price;
var $subscription_period;
var $max_variation_period;
var $subscription_period_interval;
var $max_variation_period_interval;
var $product_type;
protected $prices_array;
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product ) {
parent::__construct( $product );
$this->parent_product_type = $this->product_type;
$this->product_type = 'variable-subscription';
// Load all meta fields
$this->product_custom_fields = get_post_meta( $this->id );
// Convert selected subscription meta fields for easy access
if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) {
$this->subscription_price = $this->product_custom_fields['_subscription_price'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) {
$this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) {
$this->subscription_period = $this->product_custom_fields['_subscription_period'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) {
$this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) {
$this->subscription_length = $this->product_custom_fields['_subscription_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) {
$this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) {
$this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0];
}
$this->subscription_payment_sync_date = 0;
$this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0];
$this->subscription_limit = ( ! isset( $this->product_custom_fields['_subscription_limit'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_limit'][0];
}
/**
* Get the min or max variation (active) price.
*
* This is a copy of WooCommerce < 2.4's get_variation_price() method, because 2.4.0 introduced a new
* transient caching system which assumes asort() on prices yields correct results for min/max prices
* (which it does for prices alone, but that's not the full story for subscription prices). Unfortunately,
* the new caching system is also hard to hook into so we'll just use the old system instead as the
* @see self::variable_product_sync() uses the old method also.
*
* @param string $min_or_max - min or max
* @param boolean $display Whether the value is going to be displayed
* @return string
*/
public function get_variation_price( $min_or_max = 'min', $display = false ) {
$variation_id = get_post_meta( $this->id, '_' . $min_or_max . '_price_variation_id', true );
if ( $display ) {
if ( $variation = wc_get_product( $variation_id ) ) {
if ( 'incl' == get_option( 'woocommerce_tax_display_shop' ) ) {
$price = wcs_get_price_including_tax( $variation );
} else {
$price = wcs_get_price_excluding_tax( $variation );
}
} else {
$price = '';
}
} else {
$price = get_post_meta( $variation_id, '_price', true );
}
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display );
}
/**
* Get an array of all sale and regular prices from all variations re-ordered after WC has done a standard sort, to reflect subscription terms.
* The first and last element for each price type is the least and most expensive, respectively.
*
* @see WC_Product_Variable::get_variation_prices()
* @param bool $include_taxes Should taxes be included in the prices.
* @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID.
* @since 2.2.0
*/
public function get_variation_prices( $display = false ) {
$price_hash = $this->get_price_hash( $this, $display );
$this->prices_array[ $price_hash ] = parent::get_variation_prices( $display );
$children = array_keys( $this->prices_array[ $price_hash ]['price'] );
sort( $children );
$min_max_data = wcs_get_min_max_variation_data( $this, $children );
$min_variation_id = $min_max_data['min']['variation_id'];
$max_variation_id = $min_max_data['max']['variation_id'];
// Reorder the variable price arrays to reflect the min and max values so that WooCommerce will find them in the correct order
foreach ( $this->prices_array as $price_hash => $prices ) {
// Loop over sale_price, regular_price & price values to update them on main array
foreach ( $prices as $price_key => $variation_prices ) {
$min_price = $prices[ $price_key ][ $min_variation_id ];
$max_price = $prices[ $price_key ][ $max_variation_id ];
unset( $prices[ $price_key ][ $min_variation_id ] );
unset( $prices[ $price_key ][ $max_variation_id ] );
// append the minimum variation and prepend the maximum variation
$prices[ $price_key ] = array( $min_variation_id => $min_price ) + $prices[ $price_key ];
$prices[ $price_key ] += array( $max_variation_id => $max_price );
$this->prices_array[ $price_hash ][ $price_key ] = $prices[ $price_key ];
}
}
$this->subscription_price = $min_max_data['min']['price'];
$this->subscription_period = $min_max_data['min']['period'];
$this->subscription_period_interval = $min_max_data['min']['interval'];
$this->max_variation_price = $min_max_data['max']['price'];
$this->max_variation_period = $min_max_data['max']['period'];
$this->max_variation_period_interval = $min_max_data['max']['interval'];
$this->min_variation_price = $min_max_data['min']['price'];
$this->min_variation_regular_price = $min_max_data['min']['regular_price'];
return $this->prices_array[ $price_hash ];
}
/**
* Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters.
* DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique.
*
* @since 2.2.0
* @param WC_Product
* @param bool $display Are prices for display? If so, taxes will be calculated.
* @return string
*/
protected function get_price_hash( $display = false ) {
global $wp_filter;
if ( $display ) {
$price_hash = array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() );
} else {
$price_hash = array( false );
}
$filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' );
foreach ( $filter_names as $filter_name ) {
if ( ! empty( $wp_filter[ $filter_name ] ) ) {
$price_hash[ $filter_name ] = array();
foreach ( $wp_filter[ $filter_name ] as $priority => $callbacks ) {
$price_hash[ $filter_name ][] = array_values( wp_list_pluck( $callbacks, 'function' ) );
}
}
}
$price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $this, $display ) ) );
return $price_hash;
}
/**
* Sync variable product prices with the childs lowest/highest prices.
*
* @access public
* @return void
*/
public function variable_product_sync( $product_id = '' ) {
WC_Product_Variable::variable_product_sync( $product_id );
$child_variation_ids = $this->get_children( true );
if ( $child_variation_ids ) {
$min_max_data = wcs_get_min_max_variation_data( $this, $child_variation_ids );
update_post_meta( $this->id, '_min_price_variation_id', $min_max_data['min']['variation_id'] );
update_post_meta( $this->id, '_max_price_variation_id', $min_max_data['max']['variation_id'] );
update_post_meta( $this->id, '_price', $min_max_data['min']['price'] );
update_post_meta( $this->id, '_min_variation_price', $min_max_data['min']['price'] );
update_post_meta( $this->id, '_max_variation_price', $min_max_data['max']['price'] );
update_post_meta( $this->id, '_min_variation_regular_price', $min_max_data['min']['regular_price'] );
update_post_meta( $this->id, '_max_variation_regular_price', $min_max_data['max']['regular_price'] );
update_post_meta( $this->id, '_min_variation_sale_price', $min_max_data['min']['sale_price'] );
update_post_meta( $this->id, '_max_variation_sale_price', $min_max_data['max']['sale_price'] );
update_post_meta( $this->id, '_min_variation_period', $min_max_data['min']['period'] );
update_post_meta( $this->id, '_max_variation_period', $min_max_data['max']['period'] );
update_post_meta( $this->id, '_min_variation_period_interval', $min_max_data['min']['interval'] );
update_post_meta( $this->id, '_max_variation_period_interval', $min_max_data['max']['interval'] );
update_post_meta( $this->id, '_subscription_price', $min_max_data['min']['price'] );
update_post_meta( $this->id, '_subscription_sign_up_fee', $min_max_data['subscription']['signup-fee'] );
update_post_meta( $this->id, '_subscription_period', $min_max_data['min']['period'] );
update_post_meta( $this->id, '_subscription_period_interval', $min_max_data['min']['interval'] );
update_post_meta( $this->id, '_subscription_trial_period', $min_max_data['subscription']['trial_period'] );
update_post_meta( $this->id, '_subscription_trial_length', $min_max_data['subscription']['trial_length'] );
update_post_meta( $this->id, '_subscription_length', $min_max_data['subscription']['length'] );
$this->subscription_price = $min_max_data['min']['price'];
$this->subscription_period = $min_max_data['min']['period'];
$this->subscription_period_interval = $min_max_data['min']['interval'];
$this->subscription_sign_up_fee = $min_max_data['subscription']['signup-fee'];
$this->subscription_trial_period = $min_max_data['subscription']['trial_period'];
$this->subscription_trial_length = $min_max_data['subscription']['trial_length'];
$this->subscription_length = $min_max_data['subscription']['length'];
if ( function_exists( 'wc_delete_product_transients' ) ) {
wc_delete_product_transients( $this->id );
} else {
WC()->clear_product_transients( $this->id );
}
} else { // No variations yet
$this->subscription_price = '';
$this->subscription_sign_up_fee = '';
$this->subscription_period = 'day';
$this->subscription_period_interval = 1;
$this->subscription_trial_period = 'day';
$this->subscription_trial_length = 1;
$this->subscription_length = 0;
}
}
/**
* Returns the price in html format.
*
* @access public
* @param string $price (default: '')
* @return string
*/
public function get_price_html( $price = '' ) {
if ( ! isset( $this->subscription_period ) || ! isset( $this->subscription_period_interval ) || ! isset( $this->max_variation_period ) || ! isset( $this->max_variation_period_interval ) ) {
$this->variable_product_sync();
}
// Only create the subscription price string when a price has been set
if ( $this->subscription_price !== '' ) {
$price = '';
if ( $this->is_on_sale() && isset( $this->min_variation_price ) && $this->min_variation_regular_price !== $this->get_price() ) {
if ( ! $this->min_variation_price || $this->min_variation_price !== $this->max_variation_price ) {
$price .= wcs_get_price_html_from_text( $this );
}
$variation_id = get_post_meta( $this->id, '_min_price_variation_id', true );
$variation = wc_get_product( $variation_id );
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
$sale_price_args = array( 'qty' => 1, 'price' => $variation->get_sale_price() );
$regular_price_args = array( 'qty' => 1, 'price' => $variation->get_regular_price() );
if ( 'incl' == $tax_display_mode ) {
$sale_price = wcs_get_price_including_tax( $variation, $sale_price_args );
$regular_price = wcs_get_price_including_tax( $variation, $regular_price_args );
} else {
$sale_price = wcs_get_price_excluding_tax( $variation, $sale_price_args );
$regular_price = wcs_get_price_excluding_tax( $variation, $regular_price_args );
}
$price .= $this->get_price_html_from_to( $regular_price, $sale_price );
} else {
if ( $this->min_variation_price !== $this->max_variation_price ) {
$price .= wcs_get_price_html_from_text( $this );
}
$price .= wc_price( $this->get_variation_price( 'min', true ) );
}
// Make sure the price contains "From:" when billing schedule differs between variations
if ( false === strpos( $price, wcs_get_price_html_from_text( $this ) ) ) {
if ( $this->subscription_period !== $this->max_variation_period ) {
$price = wcs_get_price_html_from_text( $this ) . $price;
} elseif ( $this->subscription_period_interval !== $this->max_variation_period_interval ) {
$price = wcs_get_price_html_from_text( $this ) . $price;
}
}
$price .= $this->get_price_suffix();
$price = WC_Subscriptions_Product::get_price_string( $this, array( 'price' => $price ) );
}
return apply_filters( 'woocommerce_variable_subscription_price_html', $price, $this );
}
/**
* Provide the WC_Data::get_meta() function when WC < 3.0 is active.
*
* @param string $meta_key
* @param bool $single
* @param string $context
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
*/
function get_meta( $meta_key = '', $single = true, $context = 'view' ) {
return get_post_meta( $this->get_id(), $meta_key, $single );
}
/**
* get_child function.
*
* @access public
* @param mixed $child_id
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
*/
public function get_child( $child_id ) {
return wc_get_product( $child_id, array(
'product_type' => 'Subscription_Variation',
'parent_id' => $this->id,
'parent' => $this,
) );
}
/**
* Get default attributes.
*
* @since 2.2.0
* @param string $context
* @return array
*/
public function get_default_attributes( $context = 'view' ) {
return $this->get_variation_default_attributes();
}
}

View File

@@ -0,0 +1,749 @@
<?php
/**
* Subscription Legacy Object
*
* Extends WC_Subscription to provide WC 3.0 methods when running WooCommerce < 3.0.
*
* @class WC_Subscription_Legacy
* @version 2.1
* @package WooCommerce Subscriptions/Classes
* @category Class
* @author Brent Shepherd
*/
class WC_Subscription_Legacy extends WC_Subscription {
protected $schedule;
protected $status_transition = false;
/**
* Whether the object has been read. Pre WC 3.0 subscription objects are always read by default.
* Provides an accessible variable equivalent to WC_Data::$object_read pre WC 3.0.
*
* @protected boolean
*/
protected $object_read = true;
/**
* Initialize the subscription object.
*
* @param int|WC_Subscription $order
*/
public function __construct( $subscription ) {
parent::__construct( $subscription );
$this->order_type = 'shop_subscription';
$this->schedule = new stdClass();
}
/**
* Populates a subscription from the loaded post data.
*
* @param mixed $result
*/
public function populate( $result ) {
parent::populate( $result );
if ( $this->post->post_parent > 0 ) {
$this->order = wc_get_order( $this->post->post_parent );
}
}
/**
* Returns the unique ID for this object.
*
* @return int
*/
public function get_id() {
return $this->id;
}
/**
* Get parent order ID.
*
* @since 2.2.0
* @return int
*/
public function get_parent_id() {
return $this->post->post_parent;
}
/**
* Gets order currency.
*
* @return string
*/
public function get_currency() {
return $this->get_order_currency();
}
/**
* Get customer_note.
*
* @param string $context
* @return string
*/
public function get_customer_note( $context = 'view' ) {
return $this->customer_note;
}
/**
* Get prices_include_tax.
*
* @param string $context
* @return bool
*/
public function get_prices_include_tax( $context = 'view' ) {
return $this->prices_include_tax;
}
/**
* Get the payment method.
*
* @param string $context
* @return string
*/
public function get_payment_method( $context = 'view' ) {
return $this->payment_method;
}
/**
* Get the payment method's title.
*
* @param string $context
* @return string
*/
public function get_payment_method_title( $context = 'view' ) {
return $this->payment_method_title;
}
/** Address Getters **/
/**
* Get billing_first_name.
*
* @param string $context
* @return string
*/
public function get_billing_first_name( $context = 'view' ) {
return $this->billing_first_name;
}
/**
* Get billing_last_name.
*
* @param string $context
* @return string
*/
public function get_billing_last_name( $context = 'view' ) {
return $this->billing_last_name;
}
/**
* Get billing_company.
*
* @param string $context
* @return string
*/
public function get_billing_company( $context = 'view' ) {
return $this->billing_company;
}
/**
* Get billing_address_1.
*
* @param string $context
* @return string
*/
public function get_billing_address_1( $context = 'view' ) {
return $this->billing_address_1;
}
/**
* Get billing_address_2.
*
* @param string $context
* @return string $value
*/
public function get_billing_address_2( $context = 'view' ) {
return $this->billing_address_2;
}
/**
* Get billing_city.
*
* @param string $context
* @return string $value
*/
public function get_billing_city( $context = 'view' ) {
return $this->billing_city;
}
/**
* Get billing_state.
*
* @param string $context
* @return string
*/
public function get_billing_state( $context = 'view' ) {
return $this->billing_state;
}
/**
* Get billing_postcode.
*
* @param string $context
* @return string
*/
public function get_billing_postcode( $context = 'view' ) {
return $this->billing_postcode;
}
/**
* Get billing_country.
*
* @param string $context
* @return string
*/
public function get_billing_country( $context = 'view' ) {
return $this->billing_country;
}
/**
* Get billing_email.
*
* @param string $context
* @return string
*/
public function get_billing_email( $context = 'view' ) {
return $this->billing_email;
}
/**
* Get billing_phone.
*
* @param string $context
* @return string
*/
public function get_billing_phone( $context = 'view' ) {
return $this->billing_phone;
}
/**
* Get shipping_first_name.
*
* @param string $context
* @return string
*/
public function get_shipping_first_name( $context = 'view' ) {
return $this->shipping_first_name;
}
/**
* Get shipping_last_name.
*
* @param string $context
* @return string
*/
public function get_shipping_last_name( $context = 'view' ) {
return $this->shipping_last_name;
}
/**
* Get shipping_company.
*
* @param string $context
* @return string
*/
public function get_shipping_company( $context = 'view' ) {
return $this->shipping_company;
}
/**
* Get shipping_address_1.
*
* @param string $context
* @return string
*/
public function get_shipping_address_1( $context = 'view' ) {
return $this->shipping_address_1;
}
/**
* Get shipping_address_2.
*
* @param string $context
* @return string
*/
public function get_shipping_address_2( $context = 'view' ) {
return $this->shipping_address_2;
}
/**
* Get shipping_city.
*
* @param string $context
* @return string
*/
public function get_shipping_city( $context = 'view' ) {
return $this->shipping_city;
}
/**
* Get shipping_state.
*
* @param string $context
* @return string
*/
public function get_shipping_state( $context = 'view' ) {
return $this->shipping_state;
}
/**
* Get shipping_postcode.
*
* @param string $context
* @return string
*/
public function get_shipping_postcode( $context = 'view' ) {
return $this->shipping_postcode;
}
/**
* Get shipping_country.
*
* @param string $context
* @return string
*/
public function get_shipping_country( $context = 'view' ) {
return $this->shipping_country;
}
/**
* Get order key.
*
* @since 2.2.0
* @param string $context
* @return string
*/
public function get_order_key( $context = 'view' ) {
return $this->order_key;
}
/**
* Get date_created.
*
* Used by parent::get_date()
*
* @throws WC_Data_Exception
* @return DateTime|NULL object if the date is set or null if there is no date.
*/
public function get_date_created( $context = 'view' ) {
if ( '0000-00-00 00:00:00' != $this->post->post_date_gmt ) {
$datetime = new WC_DateTime( $this->post->post_date_gmt, new DateTimeZone( 'UTC' ) );
$datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
} else {
$datetime = new WC_DateTime( $this->post->post_date, new DateTimeZone( wc_timezone_string() ) );
}
// Cache it in $this->schedule for backward compatibility
if ( ! isset( $this->schedule->start ) ) {
$this->schedule->start = wcs_get_datetime_utc_string( $datetime );
}
return $datetime;
}
/**
* Get date_modified.
*
* Used by parent::get_date()
*
* @throws WC_Data_Exception
* @return DateTime|NULL object if the date is set or null if there is no date.
*/
public function get_date_modified( $context = 'view' ) {
if ( '0000-00-00 00:00:00' != $this->post->post_modified_gmt ) {
$datetime = new WC_DateTime( $this->post->post_modified_gmt, new DateTimeZone( 'UTC' ) );
$datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
} else {
$datetime = new WC_DateTime( $this->post->post_modified, new DateTimeZone( wc_timezone_string() ) );
}
return $datetime;
}
/**
* Check if a given line item on the subscription had a sign-up fee, and if so, return the value of the sign-up fee.
*
* The single quantity sign-up fee will be returned instead of the total sign-up fee paid. For example, if 3 x a product
* with a 10 BTC sign-up fee was purchased, a total 30 BTC was paid as the sign-up fee but this function will return 10 BTC.
*
* @param array|int Either an order item (in the array format returned by self::get_items()) or the ID of an order item.
* @param string $tax_inclusive_or_exclusive Whether or not to adjust sign up fee if prices inc tax - ensures that the sign up fee paid amount includes the paid tax if inc
* @return bool
* @since 2.0
*/
public function get_items_sign_up_fee( $line_item, $tax_inclusive_or_exclusive = 'exclusive_of_tax' ) {
if ( ! is_array( $line_item ) ) {
$line_item = wcs_get_order_item( $line_item, $this );
}
$parent_order = $this->get_parent();
// If there was no original order, nothing was paid up-front which means no sign-up fee
if ( false == $parent_order ) {
$sign_up_fee = 0;
} else {
$original_order_item = '';
// Find the matching item on the order
foreach ( $parent_order->get_items() as $order_item ) {
if ( wcs_get_canonical_product_id( $line_item ) == wcs_get_canonical_product_id( $order_item ) ) {
$original_order_item = $order_item;
break;
}
}
// No matching order item, so this item wasn't purchased in the original order
if ( empty( $original_order_item ) ) {
$sign_up_fee = 0;
} elseif ( isset( $line_item['item_meta']['_has_trial'] ) ) {
// Sign up is total amount paid for this item on original order when item has a free trial
$sign_up_fee = $original_order_item['line_total'] / $original_order_item['qty'];
} else {
// Sign-up fee is any amount on top of recurring amount
$sign_up_fee = max( $original_order_item['line_total'] / $original_order_item['qty'] - $line_item['line_total'] / $line_item['qty'], 0 );
}
// If prices inc tax, ensure that the sign up fee amount includes the tax
if ( 'inclusive_of_tax' === $tax_inclusive_or_exclusive && ! empty( $original_order_item ) && $this->get_prices_include_tax() ) {
$proportion = $sign_up_fee / ( $original_order_item['line_total'] / $original_order_item['qty'] );
$sign_up_fee += round( $original_order_item['line_tax'] * $proportion, 2 );
}
}
return apply_filters( 'woocommerce_subscription_items_sign_up_fee', $sign_up_fee, $line_item, $this, $tax_inclusive_or_exclusive );
}
/**
* Helper function to make sure when WC_Subscription calls get_prop() from
* it's new getters that the property is both retreived from the legacy class
* property and done so from post meta.
*
* For inherited dates props, like date_created, date_modified, date_paid,
* date_completed, we want to use our own get_date() function rather simply
* getting the stored value. Otherwise, we either get the prop set in memory
* or post meta if it's not set yet, because __get() in WC < 3.0 would fallback
* to post meta.
*
* @param string
* @param string
* @return mixed
*/
protected function get_prop( $prop, $context = 'view' ) {
if ( 'switch_data' == $prop ) {
$prop = 'subscription_switch_data';
}
// The requires manual renewal prop uses boolean values but is stored as a string so needs special handling, it also needs to be handled before the checks on $this->$prop to avoid triggering __isset() & __get() magic methods for $this->requires_manual_renewal
if ( 'requires_manual_renewal' === $prop ) {
$value = get_post_meta( $this->get_id(), '_' . $prop, true );
if ( 'false' === $value || '' === $value ) {
$value = false;
} else {
$value = true;
}
} elseif ( ! isset( $this->$prop ) || empty( $this->$prop ) ) {
$value = get_post_meta( $this->get_id(), '_' . $prop, true );
} else {
$value = $this->$prop;
}
return $value;
}
/**
* Get the stored date for a specific schedule.
*
* @param string $date_type 'date_created', 'trial_end', 'next_payment', 'last_order_date_created' or 'end'
*/
protected function get_date_prop( $date_type ) {
$datetime = parent::get_date_prop( $date_type );
// Cache the string equalivent of it in $this->schedule for backward compatibility
if ( ! isset( $this->schedule->{$date_type} ) ) {
if ( ! is_object( $datetime ) ) {
$this->schedule->{$date_type} = 0;
} else {
$this->schedule->{$date_type} = wcs_get_datetime_utc_string( $datetime );
}
}
return wcs_get_datetime_from( wcs_date_to_time( $datetime ) );
}
/*** Setters *****************************************************/
/**
* Set the unique ID for this object.
*
* @param int
*/
public function set_id( $id ) {
$this->id = absint( $id );
}
/**
* Set parent order ID. We don't use WC_Abstract_Order::set_parent_id() because we want to allow false
* parent IDs, like 0.
*
* @since 2.2.0
* @param int $value
*/
public function set_parent_id( $value ) {
// Update the parent in the database
wp_update_post( array(
'ID' => $this->id,
'post_parent' => $value,
) );
// And update the parent in memory
$this->post->post_parent = $value;
$this->order = null;
}
/**
* Set subscription status.
*
* @param string $new_status Status to change the order to. No internal wc- prefix is required.
* @return array details of change
*/
public function set_status( $new_status, $note = '', $manual_update = false ) {
$old_status = $this->get_status();
$new_status = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
wp_update_post( array( 'ID' => $this->get_id(), 'post_status' => wcs_maybe_prefix_key( $new_status, 'wc-' ) ) );
$this->post_status = $this->post->post_status = wcs_maybe_prefix_key( $new_status, 'wc-' );
if ( $old_status !== $new_status ) {
$this->status_transition = array(
'from' => ! empty( $this->status_transition['from'] ) ? $this->status_transition['from'] : $old_status,
'to' => $new_status,
'note' => $note,
'manual' => (bool) $manual_update,
);
}
return array(
'from' => $old_status,
'to' => $new_status,
);
}
/**
* Helper function to make sure when WC_Subscription calls set_prop() that property is
* both set in the legacy class property and saved in post meta immediately.
*
* @param string $prop
* @param mixed $value
*/
protected function set_prop( $prop, $value ) {
if ( 'switch_data' == $prop ) {
$prop = 'subscription_switch_data';
}
$this->$prop = $value;
// The requires manual renewal prop uses boolean values but it stored as a string
if ( 'requires_manual_renewal' === $prop ) {
if ( false === $value || '' === $value ) {
$value = 'false';
} else {
$value = 'true';
}
}
update_post_meta( $this->get_id(), '_' . $prop, $value );
}
/**
* Set the stored date for a specific schedule.
*
* @param string $date_type 'trial_end', 'next_payment', 'cancelled', 'payment_retry' or 'end'
* @param int $value UTC timestamp
*/
protected function set_date_prop( $date_type, $value ) {
$datetime = wcs_get_datetime_from( $value );
$date = ! is_null( $datetime ) ? wcs_get_datetime_utc_string( $datetime ) : 0;
$this->set_prop( $this->get_date_prop_key( $date_type ), $date );
$this->schedule->{$date_type} = $date;
}
/**
* Set a certain date type for the last order on the subscription.
*
* @since 2.2.0
* @param string $date_type
* @param string|integer|object
* @return WC_DateTime|NULL object if the date is set or null if there is no date.
*/
protected function set_last_order_date( $date_type, $date = null ) {
$last_order = $this->get_last_order( 'all' );
if ( $last_order ) {
$datetime = wcs_get_datetime_from( $date );
switch ( $date_type ) {
case 'date_paid' :
update_post_meta( $last_order->id, '_paid_date', ! is_null( $date ) ? $datetime->date( 'Y-m-d H:i:s' ) : '' );
// Preemptively set the UTC timestamp for WC 3.0+ also to avoid incorrect values when the site's timezone is changed between now and upgrading to WC 3.0
update_post_meta( $last_order->id, '_date_paid', ! is_null( $date ) ? $datetime->getTimestamp() : '' );
break;
case 'date_completed' :
update_post_meta( $last_order->id, '_completed_date', ! is_null( $date ) ? $datetime->date( 'Y-m-d H:i:s' ) : '' );
// Preemptively set the UTC timestamp for WC 3.0+ also to avoid incorrect values when the site's timezone is changed between now and upgrading to WC 3.0
update_post_meta( $last_order->id, '_date_completed', ! is_null( $date ) ? $datetime->getTimestamp() : '' );
break;
case 'date_modified' :
wp_update_post( array(
'ID' => $last_order->id,
'post_modified' => $datetime->date( 'Y-m-d H:i:s' ),
'post_modified_gmt' => wcs_get_datetime_utc_string( $datetime ),
) );
break;
case 'date_created' :
wp_update_post( array(
'ID' => $last_order->id,
'post_date' => $datetime->date( 'Y-m-d H:i:s' ),
'post_date_gmt' => wcs_get_datetime_utc_string( $datetime ),
) );
break;
}
}
}
/**
* Set date_created.
*
* Used by parent::update_dates()
*
* @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
* @throws WC_Data_Exception
*/
public function set_date_created( $date = null ) {
global $wpdb;
if ( ! is_null( $date ) ) {
$datetime_string = wcs_get_datetime_utc_string( wcs_get_datetime_from( $date ) );
// Don't use wp_update_post() to avoid infinite loops here
$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_date = %s, post_date_gmt = %s WHERE ID = %d", get_date_from_gmt( $datetime_string ), $datetime_string, $this->get_id() ) );
$this->post->post_date = get_date_from_gmt( $datetime_string );
$this->post->post_date_gmt = $datetime_string;
}
}
/**
* Set discount_total.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_discount_total( $value ) {
$this->set_total( $value, 'cart_discount' );
}
/**
* Set discount_tax.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_discount_tax( $value ) {
$this->set_total( $value, 'cart_discount_tax' );
}
/**
* Set shipping_total.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_shipping_total( $value ) {
$this->set_total( $value, 'shipping' );
}
/**
* Set shipping_tax.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_shipping_tax( $value ) {
$this->set_total( $value, 'shipping_tax' );
}
/**
* Set cart tax.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_cart_tax( $value ) {
$this->set_total( $value, 'tax' );
}
/**
* Save data to the database. Nothing to do here as it's all done separately when calling @see this->set_prop().
*
* @return int order ID
*/
public function save() {
$this->status_transition();
return $this->get_id();
}
/**
* Update meta data by key or ID, if provided.
*
* @since 2.2.0
* @param string $key
* @param string $value
* @param int $meta_id
*/
public function update_meta_data( $key, $value, $meta_id = '' ) {
if ( ! empty( $meta_id ) ) {
update_metadata_by_mid( 'post', $meta_id, $value, $key );
} else {
update_post_meta( $this->get_id(), $key, $value );
}
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* There is "magic" in PHP, and then there is this.
*
* Story time: once upon a time, in a land not too far away, WooCommerce 3.0 deprecated accessing
* all properties on objects. A conventicle of wizards known as __get(), __set() and __isset() came
* together to make sure that properties on Subscriptions products could still be used, despite not being
* accessible. However, a dark cloud hung over properties which were arrays. None of the conventicle
* new of magic powerful enough to deal with such a problem. Enter Cesar, who summoned the dark arts
* to call upon the ArrayAccess incantation.
*
* In other words, this class is used to access specific items on an array from within the magic methods
* of other objects, like WC_Product_Subscription_Variation::__get() for the property formerly known as
* $subscription_variation_level_meta_data.
*
* @package WooCommerce Subscriptions
* @category Class
* @since 2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WCS_Array_Property_Post_Meta_Black_Magic implements ArrayAccess {
/**
* Store the ID this class is being used against so that we use it for post meta calls.
*/
protected $product_id;
/**
* Constructor
*
* @access public
* @param mixed $product
*/
public function __construct( $product_id ) {
$this->product_id = $product_id;
}
/**
* offsetGet
* @param string $key
* @return mixed
*/
public function offsetGet( $key ) {
return get_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ) );
}
/**
* offsetSet
* @param string $key
* @param mixed $value
*/
public function offsetSet( $key, $value ) {
update_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ), $value );
}
/**
* offsetExists
* @param string $key
* @return bool
*/
public function offsetExists( $key ) {
return metadata_exists( 'post', $this->product_id, $this->maybe_prefix_meta_key( $key ) );
}
/**
* Nothing to do here as we access post meta directly.
*/
public function offsetUnset( $key ) {
}
/**
* We only work with post meta data that has meta keys prefixed with an underscore, so
* add a prefix if it is not already set.
*/
protected function maybe_prefix_meta_key( $key ) {
if ( '_' != substr( $key, 0, 1 ) ) {
$key = '_' . $key;
}
return $key;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Legacy Subscription Product Handler
*
* Ensures subscription products work with versions of WooCommerce prior to 3.0 by loading
* legacy classes to provide CRUD methods only added with WC 3.0.
*
* @package WooCommerce Subscriptions
* @subpackage WCS_Product_Legacy
* @category Class
* @since 2.2.0
*/
class WCS_Product_Legacy {
/**
* Set up the class, including it's hooks & filters, when the file is loaded.
*
* @since 2.2.0
**/
public static function init() {
// Use our legacy product classes when WC 3.0+ is not active
add_filter( 'woocommerce_product_class', __CLASS__ . '::set_product_class', 100, 4 );
}
/**
* Use legacy classes for WC < 3.0
*
* @return string $classname The name of the WC_Product_* class which should be instantiated to create an instance of this product.
* @since 2.2.0
*/
public static function set_product_class( $classname, $product_type, $post_type, $product_id ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) && in_array( $classname, array( 'WC_Product_Subscription', 'WC_Product_Variable_Subscription', 'WC_Product_Subscription_Variation' ) ) ) {
$classname .= '_Legacy';
}
return $classname;
}
}
WCS_Product_Legacy::init();

View File

@@ -0,0 +1,73 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( class_exists( 'WC_DateTime' ) ) {
return;
}
/**
* WC Wrapper for PHP DateTime.
*
* @class WC_DateTime
* @since 3.0.0
* @package WooCommerce/Classes
* @category Class
* @author WooThemes
*/
class WC_DateTime extends DateTime {
/**
* Output an ISO 8601 date string in local timezone.
*
* @since 3.0.0
* @return string
*/
public function __toString() {
return $this->format( DATE_ATOM );
}
/**
* Missing in PHP 5.2.
*
* @since 3.0.0
* @return int
*/
public function getTimestamp() {
return method_exists( 'DateTime', 'getTimestamp' ) ? parent::getTimestamp() : $this->format( 'U' );
}
/**
* Get the timestamp with the WordPress timezone offset added or subtracted.
*
* @since 3.0.0
* @return int
*/
public function getOffsetTimestamp() {
return $this->getTimestamp() + $this->getOffset();
}
/**
* Format a date based on the offset timestamp.
*
* @since 3.0.0
* @param string $format
* @return string
*/
public function date( $format ) {
return gmdate( $format, $this->getOffsetTimestamp() );
}
/**
* Return a localised date based on offset timestamp. Wrapper for date_i18n function.
*
* @since 3.0.0
* @param string $format
* @return string
*/
public function date_i18n( $format = 'Y-m-d' ) {
return date_i18n( $format, $this->getOffsetTimestamp() );
}
}

View File

@@ -68,7 +68,7 @@ class WCS_Retry_Email {
$email_class = $retry_rule->get_email_template( $recipient ); $email_class = $retry_rule->get_email_template( $recipient );
if ( class_exists( $email_class ) ) { if ( class_exists( $email_class ) ) {
$email = new $email_class(); $email = new $email_class();
$email->trigger( $last_order ); $email->trigger( wcs_get_objects_property( $last_order, 'id' ), $last_order );
} }
} }
} }
@@ -89,8 +89,8 @@ class WCS_Retry_Email {
remove_action( 'woocommerce_order_status_failed', 'WC_Subscriptions_Email::send_renewal_order_email', 10 ); remove_action( 'woocommerce_order_status_failed', 'WC_Subscriptions_Email::send_renewal_order_email', 10 );
// Remove email sent to admin, which is sent by WooCommerce // Remove email sent to admin, which is sent by WooCommerce
remove_action( 'woocommerce_order_status_pending_to_failed', array( 'WC_Emails', 'send_transactional_email' ), 10, 10 ); WC_Subscriptions_Email::detach_woocommerce_transactional_email( 'woocommerce_order_status_pending_to_failed' );
remove_action( 'woocommerce_order_status_on-hold_to_failed', array( 'WC_Emails', 'send_transactional_email' ), 10, 10 ); WC_Subscriptions_Email::detach_woocommerce_transactional_email( 'woocommerce_order_status_on-hold_to_failed' );
self::$removed_emails_for_order_id = $order_id; self::$removed_emails_for_order_id = $order_id;
} }
@@ -109,8 +109,8 @@ class WCS_Retry_Email {
add_action( 'woocommerce_order_status_failed', 'WC_Subscriptions_Email::send_renewal_order_email' ); add_action( 'woocommerce_order_status_failed', 'WC_Subscriptions_Email::send_renewal_order_email' );
// Reattach email sent to admin, which is sent by WooCommerce // Reattach email sent to admin, which is sent by WooCommerce
add_action( 'woocommerce_order_status_pending_to_failed', array( 'WC_Emails', 'send_transactional_email' ), 10, 10 ); WC_Subscriptions_Email::attach_woocommerce_transactional_email( 'woocommerce_order_status_pending_to_failed' );
add_action( 'woocommerce_order_status_on-hold_to_failed', array( 'WC_Emails', 'send_transactional_email' ), 10, 10 ); WC_Subscriptions_Email::attach_woocommerce_transactional_email( 'woocommerce_order_status_on-hold_to_failed' );
self::$removed_emails_for_order_id = null; self::$removed_emails_for_order_id = null;
} }

View File

@@ -209,7 +209,7 @@ class WC_Subscriptions_Upgrader {
* @since 2.1 * @since 2.1
*/ */
public static function maybe_redirect_after_upgrade_complete( $current_version, $previously_active_version ) { public static function maybe_redirect_after_upgrade_complete( $current_version, $previously_active_version ) {
if ( version_compare( $previously_active_version, '2.1.0', '<' ) && version_compare( $current_version, '2.1.0', '>=' ) ) { if ( version_compare( $previously_active_version, '2.1.0', '<' ) && version_compare( $current_version, '2.1.0', '>=' ) && version_compare( $current_version, '2.2.0', '<' ) ) {
wp_safe_redirect( self::$about_page_url ); wp_safe_redirect( self::$about_page_url );
exit(); exit();
} }

View File

@@ -58,11 +58,11 @@ class WCS_Repair_2_0_2 {
$subscription = wcs_get_subscription( $subscription_id ); $subscription = wcs_get_subscription( $subscription_id );
if ( false !== $subscription && self::maybe_repair_subscription( $subscription ) ) { if ( false !== $subscription && self::maybe_repair_subscription( $subscription ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair completed', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair completed', $subscription->get_id() ) );
$repaired_count++; $repaired_count++;
update_post_meta( $subscription_id, '_wcs_repaired_2_0_2', 'true' ); update_post_meta( $subscription_id, '_wcs_repaired_2_0_2', 'true' );
} else { } else {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no repair needed', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no repair needed', $subscription->get_id() ) );
$unrepaired_count++; $unrepaired_count++;
update_post_meta( $subscription_id, '_wcs_repaired_2_0_2', 'false' ); update_post_meta( $subscription_id, '_wcs_repaired_2_0_2', 'false' );
} }
@@ -87,10 +87,11 @@ class WCS_Repair_2_0_2 {
protected static function maybe_repair_subscription( $subscription ) { protected static function maybe_repair_subscription( $subscription ) {
$repaired_subscription = false; $repaired_subscription = false;
$parent_order = $subscription->get_parent();
// if the subscription doesn't have an order, it must have been created in 2.0, so we can ignore it // if the subscription doesn't have an order, it must have been created in 2.0, so we can ignore it
if ( false === $subscription->order ) { if ( false === $parent_order ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: it has no order.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: it has no order.', $subscription->get_id() ) );
return $repaired_subscription; return $repaired_subscription;
} }
@@ -98,7 +99,7 @@ class WCS_Repair_2_0_2 {
// if the subscription has more than one line item, it must have been created in 2.0, so we can ignore it // if the subscription has more than one line item, it must have been created in 2.0, so we can ignore it
if ( count( $subscription_line_items ) > 1 ) { if ( count( $subscription_line_items ) > 1 ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: it has more than one line item.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: it has more than one line item.', $subscription->get_id() ) );
return $repaired_subscription; return $repaired_subscription;
} }
@@ -106,7 +107,7 @@ class WCS_Repair_2_0_2 {
$subscription_line_item = array_shift( $subscription_line_items ); $subscription_line_item = array_shift( $subscription_line_items );
// Get old order item's meta // Get old order item's meta
foreach ( $subscription->order->get_items() as $line_item_id => $line_item ) { foreach ( $parent_order->get_items() as $line_item_id => $line_item ) {
if ( wcs_get_canonical_product_id( $line_item ) == wcs_get_canonical_product_id( $subscription_line_item ) ) { if ( wcs_get_canonical_product_id( $line_item ) == wcs_get_canonical_product_id( $subscription_line_item ) ) {
$matching_line_item_id = $line_item_id; $matching_line_item_id = $line_item_id;
$matching_line_item = $line_item; $matching_line_item = $line_item;
@@ -116,7 +117,7 @@ class WCS_Repair_2_0_2 {
// we couldn't find a matching line item so we can't repair it // we couldn't find a matching line item so we can't repair it
if ( ! isset( $matching_line_item ) ) { if ( ! isset( $matching_line_item ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: can not repair: it has no matching line item.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: can not repair: it has no matching line item.', $subscription->get_id() ) );
return $repaired_subscription; return $repaired_subscription;
} }
@@ -124,20 +125,20 @@ class WCS_Repair_2_0_2 {
// if the order item doesn't have migrated subscription data, the subscription wasn't migrated from 1.5 // if the order item doesn't have migrated subscription data, the subscription wasn't migrated from 1.5
if ( ! isset( $matching_line_item_meta['_wcs_migrated_subscription_status'] ) && ! isset( $matching_line_item_meta['_wcs_migrated_subscription_start_date'] ) ) { if ( ! isset( $matching_line_item_meta['_wcs_migrated_subscription_status'] ) && ! isset( $matching_line_item_meta['_wcs_migrated_subscription_start_date'] ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: matching line item has no migrated meta data.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: matching line item has no migrated meta data.', $subscription->get_id() ) );
return $repaired_subscription; return $repaired_subscription;
} }
if ( false !== self::maybe_repair_line_tax_data( $subscription_line_item_id, $matching_line_item_id, $matching_line_item ) ) { if ( false !== self::maybe_repair_line_tax_data( $subscription_line_item_id, $matching_line_item_id, $matching_line_item ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired missing line tax data.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired missing line tax data.', $subscription->get_id() ) );
$repaired_subscription = true; $repaired_subscription = true;
} else { } else {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: line tax data not added.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: line tax data not added.', $subscription->get_id() ) );
} }
// if the subscription has been cancelled, we don't need to repair any other data // if the subscription has been cancelled, we don't need to repair any other data
if ( $subscription->has_status( array( 'pending-cancel', 'cancelled' ) ) ) { if ( $subscription->has_status( array( 'pending-cancel', 'cancelled' ) ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: it has cancelled status.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair: it has cancelled status.', $subscription->get_id() ) );
return $repaired_subscription; return $repaired_subscription;
} }
@@ -157,38 +158,38 @@ class WCS_Repair_2_0_2 {
if ( ! empty( $dates_to_update ) ) { if ( ! empty( $dates_to_update ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repairing dates = %s', $subscription->id, str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repairing dates = %s', $subscription->get_id(), str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ) ) );
try { try {
$subscription->update_dates( $dates_to_update ); $subscription->update_dates( $dates_to_update );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired dates = %s', $subscription->id, str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired dates = %s', $subscription->get_id(), str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ) ) );
} catch ( Exception $e ) { } catch ( Exception $e ) {
WCS_Upgrade_Logger::add( sprintf( '!! For subscription %d: unable to repair dates (%s), exception "%s"', $subscription->id, str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ), $e->getMessage() ) ); WCS_Upgrade_Logger::add( sprintf( '!! For subscription %d: unable to repair dates (%s), exception "%s"', $subscription->get_id(), str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ), $e->getMessage() ) );
} }
try { try {
self::maybe_repair_status( $subscription, $matching_line_item_meta, $dates_to_update ); self::maybe_repair_status( $subscription, $matching_line_item_meta, $dates_to_update );
} catch ( Exception $e ) { } catch ( Exception $e ) {
WCS_Upgrade_Logger::add( sprintf( '!! For subscription %d: unable to repair status. Exception: "%s"', $subscription->id, $e->getMessage() ) ); WCS_Upgrade_Logger::add( sprintf( '!! For subscription %d: unable to repair status. Exception: "%s"', $subscription->get_id(), $e->getMessage() ) );
} }
$repaired_subscription = true; $repaired_subscription = true;
} }
if ( ! empty( $subscription->order->customer_note ) && empty( $subscription->customer_note ) ) { if ( '' !== wcs_get_objects_property( $parent_order, 'customer_note' ) && '' == $subscription->get_customer_note() ) {
$post_data = array( $post_data = array(
'ID' => $subscription->id, 'ID' => $subscription->get_id(),
'post_excerpt' => $subscription->order->customer_note, 'post_excerpt' => wcs_get_objects_property( $parent_order, 'customer_note' ),
); );
$updated_post_id = wp_update_post( $post_data, true ); $updated_post_id = wp_update_post( $post_data, true );
if ( ! is_wp_error( $updated_post_id ) ) { if ( ! is_wp_error( $updated_post_id ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired missing customer note.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired missing customer note.', $subscription->get_id() ) );
$repaired_subscription = true; $repaired_subscription = true;
} else { } else {
WCS_Upgrade_Logger::add( sprintf( '!! For subscription %d: unable to repair missing customer note. Exception: "%s"', $subscription->id, $updated_post_id->get_error_message() ) ); WCS_Upgrade_Logger::add( sprintf( '!! For subscription %d: unable to repair missing customer note. Exception: "%s"', $subscription->get_id(), $updated_post_id->get_error_message() ) );
} }
} }
@@ -211,8 +212,8 @@ class WCS_Repair_2_0_2 {
$old_trial_end_date = isset( $former_order_item_meta['_wcs_migrated_subscription_trial_expiry_date'][0] ) ? $former_order_item_meta['_wcs_migrated_subscription_trial_expiry_date'][0] : 0; $old_trial_end_date = isset( $former_order_item_meta['_wcs_migrated_subscription_trial_expiry_date'][0] ) ? $former_order_item_meta['_wcs_migrated_subscription_trial_expiry_date'][0] : 0;
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new trial end date = %s.', $subscription->id, var_export( $subscription->get_date( 'trial_end' ), true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new trial end date = %s.', $subscription->get_id(), var_export( $subscription->get_date( 'trial_end' ), true ) ) );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old trial end date = %s.', $subscription->id, var_export( $old_trial_end_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old trial end date = %s.', $subscription->get_id(), var_export( $old_trial_end_date, true ) ) );
// if the subscription has a trial end time whereas previously it didn't, we need it to be deleted // if the subscription has a trial end time whereas previously it didn't, we need it to be deleted
if ( 0 == $old_trial_end_date ) { if ( 0 == $old_trial_end_date ) {
@@ -224,7 +225,7 @@ class WCS_Repair_2_0_2 {
$repair_date = false; $repair_date = false;
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair trial end date = %s.', $subscription->id, var_export( $repair_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair trial end date = %s.', $subscription->get_id(), var_export( $repair_date, true ) ) );
return $repair_date; return $repair_date;
} }
@@ -264,8 +265,8 @@ class WCS_Repair_2_0_2 {
wcs_json_encode( $old_hook_args ) wcs_json_encode( $old_hook_args )
) ); ) );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new next payment date = %s.', $subscription->id, var_export( $subscription->get_date( 'next_payment' ), true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new next payment date = %s.', $subscription->get_id(), var_export( $subscription->get_date( 'next_payment' ), true ) ) );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old next payment date = %s.', $subscription->id, var_export( $old_next_payment_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old next payment date = %s.', $subscription->get_id(), var_export( $old_next_payment_date, true ) ) );
// if we have a date, make sure it's valid // if we have a date, make sure it's valid
if ( null !== $old_next_payment_date ) { if ( null !== $old_next_payment_date ) {
@@ -274,7 +275,7 @@ class WCS_Repair_2_0_2 {
if ( 0 == $repair_date ) { if ( 0 == $repair_date ) {
$repair_date = false; $repair_date = false;
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old next payment date is in the past, setting it to %s.', $subscription->id, var_export( $repair_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old next payment date is in the past, setting it to %s.', $subscription->get_id(), var_export( $repair_date, true ) ) );
} else { } else {
$repair_date = $old_next_payment_date; $repair_date = $old_next_payment_date;
} }
@@ -289,13 +290,13 @@ class WCS_Repair_2_0_2 {
$repair_date = false; $repair_date = false;
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no old next payment date, setting it to %s.', $subscription->id, var_export( $repair_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no old next payment date, setting it to %s.', $subscription->get_id(), var_export( $repair_date, true ) ) );
} }
} else { } else {
$repair_date = false; $repair_date = false;
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair next payment date = %s.', $subscription->id, var_export( $repair_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair next payment date = %s.', $subscription->get_id(), var_export( $repair_date, true ) ) );
return $repair_date; return $repair_date;
} }
@@ -325,8 +326,8 @@ class WCS_Repair_2_0_2 {
$old_end_date = isset( $former_order_item_meta['_wcs_migrated_subscription_expiry_date'][0] ) ? $former_order_item_meta['_wcs_migrated_subscription_expiry_date'][0] : 0; $old_end_date = isset( $former_order_item_meta['_wcs_migrated_subscription_expiry_date'][0] ) ? $former_order_item_meta['_wcs_migrated_subscription_expiry_date'][0] : 0;
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new end date = %s.', $subscription->id, var_export( $subscription->get_date( 'end' ), true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new end date = %s.', $subscription->get_id(), var_export( $subscription->get_date( 'end' ), true ) ) );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old end date = %s.', $subscription->id, var_export( $old_end_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: old end date = %s.', $subscription->get_id(), var_export( $old_end_date, true ) ) );
// if the subscription has an end time whereas previously it didn't, we need it to be deleted so set it 0 // if the subscription has an end time whereas previously it didn't, we need it to be deleted so set it 0
if ( 0 == $old_end_date ) { if ( 0 == $old_end_date ) {
@@ -338,7 +339,7 @@ class WCS_Repair_2_0_2 {
$repair_date = false; $repair_date = false;
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair end date = %s.', $subscription->id, var_export( $repair_date, true ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repair end date = %s.', $subscription->get_id(), var_export( $repair_date, true ) ) );
return $repair_date; return $repair_date;
} }
@@ -359,27 +360,27 @@ class WCS_Repair_2_0_2 {
try { try {
// we need to bypass the update_status() method here because normally an expired subscription can't have it's status changed, we also don't want normal status change hooks to be fired // we need to bypass the update_status() method here because normally an expired subscription can't have it's status changed, we also don't want normal status change hooks to be fired
wp_update_post( array( 'ID' => $subscription->id, 'post_status' => 'wc-on-hold' ) ); wp_update_post( array( 'ID' => $subscription->get_id(), 'post_status' => 'wc-on-hold' ) );
// if the payment method doesn't support date changes, we still want to reactivate the subscription but we also need to process a special failed payment at the next renewal to fix up the payment method so we'll set a special flag in post meta to handle that // if the payment method doesn't support date changes, we still want to reactivate the subscription but we also need to process a special failed payment at the next renewal to fix up the payment method so we'll set a special flag in post meta to handle that
if ( ! $subscription->payment_method_supports( 'subscription_date_changes' ) && $subscription->get_total() > 0 ) { if ( ! $subscription->payment_method_supports( 'subscription_date_changes' ) && $subscription->get_total() > 0 ) {
update_post_meta( $subscription->id, '_wcs_repaired_2_0_2_needs_failed_payment', 'true' ); update_post_meta( $subscription->get_id(), '_wcs_repaired_2_0_2_needs_failed_payment', 'true' );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: payment method does not support "subscription_date_changes" and total > 0, setting "_wcs_repaired_2_0_2_needs_failed_payment" post meta flag.', $subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: payment method does not support "subscription_date_changes" and total > 0, setting "_wcs_repaired_2_0_2_needs_failed_payment" post meta flag.', $subscription->get_id() ) );
} }
if ( 'active' == $former_order_item_meta['_wcs_migrated_subscription_status'][0] && $subscription->can_be_updated_to( 'active' ) ) { if ( 'active' == $former_order_item_meta['_wcs_migrated_subscription_status'][0] && $subscription->can_be_updated_to( 'active' ) ) {
$subscription->update_status( 'active' ); $subscription->update_status( 'active' );
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired status. Status was "expired", it is now "%s".', $subscription->id, $subscription->get_status() ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: repaired status. Status was "expired", it is now "%s".', $subscription->get_id(), $subscription->get_status() ) );
$repair_status = true; $repair_status = true;
} catch ( Exception $e ) { } catch ( Exception $e ) {
WCS_Upgrade_Logger::add( sprintf( '!!! For subscription %d: unable to repair status, exception "%s"', $subscription->id, $e->getMessage() ) ); WCS_Upgrade_Logger::add( sprintf( '!!! For subscription %d: unable to repair status, exception "%s"', $subscription->get_id(), $e->getMessage() ) );
$repair_status = false; $repair_status = false;
} }
} else { } else {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair status, current status: %s; former status: %s.', $subscription->id, $subscription->get_status(), $former_order_item_meta['_wcs_migrated_subscription_status'][0] ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: no need to repair status, current status: %s; former status: %s.', $subscription->get_id(), $subscription->get_status(), $former_order_item_meta['_wcs_migrated_subscription_status'][0] ) );
$repair_status = false; $repair_status = false;
} }
return $repair_status; return $repair_status;

View File

@@ -262,11 +262,11 @@ class WCS_Repair_2_0 {
// let's get the last 2 renewal orders // let's get the last 2 renewal orders
$last_renewal_order = array_shift( $renewal_orders ); $last_renewal_order = array_shift( $renewal_orders );
$last_renewal_date = $last_renewal_order->order_date; $last_renewal_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $last_renewal_order, 'date_created' ) );
$last_renewal_timestamp = wcs_date_to_time( $last_renewal_date ); $last_renewal_timestamp = wcs_date_to_time( $last_renewal_date );
$second_renewal_order = array_shift( $renewal_orders ); $second_renewal_order = array_shift( $renewal_orders );
$second_renewal_date = $second_renewal_order->order_date; $second_renewal_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $second_renewal_order, 'date_created' ) );
$second_renewal_timestamp = wcs_date_to_time( $second_renewal_date ); $second_renewal_timestamp = wcs_date_to_time( $second_renewal_date );
$interval = 1; $interval = 1;
@@ -284,7 +284,7 @@ class WCS_Repair_2_0 {
WCS_Upgrade_Logger::add( sprintf( '-- For order %d: we have 3 renewal orders, trying to make sure we are right.', $subscription['order_id'] ) ); WCS_Upgrade_Logger::add( sprintf( '-- For order %d: we have 3 renewal orders, trying to make sure we are right.', $subscription['order_id'] ) );
$third_renewal_order = array_shift( $renewal_orders ); $third_renewal_order = array_shift( $renewal_orders );
$third_renewal_date = $third_renewal_order->order_date; $third_renewal_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $third_renewal_order, 'date_created' ) );
$period2 = wcs_estimate_period_between( $second_renewal_date, $third_renewal_date, $interval ); $period2 = wcs_estimate_period_between( $second_renewal_date, $third_renewal_date, $interval );
@@ -335,11 +335,11 @@ class WCS_Repair_2_0 {
// let's get the last 2 renewal orders // let's get the last 2 renewal orders
$last_renewal_order = array_shift( $renewal_orders ); $last_renewal_order = array_shift( $renewal_orders );
$last_renewal_date = $last_renewal_order->order_date; $last_renewal_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $last_renewal_order, 'date_created' ) );
$last_renewal_timestamp = wcs_date_to_time( $last_renewal_date ); $last_renewal_timestamp = wcs_date_to_time( $last_renewal_date );
$second_renewal_order = array_shift( $renewal_orders ); $second_renewal_order = array_shift( $renewal_orders );
$second_renewal_date = $second_renewal_order->order_date; $second_renewal_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $second_renewal_order, 'date_created' ) );
$second_renewal_timestamp = wcs_date_to_time( $second_renewal_date ); $second_renewal_timestamp = wcs_date_to_time( $second_renewal_date );
$subscription['interval'] = wcs_estimate_periods_between( $second_renewal_timestamp, $last_renewal_timestamp, $subscription['period'] ); $subscription['interval'] = wcs_estimate_periods_between( $second_renewal_timestamp, $last_renewal_timestamp, $subscription['period'] );
@@ -470,7 +470,7 @@ class WCS_Repair_2_0 {
} else { } else {
$subscription['end_date'] = wcs_add_time( 5, 'hours', wcs_date_to_time( $last_order->order_date ) ); $subscription['end_date'] = wcs_add_time( 5, 'hours', wcs_get_objects_property( $last_order, 'date_created' )->getTimestamp() );
} }
} else { } else {

View File

@@ -82,19 +82,19 @@ class WCS_Upgrade_2_0 {
'order_id' => $old_subscription['order_id'], 'order_id' => $old_subscription['order_id'],
'customer_id' => $old_subscription['user_id'], 'customer_id' => $old_subscription['user_id'],
'start_date' => $old_subscription['start_date'], 'start_date' => $old_subscription['start_date'],
'customer_note' => ( ! empty( $original_order->customer_note ) ) ? $original_order->customer_note : '', 'customer_note' => ( '' !== wcs_get_objects_property( $original_order, 'customer_note' ) ) ? wcs_get_objects_property( $original_order, 'customer_note' ) : '',
'billing_period' => $old_subscription['period'], 'billing_period' => $old_subscription['period'],
'billing_interval' => $old_subscription['interval'], 'billing_interval' => $old_subscription['interval'],
'order_version' => ( ! empty( $original_order->order_version ) ) ? $original_order->order_version : '', // Subscriptions will default to WC_Version if $original_order->order_version is not set, but we want the version set at the time of the order 'order_version' => ( '' !== wcs_get_objects_property( $original_order, 'version' ) ) ? wcs_get_objects_property( $original_order, 'version' ) : '', // Subscriptions will default to WC_Version if order's version is not set, but we want the version set at the time of the order
) ); ) );
if ( ! is_wp_error( $new_subscription ) ) { if ( ! is_wp_error( $new_subscription ) ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: post created', $new_subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: post created', $new_subscription->get_id() ) );
// Set the order to be manual // Set the order to be manual
if ( isset( $original_order->wcs_requires_manual_renewal ) && 'true' == $original_order->wcs_requires_manual_renewal ) { if ( isset( $original_order->wcs_requires_manual_renewal ) && 'true' == $original_order->wcs_requires_manual_renewal ) {
$new_subscription->update_manual( true ); $new_subscription->set_requires_manual_renewal( true );
} }
// Add the line item from the order // Add the line item from the order
@@ -107,29 +107,29 @@ class WCS_Upgrade_2_0 {
self::migrate_dates( $new_subscription, $old_subscription ); self::migrate_dates( $new_subscription, $old_subscription );
// Set some meta from order meta // Set some meta from order meta
self::migrate_post_meta( $new_subscription->id, $original_order ); self::migrate_post_meta( $new_subscription->get_id(), $original_order );
// Copy over order notes which are now logged on the subscription // Copy over order notes which are now logged on the subscription
self::migrate_order_notes( $new_subscription->id, $original_order->id ); self::migrate_order_notes( $new_subscription->get_id(), wcs_get_objects_property( $original_order, 'id' ) );
// Migrate recurring tax, shipping and coupon line items to be plain line items on the subscription // Migrate recurring tax, shipping and coupon line items to be plain line items on the subscription
self::migrate_order_items( $new_subscription->id, $original_order->id ); self::migrate_order_items( $new_subscription->get_id(), wcs_get_objects_property( $original_order, 'id' ) );
// Update renewal orders to link via post meta key instead of post_parent column // Update renewal orders to link via post meta key instead of post_parent column
self::migrate_renewal_orders( $new_subscription->id, $original_order->id ); self::migrate_renewal_orders( $new_subscription->get_id(), wcs_get_objects_property( $original_order, 'id' ) );
// Make sure the resubscribe meta data is migrated to use the new subscription ID + meta key // Make sure the resubscribe meta data is migrated to use the new subscription ID + meta key
self::migrate_resubscribe_orders( $new_subscription->id, $original_order->id ); self::migrate_resubscribe_orders( $new_subscription->get_id(), wcs_get_objects_property( $original_order, 'id' ) );
// If the order for this subscription contains a switch, make sure the switch meta data is migrated to use the new subscription ID + meta key // If the order for this subscription contains a switch, make sure the switch meta data is migrated to use the new subscription ID + meta key
self::migrate_switch_meta( $new_subscription, $original_order, $subscription_item_id ); self::migrate_switch_meta( $new_subscription, $original_order, $subscription_item_id );
// If the subscription was in the trash, now that we've set on the meta on it, we need to trash it // If the subscription was in the trash, now that we've set on the meta on it, we need to trash it
if ( 'trash' == $old_subscription['status'] ) { if ( 'trash' == $old_subscription['status'] ) {
wp_trash_post( $new_subscription->id ); wp_trash_post( $new_subscription->get_id() );
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: upgrade complete', $new_subscription->id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: upgrade complete', $new_subscription->get_id() ) );
} else { } else {
@@ -251,12 +251,12 @@ class WCS_Upgrade_2_0 {
private static function add_product( $new_subscription, $order_item_id, $order_item ) { private static function add_product( $new_subscription, $order_item_id, $order_item ) {
global $wpdb; global $wpdb;
$item_id = wc_add_order_item( $new_subscription->id, array( $item_id = wc_add_order_item( $new_subscription->get_id(), array(
'order_item_name' => $order_item['name'], 'order_item_name' => $order_item['name'],
'order_item_type' => 'line_item', 'order_item_type' => 'line_item',
) ); ) );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new line item ID %d added', $new_subscription->id, $item_id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: new line item ID %d added', $new_subscription->get_id(), $item_id ) );
$order_item = WCS_Repair_2_0::maybe_repair_order_item( $order_item ); $order_item = WCS_Repair_2_0::maybe_repair_order_item( $order_item );
@@ -312,12 +312,12 @@ class WCS_Upgrade_2_0 {
} }
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: for item %d added %s', $new_subscription->id, $item_id, implode( ', ', $meta_keys_to_copy ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: for item %d added %s', $new_subscription->get_id(), $item_id, implode( ', ', $meta_keys_to_copy ) ) );
// Now that we've copied over the old data, prefix some the subscription meta keys with _wcs_migrated to deprecate it without deleting it (yet) // Now that we've copied over the old data, prefix some the subscription meta keys with _wcs_migrated to deprecate it without deleting it (yet)
$rows_affected = self::deprecate_item_meta( $order_item_id ); $rows_affected = self::deprecate_item_meta( $order_item_id );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: %s rows of line item meta deprecated', $new_subscription->id, $rows_affected ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: %s rows of line item meta deprecated', $new_subscription->get_id(), $rows_affected ) );
return $item_id; return $item_id;
} }
@@ -436,12 +436,12 @@ class WCS_Upgrade_2_0 {
$rows_affected = $wpdb->update( $rows_affected = $wpdb->update(
$wpdb->prefix . 'woocommerce_downloadable_product_permissions', $wpdb->prefix . 'woocommerce_downloadable_product_permissions',
array( array(
'order_id' => $subscription->id, 'order_id' => $subscription->get_id(),
'order_key' => $subscription->order_key, 'order_key' => $subscription->get_order_key(),
), ),
array( array(
'order_id' => $order->id, 'order_id' => wcs_get_objects_property( $order, 'id' ),
'order_key' => $order->order_key, 'order_key' => wcs_get_objects_property( $order, 'order_key' ),
'product_id' => $product_id, 'product_id' => $product_id,
'user_id' => absint( $subscription->get_user_id() ), 'user_id' => absint( $subscription->get_user_id() ),
), ),
@@ -449,7 +449,7 @@ class WCS_Upgrade_2_0 {
array( '%d', '%s', '%d', '%d' ) array( '%d', '%s', '%d', '%d' )
); );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: migrated %d download permissions for product %d', $subscription->id, $rows_affected, $product_id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: migrated %d download permissions for product %d', $subscription->get_id(), $rows_affected, $product_id ) );
} }
/** /**
@@ -506,7 +506,7 @@ class WCS_Upgrade_2_0 {
if ( $next_scheduled > 0 ) { if ( $next_scheduled > 0 ) {
if ( 'end_of_prepaid_term' == $new_key ) { if ( 'end_of_prepaid_term' == $new_key ) {
wc_schedule_single_action( $next_scheduled, 'woocommerce_scheduled_subscription_end_of_prepaid_term', array( 'subscription_id' => $new_subscription->id ) ); wc_schedule_single_action( $next_scheduled, 'woocommerce_scheduled_subscription_end_of_prepaid_term', array( 'subscription_id' => $new_subscription->get_id() ) );
} else { } else {
$dates_to_update[ $new_key ] = gmdate( 'Y-m-d H:i:s', $next_scheduled ); $dates_to_update[ $new_key ] = gmdate( 'Y-m-d H:i:s', $next_scheduled );
} }
@@ -517,7 +517,7 @@ class WCS_Upgrade_2_0 {
// Trash all the hooks in one go to save write requests // Trash all the hooks in one go to save write requests
$wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'post_type' => ActionScheduler_wpPostStore::POST_TYPE, 'post_content' => wcs_json_encode( $old_hook_args ) ), array( '%s', '%s' ) ); $wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'post_type' => ActionScheduler_wpPostStore::POST_TYPE, 'post_content' => wcs_json_encode( $old_hook_args ) ), array( '%s', '%s' ) );
$dates_to_update['start'] = $new_subscription->post->post_date_gmt; $dates_to_update['date_created'] = $new_subscription->post->post_date_gmt;
// v2.0 enforces new rules for dates when they are being set, so we need to massage the old data to conform to these new rules // v2.0 enforces new rules for dates when they are being set, so we need to massage the old data to conform to these new rules
foreach ( $dates_to_update as $date_type => $date ) { foreach ( $dates_to_update as $date_type => $date ) {
@@ -536,7 +536,7 @@ class WCS_Upgrade_2_0 {
$dates_to_update[ $date_type ] = $date; $dates_to_update[ $date_type ] = $date;
} }
case 'trial_end' : case 'trial_end' :
if ( array_key_exists( 'start', $dates_to_update ) && $date <= $dates_to_update['start'] ) { if ( array_key_exists( 'date_created', $dates_to_update ) && $date <= $dates_to_update['date_created'] ) {
$dates_to_update[ $date_type ] = $date; $dates_to_update[ $date_type ] = $date;
} }
} }
@@ -548,10 +548,10 @@ class WCS_Upgrade_2_0 {
$new_subscription->update_dates( $dates_to_update ); $new_subscription->update_dates( $dates_to_update );
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: updated dates = %s', $new_subscription->id, str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: updated dates = %s', $new_subscription->get_id(), str_replace( array( '{', '}', '"' ), '', wcs_json_encode( $dates_to_update ) ) ) );
} catch ( Exception $e ) { } catch ( Exception $e ) {
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: unable to update dates, exception "%s"', $new_subscription->id, $e->getMessage() ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: unable to update dates, exception "%s"', $new_subscription->get_id(), $e->getMessage() ) );
} }
} }
@@ -585,11 +585,11 @@ class WCS_Upgrade_2_0 {
'_paypal_subscription_id' => 'PayPal Subscriber ID', '_paypal_subscription_id' => 'PayPal Subscriber ID',
); );
$order_meta = get_post_meta( $order->id ); $order_meta = get_post_meta( wcs_get_objects_property( $order, 'id' ) );
foreach ( $post_meta_with_new_key as $subscription_meta_key => $order_meta_key ) { foreach ( $post_meta_with_new_key as $subscription_meta_key => $order_meta_key ) {
$order_meta_value = get_post_meta( $order->id, $order_meta_key, true ); $order_meta_value = get_post_meta( wcs_get_objects_property( $order, 'id' ), $order_meta_key, true );
if ( isset( $order_meta[ $order_meta_key ] ) && '' !== $order_meta[ $order_meta_key ] ) { if ( isset( $order_meta[ $order_meta_key ] ) && '' !== $order_meta[ $order_meta_key ] ) {
update_post_meta( $subscription_id, $subscription_meta_key, $order_meta_value ); update_post_meta( $subscription_id, $subscription_meta_key, $order_meta_value );
@@ -647,7 +647,7 @@ class WCS_Upgrade_2_0 {
} }
// Now that we've copied over the old data, deprecate it // Now that we've copied over the old data, deprecate it
$rows_affected = self::deprecate_post_meta( $order->id ); $rows_affected = self::deprecate_post_meta( wcs_get_objects_property( $order, 'id' ) );
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: %d rows of post meta deprecated', $subscription_id, $rows_affected ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: %d rows of post meta deprecated', $subscription_id, $rows_affected ) );
} }
@@ -777,7 +777,7 @@ class WCS_Upgrade_2_0 {
// Set the post meta // Set the post meta
foreach ( $renewal_order_ids as $renewal_order_id ) { foreach ( $renewal_order_ids as $renewal_order_id ) {
update_post_meta( $renewal_order_id, '_subscription_renewal', $subscription_id ); wcs_set_objects_property( wc_get_order( $renewal_order_id ), 'subscription_renewal', $subscription_id );
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: migrated data for renewal orders %s', $subscription_id, implode( ', ', $renewal_order_ids ) ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: migrated data for renewal orders %s', $subscription_id, implode( ', ', $renewal_order_ids ) ) );
@@ -817,8 +817,8 @@ class WCS_Upgrade_2_0 {
// Because self::get_subscriptions() orders by order ID, it's safe to use wcs_get_subscriptions_for_order() here because the subscription in the new format will have been created for the original order (because its ID will be < the resubscribe order's ID) // Because self::get_subscriptions() orders by order ID, it's safe to use wcs_get_subscriptions_for_order() here because the subscription in the new format will have been created for the original order (because its ID will be < the resubscribe order's ID)
foreach ( wcs_get_subscriptions_for_order( $original_order_id ) as $old_subscription ) { foreach ( wcs_get_subscriptions_for_order( $original_order_id ) as $old_subscription ) {
update_post_meta( $resubscribe_order_id, '_subscription_resubscribe', $old_subscription->id, true ); update_post_meta( $resubscribe_order_id, '_subscription_resubscribe', $old_subscription->get_id(), true );
update_post_meta( $new_subscription_id, '_subscription_resubscribe', $old_subscription->id, true ); update_post_meta( $new_subscription_id, '_subscription_resubscribe', $old_subscription->get_id(), true );
} }
$wpdb->query( $wpdb->prepare( $wpdb->query( $wpdb->prepare(
@@ -851,14 +851,14 @@ class WCS_Upgrade_2_0 {
global $wpdb; global $wpdb;
// If the order doesn't contain a switch, we don't need to do anything // If the order doesn't contain a switch, we don't need to do anything
if ( '' == get_post_meta( $switch_order->id, '_switched_subscription_key', true ) ) { if ( '' == get_post_meta( wcs_get_objects_property( $switch_order, 'id' ), '_switched_subscription_key', true ) ) {
return; return;
} }
$wpdb->query( $wpdb->prepare( $wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET `meta_key` = concat( '_wcs_migrated', `meta_key` ) "UPDATE {$wpdb->postmeta} SET `meta_key` = concat( '_wcs_migrated', `meta_key` )
WHERE `post_id` = %d AND `meta_key` IN ('_switched_subscription_first_payment_timestamp','_switched_subscription_key')", WHERE `post_id` = %d AND `meta_key` IN ('_switched_subscription_first_payment_timestamp','_switched_subscription_key')",
$switch_order->id wcs_get_objects_property( $switch_order, 'id' )
) ); ) );
// Select the orders which had the items which were switched by this order // Select the orders which had the items which were switched by this order
@@ -870,7 +870,7 @@ class WCS_Upgrade_2_0 {
'meta_query' => array( 'meta_query' => array(
array( array(
'key' => '_switched_subscription_new_order', 'key' => '_switched_subscription_new_order',
'value' => $switch_order->id, 'value' => wcs_get_objects_property( $switch_order, 'id' ),
), ),
), ),
) ); ) );
@@ -891,7 +891,7 @@ class WCS_Upgrade_2_0 {
if ( wcs_is_subscription( $old_subscription ) ) { if ( wcs_is_subscription( $old_subscription ) ) {
// Link the old subscription's ID to the switch order using the new switch meta key // Link the old subscription's ID to the switch order using the new switch meta key
update_post_meta( $switch_order->id, '_subscription_switch', $old_subscription->id ); wcs_set_objects_property( $switch_order, 'subscription_switch', $old_subscription->get_id() );
// Now store the new/old item IDs for record keeping // Now store the new/old item IDs for record keeping
foreach ( $old_subscription->get_items() as $item_id => $item ) { foreach ( $old_subscription->get_items() as $item_id => $item ) {
@@ -899,7 +899,7 @@ class WCS_Upgrade_2_0 {
wc_add_order_item_meta( $subscription_item_id, '_switched_subscription_item_id', $item_id, true ); wc_add_order_item_meta( $subscription_item_id, '_switched_subscription_item_id', $item_id, true );
} }
WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: migrated switch data for subscription %d purchased in order %d', $new_subscription->id, $old_subscription->id, $previous_order_id ) ); WCS_Upgrade_Logger::add( sprintf( 'For subscription %d: migrated switch data for subscription %d purchased in order %d', $new_subscription->get_id(), $old_subscription->get_id(), $previous_order_id ) );
} }
} }
} }

View File

@@ -46,10 +46,21 @@ class WCS_Upgrade_Logger {
* Clear entries from the upgrade log. * Clear entries from the upgrade log.
*/ */
public static function clear() { public static function clear() {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
if ( empty( self::$log ) ) { if ( empty( self::$log ) ) {
self::$log = new WC_Logger(); self::$log = new WC_Logger();
} }
self::$log->clear( self::$handle ); self::$log->clear( self::$handle );
} else {
$handler = new WC_Log_Handler_File();
$handler->clear( self::$handle );
}
} }
/** /**

View File

@@ -213,7 +213,7 @@ function wcs_cart_totals_coupon_html( $coupon, $cart ) {
$value = array(); $value = array();
if ( $amount = $cart->get_coupon_discount_amount( $coupon->code, $cart->display_cart_ex_tax ) ) { if ( $amount = $cart->get_coupon_discount_amount( wcs_get_coupon_property( $coupon, 'code' ), $cart->display_cart_ex_tax ) ) {
$discount_html = '-' . wc_price( $amount ); $discount_html = '-' . wc_price( $amount );
} else { } else {
$discount_html = ''; $discount_html = '';
@@ -221,7 +221,7 @@ function wcs_cart_totals_coupon_html( $coupon, $cart ) {
$value[] = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_html, $coupon ); $value[] = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_html, $coupon );
if ( $coupon->enable_free_shipping() ) { if ( wcs_get_coupon_property( $coupon, 'enable_free_shipping' ) ) {
$value[] = __( 'Free shipping coupon', 'woocommerce-subscriptions' ); $value[] = __( 'Free shipping coupon', 'woocommerce-subscriptions' );
} }
@@ -303,10 +303,11 @@ function wcs_cart_pluck( $cart, $field, $default = 0 ) {
$value = $cart->$field; $value = $cart->$field;
} else { } else {
foreach ( $cart->get_cart() as $cart_item ) { foreach ( $cart->get_cart() as $cart_item ) {
if ( isset( $cart_item[ $field ] ) ) { if ( isset( $cart_item[ $field ] ) ) {
$value = $cart_item[ $field ]; $value = $cart_item[ $field ];
} elseif ( $cart_item['data']->$field ) { } else {
$value = $cart_item['data']->$field; $value = WC_Subscriptions_Product::get_meta_data( $cart_item['data'], $field, $default );
} }
} }
} }

View File

@@ -43,3 +43,475 @@ function wcs_help_tip( $tip, $allow_html = false ) {
return $help_tip; return $help_tip;
} }
/**
* Access an object's property in a way that is compatible with CRUD and non-CRUD APIs for different versions of WooCommerce.
*
* We don't want to force the use of a custom legacy class for orders, similar to WC_Subscription_Legacy, because 3rd party
* code may expect the object type to be WC_Order with strict type checks.
*
* A note on dates: in WC 3.0+, dates are returned a timestamps in the site's timezone :upside_down_face:. In WC < 3.0, they were
* returned as MySQL strings in the site's timezone. We return them from here as MySQL strings in UTC timezone because that's how
* dates are used in Subscriptions in almost all cases, for sanity's sake.
*
* @param WC_Order|WC_Product|WC_Subscription $object The object whose property we want to access.
* @param string $property The property name.
* @param string $single Whether to return just the first piece of meta data with the given property key, or all meta data.
* @param mixed $default (optional) The value to return if no value is found - defaults to single -> null, multiple -> array()
* @since 2.2.0
* @return mixed
*/
function wcs_get_objects_property( $object, $property, $single = 'single', $default = null ) {
$prefixed_key = wcs_maybe_prefix_key( $property );
$value = ! is_null( $default ) ? $default : ( ( 'single' == $single ) ? null : array() );
switch ( $property ) {
case 'name' : // the replacement for post_title added in 3.0
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$value = $object->post->post_title;
} else { // WC 3.0+
$value = $object->get_name();
}
break;
case 'post' :
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$value = $object->post;
} else { // WC 3.0+
// In order to keep backwards compatibility it's required to use the parent data for variations.
if ( method_exists( $object, 'is_type' ) && $object->is_type( 'variation' ) ) {
$value = get_post( $object->get_parent_id() );
} else {
$value = get_post( $object->get_id() );
}
}
break;
case 'post_status' :
$value = wcs_get_objects_property( $object, 'post' )->post_status;
break;
case 'parent_id' :
if ( method_exists( $object, 'get_parent_id' ) ) { // WC 3.0+ or an instance of WC_Product_Subscription_Variation_Legacy with WC < 3.0
$value = $object->get_parent_id();
} else { // WC 2.1-2.6
$value = $object->get_parent();
}
break;
case 'variation_data' :
if ( function_exists( 'wc_get_product_variation_attributes' ) ) { // WC 3.0+
$value = wc_get_product_variation_attributes( $object->get_id() );
} else {
$value = $object->$property;
}
break;
case 'downloads' :
if ( method_exists( $object, 'get_downloads' ) ) { // WC 3.0+
$value = $object->get_downloads();
} else {
$value = $object->get_files();
}
break;
case 'order_version' :
case 'version' :
if ( method_exists( $object, 'get_version' ) ) { // WC 3.0+
$value = $object->get_version();
} else { // WC 2.1-2.6
$value = $object->order_version;
}
break;
case 'order_currency' :
case 'currency' :
if ( method_exists( $object, 'get_currency' ) ) { // WC 3.0+
$value = $object->get_currency();
} else { // WC 2.1-2.6
$value = $object->get_order_currency();
}
break;
// Always return a PHP DateTime object in site timezone (or null), the same thing the WC_Order::get_date_created() method returns in WC 3.0+ to make it easier to migrate away from WC < 3.0
case 'date_created' :
case 'order_date' :
case 'date' :
if ( method_exists( $object, 'get_date_created' ) ) { // WC 3.0+
$value = $object->get_date_created();
} else {
// Base the value off tht GMT value when possible and then set the DateTime's timezone based on the current site's timezone to avoid incorrect values when the timezone has changed
if ( '0000-00-00 00:00:00' != $object->post->post_date_gmt ) {
$value = new WC_DateTime( $object->post->post_date_gmt, new DateTimeZone( 'UTC' ) );
$value->setTimezone( new DateTimeZone( wc_timezone_string() ) );
} else {
$value = new WC_DateTime( $object->post->post_date, new DateTimeZone( wc_timezone_string() ) );
}
}
break;
// Always return a PHP DateTime object in site timezone (or null), the same thing the getter returns in WC 3.0+ to make it easier to migrate away from WC < 3.0
case 'date_paid' :
if ( method_exists( $object, 'get_date_paid' ) ) { // WC 3.0+
$value = $object->get_date_paid();
} else {
if ( ! empty( $object->paid_date ) ) {
// Because the paid_date post meta value was set in the site timezone at the time it was set, this won't always be correct, but is the best we can do with WC < 3.0
$value = new WC_DateTime( $object->paid_date, new DateTimeZone( wc_timezone_string() ) );
} else {
$value = null;
}
}
break;
case 'cart_discount' :
if ( method_exists( $object, 'get_total_discount' ) ) { // WC 3.0+
$value = $object->get_total_discount();
} else { // WC 2.1-2.6
$value = $object->cart_discount;
}
break;
default :
$function_name = 'get_' . $property;
if ( is_callable( array( $object, $function_name ) ) ) {
$value = $object->$function_name();
} else {
// If we don't have a method for this specific property, but we are using WC 3.0, it may be set as meta data on the object so check if we can use that
if ( method_exists( $object, 'get_meta' ) ) {
if ( $object->meta_exists( $prefixed_key ) ) {
if ( 'single' === $single ) {
$value = $object->get_meta( $prefixed_key, true );
} else {
// WC_Data::get_meta() returns an array of stdClass objects with id, key & value properties when meta is available
$value = wp_list_pluck( $object->get_meta( $prefixed_key, false ), 'value' );
}
}
} elseif ( 'single' === $single && isset( $object->$property ) ) { // WC < 3.0
$value = $object->$property;
} elseif ( metadata_exists( 'post', wcs_get_objects_property( $object, 'id' ), $prefixed_key ) ) {
// If we couldn't find a property or function, fallback to using post meta as that's what many __get() methods in WC < 3.0 did
if ( 'single' === $single ) {
$value = get_post_meta( wcs_get_objects_property( $object, 'id' ), $prefixed_key, true );
} else {
// Get all the meta values
$value = get_post_meta( wcs_get_objects_property( $object, 'id' ), $prefixed_key, false );
}
}
}
break;
}
return $value;
}
/**
* Set an object's property in a way that is compatible with CRUD and non-CRUD APIs for different versions of WooCommerce.
*
* @param WC_Order|WC_Product|WC_Subscription $object The object whose property we want to access.
* @param string $key The meta key name without '_' prefix
* @param mixed $value The data to set as the value of the meta
* @param string $save Whether to write the data to the database or not. Use 'save' to write to the database, anything else to only update it in memory.
* @param int $meta_id The meta ID of exiting meta data if you wish to overwrite an existing piece of meta.
* @param bool|string $prefix An optional prefix to add to the $key. Default '_'. Set to boolean false to have no prefix added.
* @since 2.2.0
* @return mixed
*/
function wcs_set_objects_property( &$object, $key, $value, $save = 'save', $meta_id = '' ) {
$prefixed_key = wcs_maybe_prefix_key( $key );
// WC will automatically set/update these keys when a shipping/billing address attribute changes so we can ignore these keys
if ( in_array( $prefixed_key, array( '_shipping_address_index', '_billing_address_index' ) ) ) {
return;
}
// Special cases where properties with setters which don't map nicely to their function names
$meta_setters_map = array(
'_cart_discount' => 'set_discount_total',
'_cart_discount_tax' => 'set_discount_tax',
'_customer_user' => 'set_customer_id',
'_order_tax' => 'set_cart_tax',
'_order_shipping' => 'set_shipping_total',
'_sale_price_dates_from' => 'set_date_on_sale_from',
'_sale_price_dates_to' => 'set_date_on_sale_to',
);
// If we have a 3.0 object with a predefined setter function, use it
if ( isset( $meta_setters_map[ $prefixed_key ] ) && is_callable( array( $object, $meta_setters_map[ $prefixed_key ] ) ) ) {
$function = $meta_setters_map[ $prefixed_key ];
$object->$function( $value );
// If we have a 3.0 object, use the setter if available.
} elseif ( is_callable( array( $object, 'set' . $prefixed_key ) ) ) {
// Prices include tax is stored as a boolean in props but saved in the database as a string yes/no, so we need to normalise it here to make sure if we have a string (which can be passed to it by things like wcs_copy_order_meta()) that it's converted to a boolean before being set
if ( '_prices_include_tax' === $prefixed_key && ! is_bool( $value ) ) {
$value = 'yes' === $value ? true : false;
}
$object->{ "set$prefixed_key" }( $value );
// If there is a setter without the order prefix (eg set_order_total -> set_total)
} elseif ( is_callable( array( $object, 'set' . str_replace( '_order', '', $prefixed_key ) ) ) ) {
$function_name = 'set' . str_replace( '_order', '', $prefixed_key );
$object->$function_name( $value );
// If there is no setter, treat as meta within the 3.0.x object.
} elseif ( is_callable( array( $object, 'update_meta_data' ) ) ) {
$object->update_meta_data( $prefixed_key, $value, $meta_id );
// 2.6.x handling for name which is not meta.
} elseif ( 'name' === $key ) {
$object->post->post_title = $value;
// 2.6.x handling for everything else.
} else {
$object->$key = $value;
}
// Save the data
if ( 'save' === $save ) {
if ( is_callable( array( $object, 'save' ) ) ) { // WC 3.0+
$object->save();
} elseif ( 'date_created' == $key ) { // WC < 3.0+
wp_update_post( array( 'ID' => wcs_get_objects_property( $object, 'id' ), 'post_date' => get_date_from_gmt( $value ), 'post_date_gmt' => $value ) );
} elseif ( 'name' === $key ) { // the replacement for post_title added in 3.0, need to update post_title not post meta
wp_update_post( array( 'ID' => wcs_get_objects_property( $object, 'id' ), 'post_title' => $value ) );
} else {
if ( ! empty( $meta_id ) ) {
update_metadata_by_mid( 'post', $meta_id, $value, $prefixed_key );
} else {
update_post_meta( wcs_get_objects_property( $object, 'id' ), $prefixed_key, $value );
}
}
}
}
/**
* Delete an object's property in a way that is compatible with CRUD and non-CRUD APIs for different versions of WooCommerce.
*
* @param WC_Order|WC_Product|WC_Subscription $object The object whose property we want to access.
* @param string $key The meta key name without '_' prefix
* @param mixed $value The data to set as the value of the meta
* @param string $save Whether to save the data or not, 'save' to save the data, otherwise it won't be saved.
* @since 2.2.0
* @return mixed
*/
function wcs_delete_objects_property( &$object, $key, $save = 'save', $meta_id = '' ) {
$prefixed_key = wcs_maybe_prefix_key( $key );
if ( ! empty( $meta_id ) && method_exists( $object, 'delete_meta_data_by_mid' ) ) {
$object->delete_meta_data_by_mid( $meta_id );
} elseif ( method_exists( $object, 'delete_meta_data' ) ) {
$object->delete_meta_data( $prefixed_key );
} elseif ( isset( $object->$key ) ) {
unset( $object->$key );
}
// Save the data
if ( 'save' === $save ) {
if ( method_exists( $object, 'save' ) ) { // WC 3.0+
$object->save();
} elseif ( ! empty( $meta_id ) ) {
delete_metadata_by_mid( 'post', $meta_id );
} else {
delete_post_meta( wcs_get_objects_property( $object, 'id' ), $prefixed_key );
}
}
}
/**
* Check whether an order is a standard order (i.e. not a refund or subscription) in version compatible way.
*
* WC 3.0 has the $order->get_type() API which returns 'shop_order', while WC < 3.0 provided the $order->order_type
* property which returned 'simple', so we need to check for both.
*
* @param WC_Order $order
* @since 2.2.0
* @return bool
*/
function wcs_is_order( $order ) {
if ( method_exists( $order, 'get_type' ) ) {
$is_order = ( 'shop_order' === $order->get_type() );
} else {
$is_order = ( 'simple' === $order->order_type );
}
return $is_order;
}
/**
* Find and return the value for a deprecated property property.
*
* Product properties should not be accessed directly with WooCommerce 3.0+, because of that, a lot of properties
* have been deprecated/removed in the subscription product type classes. This function centralises the handling
* of deriving deprecated properties. This saves duplicating the __get() method in WC_Product_Subscription,
* WC_Product_Variable_Subscription and WC_Product_Subscription_Variation.
*
* @param string $property
* @param WC_Product $product
* @since 2.2.0
* @return mixed
*/
function wcs_product_deprecated_property_handler( $property, $product ) {
$message_prefix = 'Product properties should not be accessed directly with WooCommerce 3.0+.';
$function_name = 'get_' . str_replace( 'subscription_', '', str_replace( 'subscription_period_', '', $property ) );
$class_name = get_class( $product );
$value = null;
if ( in_array( $property, array( 'product_type', 'parent_product_type', 'limit_subscriptions', 'subscription_limit', 'subscription_payment_sync_date', 'subscription_one_time_shipping' ) ) || ( is_callable( array( 'WC_Subscriptions_Product', $function_name ) ) && false !== strpos( $property, 'subscription' ) ) ) {
switch ( $property ) {
case 'product_type':
$value = $product->get_type();
$alternative = $class_name . '::get_type()';
break;
case 'parent_product_type':
if ( $product->is_type( 'subscription_variation' ) ) {
$value = 'variation';
$alternative = 'WC_Product_Variation::get_type()';
} else {
$value = 'variable';
$alternative = 'WC_Product_Variable::get_type()';
}
break;
case 'limit_subscriptions':
case 'subscription_limit':
$value = wcs_get_product_limitation( $product );
$alternative = 'wcs_get_product_limitation( $product )';
break;
case 'subscription_one_time_shipping':
$value = WC_Subscriptions_Product::needs_one_time_shipping( $product );
$alternative = 'WC_Subscriptions_Product::needs_one_time_shipping( $product )';
break;
case 'subscription_payment_sync_date':
$value = WC_Subscriptions_Synchroniser::get_products_payment_day( $product );
$alternative = 'WC_Subscriptions_Synchroniser::get_products_payment_day( $product )';
break;
case 'max_variation_period':
case 'max_variation_period_interval':
$meta_key = '_' . $property;
if ( '' === $product->get_meta( $meta_key ) ) {
WC_Product_Variable::sync( $product->get_id() );
}
$value = $product->get_meta( $meta_key );
$alternative = $class_name . '::get_meta( ' . $meta_key . ' ) or wcs_get_min_max_variation_data( $product )';
break;
default:
$value = call_user_func( array( 'WC_Subscriptions_Product', $function_name ), $product );
$alternative = sprintf( 'WC_Subscriptions_Product::%s( $product )', $function_name );
break;
}
wcs_deprecated_argument( $class_name . '::$' . $property, '2.2.0', sprintf( '%s Use %s', $message_prefix, $alternative ) );
}
return $value;
}
/**
* Access a coupon's property in a way that is compatible with CRUD and non-CRUD APIs for different versions of WooCommerce.
*
* Similar to @see wcs_get_objects_property
*
* @param WC_Coupon $coupon The coupon whose property we want to access.
* @param string $property The property name.
* @since 2.2
* @return mixed
*/
function wcs_get_coupon_property( $coupon, $property ) {
$value = '';
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$value = $coupon->$property;
} else {
// Some coupon properties don't map nicely to their corresponding getter function. This array contains those exceptions.
$property_to_getter_map = array(
'type' => 'get_discount_type',
'exclude_product_ids' => 'get_excluded_product_ids',
'expiry_date' => 'get_date_expires',
'exclude_product_categories' => 'get_excluded_product_categories',
'customer_email' => 'get_email_restrictions',
'enable_free_shipping' => 'get_free_shipping',
);
switch ( true ) {
case 'exists' == $property:
$value = ( $coupon->get_id() > 0 ) ? true : false;
break;
case isset( $property_to_getter_map[ $property ] ) && is_callable( array( $coupon, $property_to_getter_map[ $property ] ) ):
$function = $property_to_getter_map[ $property ];
$value = $coupon->$function();
break;
case is_callable( array( $coupon, 'get_' . $property ) ):
$value = $coupon->{ "get_$property" }();
break;
}
}
return $value;
}
/**
* Set a coupon's property in a way that is compatible with CRUD and non-CRUD APIs for different versions of WooCommerce.
*
* Similar to @see wcs_set_objects_property
*
* @param WC_Coupon $coupon The coupon whose property we want to set.
* @param string $property The property name.
* @param mixed $value The data to set as the value
* @since 2.2
*/
function wcs_set_coupon_property( &$coupon, $property, $value ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$coupon->$property = $value;
} else {
// Some coupon properties don't map nicely to their corresponding setter function. This array contains those exceptions.
$property_to_setter_map = array(
'type' => 'set_discount_type',
'exclude_product_ids' => 'set_excluded_product_ids',
'expiry_date' => 'set_date_expires',
'exclude_product_categories' => 'set_excluded_product_categories',
'customer_email' => 'set_email_restrictions',
'enable_free_shipping' => 'set_free_shipping',
);
switch ( true ) {
case 'individual_use' == $property:
// set_individual_use expects a boolean, the individual_use property use to be either 'yes' or 'no' so we need to accept both types
if ( ! is_bool( $value ) ) {
$value = ( 'yes' === $value ) ? true : false;
}
$coupon->set_individual_use( $value );
break;
case isset( $property_to_setter_map[ $property ] ) && is_callable( array( $coupon, $property_to_setter_map[ $property ] ) ):
$function = $property_to_setter_map[ $property ];
$coupon->$function( $value );
break;
case is_callable( array( $coupon, 'set_' . $property ) ):
$coupon->{ "set_$property" }( $value );
break;
}
}
}

View File

@@ -16,6 +16,73 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly exit; // Exit if accessed directly
} }
/**
* Wrapper for wc_doing_it_wrong.
*
* @since 2.2.0
* @param string $function
* @param string $version
* @param string $replacement
*/
function wcs_doing_it_wrong( $function, $message, $version ) {
if ( function_exists( 'wc_doing_it_wrong' ) ) {
wc_doing_it_wrong( $function, $message, $version );
} else {
// Reimplment wc_doing_it_wrong() when WC 3.0 is not active
if ( is_ajax() ) {
do_action( 'doing_it_wrong_run', $function, $message, $version );
error_log( "{$function} was called incorrectly. {$message}. This message was added in version {$version}." );
} else {
_doing_it_wrong( esc_attr( $function ), esc_attr( $message ), esc_attr( $version ) );
}
}
}
/**
* Wrapper for wcs_deprecated_function to improve handling of ajax requests, even when
* WooCommerce 3.0's wcs_deprecated_function method is not available.
*
* @since 2.2.0
* @param string $function
* @param string $version
* @param string $replacement
*/
function wcs_deprecated_function( $function, $version, $replacement = null ) {
if ( function_exists( 'wc_deprecated_function' ) ) {
wc_deprecated_function( $function, $version, $replacement );
} else {
// Reimplment wcs_deprecated_function() when WC 3.0 is not active
if ( is_ajax() ) {
do_action( 'deprecated_function_run', $function, $replacement, $version );
$log_string = "The {$function} function is deprecated since version {$version}.";
$log_string .= $replacement ? " Replace with {$replacement}." : '';
error_log( $log_string );
} else {
_deprecated_function( esc_attr( $function ), esc_attr( $version ), esc_attr( $replacement ) );
}
}
}
/**
* Reimplement similar logic to wc_deprecated_argument() without the first parameter confusion.
*
* @since 2.2.0
* @param string $argument
* @param string $version
* @param string $message
*/
function wcs_deprecated_argument( $function, $version, $message = null ) {
if ( is_ajax() ) {
do_action( 'deprecated_argument_run', $function, $message, $version );
error_log( "{$function} was called with an argument that is deprecated since version {$version}. {$message}" );
} else {
_deprecated_argument( esc_attr( $function ), esc_attr( $version ), esc_attr( $message ) );
}
}
/** /**
* Get the string key for a subscription used in Subscriptions prior to 2.0. * Get the string key for a subscription used in Subscriptions prior to 2.0.
* *
@@ -37,7 +104,7 @@ if ( ! defined( 'ABSPATH' ) ) {
function wcs_get_old_subscription_key( WC_Subscription $subscription ) { function wcs_get_old_subscription_key( WC_Subscription $subscription ) {
// Get an ID to use as the order ID // Get an ID to use as the order ID
$order_id = isset( $subscription->order->id ) ? $subscription->order->id : $subscription->id; $order_id = ( false == $subscription->get_parent_id() ) ? $subscription->get_id() : $subscription->get_parent_id();
// Get an ID to use as the product ID // Get an ID to use as the product ID
$subscription_items = $subscription->get_items(); $subscription_items = $subscription->get_items();
@@ -128,8 +195,15 @@ function wcs_get_subscription_in_deprecated_structure( WC_Subscription $subscrip
$completed_payments = array(); $completed_payments = array();
if ( $subscription->get_completed_payment_count() ) { if ( $subscription->get_completed_payment_count() ) {
if ( ! empty( $subscription->order ) && $subscription->order->has_status( $subscription->get_paid_order_statuses() ) ) {
$completed_payments[] = $subscription->order->post->post_date_gmt; $order = $subscription->get_parent();
if ( ! empty( $order ) ) {
$parent_order_created_date = wcs_get_objects_property( $order, 'date_created' );
if ( ! is_null( $parent_order_created_date ) ) {
$completed_payments[] = wcs_get_datetime_utc_string( $parent_order_created_date );
}
} }
$paid_renewal_order_ids = get_posts( array( $paid_renewal_order_ids = get_posts( array(
@@ -143,14 +217,17 @@ function wcs_get_subscription_in_deprecated_structure( WC_Subscription $subscrip
array( array(
'key' => '_subscription_renewal', 'key' => '_subscription_renewal',
'compare' => '=', 'compare' => '=',
'value' => $subscription->id, 'value' => $subscription->get_id(),
'type' => 'numeric', 'type' => 'numeric',
), ),
), ),
) ); ) );
foreach ( $paid_renewal_order_ids as $paid_renewal_order_id ) { foreach ( $paid_renewal_order_ids as $paid_renewal_order_id ) {
$completed_payments[] = get_post_field( 'post_date_gmt', $paid_renewal_order_id ); $date_created = wcs_get_objects_property( wc_get_order( $paid_renewal_order_id ), 'date_created' );
if ( ! is_null( $date_created ) ) {
$completed_payments[] = wcs_get_datetime_utc_string( $date_created );
}
} }
} }
@@ -160,27 +237,27 @@ function wcs_get_subscription_in_deprecated_structure( WC_Subscription $subscrip
if ( ! empty( $item ) ) { if ( ! empty( $item ) ) {
$deprecated_subscription_object = array( $deprecated_subscription_object = array(
'order_id' => $subscription->order->id, 'order_id' => $subscription->get_parent_id(),
'product_id' => isset( $item['product_id'] ) ? $item['product_id'] : 0, 'product_id' => isset( $item['product_id'] ) ? $item['product_id'] : 0,
'variation_id' => isset( $item['variation_id'] ) ? $item['variation_id'] : 0, 'variation_id' => isset( $item['variation_id'] ) ? $item['variation_id'] : 0,
'status' => $subscription->get_status(), 'status' => $subscription->get_status(),
// Subscription billing details // Subscription billing details
'period' => $subscription->billing_period, 'period' => $subscription->get_billing_period(),
'interval' => $subscription->billing_interval, 'interval' => $subscription->get_billing_interval(),
'length' => wcs_estimate_periods_between( ( 0 == $subscription->get_time( 'trial_end' ) ) ? $subscription->get_time( 'start' ) : $subscription->get_time( 'trial_end' ), $subscription->get_time( 'end' ) + 120, $subscription->billing_period, 'floor' ) / $subscription->billing_interval, // Since subscriptions no longer have a length, we need to calculate the length given the start and end dates and the period. 'length' => wcs_estimate_periods_between( ( 0 == $subscription->get_time( 'trial_end' ) ) ? $subscription->get_time( 'date_created' ) : $subscription->get_time( 'trial_end' ), $subscription->get_time( 'end' ) + 120, $subscription->get_billing_period(), 'floor' ) / $subscription->get_billing_interval(), // Since subscriptions no longer have a length, we need to calculate the length given the start and end dates and the period.
// Subscription dates // Subscription dates
'start_date' => $subscription->get_date( 'start' ), 'start_date' => $subscription->get_date( 'date_created' ),
'expiry_date' => $subscription->get_date( 'end' ), 'expiry_date' => $subscription->get_date( 'end' ),
'end_date' => $subscription->has_status( wcs_get_subscription_ended_statuses() ) ? $subscription->get_date( 'end' ) : 0, 'end_date' => $subscription->has_status( wcs_get_subscription_ended_statuses() ) ? $subscription->get_date( 'end' ) : 0,
'trial_expiry_date' => $subscription->get_date( 'trial_end' ), 'trial_expiry_date' => $subscription->get_date( 'trial_end' ),
// Payment & status change history // Payment & status change history
'failed_payments' => $subscription->failed_payment_count, 'failed_payments' => $subscription->get_failed_payment_count(),
'completed_payments' => $completed_payments, 'completed_payments' => $completed_payments,
'suspension_count' => $subscription->suspension_count, 'suspension_count' => $subscription->get_suspension_count(),
'last_payment_date' => $subscription->get_date( 'last_payment' ), 'last_payment_date' => $subscription->get_date( 'last_order_date_created' ),
); );
} else { } else {

View File

@@ -158,3 +158,31 @@ function wcs_get_rounding_precision() {
return $precision; return $precision;
} }
/**
* Add a prefix to a string if it doesn't already have it
*
* @param string
* @param string
* @since 2.2.0
* @return string
*/
function wcs_maybe_prefix_key( $key, $prefix = '_' ) {
return ( substr( $key, 0, strlen( $prefix ) ) != $prefix ) ? $prefix . $key : $key;
}
/**
* Find the name of the function which called the function which called this function.
*
* @since 2.2.0
* @return string
*/
function wcs_get_calling_function_name() {
$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 3 );
$calling_function = isset( $backtrace[2]['class'] ) ? $backtrace[2]['class'] : '';
$calling_function .= isset( $backtrace[2]['type'] ) ? $backtrace[2]['type'] : '';
$calling_function .= isset( $backtrace[2]['function'] ) ? $backtrace[2]['function'] : '';
return $calling_function;
}

View File

@@ -19,17 +19,12 @@ if ( ! defined( 'ABSPATH' ) ) {
* @return string containing the limit type * @return string containing the limit type
*/ */
function wcs_get_product_limitation( $product ) { function wcs_get_product_limitation( $product ) {
if ( ! is_object( $product ) ) {
if ( ! is_object( $product ) || ! is_a( $product, 'WC_Product' ) ) {
$product = wc_get_product( $product ); $product = wc_get_product( $product );
} }
if ( ! isset( $product->product_custom_fields['_subscription_limit'][0] ) ) { return apply_filters( 'woocommerce_subscriptions_product_limitation', WC_Subscriptions_Product::get_meta_data( $product, 'subscription_limit', 0 ), $product );
return 'no';
} elseif ( 'yes' == $product->product_custom_fields['_subscription_limit'][0] ) { // backward compatibility
return 'any';
} else {
return $product->product_custom_fields['_subscription_limit'][0];
}
} }
/** /**
@@ -43,5 +38,5 @@ function wcs_is_product_limited_for_user( $product, $user_id = 0 ) {
$product = wc_get_product( $product ); $product = wc_get_product( $product );
} }
return ( ( 'active' == wcs_get_product_limitation( $product ) && wcs_user_has_subscription( $user_id, $product->id, 'on-hold' ) ) || ( 'no' !== wcs_get_product_limitation( $product ) && wcs_user_has_subscription( $user_id, $product->id, wcs_get_product_limitation( $product ) ) ) ) ? true : false; return ( ( 'active' == wcs_get_product_limitation( $product ) && wcs_user_has_subscription( $user_id, $product->get_id(), 'on-hold' ) ) || ( 'no' !== wcs_get_product_limitation( $product ) && wcs_user_has_subscription( $user_id, $product->get_id(), wcs_get_product_limitation( $product ) ) ) ) ? true : false;
} }

View File

@@ -32,7 +32,7 @@ if ( ! defined( 'ABSPATH' ) ) {
function wcs_get_subscriptions_for_order( $order_id, $args = array() ) { function wcs_get_subscriptions_for_order( $order_id, $args = array() ) {
if ( is_object( $order_id ) ) { if ( is_object( $order_id ) ) {
$order_id = $order_id->id; $order_id = wcs_get_objects_property( $order_id, 'id' );
} }
$args = wp_parse_args( $args, array( $args = wp_parse_args( $args, array(
@@ -83,31 +83,31 @@ function wcs_copy_order_address( $from_order, $to_order, $address_type = 'all' )
if ( in_array( $address_type, array( 'shipping', 'all' ) ) ) { if ( in_array( $address_type, array( 'shipping', 'all' ) ) ) {
$to_order->set_address( array( $to_order->set_address( array(
'first_name' => $from_order->shipping_first_name, 'first_name' => wcs_get_objects_property( $from_order, 'shipping_first_name' ),
'last_name' => $from_order->shipping_last_name, 'last_name' => wcs_get_objects_property( $from_order, 'shipping_last_name' ),
'company' => $from_order->shipping_company, 'company' => wcs_get_objects_property( $from_order, 'shipping_company' ),
'address_1' => $from_order->shipping_address_1, 'address_1' => wcs_get_objects_property( $from_order, 'shipping_address_1' ),
'address_2' => $from_order->shipping_address_2, 'address_2' => wcs_get_objects_property( $from_order, 'shipping_address_2' ),
'city' => $from_order->shipping_city, 'city' => wcs_get_objects_property( $from_order, 'shipping_city' ),
'state' => $from_order->shipping_state, 'state' => wcs_get_objects_property( $from_order, 'shipping_state' ),
'postcode' => $from_order->shipping_postcode, 'postcode' => wcs_get_objects_property( $from_order, 'shipping_postcode' ),
'country' => $from_order->shipping_country, 'country' => wcs_get_objects_property( $from_order, 'shipping_country' ),
), 'shipping' ); ), 'shipping' );
} }
if ( in_array( $address_type, array( 'billing', 'all' ) ) ) { if ( in_array( $address_type, array( 'billing', 'all' ) ) ) {
$to_order->set_address( array( $to_order->set_address( array(
'first_name' => $from_order->billing_first_name, 'first_name' => wcs_get_objects_property( $from_order, 'billing_first_name' ),
'last_name' => $from_order->billing_last_name, 'last_name' => wcs_get_objects_property( $from_order, 'billing_last_name' ),
'company' => $from_order->billing_company, 'company' => wcs_get_objects_property( $from_order, 'billing_company' ),
'address_1' => $from_order->billing_address_1, 'address_1' => wcs_get_objects_property( $from_order, 'billing_address_1' ),
'address_2' => $from_order->billing_address_2, 'address_2' => wcs_get_objects_property( $from_order, 'billing_address_2' ),
'city' => $from_order->billing_city, 'city' => wcs_get_objects_property( $from_order, 'billing_city' ),
'state' => $from_order->billing_state, 'state' => wcs_get_objects_property( $from_order, 'billing_state' ),
'postcode' => $from_order->billing_postcode, 'postcode' => wcs_get_objects_property( $from_order, 'billing_postcode' ),
'country' => $from_order->billing_country, 'country' => wcs_get_objects_property( $from_order, 'billing_country' ),
'email' => $from_order->billing_email, 'email' => wcs_get_objects_property( $from_order, 'billing_email' ),
'phone' => $from_order->billing_phone, 'phone' => wcs_get_objects_property( $from_order, 'billing_phone' ),
), 'billing' ); ), 'billing' );
} }
@@ -150,7 +150,9 @@ function wcs_copy_order_meta( $from_order, $to_order, $type = 'subscription' ) {
AND `meta_key` NOT LIKE '_schedule_%%' AND `meta_key` NOT LIKE '_schedule_%%'
AND `meta_key` NOT IN ( AND `meta_key` NOT IN (
'_paid_date', '_paid_date',
'_date_paid',
'_completed_date', '_completed_date',
'_date_completed',
'_order_key', '_order_key',
'_edit_lock', '_edit_lock',
'_wc_points_earned', '_wc_points_earned',
@@ -163,7 +165,7 @@ function wcs_copy_order_meta( $from_order, $to_order, $type = 'subscription' ) {
'_payment_method', '_payment_method',
'_payment_method_title' '_payment_method_title'
)", )",
$from_order->id wcs_get_objects_property( $from_order, 'id' )
); );
if ( 'renewal_order' == $type ) { if ( 'renewal_order' == $type ) {
@@ -176,7 +178,7 @@ function wcs_copy_order_meta( $from_order, $to_order, $type = 'subscription' ) {
$meta = apply_filters( 'wcs_' . $type . '_meta', $meta, $to_order, $from_order ); $meta = apply_filters( 'wcs_' . $type . '_meta', $meta, $to_order, $from_order );
foreach ( $meta as $meta_item ) { foreach ( $meta as $meta_item ) {
update_post_meta( $to_order->id, $meta_item['meta_key'], maybe_unserialize( $meta_item['meta_value'] ) ); wcs_set_objects_property( $to_order, $meta_item['meta_key'], maybe_unserialize( $meta_item['meta_value'] ) );
} }
} }
@@ -208,11 +210,9 @@ function wcs_create_order_from_subscription( $subscription, $type ) {
$new_order = wc_create_order( array( $new_order = wc_create_order( array(
'customer_id' => $subscription->get_user_id(), 'customer_id' => $subscription->get_user_id(),
'customer_note' => $subscription->customer_note, 'customer_note' => $subscription->get_customer_note(),
) ); ) );
$new_order->post->post_title = wcs_get_new_order_title( $type );
wcs_copy_order_meta( $subscription, $new_order, $type ); wcs_copy_order_meta( $subscription, $new_order, $type );
// Copy over line items and allow extensions to add/remove items or item meta // Copy over line items and allow extensions to add/remove items or item meta
@@ -225,17 +225,24 @@ function wcs_create_order_from_subscription( $subscription, $type ) {
$item_name = apply_filters( 'wcs_' . $type . '_item_name', $item_name, $item, $subscription ); $item_name = apply_filters( 'wcs_' . $type . '_item_name', $item_name, $item, $subscription );
// Create order line item on the renewal order // Create order line item on the renewal order
$recurring_item_id = wc_add_order_item( $new_order->id, array( $order_item_id = wc_add_order_item( wcs_get_objects_property( $new_order, 'id' ), array(
'order_item_name' => $item_name, 'order_item_name' => $item_name,
'order_item_type' => $item['type'], 'order_item_type' => $item['type'],
) ); ) );
// Remove recurring line items and set item totals based on recurring line totals // Remove recurring line items and set item totals based on recurring line totals
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
foreach ( $item['item_meta'] as $meta_key => $meta_values ) { foreach ( $item['item_meta'] as $meta_key => $meta_values ) {
foreach ( $meta_values as $meta_value ) { foreach ( $meta_values as $meta_value ) {
wc_add_order_item_meta( $recurring_item_id, $meta_key, maybe_unserialize( $meta_value ) ); wc_add_order_item_meta( $order_item_id, $meta_key, maybe_unserialize( $meta_value ) );
} }
} }
} else {
$order_item = $new_order->get_item( $order_item_id );
wcs_copy_order_item( $item, $order_item );
$order_item->save();
}
// If the line item we're adding is a product line item and that product still exists, trigger the 'woocommerce_order_add_product' hook // If the line item we're adding is a product line item and that product still exists, trigger the 'woocommerce_order_add_product' hook
if ( 'line_item' == $item['type'] && isset( $item['product_id'] ) ) { if ( 'line_item' == $item['type'] && isset( $item['product_id'] ) ) {
@@ -256,8 +263,8 @@ function wcs_create_order_from_subscription( $subscription, $type ) {
); );
// If we have a variation, get the attribute meta data from teh item to pass to callbacks // If we have a variation, get the attribute meta data from teh item to pass to callbacks
if ( ! empty( $item['variation_id'] ) && ! empty( $product->variation_data ) ) { if ( ! empty( $item['variation_id'] ) && null !== ( $variation_data = wcs_get_objects_property( $product, 'variation_data' ) ) ) {
foreach ( $product->variation_data as $attribute => $variation ) { foreach ( $variation_data as $attribute => $variation ) {
if ( isset( $item[ str_replace( 'attribute_', '', $attribute ) ] ) ) { if ( isset( $item[ str_replace( 'attribute_', '', $attribute ) ] ) ) {
$args['variation'][ $attribute ] = $item[ str_replace( 'attribute_', '', $attribute ) ]; $args['variation'][ $attribute ] = $item[ str_replace( 'attribute_', '', $attribute ) ];
} }
@@ -265,11 +272,17 @@ function wcs_create_order_from_subscription( $subscription, $type ) {
} }
// Backorders // Backorders
if ( $product->backorders_require_notification() && $product->is_on_backorder( $item['qty'] ) ) { if ( isset( $order_item ) && is_callable( array( $order_item, 'set_backorder_meta' ) ) ) { // WC 3.0
wc_add_order_item_meta( $recurring_item_id, apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce-subscriptions' ) ), $item['qty'] - max( 0, $product->get_total_stock() ) ); $order_item->set_backorder_meta();
$order_item->save();
} elseif ( $product->backorders_require_notification() && $product->is_on_backorder( $item['qty'] ) ) { // WC 2.6
wc_add_order_item_meta( $order_item_id, apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce-subscriptions' ) ), $item['qty'] - max( 0, $product->get_total_stock() ) );
} }
do_action( 'woocommerce_order_add_product', $new_order->id, $recurring_item_id, $product, $item['qty'], $args ); if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
// WC 3.0+ will also trigger the 'woocommerce_order_add_product when 'woocommerce_new_order_item', which is triggered in wc_add_order_item_meta()
do_action( 'woocommerce_order_add_product', wcs_get_objects_property( $new_order, 'id' ), $order_item_id, $product, $item['qty'], $args );
}
} }
} }
} }
@@ -294,6 +307,8 @@ function wcs_create_order_from_subscription( $subscription, $type ) {
* @return string new title for a post * @return string new title for a post
*/ */
function wcs_get_new_order_title( $type ) { function wcs_get_new_order_title( $type ) {
wcs_deprecated_function( __FUNCTION__, '2.2.0' );
$type = wcs_validate_new_order_type( $type ); $type = wcs_validate_new_order_type( $type );
$order_date = strftime( _x( '%b %d, %Y @ %I:%M %p', 'Used in subscription post title. "Subscription renewal order - <this>"', 'woocommerce-subscriptions' ) ); $order_date = strftime( _x( '%b %d, %Y @ %I:%M %p', 'Used in subscription post title. "Subscription renewal order - <this>"', 'woocommerce-subscriptions' ) );
@@ -346,29 +361,29 @@ function wcs_get_order_address( $order, $address_type = 'shipping' ) {
if ( 'billing' == $address_type ) { if ( 'billing' == $address_type ) {
$address = array( $address = array(
'first_name' => $order->billing_first_name, 'first_name' => wcs_get_objects_property( $order, 'billing_first_name' ),
'last_name' => $order->billing_last_name, 'last_name' => wcs_get_objects_property( $order, 'billing_last_name' ),
'company' => $order->billing_company, 'company' => wcs_get_objects_property( $order, 'billing_company' ),
'address_1' => $order->billing_address_1, 'address_1' => wcs_get_objects_property( $order, 'billing_address_1' ),
'address_2' => $order->billing_address_2, 'address_2' => wcs_get_objects_property( $order, 'billing_address_2' ),
'city' => $order->billing_city, 'city' => wcs_get_objects_property( $order, 'billing_city' ),
'state' => $order->billing_state, 'state' => wcs_get_objects_property( $order, 'billing_state' ),
'postcode' => $order->billing_postcode, 'postcode' => wcs_get_objects_property( $order, 'billing_postcode' ),
'country' => $order->billing_country, 'country' => wcs_get_objects_property( $order, 'billing_country' ),
'email' => $order->billing_email, 'email' => wcs_get_objects_property( $order, 'billing_email' ),
'phone' => $order->billing_phone, 'phone' => wcs_get_objects_property( $order, 'billing_phone' ),
); );
} else { } else {
$address = array( $address = array(
'first_name' => $order->shipping_first_name, 'first_name' => wcs_get_objects_property( $order, 'shipping_first_name' ),
'last_name' => $order->shipping_last_name, 'last_name' => wcs_get_objects_property( $order, 'shipping_last_name' ),
'company' => $order->shipping_company, 'company' => wcs_get_objects_property( $order, 'shipping_company' ),
'address_1' => $order->shipping_address_1, 'address_1' => wcs_get_objects_property( $order, 'shipping_address_1' ),
'address_2' => $order->shipping_address_2, 'address_2' => wcs_get_objects_property( $order, 'shipping_address_2' ),
'city' => $order->shipping_city, 'city' => wcs_get_objects_property( $order, 'shipping_city' ),
'state' => $order->shipping_state, 'state' => wcs_get_objects_property( $order, 'shipping_state' ),
'postcode' => $order->shipping_postcode, 'postcode' => wcs_get_objects_property( $order, 'shipping_postcode' ),
'country' => $order->shipping_country, 'country' => wcs_get_objects_property( $order, 'shipping_country' ),
); );
} }
@@ -397,7 +412,7 @@ function wcs_order_contains_subscription( $order, $order_type = array( 'parent',
$contains_subscription = false; $contains_subscription = false;
$get_all = ( in_array( 'any', $order_type ) ) ? true : false; $get_all = ( in_array( 'any', $order_type ) ) ? true : false;
if ( ( in_array( 'parent', $order_type ) || $get_all ) && count( wcs_get_subscriptions_for_order( $order->id, array( 'order_type' => 'parent' ) ) ) > 0 ) { if ( ( in_array( 'parent', $order_type ) || $get_all ) && count( wcs_get_subscriptions_for_order( wcs_get_objects_property( $order, 'id' ), array( 'order_type' => 'parent' ) ) ) > 0 ) {
$contains_subscription = true; $contains_subscription = true;
} elseif ( ( in_array( 'renewal', $order_type ) || $get_all ) && wcs_order_contains_renewal( $order ) ) { } elseif ( ( in_array( 'renewal', $order_type ) || $get_all ) && wcs_order_contains_renewal( $order ) ) {
@@ -657,3 +672,99 @@ function wcs_get_line_item_name( $line_item ) {
return apply_filters( 'wcs_line_item_name', $line_item_name, $line_item ); return apply_filters( 'wcs_line_item_name', $line_item_name, $line_item );
} }
/**
* Display item meta data in a version compatible way.
*
* @since 2.2.0
* @param WC_Item $item
* @param WC_Order $order
* @return void
*/
function wcs_display_item_meta( $item, $order ) {
if ( function_exists( 'wc_display_item_meta' ) ) { // WC 3.0+
wc_display_item_meta( $item );
} else {
$order->display_item_meta( $item );
}
}
/**
* Display item download links in a version compatible way.
*
* @since 2.2.0
* @param WC_Item $item
* @param WC_Order $order
* @return void
*/
function wcs_display_item_downloads( $item, $order ) {
if ( function_exists( 'wc_display_item_downloads' ) ) { // WC 3.0+
wc_display_item_downloads( $item );
} else {
$order->display_item_downloads( $item );
}
}
/**
* Copy the order item data and meta data from one item to another.
*
* @since 2.2.0
* @param WC_Order_Item The order item to copy data from
* @param WC_Order_Item The order item to copy data to
* @return void
*/
function wcs_copy_order_item( $from_item, &$to_item ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
wcs_doing_it_wrong( __FUNCTION__, 'This function uses data structures introduced in WC 3.0. To copy line item meta use $from_item[\'item_meta\'] and wc_add_order_item_meta().', '2.2' );
return;
}
foreach ( $from_item->get_meta_data() as $meta_data ) {
$to_item->update_meta_data( $meta_data->key, $meta_data->value );
}
switch ( $from_item->get_type() ) {
case 'line_item':
$to_item->set_props( array(
'product_id' => $from_item->get_product_id(),
'variation_id' => $from_item->get_variation_id(),
'quantity' => $from_item->get_quantity(),
'tax_class' => $from_item->get_tax_class(),
'subtotal' => $from_item->get_subtotal(),
'total' => $from_item->get_total(),
'taxes' => $from_item->get_taxes(),
) );
break;
case 'shipping':
$to_item->set_props( array(
'method_id' => $from_item->get_method_id(),
'total' => $from_item->get_total(),
'taxes' => $from_item->get_taxes(),
) );
break;
case 'tax':
$to_item->set_props( array(
'rate_id' => $from_item->get_rate_id(),
'label' => $from_item->get_label(),
'compound' => $from_item->get_compound(),
'tax_total' => $from_item->get_tax_total(),
'shipping_tax_total' => $from_item->get_shipping_tax_total(),
) );
break;
case 'fee':
$to_item->set_props( array(
'tax_class' => $from_item->get_tax_class(),
'tax_status' => $from_item->get_tax_status(),
'total' => $from_item->get_total(),
'taxes' => $from_item->get_taxes(),
) );
break;
case 'coupon':
$to_item->set_props( array(
'discount' => $from_item->discount(),
'discount_tax' => $from_item->discount_tax(),
) );
break;
}
}

View File

@@ -0,0 +1,414 @@
<?php
/**
* WooCommerce Subscriptions Product Functions
*
* Functions for managing renewal of a subscription.
*
* @author Prospress
* @category Core
* @package WooCommerce Subscriptions/Functions
* @version 2.2.0
*/
/**
* For a given product, and optionally price/qty, work out the sign-up with tax included, based on store settings.
*
* @since 2.2.0
* @param WC_Product $product
* @param array $args
* @return float
*/
function wcs_get_price_including_tax( $product, $args = array() ) {
$args = wp_parse_args( $args, array(
'qty' => 1,
'price' => $product->get_price(),
) );
if ( function_exists( 'wc_get_price_including_tax' ) ) { // WC 3.0+
$price = wc_get_price_including_tax( $product, $args );
} else { // WC < 3.0
$price = $product->get_price_including_tax( $args['qty'], $args['price'] );
}
return $price;
}
/**
* For a given product, and optionally price/qty, work out the sign-up fee with tax excluded, based on store settings.
*
* @since 2.2.0
* @param WC_Product $product
* @param array $args
* @return float
*/
function wcs_get_price_excluding_tax( $product, $args = array() ) {
$args = wp_parse_args( $args, array(
'qty' => 1,
'price' => $product->get_price(),
) );
if ( function_exists( 'wc_get_price_excluding_tax' ) ) { // WC 3.0+
$price = wc_get_price_excluding_tax( $product, $args );
} else { // WC < 3.0
$price = $product->get_price_excluding_tax( $args['qty'], $args['price'] );
}
return $price;
}
/**
* Returns a 'from' prefix if you want to show where prices start at.
*
* @since 2.2.0
* @return string
*/
function wcs_get_price_html_from_text( $product = '' ) {
if ( function_exists( 'wc_get_price_html_from_text' ) ) { // WC 3.0+
$price_html_from_text = wc_get_price_html_from_text();
} else { // WC < 3.0
$price_html_from_text = $product->get_price_html_from_text();
}
return $price_html_from_text;
}
/**
* Get an array of the prices, used to help determine min/max values.
*
* @since 2.2.0
*/
function wcs_get_variation_prices( $variation, $variable_product ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$regular_price = $variation->regular_price;
$sale_price = $variation->sale_price;
} else {
$regular_price = $variation->get_regular_price( 'edit' );
$sale_price = $variation->get_sale_price( 'edit' );
}
$prices = array(
'price' => apply_filters( 'woocommerce_variation_prices_price', WC_Subscriptions_Product::get_price( $variation ), $variation, $variable_product ),
'regular_price' => apply_filters( 'woocommerce_variation_prices_regular_price', WC_Subscriptions_Product::get_regular_price( $variation, 'edit' ), $variation, $variable_product ),
'sale_price' => apply_filters( 'woocommerce_variation_prices_sale_price', WC_Subscriptions_Product::get_sale_price( $variation, 'edit' ), $variation, $variable_product ),
'sign_up_fee' => apply_filters( 'woocommerce_variation_prices_sign_up_fee', WC_Subscriptions_Product::get_sign_up_fee( $variation ), $variation, $variable_product ),
);
return $prices;
}
/**
* Get an array of the minimum and maximum priced variations based on subscription billing terms.
*
* @param array $child_variation_ids the IDs of product variation children ids
* @return array() Array containing the min and max variation prices and billing data
* @since 2.2.0
*/
function wcs_get_min_max_variation_data( $variable_product, $child_variation_ids = array() ) {
if ( empty( $child_variation_ids ) ) {
$child_variation_ids = is_callable( array( $variable_product, 'get_visible_children' ) ) ? $variable_product->get_visible_children() : $variable_product->get_children( true );
}
$variations_data = array();
foreach ( $child_variation_ids as $variation_id ) {
if ( $variation = wc_get_product( $variation_id ) ) {
$prices = wcs_get_variation_prices( $variation, $variable_product );
foreach ( $prices as $price_key => $amount ) {
if ( '' !== $amount ) {
if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) {
$prices[ $price_key ] = wcs_get_price_including_tax( $variable_product, array( 'price' => $amount ) );
} else {
$prices[ $price_key ] = wcs_get_price_excluding_tax( $variable_product, array( 'price' => $amount ) );
}
}
}
$variations_data[ $variation_id ] = array(
'price' => $prices['price'],
'regular_price' => $prices['regular_price'],
'sale_price' => $prices['sale_price'],
'subscription' => array(
'sign_up_fee' => $prices['sign_up_fee'],
'period' => WC_Subscriptions_Product::get_period( $variation ),
'interval' => WC_Subscriptions_Product::get_interval( $variation ),
'trial_length' => WC_Subscriptions_Product::get_trial_length( $variation ),
'trial_period' => WC_Subscriptions_Product::get_trial_period( $variation ),
'length' => WC_Subscriptions_Product::get_length( $variation ),
),
);
}
}
return wcs_calculate_min_max_variations( $variations_data );
}
/**
* Determine the minimum and maximum values for a set of structured subscription
* price data in a form created by @see wcs_get_min_max_variation_data()
*
* @param array $child_variation_ids the IDs of product variation children ids
* @return array
* @since 2.2.0
*/
function wcs_calculate_min_max_variations( $variations_data ) {
$lowest_initial_amount = $highest_initial_amount = $lowest_price = $highest_price = '';
$shortest_initial_period = $longest_initial_period = $shortest_trial_period = $longest_trial_period = $shortest_trial_length = $longest_trial_length = '';
$longest_initial_interval = $shortest_initial_interval = $variable_subscription_period = $variable_subscription_period_interval = '';
$lowest_regular_price = $highest_regular_price = $lowest_sale_price = $highest_sale_price = $max_subscription_period = $max_subscription_period_interval = '';
$variable_subscription_sign_up_fee = $variable_subscription_trial_period = $variable_subscription_trial_length = $variable_subscription_length = $variable_subscription_sign_up_fee = $variable_subscription_trial_period = $variable_subscription_trial_length = $variable_subscription_length = '';
$min_variation_id = $max_variation_id = null;
foreach ( $variations_data as $variation_id => $variation_data ) {
$is_max = $is_min = false;
if ( empty( $variation_data['price'] ) && empty( $variation_data['subscription']['sign_up_fee'] ) ) {
continue;
}
$has_free_trial = ( '' !== $variation_data['subscription']['trial_length'] && $variation_data['subscription']['trial_length'] > 0 ) ? true : false;
// Determine some recurring price flags
$is_lowest_price = ( $variation_data['price'] < $lowest_price || '' === $lowest_price ) ? true : false;
$is_longest_period = ( WC_Subscriptions::get_longest_period( $variable_subscription_period, $variation_data['subscription']['period'] ) === $variation_data['subscription']['period'] ) ? true : false;
$is_longest_interval = ( $variation_data['subscription']['interval'] >= $variable_subscription_period_interval || '' === $variable_subscription_period_interval ) ? true : false;
// Find the amount the subscriber will have to pay up-front
if ( $has_free_trial ) {
$initial_amount = $variation_data['subscription']['sign_up_fee'];
$initial_period = $variation_data['subscription']['trial_period'];
$initial_interval = $variation_data['subscription']['trial_length'];
} else {
$initial_amount = $variation_data['price'] + $variation_data['subscription']['sign_up_fee'];
$initial_period = $variation_data['subscription']['period'];
$initial_interval = $variation_data['subscription']['interval'];
}
// We have a free trial & no sign-up fee, so need to choose the longest free trial (and maybe the shortest)
if ( $has_free_trial && 0 == $variation_data['subscription']['sign_up_fee'] ) {
// First variation
if ( '' === $longest_trial_period ) {
$is_min = true;
// If two variations have the same free trial, choose the variation with the lowest recurring price for the longest period
} elseif ( $variable_subscription_trial_period === $variation_data['subscription']['trial_period'] && $variation_data['subscription']['trial_length'] === $variable_subscription_trial_length ) {
// If the variation has the lowest recurring price, it's the cheapest
if ( $is_lowest_price ) {
$is_min = true;
// When current variation's free trial is the same as the lowest, it's the cheaper if it has a longer billing schedule
} elseif ( $variation_data['price'] === $lowest_price ) {
if ( $is_longest_period && $is_longest_interval ) {
$is_min = true;
// Longest with a new billing period
} elseif ( $is_longest_period && $variation_data['subscription']['period'] !== $variable_subscription_trial_period ) {
$is_min = true;
}
}
// Otherwise the cheapest variation is the one with the longer trial
} elseif ( $variable_subscription_trial_period === $variation_data['subscription']['trial_period'] ) {
$is_min = ( $variation_data['subscription']['trial_length'] > $variable_subscription_trial_length ) ? true : false;
// Otherwise just a longer trial period (that isn't equal to the longest period)
} elseif ( WC_Subscriptions::get_longest_period( $longest_trial_period, $variation_data['subscription']['trial_period'] ) === $variation_data['subscription']['trial_period'] ) {
$is_min = true;
}
if ( $is_min ) {
$longest_trial_period = $variation_data['subscription']['trial_period'];
$longest_trial_length = $variation_data['subscription']['trial_length'];
}
// If the current cheapest variation is also free, then the shortest trial period is the most expensive
if ( 0 == $lowest_price || '' === $lowest_price ) {
if ( '' === $shortest_trial_period ) {
$is_max = true;
// Need to check trial length
} elseif ( $shortest_trial_period === $variation_data['subscription']['trial_period'] ) {
$is_max = ( $variation_data['subscription']['trial_length'] < $shortest_trial_length ) ? true : false;
// Need to find shortest period
} elseif ( WC_Subscriptions::get_shortest_period( $shortest_trial_period, $variation_data['subscription']['trial_period'] ) === $variation_data['subscription']['trial_period'] ) {
$is_max = true;
}
if ( $is_max ) {
$shortest_trial_period = $variation_data['subscription']['trial_period'];
$shortest_trial_length = $variation_data['subscription']['trial_length'];
}
}
} else {
$longest_initial_period = WC_Subscriptions::get_longest_period( $longest_initial_period, $initial_period );
$shortest_initial_period = WC_Subscriptions::get_shortest_period( $shortest_initial_period, $initial_period );
$is_lowest_initial_amount = ( $initial_amount < $lowest_initial_amount || '' === $lowest_initial_amount ) ? true : false;
$is_longest_initial_period = ( $initial_period === $longest_initial_period ) ? true : false;
$is_longest_initial_interval = ( $initial_interval >= $longest_initial_interval || '' === $longest_initial_interval ) ? true : false;
$is_highest_initial = ( $initial_amount > $highest_initial_amount || '' === $highest_initial_amount ) ? true : false;
$is_shortest_period = ( $initial_period === $shortest_initial_period || '' === $shortest_initial_period ) ? true : false;
$is_shortest_interval = ( $initial_interval < $shortest_initial_interval || '' === $shortest_initial_interval ) ? true : false;
// If we're not dealing with the lowest initial access amount, then ignore this variation
if ( ! $is_lowest_initial_amount && $initial_amount !== $lowest_initial_amount ) {
continue;
}
// If the variation has the lowest price, it's the cheapest
if ( $is_lowest_initial_amount ) {
$is_min = true;
// When current variation's price is the same as the lowest, it's the cheapest only if it has a longer billing schedule
} elseif ( $initial_amount === $lowest_initial_amount ) {
// We need to check the recurring schedule when the sign-up fee & free trial periods are equal
if ( $has_free_trial && $initial_period == $longest_initial_period && $initial_interval == $longest_initial_interval ) {
// If the variation has the lowest recurring price, it's the cheapest
if ( $is_lowest_price ) {
$is_min = true;
// When current variation's price is the same as the lowest, it's the cheapest only if it has a longer billing schedule
} elseif ( $variation_data['price'] === $lowest_price ) {
if ( $is_longest_period && $is_longest_interval ) {
$is_min = true;
// Longest with a new billing period
} elseif ( $is_longest_period && $variation_data['subscription']['period'] !== $variable_subscription_period ) {
$is_min = true;
}
}
// Longest initial term is the cheapest
} elseif ( $is_longest_initial_period && $is_longest_initial_interval ) {
$is_min = true;
// Longest with a new billing period
} elseif ( $is_longest_initial_period && $initial_period !== $variable_subscription_period ) {
$is_min = true;
}
}
// If we have the highest price for the shortest period, we might have the maximum variation
if ( $is_highest_initial && $is_shortest_period && $is_shortest_interval ) {
$is_max = true;
// But only if its for the shortest billing period
} elseif ( $variation_data['price'] === $highest_price ) {
if ( $is_shortest_period && $is_shortest_interval ) {
$is_max = true;
} elseif ( $is_shortest_period ) {
$is_max = true;
}
}
}
// If it's the min subscription terms
if ( $is_min ) {
$min_variation_id = $variation_id;
$lowest_price = $variation_data['price'];
$lowest_regular_price = $variation_data['regular_price'];
$lowest_sale_price = $variation_data['sale_price'];
$lowest_regular_price = ( '' === $lowest_regular_price ) ? 0 : $lowest_regular_price;
$lowest_sale_price = ( '' === $lowest_sale_price ) ? 0 : $lowest_sale_price;
$lowest_initial_amount = $initial_amount;
$longest_initial_period = $initial_period;
$longest_initial_interval = $initial_interval;
$variable_subscription_sign_up_fee = $variation_data['subscription']['sign_up_fee'];
$variable_subscription_period = $variation_data['subscription']['period'];
$variable_subscription_period_interval = $variation_data['subscription']['interval'];
$variable_subscription_trial_length = $variation_data['subscription']['trial_length'];
$variable_subscription_trial_period = $variation_data['subscription']['trial_period'];
$variable_subscription_length = $variation_data['subscription']['length'];
}
if ( $is_max ) {
$max_variation_id = $variation_id;
$highest_price = $variation_data['price'];
$highest_regular_price = $variation_data['regular_price'];
$highest_sale_price = $variation_data['sale_price'];
$highest_initial_amount = $initial_amount;
$highest_regular_price = ( '' === $highest_regular_price ) ? 0 : $highest_regular_price;
$highest_sale_price = ( '' === $highest_sale_price ) ? 0 : $highest_sale_price;
$max_subscription_period = $variation_data['subscription']['period'];
$max_subscription_period_interval = $variation_data['subscription']['interval'];
}
}
return array(
'min' => array(
'variation_id' => $min_variation_id,
'price' => $lowest_price,
'regular_price' => $lowest_regular_price,
'sale_price' => $lowest_sale_price,
'period' => $variable_subscription_period,
'interval' => $variable_subscription_period_interval,
),
'max' => array(
'variation_id' => $max_variation_id,
'price' => $highest_price,
'regular_price' => $highest_regular_price,
'sale_price' => $highest_sale_price,
'period' => $max_subscription_period,
'interval' => $max_subscription_period_interval,
),
'subscription' => array(
'signup-fee' => $variable_subscription_sign_up_fee,
'trial_period' => $variable_subscription_trial_period,
'trial_length' => $variable_subscription_trial_length,
'length' => $variable_subscription_length,
),
);
}

View File

@@ -32,7 +32,7 @@ function wcs_create_renewal_order( $subscription ) {
return new WP_Error( 'renewal-order-error', $renewal_order->get_error_message() ); return new WP_Error( 'renewal-order-error', $renewal_order->get_error_message() );
} }
update_post_meta( $renewal_order->id, '_subscription_renewal', $subscription->id ); wcs_set_objects_property( $renewal_order, 'subscription_renewal', $subscription->get_id(), 'save' );
return apply_filters( 'wcs_renewal_order_created', $renewal_order, $subscription ); return apply_filters( 'wcs_renewal_order_created', $renewal_order, $subscription );
} }
@@ -49,7 +49,7 @@ function wcs_order_contains_renewal( $order ) {
$order = wc_get_order( $order ); $order = wc_get_order( $order );
} }
if ( 'simple' == $order->order_type && isset( $order->subscription_renewal ) && $order->subscription_renewal > 0 ) { // It's a parent order or original order if ( wcs_is_order( $order ) && wcs_get_objects_property( $order, 'subscription_renewal' ) ) {
$is_renewal = true; $is_renewal = true;
} else { } else {
$is_renewal = false; $is_renewal = false;
@@ -119,7 +119,7 @@ function wcs_get_subscriptions_for_renewal_order( $order ) {
// Only use the order if we actually found a valid order object // Only use the order if we actually found a valid order object
if ( is_object( $order ) ) { if ( is_object( $order ) ) {
$subscription_ids = get_post_meta( $order->id, '_subscription_renewal', false ); $subscription_ids = wcs_get_objects_property( $order, 'subscription_renewal', 'multiple' );
foreach ( $subscription_ids as $subscription_id ) { foreach ( $subscription_ids as $subscription_id ) {
if ( wcs_is_subscription( $subscription_id ) ) { if ( wcs_is_subscription( $subscription_id ) ) {

View File

@@ -26,7 +26,7 @@ function wcs_order_contains_resubscribe( $order ) {
$order = new WC_Order( $order ); $order = new WC_Order( $order );
} }
if ( '' !== get_post_meta( $order->id, '_subscription_resubscribe', true ) ) { if ( wcs_get_objects_property( $order, 'subscription_resubscribe' ) ) {
$is_resubscribe_order = true; $is_resubscribe_order = true;
} else { } else {
$is_resubscribe_order = false; $is_resubscribe_order = false;
@@ -55,7 +55,7 @@ function wcs_create_resubscribe_order( $subscription ) {
} }
// Keep a record of the original subscription's ID on the new order // Keep a record of the original subscription's ID on the new order
update_post_meta( $resubscribe_order->id, '_subscription_resubscribe', $subscription->id, true ); wcs_set_objects_property( $resubscribe_order, 'subscription_resubscribe', $subscription->get_id(), true );
do_action( 'wcs_resubscribe_order_created', $resubscribe_order, $subscription ); do_action( 'wcs_resubscribe_order_created', $resubscribe_order, $subscription );
@@ -71,7 +71,7 @@ function wcs_create_resubscribe_order( $subscription ) {
*/ */
function wcs_get_users_resubscribe_link( $subscription ) { function wcs_get_users_resubscribe_link( $subscription ) {
$subscription_id = ( is_object( $subscription ) ) ? $subscription->id : $subscription; $subscription_id = ( is_object( $subscription ) ) ? $subscription->get_id() : $subscription;
$resubscribe_link = add_query_arg( array( 'resubscribe' => $subscription_id ), get_permalink( wc_get_page_id( 'myaccount' ) ) ); $resubscribe_link = add_query_arg( array( 'resubscribe' => $subscription_id ), get_permalink( wc_get_page_id( 'myaccount' ) ) );
$resubscribe_link = wp_nonce_url( $resubscribe_link, $subscription_id ); $resubscribe_link = wp_nonce_url( $resubscribe_link, $subscription_id );
@@ -143,7 +143,7 @@ function wcs_get_subscriptions_for_resubscribe_order( $order ) {
} }
$subscriptions = array(); $subscriptions = array();
$subscription_ids = get_post_meta( $order->id, '_subscription_resubscribe', false ); $subscription_ids = wcs_get_objects_property( $order, 'subscription_resubscribe', 'multiple' );
foreach ( $subscription_ids as $subscription_id ) { foreach ( $subscription_ids as $subscription_id ) {
if ( wcs_is_subscription( $subscription_id ) ) { if ( wcs_is_subscription( $subscription_id ) ) {
@@ -187,7 +187,7 @@ function wcs_can_user_resubscribe_to( $subscription, $user_id = '' ) {
$can_user_resubscribe = false; $can_user_resubscribe = false;
} elseif ( ! user_can( $user_id, 'subscribe_again', $subscription->id ) ) { } elseif ( ! user_can( $user_id, 'subscribe_again', $subscription->get_id() ) ) {
$can_user_resubscribe = false; $can_user_resubscribe = false;
@@ -206,7 +206,7 @@ function wcs_can_user_resubscribe_to( $subscription, $user_id = '' ) {
array( array(
'key' => '_subscription_resubscribe', 'key' => '_subscription_resubscribe',
'compare' => '=', 'compare' => '=',
'value' => $subscription->id, 'value' => $subscription->get_id(),
'type' => 'numeric', 'type' => 'numeric',
), ),
), ),
@@ -229,7 +229,7 @@ function wcs_can_user_resubscribe_to( $subscription, $user_id = '' ) {
break; break;
} }
if ( 'active' == wcs_get_product_limitation( $product ) && ( wcs_user_has_subscription( $user_id, $product->id, 'on-hold' ) || wcs_user_has_subscription( $user_id, $product->id, 'active' ) ) ) { if ( 'active' == wcs_get_product_limitation( $product ) && ( wcs_user_has_subscription( $user_id, $product->get_id(), 'on-hold' ) || wcs_user_has_subscription( $user_id, $product->get_id(), 'active' ) ) ) {
$has_active_limited_subscription = true; $has_active_limited_subscription = true;
break; break;
} }

View File

@@ -24,13 +24,13 @@ function wcs_order_contains_switch( $order ) {
$order = wc_get_order( $order ); $order = wc_get_order( $order );
} }
if ( 'simple' != $order->order_type || isset( $order->subscription_renewal ) ) { // It's a parent order or renewal order if ( ! wcs_is_order( $order ) || wcs_order_contains_renewal( $order ) ) {
$is_switch_order = false; $is_switch_order = false;
} else { } else {
$subscription_ids = get_post_meta( $order->id, '_subscription_switch', false ); $subscription_ids = wcs_get_objects_property( $order, 'subscription_switch', 'multiple' );
if ( ! empty( $subscription_ids ) ) { if ( ! empty( $subscription_ids ) ) {
$is_switch_order = true; $is_switch_order = true;
@@ -49,14 +49,14 @@ function wcs_order_contains_switch( $order ) {
* @return array Subscription details in post_id => WC_Subscription form. * @return array Subscription details in post_id => WC_Subscription form.
* @since 2.0 * @since 2.0
*/ */
function wcs_get_subscriptions_for_switch_order( $order_id ) { function wcs_get_subscriptions_for_switch_order( $order ) {
if ( is_object( $order_id ) ) { if ( ! is_object( $order ) ) {
$order_id = $order_id->id; $order = wc_get_order( $order );
} }
$subscriptions = array(); $subscriptions = array();
$subscription_ids = get_post_meta( $order_id, '_subscription_switch', false ); $subscription_ids = wcs_get_objects_property( $order, 'subscription_switch', 'multiple' );
foreach ( $subscription_ids as $subscription_id ) { foreach ( $subscription_ids as $subscription_id ) {
$subscriptions[ $subscription_id ] = wcs_get_subscription( $subscription_id ); $subscriptions[ $subscription_id ] = wcs_get_subscription( $subscription_id );
@@ -119,9 +119,11 @@ function wcs_is_product_switchable_type( $product ) {
} else { } else {
// back compat for parent products // back compat for parent products
if ( $product->is_type( 'subscription_variation' ) && ! empty( $product->parent ) ) { $parent_id = wcs_get_objects_property( $product, 'parent_id' );
if ( $product->is_type( 'subscription_variation' ) && ! empty( $parent_id ) ) {
$variation = $product; $variation = $product;
$product = $product->parent; $product = wc_get_product( $parent_id );;
} }
$allow_switching = get_option( WC_Subscriptions_Admin::$option_prefix . '_allow_switching', 'no' ); $allow_switching = get_option( WC_Subscriptions_Admin::$option_prefix . '_allow_switching', 'no' );
@@ -131,10 +133,10 @@ function wcs_is_product_switchable_type( $product ) {
$is_product_switchable = ( $product->is_type( array( 'variable-subscription', 'subscription_variation' ) ) ) ? true : false; $is_product_switchable = ( $product->is_type( array( 'variable-subscription', 'subscription_variation' ) ) ) ? true : false;
break; break;
case 'grouped' : case 'grouped' :
$is_product_switchable = ( 0 !== $product->post->post_parent ) ? true : false; $is_product_switchable = ( ! empty( $parent_id ) ) ? true : false;
break; break;
case 'variable_grouped' : case 'variable_grouped' :
$is_product_switchable = ( $product->is_type( array( 'variable-subscription', 'subscription_variation' ) ) || 0 !== $product->post->post_parent ) ? true : false; $is_product_switchable = ( $product->is_type( array( 'variable-subscription', 'subscription_variation' ) ) || ! empty( $parent_id ) ) ? true : false;
break; break;
case 'no' : case 'no' :
default: default:

View File

@@ -777,3 +777,70 @@ function wcs_get_subscription_ranges_tlc() {
return wcs_get_non_cached_subscription_ranges(); return wcs_get_non_cached_subscription_ranges();
} }
/**
* Take a date in the form of a timestamp, MySQL date/time string or DateTime object (or perhaps
* a WC_Datetime object when WC > 3.0 is active) and create a WC_DateTime object.
*
* @since 2.2.0
* @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
* @return null|WC_DateTime in site's timezone
*/
function wcs_get_datetime_from( $variable_date_type ) {
try {
if ( empty( $variable_date_type ) ) {
$datetime = null;
} elseif ( is_a( $variable_date_type, 'WC_DateTime' ) ) {
$datetime = $variable_date_type;
} elseif ( is_numeric( $variable_date_type ) ) {
$datetime = new WC_DateTime( "@{$variable_date_type}", new DateTimeZone( 'UTC' ) );
$datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
} else {
$datetime = new WC_DateTime( $variable_date_type, new DateTimeZone( wc_timezone_string() ) );
}
} catch ( Exception $e ) {
$datetime = null;
}
return $datetime;
}
/**
* Get a MySQL date/time string in UTC timezone from a WC_Datetime object.
*
* @since 2.2.0
* @param WC_DateTime
* @return string MySQL date/time string representation of the DateTime object in UTC timezone
*/
function wcs_get_datetime_utc_string( $datetime ) {
$date = clone $datetime; // Don't change the original date object's timezone
$date->setTimezone( new DateTimeZone( 'UTC' ) );
return $date->format( 'Y-m-d H:i:s' );
}
/**
* Format a date for output, a wrapper for wcs_format_datetime() introduced with WC 3.0.
*
* @since 2.2.0
* @param WC_DateTime $date
* @param string $format Defaults to the wc_date_format function if not set.
* @return string
*/
function wcs_format_datetime( $date, $format = '' ) {
if ( function_exists( 'wc_format_datetime' ) ) { // WC 3.0+
$formatted_datetime = wc_format_datetime( $date, $format );
} else { // WC < 3.0
if ( ! $format ) {
$format = wc_date_format();
}
if ( ! is_a( $date, 'WC_DateTime' ) ) {
return '';
}
$formatted_datetime = $date->date_i18n( $format );
}
return $formatted_datetime;
}

Some files were not shown because too many files have changed in this diff Show More