1

I would like to add 2 checkboxes to the WooCommerce coupon settings. The first checkbox, when checked, will automatically apply the coupon to products (the product setting is configured on the general coupon tab). The second checkbox, when checked, will automatically apply the coupon to the first order of new users. So I have modified the code and it is networking. Please check my code and give me some advice. Any help is appreciated.

Here is my code:

// Add a new tab
function custom_coupon_settings_tab($tabs) {
    $tabs['automatic_apply'] = array(
        'label' => __('Automatic Apply', 'mohsen'),
        'target' => 'automatic_apply_coupon_data',
        'class' => 'show_if_coupon',
    );
    return $tabs;
}
add_filter('woocommerce_coupon_data_tabs', 'custom_coupon_settings_tab');

// For content to the new tab
function custom_coupon_settings_tab_content() {
    global $post;

    $automatic_apply = get_post_meta($post->ID, 'automatic_apply', true);
    $automatic_apply_first_order = get_post_meta($post->ID, 'automatic_apply_first_order', true);

    ?>
    <div id="automatic_apply_coupon_data" class="panel woocommerce_options_panel">
        <?php
        woocommerce_wp_checkbox(
            array(
                'id' => 'automatic_apply',
                'label' => __('Automatically Apply this Coupon', 'mohsen'),
                'value' => $automatic_apply,
                'desc_tip' => true,
                'description' => __('When checked, this coupon will be automatically applied to eligible products without requiring the user to enter the code manually.', 'mohsen'),
            )
        );

        woocommerce_wp_checkbox(
            array(
                'id' => 'automatic_apply_first_order',
                'label' => __('Automatically Apply for First Order', 'mohsen'),
                'value' => $automatic_apply_first_order,
                'desc_tip' => true,
                'description' => __('When checked, this coupon will be automatically applied to the user\'s first order if they meet the coupon\'s eligibility requirements.', 'mohsen'),
            )
        );
        ?>
    </div>
    <?php
}
add_action('woocommerce_coupon_data_panels', 'custom_coupon_settings_tab_content');

function save_custom_coupon_settings($post_id) {
    $automatic_apply = isset($_POST['automatic_apply']) ? 'yes' : 'no';
    $automatic_apply_first_order = isset($_POST['automatic_apply_first_order']) ? 'yes' : 'no';
    update_post_meta($post_id, 'automatic_apply', $automatic_apply);
    update_post_meta($post_id, 'automatic_apply_first_order', $automatic_apply_first_order);
}
add_action('woocommerce_coupon_options_save', 'save_custom_coupon_settings');

// Automatically apply coupon
function apply_coupon_automatically($coupon_code) {
    if (isset(WC()->cart) && is_a(WC()->cart, 'WC_Cart')) {
        $coupon = new WC_Coupon($coupon_code);
        $automatic_apply = get_post_meta($coupon->get_id(), 'automatic_apply', true);
        $automatic_apply_first_order = get_post_meta($coupon->get_id(), 'automatic_apply_first_order', true);

        if ($automatic_apply === 'yes') {
            WC()->cart->apply_coupon($coupon_code);

            // For First Order
            if ($automatic_apply_first_order === 'yes') {
                $user_id = get_current_user_id();
                $user_orders = wc_get_orders(array(
                    'meta_key' => '_customer_user',
                    'meta_value' => $user_id,
                    'post_status' => array('wc-completed', 'wc-processing'),
                ));

                if (empty($user_orders)) {
                    WC()->cart->apply_coupon($coupon_code);
                }
            }
        }
    }
}
add_action('woocommerce_applied_coupon', 'apply_coupon_automatically'); 
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Mohsen
  • 300
  • 2
  • 13

1 Answers1

2

There are some mistakes in your code, and you are not using the right hook to auto apply coupons. Also, all paying customers have a specific user meta field, so there is no need to query orders for a customer to check if it's a new customer.

If you are using those custom coupon options, using one coupon for each "auto apply" feature ("normal" and "1st customer order"), it should be much lighter to save the data using update_option() avoiding a repetitive heavier coupon query (See at the bottom, I have added an addition).

The code:

// Utility function: Get coupon codes with "auto apply" feature enabled
function get_auto_apply_coupons() {
    $coupon_posts = (array) get_posts( array(
        'post_type'         => 'shop_coupon',
        'post_status'       => 'publish',
        'numberposts'       => -1,
        'meta_query'        => array(
            'relation'      => 'OR',
            array(
                'key'       => 'auto_apply',
                'value'     => 'yes',
            ),
            array(
                'key'       => 'auto_apply_1st_order',
                'value'     => 'yes',
            ),
        ),
    ));
    return array_column( $coupon_posts, 'post_name' );
}

// Conditional function: Check if is a paying customer
function is_paying_customer(){
    return WC()->customer->get_is_paying_customer();
}

// Admin counpon pages: Add a new tab
add_filter('woocommerce_coupon_data_tabs', 'custom_coupon_settings_tab');
function custom_coupon_settings_tab($tabs) {
    $tabs['automatic_apply'] = array(
        'label' => __('Automatic Apply', 'woocommerce'),
        'target' => 'auto_apply_coupon_data',
        'class' => 'show_if_coupon',
    );
    return $tabs;
}

// Admin counpon pages: Add fields to the new tab
add_action('woocommerce_coupon_data_panels', 'custom_coupon_settings_tab_content', 10, 2 );
function custom_coupon_settings_tab_content( $coupon_id, $coupon ) 
{
    echo '<div id="auto_apply_coupon_data" class="panel woocommerce_options_panel">';

    woocommerce_wp_checkbox( array(
        'id'            => 'auto_apply',
        'label'         => __('Automatically Apply this Coupon', 'woocommerce'),
        'desc_tip'      => true,
        'description'   => __('When checked, this coupon will be automatically applied to eligible products without requiring the user to enter the code manually.', 'woocommerce'),
    ) );

    woocommerce_wp_checkbox( array(
        'id'            => 'auto_apply_1st_order',
        'label'         => __('Automatically Apply for First Order', 'woocommerce'),
        'desc_tip'      => true,
        'description'   => __('When checked, this coupon will be automatically applied to the user\'s first order if they meet the coupon\'s eligibility requirements.', 'woocommerce'),
    ) );

    echo '</div>';
}

// Admin counpon pages: Save the field values
add_action( 'woocommerce_coupon_options_save', 'save_custom_coupon_settings', 10, 2 );
function save_custom_coupon_settings( $post_id, $coupon ) {
    $coupon->update_meta_data( 'auto_apply', isset($_POST['auto_apply']) ? 'yes' : 'no' );
    $coupon->update_meta_data( 'auto_apply_1st_order', isset($_POST['auto_apply_1st_order']) ? 'yes' : 'no' );
    $coupon->save();
}

// Automatically apply coupon that has "auto apply" feature enabled
add_action('woocommerce_before_calculate_totals', 'apply_coupon_automatically'); 
function apply_coupon_automatically( $cart ) {
    if ((is_admin() && !defined('DOING_AJAX')))
        return;

    if (did_action('woocommerce_before_calculate_totals') >= 2)
        return;

    // Get coupons with auto apply feature
    $auto_coupons = get_auto_apply_coupons(); 

    // Check if an "auto apply" coupon is not already applied to cart
    if ( count( array_intersect( $cart->get_applied_coupons(), $auto_coupons ) ) === 0 ) {
        // loop through "auto apply coupon codes
        foreach( $auto_coupons as $coupon_code ) {
            $coupon = new WC_Coupon($coupon_code); //  Get the coupon object

            $auto_apply = $coupon->get_meta('auto_apply');
            $auto_apply_1st_order = $coupon->get_meta('auto_apply_1st_order');

            // Check if it's a new customer and for coupon(s) with "auto apply" feature
            if ( ! is_paying_customer() && $auto_apply_1st_order === 'yes' ) {
                $cart->apply_coupon( $coupon_code );
                break; // stop the loop
            } elseif ( $auto_apply === 'yes' && is_paying_customer() ) {
                $cart->apply_coupon( $coupon_code );
                break; // stop the loop
            }
        }
    }
}

Code goes in your child theme's functions.php file, or in a plugin file. Tested and works.


Addition:

Avoiding heavy, repetitive queries.

Here, we will use update_option() and get_option() to avoid repetitive, heavier queries. This code will handle only one coupon code for each "auto apply" feature.

The code:

// Conditional function: Check if is a paying customer
function is_paying_customer(){
    return WC()->customer->get_is_paying_customer();
}

// Admin counpon pages: Add a new tab
add_filter('woocommerce_coupon_data_tabs', 'custom_coupon_settings_tab');
function custom_coupon_settings_tab($tabs) {
    $tabs['automatic_apply'] = array(
        'label' => __('Automatic Apply', 'woocommerce'),
        'target' => 'auto_apply_coupon_data',
        'class' => 'show_if_coupon',
    );
    return $tabs;
}

// Admin counpon pages: Add fields to the new tab
add_action('woocommerce_coupon_data_panels', 'custom_coupon_settings_tab_content', 10, 2 );
function custom_coupon_settings_tab_content( $coupon_id, $coupon ) 
{
    echo '<div id="auto_apply_coupon_data" class="panel woocommerce_options_panel">';

    woocommerce_wp_checkbox( array(
        'id'            => 'auto_apply',
        'label'         => __('Automatically Apply this Coupon', 'woocommerce'),
        'desc_tip'      => true,
        'value'         => $coupon->get_code() === get_option('auto_apply_coupon') ? 'yes' : 'no',
        'description'   => __('When checked, this coupon will be automatically applied to eligible products without requiring the user to enter the code manually.', 'woocommerce'),
    ) );

    woocommerce_wp_checkbox( array(
        'id'            => 'auto_apply_1st_order',
        'label'         => __('Automatically Apply for First Order', 'woocommerce'),
        'value'         => $coupon->get_code() === get_option('auto_apply_coupon_1st_order') ? 'yes' : 'no',
        'desc_tip'      => true,
        'description'   => __('When checked, this coupon will be automatically applied to the user\'s first order if they meet the coupon\'s eligibility requirements.', 'woocommerce'),
    ) );

    echo '</div>';
}

// Admin counpon pages: Save the field values
add_action( 'woocommerce_coupon_options_save', 'save_custom_coupon_settings', 10, 2 );
function save_custom_coupon_settings( $post_id, $coupon ) {
    $coucon_code = $coupon->get_code();
    if ( isset($_POST['auto_apply']) ) {
        update_option('auto_apply_coupon', $coucon_code );
    } elseif( get_option('auto_apply_coupon') === $coucon_code ) {
        update_option('auto_apply_coupon', '' );
    }
    if ( isset($_POST['auto_apply_1st_order']) ) {
        update_option('auto_apply_coupon_1st_order', $coucon_code );
    } elseif( get_option('auto_apply_coupon_1st_order') === $coucon_code ) {
        update_option('auto_apply_coupon_1st_order', '' );
    }
}

// Automatically apply coupon that has "auto apply" feature enabled
add_action('woocommerce_before_calculate_totals', 'apply_coupon_automatically'); 
function apply_coupon_automatically( $cart ) {
    if ((is_admin() && !defined('DOING_AJAX')))
        return;

    if (did_action('woocommerce_before_calculate_totals') >= 2)
        return;

    // Get coupons with auto apply feature
    $auto_apply = get_option('auto_apply_coupon'); 
    $auto_apply_1st_order = get_option('auto_apply_coupon_1st_order');
    $auto_coupons = array( $auto_apply, $auto_apply_1st_order );
    $applied_coupons = $cart->get_applied_coupons();

    // Check if an "auto apply" coupon is not already applied to cart
    if ( count( array_intersect( $applied_coupons, $auto_coupons ) ) === 0 ) {
        // Check if it's a new customer and for coupon(s) with "auto apply" feature
        if ( ( ! is_paying_customer() && ! empty($auto_apply_1st_order) ) ) {
            $cart->apply_coupon( $auto_apply_1st_order );
        } elseif ( ! empty($auto_apply) && is_paying_customer() ) {
            $cart->apply_coupon( $auto_apply );
        }
    }
}

Code goes in your child theme's functions.php file, or in a plugin file. Tested and works.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Hello again Sir and so many thanks for your response. While deeply apologizing, I should say that the code does not work based on "normal" and just work for "1st customer order. I have tested on several theme to insure that is not the problem on my site.Would you mind checking it again please? – Mohsen Jul 29 '23 at 10:01