diff --git a/changelog.txt b/changelog.txt index 3452d34..f559510 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ *** Woo Subscriptions Changelog *** +2024-01-11 - version 5.9.0 +* Fix: Resolved an issue that caused ordering the Admin Subscriptions List Table to not work when HPOS is enabled. +* Fix: When switching all subscription items in the same cart, ensure the previous subscription is reused where possible instead of creating all new subscriptions. +* Update: Changed the edit subscription product "Expire after" (Subscription length) so it more clearly describes when a subscription will automatically stop renewing. +* Update: Log all exceptions caught by WooCommerce while processing a subscription scheduled action. +* Dev: Update subscriptions-core to 6.7.0. + 2023-12-21 - version 5.8.0 * Fix: Incorrect switch calculation when upgrading a synchronised product before the first renewal is due where the initial payment was prorated. * Fix: When updating a subscription via the REST API, don't update the start date to the current time if the start date is not set in the request. diff --git a/includes/switching/class-wc-subscriptions-switcher.php b/includes/switching/class-wc-subscriptions-switcher.php index be60c74..e412ba7 100644 --- a/includes/switching/class-wc-subscriptions-switcher.php +++ b/includes/switching/class-wc-subscriptions-switcher.php @@ -1025,113 +1025,115 @@ class WC_Subscriptions_Switcher { $subscription->set_shipping_total( $subscription_shipping_total ); $switch_order_data[ $subscription->get_id() ]['shipping_line_items'] = $new_shipping_line_items; + } - // Loop through cart items to add them to the switched subscription. - foreach ( $recurring_cart->get_cart() as $cart_item_key => $cart_item ) { + // Loop through cart items to add them to the switched subscription. + foreach ( $recurring_cart->get_cart() as $cart_item_key => $cart_item ) { + // If we haven't calculated a first payment date, fall back to the recurring cart's next payment date. + if ( 0 == $cart_item['subscription_switch']['first_payment_timestamp'] ) { + $cart_item['subscription_switch']['first_payment_timestamp'] = wcs_date_to_time( $recurring_cart->next_payment_date ); + } - // If we haven't calculated a first payment date, fall back to the recurring cart's next payment date. - if ( 0 == $cart_item['subscription_switch']['first_payment_timestamp'] ) { - $cart_item['subscription_switch']['first_payment_timestamp'] = wcs_date_to_time( $recurring_cart->next_payment_date ); - } + $is_different_billing_schedule = self::has_different_billing_schedule( $cart_item, $subscription ); + $is_different_payment_date = self::has_different_payment_date( $cart_item, $subscription ); + $is_different_length = self::has_different_length( $recurring_cart, $subscription ); + $is_single_item_subscription = self::is_single_item_subscription( $subscription ); - $is_different_billing_schedule = self::has_different_billing_schedule( $cart_item, $subscription ); - $is_different_payment_date = self::has_different_payment_date( $cart_item, $subscription ); - $is_different_length = self::has_different_length( $recurring_cart, $subscription ); - $is_single_item_subscription = self::is_single_item_subscription( $subscription ); + $switched_item_data = array(); - $switched_item_data = array(); + if ( ! empty( $cart_item['subscription_switch']['item_id'] ) ) { + $existing_item = wcs_get_order_item( $cart_item['subscription_switch']['item_id'], $subscription ); + $switch_item = new WCS_Switch_Cart_Item( $cart_item, $subscription, $existing_item ); + $is_switch_with_matching_trials = $switch_item->is_switch_during_trial() && $switch_item->trial_periods_match(); + $switched_item_data['remove_line_item'] = $cart_item['subscription_switch' ]['item_id']; + $switched_item_data['switch_direction'] = $switch_item->get_switch_type(); + } - if ( ! empty( $cart_item['subscription_switch']['item_id'] ) ) { - $existing_item = wcs_get_order_item( $cart_item['subscription_switch']['item_id'], $subscription ); - $switch_item = new WCS_Switch_Cart_Item( $cart_item, $subscription, $existing_item ); - $is_switch_with_matching_trials = $switch_item->is_switch_during_trial() && $switch_item->trial_periods_match(); - $switched_item_data['remove_line_item'] = $cart_item['subscription_switch' ]['item_id']; - $switched_item_data['switch_direction'] = $switch_item->get_switch_type(); - } + // An existing subscription can be updated if it's a single item subscription or switches already calculated have left it with just one item. + $can_update_existing_subscription = $is_single_item_subscription || ! empty( $existing_item ) && self::is_last_remaining_item_after_previous_switches( $subscription, $existing_item, $switch_order_data ); - // If the item is on the same schedule, we can just add it to the new subscription and remove the old item. - if ( $is_single_item_subscription || ( false === $is_different_billing_schedule && false === $is_different_payment_date && false === $is_different_length ) ) { - // Add the new item - $item = new WC_Order_Item_Pending_Switch; - $item->legacy_values = $cart_item; // @deprecated For legacy actions. - $item->legacy_cart_item_key = $cart_item_key; // @deprecated For legacy actions. + // If the item is on the same schedule, we can just add it to the new subscription and remove the old item. + if ( $can_update_existing_subscription || ( false === $is_different_billing_schedule && false === $is_different_payment_date && false === $is_different_length ) ) { + // Add the new item + $item = new WC_Order_Item_Pending_Switch; + $item->legacy_values = $cart_item; // @deprecated For legacy actions. + $item->legacy_cart_item_key = $cart_item_key; // @deprecated For legacy actions. + $item->set_props( array( + 'quantity' => $cart_item['quantity'], + 'variation' => $cart_item['variation'], + 'subtotal' => $cart_item['line_subtotal'], + 'total' => $cart_item['line_total'], + 'subtotal_tax' => $cart_item['line_subtotal_tax'], + 'total_tax' => $cart_item['line_tax'], + 'taxes' => $cart_item['line_tax_data'], + ) ); + + if ( ! empty( $cart_item[ 'data' ] ) ) { + $product = $cart_item[ 'data' ]; $item->set_props( array( - 'quantity' => $cart_item['quantity'], - 'variation' => $cart_item['variation'], - 'subtotal' => $cart_item['line_subtotal'], - 'total' => $cart_item['line_total'], - 'subtotal_tax' => $cart_item['line_subtotal_tax'], - 'total_tax' => $cart_item['line_tax'], - 'taxes' => $cart_item['line_tax_data'], + 'name' => $product->get_name(), + 'tax_class' => $product->get_tax_class(), + 'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(), + 'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0, ) ); - - if ( ! empty( $cart_item[ 'data' ] ) ) { - $product = $cart_item[ 'data' ]; - $item->set_props( array( - 'name' => $product->get_name(), - 'tax_class' => $product->get_tax_class(), - 'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(), - 'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0, - ) ); - } - - if ( WC_Subscriptions_Product::get_trial_length( wcs_get_canonical_product_id( $cart_item ) ) > 0 ) { - $item->add_meta_data( '_has_trial', 'true' ); - } - - do_action( 'woocommerce_checkout_create_order_line_item', $item, $cart_item_key, $cart_item, $subscription ); - - $subscription->add_item( $item ); - - // The subscription is not saved automatically, we need to call 'save' because we added an item - $subscription->save(); - $item_id = $item->get_id(); - - $switched_item_data['add_line_item'] = $item_id; - - // Remove the item from the cart so that WC_Subscriptions_Checkout doesn't add it to a subscription - if ( 1 == count( WC()->cart->recurring_carts[ $recurring_cart_key ]->get_cart() ) ) { - // If this is the only item in the cart, clear out recurring carts so WC_Subscriptions_Checkout doesn't try to create an empty subscription - unset( WC()->cart->recurring_carts[ $recurring_cart_key ] ); - } else { - unset( WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ] ); - } } - $switch_order_data[ $subscription->get_id() ]['switches'][ $cart_item['subscription_switch']['order_line_item_id'] ] = $switched_item_data; + if ( WC_Subscriptions_Product::get_trial_length( wcs_get_canonical_product_id( $cart_item ) ) > 0 ) { + $item->add_meta_data( '_has_trial', 'true' ); + } - // If the old subscription has just one item, we can safely update its billing schedule - if ( $is_single_item_subscription ) { + do_action( 'woocommerce_checkout_create_order_line_item', $item, $cart_item_key, $cart_item, $subscription ); - if ( $is_different_billing_schedule ) { - $switch_order_data[ $subscription->get_id() ]['billing_schedule']['_billing_period'] = WC_Subscriptions_Product::get_period( $cart_item['data'] ); - $switch_order_data[ $subscription->get_id() ]['billing_schedule']['_billing_interval'] = absint( WC_Subscriptions_Product::get_interval( $cart_item['data'] ) ); - } + $subscription->add_item( $item ); - $updated_dates = array(); + // The subscription is not saved automatically, we need to call 'save' because we added an item + $subscription->save(); + $item_id = $item->get_id(); - if ( '1' == WC_Subscriptions_Product::get_length( $cart_item['data'] ) || ( 0 != $recurring_cart->end_date && gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ) >= $recurring_cart->end_date ) ) { - // Delete the next payment date. - $updated_dates['next_payment'] = 0; - } else if ( $is_different_payment_date ) { - $updated_dates['next_payment'] = gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ); - } + $switched_item_data['add_line_item'] = $item_id; - if ( $is_different_length ) { - $updated_dates['end'] = $recurring_cart->end_date; - } + // Remove the item from the cart so that WC_Subscriptions_Checkout doesn't add it to a subscription + if ( 1 == count( WC()->cart->recurring_carts[ $recurring_cart_key ]->get_cart() ) ) { + // If this is the only item in the cart, clear out recurring carts so WC_Subscriptions_Checkout doesn't try to create an empty subscription + unset( WC()->cart->recurring_carts[ $recurring_cart_key ] ); + } else { + unset( WC()->cart->recurring_carts[ $recurring_cart_key ]->cart_contents[ $cart_item_key ] ); + } + } - // If the switch should maintain the current trial or delete it. - if ( isset( $is_switch_with_matching_trials ) && $is_switch_with_matching_trials ) { - $updated_dates['trial_end'] = $subscription->get_date( 'trial_end' ); - } else { - $updated_dates['trial_end'] = 0; - } + $switch_order_data[ $subscription->get_id() ]['switches'][ $cart_item['subscription_switch']['order_line_item_id'] ] = $switched_item_data; - if ( ! empty( $updated_dates ) ) { - $subscription->validate_date_updates( $updated_dates ); - $switch_order_data[ $subscription->get_id() ]['dates']['update'] = $updated_dates; - } + // If the old subscription has just one item, we can safely update its billing schedule + if ( $can_update_existing_subscription ) { + + if ( $is_different_billing_schedule ) { + $switch_order_data[ $subscription->get_id() ]['billing_schedule']['_billing_period'] = WC_Subscriptions_Product::get_period( $cart_item['data'] ); + $switch_order_data[ $subscription->get_id() ]['billing_schedule']['_billing_interval'] = absint( WC_Subscriptions_Product::get_interval( $cart_item['data'] ) ); + } + + $updated_dates = array(); + + if ( '1' == WC_Subscriptions_Product::get_length( $cart_item['data'] ) || ( 0 != $recurring_cart->end_date && gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ) >= $recurring_cart->end_date ) ) { + // Delete the next payment date. + $updated_dates['next_payment'] = 0; + } else if ( $is_different_payment_date ) { + $updated_dates['next_payment'] = gmdate( 'Y-m-d H:i:s', $cart_item['subscription_switch']['first_payment_timestamp'] ); + } + + if ( $is_different_length ) { + $updated_dates['end'] = $recurring_cart->end_date; + } + + // If the switch should maintain the current trial or delete it. + if ( isset( $is_switch_with_matching_trials ) && $is_switch_with_matching_trials ) { + $updated_dates['trial_end'] = $subscription->get_date( 'trial_end' ); + } else { + $updated_dates['trial_end'] = 0; + } + + if ( ! empty( $updated_dates ) ) { + $subscription->validate_date_updates( $updated_dates ); + $switch_order_data[ $subscription->get_id() ]['dates']['update'] = $updated_dates; } } } @@ -2340,6 +2342,41 @@ class WC_Subscriptions_Switcher { } } + /** + * Determines if a subscription item being switched is the last remaining item on the subscription after previous switches. + * + * If the item being switched is the last remaining item on the subscription after previous switches, then the subscription + * can be updated even if the billing schedule is being changed. + * + * @param WC_Subscription $subscription The subscription being switched. + * @param WC_Order_Item_Product $switched_item The subscription line item being switched. + * @param array $switch_data Data about the switches that will occur on the subscription. + * + * @return bool True if the item being switched is the last remaining item on the subscription after previous switches. + */ + private static function is_last_remaining_item_after_previous_switches( $subscription, $switched_item, $switch_data ) { + $remaining_items = $subscription->get_items(); + + // If there is no switch data for this subscription return false. + if ( ! isset( $switch_data[ $subscription->get_id() ]['switches'] ) ) { + return false; + } + + foreach( $switch_data[ $subscription->get_id() ]['switches'] as $switch ) { + // If items are actively being added to this subscription, then it is not the last remaining item. + if ( isset( $switch['add_line_item'] ) ) { + return false; + } + + if ( isset( $switch['remove_line_item'] ) ) { + unset( $remaining_items[ $switch['remove_line_item'] ] ); + } + } + + // If there's only 1 item left and it's the item we're switching, then it's the last remaining item. + return 1 === count( $remaining_items ) && isset( $remaining_items[ $switched_item->get_id() ] ); + } + /** * Adds switch orders or switched subscriptions to the related order meta box. * diff --git a/languages/woocommerce-subscriptions.pot b/languages/woocommerce-subscriptions.pot index 9ea0b45..bed6a1b 100644 --- a/languages/woocommerce-subscriptions.pot +++ b/languages/woocommerce-subscriptions.pot @@ -1,17 +1,17 @@ -# Copyright (C) 2023 WooCommerce +# Copyright (C) 2024 WooCommerce # This file is distributed under the same license as the Woo Subscriptions plugin. msgid "" msgstr "" -"Project-Id-Version: Woo Subscriptions 5.8.0\n" +"Project-Id-Version: Woo Subscriptions 5.9.0\n" "Report-Msgid-Bugs-To: https://woocommerce.com/contact-us\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2023-12-21T00:30:53+00:00\n" +"POT-Creation-Date: 2024-01-11T06:31:17+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.8.1\n" +"X-Generator: WP-CLI 2.9.0\n" "X-Domain: woocommerce-subscriptions\n" #. Plugin Name of the plugin @@ -1090,7 +1090,7 @@ msgid "Set a maximum number of times a customer can suspend their account for ea msgstr "" #: includes/class-wcs-customer-suspension-manager.php:111 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1334 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1364 msgid "Suspend" msgstr "" @@ -1224,7 +1224,7 @@ msgid "Welcome to Woo Subscriptions %s!" msgstr "" #: includes/class-wcs-upgrade-notice-manager.php:112 -#: vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php:163 +#: vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php:221 msgid "Learn more" msgstr "" @@ -1644,7 +1644,7 @@ msgid "Choose a new subscription." msgstr "" #: includes/switching/class-wc-subscriptions-switcher.php:237 -#: includes/switching/class-wc-subscriptions-switcher.php:1238 +#: includes/switching/class-wc-subscriptions-switcher.php:1240 msgid "Your cart contained an invalid subscription switch request. It has been removed." msgid_plural "Your cart contained invalid subscription switch requests. They have been removed." msgstr[0] "" @@ -1755,7 +1755,7 @@ msgstr "" #: includes/switching/class-wc-subscriptions-switcher.php:448 #: includes/switching/class-wc-subscriptions-switcher.php:550 -#: includes/switching/class-wc-subscriptions-switcher.php:2642 +#: includes/switching/class-wc-subscriptions-switcher.php:2679 msgid "Upgrade or Downgrade" msgstr "" @@ -1774,87 +1774,87 @@ msgid "Between Grouped Subscriptions" msgstr "" #. translators: %s: order number. -#: includes/switching/class-wc-subscriptions-switcher.php:1147 +#: includes/switching/class-wc-subscriptions-switcher.php:1149 msgid "Switch order cancelled due to a new switch order being created #%s." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1366 +#: includes/switching/class-wc-subscriptions-switcher.php:1368 msgid "You can only switch to a subscription product." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1372 +#: includes/switching/class-wc-subscriptions-switcher.php:1374 msgid "We can not find your old subscription item." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1394 +#: includes/switching/class-wc-subscriptions-switcher.php:1396 msgid "You can not switch to the same subscription." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1441 +#: includes/switching/class-wc-subscriptions-switcher.php:1443 msgid "You can not switch this subscription. It appears you do not own the subscription." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1482 +#: includes/switching/class-wc-subscriptions-switcher.php:1484 msgid "There was an error locating the switch details." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1863 +#: includes/switching/class-wc-subscriptions-switcher.php:1865 msgctxt "a switch type" msgid "Downgrade" msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1866 +#: includes/switching/class-wc-subscriptions-switcher.php:1868 msgctxt "a switch type" msgid "Upgrade" msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1869 +#: includes/switching/class-wc-subscriptions-switcher.php:1871 msgctxt "a switch type" msgid "Crossgrade" msgstr "" #. translators: %1: product subtotal, %2: HTML span tag, %3: direction (upgrade, downgrade, crossgrade), %4: closing HTML span tag -#: includes/switching/class-wc-subscriptions-switcher.php:1874 +#: includes/switching/class-wc-subscriptions-switcher.php:1876 msgctxt "product subtotal string" msgid "%1$s %2$s(%3$s)%4$s" msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1990 +#: includes/switching/class-wc-subscriptions-switcher.php:1992 msgid "The original subscription item being switched cannot be found." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:1992 +#: includes/switching/class-wc-subscriptions-switcher.php:1994 msgid "The item on the switch order cannot be found." msgstr "" #. translators: 1$: old item, 2$: new item when switching -#: includes/switching/class-wc-subscriptions-switcher.php:2003 +#: includes/switching/class-wc-subscriptions-switcher.php:2005 msgctxt "used in order notes" msgid "Customer switched from: %1$s to %2$s." msgstr "" #. translators: %s: new item name. -#: includes/switching/class-wc-subscriptions-switcher.php:2006 +#: includes/switching/class-wc-subscriptions-switcher.php:2008 msgctxt "used in order notes" msgid "Customer added %s." msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:2366 -#: includes/switching/class-wc-subscriptions-switcher.php:2919 +#: includes/switching/class-wc-subscriptions-switcher.php:2403 +#: includes/switching/class-wc-subscriptions-switcher.php:2956 msgid "Switch Order" msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:2381 -#: includes/switching/class-wc-subscriptions-switcher.php:2934 +#: includes/switching/class-wc-subscriptions-switcher.php:2418 +#: includes/switching/class-wc-subscriptions-switcher.php:2971 msgid "Switched Subscription" msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:2599 +#: includes/switching/class-wc-subscriptions-switcher.php:2636 msgctxt "add to cart button text while switching a subscription" msgid "Switch subscription" msgstr "" -#: includes/switching/class-wc-subscriptions-switcher.php:2783 +#: includes/switching/class-wc-subscriptions-switcher.php:2820 #: vendor/woocommerce/subscriptions-core/wcs-functions.php:224 msgctxt "Subscription status" msgid "Switched" @@ -1941,11 +1941,11 @@ msgstr "" #: vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php:341 #: vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php:482 #: vendor/woocommerce/subscriptions-core/templates/admin/html-variation-price.php:66 -msgid "Expire after" +msgid "Stop renewing after" msgstr "" #: vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php:344 -msgid "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." +msgid "Automatically stop renewing 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." msgstr "" #. translators: %s is a currency symbol / code @@ -2332,41 +2332,41 @@ msgstr "" msgid "Lock manual price increases" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:235 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:234 msgid "Search for a product…" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:298 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:329 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:297 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:328 msgctxt "an action on a subscription" msgid "Move to Trash" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:307 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:306 msgctxt "an action on a subscription" msgid "Restore" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:308 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:307 msgctxt "an action on a subscription" msgid "Delete Permanently" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:326 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1722 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:325 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1752 msgctxt "an action on a subscription" msgid "Activate" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:327 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1723 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:326 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1753 msgctxt "an action on a subscription" msgid "Put on-hold" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:328 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1335 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1724 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:327 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1365 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1754 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1937 #: vendor/woocommerce/subscriptions-core/includes/wcs-user-functions.php:327 #: vendor/woocommerce/subscriptions-core/templates/myaccount/related-orders.php:78 @@ -2375,20 +2375,20 @@ msgid "Cancel" msgstr "" #. translators: placeholder is the number of subscriptions updated -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:416 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:415 msgid "%s subscription status changed." msgid_plural "%s subscription statuses changed." msgstr[0] "" msgstr[1] "" #. translators: 1$: is the number of subscriptions not updated, 2$: is the error message -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:435 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:434 msgid "%1$s subscription could not be updated: %2$s" msgid_plural "%1$s subscriptions could not be updated: %2$s" msgstr[0] "" msgstr[1] "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:463 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:462 #: vendor/woocommerce/subscriptions-core/includes/admin/meta-boxes/views/html-related-orders-table.php:21 #: vendor/woocommerce/subscriptions-core/templates/myaccount/my-subscriptions.php:22 #: vendor/woocommerce/subscriptions-core/templates/myaccount/my-subscriptions.php:37 @@ -2400,7 +2400,7 @@ msgstr[1] "" msgid "Status" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:464 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:463 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php:337 #: vendor/woocommerce/subscriptions-core/templates/emails/cancelled-subscription.php:21 #: vendor/woocommerce/subscriptions-core/templates/emails/expired-subscription.php:21 @@ -2410,79 +2410,79 @@ msgstr "" msgid "Subscription" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:465 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:464 msgid "Items" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:466 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:465 #: vendor/woocommerce/subscriptions-core/build/index.js:11 #: vendor/woocommerce/subscriptions-core/build/index.js:17 msgid "Total" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:467 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:466 msgid "Start Date" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:468 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:467 msgid "Trial End" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:469 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:468 msgid "Next Payment" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:470 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:469 msgid "Last Order Date" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:471 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:470 msgid "End Date" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:472 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:471 msgctxt "number of orders linked to a subscription" msgid "Orders" msgstr "" #. translators: placeholder is a subscription ID. -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:507 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:506 msgctxt "hash before subscription number" msgid "#%s" msgstr "" #. translators: Placeholder is a
HTML tag. -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:519 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:518 msgid "This subscription couldn't be loaded from the database. %s Click to learn more." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:557 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:556 msgctxt "meaning billing address" msgid "Billing:" msgstr "" #. translators: placeholder is customer's billing email -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:562 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:561 msgid "Email: %s" msgstr "" #. translators: placeholder is customer's billing phone number -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:567 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:566 msgid "Tel: %s" msgstr "" #. translators: $1: is opening link, $2: is subscription order number, $3: is closing link tag, $4: is user's name -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:597 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:596 msgctxt "Subscription title on admin table. (e.g.: #211 for John Doe)" msgid "%1$s#%2$s%3$s for %4$s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:606 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:605 msgid "Show more details" msgstr "" #. translators: %d: item count. -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:627 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:626 msgid "%d item" msgid_plural "%d items" msgstr[0] "" @@ -2490,131 +2490,131 @@ msgstr[1] "" #. translators: placeholder is the display name of a payment gateway a subscription was paid by #. translators: %s: payment method. -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:646 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:645 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2126 msgid "Via %s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:689 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:688 msgid "Y/m/d g:i:s A" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:706 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:705 msgid "Subscription payment overdue.
" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:711 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:710 msgid "This date should be treated as an estimate only. The payment gateway for this subscription controls when payments are processed.
" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1069 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1072 -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1075 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1099 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1102 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1105 msgid "Subscription updated." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1070 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1100 msgid "Custom field updated." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1071 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1101 msgid "Custom field deleted." msgstr "" #. translators: placeholder is previous post title -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1074 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1104 msgctxt "used in post updated messages" msgid "Subscription restored to revision from %s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1076 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1106 msgid "Subscription saved." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1077 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1107 msgid "Subscription submitted." msgstr "" #. translators: php date string -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1079 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1109 msgid "Subscription scheduled for: %1$s." msgstr "" #. translators: php date string -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1079 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1109 msgctxt "used in \"Subscription scheduled for \"" msgid "M j, Y @ G:i" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1080 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1110 msgid "Subscription draft updated." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1122 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1152 msgid "Any Payment Method" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1123 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1153 msgid "None" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1129 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1159 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2108 #: vendor/woocommerce/subscriptions-core/includes/class-wcs-change-payment-method-admin.php:170 msgid "Manual Renewal" msgstr "" #. translators: 1: user display name 2: user ID 3: user email -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1288 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1318 msgid "%1$s (#%2$s – %3$s)" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1295 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1325 #: vendor/woocommerce/subscriptions-core/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:96 msgid "Search for a customer…" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1333 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1363 #: vendor/woocommerce/subscriptions-core/includes/wcs-user-functions.php:311 msgid "Reactivate" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1336 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1366 msgid "Trash" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1337 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1367 msgid "Delete Permanently" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1356 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1386 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-product.php:768 msgid "Restore this item from the Trash" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1358 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1388 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-product.php:769 msgid "Restore" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1363 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1393 msgid "Move this item to the Trash" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1377 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1407 msgid "Delete this item permanently" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1388 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1418 msgid "Cancel Now" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1454 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1484 msgctxt "Used in order note. Reason why status changed." msgid "Subscription status changed by bulk edit:" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1572 +#: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1602 msgid "All" msgstr "" @@ -3665,9 +3665,9 @@ msgctxt "used in order note as reason for why subscription status changed" msgid "Subscription renewal payment due:" msgstr "" -#. translators: placeholder is an order note. +#. translators: placeholder %1 is an order note. %2 is the error message that was thrown. #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:149 -msgid "Error: Unable to create renewal order with note \"%s\"" +msgid "Error: Unable to create renewal order with note \"%1$s\". Message: %2$s" msgstr "" #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:159 @@ -4189,7 +4189,7 @@ msgstr "" msgid "Admin turned on automatic renewals by changing payment method to \"%s\" via the Edit Subscription screen." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php:158 +#: vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php:216 msgid "Ignore this error" msgstr "" @@ -4489,7 +4489,9 @@ msgid "Subscription – %s" msgstr "" #. translators: %s: Order date +#. translators: placeholders are strftime() strings. #: vendor/woocommerce/subscriptions-core/includes/data-stores/class-wcs-subscription-data-store-cpt.php:265 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:261 msgctxt "Order date parsed by DateTime::format" msgid "M d, Y @ h:i A" msgstr "" @@ -5989,36 +5991,35 @@ msgctxt "Refers to the type of the copy being performed: \"copy_order\", \"subsc msgid "Invalid data. Type of copy is not a string." msgstr "" -#. translators: placeholders are strftime() strings. -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:253 -msgctxt "Used in subscription post title. \"Subscription renewal order - \"" -msgid "%b %d, %Y @ %I:%M %p" +#. translators: placeholder %1 is the order type. %2 is the subscription ID we attempted to create the order for. +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:229 +msgid "There was an error fetching the new order (%1$s) for subscription %2$d." msgstr "" #. translators: placeholder is a date. -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:258 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:266 msgid "Subscription Renewal Order – %s" msgstr "" #. translators: placeholder is a date. -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:262 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:270 msgid "Resubscribe Order – %s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:281 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:289 msgid "$type passed to the function was not a string." msgstr "" #. translators: placeholder is an order type. -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:286 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:294 msgid "\"%s\" is not a valid new order type." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:537 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:545 msgid "Invalid data. No valid subscription / order was passed in." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:541 +#: vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php:549 msgid "Invalid data. No valid item id was passed in." msgstr "" @@ -6056,7 +6057,7 @@ msgstr[1] "" #: vendor/woocommerce/subscriptions-core/includes/wcs-time-functions.php:88 msgctxt "Subscription length" -msgid "Never expire" +msgid "Do not stop until cancelled" msgstr "" #: vendor/woocommerce/subscriptions-core/includes/wcs-time-functions.php:93 @@ -6233,7 +6234,7 @@ msgstr "" #: vendor/woocommerce/subscriptions-core/templates/admin/html-variation-price.php:67 msgctxt "Subscription Length dropdown's description in pricing fields" -msgid "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." +msgid "Automatically stop renewing 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." msgstr "" #: vendor/woocommerce/subscriptions-core/templates/cart/cart-recurring-shipping.php:32 diff --git a/vendor/autoload.php b/vendor/autoload.php index 5ea7985..d7f5a78 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInita652888dbfcef5cfe9fff35250423322::getLoader(); +return ComposerAutoloaderInit9ece70b6f360bc43bc3bc8c8cedd9233::getLoader(); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index aad5c34..f2ee97d 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInita652888dbfcef5cfe9fff35250423322 +class ComposerAutoloaderInit9ece70b6f360bc43bc3bc8c8cedd9233 { private static $loader; @@ -24,12 +24,12 @@ class ComposerAutoloaderInita652888dbfcef5cfe9fff35250423322 require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInita652888dbfcef5cfe9fff35250423322', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit9ece70b6f360bc43bc3bc8c8cedd9233', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInita652888dbfcef5cfe9fff35250423322', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit9ece70b6f360bc43bc3bc8c8cedd9233', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInita652888dbfcef5cfe9fff35250423322::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit9ece70b6f360bc43bc3bc8c8cedd9233::getInitializer($loader)); $loader->register(true); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 2658c19..315a720 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInita652888dbfcef5cfe9fff35250423322 +class ComposerStaticInit9ece70b6f360bc43bc3bc8c8cedd9233 { public static $prefixLengthsPsr4 = array ( 'C' => @@ -129,9 +129,9 @@ class ComposerStaticInita652888dbfcef5cfe9fff35250423322 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInita652888dbfcef5cfe9fff35250423322::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInita652888dbfcef5cfe9fff35250423322::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInita652888dbfcef5cfe9fff35250423322::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit9ece70b6f360bc43bc3bc8c8cedd9233::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit9ece70b6f360bc43bc3bc8c8cedd9233::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit9ece70b6f360bc43bc3bc8c8cedd9233::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index b9107bb..8dbd05c 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -156,17 +156,17 @@ }, { "name": "woocommerce/subscriptions-core", - "version": "6.6.0", - "version_normalized": "6.6.0.0", + "version": "6.7.0", + "version_normalized": "6.7.0.0", "source": { "type": "git", "url": "https://github.com/Automattic/woocommerce-subscriptions-core.git", - "reference": "5abcf9aac4e53ad9bdcf3752a34a04ae42261bac" + "reference": "2531db11c67a060cbb06c6246c162688a2c9eccb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/5abcf9aac4e53ad9bdcf3752a34a04ae42261bac", - "reference": "5abcf9aac4e53ad9bdcf3752a34a04ae42261bac", + "url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/2531db11c67a060cbb06c6246c162688a2c9eccb", + "reference": "2531db11c67a060cbb06c6246c162688a2c9eccb", "shasum": "" }, "require": { @@ -179,7 +179,7 @@ "woocommerce/woocommerce-sniffs": "0.1.0", "yoast/phpunit-polyfills": "1.1.0" }, - "time": "2023-12-20T07:19:09+00:00", + "time": "2024-01-11T04:55:48+00:00", "type": "wordpress-plugin", "extra": { "phpcodesniffer-search-depth": 2 @@ -209,7 +209,7 @@ "description": "Sell products and services with recurring payments in your WooCommerce Store.", "homepage": "https://github.com/Automattic/woocommerce-subscriptions-core", "support": { - "source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/6.6.0", + "source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/6.7.0", "issues": "https://github.com/Automattic/woocommerce-subscriptions-core/issues" }, "install-path": "../woocommerce/subscriptions-core" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index ba3b91c..eff33b2 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => 'woocommerce/woocommerce-subscriptions', - 'pretty_version' => 'dev-release/5.8.0', - 'version' => 'dev-release/5.8.0', - 'reference' => '49bca69e2b17a445dc250b21c753175bdd9ed2c6', + 'pretty_version' => 'dev-release/5.9.0', + 'version' => 'dev-release/5.9.0', + 'reference' => '559969625c750b519df548291b5ba9280cdf8690', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -32,18 +32,18 @@ ), ), 'woocommerce/subscriptions-core' => array( - 'pretty_version' => '6.6.0', - 'version' => '6.6.0.0', - 'reference' => '5abcf9aac4e53ad9bdcf3752a34a04ae42261bac', + 'pretty_version' => '6.7.0', + 'version' => '6.7.0.0', + 'reference' => '2531db11c67a060cbb06c6246c162688a2c9eccb', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../woocommerce/subscriptions-core', 'aliases' => array(), 'dev_requirement' => false, ), 'woocommerce/woocommerce-subscriptions' => array( - 'pretty_version' => 'dev-release/5.8.0', - 'version' => 'dev-release/5.8.0', - 'reference' => '49bca69e2b17a445dc250b21c753175bdd9ed2c6', + 'pretty_version' => 'dev-release/5.9.0', + 'version' => 'dev-release/5.9.0', + 'reference' => '559969625c750b519df548291b5ba9280cdf8690', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/vendor/woocommerce/subscriptions-core/changelog.txt b/vendor/woocommerce/subscriptions-core/changelog.txt index 6817b6e..dbb431f 100644 --- a/vendor/woocommerce/subscriptions-core/changelog.txt +++ b/vendor/woocommerce/subscriptions-core/changelog.txt @@ -1,5 +1,10 @@ *** WooCommerce Subscriptions Core Changelog *** += 6.7.0 - 2024-01-11 = +* Update - Changed the edit subscription product "Expire after" (Subscription length) so it more clearly describes when a subscription will automatically stop renewing. +* Update - Log all exceptions caught by WooCommerce while processing a subscription scheduled action. +* Fix - Resolved an issue that caused ordering the Admin Subscriptions List Table to not work when HPOS is enabled. + = 6.6.0 - 2023-12-20 = * Fix - When using the checkout block to pay for renewal orders, ensure the order's cart hash is updated to make sure the existing order can be used. * Fix - Prevents a PHP fatal error that occurs when the cart contains a renewal order item that no longer exists. diff --git a/vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php b/vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php index a0a8579..878a6c6 100644 --- a/vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php +++ b/vendor/woocommerce/subscriptions-core/includes/admin/class-wc-subscriptions-admin.php @@ -338,10 +338,10 @@ class WC_Subscriptions_Admin { array( 'id' => '_subscription_length', 'class' => 'wc_input_subscription_length select short wc-enhanced-select', - 'label' => __( 'Expire after', 'woocommerce-subscriptions' ), + 'label' => __( 'Stop renewing after', 'woocommerce-subscriptions' ), 'options' => wcs_get_subscription_ranges( $chosen_period ), 'desc_tip' => true, - 'description' => __( '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.', 'woocommerce-subscriptions' ), + 'description' => __( 'Automatically stop renewing 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.', 'woocommerce-subscriptions' ), ) ); @@ -479,7 +479,7 @@ class WC_Subscriptions_Admin { - + diff --git a/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php b/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php index 7ee883b..463adf6 100644 --- a/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php +++ b/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php @@ -204,7 +204,6 @@ class WCS_Admin_Post_Types { return $pieces; } - /** * Displays the dropdown for the product filter * @@ -880,6 +879,7 @@ class WCS_Admin_Post_Types { $request_query = $this->set_filter_by_customer_query( $request_query ); $request_query = $this->set_filter_by_product_query( $request_query ); $request_query = $this->set_filter_by_payment_method_query( $request_query ); + $request_query = $this->set_order_by_query_args( $request_query ); return $request_query; } @@ -1021,6 +1021,36 @@ class WCS_Admin_Post_Types { return $request_query; } + /** + * Sets the order by query args for the subscriptions list table request on HPOS enabled sites. + * + * This function is similar to the posts table equivalent function (self::request_query()) except it only sets the order by. + * + * @param array $request_query The query args sent to wc_get_orders() to populate the list table. + * @return array $request_query + */ + private function set_order_by_query_args( $request_query ) { + + if ( ! isset( $request_query['orderby'] ) ) { + return $request_query; + } + + switch ( $request_query['orderby'] ) { + case 'last_payment_date': + add_filter( 'woocommerce_orders_table_query_clauses', [ $this, 'orders_table_query_clauses' ], 10, 3 ); + break; + case 'start_date': + case 'trial_end_date': + case 'next_payment_date': + case 'end_date': + $request_query['meta_key'] = sprintf( '_schedule_%s', str_replace( '_date', '', $request_query['orderby'] ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + $request_query['orderby'] = 'meta_value'; + break; + } + + return $request_query; + } + /** * Set the 'post__in' query var with a given set of post ids. * @@ -1752,4 +1782,109 @@ class WCS_Admin_Post_Types { is_db_user_privileged() ) { + $pieces = self::orders_table_clauses_high_performance( $pieces ); + } else { + $pieces = self::orders_table_clauses_low_performance( $pieces ); + } + + $query_order = strtoupper( $args['order'] ); + + // fields and order are identical in both cases + $pieces['fields'] .= ', COALESCE(lp.last_payment, orders.date_created_gmt, 0) as lp'; + $pieces['orderby'] = "CAST(lp AS DATETIME) {$query_order}"; + + return $pieces; + } + + /** + * Adds order table query clauses to sort the subscriptions list table by last payment date. + * + * This function provides a lower performance method using a subquery to sort by last payment date. + * It is a HPOS version of @see self::posts_clauses_low_performance(). + * + * @param string[] $pieces Associative array of the clauses for the query. + * @return string[] $pieces Updated associative array of clauses for the query. + */ + private function orders_table_clauses_low_performance( $pieces ) { + $order_datastore = wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore::class ); + $order_table = $order_datastore::get_orders_table_name(); + $meta_table = $order_datastore::get_meta_table_name(); + + $pieces['join'] .= "LEFT JOIN + (SELECT + MAX( orders.date_created_gmt ) as last_payment, + order_meta.meta_value + FROM {$meta_table} as order_meta + LEFT JOIN {$order_table} orders ON orders.id = order_meta.order_id + WHERE order_meta.meta_key = '_subscription_renewal' + GROUP BY order_meta.meta_value) lp + ON {$order_table}.id = lp.meta_value + LEFT JOIN {$order_table} orders on {$order_table}.parent_order_id = orders.ID"; + + return $pieces; + } + + /** + * Adds order table query clauses to sort the subscriptions list table by last payment date. + * + * This function provides a higher performance method using a temporary table to sort by last payment date. + * It is a HPOS version of @see self::posts_clauses_high_performance(). + * + * @param string[] $pieces Associative array of the clauses for the query. + * @return string[] $pieces Updated associative array of clauses for the query. + */ + private function orders_table_clauses_high_performance( $pieces ) { + global $wpdb; + + $order_datastore = wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore::class ); + $order_table = $order_datastore::get_orders_table_name(); + $meta_table = $order_datastore::get_meta_table_name(); + $session = wp_get_session_token(); + + $table_name = substr( "{$wpdb->prefix}tmp_{$session}_lastpayment", 0, 64 ); + + // Create a temporary table, drop the previous one. + $wpdb->query( $wpdb->prepare( 'DROP TEMPORARY TABLE IF EXISTS %s', $table_name ) ); + + $wpdb->query( + $wpdb->prepare( + "CREATE TEMPORARY TABLE %s (id INT PRIMARY KEY, last_payment DATETIME) AS + SELECT order_meta.meta_value as id, MAX( orders.date_created_gmt ) as last_payment FROM %s order_meta + LEFT JOIN %s as orders ON orders.id = order_meta.order_id + WHERE order_meta.meta_key = '_subscription_renewal' + GROUP BY order_meta.meta_value", + $table_name, + $meta_table, + $order_table + ) + ); + + $pieces['join'] .= "LEFT JOIN {$table_name} lp + ON {$order_table}.id = lp.id + LEFT JOIN {$order_table} orders on {$order_table}.parent_order_id = orders.id"; + + return $pieces; + } } diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php index b81b159..1c8623b 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php @@ -16,7 +16,7 @@ class WC_Subscriptions_Core_Plugin { * The version of subscriptions-core library. * @var string */ - protected $library_version = '6.6.0'; // WRCS: DEFINED_VERSION. + protected $library_version = '6.7.0'; // WRCS: DEFINED_VERSION. /** * The subscription scheduler instance. diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php index d24a010..85a3f3d 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php @@ -145,8 +145,8 @@ class WC_Subscriptions_Manager { $renewal_order = wcs_create_renewal_order( $subscription ); if ( is_wp_error( $renewal_order ) ) { - // translators: placeholder is an order note. - throw new Exception( sprintf( __( 'Error: Unable to create renewal order with note "%s"', 'woocommerce-subscriptions' ), $order_note ) ); + // translators: placeholder %1 is an order note. %2 is the error message that was thrown. + throw new Exception( sprintf( __( 'Error: Unable to create renewal order with note "%1$s". Message: %2$s', 'woocommerce-subscriptions' ), $order_note, $renewal_order->get_error_message() ) ); } } diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php b/vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php index 6945e26..56d78c2 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wcs-failed-scheduled-action-manager.php @@ -34,6 +34,13 @@ class WCS_Failed_Scheduled_Action_Manager { */ protected $logger; + /** + * Exceptions caught by WC while this class is listening to the `woocommerce_caught_exception` action. + * + * @var Exception[] + */ + protected $exceptions = []; + /** * Constructor. * @@ -55,6 +62,7 @@ class WCS_Failed_Scheduled_Action_Manager { add_action( 'action_scheduler_failed_execution', array( $this, 'log_action_scheduler_failure' ), 10, 2 ); add_action( 'action_scheduler_unexpected_shutdown', array( $this, 'log_action_scheduler_failure' ), 10, 2 ); add_action( 'admin_notices', array( $this, 'maybe_show_admin_notice' ) ); + add_action( 'action_scheduler_begin_execute', array( $this, 'maybe_attach_exception_listener' ) ); } /** @@ -106,6 +114,56 @@ class WCS_Failed_Scheduled_Action_Manager { ); update_option( WC_Subscriptions_Admin::$option_prefix . '_failed_scheduled_actions', $failed_scheduled_actions ); + + // If there is an exception listener and it's caught exceptions, log them for additional debugging. + if ( ! empty( $this->exceptions ) ) { + foreach ( $this->exceptions as $exception ) { + $message = 'Exception: ' . $exception->getMessage() . ' in ' . $exception->getFile() . ':' . $exception->getLine(); + $this->log( $message . PHP_EOL . $exception->getTraceAsString() ); + ActionScheduler_Logger::instance()->log( $action_id, $message ); + } + + // Now that we've logged the exceptions, we can detach the exception listener. + $this->clear_exceptions_and_detach_listener(); + } + } + + /** + * Creates a new exception listener when processing subscription-related scheduled actions. + * + * @param int $action_id The ID of the scheduled action being ran. + */ + public function maybe_attach_exception_listener( $action_id ) { + $action = $this->get_action( $action_id ); + + if ( ! $action || ! isset( $this->tracked_scheduled_actions[ $action->get_hook() ] ) ) { + return; + } + + // Add an action to detach the exception listener and clear the caught exceptions after the scheduled action has been executed. + add_action( 'action_scheduler_after_execute', [ $this, 'clear_exceptions_and_detach_listener' ] ); + + // Attach the exception listener. + add_action( 'woocommerce_caught_exception', [ $this, 'handle_exception' ] ); + } + + /** + * Adds an exception to the list of exceptions caught by WC. + * + * @param Exception $exception The exception that was caught. + */ + public function handle_exception( $exception ) { + $this->exceptions[] = $exception; + } + + /** + * Clears the list of exceptions caught by WC and detaches the listener. + * + * This function is called directly and attached to an action that runs after a scheduled action has finished being executed. + */ + public function clear_exceptions_and_detach_listener() { + $this->exceptions = []; + remove_action( 'woocommerce_caught_exception', [ $this, 'handle_exception' ] ); } /** diff --git a/vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php b/vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php index 15f5d28..0cdd197 100644 --- a/vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php +++ b/vendor/woocommerce/subscriptions-core/includes/wcs-order-functions.php @@ -218,17 +218,25 @@ function wcs_create_order_from_subscription( $subscription, $type ) { // 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' ); + /* + * Fetch a fresh instance of the order because the current order instance has an empty line item cache generated before we had copied the line items. + * Fetching a new instance will ensure the line items are available via $new_order->get_items(). + */ + $order = wc_get_order( $new_order->get_id() ); + + if ( ! $order ) { + // translators: placeholder %1 is the order type. %2 is the subscription ID we attempted to create the order for. + throw new Exception( sprintf( __( 'There was an error fetching the new order (%1$s) for subscription %2$d.', 'woocommerce-subscriptions' ), $type, $subscription->get_id() ) ); + } + /** * Filters the new order created from the subscription. * - * Fetches a fresh instance of the order because the current order instance has an empty line item cache generated before we had copied the line items. - * Fetching a new instance will ensure the line items are available via $new_order->get_items(). - * * @param WC_Order $new_order The new order created from the subscription. * @param WC_Subscription $subscription The subscription the order was created from. * @param string $type The type of order being created. Either 'renewal_order' or 'resubscribe_order'. */ - return apply_filters( 'wcs_new_order_created', wc_get_order( $new_order->get_id() ), $subscription, $type ); + return apply_filters( 'wcs_new_order_created', $order, $subscription, $type ); } catch ( Exception $e ) { // There was an error adding the subscription @@ -250,7 +258,7 @@ function wcs_get_new_order_title( $type ) { $type = wcs_validate_new_order_type( $type ); // translators: placeholders are strftime() strings. - $order_date = strftime( _x( '%b %d, %Y @ %I:%M %p', 'Used in subscription post title. "Subscription renewal order - "', 'woocommerce-subscriptions' ) ); // phpcs:ignore WordPress.WP.I18n.UnorderedPlaceholdersText + $order_date = ( new DateTime( 'now' ) )->format( _x( 'M d, Y @ h:i A', 'Order date parsed by DateTime::format', 'woocommerce-subscriptions' ) ); switch ( $type ) { case 'renewal_order': diff --git a/vendor/woocommerce/subscriptions-core/includes/wcs-time-functions.php b/vendor/woocommerce/subscriptions-core/includes/wcs-time-functions.php index f69febe..cd918e4 100644 --- a/vendor/woocommerce/subscriptions-core/includes/wcs-time-functions.php +++ b/vendor/woocommerce/subscriptions-core/includes/wcs-time-functions.php @@ -85,7 +85,7 @@ function wcs_get_non_cached_subscription_ranges() { foreach ( array( 'day', 'week', 'month', 'year' ) as $period ) { $subscription_lengths = array( - _x( 'Never expire', 'Subscription length', 'woocommerce-subscriptions' ), + _x( 'Do not stop until cancelled', 'Subscription length', 'woocommerce-subscriptions' ), ); switch ( $period ) { diff --git a/vendor/woocommerce/subscriptions-core/templates/admin/html-variation-price.php b/vendor/woocommerce/subscriptions-core/templates/admin/html-variation-price.php index 290cbe5..5f51921 100644 --- a/vendor/woocommerce/subscriptions-core/templates/admin/html-variation-price.php +++ b/vendor/woocommerce/subscriptions-core/templates/admin/html-variation-price.php @@ -63,8 +63,8 @@ if ( ! defined( 'ABSPATH' ) ) {