WooCommerce - disable BACS for one shipping method - php

I would like to disable wire transfer payment method (bacs gateway) for a specific shipping method. I want customers to pay for the shipping if they select Cash on Delivery payment method but I want them also to get free shipping if they select wire transfer method.
I was able to disable COD if they select free shipping, but was not able to disable wire transfer if they select not-free shipping.
How can I disable BACS for that one particular shipping method?

I already had installed the WooCommerce Custom Payment Gateways plugin. That activates some more gateway options so I was working with it and edited one of it's classes.
The following is my modified version of woocommerce-custom-payment-gateways/class-wc-custom_payment_gateway_5.php
<?php
/**
* WC wcCpg5 Gateway Class.
* Built the wcCpg5 method.
*/
class WC_Custom_Payment_Gateway_5 extends WC_Gateway_BACS {
/**
* Constructor for the gateway.
*
* #return void
*/
public function __construct() {
global $woocommerce;
$this->id = 'wcCpg5';
$this->icon = apply_filters( 'woocommerce_wcCpg5_icon', '' );
$this->has_fields = false;
$this->method_title = __( 'Bacs', 'woocommerce' );
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->instructions = $this->get_option( 'instructions', $this->description );
$this->enable_for_methods = $this->get_option( 'enable_for_methods', array() );
// BACS account fields shown on the thanks page and in emails
$this->account_details = get_option( 'woocommerce_bacs_accounts',
array(
array(
'account_name' => $this->get_option( 'account_name' ),
'account_number' => $this->get_option( 'account_number' ),
'sort_code' => $this->get_option( 'sort_code' ),
'bank_name' => $this->get_option( 'bank_name' ),
'iban' => $this->get_option( 'iban' ),
'bic' => $this->get_option( 'bic' )
)
)
);
// Actions
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'save_account_details' ) );
add_action( 'woocommerce_thankyou_bacs', array( $this, 'thankyou_page' ) );
// Customer Emails
add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 );
// Actions.
if ( version_compare( WOOCOMMERCE_VERSION, '2.0.0', '>=' ) )
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( &$this, 'process_admin_options' ) );
else
add_action( 'woocommerce_update_options_payment_gateways', array( &$this, 'process_admin_options' ) );
}
/* Admin Panel Options.*/
function admin_options() {
?>
<h3><?php _e('Custom Payment Gateways 5','wcwcCpg5'); ?></h3>
<table class="form-table">
<?php $this->generate_settings_html(); ?>
</table> <?php
}
/* Initialise Gateway Settings Form Fields. */
public function init_form_fields() {
$shipping_methods = array();
if ( is_admin() )
foreach ( WC()->shipping->load_shipping_methods() as $method ) {
$shipping_methods[ $method->id ] = $method->get_title();
}
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable Bank Transfer', 'woocommerce' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Title', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Direct Bank Transfer', 'woocommerce' ),
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', 'woocommerce' ),
'type' => 'textarea',
'description' => __( 'Payment method description that the customer will see on your checkout.', 'woocommerce' ),
'default' => __( 'Make your payment directly into our bank account. Please use your Order ID as the payment reference. Your order won\'t be shipped until the funds have cleared in our account.', 'woocommerce' ),
'desc_tip' => true,
),
'instructions' => array(
'title' => __( 'Instructions', 'woocommerce' ),
'type' => 'textarea',
'description' => __( 'Instructions that will be added to the thank you page and emails.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'account_details' => array(
'type' => 'account_details'
),
'enable_for_methods' => array(
'title' => __( 'Enable for shipping methods', 'woocommerce' ),
'type' => 'multiselect',
'class' => 'chosen_select',
'css' => 'width: 450px;',
'default' => '',
'description' => __( 'If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.', 'woocommerce' ),
'options' => $shipping_methods,
'desc_tip' => true,
'custom_attributes' => array(
'data-placeholder' => __( 'Select shipping methods', 'woocommerce' )
)
)
);
}
/**
* Check If The Gateway Is Available For Use
*
* #return bool
*/
public function is_available() {
$order = null;
if ( ! $this->enable_for_virtual ) {
if ( WC()->cart && ! WC()->cart->needs_shipping() ) {
return false;
}
if ( is_page( wc_get_page_id( 'checkout' ) ) && 0 < get_query_var( 'order-pay' ) ) {
$order_id = absint( get_query_var( 'order-pay' ) );
$order = wc_get_order( $order_id );
// Test if order needs shipping.
$needs_shipping = false;
if ( 0 < sizeof( $order->get_items() ) ) {
foreach ( $order->get_items() as $item ) {
$_product = $order->get_product_from_item( $item );
if ( $_product->needs_shipping() ) {
$needs_shipping = true;
break;
}
}
}
$needs_shipping = apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
if ( $needs_shipping ) {
return false;
}
}
}
if ( ! empty( $this->enable_for_methods ) ) {
// Only apply if all packages are being shipped via local pickup
$chosen_shipping_methods_session = WC()->session->get( 'chosen_shipping_methods' );
if ( isset( $chosen_shipping_methods_session ) ) {
$chosen_shipping_methods = array_unique( $chosen_shipping_methods_session );
} else {
$chosen_shipping_methods = array();
}
$check_method = false;
if ( is_object( $order ) ) {
if ( $order->shipping_method ) {
$check_method = $order->shipping_method;
}
} elseif ( empty( $chosen_shipping_methods ) || sizeof( $chosen_shipping_methods ) > 1 ) {
$check_method = false;
} elseif ( sizeof( $chosen_shipping_methods ) == 1 ) {
$check_method = $chosen_shipping_methods[0];
}
if ( ! $check_method ) {
return false;
}
$found = false;
foreach ( $this->enable_for_methods as $method_id ) {
if ( strpos( $check_method, $method_id ) === 0 ) {
$found = true;
break;
}
}
if ( ! $found ) {
return false;
}
}
return parent::is_available();
}
}
with this you have the option to enable bacs for shipping methods

Related

WooCommerce custom checkout checkboxes fields validation for one mandatory checkbox

I have three custom checkout fields, and people have to check at least one for the order to go through.
This is only needed for 1 product.
So, I loop through the cart items to check if the product is in the cart, then add the fields. This part works fine:
add_action( 'woocommerce_before_order_notes', 'mmm_add_custom_checkout_field' );
function mmm_add_custom_checkout_field( $checkout ) {
$product_id = 214884;
$in_cart = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
$product_in_cart = $cart_item['product_id'];
if ( $product_in_cart === $product_id ) $in_cart = true;
}
if ( $in_cart ) {
echo '<h2>Membership Application</h2>';
echo '<p>Select all that applies</p>';
woocommerce_form_field( 'read_wog', array(
'type' => 'checkbox',
'class' => array( 'form-row-wide no-req' ),
'required' => true,
'label' => 'I accept term 1',
), $checkout->get_value( 'read_wog' ) );
woocommerce_form_field( 'one_on_one', array(
'type' => 'checkbox',
'class' => array( 'form-row-wide no-req' ),
'required' => true,
'label' => 'I accept term 2',
), $checkout->get_value( 'one_on_one' ) );
woocommerce_form_field( 'mm_sn', array(
'type' => 'checkbox',
'required' => true,
'class' => array( 'form-row-wide no-req' ),
'label' => 'I accept term 3).',
), $checkout->get_value( 'mm_sn' ) );
}
}
The site uses Paypal Express as a payment gateway, and the validation lets people go through Paypal regardless of the checkbox validation. The validation for default fields works fine. The error notice is added when manually refreshing the page though!
Here's the validation code:
add_action( 'woocommerce_checkout_process', 'mmm_validate_new_checkout_field' );
function mmm_validate_new_checkout_field() {
$product_id = 214884;
$in_cart = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
$product_in_cart = $cart_item['product_id'];
if ( $product_in_cart === $product_id ) $in_cart = true;
}
if( $in_cart && !isset($_POST['mm_sn']) && !isset($_POST['one_on_one']) && !isset($_POST['read_wog']) ) {
wc_add_notice( 'You can only have a full membership if you accept at least 1 term', 'error' );
}
}
Any idea how to make it work?
"The site uses PayPal Express as a payment gateway"
This isn't specific enough to be able to advise. If PayPal JS SDK buttons are being used (called smart buttons in the WooCommerce PayPal plugin configuration), then you can add an onClick handler as documented here: https://developer.paypal.com/docs/business/javascript-sdk/javascript-sdk-reference/#oninitonclick
You'll need to edit the outputted JS of how the WooCommerce plugin invokes paypal.Buttons to include such a function.
The following revisited code will throw an error validation message if at least one checkbox has not been checked on checkout:
// Custom function that check if a specific product is in cart
function is_product_in_cart( $product_id ) {
foreach( WC()->cart->get_cart() as $item ) {
if ( in_array( $product_id, array($item['product_id'], $item['variation_id']) ) ) {
return true;
}
}
return false;
}
// Add Custom checkout checkboxes fields
add_action( 'woocommerce_before_order_notes', 'mmm_add_custom_checkout_field' );
function mmm_add_custom_checkout_field( $checkout ) {
$targeted_id = 214884;
if ( is_product_in_cart( $targeted_id ) ) {
echo '<h2>Membership Application</h2>
<p>Select all that applies</p>';
woocommerce_form_field( 'read_wog', array(
'type' => 'checkbox',
'class' => array( 'form-row-wide no-req' ),
'required' => true,
'label' => 'I accept term 1',
), $checkout->get_value( 'read_wog' ) );
woocommerce_form_field( 'one_on_one', array(
'type' => 'checkbox',
'class' => array( 'form-row-wide no-req' ),
'required' => true,
'label' => 'I accept term 2',
), $checkout->get_value( 'one_on_one' ) );
woocommerce_form_field( 'mm_sn', array(
'type' => 'checkbox',
'required' => true,
'class' => array( 'form-row-wide no-req' ),
'label' => 'I accept term 3).',
), $checkout->get_value( 'mm_sn' ) );
}
}
// Custom checkout checkboxes fields validation
add_action( 'woocommerce_checkout_process', 'mmm_validate_new_checkout_field' );
function mmm_validate_new_checkout_field() {
$targeted_id = 214884;
if ( is_product_in_cart( $targeted_id ) && ! ( isset($_POST['read_wog']) || isset($_POST['one_on_one']) || isset($_POST['mm_sn']) ) ) {
wc_add_notice( 'You can only have a full membership if you accept at least 1 term', 'error' );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.

WooCommerce: Display custom checkout fields if specific product IDs are in cart

I am running an online-store selling grills. Only the grills are shipped via a specific carrier where additional informations are needed.
I managed to display a dropdown when a certain product_ID is in the cart. When a specific value is selected a text-area is displayed.
This should happen for about 10 products, not just one.
After reading a number of threads and searching the web, i cant figure out, how to add more than one product_ID.
add_action( 'woocommerce_after_order_notes', 'grills_versandauswahl_checkout_field' );
function grills_versandauswahl_checkout_field( $checkout ) {
$grill_in_cart = grills_is_conditional_product_in_cart ( 125 );
if ( $grill_in_cart === true ) {
echo '<div id="my_custom_checkout_field"><h3>' . __( 'Versandoptionen' ) . '</h3><p style="margin: 0 0 8px;">text</p>';
woocommerce_form_field( 'versandoption', array(
'type' => 'select',
'class' => array( 'wps-drop' ),
'label' => __( 'Versandoptionen' ),
'required' => true,
'options' => array(
'blank' => __( 'Auswählen', 'wps' ),
'fixtermin' => __( 'Fixtermin', 'wps' ),
'avis' => __( 'Telefonisches Avis', 'wps' ),
)
), $checkout->get_value( 'versandoption' ) );
woocommerce_form_field( 'inscription_textbox', array(
'type' => 'textarea',
'class' => array( 'inscription-text form-row-wide' ),
'label' => __( 'Wunschtermin / Ablageort' ),
), $checkout->get_value( 'inscription_textbox' ) );
echo '</div>';
}
}
function grills_is_conditional_product_in_cart( $product_id ) {
//Check to see if user has product in cart
global $woocommerce;
//flag no book in cart
$grill_in_cart = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id === $product_id ) {
//book is in cart!
$grill_in_cart = true;
}
}
return $grill_in_cart;
}
To make your conditional function work for many product Ids use the following:
// Custom conditional function
function grills_in_cart( $product_ids ) {
foreach ( WC()->cart->get_cart() as $item ) {
if ( array_intersect($product_ids, array( $item['product_id'], $item['variation_id']) ) ) {
return true;
}
}
return false;
}
// Conditionally display custom checkout fields for specific product IDS
add_action( 'woocommerce_after_order_notes', 'grills_versandauswahl_checkout_field' );
function grills_versandauswahl_checkout_field( $checkout ) {
// Here define the targeted product IDs in this array
$targeted_ids = array( 125, 132, 154 );
$text_domain = 'woocommerce';
if ( grills_in_cart( $targeted_ids ) ) {
echo '<div id="my_custom_checkout_field">
<h3>' . __( "Versandoptionen", $text_domain ) . '</h3>
<p style="margin: 0 0 8px;">' . __( "text", $text_domain ) . '</p>';
woocommerce_form_field( 'versandoption', array(
'type' => 'select',
'class' => array('wps-drop'),
'label' => __( "Versandoptionen", $text_domain ),
'required' => true,
'options' => array(
'blank' => __( "Auswählen", $text_domain ),
'fixtermin' => __( "Fixtermin", $text_domain ),
'avis' => __( "Telefonisches Avis", $text_domain ),
),
), $checkout->get_value( 'versandoption' ) );
woocommerce_form_field( 'inscription_textbox', array(
'type' => 'textarea',
'class' => array( 'inscription-text form-row-wide' ),
'label' => __( 'Wunschtermin / Ablageort', $text_domain ),
), $checkout->get_value( 'inscription_textbox' ) );
echo '</div>';
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Note: $global $woocommerce; + $woocommerce->cart has been replaced by WC()->cart

Editable admin custom billing fields error issue in Woocommerce 3

I have an error located in this code (that adds in order edit pages editable custom billing fields):
add_filter( 'woocommerce_admin_billing_fields' , 'order_admin_custom_fields' );
function order_admin_custom_fields( $fields ) {
global $theorder;
$fields['billing_address_3'] = array(
'label' => __( 'Home', 'woocommerce' ),
'value'=> get_post_meta( $theorder->get_id(), 'Home', true ),
'show' => true,
'wrapper_class' => 'form-field-wide',
'style' => '',
);
$fields['billing_address_4'] = array(
'label' => __( 'Entrance', 'woocommerce' ),
'value'=> get_post_meta( $theorder->get_id(), 'Entrance', true ),
'show' => true,
'wrapper_class' => 'form-field-wide',
'style' => '',
);
$fields['billing_address_5'] = array(
'label' => __( 'Floor', 'woocommerce' ),
'value'=> get_post_meta( $theorder->get_id(), 'Floor', true ),
'show' => true,
'wrapper_class' => 'form-field-wide',
'style' => '',
);
return $fields;
}
The guilty line: 'value'=> get_post_meta( $theorder->get_id(), 'Home', true ),
Report on the error:
example.com [Wed Jul 04 02:36:28 2018] [error] [pid 148187] sapi_apache2.c(362): [client 37.146.123.6:33708] PHP Fatal error: Uncaught Error: Call to a member function get_id() on null in /home/c/cb36070/example.com/public_html/wp-content/themes/theme-name/functions.php:607\nStack trace:\n#0 /home/c/cb36070/example.com/public_html/wp-includes/class-wp-hook.php(286): order_admin_custom_fields(Array)\n#1 /home/c/cb36070/example.com/public_html/wp-includes/plugin.php(203): WP_Hook->apply_filters(Array, Array)\n#2 /home/c/cb36070/example.com/public_html/wp-content/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-data.php(87): apply_filters('woocommerce_adm...', Array)\n#3 /home/c/cb36070/example.com/public_html/wp-content/plugins/woocommerce/includes/admin/meta-boxes/class-wc-meta-box-order-data.php(526): WC_Meta_Box_Order_Data::init_address_fields()\n#4 /home/c/cb36070/example.com/public_html/wp-includes/class-wp-hook.php(286): WC_Meta_Box_Order_Data::save(951, Object(WP_Post))\n#5 /home/c/cb36070/example.com/public_html/wp-includes/class-wp-hook.php(310): WP_Hook->apply_filters(NULL, Array)\n#6 /home/c/cb360 in /home/c/cb36070/example.com/public_html/wp-content/themes/theme-name/functions.php on line 607
But I need to get the saved values for those custom billing fields.
How can I solve this error issue, and get the custom billing field values?
The added (saved) meta data is made with this code:
add_action( 'woocommerce_checkout_update_order_meta', 'custom_checkout_field_update_order_meta' );
function custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['billing_address_3'] ) ) {
update_post_meta( $order_id, 'Home', sanitize_text_field( $_POST['billing_address_3'] ) );
}
if ( ! empty( $_POST['billing_address_4'] ) ) {
update_post_meta( $order_id, 'Entrance', sanitize_text_field( $_POST['billing_address_4'] ) );
}
if ( ! empty( $_POST['billing_address_5'] ) ) {
update_post_meta( $order_id, 'Floor', sanitize_text_field( $_POST['billing_address_5'] ) );
}
}
Any help is appreciated.
The problem is that $theorder is not defined and you can't use get_id() method on it.
But the main problem in your code comes from your checkout billing fields first. They should be set, displayed and saved as follow (taking a particular care of the meta_keys to be used when data is saved):
// Frontend: Display the custom billing fields (in checkout and my account)
add_filter( 'woocommerce_billing_fields' ,'add_custom_billing_fields', 20, 1 );
function add_custom_billing_fields( $fields ) {
$fields['billing_address_3'] = array(
'label' => __( 'Home', 'woocommerce' ),
'placeholder' => _x('Fill in your Home', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-wide'),
'clear' => true
);
$fields['billing_address_4'] = array(
'label' => __( 'Entrance', 'woocommerce' ),
'placeholder' => _x('Fill in your Entrance', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-wide'),
'clear' => true
);
$fields['billing_address_5'] = array(
'label' => __( 'Floor', 'woocommerce' ),
'placeholder' => _x('Fill in your Floor', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
// Save the custom billing fields (once order is placed)
add_action( 'woocommerce_checkout_create_order', 'save_custom_billingt_fields', 20, 2 );
function save_custom_billingt_fields( $order, $data ) {
if ( isset( $_POST['billing_address_3'] ) && ! empty( $_POST['billing_address_3'] ) ) {
$order->update_meta_data('_billing_address_3', sanitize_text_field( $_POST['billing_address_3'] ) );
update_user_meta( $order->get_customer_id(), 'billing_address_3', sanitize_text_field( $_POST['billing_address_3'] ) );
}
if ( isset( $_POST['billing_address_4'] ) && ! empty( $_POST['billing_address_4'] ) ) {
$order->update_meta_data('_billing_address_4', sanitize_text_field( $_POST['billing_address_4'] ) );
update_user_meta( $order->get_customer_id(), 'billing_address_4', sanitize_text_field( $_POST['billing_address_4'] ) );
}
if ( isset( $_POST['billing_address_5'] ) && ! empty( $_POST['billing_address_5'] ) ) {
$order->update_meta_data('_billing_address_5', sanitize_text_field( $_POST['billing_address_5'] ) );
update_user_meta( $order->get_customer_id(), 'billing_address_5', sanitize_text_field( $_POST['billing_address_5'] ) );
}
}
You will see that those custom fields are also in My account > addresses > Edit billing address. And everything related is auto sync. No need of additional validation or saving code...
Now in admin order pages, for woocommerce_admin_billing_fields admin hook, the 'value' key doesn't exist in the field array and it is the guilty.
As you have correctly set and saved your custom checkout fields, you don't need any 'value' array key, as the data will be auto populated, if it exist. So your code will be:
// Backend: Display editable custom billing fields
add_filter( 'woocommerce_admin_billing_fields' , 'order_admin_custom_fields' );
function order_admin_custom_fields( $fields ) {
global $the_order;
$fields['address_3'] = array(
'label' => __( 'Home', 'woocommerce' ),
'show' => true,
'wrapper_class' => 'form-field-wide',
'style' => '',
);
$fields['address_4'] = array(
'label' => __( 'Entrance', 'woocommerce' ),
'show' => true,
'wrapper_class' => 'form-field-wide',
'style' => '',
);
$fields['address_5'] = array(
'label' => __( 'Floor', 'woocommerce' ),
'show' => true,
'wrapper_class' => 'form-field-wide',
'style' => '',
);
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
The error is now definitively gone

Create a new shipping method in woocommerce 3

I need help in generating new shipping method in woocommerce version 3+. The name for new field is "Nextday delivery". Like the flat rate it also need to be there in the method but it was not displayed in the drop down select field.
The below is the code which I tried. But it's not working for me.
function request_a_shipping_quote_init() {
if ( ! class_exists( 'WC_Request_Shipping_Quote_Method' ) ) {
class WC_Request_Shipping_Quote_Method extends WC_Shipping_Method {
public function __construct() {
$this->id = 'request_a_shipping_quote'; // Id for your shipping method. Should be uunique.
$this->method_title = __( 'Request a Shipping Quote' ); // Title shown in admin
$this->method_description = __( 'Shipping method to be used where the exact shipping amount needs to be quoted' ); // Description shown in admin
$this->title = "Request a Shipping Quote"; // This can be added as an setting but for this example its forced.
$this->supports = array(
'shipping-zones'
);
$this->init();
}
function init() {
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable', 'dc_raq' ),
'type' => 'checkbox',
'description' => __( 'Enable this shipping method.', 'dc_raq' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Title', 'dc_raq' ),
'type' => 'text',
'description' => __( 'Title to be displayed on site', 'dc_raq' ),
'default' => __( 'Request a Quote', 'dc_raq' )
),
);
}
public function calculate_shipping( $packages = array() ) {
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '0.00',
'calc_tax' => 'per_item'
);
$this->add_rate( $rate );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'request_a_shipping_quote_init' );
function request_shipping_quote_shipping_method( $methods ) {
$methods['request_shipping_quote_shipping_method'] = 'WC_Request_Shipping_Quote_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'request_shipping_quote_shipping_method' );
I need it to come in the dropdown like flatrate free shipping etc but it was not coming in the dropdown.
There were some missing things and others unnecessary. The correct way to make it work is:
add_action('woocommerce_shipping_init', 'request_shipping_quote_method');
function request_shipping_quote_method() {
if ( ! class_exists( 'WC_Request_Shipping_Quote_Method' ) ) {
class WC_Request_Shipping_Quote_Method extends WC_Shipping_Method {
public function __construct( $instance_id = 0) {
$this->id = 'request_shipping_quote';
$this->instance_id = absint( $instance_id );
$this->domain = 'rasq';
$this->method_title = __( 'Request a Shipping Quote', $this->domain );
$this->method_description = __( 'Shipping method to be used where the exact shipping amount needs to be quoted', $this->domain );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
}
## Load the settings API
function init() {
$this->init_form_fields();
$this->init_settings();
$this->enabled = $this->get_option( 'enabled', $this->domain );
$this->title = $this->get_option( 'title', $this->domain );
$this->info = $this->get_option( 'info', $this->domain );
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'type' => 'text',
'title' => __('Title', $this->domain),
'description' => __( 'Title to be displayed on site.', $this->domain ),
'default' => __( 'Request a Quote ', $this->domain ),
),
'cost' => array(
'type' => 'text',
'title' => __('Coast', $this->domain),
'description' => __( 'Enter a cost', $this->domain ),
'default' => '',
),
);
}
public function calculate_shipping( $packages = array() ) {
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '0',
'calc_tax' => 'per_item'
);
$this->add_rate( $rate );
}
}
}
}
add_filter('woocommerce_shipping_methods', 'add_request_shipping_quote');
function add_request_shipping_quote( $methods ) {
$methods['request_shipping_quote'] = 'WC_Request_Shipping_Quote_Method';
return $methods;
}
Code goes in function.php file of your active child theme (active theme).
Tested and works.
Here the shipping method selector now displays this "Request a shipping coast" method:
Once selected and added, it's created this time:
If you edit it:

WooCommerce Conditional Custom Fields if Product Category in Cart

OK, I'm stumped. I've searched and read several posts including the related post Checking products in cart based on category name woocommerce? from where I derived much of this code, and Woocommerce - Add filter to display (or hide) custom checkout field if product ID == # which is specific to Product IDs, not Category IDs.
I want to display the sdc_custom_checkout_field if, and only if, the target category ID (237 in this case) is in the cart.
I tried commenting out the sdc_custom_checkout_field function and using a simple test shown below, but kept getting "Nope!", so I assume the query is incorrect.
add_action( 'woocommerce_before_order_notes', 'sdc_custom_checkout_field' );
function sdc_custom_checkout_field( $checkout ) {
//Check if Product in Cart
//$product_in_cart = check_product_in_cart();
//Product is in cart so show additional fields
if ( $product_in_cart === true ) {
echo '<div id="my_custom_checkout_field"><h3>' . __( 'Duplicate Card Information' . '</h3><br>');
woocommerce_form_field( 'dupecard_location', array(
'type' => 'text',
'class' => array( 'dupecard-location form-row-wide' ),
'label' => __( 'Course Location' ),
), $checkout->get_value( 'dupecard_location' ) );
woocommerce_form_field( 'dupecard_instructor', array(
'type' => 'text',
'class' => array( 'dupecard-instructor form-row-wide' ),
'label' => __( 'Instructor Name' ),
), $checkout->get_value( 'dupecard_instructor' ) );
woocommerce_form_field( 'dupecard_requestor_name', array(
'type' => 'text',
'class' => array( 'dupecard-requestor-name form-row-wide' ),
'label' => __( 'Requestor Name' ),
), $checkout->get_value( 'dupecard_requestor_name' ) );
woocommerce_form_field( 'dupecard_requestor_email', array(
'type' => 'text',
'class' => array( 'dupecard-requestor-email form-row-wide' ),
'label' => __( 'Requestor Email' ),
), $checkout->get_value( 'dupecard_requestor_email' ) );
woocommerce_form_field( 'dupecard_requestor_phone', array(
'type' => 'text',
'class' => array( 'dupecard-requestor-phone form-row-wide' ),
'label' => __( 'Requestor Phone' ),
), $checkout->get_value( 'dupecard_requestor_phone' ) );
echo '</div>';
}
}
function check_product_in_cart() {
//Check to see if user has product in cart
global $woocommerce;
//assign default negative value
$product_in_cart = false;
// start cart items fetch loop
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$terms = get_the_terms( $_product->id, 'product_cat' );
// second level loop search, in case some items have several categories
$cat_ids = array();
foreach ($terms as $term) {
$cat_ids[] = $term->term_id;
}
if(in_array(237, (array)$cat_ids)) {
//category is in cart!
$product_in_cart = true;
}
}
return $product_in_cart;
}
Here’s the test snippet:
if ($item_in_cart === true) {echo 'YES';}
else {echo 'Nope!';}
I also replaced
$item_in_cart
with
$product_in_cart
but it made no difference.
********** EDIT RESPONSE TO #PRAFULLA **********
#Prafulla - thanks for your input. I appreciate it. I modified my snippet as follows, incorporating yours, but was unable to get it to work. I'm a PHP newbie, so, no surprise. Do you have additional advice?
add_action( 'woocommerce_before_order_notes', 'sdc_custom_checkout_field' );
function sdc_custom_checkout_field( $checkout ) {
//Check if Product in Cart
$your_product_category = is_category_in_cart();
//Product is in cart so show additional fields
if ( $your_product_category === true ) {
echo '<div id="my_custom_checkout_field"><h3>' . __( 'Duplicate Card Information' . '</h3><br>');
woocommerce_form_field( 'dupecard_location', array(
'type' => 'text',
'class' => array( 'dupecard-location form-row-wide' ),
'label' => __( 'Course Location' ),
), $checkout->get_value( 'dupecard_location' ) );
woocommerce_form_field( 'dupecard_instructor', array(
'type' => 'text',
'class' => array( 'dupecard-instructor form-row-wide' ),
'label' => __( 'Instructor Name' ),
), $checkout->get_value( 'dupecard_instructor' ) );
woocommerce_form_field( 'dupecard_requestor_name', array(
'type' => 'text',
'class' => array( 'dupecard-requestor-name form-row-wide' ),
'label' => __( 'Requestor Name' ),
), $checkout->get_value( 'dupecard_requestor_name' ) );
woocommerce_form_field( 'dupecard_requestor_email', array(
'type' => 'text',
'class' => array( 'dupecard-requestor-email form-row-wide' ),
'label' => __( 'Requestor Email' ),
), $checkout->get_value( 'dupecard_requestor_email' ) );
woocommerce_form_field( 'dupecard_requestor_phone', array(
'type' => 'text',
'class' => array( 'dupecard-requestor-phone form-row-wide' ),
'label' => __( 'Requestor Phone' ),
), $checkout->get_value( 'dupecard_requestor_phone' ) );
echo '</div>';
}
}
function is_category_in_cart( $your_product_category = 237 ){
global $woocommerce;
$products_in_cart = $woocommerce->cart->get_cart();
$product_types_in_cart = array_column( $products_in_cart, 'data' );
//if ( $product_types_in_cart[0]->product_type == 'subscription' ) { this is what I have tested
if ( $product_types_in_cart[0]->product_cat == $your_product_category ) {
return true;
}
return $your_product_category;
}
After much work, research, and the assistance of some paid help on another website, this is the working result:
// Add the field to the checkout
add_action( 'woocommerce_before_order_notes', 'sdc_custom_checkout_field' );
function sdc_custom_checkout_field( $checkout ) {
if( check_product_category() ){
echo '<div id="sdc_checkout_field"><h3>' . __( 'Duplicate Card Information' . '</h3><br>');
woocommerce_form_field( 'dupecard_location', array(
'type' => 'text',
'required' => true,
'class' => array( 'dupecard-location form-row-wide' ),
'label' => __( 'Course Location' ),
), $checkout->get_value( 'dupecard_location' ) );
woocommerce_form_field( 'dupecard_instructor', array(
'type' => 'text',
'required' => true,
'class' => array( 'dupecard-instructor form-row-wide' ),
'label' => __( 'Instructor Name' ),
), $checkout->get_value( 'dupecard_instructor' ) );
woocommerce_form_field( 'dupecard_requestor_name', array(
'type' => 'text',
'required' => true,
'class' => array( 'dupecard-requestor-name form-row-wide' ),
'label' => __( 'Requestor Name' ),
), $checkout->get_value( 'dupecard_requestor_name' ) );
woocommerce_form_field( 'dupecard_requestor_email', array(
'type' => 'text',
'required' => true,
'class' => array( 'dupecard-requestor-email form-row-wide' ),
'label' => __( 'Requestor Email' ),
), $checkout->get_value( 'dupecard_requestor_email' ) );
woocommerce_form_field( 'dupecard_requestor_phone', array(
'type' => 'text',
'required' => true,
'class' => array( 'dupecard-requestor-phone form-row-wide' ),
'label' => __( 'Requestor Phone' ),
), $checkout->get_value( 'dupecard_requestor_phone' ) );
echo '</div>';
}
}
function check_product_category(){
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
if( is_category_in_cart( $product_id ) ){
return true;
}
}
return false;
}
function is_category_in_cart( $product_id ){
return has_term( 237,'product_cat', get_post( $product_id ) );
}
/*Save to DB as post meta*/
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['dupecard_location'] ) ) {
update_post_meta( $order_id, 'dupecard_location', sanitize_text_field( $_POST['dupecard_location'] ) );
}
if ( ! empty( $_POST['dupecard_instructor'] ) ) {
update_post_meta( $order_id, 'dupecard_instructor', sanitize_text_field( $_POST['dupecard_instructor'] ) );
}
if ( ! empty( $_POST['dupecard_requestor_name'] ) ) {
update_post_meta( $order_id, 'dupecard_requestor_name', sanitize_text_field( $_POST['dupecard_requestor_name'] ) );
}
if ( ! empty( $_POST['dupecard_requestor_email'] ) ) {
update_post_meta( $order_id, 'dupecard_requestor_email', sanitize_text_field( $_POST['dupecard_requestor_email'] ) );
}
if ( ! empty( $_POST['dupecard_requestor_phone'] ) ) {
update_post_meta( $order_id, 'dupecard_requestor_phone', sanitize_text_field( $_POST['dupecard_requestor_phone'] ) );
}
}
This is how I get the job done in my case , please try it and note where you need to give your input on this code as It is not written for direct use.
function is_category_in_cart( $your_product_category = null ){
global $woocommerce;
$products_in_cart = $woocommerce->cart->get_cart();
$product_types_in_cart = array_column( $products_in_cart, 'data' );
//if ( $product_types_in_cart[0]->product_type == 'subscription' ) { this is what I have tested
if ( $product_types_in_cart[0]->product_cat == $your_product_category ) {
return true;
}
}

Categories