Updates to 5.6.0

This commit is contained in:
WooCommerce
2023-10-18 10:12:24 +00:00
parent ad47a6a8b3
commit 5eedf6aedb
52 changed files with 1132 additions and 741 deletions

View File

@@ -1,5 +1,28 @@
*** Woo Subscriptions Changelog *** *** Woo Subscriptions Changelog ***
2023-10-18 - version 5.6.0
* Add: Introduce the "Subscription Relationship" column under the Orders list admin page when HPOS is enabled.
* Add: Use admin theme color and the correct WooCommerce colors.
* Fix: Resolved an issue that caused subscriptions to go on-hold when a customer fails or abandons an early renewal order payment.
* Fix: Resolved an issue that caused subscriptions with an unpaid early renewal order to be incorrectly considered as needing payment.
* Fix: When HPOS is enabled, make the orders_by_type_query filter box work in the WooCommerce orders screen.
* Fix: Ensure renewal orders paid via the Block Checkout are correctly linked to their subscription.
* Fix: Resolved an issue that caused paying for failed/pending parent orders that include Product Add-ons to not calculate the correct total.
* Fix: Ensure the order needs processing transient is deleted when a subscription order (eg renewal) is created. Fixes issues with renewal orders going straight to a completed status.
* Fix: Store the correct subscription start date in postmeta and ordermeta when HPOS and data syncing is being used.
* Fix: When HPOS is enabled, deleting a customer will now delete their subscriptions.
* Fix: Missing styles on the Edit Subscription page when HPOS is enabled.
* Fix: Resolve an issue that would cause additional subscriptions to be created when completing a switch via the Block Checkout.
* Fix: Resolve an issue that would cause 3rd party plugin edit product fields with the show_if_variable-subscription class to be incorrectly hidden.
* Fix: Allow gateways to execute action on payment method deletion before updating the subscription.
* Fix: Ensure subscriptions have a date created that correctly accounts for the site's timezone. Fixes issues with subscriptions having a date created double the site's UTC offset.
* Fix: When HPOS is enabled, fix quick-editing the subscription statuses on the admin list table.
* Dev: PHP 8.2: Fix "Creation of dynamic property" warnings.
* Dev: PHP 8.2: Fix "Automatic conversion of false to array is deprecated" warnings.
* Dev: PHP warnings from using debug_backtrace().
* Dev: Updated subscriptions-core to 6.4.0
* Dev: Updated the hooks for Checkout Blocks, replacing the deprecated `woocommerce_blocks_checkout_` prefixed hooks with `woocommerce_store_api_checkout`.
2023-09-21 - version 5.5.0 2023-09-21 - version 5.5.0
* Tweak - Use admin theme color in selectors. * Tweak - Use admin theme color in selectors.
* Tweak - Change plugin name to Woo Subscriptions. * Tweak - Change plugin name to Woo Subscriptions.

View File

@@ -52,6 +52,11 @@ class WCS_Report_Dashboard {
$cached_results = get_transient( strtolower( __CLASS__ ) ); $cached_results = get_transient( strtolower( __CLASS__ ) );
// Set a default value for cached results for PHP 8.2+ compatibility.
if ( empty( $cached_results ) ) {
$cached_results = [];
}
// Subscription signups this month // Subscription signups this month
$query = $wpdb->prepare( $query = $wpdb->prepare(
"SELECT COUNT(DISTINCT wcsubs.ID) AS count "SELECT COUNT(DISTINCT wcsubs.ID) AS count
@@ -69,7 +74,7 @@ class WCS_Report_Dashboard {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_signup_query', $query ) ); $cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_signup_query', $query ) );
$update_cache = true; $update_cache = true;
@@ -128,7 +133,7 @@ class WCS_Report_Dashboard {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_renewal_query', $query ) ); $cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_renewal_query', $query ) );
$update_cache = true; $update_cache = true;
@@ -162,7 +167,7 @@ class WCS_Report_Dashboard {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_renewal_revenue_query', $query ) ); $cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_renewal_revenue_query', $query ) );
$update_cache = true; $update_cache = true;
@@ -185,7 +190,7 @@ class WCS_Report_Dashboard {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_cancellation_query', $query ) ); $cached_results[ $query_hash ] = $wpdb->get_var( apply_filters( 'woocommerce_subscription_dashboard_status_widget_cancellation_query', $query ) );
$update_cache = true; $update_cache = true;

View File

@@ -239,7 +239,12 @@ class WCS_Report_Subscription_By_Customer extends WP_List_Table {
$cached_results = get_transient( strtolower( __CLASS__ ) ); $cached_results = get_transient( strtolower( __CLASS__ ) );
$query_hash = md5( $total_query ); $query_hash = md5( $total_query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { // Set a default value for cached results for PHP 8.2+ compatibility.
if ( empty( $cached_results ) ) {
$cached_results = [];
}
if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
// Enable big selects for reports // Enable big selects for reports
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_customer_total_data', $wpdb->get_row( $total_query ) ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_customer_total_data', $wpdb->get_row( $total_query ) );
@@ -268,7 +273,7 @@ class WCS_Report_Subscription_By_Customer extends WP_List_Table {
$query_hash = md5( $renewal_switch_total_query ); $query_hash = md5( $renewal_switch_total_query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
// Enable big selects for reports // Enable big selects for reports
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_customer_total_renewal_switch_data', $wpdb->get_row( $renewal_switch_total_query ) ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_customer_total_renewal_switch_data', $wpdb->get_row( $renewal_switch_total_query ) );

View File

@@ -161,7 +161,12 @@ class WCS_Report_Subscription_By_Product extends WP_List_Table {
$cached_results = get_transient( strtolower( __CLASS__ ) ); $cached_results = get_transient( strtolower( __CLASS__ ) );
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { // Set a default value for cached results for PHP 8.2+ compatibility.
if ( empty( $cached_results ) ) {
$cached_results = [];
}
if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_product_data', $wpdb->get_results( $query, OBJECT_K ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_product_data', $wpdb->get_results( $query, OBJECT_K ), $args );
set_transient( strtolower( __CLASS__ ), $cached_results, WEEK_IN_SECONDS ); set_transient( strtolower( __CLASS__ ), $cached_results, WEEK_IN_SECONDS );
@@ -214,7 +219,7 @@ class WCS_Report_Subscription_By_Product extends WP_List_Table {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_product_lifetime_value_data', $wpdb->get_results( $query, OBJECT_K ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_product_lifetime_value_data', $wpdb->get_results( $query, OBJECT_K ), $args );
set_transient( strtolower( __CLASS__ ), $cached_results, WEEK_IN_SECONDS ); set_transient( strtolower( __CLASS__ ), $cached_results, WEEK_IN_SECONDS );

View File

@@ -268,6 +268,11 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$cached_results = get_transient( strtolower( get_class( $this ) ) ); $cached_results = get_transient( strtolower( get_class( $this ) ) );
// Set a default value for cached results for PHP 8.2+ compatibility.
if ( empty( $cached_results ) ) {
$cached_results = [];
}
// Check if we need to update the cache with the query results from the figures generated by get_order_report_data(). // Check if we need to update the cache with the query results from the figures generated by get_order_report_data().
foreach ( array( 'new_subscriptions' => 'new_subscriptions', 'renewals' => 'renewal', 'resubscribes' => 'resubscribe', 'switches' => 'switch' ) as $report => $property_key ) { // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound foreach ( array( 'new_subscriptions' => 'new_subscriptions', 'renewals' => 'renewal', 'resubscribes' => 'resubscribe', 'switches' => 'switch' ) as $report => $property_key ) { // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
$query_hash = $this->report_data->{"{$report}_query_hash"}; $query_hash = $this->report_data->{"{$report}_query_hash"};
@@ -314,7 +319,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_sign_up_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_sign_up_data', (array) $wpdb->get_results( $query ), $args );
$update_cache = true; $update_cache = true;
@@ -379,7 +384,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_subscriber_count_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_subscriber_count_data', (array) $wpdb->get_results( $query ), $args );
$update_cache = true; $update_cache = true;
@@ -408,7 +413,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_cancel_count_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_cancel_count_data', (array) $wpdb->get_results( $query ), $args );
$update_cache = true; $update_cache = true;
@@ -438,7 +443,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$query_hash = md5( $query ); $query_hash = md5( $query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_ended_count_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_ended_count_data', (array) $wpdb->get_results( $query ), $args );
$update_cache = true; $update_cache = true;

View File

@@ -17,6 +17,8 @@ class WCS_Report_Upcoming_Recurring_Revenue extends WC_Admin_Report {
public $order_ids_recurring_totals = null; public $order_ids_recurring_totals = null;
public $average_sales = 0;
/** /**
* Get the legend for the main chart sidebar * Get the legend for the main chart sidebar
* @return array * @return array
@@ -161,7 +163,12 @@ class WCS_Report_Upcoming_Recurring_Revenue extends WC_Admin_Report {
$cached_results = get_transient( strtolower( get_class( $this ) ) ); $cached_results = get_transient( strtolower( get_class( $this ) ) );
$query_hash = md5( $base_query ); $query_hash = md5( $base_query );
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { // Set a default value for cached results for PHP 8.2+ compatibility.
if ( empty( $cached_results ) ) {
$cached_results = [];
}
if ( $args['no_cache'] || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_upcoming_recurring_revenue_data', $wpdb->get_results( $base_query, OBJECT_K ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_upcoming_recurring_revenue_data', $wpdb->get_results( $base_query, OBJECT_K ), $args );
set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS ); set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS );

View File

@@ -14,6 +14,13 @@ if ( ! defined( 'ABSPATH' ) ) {
class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal { class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
/**
* The meta key used to store whether the subscription dates have been updated for an early renewal.
*
* @var string
*/
const SUBSCRIPTION_DATES_UPDATED_META_KEY = '_wcs_early_renewal_subscription_dates_updated';
/** /**
* Bootstraps the class and hooks required actions & filters. * Bootstraps the class and hooks required actions & filters.
*/ */
@@ -26,27 +33,14 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
add_action( 'template_redirect', array( $this, 'maybe_setup_cart' ), 100 ); add_action( 'template_redirect', array( $this, 'maybe_setup_cart' ), 100 );
add_action( 'woocommerce_checkout_create_order', array( $this, 'copy_subscription_meta_to_order' ), 90 ); add_action( 'woocommerce_checkout_create_order', array( $this, 'copy_subscription_meta_to_order' ), 90 );
// Record early renewal payments. // Record early renewal payments.
if ( wcs_is_woocommerce_pre( '3.0' ) ) {
add_action( 'woocommerce_checkout_order_processed', array( $this, 'maybe_record_early_renewal' ), 100, 2 );
} else {
add_action( 'woocommerce_checkout_create_order', array( $this, 'add_early_renewal_metadata_to_order' ), 100, 2 ); add_action( 'woocommerce_checkout_create_order', array( $this, 'add_early_renewal_metadata_to_order' ), 100, 2 );
if ( class_exists( 'Automattic\WooCommerce\Blocks\Package' ) ) {
if ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '7.2.0', '>=' ) ) {
add_action( 'woocommerce_store_api_checkout_update_order_meta', array( $this, 'add_early_renewal_metadata_to_order' ), 100, 1 ); add_action( 'woocommerce_store_api_checkout_update_order_meta', array( $this, 'add_early_renewal_metadata_to_order' ), 100, 1 );
} elseif ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '6.3.0', '>=' ) ) {
add_action( 'woocommerce_blocks_checkout_update_order_meta', array( $this, 'add_early_renewal_metadata_to_order' ), 100, 1 );
} else {
add_action( '__experimental_woocommerce_blocks_checkout_update_order_meta', array( $this, 'add_early_renewal_metadata_to_order' ), 100, 1 );
}
}
}
// Process early renewal by making sure subscription's dates are updated. // Handle early renewal orders status changes.
add_action( 'subscriptions_activated_for_order', array( $this, 'maybe_update_dates' ) ); add_action( 'woocommerce_order_status_changed', array( $this, 'maybe_record_subscription_payment' ), 5, 4 );
// Handle early renewal orders that are cancelled.
add_action( 'woocommerce_order_status_cancelled', array( $this, 'maybe_reactivate_subscription' ), 100, 2 );
// Add a subscription note to record early renewal order. // Add a subscription note to record early renewal order.
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'add_note_to_record_early_renewal' ) ); add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'add_note_to_record_early_renewal' ) );
@@ -55,7 +49,7 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'set_cart_item_renewal_order_data' ), 5 ); add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'set_cart_item_renewal_order_data' ), 5 );
// Allow customers to cancel early renewal orders from their my account page. // Allow customers to cancel early renewal orders from their my account page.
add_filter( 'woocommerce_my_account_my_orders_actions', array( $this, 'add_cancel_order_action' ), 15, 2 ); add_filter( 'woocommerce_my_account_my_orders_actions', array( $this, 'filter_early_renewal_order_actions' ), 15, 2 );
add_action( 'wp_loaded', array( $this, 'allow_early_renewal_order_cancellation' ), 10, 3 ); add_action( 'wp_loaded', array( $this, 'allow_early_renewal_order_cancellation' ), 10, 3 );
// Handles early renew of password-protected products. // Handles early renew of password-protected products.
@@ -128,37 +122,6 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
exit; exit;
} }
/**
* Records an early renewal against order created on checkout (only for WooCommerce < 3.0).
*
* @param int $order_id The post_id of a shop_order post/WC_Order object.
* @param array $posted_data The data posted on checkout.
* @since 2.3.0
*/
public function maybe_record_early_renewal( $order_id, $posted_data ) {
if ( ! wcs_is_woocommerce_pre( '3.0' ) ) {
wcs_deprecated_function( __METHOD__, '2.0', 'WCS_Cart_Early_Renewal::add_early_renewal_metadata_to_order( $order, $posted_data )' );
}
$cart_item = $this->cart_contains();
if ( ! $cart_item ) {
return;
}
// Get the subscription.
$subscription = wcs_get_subscription( $cart_item[ $this->cart_item_key ]['subscription_id'] );
// Mark this order as a renewal.
update_post_meta( $order_id, '_subscription_renewal', $subscription->get_id() );
// Mark this order as an early renewal.
update_post_meta( $order_id, '_subscription_renewal_early', $subscription->get_id() );
// Put the subscription on hold until payment is complete.
$subscription->update_status( 'on-hold', _x( 'Customer requested to renew early:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
}
/** /**
* Copies the metadata from the subscription to the order created on checkout. * Copies the metadata from the subscription to the order created on checkout.
* *
@@ -188,8 +151,8 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
* @since 2.3.0 * @since 2.3.0
*/ */
public function add_early_renewal_metadata_to_order( $order, $data = array() ) { public function add_early_renewal_metadata_to_order( $order, $data = array() ) {
$cart_item = $this->cart_contains(); $cart_item = $this->cart_contains();
if ( ! $cart_item ) { if ( ! $cart_item ) {
return; return;
} }
@@ -202,65 +165,6 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
// Mark this order as an early renewal. // Mark this order as an early renewal.
$order->update_meta_data( '_subscription_renewal_early', $subscription->get_id() ); $order->update_meta_data( '_subscription_renewal_early', $subscription->get_id() );
// Put the subscription on hold until payment is complete.
$subscription->update_status( 'on-hold', _x( 'Customer requested to renew early:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
}
/**
* Update the next payment and end dates on a subscription to extend them and account
* for early renewal.
*
* @param int $order_id The WC Order ID which contains an early renewal.
* @since 2.3.0
*/
public function maybe_update_dates( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order || ! wcs_order_contains_early_renewal( $order ) ) {
return;
}
$subscription_id = wcs_get_objects_property( $order, 'subscription_renewal_early' );
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
return;
}
wcs_update_dates_after_early_renewal( $subscription, $order );
}
/**
* Reactivates an on hold subscription when an early renewal order
* is cancelled by the user.
*
* @param int $order_id The WC Order ID which contains an early renewal.
* @since 2.3.0
*/
public function maybe_reactivate_subscription( $order_id ) {
// Get the order and make sure we have one.
$order = wc_get_order( $order_id );
if ( wcs_order_contains_early_renewal( $order ) ) {
// Get the subscription and make sure we have one.
$subscription = wcs_get_subscription( wcs_get_objects_property( $order, 'subscription_renewal_early' ) );
if ( ! $subscription || ! $subscription->has_status( 'on-hold' ) ) {
return;
}
// Make sure the next payment date isn't in the past.
if ( strtotime( $subscription->get_date( 'next_payment' ) ) < time() ) {
return;
}
// Reactivate the subscription.
$subscription->update_status( 'active' );
}
} }
/** /**
@@ -346,19 +250,33 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
} }
/** /**
* Ensure customers can cancel early renewal orders. * Filters the list of actions customers can make on an order from their My Account page.
* *
* Renewal orders are usually not cancellable because @see WCS_Cart_Renewal::filter_my_account_my_orders_actions() prevents it. * Unlike standard renewal orders early renewal orders can be cancelled and cannot be paid.
* In the case of early renewals, the customer has opted for early renewal and so should be able to cancel it in order to reactivate their subscription. *
* This function is intended to run after @see WCS_Cart_Renewal::filter_my_account_my_orders_actions() which removes the cancel and pay option.
*
* @param array $actions A list of actions customers can make on an order from their My Account page.
* @param WC_Order $order The order.
* *
* @param array $actions A list of actions customers can make on an order from their My Account page
* @param WC_Order $order The order the list of actions relate to.
* @return array $actions * @return array $actions
* @since 2.3.0
*/ */
public static function add_cancel_order_action( $actions, $order ) { public static function filter_early_renewal_order_actions( $actions, $order ) {
if ( ! isset( $actions['cancel'] ) && wcs_order_contains_early_renewal( $order ) && in_array( $order->get_status(), apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ), $order ) ) ) { // Bail if the order can already be cancelled and cannot be paid.
if ( isset( $actions['cancel'] ) && ! isset( $actions['pay'] ) ) {
return $actions;
}
if ( ! wcs_order_contains_early_renewal( $order ) ) {
return $actions;
}
// Early renewal orders that failed, cannot be paid. The customer must retry by following the early renewal flow again.
unset( $actions['pay'] );
// Add the cancel action back if the order has a status that allows it to be cancelled.
if ( ! isset( $actions['cancel'] ) && in_array( $order->get_status(), apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ), $order ) ) ) {
$redirect = wc_get_page_permalink( 'myaccount' ); $redirect = wc_get_page_permalink( 'myaccount' );
// Redirect the customer back to the view subscription page if that is where they cancel the order from. // Redirect the customer back to the view subscription page if that is where they cancel the order from.
@@ -384,7 +302,7 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
* Allow customers to cancel early renewal orders from their account page. * Allow customers to cancel early renewal orders from their account page.
* *
* Renewal orders are usually not cancellable because @see WC_Subscriptions_Renewal_Order::prevent_cancelling_renewal_orders() prevents the request from being processed. * Renewal orders are usually not cancellable because @see WC_Subscriptions_Renewal_Order::prevent_cancelling_renewal_orders() prevents the request from being processed.
* In the case of early renewals, the customer has opted for early renewal and so should be able to cancel it in order to reactivate their subscription. * In the case of early renewals, the customer has opted for early renewal and so should be able to cancel it.
* *
* @since 2.3.0 * @since 2.3.0
*/ */
@@ -519,4 +437,194 @@ class WCS_Cart_Early_Renewal extends WCS_Cart_Renewal {
return $order_meta; return $order_meta;
} }
/**
* Records successful and unsuccessful subscription payments for early renewal orders.
*
* @param int $order_id The ID of the order transitioned.
* @param string $old_status The old order's status.
* @param string $new_status The new order's status.
* @param WC_Order $order The order object. Optional. Older versions of WC didn't provide this. Falls back to the order_id if not provided.
*/
public function maybe_record_subscription_payment( $order_id, $old_status, $new_status, $order = null ) {
// We're only interested in order status transitions that involve payment.
if ( in_array( $new_status, [ 'cancelled', 'refunded' ] ) ) {
return;
}
if ( ! $order ) {
$order = wc_get_order( $order_id );
}
// Only continue if this is an early renewal order.
if ( ! $order || ! wcs_order_contains_early_renewal( $order ) ) {
return;
}
// Prevent the default renewal order status transitions from updating the subscription status.
// Early renewal orders are optional and should not affect the subscription status.
if ( remove_action( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment', 10 ) ) {
// Add a callback to reattach the function which handles renewal order payment status transitions, after the current request has finished.
add_action( 'woocommerce_order_status_changed', array( $this, 'reattach_renewal_order_status_handling' ), 11 );
}
// We're only interested in processing order transitions from a status that required payment.
if ( ! in_array( $old_status, apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'on-hold', 'failed' ), $order ) ) ) {
return;
}
$subscription = wcs_get_subscription( absint( $order->get_meta( '_subscription_renewal_early' ) ) );
// Payment success - if payment was successful and dates haven't been updated for this order, update the subscription dates and store meta to prevent dates being updated multiple times for the same order.
if ( $subscription && $order->is_paid() && ! $order->meta_exists( self::SUBSCRIPTION_DATES_UPDATED_META_KEY ) ) {
wcs_update_dates_after_early_renewal( $subscription, $order );
$order->update_meta_data( self::SUBSCRIPTION_DATES_UPDATED_META_KEY, wc_bool_to_string( true ) );
$order->save();
}
}
/**
* Reattaches the function which handles renewal order payment status transitions.
*
* The default renewal order status transition is detached when processing an early renewal
* order but needs to be reattached otherwise any renewal order status updates later in
* this request will not be processed.
*
* @see self::maybe_record_subscription_payment()
*
* @since 5.2.0
*/
public function reattach_renewal_order_status_handling() {
add_action( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment', 10, 3 );
}
// DEPRECATED FUNCTIONS.
/**
* Update the next payment and end dates on a subscription to extend them and account
* for early renewal.
*
* @deprecated 5.2.0
*
* @param int $order_id The WC Order ID which contains an early renewal.
* @since 2.3.0
*/
public function maybe_update_dates( $order_id ) {
wcs_deprecated_function( __METHOD__, '5.2.0' );
$order = wc_get_order( $order_id );
if ( ! $order || ! wcs_order_contains_early_renewal( $order ) ) {
return;
}
$subscription_id = wcs_get_objects_property( $order, 'subscription_renewal_early' );
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
return;
}
wcs_update_dates_after_early_renewal( $subscription, $order );
}
/**
* Reactivates an on hold subscription when an early renewal order
* is cancelled by the user.
*
* @param int $order_id The WC Order ID which contains an early renewal.
* @since 2.3.0
*/
public function maybe_reactivate_subscription( $order_id ) {
wcs_deprecated_function( __METHOD__, '5.2.0' );
// Get the order and make sure we have one.
$order = wc_get_order( $order_id );
if ( wcs_order_contains_early_renewal( $order ) ) {
// Get the subscription and make sure we have one.
$subscription = wcs_get_subscription( wcs_get_objects_property( $order, 'subscription_renewal_early' ) );
if ( ! $subscription || ! $subscription->has_status( 'on-hold' ) ) {
return;
}
// Make sure the next payment date isn't in the past.
if ( strtotime( $subscription->get_date( 'next_payment' ) ) < time() ) {
return;
}
// Reactivate the subscription.
$subscription->update_status( 'active' );
}
}
/**
* Records an early renewal against order created on checkout (only for WooCommerce < 3.0).
*
* @param int $order_id The post_id of a shop_order post/WC_Order object.
* @param array $posted_data The data posted on checkout.
* @since 2.3.0
*/
public function maybe_record_early_renewal( $order_id, $posted_data ) {
wcs_deprecated_function( __METHOD__, '5.2.0', 'WCS_Cart_Early_Renewal::add_early_renewal_metadata_to_order( $order, $posted_data )' );
$cart_item = $this->cart_contains();
if ( ! $cart_item ) {
return;
}
// Get the subscription.
$subscription = wcs_get_subscription( $cart_item[ $this->cart_item_key ]['subscription_id'] );
// Mark this order as a renewal.
update_post_meta( $order_id, '_subscription_renewal', $subscription->get_id() );
// Mark this order as an early renewal.
update_post_meta( $order_id, '_subscription_renewal_early', $subscription->get_id() );
// Put the subscription on hold until payment is complete.
$subscription->update_status( 'on-hold', _x( 'Customer requested to renew early:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
}
/**
* Ensure customers can cancel early renewal orders.
*
* Renewal orders are usually not cancellable because @see WCS_Cart_Renewal::filter_my_account_my_orders_actions() prevents it.
* In the case of early renewals, the customer has opted for early renewal and so should be able to cancel it.
*
* @param array $actions A list of actions customers can make on an order from their My Account page
* @param WC_Order $order The order the list of actions relate to.
* @return array $actions
* @since 2.3.0
*/
public static function add_cancel_order_action( $actions, $order ) {
wcs_deprecated_function( __METHOD__, '5.6.0', __CLASS__ . '::filter_early_renewal_order_actions()' );
if ( ! isset( $actions['cancel'] ) && wcs_order_contains_early_renewal( $order ) && in_array( $order->get_status(), apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ), $order ) ) ) {
$redirect = wc_get_page_permalink( 'myaccount' );
// Redirect the customer back to the view subscription page if that is where they cancel the order from.
if ( wcs_is_view_subscription_page() ) {
global $wp;
$subscription = wcs_get_subscription( $wp->query_vars['view-subscription'] );
if ( wcs_is_subscription( $subscription ) ) {
$redirect = $subscription->get_view_order_url();
}
}
$actions['cancel'] = array(
'url' => $order->get_cancel_order_url( $redirect ),
'name' => __( 'Cancel', 'woocommerce-subscriptions' ),
);
}
return $actions;
}
} }

View File

@@ -14,6 +14,11 @@ if ( ! defined( 'ABSPATH' ) ) {
class WCS_Retry_Admin { class WCS_Retry_Admin {
/**
* @var string The ID of the setting to enable/disable the retry system.
*/
public $setting_id;
/** /**
* Constructor * Constructor
*/ */

View File

@@ -54,15 +54,7 @@ class WC_Subscriptions_Switcher {
add_action( 'woocommerce_checkout_update_order_meta', array( __CLASS__, 'add_order_meta' ), 10, 2 ); add_action( 'woocommerce_checkout_update_order_meta', array( __CLASS__, 'add_order_meta' ), 10, 2 );
// Same as above for WooCommerce Blocks. // Same as above for WooCommerce Blocks.
if ( class_exists( 'Automattic\WooCommerce\Blocks\Package' ) ) {
if ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '7.2.0', '>=' ) ) {
add_action( 'woocommerce_store_api_checkout_update_order_meta', array( __CLASS__, 'add_order_meta' ), 10, 1 ); add_action( 'woocommerce_store_api_checkout_update_order_meta', array( __CLASS__, 'add_order_meta' ), 10, 1 );
} elseif ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '6.3.0', '>=' ) ) {
add_action( 'woocommerce_blocks_checkout_update_order_meta', array( __CLASS__, 'add_order_meta' ), 10, 1 );
} else {
add_action( '__experimental_woocommerce_blocks_checkout_update_order_meta', array( __CLASS__, 'add_order_meta' ), 10, 1 );
}
}
// Don't allow switching to the same product // Don't allow switching to the same product
add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'validate_switch_request' ), 10, 4 ); add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'validate_switch_request' ), 10, 4 );
@@ -1224,14 +1216,14 @@ class WC_Subscriptions_Switcher {
* @return bool|array Returns cart items that modify subscription contents, or false if no such items exist. * @return bool|array Returns cart items that modify subscription contents, or false if no such items exist.
*/ */
public static function cart_contains_switches( $item_action = 'switch' ) { public static function cart_contains_switches( $item_action = 'switch' ) {
$subscription_switches = false; $subscription_switches = [];
if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || false == DOING_AJAX ) ) { if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || false == DOING_AJAX ) ) {
return $subscription_switches; return false;
} }
if ( ! isset( WC()->cart ) ) { if ( ! isset( WC()->cart ) ) {
return $subscription_switches; return false;
} }
// We use WC()->cart->cart_contents instead of WC()->cart->get_cart() to prevent recursion caused when get_cart_from_session() is called too early ref: https://github.com/woocommerce/woocommerce/commit/1f3365f2066b1e9d7e84aca7b1d7e89a6989c213 // We use WC()->cart->cart_contents instead of WC()->cart->get_cart() to prevent recursion caused when get_cart_from_session() is called too early ref: https://github.com/woocommerce/woocommerce/commit/1f3365f2066b1e9d7e84aca7b1d7e89a6989c213
@@ -1263,7 +1255,7 @@ class WC_Subscriptions_Switcher {
} }
} }
return $subscription_switches; return ! empty( $subscription_switches ) ? $subscription_switches : false;
} }
/** /**

View File

@@ -106,6 +106,12 @@ class WCS_Switch_Cart_Item {
*/ */
public $switch_type; public $switch_type;
/**
* Whether the last order was a switch and was a fully reduced pre-paid term.
* @var bool
*/
public $is_switch_after_fully_reduced_prepaid_term;
/** /**
* Constructor. * Constructor.
* *

File diff suppressed because it is too large Load Diff

2
vendor/autoload.php vendored
View File

@@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php'; require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit06e38849db55c37d72e7daef1d52dece::getLoader(); return ComposerAutoloaderInit59c7b20d3f201de5581a0bc09b6c2289::getLoader();

View File

@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer // autoload_real.php @generated by Composer
class ComposerAutoloaderInit06e38849db55c37d72e7daef1d52dece class ComposerAutoloaderInit59c7b20d3f201de5581a0bc09b6c2289
{ {
private static $loader; private static $loader;
@@ -24,12 +24,12 @@ class ComposerAutoloaderInit06e38849db55c37d72e7daef1d52dece
require __DIR__ . '/platform_check.php'; require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit06e38849db55c37d72e7daef1d52dece', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInit59c7b20d3f201de5581a0bc09b6c2289', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit06e38849db55c37d72e7daef1d52dece', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInit59c7b20d3f201de5581a0bc09b6c2289', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php'; require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit06e38849db55c37d72e7daef1d52dece::getInitializer($loader)); call_user_func(\Composer\Autoload\ComposerStaticInit59c7b20d3f201de5581a0bc09b6c2289::getInitializer($loader));
$loader->register(true); $loader->register(true);

View File

@@ -4,7 +4,7 @@
namespace Composer\Autoload; namespace Composer\Autoload;
class ComposerStaticInit06e38849db55c37d72e7daef1d52dece class ComposerStaticInit59c7b20d3f201de5581a0bc09b6c2289
{ {
public static $prefixLengthsPsr4 = array ( public static $prefixLengthsPsr4 = array (
'C' => 'C' =>
@@ -129,9 +129,9 @@ class ComposerStaticInit06e38849db55c37d72e7daef1d52dece
public static function getInitializer(ClassLoader $loader) public static function getInitializer(ClassLoader $loader)
{ {
return \Closure::bind(function () use ($loader) { return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit06e38849db55c37d72e7daef1d52dece::$prefixLengthsPsr4; $loader->prefixLengthsPsr4 = ComposerStaticInit59c7b20d3f201de5581a0bc09b6c2289::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit06e38849db55c37d72e7daef1d52dece::$prefixDirsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit59c7b20d3f201de5581a0bc09b6c2289::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit06e38849db55c37d72e7daef1d52dece::$classMap; $loader->classMap = ComposerStaticInit59c7b20d3f201de5581a0bc09b6c2289::$classMap;
}, null, ClassLoader::class); }, null, ClassLoader::class);
} }

View File

@@ -156,17 +156,17 @@
}, },
{ {
"name": "woocommerce/subscriptions-core", "name": "woocommerce/subscriptions-core",
"version": "6.2.0", "version": "6.4.0",
"version_normalized": "6.2.0.0", "version_normalized": "6.4.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Automattic/woocommerce-subscriptions-core.git", "url": "https://github.com/Automattic/woocommerce-subscriptions-core.git",
"reference": "47cfe92d60239d1b8b12a5f640a3772b0e4e1272" "reference": "a94c9aab6d47f32461974ed09a4d3cad590f25b0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/47cfe92d60239d1b8b12a5f640a3772b0e4e1272", "url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/a94c9aab6d47f32461974ed09a4d3cad590f25b0",
"reference": "47cfe92d60239d1b8b12a5f640a3772b0e4e1272", "reference": "a94c9aab6d47f32461974ed09a4d3cad590f25b0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -179,7 +179,7 @@
"woocommerce/woocommerce-sniffs": "0.1.0", "woocommerce/woocommerce-sniffs": "0.1.0",
"yoast/phpunit-polyfills": "1.0.3" "yoast/phpunit-polyfills": "1.0.3"
}, },
"time": "2023-08-10T23:43:48+00:00", "time": "2023-10-18T03:32:50+00:00",
"type": "wordpress-plugin", "type": "wordpress-plugin",
"extra": { "extra": {
"phpcodesniffer-search-depth": 2 "phpcodesniffer-search-depth": 2
@@ -209,7 +209,7 @@
"description": "Sell products and services with recurring payments in your WooCommerce Store.", "description": "Sell products and services with recurring payments in your WooCommerce Store.",
"homepage": "https://github.com/Automattic/woocommerce-subscriptions-core", "homepage": "https://github.com/Automattic/woocommerce-subscriptions-core",
"support": { "support": {
"source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/6.2.0", "source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/6.4.0",
"issues": "https://github.com/Automattic/woocommerce-subscriptions-core/issues" "issues": "https://github.com/Automattic/woocommerce-subscriptions-core/issues"
}, },
"install-path": "../woocommerce/subscriptions-core" "install-path": "../woocommerce/subscriptions-core"

View File

@@ -1,9 +1,9 @@
<?php return array( <?php return array(
'root' => array( 'root' => array(
'name' => 'woocommerce/woocommerce-subscriptions', 'name' => 'woocommerce/woocommerce-subscriptions',
'pretty_version' => 'dev-release/5.5.0', 'pretty_version' => 'dev-release/5.6.0',
'version' => 'dev-release/5.5.0', 'version' => 'dev-release/5.6.0',
'reference' => '9c5944431141ef588b010663dd1539c41d12cf69', 'reference' => '434da4e19c4fde75e431338fa82595320bd5b6c1',
'type' => 'wordpress-plugin', 'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@@ -32,18 +32,18 @@
), ),
), ),
'woocommerce/subscriptions-core' => array( 'woocommerce/subscriptions-core' => array(
'pretty_version' => '6.2.0', 'pretty_version' => '6.4.0',
'version' => '6.2.0.0', 'version' => '6.4.0.0',
'reference' => '47cfe92d60239d1b8b12a5f640a3772b0e4e1272', 'reference' => 'a94c9aab6d47f32461974ed09a4d3cad590f25b0',
'type' => 'wordpress-plugin', 'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../woocommerce/subscriptions-core', 'install_path' => __DIR__ . '/../woocommerce/subscriptions-core',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'woocommerce/woocommerce-subscriptions' => array( 'woocommerce/woocommerce-subscriptions' => array(
'pretty_version' => 'dev-release/5.5.0', 'pretty_version' => 'dev-release/5.6.0',
'version' => 'dev-release/5.5.0', 'version' => 'dev-release/5.6.0',
'reference' => '9c5944431141ef588b010663dd1539c41d12cf69', 'reference' => '434da4e19c4fde75e431338fa82595320bd5b6c1',
'type' => 'wordpress-plugin', 'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),

View File

@@ -27,7 +27,7 @@ ul {
} }
.wcs-badge { .wcs-badge {
position: relative; position: relative;
background: #9c5d90; background: #7F54B3;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
padding-top: 150px; padding-top: 150px;
height: 52px; height: 52px;
@@ -35,7 +35,7 @@ ul {
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;
text-align: center; text-align: center;
color: #ddc8d9; color: #fff;
margin: 5px 0 0 0; margin: 5px 0 0 0;
-webkit-box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.2 ); -webkit-box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.2 );
box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.2 ); box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.2 );
@@ -92,44 +92,10 @@ ul {
} }
.woocommerce-message { .woocommerce-message {
position: relative; position: relative;
border-left-color: #cc99c2 !important; border-left-color: #7F54B3 !important;
overflow: hidden; overflow: hidden;
} }
.woocommerce-message a.button-primary,
p.woocommerce-actions a.button-primary,
.woocommerce-message a.button-primary:focus,
p.woocommerce-actions a.button-primary:focus,
.woocommerce-message a.button-primary:active,
p.woocommerce-actions a.button-primary:active {
background: #b366a4;
border-color: #b366a4;
-webkit-box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ),
0 1px 0 rgba( 0, 0, 0, 0.15 );
box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ),
0 1px 0 rgba( 0, 0, 0, 0.15 );
text-shadow: 0 -1px 1px #b366a4, 1px 0 1px #b366a4, 0 1px 1px #b366a4,
-1px 0 1px #b366a4;
color: #fff;
text-decoration: none;
}
.woocommerce-message a.button-primary:hover,
p.woocommerce-actions a.button-primary:hover {
background: #bb77ae;
border-color: #aa559a;
-webkit-box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ),
0 1px 0 rgba( 0, 0, 0, 0.15 );
box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ),
0 1px 0 rgba( 0, 0, 0, 0.15 );
}
.woocommerce-message a.button-primary:active,
p.woocommerce-actions a.button-primary:active {
background: #aa559a;
border-color: #aa559a;
}
.woocommerce-message a.skip, .woocommerce-message a.skip,
p.woocommerce-actions a.skip { p.woocommerce-actions a.skip {
opacity: 0.7; opacity: 0.7;

View File

@@ -2,19 +2,6 @@
.woocommerce-subscriptions-activated p a.button-primary { .woocommerce-subscriptions-activated p a.button-primary {
display: inline-block; display: inline-block;
} }
.woocommerce-subscriptions-activated a.button-primary:hover {
background: #bb77ae;
border-color: #aa559a;
-webkit-box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ),
0 1px 0 rgba( 0, 0, 0, 0.15 );
box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ),
0 1px 0 rgba( 0, 0, 0, 0.15 );
}
.woocommerce-subscriptions-activated a.button-primary:active,
.woocommerce-subscriptions-activated a.button-primary:active {
background: #aa559a;
border-color: #aa559a;
}
/* Subscriptions Admin Page */ /* Subscriptions Admin Page */
.woocommerce_page_wc-orders--shop_subscription .tablenav input, .woocommerce_page_wc-orders--shop_subscription .tablenav input,
@@ -314,6 +301,25 @@ a.close-subscriptions-search {
width: auto !important; width: auto !important;
} }
.wc_input_subscription_payment_sync +.select2,
.wc_input_subscription_length +.select2,
#_subscription_limit +.select2 {
min-width: 180px;
width: 80% !important;
margin-bottom: 4px;
}
.wc_input_subscription_period + .select2,
.wc_input_subscription_trial_period + .select2 {
width: 30.75% !important;
margin-top: 0;
}
.wc_input_subscription_period_interval + .select2 {
margin-right: 3.8%;
width: 30.75% !important;
}
.variable_subscription_sync p._subscription_payment_sync_field { .variable_subscription_sync p._subscription_payment_sync_field {
padding-left: 0 !important; padding-left: 0 !important;
} }
@@ -360,7 +366,32 @@ a.close-subscriptions-search {
.wc_input_subscription_period_interval { .wc_input_subscription_period_interval {
max-width: 33%; max-width: 33%;
float: left; float: left;
margin-right: 2px;
} }
#variable_product_options .select2 {
margin: 2px 2px 0 0;
}
#variable_product_options
.select2-container
.select2-selection--single {
min-height: 40px;
}
#variable_product_options
.select2-container
.select2-selection--single
.select2-selection__rendered {
line-height: 36px;
}
#variable_product_options
.select2-container
.select2-selection--single
.select2-selection__arrow {
height: 36px;
}
.variable_subscription_pricing_2_3 .wc_input_subscription_price { .variable_subscription_pricing_2_3 .wc_input_subscription_price {
clear: left; clear: left;
} }
@@ -735,7 +766,7 @@ table.wp-list-table .subscription_renewal_order::after {
height: 100%; height: 100%;
text-align: center; text-align: center;
content: '\e018'; content: '\e018';
color: #a46497; color: var(--wp-admin-theme-color);
} }
table.wc_gateways .renewals .tips { table.wc_gateways .renewals .tips {

View File

@@ -40,21 +40,14 @@ jQuery( function ( $ ) {
'hide_if_variable-subscription' 'hide_if_variable-subscription'
); );
/** var product_type = $( 'select#product-type' ).val();
* WC core will hide and show product specific fields in show_and_hide_panels(), however that function only runs on specific events, but not
* when variations are added or loaded. To make sure our subscription-related fields aren't shown by default when a variation is added, we set
* subscription pricing elements "base" cases here.
*
* Note: show() being called on the 'hide_if_' fields and vice versa is intentional. All fields are set in their inverse state first, and
* then shown/hidden by product type afterwards.
*/
$( '.hide_if_variable-subscription' ).show();
$( '.show_if_variable-subscription' ).hide();
if ( $( 'select#product-type' ).val() == 'variable-subscription' ) { if ( 'variable-subscription' === product_type ) {
// Hide and show subscription fields when variable subscription is selected
$( 'input#_downloadable' ).prop( 'checked', false ); $( 'input#_downloadable' ).prop( 'checked', false );
$( 'input#_virtual' ).prop( 'checked', false ); $( 'input#_virtual' ).prop( 'checked', false );
// Variable subscriptions inherit fields from variable products.
$( '.show_if_variable' ).show(); $( '.show_if_variable' ).show();
$( '.hide_if_variable' ).hide(); $( '.hide_if_variable' ).hide();
$( '.show_if_variable-subscription' ).show(); $( '.show_if_variable-subscription' ).show();
@@ -67,14 +60,7 @@ jQuery( function ( $ ) {
.addClass( 'form-row-full' ) .addClass( 'form-row-full' )
.removeClass( 'form-row-last' ); .removeClass( 'form-row-last' );
} else { } else {
if ( 'variable' === $( 'select#product-type' ).val() ) { if ( 'subscription' === product_type ) {
$( '.show_if_variable-subscription' ).hide();
$( '.show_if_variable' ).show();
$( '.hide_if_variable' ).hide();
$.showOrHideStockFields();
}
if ( 'subscription' === $( 'select#product-type' ).val() ) {
$( '.show_if_subscription' ).show(); $( '.show_if_subscription' ).show();
$( '.hide_if_subscription' ).hide(); $( '.hide_if_subscription' ).hide();
} }

View File

@@ -1 +1 @@
<?php return array('dependencies' => array('wc-blocks-checkout', 'wc-price-format', 'wc-settings', 'wp-element', 'wp-i18n', 'wp-plugins'), 'version' => '7855adadf2c3ec0499bc9a7854758556'); <?php return array('dependencies' => array('wc-blocks-checkout', 'wc-price-format', 'wc-settings', 'wp-element', 'wp-i18n', 'wp-plugins'), 'version' => '5386ce4810e198e8d50d4e1491067c34');

View File

@@ -14,7 +14,7 @@ Object(l.__)("Recurring total every 2nd %1$s","woocommerce-subscriptions"),n);ca
/* Translators: %1$s is week, month, year */ /* Translators: %1$s is week, month, year */
Object(l.__)("Recurring total every 3rd %1$s","woocommerce-subscriptions"),n);default:return Object(l.sprintf)( Object(l.__)("Recurring total every 3rd %1$s","woocommerce-subscriptions"),n);default:return Object(l.sprintf)(
/* Translators: %1$d is number of weeks, months, days, years. %2$s is week, month, year */ /* Translators: %1$d is number of weeks, months, days, years. %2$s is week, month, year */
Object(l.__)("Recurring total every %1$dth %2$s","woocommerce-subscriptions"),t,n)}}({billingInterval:n,billingPeriod:c});return Object(r.createElement)(i.TotalsItem,{className:"wcs-recurring-totals-panel__title",currency:t,label:u,value:a,description:Object(r.createElement)(f,{nextPaymentDate:o,subscriptionLength:s,billingInterval:n,billingPeriod:c})})},j=function(e){var t,n,c,o=e.subscription,s=e.needsShipping,u=e.calculatedShipping,p=o.totals,b=o.billing_interval,m=o.billing_period,f=o.next_payment_date,j=o.subscription_length,w=o.shipping_rates;if(!f)return null;var v=null==w||null===(t=w[0])||void 0===t||null===(n=t.shipping_rates)||void 0===n||null===(c=n.find((function(e){return e.selected})))||void 0===c?void 0:c.name,y=Object(a.getCurrencyFromPriceResponse)(p);return Object(r.createElement)("div",{className:"wcs-recurring-totals-panel"},Object(r.createElement)(O,{billingInterval:b,billingPeriod:m,nextPaymentDate:f,subscriptionLength:j,totals:parseInt(p.total_price,10),currency:y}),Object(r.createElement)(i.Panel,{className:"wcs-recurring-totals-panel__details",initialOpen:!1,title:Object(l.__)("Details","woocommerce-subscriptions")},Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(i.Subtotal,{currency:y,values:p}),Object(r.createElement)(d,{currency:y,values:p})),Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(_,{currency:y,needsShipping:s,calculatedShipping:u,values:p,selectedRate:v})),!g&&Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(i.TotalsTaxes,{currency:y,values:p})),Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(i.TotalsItem,{className:"wcs-recurring-totals-panel__details-total",currency:y,label:Object(l.__)("Total","woocommerce-subscriptions"),value:parseInt(p.total_price,10)}))))},w=function(e){var t=e.extensions,n=e.cart,c=t.subscriptions,i=n.cartNeedsShipping,o=n.cartHasCalculatedShipping;return c&&0!==c.length?c.map((function(e){var t=e.key,n=s()(e,["key"]);return Object(r.createElement)(j,{subscription:n,needsShipping:i,calculatedShipping:o,key:t})})):null},v=function(e){var t=e.extensions,n=e.collapsible,c=e.collapse,i=e.showItems,o=e.noResultsMessage,l=e.renderOption,a=e.components,u=t.subscriptions,p=void 0===u?[]:u,b=a.ShippingRatesControlPackage,m=Object(r.useMemo)((function(){return Object.values(p).map((function(e){return e.shipping_rates})).filter(Boolean).flat()}),[p]),g=Object(r.useMemo)((function(){return 1<m.length||c}),[m.length,c]),d=Object(r.useMemo)((function(){return 1<m.length||i}),[m.length,i]);return m.map((function(e){var t=e.package_id,c=s()(e,["package_id"]);return Object(r.createElement)(b,{key:t,packageId:t,packageData:c,collapsible:n,collapse:g,showItems:d,noResultsMessage:o,renderOption:l})}))};n(9),Object(c.registerPlugin)("woocommerce-subscriptions",{render:function(){return Object(r.createElement)(r.Fragment,null,Object(r.createElement)(i.ExperimentalOrderShippingPackages,null,Object(r.createElement)(v,null)),Object(r.createElement)(i.ExperimentalOrderMeta,null,Object(r.createElement)(w,null)))},scope:"woocommerce-checkout"}),Object(i.__experimentalRegisterCheckoutFilters)("woocommerce-subscriptions",{totalLabel:function(e,t){var n=t.subscriptions;return 0<(null==n?void 0:n.length)?Object(l.__)("Total due today","woocommerce-subscriptions"):e},subtotalPriceFormat:function(e,t){var n=t.subscriptions;if(null!=n&&n.billing_period&&null!=n&&n.billing_interval){var r=n.billing_interval,c=n.subscription_length;return m({subscriptionLength:c,billingInterval:r})?b(n,1===c?// translators: the word used to describe billing frequency, e.g. "fo1" 1 day or "for" 1 month. Object(l.__)("Recurring total every %1$dth %2$s","woocommerce-subscriptions"),t,n)}}({billingInterval:n,billingPeriod:c});return Object(r.createElement)(i.TotalsItem,{className:"wcs-recurring-totals-panel__title",currency:t,label:u,value:a,description:Object(r.createElement)(f,{nextPaymentDate:o,subscriptionLength:s,billingInterval:n,billingPeriod:c})})},j=function(e){var t,n,c,o=e.subscription,s=e.needsShipping,u=e.calculatedShipping,p=o.totals,b=o.billing_interval,m=o.billing_period,f=o.next_payment_date,j=o.subscription_length,w=o.shipping_rates;if(!f)return null;var v=null==w||null===(t=w[0])||void 0===t||null===(n=t.shipping_rates)||void 0===n||null===(c=n.find((function(e){return e.selected})))||void 0===c?void 0:c.name,y=Object(a.getCurrencyFromPriceResponse)(p);return Object(r.createElement)("div",{className:"wcs-recurring-totals-panel"},Object(r.createElement)(O,{billingInterval:b,billingPeriod:m,nextPaymentDate:f,subscriptionLength:j,totals:parseInt(p.total_price,10),currency:y}),Object(r.createElement)(i.Panel,{className:"wcs-recurring-totals-panel__details",initialOpen:!1,title:Object(l.__)("Details","woocommerce-subscriptions")},Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(i.Subtotal,{currency:y,values:p}),Object(r.createElement)(d,{currency:y,values:p})),Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(_,{currency:y,needsShipping:s,calculatedShipping:u,values:p,selectedRate:v})),!g&&Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(i.TotalsTaxes,{currency:y,values:p})),Object(r.createElement)(i.TotalsWrapper,null,Object(r.createElement)(i.TotalsItem,{className:"wcs-recurring-totals-panel__details-total",currency:y,label:Object(l.__)("Total","woocommerce-subscriptions"),value:parseInt(p.total_price,10)}))))},w=function(e){var t=e.extensions,n=e.cart,c=t.subscriptions,i=n.cartNeedsShipping,o=n.cartHasCalculatedShipping;return c&&0!==c.length?c.map((function(e){var t=e.key,n=s()(e,["key"]);return Object(r.createElement)(j,{subscription:n,needsShipping:i,calculatedShipping:o,key:t})})):null},v=function(e){var t=e.extensions,n=e.collapsible,c=e.collapse,i=e.showItems,o=e.noResultsMessage,l=e.renderOption,a=e.components,u=t.subscriptions,p=void 0===u?[]:u,b=a.ShippingRatesControlPackage,m=Object(r.useMemo)((function(){return Object.values(p).map((function(e){return e.shipping_rates})).filter(Boolean).flat()}),[p]),g=Object(r.useMemo)((function(){return 1<m.length||c}),[m.length,c]),d=Object(r.useMemo)((function(){return 1<m.length||i}),[m.length,i]);return m.map((function(e){var t=e.package_id,c=s()(e,["package_id"]);return Object(r.createElement)(b,{key:t,packageId:t,packageData:c,collapsible:n,collapse:g,showItems:d,noResultsMessage:o,renderOption:l})}))};n(9),Object(c.registerPlugin)("woocommerce-subscriptions",{render:function(){return Object(r.createElement)(r.Fragment,null,Object(r.createElement)(i.ExperimentalOrderShippingPackages,null,Object(r.createElement)(v,null)),Object(r.createElement)(i.ExperimentalOrderMeta,null,Object(r.createElement)(w,null)))},scope:"woocommerce-checkout"}),Object(i.registerCheckoutFilters)("woocommerce-subscriptions",{totalLabel:function(e,t){var n=t.subscriptions;return 0<(null==n?void 0:n.length)?Object(l.__)("Total due today","woocommerce-subscriptions"):e},subtotalPriceFormat:function(e,t){var n=t.subscriptions;if(null!=n&&n.billing_period&&null!=n&&n.billing_interval){var r=n.billing_interval,c=n.subscription_length;return m({subscriptionLength:c,billingInterval:r})?b(n,1===c?// translators: the word used to describe billing frequency, e.g. "fo1" 1 day or "for" 1 month.
Object(l.__)("for 1","woocommerce-subscriptions"):// translators: the word used to describe billing frequency, e.g. "for" 6 days or "for" 2 weeks. Object(l.__)("for 1","woocommerce-subscriptions"):// translators: the word used to describe billing frequency, e.g. "for" 6 days or "for" 2 weeks.
Object(l.__)("for","woocommerce-subscriptions"),e):b(n,// translators: the word used to describe billing frequency, e.g. "every" 6 days or "every" 2 weeks. Object(l.__)("for","woocommerce-subscriptions"),e):b(n,// translators: the word used to describe billing frequency, e.g. "every" 6 days or "every" 2 weeks.
Object(l.__)("every","woocommerce-subscriptions"),e)}return e},saleBadgePriceFormat:function(e,t){var n=t.subscriptions;return null!=n&&n.billing_period&&null!=n&&n.billing_interval?b(n,"/",e):e},itemName:function(e,t){var n=t.subscriptions;return null!=n&&n.is_resubscribe?Object(l.sprintf)(// translators: %s Product name. Object(l.__)("every","woocommerce-subscriptions"),e)}return e},saleBadgePriceFormat:function(e,t){var n=t.subscriptions;return null!=n&&n.billing_period&&null!=n&&n.billing_interval?b(n,"/",e):e},itemName:function(e,t){var n=t.subscriptions;return null!=n&&n.is_resubscribe?Object(l.sprintf)(// translators: %s Product name.

View File

@@ -1,5 +1,25 @@
*** WooCommerce Subscriptions Core Changelog *** *** WooCommerce Subscriptions Core Changelog ***
= 6.4.0 - 2023-10-18 =
* Add - Use admin theme color and the correct WooCommerce colors.
* Fix - Resolve an issue that would cause 3rd party plugin edit product fields with the show_if_variable-subscription class to be incorrectly hidden.
* Fix - Allow gateways to execute action on payment method deletion before updating the subscription.
* Fix - Ensure subscriptions have a date created that correctly accounts for the site's timezone. Fixes issues with subscriptions having a date created double the site's UTC offset.
* Fix - When HPOS is enabled, fix quick-editing the subscription statuses on the admin list table.
* Dev - Updated the hooks for Checkout Blocks, replacing the deprecated `woocommerce_blocks_checkout_` prefixed hooks with `woocommerce_store_api_checkout`.
* Dev - PHP 8.2: Fix "Creation of dynamic property" warnings.
= 6.3.0 - 2023-10-06 =
* Add - Introduce the "Subscription Relationship" column under the Orders list admin page when HPOS is enabled.
* Fix - Resolved an issue that caused subscriptions with an unpaid early renewal order to be incorrectly considered as needing payment.
* Fix - When HPOS is enabled, make the orders_by_type_query filter box work in the WooCommerce orders screen.
* Fix - Ensure renewal orders paid via the Block Checkout are correctly linked to their subscription.
* Fix - Resolved an issue that caused paying for failed/pending parent orders that include Product Add-ons to not calculate the correct total.
* Fix - Ensure the order needs processing transient is deleted when a subscription order (eg renewal) is created. Fixes issues with renewal orders going straight to a completed status.
* Fix - Store the correct subscription start date in postmeta and ordermeta when HPOS and data syncing is being used.
* Fix - When HPOS is enabled, deleting a customer will now delete their subscriptions.
* Fix - Missing styles on the Edit Subscription page when HPOS is enabled.
= 6.2.0 - 2023-08-10 = = 6.2.0 - 2023-08-10 =
* Add - Introduce an updated empty state screen for the WooCommerce > Subscriptions list table. * Add - Introduce an updated empty state screen for the WooCommerce > Subscriptions list table.
* Fix - Ensure subscription checkout and cart block integrations are loaded on store environments where WooPayments is not enabled. * Fix - Ensure subscription checkout and cart block integrations are loaded on store environments where WooPayments is not enabled.

View File

@@ -317,13 +317,13 @@ class WC_Subscriptions_Admin {
<span class="wrap"> <span class="wrap">
<input type="text" id="_subscription_price" name="_subscription_price" class="wc_input_price wc_input_subscription_price" placeholder="<?php echo esc_attr_x( 'e.g. 5.90', 'example price', 'woocommerce-subscriptions' ); ?>" step="any" min="0" value="<?php echo esc_attr( wc_format_localized_price( $chosen_price ) ); ?>" /> <input type="text" id="_subscription_price" name="_subscription_price" class="wc_input_price wc_input_subscription_price" placeholder="<?php echo esc_attr_x( 'e.g. 5.90', 'example price', 'woocommerce-subscriptions' ); ?>" step="any" min="0" value="<?php echo esc_attr( wc_format_localized_price( $chosen_price ) ); ?>" />
<label for="_subscription_period_interval" class="wcs_hidden_label"><?php esc_html_e( 'Subscription interval', 'woocommerce-subscriptions' ); ?></label> <label for="_subscription_period_interval" class="wcs_hidden_label"><?php esc_html_e( 'Subscription interval', 'woocommerce-subscriptions' ); ?></label>
<select id="_subscription_period_interval" name="_subscription_period_interval" class="wc_input_subscription_period_interval"> <select id="_subscription_period_interval" name="_subscription_period_interval" class="wc_input_subscription_period_interval wc-enhanced-select">
<?php foreach ( wcs_get_subscription_period_interval_strings() as $value => $label ) { ?> <?php foreach ( wcs_get_subscription_period_interval_strings() as $value => $label ) { ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $chosen_interval, true ); ?>><?php echo esc_html( $label ); ?></option> <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $chosen_interval, true ); ?>><?php echo esc_html( $label ); ?></option>
<?php } ?> <?php } ?>
</select> </select>
<label for="_subscription_period" class="wcs_hidden_label"><?php esc_html_e( 'Subscription period', 'woocommerce-subscriptions' ); ?></label> <label for="_subscription_period" class="wcs_hidden_label"><?php esc_html_e( 'Subscription period', 'woocommerce-subscriptions' ); ?></label>
<select id="_subscription_period" name="_subscription_period" class="wc_input_subscription_period last" > <select id="_subscription_period" name="_subscription_period" class="wc_input_subscription_period last wc-enhanced-select" >
<?php foreach ( wcs_get_subscription_period_strings() as $value => $label ) { ?> <?php foreach ( wcs_get_subscription_period_strings() as $value => $label ) { ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $chosen_period, true ); ?>><?php echo esc_html( $label ); ?></option> <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $chosen_period, true ); ?>><?php echo esc_html( $label ); ?></option>
<?php } ?> <?php } ?>
@@ -337,7 +337,7 @@ class WC_Subscriptions_Admin {
woocommerce_wp_select( woocommerce_wp_select(
array( array(
'id' => '_subscription_length', 'id' => '_subscription_length',
'class' => 'wc_input_subscription_length select short', 'class' => 'wc_input_subscription_length select short wc-enhanced-select',
'label' => __( 'Expire after', 'woocommerce-subscriptions' ), 'label' => __( 'Expire after', 'woocommerce-subscriptions' ),
'options' => wcs_get_subscription_ranges( $chosen_period ), 'options' => wcs_get_subscription_ranges( $chosen_period ),
'desc_tip' => true, 'desc_tip' => true,
@@ -372,7 +372,7 @@ class WC_Subscriptions_Admin {
<span class="wrap"> <span class="wrap">
<input type="text" id="_subscription_trial_length" name="_subscription_trial_length" class="wc_input_subscription_trial_length" value="<?php echo esc_attr( $chosen_trial_length ); ?>" /> <input type="text" id="_subscription_trial_length" name="_subscription_trial_length" class="wc_input_subscription_trial_length" value="<?php echo esc_attr( $chosen_trial_length ); ?>" />
<label for="_subscription_trial_period" class="wcs_hidden_label"><?php esc_html_e( 'Subscription Trial Period', 'woocommerce-subscriptions' ); ?></label> <label for="_subscription_trial_period" class="wcs_hidden_label"><?php esc_html_e( 'Subscription Trial Period', 'woocommerce-subscriptions' ); ?></label>
<select id="_subscription_trial_period" name="_subscription_trial_period" class="wc_input_subscription_trial_period last" > <select id="_subscription_trial_period" name="_subscription_trial_period" class="wc_input_subscription_trial_period last wc-enhanced-select" >
<?php foreach ( wcs_get_available_time_periods() as $value => $label ) { ?> <?php foreach ( wcs_get_available_time_periods() as $value => $label ) { ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $chosen_trial_period, true ); ?>><?php echo esc_html( $label ); ?></option> <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $chosen_trial_period, true ); ?>><?php echo esc_html( $label ); ?></option>
<?php } ?> <?php } ?>
@@ -943,7 +943,7 @@ class WC_Subscriptions_Admin {
} }
if ( $is_woocommerce_screen || 'edit-product' == $screen->id || ( isset( $_GET['page'], $_GET['tab'] ) && 'wc-reports' === $_GET['page'] && 'subscriptions' === $_GET['tab'] ) ) { if ( $is_woocommerce_screen || 'edit-product' == $screen->id || ( isset( $_GET['page'], $_GET['tab'] ) && 'wc-reports' === $_GET['page'] && 'subscriptions' === $_GET['tab'] ) ) {
wp_enqueue_style( 'woocommerce_admin_styles', WC()->plugin_url() . '/assets/css/admin.css', array(), WC_Subscriptions_Core_Plugin::instance()->get_library_version() ); wp_enqueue_style( 'woocommerce_admin_styles', WC()->plugin_url() . '/assets/css/admin.css', [ 'wc-components' ], WC_Subscriptions_Core_Plugin::instance()->get_library_version() );
wp_enqueue_style( 'woocommerce_subscriptions_admin', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/css/admin.css' ), array( 'woocommerce_admin_styles' ), WC_Subscriptions_Core_Plugin::instance()->get_library_version() ); wp_enqueue_style( 'woocommerce_subscriptions_admin', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/css/admin.css' ), array( 'woocommerce_admin_styles' ), WC_Subscriptions_Core_Plugin::instance()->get_library_version() );
} }
} }

View File

@@ -1315,8 +1315,9 @@ class WCS_Admin_Post_Types {
// On HPOS environments, WC expects a slightly different format for the bulk actions. // On HPOS environments, WC expects a slightly different format for the bulk actions.
if ( $is_hpos_enabled ) { if ( $is_hpos_enabled ) {
$id_key = wcs_is_woocommerce_pre( '8.1' ) ? 'order' : 'id';
$action_url_args = [ $action_url_args = [
'order' => [ $subscription->get_id() ], $id_key => [ $subscription->get_id() ],
'_wpnonce' => wp_create_nonce( 'bulk-orders' ), '_wpnonce' => wp_create_nonce( 'bulk-orders' ),
]; ];
} else { } else {

View File

@@ -251,31 +251,30 @@ class WC_Subscription extends WC_Order {
} }
/** /**
* Checks if the subscription has an unpaid order or renewal order (and therefore, needs payment). * Checks if the subscription needs payment.
*
* A subscription requires payment if it:
* - is pending or failed,
* - has an unpaid parent order, or
* - has an unpaid order or renewal order (and therefore, needs payment)
* *
* @param string $subscription_key A subscription key of the form created by @see self::get_subscription_key()
* @param int $user_id The ID of the user who owns the subscriptions. Although this parameter is optional, if you have the User ID you should pass it to improve performance.
* @return bool True if the subscription has an unpaid renewal order, false if the subscription has no unpaid renewal orders.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*
* @return bool True if the subscription requires payment, otherwise false.
*/ */
public function needs_payment() { public function needs_payment() {
$needs_payment = false; $needs_payment = false;
$parent_order = $this->get_parent();
// First check if the subscription is pending or failed or is for $0 // If the subscription is pending or failed and it has a total > 0, it needs payment.
if ( parent::needs_payment() ) { if ( parent::needs_payment() ) {
$needs_payment = true; $needs_payment = true;
} elseif ( $parent_order && ( $parent_order->needs_payment() || $parent_order->has_status( array( 'on-hold', 'cancelled' ) ) ) ) {
// Now make sure the parent order doesn't need payment // If the subscription has an unpaid parent order, it needs payment.
} elseif ( ( $parent_order = $this->get_parent() ) && ( $parent_order->needs_payment() || $parent_order->has_status( array( 'on-hold', 'cancelled' ) ) ) ) {
$needs_payment = true; $needs_payment = true;
// And finally, check that the latest order (switch or renewal) doesn't need payment
} else { } else {
// Lastly, check if the last non-early renewal order needs payment.
$order = $this->get_last_order( 'all', array( 'renewal', 'switch' ) ); $order = wcs_get_last_non_early_renewal_order( $this );
if ( $order && ( $order->needs_payment() || $order->has_status( array( 'on-hold', 'failed', 'cancelled' ) ) ) ) { if ( $order && ( $order->needs_payment() || $order->has_status( array( 'on-hold', 'failed', 'cancelled' ) ) ) ) {
$needs_payment = true; $needs_payment = true;

View File

@@ -24,11 +24,7 @@ class WC_Subscriptions_Checkout {
add_action( 'woocommerce_checkout_order_processed', array( __CLASS__, 'process_checkout' ), 100, 2 ); add_action( 'woocommerce_checkout_order_processed', array( __CLASS__, 'process_checkout' ), 100, 2 );
// Same as above, but this is for the Checkout block. // Same as above, but this is for the Checkout block.
if ( class_exists( 'Automattic\WooCommerce\Blocks\Package' ) && ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '6.3.0', '>=' ) || \Automattic\WooCommerce\Blocks\Package::is_experimental_build() ) ) { add_action( 'woocommerce_store_api_checkout_order_processed', array( __CLASS__, 'process_checkout' ), 100, 1 );
add_action( 'woocommerce_blocks_checkout_order_processed', array( __CLASS__, 'process_checkout' ), 100, 1 );
} else {
add_action( '__experimental_woocommerce_blocks_checkout_order_processed', array( __CLASS__, 'process_checkout' ), 100, 1 );
}
// Some callbacks need to hooked after WC has loaded. // Some callbacks need to hooked after WC has loaded.
add_action( 'woocommerce_loaded', array( __CLASS__, 'attach_dependant_hooks' ) ); add_action( 'woocommerce_loaded', array( __CLASS__, 'attach_dependant_hooks' ) );

View File

@@ -16,7 +16,7 @@ class WC_Subscriptions_Core_Plugin {
* The version of subscriptions-core library. * The version of subscriptions-core library.
* @var string * @var string
*/ */
protected $library_version = '6.2.0'; // WRCS: DEFINED_VERSION. protected $library_version = '6.4.0'; // WRCS: DEFINED_VERSION.
/** /**
* The subscription scheduler instance. * The subscription scheduler instance.

View File

@@ -1001,7 +1001,7 @@ class WC_Subscriptions_Manager {
} }
} }
wp_delete_post( $subscription->get_id() ); $subscription->delete( true );
} }
} }
} }

View File

@@ -32,6 +32,10 @@ class WC_Subscriptions_Order {
add_filter( 'manage_edit-shop_order_columns', __CLASS__ . '::add_contains_subscription_column' ); add_filter( 'manage_edit-shop_order_columns', __CLASS__ . '::add_contains_subscription_column' );
add_action( 'manage_shop_order_posts_custom_column', __CLASS__ . '::add_contains_subscription_column_content', 10, 1 ); add_action( 'manage_shop_order_posts_custom_column', __CLASS__ . '::add_contains_subscription_column_content', 10, 1 );
// HPOS - Add column that indicates whether an order is parent or renewal for a subscription.
add_filter( 'woocommerce_shop_order_list_table_columns', __CLASS__ . '::add_contains_subscription_column' );
add_action( 'woocommerce_shop_order_list_table_custom_column', __CLASS__ . '::add_contains_subscription_column_content_orders_table', 10, 2 );
// Record initial payment against the subscription & set start date based on that payment // Record initial payment against the subscription & set start date based on that payment
add_action( 'woocommerce_order_status_changed', __CLASS__ . '::maybe_record_subscription_payment', 9, 3 ); add_action( 'woocommerce_order_status_changed', __CLASS__ . '::maybe_record_subscription_payment', 9, 3 );
@@ -44,8 +48,13 @@ class WC_Subscriptions_Order {
// Add dropdown to admin orders screen to filter on order type // Add dropdown to admin orders screen to filter on order type
add_action( 'restrict_manage_posts', __CLASS__ . '::restrict_manage_subscriptions', 50 ); add_action( 'restrict_manage_posts', __CLASS__ . '::restrict_manage_subscriptions', 50 );
// For HPOS - Add dropdown to admin orders screen to filter on order type.
add_action( 'woocommerce_order_list_table_restrict_manage_orders', __CLASS__ . '::restrict_manage_subscriptions_hpos' );
// Add filter to queries on admin orders screen to filter on order type. To avoid WC overriding our query args, we need to hook on after them on 10. // Add filter to queries on admin orders screen to filter on order type. To avoid WC overriding our query args, we need to hook on after them on 10.
add_filter( 'request', __CLASS__ . '::orders_by_type_query', 11 ); add_filter( 'request', __CLASS__ . '::orders_by_type_query', 11 );
// HPOS - Add filter to queries on admin orders screen to filter on order type. Only triggered for the shop_order order type.
add_filter( 'woocommerce_shop_order_list_table_prepare_items_query_args', __CLASS__ . '::maybe_modify_orders_by_type_query_from_request', 11 );
// Don't display migrated order item meta on the Edit Order screen // Don't display migrated order item meta on the Edit Order screen
add_filter( 'woocommerce_hidden_order_itemmeta', __CLASS__ . '::hide_order_itemmeta' ); add_filter( 'woocommerce_hidden_order_itemmeta', __CLASS__ . '::hide_order_itemmeta' );
@@ -412,8 +421,9 @@ class WC_Subscriptions_Order {
/** /**
* Add column content to the WooCommerce -> Orders admin screen to indicate whether an * Add column content to the WooCommerce -> Orders admin screen to indicate whether an
* order is a parent of a subscription, a renewal order for a subscription, or a * order is a parent of a subscription, a renewal order for a subscription, or a regular order.
* regular order. *
* @see add_contains_subscription_column_content_orders_table For when HPOS is enabled.
* *
* @param string $column The string of the current column * @param string $column The string of the current column
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
@@ -421,17 +431,26 @@ class WC_Subscriptions_Order {
public static function add_contains_subscription_column_content( $column ) { public static function add_contains_subscription_column_content( $column ) {
global $post; global $post;
if ( 'subscription_relationship' == $column ) { if ( 'subscription_relationship' === $column ) {
if ( wcs_order_contains_subscription( $post->ID, 'renewal' ) ) { self::render_contains_subscription_column_content( $post->ID );
echo '<span class="subscription_renewal_order tips" data-tip="' . esc_attr__( 'Renewal Order', 'woocommerce-subscriptions' ) . '"></span>';
} elseif ( wcs_order_contains_subscription( $post->ID, 'resubscribe' ) ) {
echo '<span class="subscription_resubscribe_order tips" data-tip="' . esc_attr__( 'Resubscribe Order', 'woocommerce-subscriptions' ) . '"></span>';
} elseif ( wcs_order_contains_subscription( $post->ID, 'parent' ) ) {
echo '<span class="subscription_parent_order tips" data-tip="' . esc_attr__( 'Parent Order', 'woocommerce-subscriptions' ) . '"></span>';
} else {
echo '<span class="normal_order">&ndash;</span>';
} }
} }
/**
* Add column content to the WooCommerce -> Orders admin screen to indicate whether an
* order is a parent of a subscription, a renewal order for a subscription, or a regular order.
*
* @see add_contains_subscription_column_content For when HPOS is disabled.
*
* @since 6.3.0
*
* @param string $column_name Identifier for the custom column.
* @param WC_Order $order Current WooCommerce order object.
*/
public static function add_contains_subscription_column_content_orders_table( string $column_name, WC_Order $order ) {
if ( 'subscription_relationship' === $column_name ) {
self::render_contains_subscription_column_content( $order->get_id() );
}
} }
/** /**
@@ -708,33 +727,26 @@ class WC_Subscriptions_Order {
public static function restrict_manage_subscriptions() { public static function restrict_manage_subscriptions() {
global $typenow; global $typenow;
if ( 'shop_order' != $typenow ) { if ( 'shop_order' !== $typenow ) {
return; return;
}?>
<select name='shop_order_subtype' id='dropdown_shop_order_subtype'>
<option value=""><?php esc_html_e( 'All orders types', 'woocommerce-subscriptions' ); ?></option>
<?php
$order_types = apply_filters( 'woocommerce_subscriptions_order_type_dropdown', array(
'original' => _x( 'Original', 'An order type', 'woocommerce-subscriptions' ),
'parent' => _x( 'Subscription Parent', 'An order type', 'woocommerce-subscriptions' ),
'renewal' => _x( 'Subscription Renewal', 'An order type', 'woocommerce-subscriptions' ),
'resubscribe' => _x( 'Subscription Resubscribe', 'An order type', 'woocommerce-subscriptions' ),
'switch' => _x( 'Subscription Switch', 'An order type', 'woocommerce-subscriptions' ),
'regular' => _x( 'Non-subscription', 'An order type', 'woocommerce-subscriptions' ),
) );
foreach ( $order_types as $order_type_key => $order_type_description ) {
echo '<option value="' . esc_attr( $order_type_key ) . '"';
if ( isset( $_GET['shop_order_subtype'] ) && $_GET['shop_order_subtype'] ) {
selected( $order_type_key, $_GET['shop_order_subtype'] );
} }
echo '>' . esc_html( $order_type_description ) . '</option>'; self::render_restrict_manage_subscriptions_dropdown();
} }
?>
</select> /**
<?php * When HPOS is active, adds admin dropdown for order types to Woocommerce -> Orders screen
*
* @since 6.3.0
*
* @param string $order_type The order type.
*/
public static function restrict_manage_subscriptions_hpos( string $order_type ) {
if ( 'shop_order' !== $order_type ) {
return;
}
self::render_restrict_manage_subscriptions_dropdown();
} }
/** /**
@@ -746,31 +758,54 @@ class WC_Subscriptions_Order {
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5
*/ */
public static function orders_by_type_query( $vars ) { public static function orders_by_type_query( $vars ) {
global $typenow, $wpdb; global $typenow;
if ( 'shop_order' == $typenow && ! empty( $_GET['shop_order_subtype'] ) ) { if ( 'shop_order' === $typenow ) {
return self::maybe_modify_orders_by_type_query_from_request( $vars );
}
if ( 'original' == $_GET['shop_order_subtype'] || 'regular' == $_GET['shop_order_subtype'] ) { return $vars;
}
$vars['meta_query']['relation'] = 'AND'; /**
* Filters the arguments to be pased to `wc_get_orders()` under the Woocommerce -> Orders screen.
*
* @since 6.3.0
*
* @param array $order_query_args Arguments to be passed to `wc_get_orders()`.
*
* @return array
*/
public static function maybe_modify_orders_by_type_query_from_request( array $order_query_args ): array {
// The order subtype selected by the user in the dropdown.
$selected_shop_order_subtype = isset( $_GET['shop_order_subtype'] ) ? wc_clean( wp_unslash( $_GET['shop_order_subtype'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$vars['meta_query'][] = array( // Don't modify the query args if no order subtype was selected.
if ( empty( $selected_shop_order_subtype ) ) {
return $order_query_args;
}
if ( 'original' === $selected_shop_order_subtype || 'regular' === $selected_shop_order_subtype ) {
$order_query_args['meta_query']['relation'] = 'AND';
$order_query_args['meta_query'][] = array(
'key' => '_subscription_renewal', 'key' => '_subscription_renewal',
'compare' => 'NOT EXISTS', 'compare' => 'NOT EXISTS',
); );
$vars['meta_query'][] = array( $order_query_args['meta_query'][] = array(
'key' => '_subscription_switch', 'key' => '_subscription_switch',
'compare' => 'NOT EXISTS', 'compare' => 'NOT EXISTS',
); );
} elseif ( 'parent' == $_GET['shop_order_subtype'] ) { } elseif ( 'parent' === $selected_shop_order_subtype ) {
$vars['post__in'] = wcs_get_subscription_orders(); $order_query_args['post__in'] = wcs_get_subscription_orders();
} else { } else {
switch ( $_GET['shop_order_subtype'] ) { switch ( $selected_shop_order_subtype ) {
case 'renewal': case 'renewal':
$meta_key = '_subscription_renewal'; $meta_key = '_subscription_renewal';
break; break;
@@ -785,10 +820,10 @@ class WC_Subscriptions_Order {
break; break;
} }
$meta_key = apply_filters( 'woocommerce_subscriptions_admin_order_type_filter_meta_key', $meta_key, $_GET['shop_order_subtype'] ); $meta_key = apply_filters( 'woocommerce_subscriptions_admin_order_type_filter_meta_key', $meta_key, $selected_shop_order_subtype );
if ( ! empty( $meta_key ) ) { if ( ! empty( $meta_key ) ) {
$vars['meta_query'][] = array( $order_query_args['meta_query'][] = array(
'key' => $meta_key, 'key' => $meta_key,
'compare' => 'EXISTS', 'compare' => 'EXISTS',
); );
@@ -796,12 +831,11 @@ class WC_Subscriptions_Order {
} }
// Also exclude parent orders from non-subscription query // Also exclude parent orders from non-subscription query
if ( 'regular' == $_GET['shop_order_subtype'] ) { if ( 'regular' === $selected_shop_order_subtype ) {
$vars['post__not_in'] = wcs_get_subscription_orders(); $order_query_args['post__not_in'] = wcs_get_subscription_orders();
}
} }
return $vars; return $order_query_args;
} }
/** /**
@@ -2280,4 +2314,64 @@ class WC_Subscriptions_Order {
return $meta_value; return $meta_value;
} }
/**
* Prints the HTML for the admin dropdown for order types to Woocommerce -> Orders screen.
*
* @since 6.3.0
*/
private static function render_restrict_manage_subscriptions_dropdown() {
$order_types = apply_filters(
'woocommerce_subscriptions_order_type_dropdown',
array(
'original' => _x( 'Original', 'An order type', 'woocommerce-subscriptions' ),
'parent' => _x( 'Subscription Parent', 'An order type', 'woocommerce-subscriptions' ),
'renewal' => _x( 'Subscription Renewal', 'An order type', 'woocommerce-subscriptions' ),
'resubscribe' => _x( 'Subscription Resubscribe', 'An order type', 'woocommerce-subscriptions' ),
'switch' => _x( 'Subscription Switch', 'An order type', 'woocommerce-subscriptions' ),
'regular' => _x( 'Non-subscription', 'An order type', 'woocommerce-subscriptions' ),
)
);
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$selected_shop_order_subtype = isset( $_GET['shop_order_subtype'] ) ? wc_clean( wp_unslash( $_GET['shop_order_subtype'] ) ) : '';
?>
<select name='shop_order_subtype' id='dropdown_shop_order_subtype'>
<option value=""><?php esc_html_e( 'All orders types', 'woocommerce-subscriptions' ); ?></option>
<?php foreach ( $order_types as $order_type_key => $order_type_description ) : ?>
<option
value="<?php echo esc_attr( $order_type_key ); ?>"
<?php selected( $selected_shop_order_subtype, $order_type_key ); ?>
>
<?php echo esc_html( $order_type_description ); ?>
</option>
<?php endforeach; ?>
</select>
<?php
}
/**
* Renders the contents of the "contains_subscription" column.
*
* This column indicates whether an order is a parent of a subscription,
* a renewal order for a subscription, or a regular order.
*
* @since 6.3.0
*
* @param integer $order_id The ID of the order in the current row.
*/
private static function render_contains_subscription_column_content( int $order_id ) {
if ( wcs_order_contains_subscription( $order_id, 'renewal' ) ) {
echo '<span class="subscription_renewal_order tips" data-tip="' . esc_attr__( 'Renewal Order', 'woocommerce-subscriptions' ) . '"></span>';
} elseif ( wcs_order_contains_subscription( $order_id, 'resubscribe' ) ) {
echo '<span class="subscription_resubscribe_order tips" data-tip="' . esc_attr__( 'Resubscribe Order', 'woocommerce-subscriptions' ) . '"></span>';
} elseif ( wcs_order_contains_subscription( $order_id, 'parent' ) ) {
echo '<span class="subscription_parent_order tips" data-tip="' . esc_attr__( 'Parent Order', 'woocommerce-subscriptions' ) . '"></span>';
} else {
echo '<span class="normal_order">&ndash;</span>';
}
}
} }

View File

@@ -218,6 +218,7 @@ class WC_Subscriptions_Synchroniser {
'css' => 'min-width:150px;', 'css' => 'min-width:150px;',
'default' => 'no', 'default' => 'no',
'type' => 'select', 'type' => 'select',
'class' => 'wc-enhanced-select',
'options' => array( 'options' => array(
'no' => _x( 'Never (do not charge any recurring amount)', 'when to prorate first payment / subscription length', 'woocommerce-subscriptions' ), 'no' => _x( 'Never (do not charge any recurring amount)', 'when to prorate first payment / subscription length', 'woocommerce-subscriptions' ),
'recurring' => _x( 'Never (charge the full recurring amount at sign-up)', 'when to prorate first payment / subscription length', 'woocommerce-subscriptions' ), 'recurring' => _x( 'Never (charge the full recurring amount at sign-up)', 'when to prorate first payment / subscription length', 'woocommerce-subscriptions' ),
@@ -285,7 +286,7 @@ class WC_Subscriptions_Synchroniser {
woocommerce_wp_select( woocommerce_wp_select(
array( array(
'id' => self::$post_meta_key, 'id' => self::$post_meta_key,
'class' => 'wc_input_subscription_payment_sync select short', 'class' => 'wc_input_subscription_payment_sync select short wc-enhanced-select',
'label' => self::$sync_field_label, 'label' => self::$sync_field_label,
'options' => self::get_billing_period_ranges( $subscription_period ), 'options' => self::get_billing_period_ranges( $subscription_period ),
'description' => self::$sync_description, 'description' => self::$sync_description,
@@ -303,14 +304,14 @@ class WC_Subscriptions_Synchroniser {
<span class="wrap"> <span class="wrap">
<label for="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wcs_hidden_label"><?php esc_html_e( 'Month for Synchronisation', 'woocommerce-subscriptions' ); ?></label> <label for="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wcs_hidden_label"><?php esc_html_e( 'Month for Synchronisation', 'woocommerce-subscriptions' ); ?></label>
<select id="<?php echo esc_attr( self::$post_meta_key_month ); ?>" name="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wc_input_subscription_payment_sync last" > <select id="<?php echo esc_attr( self::$post_meta_key_month ); ?>" name="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wc_input_subscription_payment_sync last wc-enhanced-select" >
<?php foreach ( self::get_year_sync_options() as $value => $label ) { ?> <?php foreach ( self::get_year_sync_options() as $value => $label ) { ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $payment_month, true ) ?>><?php echo esc_html( $label ); ?></option> <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $payment_month, true ) ?>><?php echo esc_html( $label ); ?></option>
<?php } ?> <?php } ?>
</select> </select>
<?php $daysInMonth = $payment_month ? gmdate( 't', wc_string_to_timestamp( "2001-{$payment_month}-01" ) ) : 0; ?> <?php $days_in_month = $payment_month ? gmdate( 't', wc_string_to_timestamp( "2001-{$payment_month}-01" ) ) : 0; ?>
<input type="number" id="<?php echo esc_attr( self::$post_meta_key_day ); ?>" name="<?php echo esc_attr( self::$post_meta_key_day ); ?>" class="wc_input_subscription_payment_sync" value="<?php echo esc_attr( $payment_day ); ?>" placeholder="<?php echo esc_attr_x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ); ?>" step="1" min="<?php echo esc_attr( min( 1, $daysInMonth ) ); ?>" max="<?php echo esc_attr( $daysInMonth ); ?>" <?php disabled( 0, $payment_month, true ); ?> /> <input type="number" id="<?php echo esc_attr( self::$post_meta_key_day ); ?>" name="<?php echo esc_attr( self::$post_meta_key_day ); ?>" class="wc_input_subscription_payment_sync wc-enhanced-select" value="<?php echo esc_attr( $payment_day ); ?>" placeholder="<?php echo esc_attr_x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ); ?>" step="1" min="<?php echo esc_attr( min( 1, $days_in_month ) ); ?>" max="<?php echo esc_attr( $days_in_month ); ?>" <?php disabled( 0, $payment_month, true ); ?> />
</span> </span>
<?php echo wcs_help_tip( self::$sync_description_year ); ?> <?php echo wcs_help_tip( self::$sync_description_year ); ?>
</p><?php </p><?php

View File

@@ -134,7 +134,7 @@ class WCS_Cart_Initial_Payment extends WCS_Cart_Renewal {
} }
/** /**
* Deteremines if the cart should honor the granfathered subscription/order line item total. * Determines if the cart should honor the grandfathered subscription/order line item total.
* *
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.10 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.10
* *

View File

@@ -35,11 +35,9 @@ class WCS_Cart_Renewal {
// Remove order action buttons from the My Account page // Remove order action buttons from the My Account page
add_filter( 'woocommerce_my_account_my_orders_actions', array( &$this, 'filter_my_account_my_orders_actions' ), 10, 2 ); add_filter( 'woocommerce_my_account_my_orders_actions', array( &$this, 'filter_my_account_my_orders_actions' ), 10, 2 );
// When a failed renewal order is paid for via checkout, make sure WC_Checkout::create_order() preserves its "failed" status until it is paid
add_filter( 'woocommerce_default_order_status', array( &$this, 'maybe_preserve_order_status' ) );
// When a failed/pending renewal order is paid for via checkout, ensure a new order isn't created due to mismatched cart hashes // When a failed/pending renewal order is paid for via checkout, ensure a new order isn't created due to mismatched cart hashes
add_filter( 'woocommerce_create_order', array( &$this, 'update_cart_hash' ), 10, 1 ); add_filter( 'woocommerce_create_order', array( &$this, 'update_cart_hash' ), 10, 1 );
add_filter( 'woocommerce_order_has_status', array( &$this, 'set_renewal_order_cart_hash_on_block_checkout' ), 10, 3 );
// 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, 2 ); add_filter( 'woocommerce_login_redirect', array( &$this, 'maybe_redirect_after_login' ), 10, 2 );
@@ -93,11 +91,7 @@ class WCS_Cart_Renewal {
add_action( 'woocommerce_checkout_update_order_meta', array( &$this, 'set_order_item_id' ), 10, 2 ); add_action( 'woocommerce_checkout_update_order_meta', array( &$this, 'set_order_item_id' ), 10, 2 );
// After order meta is saved, get the order line item ID for the renewal so we can update it later // After order meta is saved, get the order line item ID for the renewal so we can update it later
if ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '7.2.0', '>=' ) ) {
add_action( 'woocommerce_store_api_checkout_update_order_meta', array( &$this, 'set_order_item_id' ) ); add_action( 'woocommerce_store_api_checkout_update_order_meta', array( &$this, 'set_order_item_id' ) );
} else {
add_action( 'woocommerce_blocks_checkout_update_order_meta', array( &$this, 'set_order_item_id' ) );
}
// Don't display cart item key meta stored above on the Edit Order screen // 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 ); add_action( 'woocommerce_hidden_order_itemmeta', array( &$this, 'hidden_order_itemmeta' ), 10 );
@@ -416,6 +410,10 @@ class WCS_Cart_Renewal {
* Restore renewal flag when cart is reset and modify Product object with renewal order related info * Restore renewal flag when cart is reset and modify Product object with renewal order related info
* *
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*
* @param array $cart_item_session_data Cart item session data.
* @param array $cart_item Cart item data.
* @param string $key Cart item key.
*/ */
public function get_cart_item_from_session( $cart_item_session_data, $cart_item, $key ) { public function get_cart_item_from_session( $cart_item_session_data, $cart_item, $key ) {
@@ -429,7 +427,23 @@ class WCS_Cart_Renewal {
if ( $subscription ) { if ( $subscription ) {
$subscription_items = $subscription->get_items(); $subscription_items = $subscription->get_items();
/**
* Find the subscription or order line item that represents this cart item.
*
* If cart item data correctly records a valid line item ID, use that to find the line item.
* Otherwise, use the cart item key stored in line item meta.
*/
if ( isset( $subscription_items[ $cart_item_session_data[ $this->cart_item_key ]['line_item_id'] ] ) ) {
$item_to_renew = $subscription_items[ $cart_item_session_data[ $this->cart_item_key ]['line_item_id'] ]; $item_to_renew = $subscription_items[ $cart_item_session_data[ $this->cart_item_key ]['line_item_id'] ];
} else {
foreach ( $subscription_items as $item ) {
if ( $item->get_meta( '_cart_item_key_' . $this->cart_item_key, true ) === $key ) {
$item_to_renew = $item;
break;
}
}
}
$price = $item_to_renew['line_subtotal']; $price = $item_to_renew['line_subtotal'];
@@ -635,33 +649,6 @@ class WCS_Cart_Renewal {
return $actions; return $actions;
} }
/**
* When a failed renewal order is being paid for via checkout, make sure WC_Checkout::create_order() preserves its
* status as 'failed' until it is paid. By default, it will always set it to 'pending', but we need it left as 'failed'
* so that we can correctly identify the status change in @see self::maybe_change_subscription_status().
*
* @param string Default order status for orders paid for via checkout. Default 'pending'
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*/
public function maybe_preserve_order_status( $order_status ) {
if ( null !== WC()->session && 'failed' !== $order_status ) {
$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' ) ) {
$order_status = 'failed';
}
add_filter( 'woocommerce_default_order_status', array( &$this, __FUNCTION__ ) );
}
return $order_status;
}
/** /**
* Removes all the linked renewal/resubscribe items from the cart if a renewal/resubscribe item is removed. * Removes all the linked renewal/resubscribe items from the cart if a renewal/resubscribe item is removed.
* *
@@ -946,7 +933,7 @@ class WCS_Cart_Renewal {
*/ */
public function product_addons_adjust_price( $adjust_price, $cart_item ) { public function product_addons_adjust_price( $adjust_price, $cart_item ) {
if ( true === $adjust_price && isset( $cart_item[ $this->cart_item_key ] ) ) { if ( true === $adjust_price && isset( $cart_item[ $this->cart_item_key ] ) && $this->should_honor_subscription_prices( $cart_item ) ) {
$adjust_price = false; $adjust_price = false;
} }
@@ -979,10 +966,19 @@ class WCS_Cart_Renewal {
* order items haven't changed by checking for a cart hash on the order, so we need to set * order items haven't changed by checking for a cart hash on the order, so we need to set
* that here. @see WC_Checkout::create_order() * that here. @see WC_Checkout::create_order()
* *
* @param WC_Order|int $order The order object or order ID.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.14 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.14
*/ */
protected function set_cart_hash( $order_id ) { protected function set_cart_hash( $order ) {
$order = wc_get_order( $order_id );
if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
$order = wc_get_order( $order );
if ( ! $order ) {
return;
}
}
// Use cart hash generator introduced in WooCommerce 3.6 // Use cart hash generator introduced in WooCommerce 3.6
if ( is_callable( array( WC()->cart, 'get_cart_hash' ) ) ) { if ( is_callable( array( WC()->cart, 'get_cart_hash' ) ) ) {
@@ -991,7 +987,8 @@ class WCS_Cart_Renewal {
$cart_hash = md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total ); $cart_hash = md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total );
} }
wcs_set_objects_property( $order, 'cart_hash', $cart_hash ); $order->set_cart_hash( $cart_hash );
$order->save();
} }
/** /**
@@ -1494,7 +1491,7 @@ class WCS_Cart_Renewal {
/** /**
* Deteremines if the cart should honor the granfathered subscription/order line item total. * Determines if the cart should honor the grandfathered subscription/order line item total.
* *
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.10 * @since 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.10
* *
@@ -1590,6 +1587,53 @@ class WCS_Cart_Renewal {
return current_user_can( 'pay_for_order', $order->get_id() ); return current_user_can( 'pay_for_order', $order->get_id() );
} }
/**
* Sets the order cart hash when paying for a renewal order via the Block Checkout.
*
* This function is hooked onto the 'woocommerce_order_has_status' filter, is only applied during REST API requests, only applies to the
* 'checkout-draft' status (which only Block Checkout orders use) and to renewal orders that are currently being paid for in the cart.
* All other order statuses, orders and scenarios remain unaffected by this function.
*
* This function is necessary to override the default logic in @see DraftOrderTrait::is_valid_draft_order().
* This function behaves similarly to @see WCS_Cart_Renewal::update_cart_hash() for the standard checkout and is hooked onto the 'woocommerce_create_order' filter.
*
* @param bool $has_status Whether the order has the status.
* @param WC_Order $order The order.
* @param string $status The status to check.
*
* @return bool Whether the order has the status. Unchanged by this function.
*/
public function set_renewal_order_cart_hash_on_block_checkout( $has_status, $order, $status ) {
/**
* We only need to update the order's cart hash when the has_status() check is for 'checkout-draft' (indicating
* this is the status check in DraftOrderTrait::is_valid_draft_order()) and the order doesn't have that status. Orders
* which already have the checkout-draft status don't need to be updated to bypass the checkout block logic.
*/
if ( $has_status || 'checkout-draft' !== $status ) {
return $has_status;
}
/**
* This function is only concerned with updating the order cart hash during REST API requests - which is the request
* context where the Store API Checkout Block validates the order for payment resumption.
*/
if ( ! WC()->is_rest_api_request() ) {
return $has_status;
}
// If the order being validated is the order in the cart, then we need to update the cart hash so it can be resumed.
if ( $order && $order->get_id() === WC()->session->get( 'store_api_draft_order', 0 ) ) {
$cart_order = $this->get_order();
if ( $cart_order && $cart_order->get_id() === $order->get_id() ) {
// Note: We need to pass the order object so the order instance WooCommerce uses will have the updated hash.
$this->set_cart_hash( $order );
}
}
return $has_status;
}
/* Deprecated */ /* Deprecated */
/** /**
@@ -1765,4 +1809,35 @@ class WCS_Cart_Renewal {
} }
} }
} }
/**
* When a failed renewal order is being paid for via checkout, make sure WC_Checkout::create_order() preserves its
* status as 'failed' until it is paid. By default, it will always set it to 'pending', but we need it left as 'failed'
* so that we can correctly identify the status change in @see self::maybe_change_subscription_status().
*
* @param string Default order status for orders paid for via checkout. Default 'pending'
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*
* @deprecated 6.3.0
*/
public function maybe_preserve_order_status( $order_status ) {
wcs_deprecated_function( __METHOD__, '6.3.0' );
if ( null !== WC()->session && 'failed' !== $order_status ) {
$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 );
$order = $order_id > 0 ? wc_get_order( $order_id ) : null;
if ( $order && wcs_order_contains_renewal( $order ) && $order->has_status( 'failed' ) ) {
$order_status = 'failed';
}
add_filter( 'woocommerce_default_order_status', array( &$this, __FUNCTION__ ) );
}
return $order_status;
}
} }

View File

@@ -43,6 +43,7 @@ class WCS_Limiter {
woocommerce_wp_select( woocommerce_wp_select(
array( array(
'id' => '_subscription_limit', 'id' => '_subscription_limit',
'class' => 'wc-enhanced-select',
'label' => __( 'Limit subscription', 'woocommerce-subscriptions' ), 'label' => __( 'Limit subscription', 'woocommerce-subscriptions' ),
// translators: placeholders are opening and closing link tags // translators: placeholders are opening and closing link tags
'description' => sprintf( __( 'Only allow a customer to have one subscription to this product. %1$sLearn more%2$s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/store-manager-guide/#limit-subscription">', '</a>' ), 'description' => sprintf( __( 'Only allow a customer to have one subscription to this product. %1$sLearn more%2$s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/store-manager-guide/#limit-subscription">', '</a>' ),

View File

@@ -21,7 +21,9 @@ class WCS_My_Account_Payment_Methods {
} }
add_filter( 'woocommerce_payment_methods_list_item', array( __CLASS__, 'flag_subscription_payment_token_deletions' ), 10, 2 ); add_filter( 'woocommerce_payment_methods_list_item', array( __CLASS__, 'flag_subscription_payment_token_deletions' ), 10, 2 );
add_action( 'woocommerce_payment_token_deleted', array( __CLASS__, 'maybe_update_subscriptions_payment_meta' ), 10, 2 );
// This needs to run after the payment plugins had a chance to execute their delete actions.
add_action( 'woocommerce_payment_token_deleted', array( __CLASS__, 'maybe_update_subscriptions_payment_meta' ), 11, 2 );
add_action( 'woocommerce_payment_token_set_default', array( __CLASS__, 'display_default_payment_token_change_notice' ), 10, 2 ); add_action( 'woocommerce_payment_token_set_default', array( __CLASS__, 'display_default_payment_token_change_notice' ), 10, 2 );
add_action( 'wp', array( __CLASS__, 'update_subscription_tokens' ) ); add_action( 'wp', array( __CLASS__, 'update_subscription_tokens' ) );

View File

@@ -13,8 +13,6 @@ class WCS_Template_Loader {
* @var array[] Array of file names and their directory found in templates/ * @var array[] Array of file names and their directory found in templates/
*/ */
private static $relocated_templates = [ private static $relocated_templates = [
'html-variation-price.php' => 'admin/deprecated/',
'html-variation-synchronisation.php' => 'admin/deprecated/',
'order-shipping-html.php' => 'admin/deprecated/', 'order-shipping-html.php' => 'admin/deprecated/',
'order-tax-html.php' => 'admin/deprecated/', 'order-tax-html.php' => 'admin/deprecated/',
'html-admin-notice.php' => 'admin/', 'html-admin-notice.php' => 'admin/',

View File

@@ -609,6 +609,11 @@ class WCS_Orders_Table_Subscription_Data_Store extends \Automattic\WooCommerce\I
]; ];
if ( empty( $existing_meta_data ) ) { if ( empty( $existing_meta_data ) ) {
// If we're saving a start date for the first time and it's empty, set it to the created date as a default.
if ( '_schedule_start' === $new_meta_data['key'] && empty( $new_meta_data['value'] ) ) {
$new_meta_data['value'] = $subscription->get_date( 'date_created' );
}
$this->data_store_meta->add_meta( $subscription, (object) $new_meta_data ); $this->data_store_meta->add_meta( $subscription, (object) $new_meta_data );
} elseif ( $existing_meta_data->meta_value !== $new_meta_data['value'] ) { } elseif ( $existing_meta_data->meta_value !== $new_meta_data['value'] ) {
$new_meta_data['id'] = $existing_meta_data->meta_id; $new_meta_data['id'] = $existing_meta_data->meta_id;
@@ -649,7 +654,7 @@ class WCS_Orders_Table_Subscription_Data_Store extends \Automattic\WooCommerce\I
continue; continue;
} }
// If we're setting the start date and it's missing, we set it to the created date. // If we're reading in the start date and it's missing, set it in memory to the created date.
if ( 'schedule_start' === $prop_key && empty( $meta_data[ $meta_key ] ) ) { if ( 'schedule_start' === $prop_key && empty( $meta_data[ $meta_key ] ) ) {
$meta_data[ $meta_key ] = $subscription->get_date( 'date_created' ); $meta_data[ $meta_key ] = $subscription->get_date( 'date_created' );
} }

View File

@@ -221,6 +221,10 @@ class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements
foreach ( $this->get_props_to_update( $subscription, $this->subscription_meta_keys_to_props ) as $meta_key => $prop ) { 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' ); $meta_value = ( 'schedule_' == substr( $prop, 0, 9 ) ) ? $subscription->get_date( $prop ) : $subscription->{"get_$prop"}( 'edit' );
if ( 'schedule_start' === $prop && ! $meta_value ) {
$meta_value = $subscription->get_date( 'date_created' );
}
// Store as a string of the boolean for backward compatibility (yep, it's gross) // Store as a string of the boolean for backward compatibility (yep, it's gross)
if ( 'requires_manual_renewal' === $prop ) { if ( 'requires_manual_renewal' === $prop ) {
$meta_value = $meta_value ? 'true' : 'false'; $meta_value = $meta_value ? 'true' : 'false';

View File

@@ -174,7 +174,7 @@ class WCS_Email_Cancelled_Subscription extends WC_Email {
'type' => 'select', 'type' => 'select',
'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ), 'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ),
'default' => 'html', 'default' => 'html',
'class' => 'email_type', 'class' => 'email_type wc-enhanced-select',
'options' => array( 'options' => array(
'plain' => _x( 'Plain text', 'email type', 'woocommerce-subscriptions' ), 'plain' => _x( 'Plain text', 'email type', 'woocommerce-subscriptions' ),
'html' => _x( 'HTML', 'email type', 'woocommerce-subscriptions' ), 'html' => _x( 'HTML', 'email type', 'woocommerce-subscriptions' ),

View File

@@ -34,11 +34,6 @@ class WCS_Email_Completed_Renewal_Order extends WC_Email_Customer_Completed_Orde
$this->template_plain = 'emails/plain/customer-completed-renewal-order.php'; $this->template_plain = 'emails/plain/customer-completed-renewal-order.php';
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' ); $this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
// Other settings
$this->heading_downloadable = $this->get_option( 'heading_downloadable', _x( 'Your subscription renewal order is complete - download your files', 'Default email heading for email with downloadable files in it', 'woocommerce-subscriptions' ) );
// translators: $1: {blogname}, $2: {order_date}, variables will be substituted when email is sent out
$this->subject_downloadable = $this->get_option( 'subject_downloadable', sprintf( _x( 'Your %1$s subscription renewal order from %2$s is complete - download your files', 'Default email subject for email with downloadable files in it', 'woocommerce-subscriptions' ), '{blogname}', '{order_date}' ) );
// Triggers for this email // Triggers for this email
add_action( 'woocommerce_order_status_completed_renewal_notification', array( $this, 'trigger' ) ); add_action( 'woocommerce_order_status_completed_renewal_notification', array( $this, 'trigger' ) );
@@ -168,4 +163,26 @@ class WCS_Email_Completed_Renewal_Order extends WC_Email_Customer_Completed_Orde
$this->template_base $this->template_base
); );
} }
/**
* Gets the deprecated public variables for backwards compatibility.
*
* @param string $key Key.
*
* @return string|null
*/
public function __get( $key ) {
if ( 'heading_downloadable' === $key ) {
wcs_deprecated_argument( __CLASS__ . '::$' . $key, '5.6.0', 'The heading_downloadable property used for emails with downloadable files was removed in WooCommerce 3.1. Use the heading property instead.' );
return $this->get_option( 'heading_downloadable', _x( 'Your subscription renewal order is complete - download your files', 'Default email heading for email with downloadable files in it', 'woocommerce-subscriptions' ) );
} elseif ( 'subject_downloadable' === $key ) {
wcs_deprecated_argument( __CLASS__ . '::$' . $key, '5.6.0', 'The subject_downloadabl property used for emails with downloadable files was removed in WooCommerce 3.1. Use the subject property instead.' );
// translators: $1: {blogname}, $2: {order_date}, variables will be substituted when email is sent out
return $this->get_option( 'subject_downloadable', sprintf( _x( 'Your %1$s subscription renewal order from %2$s is complete - download your files', 'Default email subject for email with downloadable files in it', 'woocommerce-subscriptions' ), '{blogname}', '{order_date}' ) );
} else {
return;
}
}
} }

View File

@@ -16,6 +16,11 @@ if ( ! defined( 'ABSPATH' ) ) {
*/ */
class WCS_Email_Completed_Switch_Order extends WC_Email_Customer_Completed_Order { class WCS_Email_Completed_Switch_Order extends WC_Email_Customer_Completed_Order {
/**
* @var array Subscriptions linked to the switch order.
*/
public $subscriptions;
/** /**
* Constructor * Constructor
*/ */
@@ -34,10 +39,6 @@ class WCS_Email_Completed_Switch_Order extends WC_Email_Customer_Completed_Order
$this->template_plain = 'emails/plain/customer-completed-switch-order.php'; $this->template_plain = 'emails/plain/customer-completed-switch-order.php';
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' ); $this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
// Other settings
$this->heading_downloadable = $this->get_option( 'heading_downloadable', __( 'Your subscription change is complete - download your files', 'woocommerce-subscriptions' ) );
$this->subject_downloadable = $this->get_option( 'subject_downloadable', __( 'Your {blogname} subscription change from {order_date} is complete - download your files', 'woocommerce-subscriptions' ) );
// Triggers for this email // Triggers for this email
add_action( 'woocommerce_subscriptions_switch_completed_switch_notification', array( $this, 'trigger' ) ); add_action( 'woocommerce_subscriptions_switch_completed_switch_notification', array( $this, 'trigger' ) );
@@ -171,4 +172,25 @@ class WCS_Email_Completed_Switch_Order extends WC_Email_Customer_Completed_Order
$this->template_base $this->template_base
); );
} }
/**
* Gets the deprecated public variables for backwards compatibility.
*
* @param string $key Key.
*
* @return string|null
*/
public function __get( $key ) {
if ( 'heading_downloadable' === $key ) {
wcs_deprecated_argument( __CLASS__ . '::$' . $key, '5.6.0', 'The heading_downloadable property used for emails with downloadable files was removed in WooCommerce 3.1. Use the heading property instead.' );
return $this->get_option( 'heading_downloadable', __( 'Your subscription change is complete - download your files', 'woocommerce-subscriptions' ) );
} elseif ( 'subject_downloadable' === $key ) {
wcs_deprecated_argument( __CLASS__ . '::$' . $key, '5.6.0', 'The subject_downloadable property used for emails with downloadable files was removed in WooCommerce 3.1. Use the subject property instead.' );
return $this->get_option( 'subject_downloadable', __( 'Your {blogname} subscription change from {order_date} is complete - download your files', 'woocommerce-subscriptions' ) );
} else {
return;
}
}
} }

View File

@@ -172,7 +172,7 @@ class WCS_Email_Expired_Subscription extends WC_Email {
'type' => 'select', 'type' => 'select',
'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ), 'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ),
'default' => 'html', 'default' => 'html',
'class' => 'email_type', 'class' => 'email_type wc-enhanced-select',
'options' => array( 'options' => array(
'plain' => _x( 'Plain text', 'email type', 'woocommerce-subscriptions' ), 'plain' => _x( 'Plain text', 'email type', 'woocommerce-subscriptions' ),
'html' => _x( 'HTML', 'email type', 'woocommerce-subscriptions' ), 'html' => _x( 'HTML', 'email type', 'woocommerce-subscriptions' ),

View File

@@ -13,6 +13,11 @@ if ( ! defined( 'ABSPATH' ) ) {
*/ */
class WCS_Email_New_Switch_Order extends WC_Email_New_Order { class WCS_Email_New_Switch_Order extends WC_Email_New_Order {
/**
* @var array Subscriptions linked to the switch order.
*/
public $subscriptions;
/** /**
* Constructor * Constructor
*/ */

View File

@@ -172,7 +172,7 @@ class WCS_Email_On_Hold_Subscription extends WC_Email {
'type' => 'select', 'type' => 'select',
'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ), 'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ),
'default' => 'html', 'default' => 'html',
'class' => 'email_type', 'class' => 'email_type wc-enhanced-select',
'options' => array( 'options' => array(
'plain' => _x( 'Plain text', 'email type', 'woocommerce-subscriptions' ), 'plain' => _x( 'Plain text', 'email type', 'woocommerce-subscriptions' ),
'html' => _x( 'HTML', 'email type', 'woocommerce-subscriptions' ), 'html' => _x( 'HTML', 'email type', 'woocommerce-subscriptions' ),

View File

@@ -44,6 +44,7 @@ class WCS_Array_Property_Post_Meta_Black_Magic implements ArrayAccess {
* @param string $key * @param string $key
* @return mixed * @return mixed
*/ */
#[\ReturnTypeWillChange]
public function offsetGet( $key ) { public function offsetGet( $key ) {
return get_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ) ); return get_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ) );
} }
@@ -53,6 +54,7 @@ class WCS_Array_Property_Post_Meta_Black_Magic implements ArrayAccess {
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
*/ */
#[\ReturnTypeWillChange]
public function offsetSet( $key, $value ) { public function offsetSet( $key, $value ) {
update_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ), $value ); update_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ), $value );
} }
@@ -62,6 +64,7 @@ class WCS_Array_Property_Post_Meta_Black_Magic implements ArrayAccess {
* @param string $key * @param string $key
* @return bool * @return bool
*/ */
#[\ReturnTypeWillChange]
public function offsetExists( $key ) { public function offsetExists( $key ) {
return metadata_exists( 'post', $this->product_id, $this->maybe_prefix_meta_key( $key ) ); return metadata_exists( 'post', $this->product_id, $this->maybe_prefix_meta_key( $key ) );
} }
@@ -69,6 +72,7 @@ class WCS_Array_Property_Post_Meta_Black_Magic implements ArrayAccess {
/** /**
* Nothing to do here as we access post meta directly. * Nothing to do here as we access post meta directly.
*/ */
#[\ReturnTypeWillChange]
public function offsetUnset( $key ) { public function offsetUnset( $key ) {
} }

View File

@@ -215,6 +215,9 @@ function wcs_create_order_from_subscription( $subscription, $type ) {
// If we got here, the subscription was created without problems // If we got here, the subscription was created without problems
$transaction->commit(); $transaction->commit();
// Delete the transient that caches whether the order needs processing. Because we've added line items, the order may now need processing.
delete_transient( 'wc_order_' . $new_order->get_id() . '_needs_processing' );
/** /**
* Filters the new order created from the subscription. * Filters the new order created from the subscription.
* *

View File

@@ -41,7 +41,7 @@ if ( ! defined( 'ABSPATH' ) ) {
// Subscription Period Interval // Subscription Period Interval
woocommerce_wp_select( array( woocommerce_wp_select( array(
'id' => 'variable_subscription_period_interval[' . $loop . ']', 'id' => 'variable_subscription_period_interval[' . $loop . ']',
'class' => 'wc_input_subscription_period_interval', 'class' => 'wc_input_subscription_period_interval wc-enhanced-select',
'wrapper_class' => '_subscription_period_interval_field', 'wrapper_class' => '_subscription_period_interval_field',
'label' => __( 'Subscription Periods', 'woocommerce-subscriptions' ), 'label' => __( 'Subscription Periods', 'woocommerce-subscriptions' ),
'options' => wcs_get_subscription_period_interval_strings(), 'options' => wcs_get_subscription_period_interval_strings(),
@@ -52,7 +52,7 @@ if ( ! defined( 'ABSPATH' ) ) {
// Billing Period // Billing Period
woocommerce_wp_select( array( woocommerce_wp_select( array(
'id' => 'variable_subscription_period[' . $loop . ']', 'id' => 'variable_subscription_period[' . $loop . ']',
'class' => 'wc_input_subscription_period', 'class' => 'wc_input_subscription_period wc-enhanced-select',
'wrapper_class' => '_subscription_period_field', 'wrapper_class' => '_subscription_period_field',
'label' => __( 'Billing Period', 'woocommerce-subscriptions' ), 'label' => __( 'Billing Period', 'woocommerce-subscriptions' ),
'value' => $subscription_period, 'value' => $subscription_period,
@@ -64,7 +64,7 @@ if ( ! defined( 'ABSPATH' ) ) {
// Subscription Length // Subscription Length
woocommerce_wp_select( array( woocommerce_wp_select( array(
'id' => 'variable_subscription_length[' . $loop . ']', 'id' => 'variable_subscription_length[' . $loop . ']',
'class' => 'wc_input_subscription_length', 'class' => 'wc_input_subscription_length wc-enhanced-select',
'wrapper_class' => '_subscription_length_field', 'wrapper_class' => '_subscription_length_field',
'label' => __( 'Subscription Length', 'woocommerce-subscriptions' ), 'label' => __( 'Subscription Length', 'woocommerce-subscriptions' ),
'options' => wcs_get_subscription_ranges( $subscription_period ), 'options' => wcs_get_subscription_ranges( $subscription_period ),
@@ -110,7 +110,7 @@ if ( ! defined( 'ABSPATH' ) ) {
// Trial Period // Trial Period
woocommerce_wp_select( array( woocommerce_wp_select( array(
'id' => 'variable_subscription_trial_period[' . $loop . ']', 'id' => 'variable_subscription_trial_period[' . $loop . ']',
'class' => 'wc_input_subscription_trial_period', 'class' => 'wc_input_subscription_trial_period wc-enhanced-select',
'wrapper_class' => '_subscription_trial_period_field', 'wrapper_class' => '_subscription_trial_period_field',
'label' => __( 'Subscription Trial Period', 'woocommerce-subscriptions' ), 'label' => __( 'Subscription Trial Period', 'woocommerce-subscriptions' ),
'options' => wcs_get_available_time_periods(), 'options' => wcs_get_available_time_periods(),

View File

@@ -17,7 +17,7 @@ global $wp_locale;
<td colspan="1" class="subscription_sync_week_month"<?php echo esc_attr( $display_week_month_select ); ?>> <td colspan="1" class="subscription_sync_week_month"<?php echo esc_attr( $display_week_month_select ); ?>>
<?php woocommerce_wp_select( array( <?php woocommerce_wp_select( array(
'id' => 'variable' . WC_Subscriptions_Synchroniser::$post_meta_key . '[' . $loop . ']', 'id' => 'variable' . WC_Subscriptions_Synchroniser::$post_meta_key . '[' . $loop . ']',
'class' => 'wc_input_subscription_payment_sync', 'class' => 'wc_input_subscription_payment_sync wc-enhanced-select',
'wrapper_class' => '_subscription_payment_sync_field', 'wrapper_class' => '_subscription_payment_sync_field',
'label' => WC_Subscriptions_Synchroniser::$sync_field_label, 'label' => WC_Subscriptions_Synchroniser::$sync_field_label,
'options' => WC_Subscriptions_Synchroniser::get_billing_period_ranges( $subscription_period ), 'options' => WC_Subscriptions_Synchroniser::get_billing_period_ranges( $subscription_period ),
@@ -30,7 +30,7 @@ global $wp_locale;
<label><?php esc_html_e( 'Synchronise Renewals', 'woocommerce-subscriptions' ); ?></label> <label><?php esc_html_e( 'Synchronise Renewals', 'woocommerce-subscriptions' ); ?></label>
<?php woocommerce_wp_text_input( array( <?php woocommerce_wp_text_input( array(
'id' => 'variable' . WC_Subscriptions_Synchroniser::$post_meta_key_day . '[' . $loop . ']', 'id' => 'variable' . WC_Subscriptions_Synchroniser::$post_meta_key_day . '[' . $loop . ']',
'class' => 'wc_input_subscription_payment_sync', 'class' => 'wc_input_subscription_payment_sync wc-enhanced-select',
'wrapper_class' => '_subscription_payment_sync_field', 'wrapper_class' => '_subscription_payment_sync_field',
'label' => WC_Subscriptions_Synchroniser::$sync_field_label, 'label' => WC_Subscriptions_Synchroniser::$sync_field_label,
'placeholder' => _x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ), 'placeholder' => _x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ),
@@ -45,7 +45,7 @@ global $wp_locale;
woocommerce_wp_select( array( woocommerce_wp_select( array(
'id' => 'variable' . WC_Subscriptions_Synchroniser::$post_meta_key_month . '[' . $loop . ']', 'id' => 'variable' . WC_Subscriptions_Synchroniser::$post_meta_key_month . '[' . $loop . ']',
'class' => 'wc_input_subscription_payment_sync', 'class' => 'wc_input_subscription_payment_sync wc-enhanced-select',
'wrapper_class' => '_subscription_payment_sync_field', 'wrapper_class' => '_subscription_payment_sync_field',
'label' => '', 'label' => '',
'options' => $wp_locale->month, 'options' => $wp_locale->month,

View File

@@ -15,7 +15,7 @@ if ( ! defined( 'ABSPATH' ) ) {
} }
?> ?>
<div class="variable_subscription_trial variable_subscription_pricing_2_3 show_if_variable-subscription variable_subscription_trial_sign_up"> <div class="variable_subscription_trial variable_subscription_pricing_2_3 show_if_variable-subscription variable_subscription_trial_sign_up" style="display: none">
<p class="form-row form-row-first form-field show_if_variable-subscription sign-up-fee-cell"> <p class="form-row form-row-first form-field show_if_variable-subscription sign-up-fee-cell">
<label for="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]"><?php printf( esc_html__( 'Sign-up fee (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) ); ?></label> <label for="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]"><?php printf( esc_html__( 'Sign-up fee (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) ); ?></label>
<input type="text" class="wc_input_price wc_input_subscription_intial_price wc_input_subscription_initial_price" name="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_price( WC_Subscriptions_Product::get_sign_up_fee( $variation_product ) ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>"> <input type="text" class="wc_input_price wc_input_subscription_intial_price wc_input_subscription_initial_price" name="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_price( WC_Subscriptions_Product::get_sign_up_fee( $variation_product ) ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>">
@@ -29,14 +29,14 @@ if ( ! defined( 'ABSPATH' ) ) {
<input type="text" class="wc_input_subscription_trial_length" name="variable_subscription_trial_length[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( WC_Subscriptions_Product::get_trial_length( $variation_product ) ); ?>"> <input type="text" class="wc_input_subscription_trial_length" name="variable_subscription_trial_length[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( WC_Subscriptions_Product::get_trial_length( $variation_product ) ); ?>">
<label for="variable_subscription_trial_period[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Subscription trial period:', 'woocommerce-subscriptions' ); ?></label> <label for="variable_subscription_trial_period[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Subscription trial period:', 'woocommerce-subscriptions' ); ?></label>
<select name="variable_subscription_trial_period[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_trial_period"> <select name="variable_subscription_trial_period[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_trial_period wc-enhanced-select">
<?php foreach ( wcs_get_available_time_periods() as $key => $value ) : ?> <?php foreach ( wcs_get_available_time_periods() as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, WC_Subscriptions_Product::get_trial_period( $variation_product ) ); ?>><?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, WC_Subscriptions_Product::get_trial_period( $variation_product ) ); ?>><?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</p> </p>
</div> </div>
<div class="variable_subscription_pricing variable_subscription_pricing_2_3 show_if_variable-subscription"> <div class="variable_subscription_pricing variable_subscription_pricing_2_3 show_if_variable-subscription" style="display: none">
<p class="form-row form-row-first form-field show_if_variable-subscription _subscription_price_field"> <p class="form-row form-row-first form-field show_if_variable-subscription _subscription_price_field">
<label for="variable_subscription_price[<?php echo esc_attr( $loop ); ?>]"> <label for="variable_subscription_price[<?php echo esc_attr( $loop ); ?>]">
<?php <?php
@@ -47,26 +47,26 @@ if ( ! defined( 'ABSPATH' ) ) {
<input type="text" class="wc_input_price wc_input_subscription_price" name="variable_subscription_price[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_price( WC_Subscriptions_Product::get_regular_price( $variation_product ) ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>"> <input type="text" class="wc_input_price wc_input_subscription_price" name="variable_subscription_price[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_price( WC_Subscriptions_Product::get_regular_price( $variation_product ) ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>">
<label for="variable_subscription_period_interval[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Billing interval:', 'woocommerce-subscriptions' ); ?></label> <label for="variable_subscription_period_interval[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Billing interval:', 'woocommerce-subscriptions' ); ?></label>
<select name="variable_subscription_period_interval[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_period_interval"> <select name="variable_subscription_period_interval[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_period_interval wc-enhanced-select">
<?php foreach ( wcs_get_subscription_period_interval_strings() as $key => $value ) : ?> <?php foreach ( wcs_get_subscription_period_interval_strings() as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, WC_Subscriptions_Product::get_interval( $variation_product ) ); ?>><?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, WC_Subscriptions_Product::get_interval( $variation_product ) ); ?>><?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<label for="variable_subscription_period[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Billing Period:', 'woocommerce-subscriptions' ); ?></label> <label for="variable_subscription_period[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Billing Period:', 'woocommerce-subscriptions' ); ?></label>
<select name="variable_subscription_period[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_period"> <select name="variable_subscription_period[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_period wc-enhanced-select">
<?php foreach ( wcs_get_subscription_period_strings() as $key => $value ) : ?> <?php foreach ( wcs_get_subscription_period_strings() as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $billing_period ); ?>><?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $billing_period ); ?>><?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</p> </p>
<p class="form-row form-row-last show_if_variable-subscription _subscription_length_field"> <p class="form-row form-row-last show_if_variable-subscription _subscription_length_field" style="display: none">
<label for="variable_subscription_length[<?php echo esc_attr( $loop ); ?>]"> <label for="variable_subscription_length[<?php echo esc_attr( $loop ); ?>]">
<?php esc_html_e( 'Expire after', 'woocommerce-subscriptions' ); ?> <?php esc_html_e( 'Expire after', 'woocommerce-subscriptions' ); ?>
<?php echo wcs_help_tip( _x( 'Automatically expire the subscription after this length of time. This length is in addition to any free trial or amount of time provided before a synchronised first renewal date.', 'Subscription Length dropdown\'s description in pricing fields', 'woocommerce-subscriptions' ) ); ?> <?php echo wcs_help_tip( _x( 'Automatically expire the subscription after this length of time. This length is in addition to any free trial or amount of time provided before a synchronised first renewal date.', 'Subscription Length dropdown\'s description in pricing fields', 'woocommerce-subscriptions' ) ); ?>
</label> </label>
<select name="variable_subscription_length[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_length"> <select name="variable_subscription_length[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_length wc-enhanced-select">
<?php foreach ( wcs_get_subscription_ranges( $billing_period ) as $key => $value ) : ?> <?php foreach ( wcs_get_subscription_ranges( $billing_period ) as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, WC_Subscriptions_Product::get_length( $variation_product ) ); ?>> <?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, WC_Subscriptions_Product::get_length( $variation_product ) ); ?>> <?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>

View File

@@ -15,14 +15,14 @@ if ( ! defined( 'ABSPATH' ) ) {
global $wp_locale; global $wp_locale;
?> ?>
<div class="variable_subscription_sync show_if_variable-subscription variable_subscription_pricing_2_3"> <div class="variable_subscription_sync show_if_variable-subscription variable_subscription_pricing_2_3" style="display: none">
<div class="form-row form-row-full"> <div class="form-row form-row-full">
<div class="subscription_sync_week_month" style="<?php echo esc_attr( $display_week_month_select ); ?>"> <div class="subscription_sync_week_month" style="<?php echo esc_attr( $display_week_month_select ); ?>">
<label for="variable_subscription_payment_sync_date[<?php echo esc_attr( $loop ); ?>]"> <label for="variable_subscription_payment_sync_date[<?php echo esc_attr( $loop ); ?>]">
<?php echo esc_html( WC_Subscriptions_Synchroniser::$sync_field_label ); ?> <?php echo esc_html( WC_Subscriptions_Synchroniser::$sync_field_label ); ?>
<?php echo wcs_help_tip( WC_Subscriptions_Synchroniser::$sync_description ); ?> <?php echo wcs_help_tip( WC_Subscriptions_Synchroniser::$sync_description ); ?>
</label> </label>
<select name="variable_subscription_payment_sync_date[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_payment_sync"> <select name="variable_subscription_payment_sync_date[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_payment_sync wc-enhanced-select">
<?php foreach ( WC_Subscriptions_Synchroniser::get_billing_period_ranges( $subscription_period ) as $key => $value ) : ?> <?php foreach ( WC_Subscriptions_Synchroniser::get_billing_period_ranges( $subscription_period ) as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $payment_day ); ?>><?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $payment_day ); ?>><?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
@@ -33,7 +33,7 @@ global $wp_locale;
<?php echo esc_html( WC_Subscriptions_Synchroniser::$sync_field_label ); ?> <?php echo esc_html( WC_Subscriptions_Synchroniser::$sync_field_label ); ?>
<?php echo wcs_help_tip( WC_Subscriptions_Synchroniser::$sync_description_year ); ?> <?php echo wcs_help_tip( WC_Subscriptions_Synchroniser::$sync_description_year ); ?>
</label> </label>
<select name="variable_subscription_payment_sync_date_month[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_payment_sync wc_input_subscription_payment_sync_month"> <select name="variable_subscription_payment_sync_date_month[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_payment_sync wc_input_subscription_payment_sync_month wc-enhanced-select">
<?php foreach ( WC_Subscriptions_Synchroniser::get_year_sync_options() as $key => $value ) : ?> <?php foreach ( WC_Subscriptions_Synchroniser::get_year_sync_options() as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $payment_month ); ?>><?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $payment_month ); ?>><?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>

View File

@@ -174,7 +174,7 @@ function wcs_create_subscription( $args = array() ) {
$subscription->set_customer_note( $args['customer_note'] ?? '' ); $subscription->set_customer_note( $args['customer_note'] ?? '' );
$subscription->set_customer_id( $args['customer_id'] ); $subscription->set_customer_id( $args['customer_id'] );
$subscription->set_date_created( $args['date_created'] ); $subscription->set_date_created( wcs_date_to_time( $args['date_created'] ) );
$subscription->set_created_via( $args['created_via'] ); $subscription->set_created_via( $args['created_via'] );
$subscription->set_currency( $args['currency'] ); $subscription->set_currency( $args['currency'] );
$subscription->set_prices_include_tax( 'no' !== $args['prices_include_tax'] ); $subscription->set_prices_include_tax( 'no' !== $args['prices_include_tax'] );

View File

@@ -6,5 +6,5 @@
* Author: Automattic * Author: Automattic
* Author URI: https://woocommerce.com/ * Author URI: https://woocommerce.com/
* Requires WP: 5.6 * Requires WP: 5.6
* Version: 6.2.0 * Version: 6.4.0
*/ */

View File

@@ -5,10 +5,10 @@
* Description: Sell products and services with recurring payments in your WooCommerce Store. * Description: Sell products and services with recurring payments in your WooCommerce Store.
* Author: WooCommerce * Author: WooCommerce
* Author URI: https://woocommerce.com/ * Author URI: https://woocommerce.com/
* Version: 5.5.0 * Version: 5.6.0
* *
* WC requires at least: 7.6.0 * WC requires at least: 7.7.0
* WC tested up to: 8.1.0 * WC tested up to: 8.2.0
* Woo: 27147:6115e6d7e297b623a169fdcf5728b224 * Woo: 27147:6115e6d7e297b623a169fdcf5728b224
* *
* Copyright 2019 WooCommerce * Copyright 2019 WooCommerce
@@ -77,7 +77,7 @@ class WC_Subscriptions {
public static $plugin_file = __FILE__; public static $plugin_file = __FILE__;
/** @var string */ /** @var string */
public static $version = '5.5.0'; // WRCS: DEFINED_VERSION. public static $version = '5.6.0'; // WRCS: DEFINED_VERSION.
/** @var string */ /** @var string */
public static $wc_minimum_supported_version = '7.7'; public static $wc_minimum_supported_version = '7.7';
@@ -145,7 +145,7 @@ class WC_Subscriptions {
} else { } else {
// Trigger an error consistant with PHP if the function called doesn't exist. // Trigger an error consistant with PHP if the function called doesn't exist.
$class = __CLASS__; $class = __CLASS__;
$trace = debug_backtrace(); $trace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 1 );
$file = $trace[0]['file']; $file = $trace[0]['file'];
$line = $trace[0]['line']; $line = $trace[0]['line'];
trigger_error( "Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR ); //phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped trigger_error( "Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR ); //phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped