752 lines
28 KiB
PHP
752 lines
28 KiB
PHP
<?php
|
|
/**
|
|
* Auto apply coupon
|
|
*
|
|
* @author StoreApps
|
|
* @since 4.6.0
|
|
* @version 2.8.0
|
|
*
|
|
* @package woocommerce-smart-coupons/includes/
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit; // Exit if accessed directly.
|
|
}
|
|
|
|
if ( ! class_exists( 'WC_SC_Auto_Apply_Coupon' ) ) {
|
|
|
|
/**
|
|
* Class for handling coupons applied via URL
|
|
*/
|
|
class WC_SC_Auto_Apply_Coupon {
|
|
|
|
/**
|
|
* Variable to hold instance of WC_SC_Auto_Apply_Coupon
|
|
*
|
|
* @var $instance
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Variable to hold coupon notices
|
|
*
|
|
* @var $coupon_notices
|
|
*/
|
|
private $coupon_notices = array();
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
|
|
add_action( 'woocommerce_coupon_options', array( $this, 'usage_restriction' ), 10, 2 );
|
|
add_action( 'woocommerce_coupon_options_save', array( $this, 'process_meta' ), 10, 2 );
|
|
add_filter( 'wc_smart_coupons_export_headers', array( $this, 'export_headers' ) );
|
|
add_filter( 'smart_coupons_parser_postmeta_defaults', array( $this, 'postmeta_defaults' ) );
|
|
add_filter( 'sc_generate_coupon_meta', array( $this, 'generate_coupon_meta' ), 10, 2 );
|
|
add_filter( 'wc_sc_process_coupon_meta_value_for_import', array( $this, 'process_coupon_meta_value_for_import' ), 10, 2 );
|
|
add_filter( 'is_protected_meta', array( $this, 'make_action_meta_protected' ), 10, 3 );
|
|
|
|
add_action( 'wp_loaded', array( $this, 'handle_auto_apply_hooks' ), 15 );
|
|
|
|
add_action( 'woocommerce_cart_calculate_fees', array( $this, 'remove_coupon_if_zero' ), 10, 1 );
|
|
}
|
|
|
|
/**
|
|
* Get single instance of WC_SC_Auto_Apply_Coupon
|
|
*
|
|
* @return WC_SC_Auto_Apply_Coupon Singleton object of WC_SC_Auto_Apply_Coupon
|
|
*/
|
|
public static function get_instance() {
|
|
// Check if instance is already exists.
|
|
if ( is_null( self::$instance ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Handle call to functions which is not available in this class
|
|
*
|
|
* @param string $function_name The function name.
|
|
* @param array $arguments Array of arguments passed while calling $function_name.
|
|
* @return result of function call
|
|
*/
|
|
public function __call( $function_name, $arguments = array() ) {
|
|
|
|
global $woocommerce_smart_coupon;
|
|
|
|
if ( ! is_callable( array( $woocommerce_smart_coupon, $function_name ) ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! empty( $arguments ) ) {
|
|
return call_user_func_array( array( $woocommerce_smart_coupon, $function_name ), $arguments );
|
|
} else {
|
|
return call_user_func( array( $woocommerce_smart_coupon, $function_name ) );
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Display field for auto apply coupon
|
|
*
|
|
* @param integer $coupon_id The coupon id.
|
|
* @param WC_Coupon $coupon The coupon object.
|
|
*/
|
|
public function usage_restriction( $coupon_id = 0, $coupon = null ) {
|
|
?>
|
|
<script type="text/javascript">
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const showHideAutoApplyCouponField = () => {
|
|
let discountType = document.querySelector('select#discount_type').value;
|
|
let autoApplyField = document.querySelector('.wc_sc_auto_apply_coupon_field');
|
|
autoApplyField.style.display = ( 'smart_coupon' === discountType ) ? 'none' : 'block';
|
|
};
|
|
showHideAutoApplyCouponField();
|
|
document.querySelector('select#discount_type').addEventListener('change', () => {
|
|
showHideAutoApplyCouponField();
|
|
});
|
|
});
|
|
</script>
|
|
<div class="options_group smart-coupons-field">
|
|
<?php
|
|
$is_allow_auto_apply = $this->sc_get_option( 'wc_sc_allow_auto_apply', 'yes' );
|
|
if ( 'yes' === $is_allow_auto_apply ) {
|
|
woocommerce_wp_checkbox(
|
|
array(
|
|
'id' => 'wc_sc_auto_apply_coupon',
|
|
'label' => __( 'Auto apply?', 'woocommerce-smart-coupons' ),
|
|
'description' => __( 'When checked, this coupon will be applied automatically, if it is valid. If enabled in more than 5 coupons, only 5 coupons will be applied automatically, rest will be ignored.', 'woocommerce-smart-coupons' ),
|
|
)
|
|
);
|
|
}
|
|
?>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Save auto apply coupon in meta
|
|
*
|
|
* @param Integer $post_id The coupon post ID.
|
|
* @param WC_Coupon $coupon The coupon object.
|
|
*/
|
|
public function process_meta( $post_id = 0, $coupon = null ) {
|
|
|
|
if ( empty( $post_id ) ) {
|
|
return;
|
|
}
|
|
|
|
$coupon = new WC_Coupon( $coupon );
|
|
|
|
$is_callable_coupon_update_meta = $this->is_callable( $coupon, 'update_meta_data' ) && $this->is_callable( $coupon, 'save' );
|
|
|
|
// Get list of ids of coupons to auto apply.
|
|
$auto_apply_coupon_ids = get_option( 'wc_sc_auto_apply_coupon_ids', array() );
|
|
$auto_apply_coupon_ids = ( empty( $auto_apply_coupon_ids ) || ! is_array( $auto_apply_coupon_ids ) ) ? array() : $auto_apply_coupon_ids;
|
|
$auto_apply_coupon_ids = array_map( 'absint', $auto_apply_coupon_ids );
|
|
$post_id = absint( $post_id );
|
|
if ( isset( $_POST['wc_sc_auto_apply_coupon'] ) && isset( $_POST['discount_type'] ) && 'smart_coupon' !== wc_clean( wp_unslash( $_POST['discount_type'] ) ) ) { // phpcs:ignore
|
|
$auto_apply_coupon = wc_clean( wp_unslash( $_POST['wc_sc_auto_apply_coupon'] ) ); // phpcs:ignore
|
|
if ( true === $is_callable_coupon_update_meta ) {
|
|
$coupon->update_meta_data( 'wc_sc_auto_apply_coupon', $auto_apply_coupon );
|
|
$coupon->save();
|
|
} else {
|
|
$this->update_post_meta( $post_id, 'wc_sc_auto_apply_coupon', $auto_apply_coupon );
|
|
}
|
|
// Add coupon id to auto apply coupon list if haven't added already.
|
|
if ( is_array( $auto_apply_coupon_ids ) && ! in_array( $post_id, $auto_apply_coupon_ids, true ) ) {
|
|
$auto_apply_coupon_ids[] = $post_id;
|
|
}
|
|
} else {
|
|
if ( true === $is_callable_coupon_update_meta ) {
|
|
$coupon->update_meta_data( 'wc_sc_auto_apply_coupon', 'no' );
|
|
$coupon->save();
|
|
} else {
|
|
$this->update_post_meta( $post_id, 'wc_sc_auto_apply_coupon', 'no' );
|
|
}
|
|
// Remove coupon id from auto apply coupon list if auto apply is disabled.
|
|
if ( is_array( $auto_apply_coupon_ids ) && in_array( $post_id, $auto_apply_coupon_ids, true ) ) {
|
|
$auto_apply_coupon_ids = array_diff( $auto_apply_coupon_ids, array( $post_id ) );
|
|
}
|
|
}
|
|
update_option( 'wc_sc_auto_apply_coupon_ids', $auto_apply_coupon_ids, 'no' );
|
|
}
|
|
|
|
/**
|
|
* Add meta in export headers
|
|
*
|
|
* @param array $headers Existing headers.
|
|
* @return array
|
|
*/
|
|
public function export_headers( $headers = array() ) {
|
|
|
|
$headers['wc_sc_auto_apply_coupon'] = __( 'Auto apply?', 'woocommerce-smart-coupons' );
|
|
|
|
return $headers;
|
|
}
|
|
|
|
/**
|
|
* Post meta defaults for auto apply coupon meta
|
|
*
|
|
* @param array $defaults Existing postmeta defaults.
|
|
* @return array $defaults Modified postmeta defaults
|
|
*/
|
|
public function postmeta_defaults( $defaults = array() ) {
|
|
|
|
$defaults['wc_sc_auto_apply_coupon'] = '';
|
|
|
|
return $defaults;
|
|
}
|
|
|
|
/**
|
|
* Add auto apply coupon's meta with value in coupon meta
|
|
*
|
|
* @param array $data The row data.
|
|
* @param array $post The POST values.
|
|
* @return array $data Modified row data
|
|
*/
|
|
public function generate_coupon_meta( $data = array(), $post = array() ) {
|
|
|
|
if ( isset( $post['discount_type'] ) && 'smart_coupon' !== $post['discount_type'] ) {
|
|
$data['wc_sc_auto_apply_coupon'] = ( isset( $post['wc_sc_auto_apply_coupon'] ) ) ? $post['wc_sc_auto_apply_coupon'] : '';
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Process coupon meta value for import
|
|
*
|
|
* @param mixed $meta_value The meta value.
|
|
* @param array $args Additional Arguments.
|
|
* @return mixed $meta_value
|
|
*/
|
|
public function process_coupon_meta_value_for_import( $meta_value = null, $args = array() ) {
|
|
|
|
$discount_type = isset( $args['discount_type'] ) ? $args['discount_type'] : '';
|
|
if ( 'smart_coupon' !== $discount_type && ! empty( $args['meta_key'] ) && 'wc_sc_auto_apply_coupon' === $args['meta_key'] ) {
|
|
$auto_apply_coupon = $meta_value;
|
|
if ( 'yes' === $auto_apply_coupon ) {
|
|
$auto_apply_coupon_ids = get_option( 'wc_sc_auto_apply_coupon_ids', array() );
|
|
$auto_apply_coupon_ids = ( empty( $auto_apply_coupon_ids ) || ! is_array( $auto_apply_coupon_ids ) ) ? array() : $auto_apply_coupon_ids;
|
|
$auto_apply_coupon_ids = array_map( 'absint', $auto_apply_coupon_ids );
|
|
$coupon_id = ( isset( $args['post']['post_id'] ) ) ? absint( $args['post']['post_id'] ) : 0;
|
|
if ( ! empty( $coupon_id ) && ! in_array( $coupon_id, $auto_apply_coupon_ids, true ) ) {
|
|
$auto_apply_coupon_ids[] = $coupon_id;
|
|
update_option( 'wc_sc_auto_apply_coupon_ids', $auto_apply_coupon_ids, 'no' );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $meta_value;
|
|
}
|
|
|
|
/**
|
|
* Make meta data of auto apply coupon meta protected
|
|
*
|
|
* @param bool $protected Is protected.
|
|
* @param string $meta_key The meta key.
|
|
* @param string $meta_type The meta type.
|
|
* @return bool $protected
|
|
*/
|
|
public function make_action_meta_protected( $protected = false, $meta_key = '', $meta_type = '' ) {
|
|
|
|
if ( 'wc_sc_auto_apply_coupon' === $meta_key ) {
|
|
return true;
|
|
}
|
|
|
|
return $protected;
|
|
}
|
|
|
|
/**
|
|
* Get auto applied coupons
|
|
*
|
|
* @since 4.27.0
|
|
* @return array
|
|
*/
|
|
public function get_auto_applied_coupons() {
|
|
$coupons = ( is_object( WC()->session ) && is_callable( array( WC()->session, 'get' ) ) ) ? WC()->session->get( 'wc_sc_auto_applied_coupons' ) : array();
|
|
$coupons = ( ! empty( $coupons ) && is_array( $coupons ) ) ? array_filter( array_unique( $coupons ) ) : array();
|
|
return apply_filters( 'wc_sc_' . __FUNCTION__, $coupons, array( 'source' => $this ) );
|
|
}
|
|
|
|
/**
|
|
* Add auto applied coupon to WC session
|
|
*
|
|
* @since 4.27.0
|
|
* @param string $coupon_code Coupon Code.
|
|
*/
|
|
public function set_auto_applied_coupon( $coupon_code = '' ) {
|
|
if ( ! empty( $coupon_code ) ) {
|
|
$coupons = $this->get_auto_applied_coupons();
|
|
// Check if auto applied coupons are not empty.
|
|
if ( ! empty( $coupons ) && is_array( $coupons ) ) {
|
|
$coupons[] = $coupon_code;
|
|
} else {
|
|
$coupons = array( $coupon_code );
|
|
}
|
|
if ( is_object( WC()->session ) && is_callable( array( WC()->session, 'set' ) ) ) {
|
|
WC()->session->set( 'wc_sc_auto_applied_coupons', $coupons );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove an auto applied coupon from WC session
|
|
*
|
|
* @since 4.31.0
|
|
* @param string $coupon_code Coupon Code.
|
|
*/
|
|
public function unset_auto_applied_coupon( $coupon_code = '' ) {
|
|
if ( ! empty( $coupon_code ) ) {
|
|
$update = false;
|
|
$coupons = $this->get_auto_applied_coupons();
|
|
// Check if auto applied coupons are not empty.
|
|
if ( ! empty( $coupons ) && in_array( $coupon_code, $coupons, true ) ) {
|
|
$coupons = array_diff( $coupons, array( $coupon_code ) );
|
|
$update = true;
|
|
}
|
|
if ( true === $update && is_object( WC()->session ) && is_callable( array( WC()->session, 'set' ) ) ) {
|
|
$coupons = array_values( array_filter( $coupons ) );
|
|
WC()->session->set( 'wc_sc_auto_applied_coupons', $coupons );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset cart session data.
|
|
*
|
|
* @since 4.27.0
|
|
*/
|
|
public function reset_auto_applied_coupons_session() {
|
|
if ( is_object( WC()->session ) && is_callable( array( WC()->session, 'set' ) ) ) {
|
|
WC()->session->set( 'wc_sc_auto_applied_coupons', null );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs after a coupon is removed
|
|
*
|
|
* @since 4.31.0
|
|
* @param string $coupon_code The coupon code.
|
|
* @return void
|
|
*/
|
|
public function wc_sc_removed_coupon( $coupon_code = '' ) {
|
|
$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore
|
|
$is_automatic = true;
|
|
if ( ! empty( $backtrace ) ) {
|
|
foreach ( $backtrace as $trace ) {
|
|
if (
|
|
isset( $trace['file'] ) &&
|
|
(
|
|
false !== strpos( $trace['file'], 'StoreApi/Routes/V1/CartRemoveCoupon.php' ) ||
|
|
( ! empty( $trace['function'] ) && 'remove_coupon' === $trace['function'] && ! empty( $trace['class'] ) && 'WC_AJAX' === $trace['class'] )
|
|
)
|
|
) {
|
|
if ( false !== strpos( $trace['file'], 'StoreApi/Routes/V1/CartRemoveCoupon.php' ) ) {
|
|
// Call auto_apply_coupons() if removal is from StoreApi/Routes/V1/CartRemoveCoupon.php.
|
|
$this->auto_apply_coupons();
|
|
return; // Exit function after calling auto_apply_coupons().
|
|
}
|
|
|
|
// Coupon was removed by the user.
|
|
$is_automatic = false;
|
|
break; // Exit loop once the user removal is identified.
|
|
}
|
|
}
|
|
}
|
|
if ( $is_automatic ) {
|
|
$this->unset_auto_applied_coupon( $coupon_code );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if auto apply coupon allowed in the cart
|
|
*
|
|
* @since 4.27.0
|
|
* @return bool.
|
|
*/
|
|
public function is_allow_auto_apply_coupons() {
|
|
$auto_applied_coupons = $this->get_auto_applied_coupons();
|
|
$auto_applied_coupons_count = ! empty( $auto_applied_coupons ) && is_array( $auto_applied_coupons ) ? count( $auto_applied_coupons ) : 0;
|
|
$max_auto_apply_coupons_limit = apply_filters( 'wc_sc_max_auto_apply_coupons_limit', get_option( 'wc_sc_max_auto_apply_coupons_limit', 5 ), array( 'source' => $this ) );
|
|
|
|
return apply_filters(
|
|
'wc_sc_' . __FUNCTION__,
|
|
$auto_applied_coupons_count < $max_auto_apply_coupons_limit,
|
|
array(
|
|
'source' => $this,
|
|
'auto_applied_coupons' => $auto_applied_coupons,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if the auto apply removable
|
|
*
|
|
* @since 4.27.0
|
|
* @param string $coupon_code Coupon Code.
|
|
* @return bool.
|
|
*/
|
|
public function is_auto_apply_coupon_removable( $coupon_code = '' ) {
|
|
|
|
return apply_filters(
|
|
'wc_sc_' . __FUNCTION__,
|
|
get_option( 'wc_sc_auto_apply_coupon_removable', 'yes' ),
|
|
array(
|
|
'source' => $this,
|
|
'coupon_code' => $coupon_code,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if the coupon is applied through auto apply
|
|
*
|
|
* @since 4.27.0
|
|
* @param string $coupon_code Coupon Code.
|
|
* @return bool.
|
|
*/
|
|
public function is_coupon_applied_by_auto_apply( $coupon_code = '' ) {
|
|
if ( ! empty( $coupon_code ) ) {
|
|
$applied_coupons = $this->get_auto_applied_coupons();
|
|
if ( ! empty( $applied_coupons ) && is_array( $applied_coupons ) && in_array( $coupon_code, $applied_coupons, true ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if coupon is applicable for auto apply
|
|
*
|
|
* @since 4.26.0
|
|
* @param WC_Coupon $coupon WooCommerce coupon object.
|
|
* @return bool
|
|
*/
|
|
public function is_coupon_valid_for_auto_apply( $coupon = null ) {
|
|
|
|
$valid = false;
|
|
if ( is_object( $coupon ) && $coupon instanceof WC_Coupon ) {
|
|
|
|
if ( $this->is_wc_gte_30() ) {
|
|
$coupon_code = is_callable( array( $coupon, 'get_code' ) ) ? $coupon->get_code() : '';
|
|
$discount_type = is_callable( array( $coupon, 'get_discount_type' ) ) ? $coupon->get_discount_type() : '';
|
|
$is_auto_generate_coupon = is_callable( array( $coupon, 'get_meta' ) ) ? $coupon->get_meta( 'auto_generate_coupon' ) : 'no';
|
|
$is_disable_email_restrict = is_callable( array( $coupon, 'get_meta' ) ) ? $coupon->get_meta( 'sc_disable_email_restriction' ) : 'no';
|
|
} else {
|
|
$coupon_id = ( ! empty( $coupon->id ) ) ? $coupon->id : 0;
|
|
$coupon_code = ( ! empty( $coupon->code ) ) ? $coupon->code : '';
|
|
$discount_type = get_post_meta( $coupon_id, 'discount_type', true );
|
|
$is_auto_generate_coupon = get_post_meta( $coupon_id, 'auto_generate_coupon', true );
|
|
$is_disable_email_restrict = get_post_meta( $coupon_id, 'sc_disable_email_restriction', true );
|
|
}
|
|
|
|
$is_removable = $this->is_auto_apply_coupon_removable( $coupon_code );
|
|
$is_auto_applied = $this->is_coupon_applied_by_auto_apply( $coupon_code );
|
|
|
|
/**
|
|
* Validate coupon for auto apply if
|
|
*
|
|
* Discount type is not smart_coupon.
|
|
* Auto generate is not enabled.
|
|
* Coupon should not be auto applied OR auto applied coupon should not be removable.
|
|
* Coupon code is valid.
|
|
*/
|
|
$valid = 'smart_coupon' !== $discount_type
|
|
&& 'yes' !== $is_auto_generate_coupon
|
|
&& ( ! $is_auto_applied || 'yes' !== $is_removable )
|
|
&& $this->is_valid( $coupon );
|
|
}
|
|
|
|
return apply_filters(
|
|
'wc_sc_' . __FUNCTION__,
|
|
$valid,
|
|
array(
|
|
'coupon_obj' => $coupon,
|
|
'source' => $this,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Function to apply coupons automatically.
|
|
*
|
|
* TODO: IF we need another variable for removed coupons;
|
|
* There will be 2 session variables: wc_sc_auto_applied_coupons and wc_sc_removed_auto_applied_coupons.
|
|
* Whenever a coupon will be auto-applied, it'll be stored in wc_sc_auto_applied_coupons.
|
|
* Whenever a coupon will be removed, it'll be moved from wc_sc_auto_applied_coupons to wc_sc_removed_auto_applied_coupons.
|
|
* And before applying an auto-apply coupon, it'll be made sure that the coupon doesn't exist in wc_sc_removed_auto_applied_coupons
|
|
* And sum of counts of both session variable will be considered before auto applying coupons. It will be made sure that the sum of counts in not exceeding option `wc_sc_max_auto_apply_coupons_limit`
|
|
* Reference: issues/234#note_27085
|
|
*/
|
|
public function auto_apply_coupons() {
|
|
$cart = ( is_object( WC() ) && isset( WC()->cart ) ) ? WC()->cart : null;
|
|
if ( is_object( $cart ) && is_callable( array( $cart, 'is_empty' ) ) && ! $cart->is_empty() && $this->is_allow_auto_apply_coupons() ) {
|
|
global $wpdb;
|
|
$user_role = '';
|
|
$email = '';
|
|
if ( ! is_admin() ) {
|
|
$current_user = wp_get_current_user();
|
|
if ( ! empty( $current_user->ID ) ) {
|
|
$max_user_roles_limit = apply_filters( 'wc_sc_max_user_roles_limit', 5 );
|
|
$user_roles = ( ! empty( $current_user->roles ) ) ? $current_user->roles : array();
|
|
if ( count( $user_roles ) > $max_user_roles_limit ) {
|
|
$user_roles = array_slice( $user_roles, 0, $max_user_roles_limit );
|
|
}
|
|
$email = get_user_meta( $current_user->ID, 'billing_email', true );
|
|
$email = ( ! empty( $email ) ) ? $email : $current_user->user_email;
|
|
}
|
|
}
|
|
$query = $wpdb->prepare(
|
|
"SELECT DISTINCT p.ID
|
|
FROM {$wpdb->posts} AS p
|
|
JOIN {$wpdb->postmeta} AS pm1
|
|
ON (p.ID = pm1.post_id
|
|
AND p.post_type = %s
|
|
AND p.post_status = %s
|
|
AND pm1.meta_key = %s
|
|
AND pm1.meta_value = %s)
|
|
JOIN {$wpdb->postmeta} AS pm2
|
|
ON (p.ID = pm2.post_id
|
|
AND pm2.meta_key IN ('wc_sc_user_role_ids', 'customer_email')
|
|
AND (pm2.meta_value = ''
|
|
OR pm2.meta_value = 'a:0:{}'",
|
|
'shop_coupon',
|
|
'publish',
|
|
'wc_sc_auto_apply_coupon',
|
|
'yes'
|
|
);
|
|
if ( ! empty( $user_roles ) ) {
|
|
foreach ( $user_roles as $user_role ) {
|
|
$query .= $wpdb->prepare(
|
|
' OR pm2.meta_value LIKE %s',
|
|
'%' . $wpdb->esc_like( $user_role ) . '%'
|
|
);
|
|
}
|
|
}
|
|
if ( ! empty( $email ) ) {
|
|
$query .= $wpdb->prepare(
|
|
' OR pm2.meta_value LIKE %s',
|
|
'%' . $wpdb->esc_like( $email ) . '%'
|
|
);
|
|
}
|
|
$query .= '))';
|
|
$auto_apply_coupon_ids = $wpdb->get_col( $query ); // phpcs:ignore
|
|
$auto_apply_coupon_ids = ( empty( $auto_apply_coupon_ids ) || ! is_array( $auto_apply_coupon_ids ) ) ? array() : $auto_apply_coupon_ids;
|
|
$auto_apply_coupon_ids = array_filter( array_map( 'absint', $auto_apply_coupon_ids ) );
|
|
if ( ! empty( $auto_apply_coupon_ids ) && is_array( $auto_apply_coupon_ids ) ) {
|
|
$valid_coupon_counter = 0;
|
|
$max_auto_apply_coupons_limit = apply_filters( 'wc_sc_max_auto_apply_coupons_limit', get_option( 'wc_sc_max_auto_apply_coupons_limit', 5 ), array( 'source' => $this ) );
|
|
$current_filter = current_filter();
|
|
do_action(
|
|
'wc_sc_before_auto_apply_coupons',
|
|
array(
|
|
'source' => $this,
|
|
'current_filter' => $current_filter,
|
|
)
|
|
);
|
|
foreach ( $auto_apply_coupon_ids as $apply_coupon_id ) {
|
|
// Process only five coupons.
|
|
if ( absint( $max_auto_apply_coupons_limit ) === $valid_coupon_counter ) {
|
|
break;
|
|
}
|
|
$coupon = new WC_Coupon( absint( $apply_coupon_id ) );
|
|
if ( $this->is_wc_gte_30() ) {
|
|
$coupon_id = ( ! empty( $coupon ) && is_callable( array( $coupon, 'get_id' ) ) ) ? $coupon->get_id() : 0;
|
|
$coupon_code = ( ! empty( $coupon ) && is_callable( array( $coupon, 'get_code' ) ) ) ? $coupon->get_code() : '';
|
|
} else {
|
|
$coupon_id = ( ! empty( $coupon->id ) ) ? $coupon->id : 0;
|
|
$coupon_code = ( ! empty( $coupon->code ) ) ? $coupon->code : '';
|
|
}
|
|
|
|
// If coupon has payment method restriction and already store wc_sc_auto_applied_coupons.
|
|
$payment_method_ids = $this->get_post_meta( $coupon_id, 'wc_sc_payment_method_ids', true );
|
|
if ( ( is_array( $payment_method_ids ) && count( $payment_method_ids ) > 0 ) && $this->is_coupon_applied_by_auto_apply( $coupon_code ) ) {
|
|
$this->unset_auto_applied_coupon( $coupon_code );
|
|
}
|
|
|
|
// Check if it is a valid coupon object.
|
|
if ( $apply_coupon_id === $coupon_id && ! empty( $coupon_code ) && $this->is_coupon_valid_for_auto_apply( $coupon ) ) {
|
|
$cart_total = ( $this->is_wc_greater_than( '3.1.2' ) ) ? $cart->get_cart_contents_total() : $cart->cart_contents_total;
|
|
$is_auto_apply = apply_filters(
|
|
'wc_sc_is_auto_apply',
|
|
( $cart_total > 0 ),
|
|
array(
|
|
'source' => $this,
|
|
'cart_obj' => $cart,
|
|
'coupon_obj' => $coupon,
|
|
'cart_total' => $cart_total,
|
|
)
|
|
);
|
|
// Check if cart still requires a coupon discount and does not have coupon already applied.
|
|
if ( true === $is_auto_apply && ! $cart->has_discount( $coupon_code ) ) {
|
|
$cart->add_discount( $coupon_code );
|
|
$cart->calculate_shipping();
|
|
$cart->calculate_totals();
|
|
$this->set_auto_applied_coupon( $coupon_code );
|
|
}
|
|
$valid_coupon_counter++;
|
|
} // End if to check valid coupon.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function to apply coupons for cart and checkout block.
|
|
*
|
|
* @param bool $pre_render Is protected.
|
|
* @param array $parsed_block The meta key.
|
|
* @param array $parent_block The meta type.
|
|
* @return bool
|
|
*/
|
|
public function auto_apply_coupons_to_cart_checkout_block( $pre_render = null, $parsed_block = array(), $parent_block = null ) {
|
|
if ( isset( $parsed_block['blockName'] ) && in_array( $parsed_block['blockName'], array( 'woocommerce/cart', 'woocommerce/checkout' ), true ) ) {
|
|
$this->auto_apply_coupons();
|
|
}
|
|
return $pre_render;
|
|
}
|
|
|
|
/**
|
|
* Function to Automatically Apply Coupons on Cart Update.
|
|
*
|
|
* @param string $cart_item_key contains the id of the cart item. This may be empty if the cart item does not exist any more.
|
|
* @param int $quantity contains the quantity of the item.
|
|
* @param WC_Cart $cart Cart class.
|
|
*/
|
|
public function auto_apply_coupons_on_cart_update( $cart_item_key, $quantity, $cart ) {
|
|
if ( WC()->is_rest_api_request() ) {
|
|
$this->auto_apply_coupons();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Automatically apply coupons during the WooCommerce checkout order review update process.
|
|
* This function checks for changes in the shipping method and updates the chosen shipping method in the session.
|
|
* Then, it automatically applies any eligible coupons.
|
|
*/
|
|
public function wc_checkout_update_order_review_auto_apply_coupons() {
|
|
check_ajax_referer( 'update-order-review', 'security' );
|
|
|
|
// Get the posted shipping methods from the form data, if available.
|
|
$posted_shipping_methods = isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : array(); // phpcs:ignore
|
|
|
|
if ( ! empty( $posted_shipping_methods ) ) {
|
|
// Retrieve the current chosen shipping methods from the session.
|
|
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
|
|
|
|
if ( is_array( $posted_shipping_methods ) ) {
|
|
// Update the chosen shipping methods with the posted values.
|
|
foreach ( $posted_shipping_methods as $i => $value ) {
|
|
if ( ! is_string( $value ) ) {
|
|
continue;
|
|
}
|
|
$chosen_shipping_methods[ $i ] = $value;
|
|
}
|
|
}
|
|
|
|
// Save the updated chosen shipping methods back to the session.
|
|
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
|
|
}
|
|
|
|
// Automatically apply any eligible coupons.
|
|
$this->auto_apply_coupons();
|
|
}
|
|
|
|
/**
|
|
* Remove coupons that result in zero discount.
|
|
*
|
|
* This method loops through all applied coupons in the cart and checks if they are
|
|
* not present in the coupon discount totals. If a coupon is found to have a zero
|
|
* discount (i.e., it is not in the coupon discount totals), it is removed from the
|
|
* applied coupons list and the auto-applied coupon logic is handled.
|
|
*
|
|
* @param WC_Cart $cart The WooCommerce cart object.
|
|
*/
|
|
public function remove_coupon_if_zero( $cart ) {
|
|
// Get all applied coupons.
|
|
$applied_coupons = $cart->get_applied_coupons();
|
|
|
|
if ( ! empty( $applied_coupons ) ) {
|
|
foreach ( $applied_coupons as $coupon_code ) {
|
|
// Check if the coupon is not in the coupon discount totals (indicating a zero discount).
|
|
if ( ! array_key_exists( $coupon_code, $cart->get_coupon_discount_totals() ) ) {
|
|
// Remove the coupon from the applied coupons array.
|
|
$updated_coupons = array_diff( $applied_coupons, array( $coupon_code ) );
|
|
$cart->set_applied_coupons( $updated_coupons );
|
|
|
|
// Unset the auto-applied coupon from session.
|
|
$this->unset_auto_applied_coupon( $coupon_code );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Automatically apply coupons to the cart when the shipping method is changed.
|
|
* This method checks if the request is an AJAX request to update the shipping method,
|
|
* and if so, it calls the auto_apply_coupons method to apply any eligible coupons.
|
|
*/
|
|
public function auto_apply_coupons_to_cart_on_shipping_change() {
|
|
if ( is_ajax() ) {
|
|
// Check if the request is an AJAX request to update the shipping method.
|
|
check_ajax_referer( 'update-shipping-method', 'security' );
|
|
$this->auto_apply_coupons();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle auto apply related hooks
|
|
*/
|
|
public function handle_auto_apply_hooks() {
|
|
$is_allow_auto_apply = $this->sc_get_option( 'wc_sc_allow_auto_apply', 'yes' );
|
|
if ( 'yes' === $is_allow_auto_apply ) {
|
|
// Action to auto apply coupons.
|
|
add_action( 'woocommerce_cart_is_empty', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_shortcode_before_product_cat_loop', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_before_shop_loop', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_before_single_product', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_before_cart', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_before_checkout_form', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_account_content', array( $this, 'auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_checkout_update_order_review', array( $this, 'wc_checkout_update_order_review_auto_apply_coupons' ) );
|
|
add_action( 'woocommerce_cart_emptied', array( $this, 'reset_auto_applied_coupons_session' ) );
|
|
add_action( 'woocommerce_removed_coupon', array( $this, 'blocks_removed_coupon' ), 20 );
|
|
add_action( 'woocommerce_removed_coupon', array( $this, 'wc_sc_removed_coupon' ), 99 );
|
|
|
|
add_filter( 'pre_render_block', array( $this, 'auto_apply_coupons_to_cart_checkout_block' ), 10, 3 );
|
|
|
|
add_action( 'woocommerce_cart_item_set_quantity', array( $this, 'auto_apply_coupons_on_cart_update' ), 10, 4 );
|
|
|
|
add_action( 'woocommerce_before_cart_totals', array( $this, 'auto_apply_coupons_to_cart_on_shipping_change' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recheck coupon removed from auto-apply
|
|
*
|
|
* @param string $code The coupon code.
|
|
*/
|
|
public function blocks_removed_coupon( $code = '' ) {
|
|
if ( empty( $code ) ) {
|
|
return;
|
|
}
|
|
if ( function_exists( 'WC' ) && isset( WC()->cart ) && is_a( WC()->cart, 'WC_Cart' ) ) {
|
|
$coupon = new WC_Coupon( $code );
|
|
$is_removable = $this->is_auto_apply_coupon_removable( $code );
|
|
$is_auto_applied = $this->is_coupon_applied_by_auto_apply( $code );
|
|
if ( true === $is_auto_applied && 'yes' !== $is_removable && $this->is_callable( WC()->cart, 'add_discount' ) ) {
|
|
WC()->cart->add_discount( $code );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
WC_SC_Auto_Apply_Coupon::get_instance();
|