PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 1.10.5
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v1.10.5
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.10.5, at inc/modules/pre-orders/class-pre-orders-main-functionality.php

1,419 lines 43.6 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 foreach ( $variations as $variation_id ) {
529 $variation = wc_get_product( $variation_id );
530 $variation_offer = self::available_product_rule( $variation_id );
531 $regular_price = $variation->get_regular_price();
532
533 if ( empty( $variation_offer ) ) {
534 $variation_offer = self::available_product_rule( $product->get_id() );
535
536 $is_excluded = merchant_is_product_excluded( $variation_id, $variation_offer );
537 if ( $is_excluded ) {
538 $prices[] = $regular_price;
539 continue;
540 }
541 }
542
543 if ( empty( $variation_offer ) ) {
544 if ( $variation->is_on_sale() ) {
545 $sale_price = wc_get_price_to_display( $variation );
546 } else {
547 $sale_price = $regular_price;
548 }
549 $prices[] = $sale_price;
550 continue;
551 }
552 $sale_price = $this->calculate_discounted_price( $regular_price, $variation_offer, $variation );
553 if ( $sale_price <= 0 ) {
554 // If the price is less than 0, set it to the regular/sale price
555 if ( $variation->is_on_sale() ) {
556 $sale_price = wc_get_price_to_display( $variation );
557 } else {
558 $sale_price = $regular_price;
559 }
560 }
561 $prices[] = $sale_price;
562 }
563
564 $min_price = min( $prices );
565 $max_price = max( $prices );
566 if ( $min_price !== $max_price ) {
567 return wc_format_price_range( $min_price, $max_price );
568 }
569
570 return wc_price( $min_price );
571 }
572
573 /**
574 * Simple product price html.
575 *
576 * @param WC_Product $product The product object.
577 * @param array $offer The offer details.
578 *
579 * @return string The price html.
580 */
581 private function simple_product_price_html( $product, $offer, $html_price ) {
582 $sale = self::get_rule_sale( $offer );
583 if ( ! $sale ) {
584 return $html_price;
585 }
586 $regular_price = wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) );
587 $discounted_price = $this->calculate_discounted_price( $regular_price, $offer, $product );
588
589 return wc_format_sale_price( $regular_price, $discounted_price );
590 }
591
592 /**
593 * Calculate the offer discount for certain price.
594 *
595 * @param float $price The price.
596 * @param array $offer The offer.
597 * @param WC_Product $product The product object (only supplied to the filter).
598 *
599 * @return float The discounted price.
600 */
601 public function calculate_discounted_price( $price, $offer, $product = null ) {
602 $sale = self::get_rule_sale( $offer );
603 $discount_type = $discount_value = '';
604 if ( $sale ) {
605 $discount_type = $offer['discount_type'];
606 $discount_value = $offer['discount_amount'];
607 if ( 'percentage' === $discount_type ) {
608 $price = (float) $price - ( (float) $price * ( (float) $discount_value / 100 ) );
609 } else {
610 $price = (float) $price - (float) $discount_value;
611 }
612 }
613
614 /**
615 * Filter the discounted price.
616 *
617 * @param float $price The price.
618 * @param array $offer The offer.
619 * @param string $discount_type The discount type.
620 * @param float $discount_value The discount value.
621 * @param WC_Product $product The product object.
622 *
623 * @since 1.9.5
624 */
625 return apply_filters(
626 'merchant_pre_order_sale_calculate_discounted_price',
627 $price,
628 $offer,
629 $discount_type,
630 $discount_value,
631 $product
632 );
633 }
634
635 /**
636 * Set the discounted cart price for the product.
637 *
638 * @param $cart_object array The cart object.
639 *
640 * @return void
641 */
642 public function dynamic_discount_cart_price() {
643 foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
644 /**
645 * Disable the cart price change.
646 *
647 * @param bool $disable_cart_price The cart price change status.
648 * @param array $cart_item The cart item.
649 * @param string $cart_item_key The cart item key.
650 *
651 * @since 1.9.7
652 */
653 if ( apply_filters( 'merchant_pre_order_sale_disable_cart_price', false, $cart_item, $cart_item_key ) ) {
654 continue;
655 }
656 $product_id = $cart_item['data']->get_id();
657 $product = wc_get_product( $product_id );
658
659 if ( ( ! is_admin() || wp_doing_ajax() ) && in_array( $product->get_type(), $this->allowed_product_types(), true ) ) {
660 $offer = self::available_product_rule( $product->get_id() );
661
662 if ( $product->is_type( 'variable' ) ) {
663 $product_id = $cart_item['variation_id'];
664 $product = wc_get_product( $product_id );
665 // check if the variation has an offer
666 $offer = self::available_product_rule( $product_id );
667 }
668
669 if ( empty( $offer ) && $product->is_type( 'variation' ) ) {
670 $offer = self::available_product_rule( $product->get_parent_id() );
671
672 $is_excluded = merchant_is_product_excluded( $product_id, $offer );
673 if ( $is_excluded ) {
674 continue;
675 }
676 }
677
678 if ( empty( $offer ) ) {
679 continue;
680 }
681 if ( ! isset( $offer['discount_toggle'] ) ) {
682 continue;
683 }
684 $regular_price = $product->get_regular_price();
685 $sale_price = $this->calculate_discounted_price( $regular_price, $offer, $product );
686 $cart_item['data']->set_price( $sale_price );
687 $cart_item['data']->set_meta_data( array( '_merchant_pre_order_sale' => $offer ) );
688 }
689 }
690 }
691
692 /**
693 * Validation (one type only).
694 *
695 * @param boolean $passed
696 * @param integer $product_id
697 *
698 * @return boolean
699 */
700 public function allow_one_type_only( $passed, $product_id ) {
701 $products = array_filter( WC()->cart->get_cart_contents() );
702 $pre_orders = 0;
703 $has_pre_orders = false;
704 foreach ( $products as $product ) {
705 $is_pre_order = $this->is_pre_order( $product['data']->get_id() );
706 if ( $is_pre_order ) {
707 ++ $pre_orders;
708 }
709 }
710 if ( $pre_orders ) {
711 $has_pre_orders = true;
712 }
713 $input_post_data = array(
714 'variation_id' => filter_input( INPUT_POST, 'variation_id', FILTER_SANITIZE_NUMBER_INT ),
715 );
716
717 $variable_id = ( isset( $input_post_data['variation_id'] ) ) ? sanitize_text_field( wp_unslash( $input_post_data['variation_id'] ) ) : 0;
718 $is_variable_has_pre_order = $this->is_pre_order( $product_id ) || $this->is_pre_order( $variable_id );
719
720 if ( empty( $products ) || ( $is_variable_has_pre_order && $has_pre_orders ) || ( false === $is_variable_has_pre_order && false === $has_pre_orders ) ) {
721 $passed = true;
722 } else {
723 $mode = Merchant_Admin_Options::get( self::MODULE_ID, 'modes', 'unified_order' );
724 if ( 'only_pre_orders' === $mode ) {
725 $passed = false;
726 if ( $is_variable_has_pre_order ) {
727 $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.',
728 'merchant' );
729 } else {
730 $notice = esc_html__( 'We detected that your cart has pre-order products. Please remove them before being able to add this product.', 'merchant' );
731 }
732 wc_add_notice( $notice, 'error' );
733 }
734 }
735
736 return $passed;
737 }
738
739 /**
740 * Check if product is a pre order item by product ID.
741 *
742 * @param integer $product_id
743 *
744 * @return boolean
745 */
746 public function is_pre_order( $product_id ) {
747 $available_pre_order = self::available_product_rule( $product_id );
748
749 return ! empty( $available_pre_order );
750 }
751
752 /**
753 * Check for pre orders and maybe update the status.
754 *
755 * @return void
756 */
757 public function check_for_pre_orders_and_maybe_update_status() {
758 $args = array(
759 'status' => 'wc-pre-ordered',
760 );
761
762 $pre_ordered_orders = wc_get_orders( $args );
763
764 foreach ( $pre_ordered_orders as $order ) {
765 $pre_order_date = $order->get_meta( '_merchant_order_pre_order_shipping_date' );
766
767 if ( $pre_order_date < time() ) {
768 $parent_order_id = $order->get_parent_id();
769
770 if ( 0 !== $parent_order_id ) {
771 $parent_order = wc_get_order( $parent_order_id );
772
773 if ( $parent_order->get_status() === 'completed' ) {
774 $order->update_status( 'processing', '[Merchant Pre Orders] ' );
775 }
776 } elseif ( $order->payment_complete() ) {
777 $order->update_status( 'processing', '[Merchant Pre Orders] ' );
778 }
779 }
780 }
781 }
782
783 /**
784 * Change pre-order button text.
785 *
786 * @param string $text
787 * @param WC_Product $product
788 *
789 * @return string
790 */
791 public function change_button_text( $text, $product ) {
792 if ( $product && $this->is_pre_order( $product->get_id() ) ) {
793 $pre_order_rule = self::available_product_rule( $product->get_id() );
794 $text = $pre_order_rule['button_text'] ? Merchant_Translator::translate( $pre_order_rule['button_text'] ) : esc_html__( 'Pre Order Now!', 'merchant' );
795 }
796
797 return $text;
798 }
799
800 /**
801 * Change pre-order button text for variable products.
802 *
803 * @param array $data
804 * @param object $product
805 * @param object $variation
806 *
807 * @return array
808 */
809 public function change_button_text_for_variable_products( $data, $product, $variation ) {
810 if ( $this->is_pre_order( $variation->get_id() ) ) {
811 $pre_order_rule = self::available_product_rule( $variation->get_id() );
812 $data['is_pre_order'] = true;
813
814 $additional_text = $pre_order_rule['additional_text'] ? Merchant_Translator::translate( $pre_order_rule['additional_text'] )
815 : esc_html__( 'Ships on {date}.', 'merchant' );
816 $time_format = date_i18n( get_option( 'date_format' ), $pre_order_rule['shipping_timestamp'] );
817 $text = $this->replace_date_text( $additional_text, $time_format );
818
819 if ( ! empty( $text ) ) {
820 $data['is_pre_order_date'] = $this->replace_date_text( $additional_text, $time_format );
821 }
822 }
823
824 return $data;
825 }
826
827 /**
828 * Replace {date} markup with new text.
829 *
830 * @param string $string_contains_date_tag
831 * @param string $time_format
832 *
833 * @return string
834 */
835 public function replace_date_text( $string_contains_date_tag, $time_format ) {
836 $from = array( '{date}' );
837 $to = array( $time_format );
838
839 return str_replace( $from, $to, $string_contains_date_tag );
840 }
841
842 /**
843 * Display pre order additional information before add to cart form.
844 *
845 * @return void
846 */
847 public function additional_information_before_cart_form() {
848 $input_post_data = array(
849 'product_id' => filter_input( INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT ),
850 );
851
852 global $post, $product;
853
854 // Do not override globals.
855 $_post = $post;
856 $_product = $product;
857
858 // In some cases the $post might be null. e.g inside quick view popup.
859 if ( ! $_post && isset( $input_post_data['product_id'] ) ) {
860 $_post = get_post( absint( $input_post_data['product_id'] ) );
861 $_product = wc_get_product( $_post->ID );
862 }
863 if ( $this->is_pre_order( $_post->ID ) ) {
864 $pre_order_rule = self::available_product_rule( $_product->get_id() );
865 if ( ! empty( $pre_order_rule ) && $pre_order_rule['placement'] === 'before' ) {
866 $this->maybe_render_additional_information( $pre_order_rule );
867 }
868 }
869 }
870
871 /**
872 * Display pre order additional information after add to cart form.
873 *
874 * @return void
875 */
876 public function additional_information_after_cart_form() {
877 $input_post_data = array(
878 'product_id' => filter_input( INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT ),
879 );
880
881 global $post, $product;
882
883 // Do not override globals.
884 $_post = $post;
885 $_product = $product;
886
887 // In some cases the $post might be null. e.g inside quick view popup.
888 if ( ! $_post && isset( $input_post_data['product_id'] ) ) {
889 $_post = get_post( absint( $input_post_data['product_id'] ) );
890 $_product = wc_get_product( $_post->ID );
891 }
892 if ( $_product && $this->is_pre_order( $_post->ID ) ) {
893 $pre_order_rule = self::available_product_rule( $_product->get_id() );
894 if ( ! empty( $pre_order_rule ) && $pre_order_rule['placement'] === 'after' ) {
895 $this->maybe_render_additional_information( $pre_order_rule );
896 }
897 }
898 }
899
900 /**
901 * Maybe render additional information.
902 *
903 * @return void
904 */
905 public function maybe_render_additional_information( $rule ) {
906 if ( ! empty( $rule ) ) {
907 $additional_text = $rule['additional_text'] ? Merchant_Translator::translate( $rule['additional_text'] )
908 : esc_html__( 'Ships on {date}.', 'merchant' );
909 $time_format = date_i18n( get_option( 'date_format' ), $rule['shipping_timestamp'] );
910 $text = $this->replace_date_text( $additional_text, $time_format );
911
912 printf( '<div class="merchant-pre-orders-date">%s</div>', esc_html( $text ) );
913 }
914 }
915
916 /**
917 * Register pre orders status.
918 *
919 * @return void
920 */
921 public function register_pre_orders_order_status() {
922 register_post_status( 'wc-pre-ordered', array(
923 'label' => esc_html__( 'Pre Ordered', 'merchant' ),
924 'public' => true,
925 'show_in_admin_status_list' => true,
926 'show_in_admin_all_list' => true,
927 'exclude_from_search' => false,
928 /* translators: %s: pre ordered product count */
929 'label_count' => _n_noop( 'Pre Ordered <span class="count">(%s)</span>', 'Pre Ordered <span class="count">(%s)</span>', 'merchant' ),
930 ) );
931 }
932
933 /**
934 * Add pre orders status to order statuses.
935 *
936 * @param array $order_statuses
937 *
938 * @return array
939 */
940 public function add_pre_orders_order_statuses( $order_statuses ) {
941 $order_statuses['wc-pre-ordered'] = esc_html__( 'Pre-Ordered', 'merchant' );
942
943 return $order_statuses;
944 }
945
946 /**
947 * Add pre orders class identifies to the body tag.
948 *
949 */
950 public function pre_orders_post_class( $classes, $product ) {
951 if ( $this->is_pre_order( $product->get_id() ) ) {
952 $classes[] = 'merchant-pre-ordered-product';
953 }
954
955 return $classes;
956 }
957
958 /**
959 * Filters cart item data to display cart.
960 *
961 * @param array $item_data Cart item data.
962 * @param array $cart_item Cart item array.
963 *
964 * @return array
965 *
966 * @see wc_get_formatted_cart_item_data for filter usage.
967 */
968 public function cart_message_handler( $item_data, $cart_item ) {
969 $product_id = $cart_item['product_id'];
970 if ( $cart_item['data']->is_type( 'variation' ) ) {
971 $product_id = $cart_item['variation_id'];
972 }
973 if ( $this->is_pre_order( $product_id ) ) {
974 $pre_order_rule = self::available_product_rule( $product_id );
975 if ( $pre_order_rule ) {
976 $label_text = $pre_order_rule['cart_label_text'] ? Merchant_Translator::translate( $pre_order_rule['cart_label_text'] ) : esc_html__( 'Ships on', 'merchant' );
977 $pre_order_date = date_i18n( get_option( 'date_format' ), $pre_order_rule['shipping_timestamp'] );
978
979 $item_data[] = array(
980 'key' => $label_text,
981 'value' => $pre_order_date,
982 'display' => '',
983 );
984 }
985 }
986
987 return $item_data;
988 }
989
990 /**
991 * filter name: woocommerce_order_item_meta_end
992 * Adds pre order additional text to the order item name
993 *
994 * @param int $item_id Product ID.
995 * @param \WC_Order_Item_Product $item Item array data.
996 * @param \WC_Order $order Order data.
997 * @param bool $plain_text Is plain text or not.
998 *
999 * @return string
1000 */
1001 public function order_item_meta_end( $item_id, $item, $order, $plain_text ) {
1002 echo $this->get_pre_order_text( $item->get_product()->get_id(), $plain_text ? '' : 'dl' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1003 }
1004
1005 /**
1006 * Render pre order text.
1007 */
1008 public function shop_loop_item_title() {
1009 echo $this->get_pre_order_text( get_the_ID(), 'span' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1010 }
1011
1012 /**
1013 * Add the_title filter for block rendering.
1014 *
1015 * @param array $context Default context.
1016 *
1017 * @return array
1018 */
1019 public function add_block_title_filter( $context ) {
1020 if ( ! empty( $context['postType'] ) && 'product' === $context['postType'] && ! $this->is_pre_order_filter_on ) {
1021 $this->is_pre_order_filter_on = true;
1022 add_filter( 'the_title', array( $this, 'block_add_the_title_filter' ), 10, 2 );
1023 add_filter( 'render_block', array( $this, 'block_remove_the_title_filter' ), 10, 3 );
1024 }
1025
1026 return $context;
1027 }
1028
1029 /**
1030 * Add pre order info to product grid block.
1031 *
1032 * @param string $html Product grid item HTML.
1033 * @param object $data Product data passed to the template.
1034 * @param \WC_Product $product Product object.
1035 *
1036 * @return string Updated product grid item HTML.
1037 */
1038 public function override_product_grid_block( $html, $data, $product ) {
1039 $pre_order_text = $this->get_pre_order_text( $product->get_id(), 'span' );
1040 if ( $pre_order_text ) {
1041 $html = str_replace( $data->title, $data->title . $pre_order_text, $html );
1042 }
1043
1044 return $html;
1045 }
1046
1047 /**
1048 * Adds pre order info to post_title.
1049 *
1050 * @param string $title Title.
1051 * @param int $post_id Post ID.
1052 *
1053 * @return string
1054 */
1055 public function block_add_the_title_filter( $title, $post_id ) {
1056 return $title . $this->get_pre_order_text( $post_id, 'span' );
1057 }
1058
1059 /**
1060 * Remove the_title filter when block rendering finished.
1061 *
1062 * @param string $block_content The block content.
1063 * @param array $parsed_block The full block, including name and attributes.
1064 * @param WP_Block $block The block instance.
1065 *
1066 * @return string
1067 */
1068 public function block_remove_the_title_filter( $block_content, $parsed_block, $block ) {
1069 if ( $this->is_pre_order_filter_on ) {
1070 remove_filter( 'the_title', array( $this, 'block_add_the_title_filter' ), 10 );
1071 $this->is_pre_order_filter_on = false;
1072 }
1073
1074 return $block_content;
1075 }
1076
1077 /**
1078 * Get pre order text by product ID.
1079 *
1080 * @param int $product_id
1081 * @param string $render_type Render template type string. 'span', 'dl', ''. Default ''.
1082 *
1083 * @return string
1084 */
1085 private function get_pre_order_text( $product_id, $render_type = '' ) {
1086 if ( ! $this->is_pre_order( $product_id ) ) {
1087 return '';
1088 }
1089
1090 $pre_order_rule = self::available_product_rule( $product_id );
1091 $label_text = $pre_order_rule['cart_label_text'] ? Merchant_Translator::translate( $pre_order_rule['cart_label_text'] ) : esc_html__( 'Ships on', 'merchant' );
1092 $pre_order_date = date_i18n( get_option( 'date_format' ), $pre_order_rule['shipping_timestamp'] );
1093 if ( 'span' === $render_type ) {
1094 return sprintf(
1095 '<span class="merchant-pre-orders-note"><span class="merchant-pre-orders-label">%s:</span><span>%s</span></span>',
1096 esc_html( $label_text ),
1097 $pre_order_date
1098 );
1099 } elseif ( 'dl' === $render_type ) {
1100 return sprintf(
1101 '<dl class="merchant-pre-orders-note"><dt>%s:</dt><dd>%s</dd></dl>',
1102 esc_html( $label_text ),
1103 $pre_order_date
1104 );
1105 } else {
1106 return sprintf(
1107 '%s: %s',
1108 esc_html( $label_text ),
1109 $pre_order_date
1110 );
1111 }
1112 }
1113
1114 /**
1115 * Get the product types that are allowed to have storewide sale.
1116 *
1117 * @return array The allowed product types.
1118 */
1119 public function allowed_product_types() {
1120 /**
1121 * Filter the product types that are allowed to have storewide sale.
1122 *
1123 * @param array $product_types
1124 *
1125 * @since 1.9.5
1126 */
1127 return apply_filters( 'merchant_pre_order_allowed_product_types', array(
1128 'simple',
1129 'variation',
1130 'variable',
1131 ) );
1132 }
1133
1134 /**
1135 * Get the pre order rules.
1136 *
1137 * @param array $rule The rule to get.
1138 *
1139 * @return array|false The pre order rules or false if there are no rule sale.
1140 */
1141 private static function get_rule_sale( $rule ) {
1142 $sale = false;
1143 if ( isset( $rule['discount_toggle'] ) && $rule['discount_toggle'] ) {
1144 $discount_type = $rule['discount_type'];
1145 $discount_amount = $rule['discount_amount'];
1146
1147 $sale = array(
1148 'discount_type' => $discount_type,
1149 'discount_amount' => $discount_amount,
1150 );
1151 }
1152
1153 /**
1154 * Filter the pre order sale.
1155 *
1156 * @param array $sale The pre order sale.
1157 * @param array $rule The pre order rule.
1158 *
1159 * @since 1.9.9
1160 */
1161 return apply_filters( 'merchant_pre_order_rule_sale', $sale, $rule );
1162 }
1163
1164 /**
1165 * Get the pre order rules.
1166 *
1167 * @return array The pre order rules.
1168 */
1169 private static function pre_order_rules() {
1170 return Merchant_Admin_Options::get( self::MODULE_ID, 'rules', array() );
1171 }
1172
1173 /**
1174 * Check if the rule is valid.
1175 *
1176 * @param array $rule The rule to check.
1177 *
1178 * @return boolean True if the rule is valid, false otherwise.
1179 */
1180 private static function is_valid_rule( $rule ) {
1181 if ( ! isset( $rule['trigger_on'] ) ) {
1182 return false;
1183 }
1184
1185 if ( 'product' === $rule['trigger_on'] && empty( $rule['product_ids'] ) ) {
1186 return false;
1187 }
1188
1189 if ( 'category' === $rule['trigger_on'] && empty( $rule['category_slugs'] ) ) {
1190 return false;
1191 }
1192
1193 if ( 'tags' === $rule['trigger_on'] && empty( $rule['tag_slugs'] ) ) {
1194 return false;
1195 }
1196
1197 if ( isset( $rule['discount_toggle'] ) && $rule['discount_toggle'] === true ) {
1198 if ( ! isset( $rule['discount_type'] ) ) {
1199 return false;
1200 }
1201 if ( ! isset( $rule['discount_amount'] ) ) {
1202 return false;
1203 }
1204 }
1205
1206 if ( isset( $rule['partial_payment_toggle'] ) && $rule['partial_payment_toggle'] === true ) {
1207 if ( ! isset( $rule['partial_payment_type'] ) ) {
1208 return false;
1209 }
1210 if ( ! isset( $rule['partial_payment_amount'] ) ) {
1211 return false;
1212 }
1213 }
1214
1215 if ( ! isset( $rule['user_condition'] ) ) {
1216 return false;
1217 }
1218
1219 if ( ( 'customers' === $rule['user_condition'] ) && empty( $rule['user_condition_users'] ) ) {
1220 return false;
1221 }
1222
1223 if ( ( 'roles' === $rule['user_condition'] ) && empty( $rule['user_condition_roles'] ) ) {
1224 return false;
1225 }
1226
1227 if ( empty( $rule['shipping_date'] ) ) {
1228 return false;
1229 }
1230
1231 if ( empty( $rule['button_text'] ) ) {
1232 return false;
1233 }
1234
1235 if ( empty( $rule['placement'] ) ) {
1236 return false;
1237 }
1238
1239 return true;
1240 }
1241
1242 /**
1243 * Prepare the rule fields.
1244 *
1245 * @param array $rule The rule to prepare.
1246 *
1247 * @return array The prepared rule.
1248 */
1249 private static function prepare_rule( $rule ) {
1250 if ( 'product' === $rule['trigger_on'] ) {
1251 $rule['product_ids'] = array_map( 'intval', explode( ',', $rule['product_ids'] ) );
1252 }
1253
1254 if ( ! empty( $rule['pre_order_start'] ) ) {
1255 $rule['pre_order_start'] = merchant_convert_date_to_timestamp( $rule['pre_order_start'], self::DATE_TIME_FORMAT );
1256 }
1257
1258 if ( ! empty( $rule['pre_order_end'] ) ) {
1259 $rule['pre_order_end'] = merchant_convert_date_to_timestamp( $rule['pre_order_end'], self::DATE_TIME_FORMAT );
1260 }
1261
1262 $rule['shipping_timestamp'] = merchant_convert_date_to_timestamp( $rule['shipping_date'], self::DATE_TIME_FORMAT );
1263
1264 return $rule;
1265 }
1266
1267 /**
1268 * Get the available product rule.
1269 *
1270 * @param string $product_id The product ID.
1271 *
1272 * @return array The available product rule.
1273 */
1274 public static function available_product_rule( $product_id ) {
1275 $available_rule = array();
1276 $rules = self::pre_order_rules();
1277 $current_time = merchant_get_current_timestamp();
1278 $current_user = wp_get_current_user();
1279 foreach ( $rules as $rule ) {
1280 if ( self::is_valid_rule( $rule ) ) {
1281 $rule = self::prepare_rule( $rule );
1282
1283 // check if pre-order start date is set and if it is not in the future
1284 if ( ! empty( $rule['pre_order_start'] ) && $rule['pre_order_start'] > $current_time ) {
1285 continue;
1286 }
1287
1288 // check if pre-order end date is set and if it is in the past
1289 if ( ! empty( $rule['pre_order_end'] ) && $rule['pre_order_end'] < $current_time ) {
1290 continue;
1291 }
1292
1293 if ( 'roles' === $rule['user_condition'] ) {
1294 $allowed_roles = $rule['user_condition_roles'];
1295 $user_roles = $current_user->roles;
1296 $intersect = array_intersect( $allowed_roles, $user_roles );
1297 if ( empty( $intersect ) ) {
1298 continue;
1299 }
1300 }
1301
1302 if ( 'customers' === $rule['user_condition'] ) {
1303 $allowed_users = $rule['user_condition_users'];
1304 $current_user_id = $current_user->ID;
1305 if ( ! in_array( $current_user_id, $allowed_users, true ) ) {
1306 continue;
1307 }
1308 }
1309
1310 $trigger = $rule['trigger_on'] ?? 'product';
1311
1312 $is_excluded = merchant_is_product_excluded( $product_id, $rule );
1313 if ( $is_excluded ) {
1314 continue;
1315 }
1316
1317 if ( 'product' === $trigger && in_array( $product_id, $rule['product_ids'], true ) ) {
1318 $available_rule = $rule;
1319 break;
1320 } elseif ( 'category' === $trigger || 'tags' === $trigger ) {
1321 $taxonomy = $trigger === 'category' ? 'product_cat' : 'product_tag';
1322 $slugs = $trigger === 'category' ? ( $rule['category_slugs'] ?? array() ) : ( $rule['tag_slugs'] ?? array() );
1323
1324 $terms = get_the_terms( $product_id, $taxonomy );
1325 if ( ! empty( $terms ) ) {
1326 foreach ( $terms as $term ) {
1327 if ( in_array( $term->slug, $slugs, true ) ) {
1328 $available_rule = $rule;
1329 break;
1330 }
1331 }
1332 }
1333 } elseif ( 'all' === $trigger ) {
1334 $available_rule = $rule;
1335 }
1336 }
1337 }
1338
1339 /**
1340 * Filter the available product rule.
1341 *
1342 * @param array $available_rule The available product rule.
1343 * @param int $product_id The product ID.
1344 *
1345 * @return array The available product rule.
1346 *
1347 * @since 1.9.9
1348 */
1349 return apply_filters( 'merchant_pre_order_available_rule', $available_rule, $product_id );
1350 }
1351
1352 /**
1353 * Migrate old data to the new module storage.
1354 *
1355 * @return void
1356 */
1357 public static function data_migration() {
1358 $args = array(
1359 'post_type' => array( 'product', 'product_variation' ),
1360 'posts_per_page' => - 1,
1361 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
1362 'meta_query' => array(
1363 'relation' => 'AND',
1364 array(
1365 'key' => '_pre_order_date',
1366 'compare' => 'EXISTS',
1367 ),
1368 array(
1369 'key' => '_pre_order_date',
1370 'compare' => '!=',
1371 'value' => '',
1372 ),
1373 array(
1374 'key' => '_is_pre_order',
1375 'compare' => 'EXISTS',
1376 ),
1377 array(
1378 'key' => '_is_pre_order',
1379 'compare' => 'in',
1380 'value' => array( 'yes', '1' ),
1381 ),
1382 ),
1383 );
1384 $migrated = get_option( 'merchant_pre_orders_migrated' );
1385 if ( ! $migrated ) {
1386 $products = new WP_Query( $args );
1387 if ( $products->have_posts() ) {
1388 $rules = self::pre_order_rules();
1389 while ( $products->have_posts() ) {
1390 $products->the_post();
1391 $product_id = get_the_ID();
1392 $pre_order_date = get_post_meta( $product_id, '_pre_order_date', true );
1393 $is_pre_order = get_post_meta( $product_id, '_is_pre_order', true );
1394 $pre_order_date = merchant_convert_date_to_timestamp( $pre_order_date, 'yy-m-d' );
1395 $pre_order_date = date_i18n( self::DATE_TIME_FORMAT, $pre_order_date );
1396 if ( $is_pre_order ) {
1397 $rules[] = array(
1398 'offer-title' => esc_html__( 'Custom Pre-order', 'merchant' ),
1399 'trigger_on' => 'product',
1400 'product_ids' => $product_id,
1401 'discount_toggle' => false,
1402 'user_condition' => 'all',
1403 'shipping_date' => $pre_order_date,
1404 'button_text' => esc_html__( 'Pre Order Now!', 'merchant' ),
1405 'additional_text' => esc_html__( 'Ships on {date}.', 'merchant' ),
1406 'placement' => 'before',
1407 'cart_label_text' => esc_html__( 'Ships on', 'merchant' ),
1408 'layout' => 'rule-details',
1409 );
1410 }
1411 }
1412 Merchant_Admin_Options::set( self::MODULE_ID, 'rules', $rules );
1413 }
1414 update_option( 'merchant_pre_orders_migrated', true );
1415 }
1416 }
1417 }
1418
1419 add_action( 'init', 'Merchant_Pre_Orders_Main_Functionality::data_migration' );