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

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