0

I'm building some functionality in WooCommerce that allows users to input an externally generated voucher code when adding a product to the cart. This voucher code is then checked to see if it's been previously used and if not I want to apply a generic coupon code to the order.

I have the validation working using the below code

// Define field label name
function get_field_label_name()
{
    return __("Voucher");
}

// Add a custom product note below product meta in single product pages
add_action('woocommerce_before_single_variation', 'my_product_custom_field', 100);
function my_product_custom_field()
{

    $voucher_check = get_field('voucher');
    $voucher_text = get_field('voucher_text');

    if ($voucher_check) {
        echo '<div class="vouchers_single">';
        echo '<p>' . $voucher_text . '</p>';


        woocommerce_form_field('custom_field1', array(
            'type'        => 'text',
            'class'       => array('my-field-class form-row-wide'),
            'label'       => get_field_label_name(),
            'placeholder' => __("The field placeholder…"),
            'required'    => true, // Or false
        ), '');

        echo '</div>';
    }
}

// Check if the custom field value is unique
add_filter('woocommerce_add_to_cart_validation', 'wc_add_on_feature', 20, 3);
function wc_add_on_feature($passed, $product_id, $quantity)
{
    if (isset($_POST['custom_field1']) && !empty($_POST['custom_field1'])) {
        global $wpdb;

        $label = get_field_label_name();
        $value = sanitize_text_field($_POST['custom_field1']);

        // Check if value exits already
        $result = $wpdb->get_var("
            SELECT COUNT(meta_value) FROM {$wpdb->prefix}woocommerce_order_itemmeta
            WHERE meta_key LIKE '$label' AND meta_value LIKE '$value'
        ");

        // If it exist we don't allow add to cart
        if ($result > 0) {
            // Display an error notice
            wc_add_notice(sprintf(__('This "%s" input already exist. Please choose another one…'), $value), 'error');
            $passed = false;
        }
    }
    return $passed;
}

I also have this code snippet to apply the code

add_action('woocommerce_before_cart_table', 'ts_apply_discount_to_cart');

function ts_apply_discount_to_cart()
{

    $order_total = WC()->cart->get_subtotal();

    if ($order_total > 250) {
        $coupon_code = 'retrofitwest';
        if (!WC()->cart->add_discount(sanitize_text_field($coupon_code))) {
            wc_print_notices();
        }
    }
}

But what filter do I need to use to apply the coupon once the custom_field validation is passed?

Any help would be greatly appreciated.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399

1 Answers1

2

In the following, I have revisited mostly all your code:

  • When making an SQL query, always use prepare() function to avoid SQL injections issues. I have added that code in a separated function just for that query.
  • Added a Function to save the voucher code on add to cart as custom cart item data.
  • I have renamed "custom_field1" as "voucher_code".
  • I have changed your last function completely (If the voucher code has been added as custom cart item data, the code is applied as a coupon, if not 'retrofitwest' coupon code is applied instead).

The code (untested as you are using some ACF fields):

// Define field label name
function get_field_label_name()
{
    return __("Voucher");
}

// Check (securely) if a voucher code has been used before
function check_submitted_voucher_code( $value ) {
    global $wpdb;

    return $wpdb->get_var( $wpdb->prepare("
        SELECT COUNT(meta_value) FROM {$wpdb->prefix}woocommerce_order_itemmeta
        WHERE meta_key = '%s' AND meta_value = '%s'
    ", get_field_label_name(), $value ) );
}

// Add a custom product note below product meta in single product pages
add_action('woocommerce_before_single_variation', 'my_product_custom_field', 100);
function my_product_custom_field() {
    if ( get_field('voucher') ) {
        echo '<div class="vouchers_single">
        <p>' . get_field('voucher_text') . '</p>';

        woocommerce_form_field('voucher_code', array(
            'type'        => 'text',
            'class'       => array('my-field-class form-row-wide'),
            'label'       => get_field_label_name(),
            'placeholder' => __("The field placeholder…"),
            'required'    => true, // Or false
        ), '');

        echo '</div>';
    }
}

// Voucher code validation
add_filter('woocommerce_add_to_cart_validation', 'wc_add_on_feature' );
function wc_add_on_feature( $passed ){
    if (isset($_POST['voucher_code']) && ! empty($_POST['voucher_code'])) {
        $value = sanitize_text_field($_POST['voucher_code']);

        // Check if value exits already avoiding add to cart
        if ( check_submitted_voucher_code( $value ) > 0) {
            // Display an error notice
            wc_add_notice(sprintf(__('This "%s" input already exist. Please choose another one…'), $value), 'error');
            $passed = false;
        }
    }
    return $passed;
}

// Save submitted valid voucher code
add_filter('woocommerce_add_cart_item_data', 'save_custom_cart_item_data' );
function save_custom_cart_item_data( $cart_item_data ){
    if (isset($_POST['voucher_code']) && ! empty($_POST['voucher_code'])) {
        // Set the custom data in the cart item
        $cart_item_data['voucher_code'] = sanitize_text_field($_POST['voucher_code']);
    }
    return $cart_item_data;
}

// Apply the discount
add_action('woocommerce_before_calculate_totals', 'applied_cart_discount');
function applied_cart_discount( $cart ){
    if ( is_admin() && ! defined('DOING_AJAX') )
        return;

    $voucher_code  = ''; // Initializing

    // Loop through cart items
    foreach( $cart->get_cart() as $item ) {
        if( isset($item['voucher_code']) ) {
            $voucher_code = $item['voucher_code'];
            break;
        }
    }

    if ( $cart->get_subtotal() > 250 ) {
        $coupon_code = ! empty($voucher_code) ? $voucher_code : 'retrofitwest';
        $cart->apply_coupon($coupon_code);
    }
}

Code goes in functions.php file of your child theme (or in a plugin). It should work.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Hi Loic, I did some testing and noticed that the coupon wasn't being used if there was a voucher code. If the voucher code isn't a duplicate, I only need the restrofitwest coupon to be used to apply the discount. I've updated the bottom function 'applied_cart_discount' to this: if ($cart->get_subtotal() > 250) { if (!empty($voucher_code)) { $coupon_code = 'retrofitwest'; $cart->apply_coupon($coupon_code); } } Can you give me any other pointers please? – Paddy Winsley Aug 31 '23 at 13:11
  • Just the coupon code. It's a generic one for a set discount. Because we don't know the generated voucher is going to be, the best we can do is check for duplicates. If the voucher hasn't been used, then I'd like to set the coupon code for that order. If that makes sense? I appreciate your help! – Paddy Winsley Aug 31 '23 at 13:35
  • I want the coupon 'retrofitwest' to be automatically applied if voucher_code isn't already in the database. – Paddy Winsley Aug 31 '23 at 14:02
  • Apologies, but I can't reply when it's accepted. I'm still learning how WC works. I don't think we need to use this coupon_code_exists as I'm using retrofitwest as a coupon code to apply a discount. I only want to check the voucher_code field. I think the issue is in the applied_cart_discount function. I feel like it's this part that isn't working as the coupon 'retrofitwest' isn't being applied if ( $cart->get_subtotal() > 250 ) { $coupon_code = ! empty($voucher_code) ? $voucher_code : 'retrofitwest'; $cart->apply_coupon($coupon_code); } – Paddy Winsley Aug 31 '23 at 15:14
  • I've already tried that and it doesn't work. – Paddy Winsley Aug 31 '23 at 15:31