PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.3
WooCommerce v10.9.3
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Blocks / Domain / Services / DraftOrders.php
woocommerce / src / Blocks / Domain / Services Last commit date
CheckoutFieldsSchema 11 months ago Email 1 year ago CheckoutFields.php 11 months ago CheckoutFieldsAdmin.php 2 years ago CheckoutFieldsFrontend.php 11 months ago CheckoutLink.php 11 months ago CreateAccount.php 1 year ago DraftOrders.php 2 months ago FeatureGating.php 1 year ago GoogleAnalytics.php 2 years ago Hydration.php 5 months ago Notices.php 1 year ago functions.php 2 years ago
DraftOrders.php
267 lines
1 <?php
2 namespace Automattic\WooCommerce\Blocks\Domain\Services;
3
4 use Automattic\WooCommerce\Blocks\Domain\Package;
5 use Exception;
6 use WC_Order;
7
8 /**
9 * Service class for adding DraftOrder functionality to WooCommerce core.
10 *
11 * Sets up all logic related to the Checkout Draft Orders service
12 *
13 * @internal
14 */
15 class DraftOrders {
16
17 const DB_STATUS = 'wc-checkout-draft';
18 const STATUS = 'checkout-draft';
19
20 const DRAFT_CLEANUP_EVENT_HOOK = 'woocommerce_cleanup_draft_orders';
21
22 /**
23 * Holds the Package instance
24 *
25 * @var Package
26 */
27 private $package;
28
29 /**
30 * Constructor
31 *
32 * @param Package $package An instance of the package class.
33 */
34 public function __construct( Package $package ) {
35 $this->package = $package;
36 }
37
38 /**
39 * Set all hooks related to adding Checkout Draft order functionality to Woo Core.
40 */
41 public function init() {
42 add_filter( 'wc_order_statuses', [ $this, 'register_draft_order_status' ] );
43 add_filter( 'woocommerce_register_shop_order_post_statuses', [ $this, 'register_draft_order_post_status' ] );
44 add_filter( 'woocommerce_analytics_excluded_order_statuses', [ $this, 'append_draft_order_post_status' ] );
45 add_filter( 'woocommerce_valid_order_statuses_for_payment', [ $this, 'append_draft_order_post_status' ], 999 );
46 add_filter( 'woocommerce_valid_order_statuses_for_payment_complete', [ $this, 'append_draft_order_post_status' ], 999 );
47 // Hook into the query to retrieve My Account orders so draft status is excluded.
48 add_action( 'woocommerce_my_account_my_orders_query', [ $this, 'delete_draft_order_post_status_from_args' ] );
49 add_action( self::DRAFT_CLEANUP_EVENT_HOOK, [ $this, 'delete_expired_draft_orders' ] );
50 add_action( 'admin_init', [ $this, 'install' ] );
51
52 if ( defined( 'WC_PLUGIN_BASENAME' ) ) {
53 add_action( 'deactivate_' . WC_PLUGIN_BASENAME, [ $this, 'unschedule_cronjobs' ] );
54 }
55 }
56
57 /**
58 * Installation related logic for Draft order functionality.
59 *
60 * @internal
61 */
62 public function install() {
63 $this->maybe_create_cronjobs();
64 }
65
66 /**
67 * Unschedule recurring actions when plugin is deactivated.
68 *
69 * @since 10.0.0
70 * @internal
71 */
72 public function unschedule_cronjobs() {
73 WC()->queue()->cancel_all( self::DRAFT_CLEANUP_EVENT_HOOK );
74 }
75
76 /**
77 * Maybe create cron events.
78 */
79 protected function maybe_create_cronjobs() {
80 $has_scheduled_action = function_exists( 'as_has_scheduled_action' ) ? 'as_has_scheduled_action' : 'as_next_scheduled_action';
81 if ( false === call_user_func( $has_scheduled_action, self::DRAFT_CLEANUP_EVENT_HOOK ) ) {
82 $midnight_tonight = strtotime( 'midnight tonight' );
83 if ( false !== $midnight_tonight ) {
84 as_schedule_recurring_action( $midnight_tonight, DAY_IN_SECONDS, self::DRAFT_CLEANUP_EVENT_HOOK );
85 }
86 }
87 }
88
89 /**
90 * Register custom order status for orders created via the API during checkout.
91 *
92 * Draft order status is used before payment is attempted, during checkout, when a cart is converted to an order.
93 *
94 * @param array $statuses Array of statuses.
95 * @internal
96 * @return array
97 */
98 public function register_draft_order_status( array $statuses ) {
99 $statuses[ self::DB_STATUS ] = _x( 'Draft', 'Order status', 'woocommerce' );
100 return $statuses;
101 }
102
103 /**
104 * Register custom order post status for orders created via the API during checkout.
105 *
106 * @param array $statuses Array of statuses.
107 * @internal
108
109 * @return array
110 */
111 public function register_draft_order_post_status( array $statuses ) {
112 $statuses[ self::DB_STATUS ] = $this->get_post_status_properties();
113 return $statuses;
114 }
115
116 /**
117 * Returns the properties of this post status for registration.
118 *
119 * @return array
120 */
121 private function get_post_status_properties() {
122 return [
123 'label' => _x( 'Draft', 'Order status', 'woocommerce' ),
124 'public' => false,
125 'exclude_from_search' => true,
126 'show_in_admin_all_list' => false,
127 'show_in_admin_status_list' => true,
128 /* translators: %s: number of orders */
129 'label_count' => _n_noop( 'Drafts <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>', 'woocommerce' ),
130 ];
131 }
132
133 /**
134 * Remove draft status from the 'status' argument of an $args array.
135 *
136 * @param array $args Array of arguments containing statuses in the status key.
137 * @internal
138 * @return array
139 */
140 public function delete_draft_order_post_status_from_args( $args ) {
141 if ( ! array_key_exists( 'status', $args ) ) {
142 $statuses = [];
143 foreach ( wc_get_order_statuses() as $key => $label ) {
144 if ( self::DB_STATUS !== $key ) {
145 $statuses[] = str_replace( 'wc-', '', $key );
146 }
147 }
148 $args['status'] = $statuses;
149 } elseif ( self::DB_STATUS === $args['status'] ) {
150 $args['status'] = '';
151 } elseif ( is_array( $args['status'] ) ) {
152 $args['status'] = array_diff_key( $args['status'], array( self::STATUS => null ) );
153 }
154
155 return $args;
156 }
157
158 /**
159 * Append draft status to a list of statuses.
160 *
161 * @param array $statuses Array of statuses.
162 * @internal
163
164 * @return array
165 */
166 public function append_draft_order_post_status( $statuses ) {
167 $statuses[] = self::STATUS;
168 return $statuses;
169 }
170
171 /**
172 * Delete draft orders older than a day in configurable batches (default: 20).
173 *
174 * Ran on a daily cron schedule. Batch size is filterable via
175 * `woocommerce_delete_expired_draft_orders_batch_size`.
176 *
177 * @internal
178 */
179 public function delete_expired_draft_orders() {
180 $count = 0;
181 /**
182 * Filters the number of draft orders deleted per batch during cleanup.
183 *
184 * Increasing this value can help improve deletion throughput for high-volume or busy stores
185 * when the cleanup task cannot keep up with the draft orders backlog.
186 *
187 * @since 10.7.0
188 * @param int $batch_size Number of draft orders to delete per batch. Default 20.
189 */
190 $batch_size = max( 1, (int) apply_filters( 'woocommerce_delete_expired_draft_orders_batch_size', 20 ) );
191 $this->ensure_draft_status_registered();
192 $orders = wc_get_orders(
193 [
194 'date_modified' => '<=' . strtotime( '-1 DAY' ),
195 'limit' => $batch_size,
196 'status' => self::DB_STATUS,
197 'type' => 'shop_order',
198 ]
199 );
200
201 // do we bail because the query results are unexpected?
202 try {
203 $this->assert_order_results( $orders, $batch_size );
204 if ( $orders ) {
205 foreach ( $orders as $order ) {
206 $order->delete( true );
207 ++$count;
208 }
209 }
210 if ( $batch_size === $count && function_exists( 'as_enqueue_async_action' ) ) {
211 as_enqueue_async_action( self::DRAFT_CLEANUP_EVENT_HOOK );
212 }
213 } catch ( Exception $error ) {
214 wc_caught_exception( $error, __METHOD__ );
215 }
216 }
217
218 /**
219 * Since it's possible for third party code to clobber the `$wp_post_statuses` global,
220 * we need to do a final check here to make sure the draft post status is
221 * registered with the global so that it is not removed by WP_Query status
222 * validation checks.
223 */
224 private function ensure_draft_status_registered() {
225 $is_registered = get_post_stati( [ 'name' => self::DB_STATUS ] );
226 if ( empty( $is_registered ) ) {
227 register_post_status(
228 self::DB_STATUS,
229 $this->get_post_status_properties()
230 );
231 }
232 }
233
234 /**
235 * Asserts whether incoming order results are expected given the query
236 * this service class executes.
237 *
238 * @param WC_Order[] $order_results The order results being asserted.
239 * @param int $expected_batch_size The expected batch size for the results.
240 * @throws Exception If any assertions fail, an exception is thrown.
241 */
242 private function assert_order_results( $order_results, $expected_batch_size ) {
243 // if not an array, then just return because it won't get handled
244 // anyways.
245 if ( ! is_array( $order_results ) ) {
246 return;
247 }
248
249 $suffix = ' This is an indicator that something is filtering WooCommerce or WordPress queries and modifying the query parameters.';
250
251 // if count is greater than our expected batch size, then that's a problem.
252 if ( count( $order_results ) > $expected_batch_size ) {
253 throw new Exception( 'There are an unexpected number of results returned from the query.' . $suffix );
254 }
255
256 // if any of the returned orders are not draft (or not a WC_Order), then that's a problem.
257 foreach ( $order_results as $order ) {
258 if ( ! ( $order instanceof WC_Order ) ) {
259 throw new Exception( 'The returned results contain a value that is not a WC_Order.' . $suffix );
260 }
261 if ( ! $order->has_status( self::STATUS ) ) {
262 throw new Exception( 'The results contain an order that is not a `wc-checkout-draft` status in the results.' . $suffix );
263 }
264 }
265 }
266 }
267