PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 1.11.0
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v1.11.0
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / inc / modules / pre-orders / class-pre-orders-main-functionality.php

class-pre-orders-main-functionality.php in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 1.11.0, at inc/modules/pre-orders/class-pre-orders-main-functionality.php

1,445 lines 44.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Pre Orders.
5 *
6 * @package Merchant
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit; // Exit if accessed directly
11 }
12
13 /**
14 * Pre Orders Class.
15 *
16 */
17 class Merchant_Pre_Orders_Main_Functionality {
18
19 /**
20 * Module ID.
21 *
22 */
23 const MODULE_ID = 'pre-orders';
24
25 /**
26 * @var string Date time format
27 */
28 public const DATE_TIME_FORMAT = 'm-d-Y h:i A';
29
30 /**
31 * Flag if the_title filter is added or not.
32 *
33 * @var bool
34 */
35 private $is_pre_order_filter_on = false;
36
37 /**
38 * Init.
39 *
40 * @return void
41 */
42 public function init() {
43 add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'allow_one_type_only' ), 99, 2 );
44 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_cart_item_data' ), 10, 4 );
45 add_filter( 'woocommerce_hidden_order_itemmeta', array( $this, 'hidden_order_itemmeta' ) );
46 add_action( 'woocommerce_add_order_item_meta', array( $this, 'add_order_item_meta' ), 10, 2 );
47
48 // Cronjob.
49 if ( ! wp_next_scheduled( 'check_for_released_preorders' ) ) {
50 wp_schedule_event( time(), 'twicedaily', 'check_for_released_preorders' );
51 }
52
53 add_action( 'check_for_released_preorders', array( $this, 'check_for_pre_orders_and_maybe_update_status' ) );
54
55 add_filter( 'woocommerce_product_add_to_cart_text', array( $this, 'change_button_text' ), 10, 2 );
56 add_filter( 'woocommerce_product_single_add_to_cart_text', array( $this, 'change_button_text' ), 10, 2 );
57 add_filter( 'woocommerce_available_variation', array( $this, 'change_button_text_for_variable_products' ), 10, 3 );
58 add_action( 'woocommerce_before_add_to_cart_form', array( $this, 'additional_information_before_cart_form' ) );
59 add_action( 'woocommerce_after_add_to_cart_form', array( $this, 'additional_information_after_cart_form' ) );
60
61 // Cart
62 add_filter( 'woocommerce_get_item_data', array( $this, 'cart_message_handler' ), 10, 2 );
63
64 add_action( 'woocommerce_order_item_meta_end', array( $this, 'order_item_meta_end' ), 10, 4 );
65 add_action( 'woocommerce_shop_loop_item_title', array( $this, 'shop_loop_item_title' ) );
66
67 // Products block.
68 add_filter( 'render_block_context', array( $this, 'add_block_title_filter' ), 10, 1 );
69 add_filter( 'woocommerce_blocks_product_grid_item_html', array( $this, 'override_product_grid_block' ), PHP_INT_MAX, 3 );
70
71 $this->register_pre_orders_order_status();
72
73 add_filter( 'wc_order_statuses', array( $this, 'add_pre_orders_order_statuses' ) );
74 add_filter( 'woocommerce_post_class', array( $this, 'pre_orders_post_class' ), 10, 3 );
75
76 add_filter( 'woocommerce_get_price_html', array( $this, 'dynamic_discount_price_html' ), 10, 2 );
77 add_action( 'woocommerce_before_calculate_totals', array( $this, 'dynamic_discount_cart_price' ) );
78 add_action( 'woocommerce_thankyou', array( $this, 'splitting_orders' ) );
79 add_filter( 'manage_woocommerce_page_wc-orders_columns', array( $this, 'shop_order_column' ), 11 );
80 add_filter( 'manage_edit-shop_order_columns', array( $this, 'shop_order_column' ), 11 );
81 add_action( 'manage_woocommerce_page_wc-orders_custom_column', array( $this, 'shop_order_column_content' ), 10, 2 );
82 add_action( 'manage_shop_order_posts_custom_column', array( $this, 'shop_order_column_content' ), 10, 2 );
83 }
84
85 /**
86 * Add shipping date column to the orders list.
87 *
88 * @param $columns array The columns.
89 *
90 * @return array The columns.
91 */
92 public function shop_order_column( $columns ) {
93 $columns['pre_order_shipping_date'] = esc_html__( 'Shipping Date', 'merchant' );
94
95 return $columns;
96 }
97
98 /**
99 * Display the shipping date in the orders list.
100 *
101 * @param $column string The column.
102 * @param $order WC_Order|int The order object in HPOS or order id.
103 *
104 * @return void
105 */
106 public function shop_order_column_content( $column, $order ) {
107 if ( $column === 'pre_order_shipping_date' ) {
108 if ( is_numeric( $order ) ) {
109 $order = wc_get_order( $order );
110 }
111 /**
112 * Filter the pre order shipping date.
113 *
114 * @param int $shipping_date The shipping date.
115 * @param WC_Order $order The order object.
116 *
117 * @since 1.9.9
118 */
119 $shipping_date = apply_filters(
120 'merchant_pre_order_shipping_date_column',
121 $order->get_meta( '_merchant_order_pre_order_shipping_date' ),
122 $order
123 );
124 if ( $shipping_date ) {
125 echo '<strong>' . esc_html( $this->convert_timestamp_to_human_readable( $shipping_date ) ) . '</strong>';
126 }
127 }
128 }
129
130 /**
131 * Convert timestamp to human readable date.
132 *
133 * @param $timestamp int The timestamp.
134 *
135 * @return string The human readable date.
136 */
137 private function convert_timestamp_to_human_readable( $timestamp ) {
138 $timezone = new DateTimeZone( merchant_timezone() );
139 $date = new \DateTime( 'now', $timezone );
140 $date->setTimestamp( $timestamp );
141
142 return $date->format( self::DATE_TIME_FORMAT );
143 }
144
145 /**
146 * Add offer details to the product.
147 *
148 * @param $cart_item_data array The cart item data.
149 * @param $product_id int The product ID.
150 * @param $variation_id int The variation ID.
151 * @param $quantity int The quantity.
152 *
153 * @return array The cart item data.
154 */
155 public function add_cart_item_data( $cart_item_data, $product_id, $variation_id, $quantity ) {
156 $product = wc_get_product( $product_id );
157 $offer = self::available_product_rule( $product_id );
158 if ( empty( $offer ) && $product->is_type( 'variable' ) ) {
159 $offer = self::available_product_rule( $variation_id );
160 }
161 if ( ! empty( $offer ) ) {
162 $cart_item_data['_merchant_pre_order'] = $offer;
163 $cart_item_data['_merchant_pre_order_shipping_date'] = $offer['shipping_timestamp'];
164 }
165
166 return $cart_item_data;
167 }
168
169 /**
170 * Add pre-order data to order item meta.
171 *
172 * @param $item_id int The item ID.
173 * @param $values array The values.
174 *
175 * @return void
176 */
177 public function add_order_item_meta( $item_id, $values ) {
178 if ( isset( $values['_merchant_pre_order'] ) ) {
179 wc_add_order_item_meta( $item_id, '_merchant_pre_order', $values['_merchant_pre_order'] );
180 }
181
182 if ( isset( $values['_merchant_pre_order_shipping_date'] ) ) {
183 wc_add_order_item_meta( $item_id, '_merchant_pre_order_shipping_date', $values['_merchant_pre_order_shipping_date'] );
184 }
185 }
186
187 /**
188 * Splitting orders.
189 *
190 * @param $order_id int The order ID.
191 *
192 * @return void
193 */
194 public function splitting_orders( $order_id ) {
195 $order = wc_get_order( $order_id );
196 $mode = Merchant_Admin_Options::get( self::MODULE_ID, 'modes', 'unified_order' );
197
198 $this->mark_whole_order_as_pre_order( $order );
199
200 // if ( 'unified_order' === $mode || 'only_pre_orders' === $mode ) {
201 // $this->mark_whole_order_as_pre_order( $order );
202 // } elseif ( 'group_pre_order_into_one_order' === $mode ) {
203 // $this->group_pre_order_into_one_order( $order );
204 // } elseif ( 'separate_order_for_pre_orders' === $mode ) {
205 // $this->separate_order_for_pre_orders( $order );
206 // }
207 }
208
209 /**
210 * Mark whole order as pre-order if it contains pre-order products.
211 *
212 * @param $order WC_Order The order object.
213 *
214 * @return void
215 */
216 public function mark_whole_order_as_pre_order( $order ) {
217 $has_pre_order = 0;
218 $shipping_dates = array();
219 foreach ( $order->get_items() as $item_id => $item ) {
220 $product_id = $item->get_product_id();
221 if ( $this->is_pre_order( $product_id ) ) {
222 $shipping_dates[] = $item->get_meta( '_merchant_pre_order_shipping_date' );
223 ++ $has_pre_order;
224 }
225 }
226
227 if ( $has_pre_order ) {
228 $order->set_status( 'wc-pre-ordered' );
229 $order->add_meta_data( '_is_pre_order', true );
230 $order->add_meta_data( '_merchant_order_pre_order_shipping_date', max( $shipping_dates ) );
231 $order->save();
232 $this->trigger_emails( $order );
233 }
234 }
235
236 /**
237 * Group pre-order products into one order and keep the original order.
238 *
239 * @param WC_Order $order The order object.
240 *
241 * @return void
242 */
243 public function group_pre_order_into_one_order( $order ) {
244 // Get the original order
245 $original_order = $order;
246
247 // Store sub-orders IDs
248 $sub_order_ids = $pre_order_products = array();
249
250 // Loop through each product in the original order
251 foreach ( $original_order->get_items() as $item_id => $item ) {
252 if ( $this->is_pre_order( $item->get_product()->get_id() ) ) {
253 $pre_order_products[] = $item;
254 }
255 }
256
257 if ( ! empty( $pre_order_products ) ) {
258 $shipping_dates = array();
259 // Create a new order
260 $new_order = wc_create_order(
261 array(
262 'parent' => $original_order->get_id(),
263 )
264 );
265
266 // Copy order details from original order to new order.
267 $this->copy_order_details( $original_order, $new_order );
268
269 $new_order->add_meta_data( '_is_sub_order', true );
270 $new_order->add_meta_data( '_is_pre_order', true );
271 $new_order->set_status( 'wc-pre-ordered' );
272
273 // Copy order meta data from original order
274 foreach ( $original_order->get_meta_data() as $meta ) {
275 $new_order->add_meta_data( $meta->key, $meta->value );
276 }
277
278 // add pre-order products to the new order
279 foreach ( $pre_order_products as $item ) {
280 $rule = self::available_product_rule( $item->get_product()->get_id() );
281 $new_item = $this->clone_order_item( $item );
282 $new_item->add_meta_data( '_merchant_pre_order', $rule );
283 $new_item->add_meta_data( '_merchant_is_pre_order_product', true );
284 $new_item->add_meta_data( '_merchant_pre_order_shipping_date', $rule['shipping_timestamp'] );
285 $new_order->add_item( $new_item );
286 $shipping_dates[] = $rule['shipping_timestamp'];
287 }
288
289 // Calculate totals and set status
290 $new_order->calculate_totals();
291 $new_order->add_meta_data( '_merchant_order_pre_order_shipping_date', max( $shipping_dates ) );
292 $new_order->save();
293
294 $sub_order_ids[] = $new_order->get_id();
295
296 $this->trigger_emails( $new_order );
297
298 // Save sub-order IDs to the original order
299 $original_order->update_meta_data( '_sub_order_ids', $sub_order_ids );
300
301
302 // Update original order totals after removing items
303 $original_order->calculate_totals();
304 $original_order->save();
305 }
306 }
307
308 private function clone_order_item( $item ) {
309 $new_item = new WC_Order_Item_Product();
310 $new_item->set_product_id( $item->get_product_id() );
311 $new_item->set_variation_id( $item->get_variation_id() );
312 $new_item->set_name( $item->get_name() );
313 $new_item->set_quantity( $item->get_quantity() );
314 $new_item->set_subtotal( $item->get_subtotal() );
315 $new_item->set_total( $item->get_total() );
316 $new_item->set_taxes( $item->get_taxes() );
317 $new_item->set_meta_data( $item->get_meta_data() );
318
319 return $new_item;
320 }
321
322 /**
323 * Generate separate orders for each pre-order product.
324 *
325 * @param WC_Order $order The order object.
326 *
327 * @return void
328 */
329 public function separate_order_for_pre_orders( $order ) {
330 // Get the original order
331 $original_order = $order;
332
333 // Store sub-orders IDs
334 $sub_order_ids = array();
335
336 // Loop through each product in the original order
337 foreach ( $original_order->get_items() as $item_id => $item ) {
338 //check if the item is a product
339
340 // Get product details
341 $product_id = $item->get_product()->get_id();
342 if ( ! $product_id ) {
343 continue;
344 }
345
346 // check if the product is a pre-order
347 if ( ! $this->is_pre_order( $product_id ) ) {
348 continue;
349 }
350
351 // Create a new order
352 $new_order = wc_create_order(
353 array(
354 'parent' => $original_order->get_id(),
355 )
356 );
357
358 $new_item = $this->clone_order_item( $item );
359 $rule = self::available_product_rule( $product_id );
360 $new_item->add_meta_data( '_merchant_pre_order', $rule );
361 $new_item->add_meta_data( '_merchant_is_pre_order_product', true );
362 $new_item->add_meta_data( '_merchant_pre_order_shipping_date', $rule['shipping_timestamp'] );
363 $new_order->add_item( $new_item );
364
365 // Copy order details from original order to new order.
366 $this->copy_order_details( $original_order, $new_order );
367
368 // Calculate totals and set status
369 $new_order->calculate_totals();
370
371 // Save new order and store its ID
372 $new_order->add_meta_data( '_is_sub_order', true );
373 $new_order->add_meta_data( '_is_pre_order', true );
374 $new_order->add_meta_data( '_merchant_order_pre_order_shipping_date', $rule['shipping_timestamp'] );
375 $new_order->set_status( 'wc-pre-ordered' );
376 $new_order->save();
377 $this->trigger_emails( $new_order );
378 $sub_order_ids[] = $new_order->get_id();
379 }
380
381 // Save sub-order IDs to the original order
382 $original_order->update_meta_data( '_sub_order_ids', $sub_order_ids );
383
384 // Update original order totals after removing items
385 $original_order->calculate_totals();
386 $original_order->save();
387 }
388
389 /**
390 * Trigger sending emails for specific order.
391 *
392 * @param $order WC_Order The order object.
393 *
394 * @return void
395 */
396 private function trigger_emails( $order ) {
397 $mailer = WC()->mailer();
398 // Send customer email
399 // $customer_email_instance = $mailer->emails['WC_Email_Customer_Processing_Order'];
400 // $customer_email_instance->trigger( $order->get_id(), $order );
401
402 // Send admin email
403 $admin_email_instance = $mailer->emails['WC_Email_New_Order'];
404 $admin_email_instance->trigger( $order->get_id(), $order );
405 }
406
407 /**
408 * Hide order item meta that will be used for internal use only.
409 *
410 * @param $args array The hidden order item meta.
411 *
412 * @return array The hidden order item meta.
413 */
414 function hidden_order_itemmeta( $args ) {
415 $args[] = '_merchant_pre_order';
416 $args[] = '_merchant_is_pre_order_product';
417 $args[] = '_merchant_pre_order_shipping_date';
418
419 return $args;
420 }
421
422 /**
423 * Copy order details from the original order to the new child order.
424 *
425 * @param $original_order WC_Order The original order.
426 * @param $new_order WC_Order The new child order.
427 *
428 * @return void
429 */
430 private function copy_order_details( $original_order, $new_order ) {
431 $new_order->set_customer_id( $original_order->get_customer_id() );
432
433 // set customer billing details
434 $new_order->set_billing_first_name( $original_order->get_billing_first_name() );
435 $new_order->set_billing_last_name( $original_order->get_billing_last_name() );
436 $new_order->set_billing_phone( $original_order->get_billing_phone() );
437 $new_order->set_billing_email( $original_order->get_billing_email() );
438 $new_order->set_billing_address_1( $original_order->get_billing_address_1() );
439 $new_order->set_billing_address_2( $original_order->get_billing_address_2() );
440 $new_order->set_billing_city( $original_order->get_billing_city() );
441 $new_order->set_billing_state( $original_order->get_billing_state() );
442 $new_order->set_billing_postcode( $original_order->get_billing_postcode() );
443 $new_order->set_billing_country( $original_order->get_billing_country() );
444
445 // set customer shipping details
446 $new_order->set_shipping_first_name( $original_order->get_shipping_first_name() );
447 $new_order->set_shipping_last_name( $original_order->get_shipping_last_name() );
448 $new_order->set_shipping_address_1( $original_order->get_shipping_address_1() );
449 $new_order->set_shipping_address_2( $original_order->get_shipping_address_2() );
450 $new_order->set_shipping_city( $original_order->get_shipping_city() );
451 $new_order->set_shipping_state( $original_order->get_shipping_state() );
452 $new_order->set_shipping_postcode( $original_order->get_shipping_postcode() );
453 $new_order->set_shipping_country( $original_order->get_shipping_country() );
454
455 $new_order->set_customer_note( $original_order->get_customer_note() );
456 /**
457 * This action is documented in woocommerce/includes/class-wc-checkout.php
458 *
459 * @since 3.0.0 or earlier
460 */
461 $new_order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
462 $new_order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
463 $new_order->set_customer_user_agent( wc_get_user_agent() );
464 $new_order->set_currency( get_woocommerce_currency() );
465 $new_order->set_created_via( 'checkout' );
466
467 // Copy order meta data from original order
468 foreach ( $original_order->get_meta_data() as $meta ) {
469 $new_order->add_meta_data( $meta->key, $meta->value );
470 }
471 }
472
473 /**
474 * Update the product price html based on the offer.
475 *
476 * @return string The price html.
477 */
478 public function dynamic_discount_price_html( $html_price, $product ) {
479 /**
480 * Disable the sale price change.
481 *
482 * @param bool $disable_sale_price The sale price change status.
483 * @param string $html_price The price.
484 * @param WC_Product $product The product object.
485 *
486 * @since 1.9.9
487 */
488 if ( apply_filters( 'merchant_pre_order_disable_sale_price_html', false, $html_price, $product ) ) {
489 return $html_price;
490 }
491
492 if ( ( ! is_admin() || wp_doing_ajax() ) && in_array( $product->get_type(), $this->allowed_product_types(), true ) ) {
493 if ( '' === $product->get_price() ) {
494 return $html_price;
495 }
496
497 $offer = self::available_product_rule( $product->get_id() );
498
499 if ( empty( $offer ) && $product->is_type( 'variation' ) ) {
500 $offer = self::available_product_rule( $product->get_parent_id() );
501 $is_excluded = merchant_is_product_excluded( $product->get_id(), $offer );
502 if ( $is_excluded ) {
503 $offer = array();
504 }
505 }
506
507 if ( $product->is_type( 'variable' ) ) {
508 $html_price = $this->variable_product_price_html( $product );
509 } else {
510 $html_price = $this->simple_product_price_html( $product, $offer, $html_price );
511 }
512 }
513
514 return $html_price;
515 }
516
517 /**
518 * Variable product price html.
519 *
520 * @param WC_Product $product The product object.
521 *
522 * @return string The price html.
523 */
524 private function variable_product_price_html( $product ) {
525 $prices = array();
526 $variations = $product->get_children();
527
528 $regular_prices = array();
529 $sale_prices = array();
530
531 foreach ( $variations as $variation_id ) {
532 $variation = wc_get_product( $variation_id );
533 $variation_offer = self::available_product_rule( $variation_id );
534 $regular_price = $variation->get_regular_price();
535
536 $regular_prices[] = $regular_price;
537
538 if ( empty( $variation_offer ) ) {
539 $variation_offer = self::available_product_rule( $product->get_id() );
540
541 $is_excluded = merchant_is_product_excluded( $variation_id, $variation_offer );
542 if ( $is_excluded ) {
543 $prices[] = $regular_price;
544 $sale_prices[] = $regular_price;
545 continue;
546 }
547 }
548
549 if ( empty( $variation_offer ) ) {
550 if ( $variation->is_on_sale() ) {
551 $sale_price = wc_get_price_to_display( $variation );
552 } else {
553 $sale_price = $regular_price;
554 }
555 $prices[] = $sale_price;
556 $sale_prices[] = $sale_price;
557 continue;
558 }
559
560 $sale_price = $this->calculate_discounted_price( $regular_price, $variation_offer, $variation );
561 if ( $sale_price <= 0 ) {
562 // If the price is less than 0, set it to the regular/sale price
563 if ( $variation->is_on_sale() ) {
564 $sale_price = wc_get_price_to_display( $variation );
565 } else {
566 $sale_price = $regular_price;
567 }
568 }
569 $prices[] = $sale_price;
570 $sale_prices[] = $sale_price;
571 }
572
573 $regular_prices = array_unique( $regular_prices );
574 $sale_prices = array_unique( $sale_prices );
575
576 if ( 1 === count( $regular_prices ) && 1 === count( $sale_prices ) ) {
577 $regular_price = reset( $regular_prices );
578 $sale_price = reset( $sale_prices );
579 return wc_format_sale_price( $regular_price, $sale_price );
580 }
581
582 // Ensure $prices is not empty
583 if ( ! empty( $prices ) ) {
584 $min_price = min( $prices );
585 $max_price = max( $prices );
586
587 if ( $min_price !== $max_price ) {
588 return wc_format_price_range( $min_price, $max_price );
589 }
590
591 return wc_price( $min_price );
592 }
593
594 // If $prices is empty.
595 return '';
596 }
597
598 /**
599 * Simple product price html.
600 *
601 * @param WC_Product $product The product object.
602 * @param array $offer The offer details.
603 *
604 * @return string The price html.
605 */
606 private function simple_product_price_html( $product, $offer, $html_price ) {
607 $sale = self::get_rule_sale( $offer );
608 if ( ! $sale ) {
609 return $html_price;
610 }
611 $regular_price = wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) );
612 $discounted_price = $this->calculate_discounted_price( $regular_price, $offer, $product );
613
614 return wc_format_sale_price( $regular_price, $discounted_price );
615 }
616
617 /**
618 * Calculate the offer discount for certain price.
619 *
620 * @param float $price The price.
621 * @param array $offer The offer.
622 * @param WC_Product $product The product object (only supplied to the filter).
623 *
624 * @return float The discounted price.
625 */
626 public function calculate_discounted_price( $price, $offer, $product = null ) {
627 $sale = self::get_rule_sale( $offer );
628 $discount_type = $discount_value = '';
629 if ( $sale ) {
630 $discount_type = $offer['discount_type'];
631 $discount_value = $offer['discount_amount'];
632 if ( 'percentage' === $discount_type ) {
633 $price = (float) $price - ( (float) $price * ( (float) $discount_value / 100 ) );
634 } else {
635 $price = (float) $price - (float) $discount_value;
636 }
637 }
638
639 /**
640 * Filter the discounted price.
641 *
642 * @param float $price The price.
643 * @param array $offer The offer.
644 * @param string $discount_type The discount type.
645 * @param float $discount_value The discount value.
646 * @param WC_Product $product The product object.
647 *
648 * @since 1.9.5
649 */
650 return apply_filters(
651 'merchant_pre_order_sale_calculate_discounted_price',
652 $price,
653 $offer,
654 $discount_type,
655 $discount_value,
656 $product
657 );
658 }
659
660 /**
661 * Set the discounted cart price for the product.
662 *
663 * @param $cart_object array The cart object.
664 *
665 * @return void
666 */
667 public function dynamic_discount_cart_price() {
668 foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
669 /**
670 * Disable the cart price change.
671 *
672 * @param bool $disable_cart_price The cart price change status.
673 * @param array $cart_item The cart item.
674 * @param string $cart_item_key The cart item key.
675 *
676 * @since 1.9.7
677 */
678 if ( apply_filters( 'merchant_pre_order_sale_disable_cart_price', false, $cart_item, $cart_item_key ) ) {
679 continue;
680 }
681 $product_id = $cart_item['data']->get_id();
682 $product = wc_get_product( $product_id );
683
684 if ( ( ! is_admin() || wp_doing_ajax() ) && in_array( $product->get_type(), $this->allowed_product_types(), true ) ) {
685 $offer = self::available_product_rule( $product->get_id() );
686
687 if ( $product->is_type( 'variable' ) ) {
688 $product_id = $cart_item['variation_id'];
689 $product = wc_get_product( $product_id );
690 // check if the variation has an offer
691 $offer = self::available_product_rule( $product_id );
692 }
693
694 if ( empty( $offer ) && $product->is_type( 'variation' ) ) {
695 $offer = self::available_product_rule( $product->get_parent_id() );
696
697 $is_excluded = merchant_is_product_excluded( $product_id, $offer );
698 if ( $is_excluded ) {
699 continue;
700 }
701 }
702
703 if ( empty( $offer ) ) {
704 continue;
705 }
706 if ( ! isset( $offer['discount_toggle'] ) ) {
707 continue;
708 }
709 $regular_price = $product->get_regular_price();
710 $sale_price = $this->calculate_discounted_price( $regular_price, $offer, $product );
711 $cart_item['data']->set_price( $sale_price );
712 $cart_item['data']->set_meta_data( array( '_merchant_pre_order_sale' => $offer ) );
713 }
714 }
715 }
716
717 /**
718 * Validation (one type only).
719 *
720 * @param boolean $passed
721 * @param integer $product_id
722 *
723 * @return boolean
724 */
725 public function allow_one_type_only( $passed, $product_id ) {
726 $products = array_filter( WC()->cart->get_cart_contents() );
727 $pre_orders = 0;
728 $has_pre_orders = false;
729 foreach ( $products as $product ) {
730 $is_pre_order = $this->is_pre_order( $product['data']->get_id() );
731 if ( $is_pre_order ) {
732 ++ $pre_orders;
733 }
734 }
735 if ( $pre_orders ) {
736 $has_pre_orders = true;
737 }
738 $input_post_data = array(
739 'variation_id' => filter_input( INPUT_POST, 'variation_id', FILTER_SANITIZE_NUMBER_INT ),
740 );
741
742 $variable_id = ( isset( $input_post_data['variation_id'] ) ) ? sanitize_text_field( wp_unslash( $input_post_data['variation_id'] ) ) : 0;
743 $is_variable_has_pre_order = $this->is_pre_order( $product_id ) || $this->is_pre_order( $variable_id );
744
745 if ( empty( $products ) || ( $is_variable_has_pre_order && $has_pre_orders ) || ( false === $is_variable_has_pre_order && false === $has_pre_orders ) ) {
746 $passed = true;
747 } else {
748 $mode = Merchant_Admin_Options::get( self::MODULE_ID, 'modes', 'unified_order' );
749 if ( 'only_pre_orders' === $mode ) {
750 $passed = false;
751 if ( $is_variable_has_pre_order ) {
752 $notice = esc_html__( 'We detected that you are trying to add a pre-order product in your cart. Please remove the rest of the products before proceeding.',
753 'merchant' );
754 } else {
755 $notice = esc_html__( 'We detected that your cart has pre-order products. Please remove them before being able to add this product.', 'merchant' );
756 }
757 wc_add_notice( $notice, 'error' );
758 }
759 }
760
761 return $passed;
762 }
763
764 /**
765 * Check if product is a pre order item by product ID.
766 *
767 * @param integer $product_id
768 *
769 * @return boolean
770 */
771 public function is_pre_order( $product_id ) {
772 $available_pre_order = self::available_product_rule( $product_id );
773
774 return ! empty( $available_pre_order );
775 }
776
777 /**
778 * Check for pre orders and maybe update the status.
779 *
780 * @return void
781 */
782 public function check_for_pre_orders_and_maybe_update_status() {
783 $args = array(
784 'status' => 'wc-pre-ordered',
785 );
786
787 $pre_ordered_orders = wc_get_orders( $args );
788
789 foreach ( $pre_ordered_orders as $order ) {
790 $pre_order_date = $order->get_meta( '_merchant_order_pre_order_shipping_date' );
791
792 if ( $pre_order_date < time() ) {
793 $parent_order_id = $order->get_parent_id();
794
795 if ( 0 !== $parent_order_id ) {
796 $parent_order = wc_get_order( $parent_order_id );
797
798 if ( $parent_order->get_status() === 'completed' ) {
799 $order->update_status( 'processing', '[Merchant Pre Orders] ' );
800 }
801 } elseif ( $order->payment_complete() ) {
802 $order->update_status( 'processing', '[Merchant Pre Orders] ' );
803 }
804 }
805 }
806 }
807
808 /**
809 * Change pre-order button text.
810 *
811 * @param string $text
812 * @param WC_Product $product
813 *
814 * @return string
815 */
816 public function change_button_text( $text, $product ) {
817 if ( $product && $this->is_pre_order( $product->get_id() ) ) {
818 $pre_order_rule = self::available_product_rule( $product->get_id() );
819 $text = $pre_order_rule['button_text'] ? Merchant_Translator::translate( $pre_order_rule['button_text'] ) : esc_html__( 'Pre Order Now!', 'merchant' );
820 }
821
822 return $text;
823 }
824
825 /**
826 * Change pre-order button text for variable products.
827 *
828 * @param array $data
829 * @param object $product
830 * @param object $variation
831 *
832 * @return array
833 */
834 public function change_button_text_for_variable_products( $data, $product, $variation ) {
835 if ( $this->is_pre_order( $variation->get_id() ) ) {
836 $pre_order_rule = self::available_product_rule( $variation->get_id() );
837 $data['is_pre_order'] = true;
838
839 $additional_text = $pre_order_rule['additional_text'] ? Merchant_Translator::translate( $pre_order_rule['additional_text'] )
840 : esc_html__( 'Ships on {date}.', 'merchant' );
841 $time_format = date_i18n( get_option( 'date_format' ), $pre_order_rule['shipping_timestamp'] );
842 $text = $this->replace_date_text( $additional_text, $time_format );
843
844 if ( ! empty( $text ) ) {
845 $data['is_pre_order_date'] = $this->replace_date_text( $additional_text, $time_format );
846 }
847 }
848
849 return $data;
850 }
851
852 /**
853 * Replace {date} markup with new text.
854 *
855 * @param string $string_contains_date_tag
856 * @param string $time_format
857 *
858 * @return string
859 */
860 public function replace_date_text( $string_contains_date_tag, $time_format ) {
861 $from = array( '{date}' );
862 $to = array( $time_format );
863
864 return str_replace( $from, $to, $string_contains_date_tag );
865 }
866
867 /**
868 * Display pre order additional information before add to cart form.
869 *
870 * @return void
871 */
872 public function additional_information_before_cart_form() {
873 $input_post_data = array(
874 'product_id' => filter_input( INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT ),
875 );
876
877 global $post, $product;
878
879 // Do not override globals.
880 $_post = $post;
881 $_product = $product;
882
883 // In some cases the $post might be null. e.g inside quick view popup.
884 if ( ! $_post && isset( $input_post_data['product_id'] ) ) {
885 $_post = get_post( absint( $input_post_data['product_id'] ) );
886 $_product = wc_get_product( $_post->ID );
887 }
888 if ( $this->is_pre_order( $_post->ID ) ) {
889 $pre_order_rule = self::available_product_rule( $_product->get_id() );
890 if ( ! empty( $pre_order_rule ) && $pre_order_rule['placement'] === 'before' ) {
891 $this->maybe_render_additional_information( $pre_order_rule );
892 }
893 }
894 }
895
896 /**
897 * Display pre order additional information after add to cart form.
898 *
899 * @return void
900 */
901 public function additional_information_after_cart_form() {
902 $input_post_data = array(
903 'product_id' => filter_input( INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT ),
904 );
905
906 global $post, $product;
907
908 // Do not override globals.
909 $_post = $post;
910 $_product = $product;
911
912 // In some cases the $post might be null. e.g inside quick view popup.
913 if ( ! $_post && isset( $input_post_data['product_id'] ) ) {
914 $_post = get_post( absint( $input_post_data['product_id'] ) );
915 $_product = wc_get_product( $_post->ID );
916 }
917 if ( $_product && $this->is_pre_order( $_post->ID ) ) {
918 $pre_order_rule = self::available_product_rule( $_product->get_id() );
919 if ( ! empty( $pre_order_rule ) && $pre_order_rule['placement'] === 'after' ) {
920 $this->maybe_render_additional_information( $pre_order_rule );
921 }
922 }
923 }
924
925 /**
926 * Maybe render additional information.
927 *
928 * @return void
929 */
930 public function maybe_render_additional_information( $rule ) {
931 if ( ! empty( $rule ) ) {
932 $additional_text = $rule['additional_text'] ? Merchant_Translator::translate( $rule['additional_text'] )
933 : esc_html__( 'Ships on {date}.', 'merchant' );
934 $time_format = date_i18n( get_option( 'date_format' ), $rule['shipping_timestamp'] );
935 $text = $this->replace_date_text( $additional_text, $time_format );
936
937 printf( '<div class="merchant-pre-orders-date">%s</div>', esc_html( $text ) );
938 }
939 }
940
941 /**
942 * Register pre orders status.
943 *
944 * @return void
945 */
946 public function register_pre_orders_order_status() {
947 register_post_status( 'wc-pre-ordered', array(
948 'label' => esc_html__( 'Pre Ordered', 'merchant' ),
949 'public' => true,
950 'show_in_admin_status_list' => true,
951 'show_in_admin_all_list' => true,
952 'exclude_from_search' => false,
953 /* translators: %s: pre ordered product count */
954 'label_count' => _n_noop( 'Pre Ordered <span class="count">(%s)</span>', 'Pre Ordered <span class="count">(%s)</span>', 'merchant' ),
955 ) );
956 }
957
958 /**
959 * Add pre orders status to order statuses.
960 *
961 * @param array $order_statuses
962 *
963 * @return array
964 */
965 public function add_pre_orders_order_statuses( $order_statuses ) {
966 $order_statuses['wc-pre-ordered'] = esc_html__( 'Pre-Ordered', 'merchant' );
967
968 return $order_statuses;
969 }
970
971 /**
972 * Add pre orders class identifies to the body tag.
973 *
974 */
975 public function pre_orders_post_class( $classes, $product ) {
976 if ( $this->is_pre_order( $product->get_id() ) ) {
977 $classes[] = 'merchant-pre-ordered-product';
978 }
979
980 return $classes;
981 }
982
983 /**
984 * Filters cart item data to display cart.
985 *
986 * @param array $item_data Cart item data.
987 * @param array $cart_item Cart item array.
988 *
989 * @return array
990 *
991 * @see wc_get_formatted_cart_item_data for filter usage.
992 */
993 public function cart_message_handler( $item_data, $cart_item ) {
994 $product_id = $cart_item['product_id'];
995 if ( $cart_item['data']->is_type( 'variation' ) ) {
996 $product_id = $cart_item['variation_id'];
997 }
998 if ( $this->is_pre_order( $product_id ) ) {
999 $pre_order_rule = self::available_product_rule( $product_id );
1000 if ( $pre_order_rule ) {
1001 $label_text = $pre_order_rule['cart_label_text'] ? Merchant_Translator::translate( $pre_order_rule['cart_label_text'] ) : esc_html__( 'Ships on', 'merchant' );
1002 $pre_order_date = date_i18n( get_option( 'date_format' ), $pre_order_rule['shipping_timestamp'] );
1003
1004 $item_data[] = array(
1005 'key' => $label_text,
1006 'value' => $pre_order_date,
1007 'display' => '',
1008 );
1009 }
1010 }
1011
1012 return $item_data;
1013 }
1014
1015 /**
1016 * filter name: woocommerce_order_item_meta_end
1017 * Adds pre order additional text to the order item name
1018 *
1019 * @param int $item_id Product ID.
1020 * @param \WC_Order_Item_Product $item Item array data.
1021 * @param \WC_Order $order Order data.
1022 * @param bool $plain_text Is plain text or not.
1023 *
1024 * @return string
1025 */
1026 public function order_item_meta_end( $item_id, $item, $order, $plain_text ) {
1027 echo $this->get_pre_order_text( $item->get_product()->get_id(), $plain_text ? '' : 'dl' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1028 }
1029
1030 /**
1031 * Render pre order text.
1032 */
1033 public function shop_loop_item_title() {
1034 echo $this->get_pre_order_text( get_the_ID(), 'span' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1035 }
1036
1037 /**
1038 * Add the_title filter for block rendering.
1039 *
1040 * @param array $context Default context.
1041 *
1042 * @return array
1043 */
1044 public function add_block_title_filter( $context ) {
1045 if ( ! empty( $context['postType'] ) && 'product' === $context['postType'] && ! $this->is_pre_order_filter_on ) {
1046 $this->is_pre_order_filter_on = true;
1047 add_filter( 'the_title', array( $this, 'block_add_the_title_filter' ), 10, 2 );
1048 add_filter( 'render_block', array( $this, 'block_remove_the_title_filter' ), 10, 3 );
1049 }
1050
1051 return $context;
1052 }
1053
1054 /**
1055 * Add pre order info to product grid block.
1056 *
1057 * @param string $html Product grid item HTML.
1058 * @param object $data Product data passed to the template.
1059 * @param \WC_Product $product Product object.
1060 *
1061 * @return string Updated product grid item HTML.
1062 */
1063 public function override_product_grid_block( $html, $data, $product ) {
1064 $pre_order_text = $this->get_pre_order_text( $product->get_id(), 'span' );
1065 if ( $pre_order_text ) {
1066 $html = str_replace( $data->title, $data->title . $pre_order_text, $html );
1067 }
1068
1069 return $html;
1070 }
1071
1072 /**
1073 * Adds pre order info to post_title.
1074 *
1075 * @param string $title Title.
1076 * @param int $post_id Post ID.
1077 *
1078 * @return string
1079 */
1080 public function block_add_the_title_filter( $title, $post_id ) {
1081 return $title . $this->get_pre_order_text( $post_id, 'span' );
1082 }
1083
1084 /**
1085 * Remove the_title filter when block rendering finished.
1086 *
1087 * @param string $block_content The block content.
1088 * @param array $parsed_block The full block, including name and attributes.
1089 * @param WP_Block $block The block instance.
1090 *
1091 * @return string
1092 */
1093 public function block_remove_the_title_filter( $block_content, $parsed_block, $block ) {
1094 if ( $this->is_pre_order_filter_on ) {
1095 remove_filter( 'the_title', array( $this, 'block_add_the_title_filter' ), 10 );
1096 $this->is_pre_order_filter_on = false;
1097 }
1098
1099 return $block_content;
1100 }
1101
1102 /**
1103 * Get pre order text by product ID.
1104 *
1105 * @param int $product_id
1106 * @param string $render_type Render template type string. 'span', 'dl', ''. Default ''.
1107 *
1108 * @return string
1109 */
1110 private function get_pre_order_text( $product_id, $render_type = '' ) {
1111 if ( ! $this->is_pre_order( $product_id ) ) {
1112 return '';
1113 }
1114
1115 $pre_order_rule = self::available_product_rule( $product_id );
1116 $label_text = $pre_order_rule['cart_label_text'] ? Merchant_Translator::translate( $pre_order_rule['cart_label_text'] ) : esc_html__( 'Ships on', 'merchant' );
1117 $pre_order_date = date_i18n( get_option( 'date_format' ), $pre_order_rule['shipping_timestamp'] );
1118 if ( 'span' === $render_type ) {
1119 return sprintf(
1120 '<span class="merchant-pre-orders-note"><span class="merchant-pre-orders-label">%s:</span><span>%s</span></span>',
1121 esc_html( $label_text ),
1122 $pre_order_date
1123 );
1124 } elseif ( 'dl' === $render_type ) {
1125 return sprintf(
1126 '<dl class="merchant-pre-orders-note"><dt>%s:</dt><dd>%s</dd></dl>',
1127 esc_html( $label_text ),
1128 $pre_order_date
1129 );
1130 } else {
1131 return sprintf(
1132 '%s: %s',
1133 esc_html( $label_text ),
1134 $pre_order_date
1135 );
1136 }
1137 }
1138
1139 /**
1140 * Get the product types that are allowed to have storewide sale.
1141 *
1142 * @return array The allowed product types.
1143 */
1144 public function allowed_product_types() {
1145 /**
1146 * Filter the product types that are allowed to have storewide sale.
1147 *
1148 * @param array $product_types
1149 *
1150 * @since 1.9.5
1151 */
1152 return apply_filters( 'merchant_pre_order_allowed_product_types', array(
1153 'simple',
1154 'variation',
1155 'variable',
1156 ) );
1157 }
1158
1159 /**
1160 * Get the pre order rules.
1161 *
1162 * @param array $rule The rule to get.
1163 *
1164 * @return array|false The pre order rules or false if there are no rule sale.
1165 */
1166 private static function get_rule_sale( $rule ) {
1167 $sale = false;
1168 if ( isset( $rule['discount_toggle'] ) && $rule['discount_toggle'] ) {
1169 $discount_type = $rule['discount_type'];
1170 $discount_amount = $rule['discount_amount'];
1171
1172 $sale = array(
1173 'discount_type' => $discount_type,
1174 'discount_amount' => $discount_amount,
1175 );
1176 }
1177
1178 /**
1179 * Filter the pre order sale.
1180 *
1181 * @param array $sale The pre order sale.
1182 * @param array $rule The pre order rule.
1183 *
1184 * @since 1.9.9
1185 */
1186 return apply_filters( 'merchant_pre_order_rule_sale', $sale, $rule );
1187 }
1188
1189 /**
1190 * Get the pre order rules.
1191 *
1192 * @return array The pre order rules.
1193 */
1194 private static function pre_order_rules() {
1195 return Merchant_Admin_Options::get( self::MODULE_ID, 'rules', array() );
1196 }
1197
1198 /**
1199 * Check if the rule is valid.
1200 *
1201 * @param array $rule The rule to check.
1202 *
1203 * @return boolean True if the rule is valid, false otherwise.
1204 */
1205 private static function is_valid_rule( $rule ) {
1206 if ( ! isset( $rule['trigger_on'] ) ) {
1207 return false;
1208 }
1209
1210 if ( 'product' === $rule['trigger_on'] && empty( $rule['product_ids'] ) ) {
1211 return false;
1212 }
1213
1214 if ( 'category' === $rule['trigger_on'] && empty( $rule['category_slugs'] ) ) {
1215 return false;
1216 }
1217
1218 if ( 'tags' === $rule['trigger_on'] && empty( $rule['tag_slugs'] ) ) {
1219 return false;
1220 }
1221
1222 if ( isset( $rule['discount_toggle'] ) && $rule['discount_toggle'] === true ) {
1223 if ( ! isset( $rule['discount_type'] ) ) {
1224 return false;
1225 }
1226 if ( ! isset( $rule['discount_amount'] ) ) {
1227 return false;
1228 }
1229 }
1230
1231 if ( isset( $rule['partial_payment_toggle'] ) && $rule['partial_payment_toggle'] === true ) {
1232 if ( ! isset( $rule['partial_payment_type'] ) ) {
1233 return false;
1234 }
1235 if ( ! isset( $rule['partial_payment_amount'] ) ) {
1236 return false;
1237 }
1238 }
1239
1240 if ( ! isset( $rule['user_condition'] ) ) {
1241 return false;
1242 }
1243
1244 if ( ( 'customers' === $rule['user_condition'] ) && empty( $rule['user_condition_users'] ) ) {
1245 return false;
1246 }
1247
1248 if ( ( 'roles' === $rule['user_condition'] ) && empty( $rule['user_condition_roles'] ) ) {
1249 return false;
1250 }
1251
1252 if ( empty( $rule['shipping_date'] ) ) {
1253 return false;
1254 }
1255
1256 if ( empty( $rule['button_text'] ) ) {
1257 return false;
1258 }
1259
1260 if ( empty( $rule['placement'] ) ) {
1261 return false;
1262 }
1263
1264 return true;
1265 }
1266
1267 /**
1268 * Prepare the rule fields.
1269 *
1270 * @param array $rule The rule to prepare.
1271 *
1272 * @return array The prepared rule.
1273 */
1274 private static function prepare_rule( $rule ) {
1275 if ( 'product' === $rule['trigger_on'] ) {
1276 $rule['product_ids'] = array_map( 'intval', explode( ',', $rule['product_ids'] ) );
1277 }
1278
1279 if ( ! empty( $rule['pre_order_start'] ) ) {
1280 $rule['pre_order_start'] = merchant_convert_date_to_timestamp( $rule['pre_order_start'], self::DATE_TIME_FORMAT );
1281 }
1282
1283 if ( ! empty( $rule['pre_order_end'] ) ) {
1284 $rule['pre_order_end'] = merchant_convert_date_to_timestamp( $rule['pre_order_end'], self::DATE_TIME_FORMAT );
1285 }
1286
1287 $rule['shipping_timestamp'] = merchant_convert_date_to_timestamp( $rule['shipping_date'], self::DATE_TIME_FORMAT );
1288
1289 return $rule;
1290 }
1291
1292 /**
1293 * Get the available product rule.
1294 *
1295 * @param string $product_id The product ID.
1296 *
1297 * @return array The available product rule.
1298 */
1299 public static function available_product_rule( $product_id ) {
1300 $available_rule = array();
1301 $rules = self::pre_order_rules();
1302 $current_time = merchant_get_current_timestamp();
1303 $current_user = wp_get_current_user();
1304 foreach ( $rules as $rule ) {
1305 if ( self::is_valid_rule( $rule ) ) {
1306 $rule = self::prepare_rule( $rule );
1307
1308 // check if pre-order start date is set and if it is not in the future
1309 if ( ! empty( $rule['pre_order_start'] ) && $rule['pre_order_start'] > $current_time ) {
1310 continue;
1311 }
1312
1313 // check if pre-order end date is set and if it is in the past
1314 if ( ! empty( $rule['pre_order_end'] ) && $rule['pre_order_end'] < $current_time ) {
1315 continue;
1316 }
1317
1318 if ( 'roles' === $rule['user_condition'] ) {
1319 $allowed_roles = $rule['user_condition_roles'];
1320 $user_roles = $current_user->roles;
1321 $intersect = array_intersect( $allowed_roles, $user_roles );
1322 if ( empty( $intersect ) ) {
1323 continue;
1324 }
1325 }
1326
1327 if ( 'customers' === $rule['user_condition'] ) {
1328 $allowed_users = $rule['user_condition_users'];
1329 $current_user_id = $current_user->ID;
1330 if ( ! in_array( $current_user_id, $allowed_users, true ) ) {
1331 continue;
1332 }
1333 }
1334
1335 $trigger = $rule['trigger_on'] ?? 'product';
1336
1337 $is_excluded = merchant_is_product_excluded( $product_id, $rule );
1338 if ( $is_excluded ) {
1339 continue;
1340 }
1341
1342 if ( 'product' === $trigger && in_array( $product_id, $rule['product_ids'], true ) ) {
1343 $available_rule = $rule;
1344 break;
1345 } elseif ( 'category' === $trigger || 'tags' === $trigger ) {
1346 $taxonomy = $trigger === 'category' ? 'product_cat' : 'product_tag';
1347 $slugs = $trigger === 'category' ? ( $rule['category_slugs'] ?? array() ) : ( $rule['tag_slugs'] ?? array() );
1348
1349 $terms = get_the_terms( $product_id, $taxonomy );
1350 if ( ! empty( $terms ) ) {
1351 foreach ( $terms as $term ) {
1352 if ( in_array( $term->slug, $slugs, true ) ) {
1353 $available_rule = $rule;
1354 break;
1355 }
1356 }
1357 }
1358 } elseif ( 'all' === $trigger ) {
1359 $available_rule = $rule;
1360 }
1361 }
1362 }
1363
1364 /**
1365 * Filter the available product rule.
1366 *
1367 * @param array $available_rule The available product rule.
1368 * @param int $product_id The product ID.
1369 *
1370 * @return array The available product rule.
1371 *
1372 * @since 1.9.9
1373 */
1374 return apply_filters( 'merchant_pre_order_available_rule', $available_rule, $product_id );
1375 }
1376
1377 /**
1378 * Migrate old data to the new module storage.
1379 *
1380 * @return void
1381 */
1382 public static function data_migration() {
1383 $args = array(
1384 'post_type' => array( 'product', 'product_variation' ),
1385 'posts_per_page' => - 1,
1386 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
1387 'meta_query' => array(
1388 'relation' => 'AND',
1389 array(
1390 'key' => '_pre_order_date',
1391 'compare' => 'EXISTS',
1392 ),
1393 array(
1394 'key' => '_pre_order_date',
1395 'compare' => '!=',
1396 'value' => '',
1397 ),
1398 array(
1399 'key' => '_is_pre_order',
1400 'compare' => 'EXISTS',
1401 ),
1402 array(
1403 'key' => '_is_pre_order',
1404 'compare' => 'in',
1405 'value' => array( 'yes', '1' ),
1406 ),
1407 ),
1408 );
1409 $migrated = get_option( 'merchant_pre_orders_migrated' );
1410 if ( ! $migrated ) {
1411 $products = new WP_Query( $args );
1412 if ( $products->have_posts() ) {
1413 $rules = self::pre_order_rules();
1414 while ( $products->have_posts() ) {
1415 $products->the_post();
1416 $product_id = get_the_ID();
1417 $pre_order_date = get_post_meta( $product_id, '_pre_order_date', true );
1418 $is_pre_order = get_post_meta( $product_id, '_is_pre_order', true );
1419 $pre_order_date = merchant_convert_date_to_timestamp( $pre_order_date, 'yy-m-d' );
1420 $pre_order_date = date_i18n( self::DATE_TIME_FORMAT, $pre_order_date );
1421 if ( $is_pre_order ) {
1422 $rules[] = array(
1423 'offer-title' => esc_html__( 'Custom Pre-order', 'merchant' ),
1424 'trigger_on' => 'product',
1425 'product_ids' => $product_id,
1426 'discount_toggle' => false,
1427 'user_condition' => 'all',
1428 'shipping_date' => $pre_order_date,
1429 'button_text' => esc_html__( 'Pre Order Now!', 'merchant' ),
1430 'additional_text' => esc_html__( 'Ships on {date}.', 'merchant' ),
1431 'placement' => 'before',
1432 'cart_label_text' => esc_html__( 'Ships on', 'merchant' ),
1433 'layout' => 'rule-details',
1434 );
1435 }
1436 }
1437 Merchant_Admin_Options::set( self::MODULE_ID, 'rules', $rules );
1438 }
1439 update_option( 'merchant_pre_orders_migrated', true );
1440 }
1441 }
1442 }
1443
1444 add_action( 'init', 'Merchant_Pre_Orders_Main_Functionality::data_migration' );
1445