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

536 lines 17.4 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 * Pre order products.
21 *
22 */
23 private $pre_order_products = [];
24
25 /**
26 * Init.
27 *
28 * @return void
29 */
30 public function init() {
31 add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'allow_one_type_only' ), 99, 2 );
32
33 add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'manage_pre_orders' ), 10, 2 );
34 add_filter( 'woocommerce_thankyou', array( $this, 'set_pre_order_status'), 10 );
35 add_filter( 'woocommerce_billing_fields', array( $this, 'add_shipping_date_field' ) );
36
37 // Cronjob.
38 if ( ! wp_next_scheduled( 'check_for_released_preorders' ) ) {
39 wp_schedule_event( time(), 'twicedaily', 'check_for_released_preorders' );
40 }
41
42 add_action( 'check_for_released_preorders', array( $this, 'check_for_pre_orders_and_maybe_update_status' ) );
43
44 // Variations tab
45 add_action( 'woocommerce_product_after_variable_attributes', array( $this, 'custom_fields_for_variable_products' ), 10, 3 );
46
47 // Inventory tab
48 add_action( 'woocommerce_product_options_stock_status', array( $this, 'custom_fields_for_simple_products' ) );
49
50 add_action( 'woocommerce_save_product_variation', array( $this, 'custom_fields_for_variable_products_save' ), 10, 2 );
51 add_action( 'woocommerce_process_product_meta', array( $this, 'custom_fields_for_simple_products_save' ), 10, 2 );
52
53 add_filter( 'woocommerce_product_add_to_cart_text', array( $this, 'change_button_text' ), 10, 2 );
54 add_filter( 'woocommerce_product_single_add_to_cart_text', array( $this, 'change_button_text' ), 10, 2 );
55 add_filter( 'woocommerce_available_variation', array( $this, 'change_button_text_for_variable_products' ), 10, 3 );
56 add_action( 'woocommerce_before_add_to_cart_form', array( $this, 'maybe_render_additional_information' ), 10 );
57
58 $this->register_pre_orders_order_status();
59 add_filter( 'wc_order_statuses', array( $this, 'add_pre_orders_order_statuses' ) );
60 add_filter( 'woocommerce_post_class', array( $this, 'pre_orders_post_class' ), 10, 3 );
61 }
62
63 /**
64 * Validation (one type only).
65 *
66 * @param boolean $passed
67 * @param integer $product_id
68 * @return boolean
69 */
70 public function allow_one_type_only( $passed, $product_id ) {
71 $products = array_filter( WC()->cart->get_cart_contents() );
72 $has_pre_orders = false;
73
74 foreach ( $products as $product ) {
75 $isPreOrder = $this->isPreOrder( $product['data']->get_id() );
76 if ( $isPreOrder ) {
77 $has_pre_orders = true;
78 }
79 }
80
81 $input_post_data = array(
82 'variation_id' => filter_input(INPUT_POST, 'variation_id', FILTER_SANITIZE_NUMBER_INT)
83 );
84
85 $variableId = ( isset( $input_post_data['variation_id'] ) ) ? sanitize_text_field( wp_unslash( $input_post_data['variation_id'] ) ) : 0;
86 $is_variable_has_pre_order = $this->isPreOrder( $product_id, $variableId );
87
88 if ( empty( $products ) || ( $is_variable_has_pre_order && $has_pre_orders ) || ( false === $is_variable_has_pre_order && false === $has_pre_orders ) ) {
89 $passed = true;
90 } else {
91 $passed = false;
92 if ( $is_variable_has_pre_order ) {
93 $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.', 'merchant' );
94 } else {
95 $notice = esc_html__( 'We detected that your cart has pre-order products. Please remove them before being able to add this product.', 'merchant' );
96 }
97 wc_add_notice( $notice, 'error' );
98 }
99
100 return $passed;
101 }
102
103 /**
104 * Check if is pre order.
105 *
106 * @param integer $product_id
107 * @param integer $variableId
108 * @return boolean
109 */
110 public function isPreOrder( $product_id, $variableId = 0 ) {
111 if ( 'yes' === get_post_meta( $product_id, '_is_pre_order', true ) && new DateTime( get_post_meta( $product_id, '_pre_order_date', true ) ) > new DateTime() ) {
112 return true;
113 } elseif ( 'yes' === get_post_meta( $variableId, '_is_pre_order', true ) && new DateTime( get_post_meta( $variableId, '_pre_order_date', true ) ) > new DateTime() ) {
114 return true;
115 }
116
117 return false;
118 }
119
120 /**
121 * Update pre order meta data (date).
122 *
123 * @param integer $orderId
124 * @param array $data
125 * @return void
126 */
127 public function manage_pre_orders( $orderId, $data ) {
128 $order = wc_get_order( $orderId );
129
130 if ( isset( $data['preorder_date'] ) ) {
131 $order->update_meta_data( '_preorder_date', esc_attr( $data['preorder_date'] ) );
132 $order->save();
133 }
134 }
135
136 /**
137 * Set pre order status.
138 *
139 * @param string $status
140 * @param integer $order_id
141 * @param object $order
142 * @return string
143 */
144 public function set_pre_order_status( $order_id ) {
145 if ( ! $order_id ) {
146 return;
147 }
148
149 $order = wc_get_order($order_id);
150
151 // Change the order status.
152 if ( $order->get_meta( '_preorder_date' ) ) {
153 $order->update_status( 'wc-pre-ordered' );
154 }
155
156 // Save.
157 $order->save();
158 }
159
160 /**
161 * Add shipping date field.
162 *
163 * @param array $fields
164 * @return array
165 */
166 public function add_shipping_date_field( $fields ) {
167 if ( ! is_checkout() && ! is_cart() ) {
168 return $fields;
169 }
170
171 global $woocommerce;
172
173 $this->check_pre_order_products( $woocommerce->cart->get_cart() );
174
175 if ( count( $this->get_pre_order_products() ) > 0 ) {
176 $fields['preorder_date'] = [
177 'type' => 'hidden',
178 'class' => array( 'merchant-hidden' ),
179 'required' => true,
180 'default' => $this->get_oldest_date(),
181 ];
182 }
183
184 return $fields;
185 }
186
187 /**
188 * Check pre order products in cart.
189 *
190 * @param array $items
191 * @return void
192 */
193 public function check_pre_order_products( $items ) {
194 if ( isset( $items['line_items'] ) ) {
195 $items = $items['line_items'];
196 }
197
198 $pre_order_products = array_filter( $items, function ( $v ) {
199 return 'yes' === get_post_meta( $v['product_id'], '_is_pre_order', true ) && new DateTime( get_post_meta( $v['product_id'], '_pre_order_date', true ) ) > new DateTime() || 'yes' === get_post_meta( $v['variation_id'], '_is_pre_order', true ) && new DateTime( get_post_meta( $v['variation_id'], '_pre_order_date', true ) ) > new DateTime();
200 } );
201
202 $this->set_pre_order_products( $pre_order_products );
203 }
204
205 /**
206 * Get the oldest date.
207 *
208 * @return string
209 */
210 public function get_oldest_date() {
211 $product_with_oldest_date = array_reduce( $this->get_pre_order_products(), function ( $a, $b ) {
212 if ( null === $a ) {
213 return $b;
214 }
215 $aId = isset( $a['variation_id'] ) && 0 !== $a['variation_id'] ? $a['variation_id'] : $a['product_id'];
216 $bId = isset( $b['variation_id'] ) && 0 !== $b['variation_id'] ? $b['variation_id'] : $b['product_id'];
217 return $a ? ( strtotime( get_post_meta( $aId, '_pre_order_date', true ) ) > strtotime( get_post_meta( $bId, '_pre_order_date', true ) ) ? $a : $b ) : $b;
218 } );
219
220 $oldestId = isset( $product_with_oldest_date['variation_id'] ) && 0 !== $product_with_oldest_date['variation_id'] ? $product_with_oldest_date['variation_id'] : $product_with_oldest_date['product_id'];
221
222 return get_post_meta( $oldestId, '_pre_order_date', true );
223 }
224
225 /**
226 * Get pre order products.
227 *
228 * @return array
229 */
230 public function get_pre_order_products() {
231 return $this->pre_order_products;
232 }
233
234 /**
235 * Set pre order products.
236 *
237 * @param array $pre_order_products
238 * @return $this
239 */
240 public function set_pre_order_products( $pre_order_products ) {
241 $this->pre_order_products = $pre_order_products;
242 return $this;
243 }
244
245 /**
246 * Check for pre orders and maybe update the status.
247 *
248 * @return void
249 */
250 public function check_for_pre_orders_and_maybe_update_status() {
251 $args = [
252 'status' => 'wc-pre-ordered',
253 ];
254
255 $pre_ordered_orders = wc_get_orders( $args );
256
257 foreach ( $pre_ordered_orders as $order ) {
258 $pre_order_date = strtotime( $order->get_meta('_preorder_date') );
259
260 if ( $pre_order_date < time() ) {
261 $parent_order_id = $order->get_parent_id();
262
263 if ( 0 !== $parent_order_id ) {
264 $parent_order = wc_get_order( $parent_order_id );
265
266 if ( $parent_order->get_status() === 'completed' ) {
267 $order->update_status( 'wc-completed', '[WooCommerce Pre Orders] ' );
268 }
269 } else {
270 if ( $order->get_status() === 'wc-pre-ordered' && $order->payment_complete() ) {
271 $order->update_status( 'wc-completed', '[WooCommerce Pre Orders] ' );
272 }
273 }
274 }
275 }
276 }
277
278 /**
279 * Custom pre-order fields for variations.
280 *
281 * @param integer $loop
282 * @param array $variation_data
283 * @param object $variation
284 * @return void
285 */
286 public function custom_fields_for_variable_products( $loop, $variation_data, $variation ) {
287 echo '<div class="is_pre_order_meta_field form-row form-row-full" style="display: flex; align-items: center;">';
288 woocommerce_wp_checkbox(
289 [
290 'id' => '_is_pre_order_' . $variation->ID,
291 'label' => '&nbsp;' . esc_html__( 'Pre-Order Product - Set this product as pre-order', 'merchant' ),
292 'value' => get_post_meta( $variation->ID, '_is_pre_order', true ),
293 ]
294 );
295
296 echo wc_help_tip( __( 'Important: To pre-order out of stock products you must enable the \'Backorder\' stock option.', 'merchant' ) );
297 echo '</div>';
298 echo '<div class="form-row form-row-full">';
299 woocommerce_wp_text_input(
300 [
301 'type' => 'date',
302 'id' => '_pre_order_date_' . $variation->ID,
303 'label' => esc_html__( 'Pre-Order Shipping Date', 'merchant' ),
304 'value' => get_post_meta( $variation->ID, '_pre_order_date', true ),
305 ]
306 );
307 echo '</div>';
308 }
309
310 /**
311 * Custom pre-order fields for simple products.
312 *
313 * @return void
314 */
315 public function custom_fields_for_simple_products() {
316 echo '<div class="is_pre_order_meta_field form-row form-row-full hide_if_variable" style="display: flex; align-items: center;">';
317 woocommerce_wp_checkbox(
318 [
319 'id' => '_is_pre_order',
320 'label' => esc_html__( 'Pre-Order Product', 'merchant' ),
321 'description' => esc_html__( 'Set this product as pre-order', 'merchant' ),
322 'value' => get_post_meta( get_the_ID(), '_is_pre_order', true ),
323 ]
324 );
325
326 echo '<div style="margin-left: -20px">';
327 echo wc_help_tip( __( 'Important: To pre-order out of stock products you must enable the \'Backorder\' stock option.', 'merchant' ) );
328 echo '</div>';
329 echo '</div>';
330 echo '<div class="form-row form-row-full hide_if_variable">';
331 woocommerce_wp_text_input(
332 array(
333 'type' => 'date',
334 'id' => '_pre_order_date',
335 'label' => esc_html__( 'Pre-Order Shipping Date', 'merchant' ),
336 'value' => get_post_meta( get_the_ID(), '_pre_order_date', true ),
337 )
338 );
339 echo '</div>';
340
341 }
342
343 /**
344 * Save custom fields for variable products.
345 *
346 * @param integer $post_id
347 * @return void
348 */
349 public function custom_fields_for_variable_products_save( $post_id ) {
350 $input_post_data = array(
351 '_is_pre_order_by_post_id' => filter_input(INPUT_POST, '_is_pre_order_' . $post_id, FILTER_SANITIZE_FULL_SPECIAL_CHARS),
352 '_pre_order_data_by_post_id' => filter_input(INPUT_POST, '_pre_order_date_' . $post_id, FILTER_SANITIZE_FULL_SPECIAL_CHARS),
353 );
354
355 $product = wc_get_product( $post_id );
356
357 $is_pre_order_variation = isset( $input_post_data[ '_is_pre_order_by_post_id' ] ) ? 'yes' : 'no';
358 $product->update_meta_data( '_is_pre_order', $is_pre_order_variation );
359
360 if ( 'yes' === $is_pre_order_variation && isset( $input_post_data[ '_pre_order_data_by_post_id' ] ) ) {
361 $pre_order_date_value = sanitize_text_field( wp_unslash( $input_post_data[ '_pre_order_data_by_post_id' ] ) );
362 $product->update_meta_data( '_pre_order_date', $pre_order_date_value );
363 }
364
365 $product->save();
366 }
367
368 /**
369 * Save custom fields for simple products.
370 *
371 * @param integer $post_id
372 * @return void
373 */
374 public function custom_fields_for_simple_products_save( $post_id ) {
375 $input_post_data = array(
376 '_is_pre_order' => filter_input(INPUT_POST, '_is_pre_order', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
377 '_pre_order_date' => filter_input(INPUT_POST, '_pre_order_date', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
378 );
379
380 $product = wc_get_product( $post_id );
381 $is_pre_order = isset( $input_post_data['_is_pre_order'] ) ? 'yes' : 'no';
382 $product->update_meta_data( '_is_pre_order', $is_pre_order );
383
384 if ( 'yes' === $is_pre_order && isset( $input_post_data['_pre_order_date'] ) ) {
385 $pre_order_date_value = sanitize_text_field( wp_unslash( $input_post_data['_pre_order_date'] ) );
386 $product->update_meta_data( '_pre_order_date', esc_attr( $pre_order_date_value ) );
387 } else {
388 $product->update_meta_data( '_pre_order_date', '' );
389 }
390
391 $product->save();
392 }
393
394 /**
395 * Change pre-order button text.
396 *
397 * @param string $text
398 * @return string
399 */
400 public function change_button_text( $text ) {
401 $input_post_data = array(
402 'product_id' => filter_input(INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT),
403 );
404
405 global $post;
406 $_post = $post;
407
408 // In some cases the $post might be null. e.g inside quick view popup.
409 if ( ! $_post && isset( $input_post_data[ 'product_id' ] ) ) {
410 $_post = get_post( absint( $input_post_data[ 'product_id' ] ) );
411 }
412
413 if ( 'yes' === get_post_meta( $_post->ID, '_is_pre_order', true ) && strtotime( get_post_meta( $_post->ID, '_pre_order_date', true ) ) > time() ) {
414 $text = Merchant_Admin_Options::get( 'pre-orders', 'button_text', esc_html__( 'Pre Order Now!', 'merchant' ) );
415 }
416
417 return $text;
418 }
419
420 /**
421 * Change pre-order button text for variable products.
422 *
423 * @param array $data
424 * @param object $product
425 * @param object $variation
426 * @return array
427 */
428 public function change_button_text_for_variable_products( $data, $product, $variation ) {
429 global $product;
430
431 if ( get_post_meta( $variation->get_id(), '_is_pre_order', true ) === 'yes' && strtotime( get_post_meta( $variation->get_id(), '_pre_order_date', true ) ) > time() ) {
432
433 $data['is_pre_order'] = true;
434
435 $additional_text = Merchant_Admin_Options::get( 'pre-orders', 'additional_text', esc_html__( 'Ships on {date}.', 'merchant' ) );
436 $time_format = date_i18n( get_option( 'date_format' ), strtotime( get_post_meta( $variation->get_id(), '_pre_order_date', true ) ) );
437 $text = $this->replaceDateTxt( $additional_text, $time_format );
438
439 if ( ! empty( $text ) ) {
440 $data['is_pre_order_date'] = $this->replaceDateTxt( $additional_text, $time_format );
441 }
442
443 }
444
445 return $data;
446 }
447
448 /**
449 * Replace {date} markup with new text.
450 *
451 * @param string $string
452 * @param string $time_format
453 * @return string
454 */
455 public function replaceDateTxt( $string, $time_format ) {
456 $from = array( '{date}' );
457 $to = array( $time_format );
458
459 return str_replace( $from, $to, $string );
460 }
461
462 /**
463 * Maybe render additional information.
464 *
465 * @return void
466 */
467 public function maybe_render_additional_information() {
468 $input_post_data = array(
469 'product_id' => filter_input(INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT),
470 );
471
472 global $post, $product;
473
474 // Do not override globals.
475 $_post = $post;
476 $_product = $product;
477
478 // In some cases the $post might be null. e.g inside quick view popup.
479 if ( ! $_post && isset( $input_post_data[ 'product_id' ] ) ) {
480 $_post = get_post( absint( $input_post_data[ 'product_id' ] ) );
481 $_product = wc_get_product( $_post->ID );
482 }
483
484 if ( null !== $_product ) {
485 if ( 'yes' === get_post_meta( $_post->ID, '_is_pre_order', true ) && strtotime( get_post_meta( $_post->ID, '_pre_order_date', true ) ) > time() ) {
486 $additional_text = Merchant_Admin_Options::get( 'pre-orders', 'additional_text', esc_html__( 'Ships on {date}.', 'merchant' ) );
487 $time_format = date_i18n( get_option( 'date_format' ), strtotime( get_post_meta( $_post->ID, '_pre_order_date', true ) ) );
488 $text = $this->replaceDateTxt( $additional_text, $time_format );
489
490 echo sprintf( '<div class="merchant-pre-orders-date">%s</div>', esc_html( $text ) );
491 }
492 }
493 }
494
495 /**
496 * Register pre orders status.
497 *
498 * @return void
499 */
500 public function register_pre_orders_order_status() {
501 register_post_status( 'wc-pre-ordered', array(
502 'label' => esc_html__( 'Pre Ordered', 'merchant' ),
503 'public' => true,
504 'show_in_admin_status_list' => true,
505 'show_in_admin_all_list' => true,
506 'exclude_from_search' => false,
507 /* translators: %s: pre ordered product count */
508 'label_count' => _n_noop( 'Pre Ordered <span class="count">(%s)</span>', 'Pre Ordered <span class="count">(%s)</span>', 'merchant' )
509 ) );
510 }
511
512 /**
513 * Add pre orders status to order statuses.
514 *
515 * @param array $order_statuses
516 * @return array
517 */
518 public function add_pre_orders_order_statuses( $order_statuses ) {
519 $order_statuses['wc-pre-ordered'] = 'Pre Ordered';
520
521 return $order_statuses;
522 }
523
524 /**
525 * Add pre orders class identifies to the body tag.
526 *
527 */
528 public function pre_orders_post_class( $classes, $product ) {
529 if ( 'yes' === get_post_meta( $product->get_id(), '_is_pre_order', true ) && strtotime( get_post_meta( $product->get_id(), '_pre_order_date', true ) ) > time() ) {
530 $classes[] = 'merchant-pre-ordered-product';
531 }
532
533 return $classes;
534 }
535
536 }