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

533 lines 17.5 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 = array();
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'] = array(
177 'type' => 'text',
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 = array(
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 } elseif ( $order->get_status() === 'wc-pre-ordered' && $order->payment_complete() ) {
270 $order->update_status( 'wc-completed', '[WooCommerce Pre Orders] ' );
271 }
272 }
273 }
274 }
275
276 /**
277 * Custom pre-order fields for variations.
278 *
279 * @param integer $loop
280 * @param array $variation_data
281 * @param object $variation
282 * @return void
283 */
284 public function custom_fields_for_variable_products( $loop, $variation_data, $variation ) {
285 echo '<div class="is_pre_order_meta_field form-row form-row-full" style="display: flex; align-items: center;">';
286 woocommerce_wp_checkbox(
287 array(
288 'id' => '_is_pre_order_' . $variation->ID,
289 'label' => '&nbsp;' . esc_html__( 'Pre-Order Product - Set this product as pre-order', 'merchant' ),
290 'value' => get_post_meta( $variation->ID, '_is_pre_order', true ),
291 )
292 );
293
294 echo wc_help_tip( __( 'Important: To pre-order out of stock products you must enable the \'Backorder\' stock option.', 'merchant' ) );
295 echo '</div>';
296 echo '<div class="form-row form-row-full">';
297 woocommerce_wp_text_input(
298 array(
299 'type' => 'date',
300 'id' => '_pre_order_date_' . $variation->ID,
301 'label' => esc_html__( 'Pre-Order Shipping Date', 'merchant' ),
302 'value' => get_post_meta( $variation->ID, '_pre_order_date', true ),
303 )
304 );
305 echo '</div>';
306 }
307
308 /**
309 * Custom pre-order fields for simple products.
310 *
311 * @return void
312 */
313 public function custom_fields_for_simple_products() {
314 echo '<div class="is_pre_order_meta_field form-row form-row-full hide_if_variable" style="display: flex; align-items: center;">';
315 woocommerce_wp_checkbox(
316 array(
317 'id' => '_is_pre_order',
318 'label' => esc_html__( 'Pre-Order Product', 'merchant' ),
319 'description' => esc_html__( 'Set this product as pre-order', 'merchant' ),
320 'value' => get_post_meta( get_the_ID(), '_is_pre_order', true ),
321 )
322 );
323
324 echo '<div style="margin-left: -20px">';
325 echo wc_help_tip( __( 'Important: To pre-order out of stock products you must enable the \'Backorder\' stock option.', 'merchant' ) );
326 echo '</div>';
327 echo '</div>';
328 echo '<div class="form-row form-row-full hide_if_variable">';
329 woocommerce_wp_text_input(
330 array(
331 'type' => 'date',
332 'id' => '_pre_order_date',
333 'label' => esc_html__( 'Pre-Order Shipping Date', 'merchant' ),
334 'value' => get_post_meta( get_the_ID(), '_pre_order_date', true ),
335 )
336 );
337 echo '</div>';
338 }
339
340 /**
341 * Save custom fields for variable products.
342 *
343 * @param integer $post_id
344 * @return void
345 */
346 public function custom_fields_for_variable_products_save( $post_id ) {
347 $input_post_data = array(
348 '_is_pre_order_by_post_id' => filter_input(INPUT_POST, '_is_pre_order_' . $post_id, FILTER_SANITIZE_FULL_SPECIAL_CHARS),
349 '_pre_order_data_by_post_id' => filter_input(INPUT_POST, '_pre_order_date_' . $post_id, FILTER_SANITIZE_FULL_SPECIAL_CHARS),
350 );
351
352 $product = wc_get_product( $post_id );
353
354 $is_pre_order_variation = isset( $input_post_data[ '_is_pre_order_by_post_id' ] ) ? 'yes' : 'no';
355 $product->update_meta_data( '_is_pre_order', $is_pre_order_variation );
356
357 if ( 'yes' === $is_pre_order_variation && isset( $input_post_data[ '_pre_order_data_by_post_id' ] ) ) {
358 $pre_order_date_value = sanitize_text_field( wp_unslash( $input_post_data[ '_pre_order_data_by_post_id' ] ) );
359 $product->update_meta_data( '_pre_order_date', $pre_order_date_value );
360 }
361
362 $product->save();
363 }
364
365 /**
366 * Save custom fields for simple products.
367 *
368 * @param integer $post_id
369 * @return void
370 */
371 public function custom_fields_for_simple_products_save( $post_id ) {
372 $input_post_data = array(
373 '_is_pre_order' => filter_input(INPUT_POST, '_is_pre_order', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
374 '_pre_order_date' => filter_input(INPUT_POST, '_pre_order_date', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
375 );
376
377 $product = wc_get_product( $post_id );
378 $is_pre_order = isset( $input_post_data['_is_pre_order'] ) ? 'yes' : 'no';
379 $product->update_meta_data( '_is_pre_order', $is_pre_order );
380
381 if ( 'yes' === $is_pre_order && isset( $input_post_data['_pre_order_date'] ) ) {
382 $pre_order_date_value = sanitize_text_field( wp_unslash( $input_post_data['_pre_order_date'] ) );
383 $product->update_meta_data( '_pre_order_date', esc_attr( $pre_order_date_value ) );
384 } else {
385 $product->update_meta_data( '_pre_order_date', '' );
386 }
387
388 $product->save();
389 }
390
391 /**
392 * Change pre-order button text.
393 *
394 * @param string $text
395 * @return string
396 */
397 public function change_button_text( $text ) {
398 $input_post_data = array(
399 'product_id' => filter_input(INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT),
400 );
401
402 global $post;
403 $_post = $post;
404
405 // In some cases the $post might be null. e.g inside quick view popup.
406 if ( ! $_post && isset( $input_post_data[ 'product_id' ] ) ) {
407 $_post = get_post( absint( $input_post_data[ 'product_id' ] ) );
408 }
409
410 if ( 'yes' === get_post_meta( $_post->ID, '_is_pre_order', true ) && strtotime( get_post_meta( $_post->ID, '_pre_order_date', true ) ) > time() ) {
411 $text = Merchant_Admin_Options::get( 'pre-orders', 'button_text', esc_html__( 'Pre Order Now!', 'merchant' ) );
412 }
413
414 return $text;
415 }
416
417 /**
418 * Change pre-order button text for variable products.
419 *
420 * @param array $data
421 * @param object $product
422 * @param object $variation
423 * @return array
424 */
425 public function change_button_text_for_variable_products( $data, $product, $variation ) {
426 global $product;
427
428 if ( get_post_meta( $variation->get_id(), '_is_pre_order', true ) === 'yes' && strtotime( get_post_meta( $variation->get_id(), '_pre_order_date', true ) ) > time() ) {
429
430 $data['is_pre_order'] = true;
431
432 $additional_text = Merchant_Admin_Options::get( 'pre-orders', 'additional_text', esc_html__( 'Ships on {date}.', 'merchant' ) );
433 $time_format = date_i18n( get_option( 'date_format' ), strtotime( get_post_meta( $variation->get_id(), '_pre_order_date', true ) ) );
434 $text = $this->replaceDateTxt( $additional_text, $time_format );
435
436 if ( ! empty( $text ) ) {
437 $data['is_pre_order_date'] = $this->replaceDateTxt( $additional_text, $time_format );
438 }
439
440 }
441
442 return $data;
443 }
444
445 /**
446 * Replace {date} markup with new text.
447 *
448 * @param string $string_contains_date_tag
449 * @param string $time_format
450 *
451 * @return string
452 */
453 public function replaceDateTxt( $string_contains_date_tag, $time_format ) {
454 $from = array( '{date}' );
455 $to = array( $time_format );
456
457 return str_replace( $from, $to, $string_contains_date_tag );
458 }
459
460 /**
461 * Maybe render additional information.
462 *
463 * @return void
464 */
465 public function maybe_render_additional_information() {
466 $input_post_data = array(
467 'product_id' => filter_input(INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT),
468 );
469
470 global $post, $product;
471
472 // Do not override globals.
473 $_post = $post;
474 $_product = $product;
475
476 // In some cases the $post might be null. e.g inside quick view popup.
477 if ( ! $_post && isset( $input_post_data[ 'product_id' ] ) ) {
478 $_post = get_post( absint( $input_post_data[ 'product_id' ] ) );
479 $_product = wc_get_product( $_post->ID );
480 }
481
482 if ( null !== $_product ) {
483 if ( 'yes' === get_post_meta( $_post->ID, '_is_pre_order', true ) && strtotime( get_post_meta( $_post->ID, '_pre_order_date', true ) ) > time() ) {
484 $additional_text = Merchant_Admin_Options::get( 'pre-orders', 'additional_text', esc_html__( 'Ships on {date}.', 'merchant' ) );
485 $time_format = date_i18n( get_option( 'date_format' ), strtotime( get_post_meta( $_post->ID, '_pre_order_date', true ) ) );
486 $text = $this->replaceDateTxt( $additional_text, $time_format );
487
488 printf( '<div class="merchant-pre-orders-date">%s</div>', esc_html( $text ) );
489 }
490 }
491 }
492
493 /**
494 * Register pre orders status.
495 *
496 * @return void
497 */
498 public function register_pre_orders_order_status() {
499 register_post_status( 'wc-pre-ordered', array(
500 'label' => esc_html__( 'Pre Ordered', 'merchant' ),
501 'public' => true,
502 'show_in_admin_status_list' => true,
503 'show_in_admin_all_list' => true,
504 'exclude_from_search' => false,
505 /* translators: %s: pre ordered product count */
506 'label_count' => _n_noop( 'Pre Ordered <span class="count">(%s)</span>', 'Pre Ordered <span class="count">(%s)</span>', 'merchant' ),
507 ) );
508 }
509
510 /**
511 * Add pre orders status to order statuses.
512 *
513 * @param array $order_statuses
514 * @return array
515 */
516 public function add_pre_orders_order_statuses( $order_statuses ) {
517 $order_statuses['wc-pre-ordered'] = 'Pre Ordered';
518
519 return $order_statuses;
520 }
521
522 /**
523 * Add pre orders class identifies to the body tag.
524 *
525 */
526 public function pre_orders_post_class( $classes, $product ) {
527 if ( 'yes' === get_post_meta( $product->get_id(), '_is_pre_order', true ) && strtotime( get_post_meta( $product->get_id(), '_pre_order_date', true ) ) > time() ) {
528 $classes[] = 'merchant-pre-ordered-product';
529 }
530
531 return $classes;
532 }
533 }