1
votes

I want to create several shipping rates in WooCommerce, based on a different fixed cost per product, which I'm saving as meta values. I'd like to offer a different selection of rates for each of my shipping zones: for example, in the UK I'd like to have UK First and UK Second, each of which has a different cost for each product.

I've created the first of my shipping methods, and I can add it to a shipping zone. But when I try to check out in that zone, the cart says No shipping options were found. Can anyone spot what I'm doing wrong?

  • I've turned on debug mode and confirmed that I really am checking out in the UK zone.
  • I've also tried just adding the rate and returning from the top of calculate_shipping:
$this->add_rate(
    $rate = array(
        'id'    => $this->id,
        'label' => $this->title,
        'cost'  => 101,
    )
);
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {

    function shipping_method_uk_first_init() {

        class Shipping_Method_UK_First extends \WC_Shipping_Method {

            private string $meta_key = 'shipping_uk_1st';

            public function __construct() {
                $this->id                 = 'shipping_method_uk_first';
                $this->method_title       = __( 'UK First' );
                $this->method_description = __( 'Royal Mail first class' );
                $this->enabled            = 'yes';
                $this->title              = 'UK First Class';
                $this->supports           = array(
                    'shipping-zones',
                    'instance-settings',
                );
                $this->init();
            }

            function init() {
                $this->init_form_fields();
                $this->init_settings();

                add_action(
                    'woocommerce_update_options_shipping_' . $this->id,
                    array(
                        $this,
                        'process_admin_options',
                    )
                );
            }

            public function calculate_shipping( $package = array() ) {
                $cost = 0;

                foreach ( $package['contents'] as $item_id => $values ) {
                    $product                      = $values['data'];
                    $product_shipping_method_cost = $product->get_meta( $this->meta_key );
                    $cost += floatval( $product_shipping_method_cost );
                }

                $rate = array(
                    'id'    => $this->id,
                    'label' => $this->title,
                    'cost'  => $cost,
                );

                $this->add_rate( $rate );
            }
        }

    }

    add_action( 'woocommerce_shipping_init', 'shipping_method_uk_first_init' );

    function add_shipping_method_uk_first( $methods ) {
        $methods['shipping_method_uk_first'] = 'Shipping_Method_UK_First';

        return $methods;
    }

    add_filter( 'woocommerce_shipping_methods', 'add_shipping_method_uk_first' );

}
1

1 Answers

2
votes

There are some mistakes and missing things. I have also added a product custom field to product shipping options. Try the following:

if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {

    function shipping_method_uk_first_init() {

        class Shipping_Method_UK_First extends \WC_Shipping_Method {

            public function __construct( $instance_id = 0 ) {
                $this->id                 = 'shipping_method_uk_first';
                $this->instance_id        = absint( $instance_id );
                $this->method_title       = __( 'UK First' );
                $this->method_description = __( 'Royal Mail first class' );
                $this->enabled            = 'yes';
                $this->meta_key           = 'shipping_uk_1st'; // <= HERE define the related product meta key
                $this->title              = __('UK First Class' );
                $this->supports           = array(
                    'shipping-zones',
                    'instance-settings',
                );
                $this->init();
            }

            function init() {
                $this->init_form_fields();
                $this->init_settings();

                add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
            }

            public function calculate_shipping( $package = array() ) {
                $cost = 0; // Initializing

                foreach ( $package['contents'] as $item_key => $item ) {
                    // Get the parent variable product for product variation items
                    $product = $item['variation_id'] > 0 ? wc_get_product( $item['product_id']) : $item['data'];

                    $cost += floatval( $product->get_meta( $this->meta_key ) );
                }

                $rate = array(
                    'id'    => $this->id,
                    'label' => $this->title,
                    'cost'  => $cost,
                );

                $this->add_rate( $rate );
            }
        }

    }

    add_action( 'woocommerce_shipping_init', 'shipping_method_uk_first_init' );

    function add_shipping_method_uk_first( $methods ) {
        $methods['shipping_method_uk_first'] = 'Shipping_Method_UK_First';

        return $methods;
    }

    add_filter( 'woocommerce_shipping_methods', 'add_shipping_method_uk_first' );

    // Add a custom field to product shipping options
    function add_custom_field_product_options_shipping() {
        global $product_object;

        echo '</div><div class="options_group">'; // New option group

        woocommerce_wp_text_input( array(
            'id'          => 'shipping_uk_1st',
            'label'       => __( 'UK First shipping cost', 'woocommerce' ),
            'placeholder' => '',
            'desc_tip'    => 'true',
            'description' => __( 'Enter the UK First shipping cost value here.', 'woocommerce' ),
            'value'       => (float) $product_object->get_meta( 'shipping_uk_1st' ),
        ) );
    }

    // Save product custom field shipping option value
    function save_custom_field_product_options_shipping( $product ) {
        if ( isset($_POST['shipping_uk_1st']) ) {
            $product->update_meta_data( 'shipping_uk_1st', (float) sanitize_text_field($_POST['shipping_uk_1st']) );
        }
    }

    add_action( 'woocommerce_product_options_shipping', 'add_custom_field_product_options_shipping', 5 );
    add_action( 'woocommerce_admin_process_product_object', 'save_custom_field_product_options_shipping' );
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.

enter image description here

enter image description here