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' );
}
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.
Related
I have a user role called "Wholesale" already created.
I have used to following snippet to create a custom field on every product that allows me to input some value in it:
function snippet_add_custom_pricing() {
$args = array(
'label' => __( 'Wholesale', 'woocommerce' ),
'placeholder' => __( 'Price for wholesale', 'woocommerce' ),
'id' => 'snippet_wholesale',
'desc_tip' => true,
'description' => __( 'Price for Wholesale.', 'woocommerce' ),
);
woocommerce_wp_text_input( $args );
}
add_action( 'woocommerce_product_options_pricing', 'snippet_add_custom_pricing' );
function snippet_save_custom_meta( $post_id ) {
// Value of the price
$pricing = isset( $_POST[ 'snippet_wholesale' ] ) ? sanitize_text_field( $_POST[ 'snippet_wholesale' ] ) : '';
// Name of the product
$product = wc_get_product( $post_id );
// Saves Metafield
$product->update_meta_data( 'snippet_wholesale', $pricing );
$product->save();
}
add_action( 'woocommerce_process_product_meta', 'snippet_save_custom_meta' );
This is working, now I just want to show the values introduced here to all Wholesale users, so that when they login they get the price that is inserted on this field and not the regular price. Is this possible?
I've tried this thread but it did not work for me.
I have been using this technique to update prices on the front end, I just modified it a little bit for you, and I hope it will work for you.
add_filter( 'woocommerce_get_price_html', 'get_wholesaler_price', 100, 2 );
function get_wholesaler_price( $price, $product ) {
if(!is_user_logged_in()) {
return $price;
}else{
$user = wp_get_current_user();
$user_roles = $user->roles;
if(in_array('wholesaler', $user_roles)){
$price = get_post_meta($product->id,'snippet_wholesale', true);
//or
//$price = $product->get_meta_data('snippet_wholesale');
return $price;
}else{
return $price;
}
}
}
I inserted two radio buttons as product add ons by using the following to calculate extra packaging cost. But I have two problems.
First instead of getting the value of my custom field eg 4.35 it gets a value of 4.00.
Second how can I update the sales price in my product page when I choose an option
function add_custom_fees_before_add_to_cart() {
global $product;
$myfee = floatval(get_post_meta(get_the_ID(), '_skrprom', TRUE)) + floatval(0.90);
$args = array(
'type' => 'radio',
'class' => array( 'form-row-wide' ),
'options' => array(
'' => 'Τηλεφωνική Παραγγελία',
$myfee => 'Έξοδα Συσκευασίας '.$myfee.'€',
),
'default' => ''
);
?>
<div class="custom-fees-wrap">
<label for="iconic-engraving"><?php _e( 'Customize Your Order!', 'textdomain' ); ?></label>
<?php woocommerce_form_field( 'custom_fees', $args, '' ); ?>
</div>
<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_fees_before_add_to_cart', 99 );
function save_value_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
$custom_fees = filter_input( INPUT_POST, 'custom_fees' );
if ( empty( $custom_fees ) ) {
return $cart_item_data;
}
$cart_item_data['custom_fees'] = $custom_fees;
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_value_add_cart_item_data', 99, 3 );
function calculate_add_cart_fee() {
global $woocommerce;
$cart_items = $woocommerce->cart->get_cart();
foreach( $cart_items as $key => $item ) {
if( !isset( $item['custom_fees'] ) && empty( $item['custom_fees'] ) ) continue;
$woocommerce->cart->add_fee( __('Έξοδα Συσκευασίας', 'textdomain'), $item['custom_fees'] );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'calculate_add_cart_fee', 99 );
This code display tax status in cloumn at admin product list page. but i need to display tax class how can i display it ?
add_filter( 'manage_edit-product_columns', 'tax_status_product_column');
function tax_status_product_column($columns){
$new_columns = [];
foreach( $columns as $key => $column ){
$new_columns[$key] = $columns[$key];
if( $key == 'is_in_stock' ) {
$new_columns['tax_status'] = __( 'Tax status','woocommerce');
}
}
return $new_columns;
}
add_action( 'manage_product_posts_custom_column', 'tax_status_product_column_content', 10, 2 );
function tax_status_product_column_content( $column, $post_id ){
if( $column == 'tax_status' ){
global $post, $product;
// Excluding variable and grouped products
if( is_a( $product, 'WC_Product' ) ) {
$args = array(
'taxable' => __( 'Taxable', 'woocommerce' ),
'shipping' => __( 'Shipping only', 'woocommerce' ),
'none' => _x( 'None', 'Tax status', 'woocommerce' ),
);
echo $args[$product->get_tax_status()];
}
}
}
To display a product tax class column in admin product list, use the following:
add_filter( 'manage_edit-product_columns', 'tax_class_product_column');
function tax_class_product_column($columns){
$new_columns = [];
foreach( $columns as $key => $column ){
$new_columns[$key] = $columns[$key];
if( $key == 'is_in_stock' ) {
$new_columns['tax_class'] = __( 'Tax class','woocommerce');
}
}
return $new_columns;
}
add_action( 'manage_product_posts_custom_column', 'tax_class_product_column_content', 10, 2 );
function tax_class_product_column_content( $column, $post_id ){
if( $column == 'tax_class' ){
global $post, $product;
// Excluding variable and grouped products
if( is_a( $product, 'WC_Product' ) ) {
$args = wc_get_product_tax_class_options();
echo $args[$product->get_tax_class()];
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Using Woocommerce, I want to force a shipping method when a certain valid coupon is applied at the cart/checkout phase.
Below my code. It extends WC_Shipping_Method, gets the whole woocommerce coupon list, let admin to select one or more, save them and then, when a costumer applies one of those chosen coupons checking out, forces to use only one this shipping method.
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
/* Custom LocalPickup Class */
class WC_Shipping_LocalPickUp_On_Coupon extends WC_Shipping_Method {
public $requires = '';
public function __construct( $instance_id = 0 ) {
$this->id = 'localpickup_on_coupon';
$this->instance_id = absint( $instance_id );
$this->method_title = __( 'Pickup with Coupon', 'Valeo' );
$this->method_description = __( 'Pickup with Coupon', 'Valeo' );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
/*Getting Coupon List */
$args = array(
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'asc',
'post_type' => 'shop_coupon',
'post_status' => 'publish',
);
$coupons = get_posts( $args );
$this->list = [];
foreach($coupons as $item){
$this->list[$item->post_name] = $item->post_title;
}
$this->init();
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function init() {
$this->init_form_fields();
$this->init_settings();
$this->title = $this->get_option( 'title' );
$this->requires = $this->get_option( 'requires' );
}
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Title', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => $this->method_title,
'desc_tip' => true,
),
'requires' => array(
'title' => __( 'Il ritiro in sede richiede...', 'Valeo' ),
'type' => 'multiselect',
'class' => 'valeo_multiselect',
'default' => '',
'options' => $this->list
),
);
}
public function is_available( $package ) {
$has_coupon = false;
$coupons = WC()->cart->get_coupons();
if(!empty($coupons)) {
$couponKey = array_keys($coupons)[0];
if ( in_array( $couponKey, $this->requires ) ) {
if ( $coupons ) {
foreach ( $coupons as $code => $coupon ) {
if ( $coupon->is_valid() ) {
$has_coupon = true;
break;
}
}
}
}
}
return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', $has_coupon, $package, $this );
}
public function calculate_shipping( $package = array() ) {
$rate = array(
'id' => $this->get_rate_id(),
'label' => $this->title,
'cost' => 0,
'taxes' => 0,
'package' => $package,
);
$this->add_rate($rate);
do_action( 'woocommerce_' . $this->id . '_shipping_add_rate', $this, $rate );
}
}
add_filter( 'woocommerce_shipping_methods', 'register_devso_method' );
function register_devso_method( $methods ) {
$methods[ 'localpickup_on_coupon' ] = 'WC_Shipping_LocalPickUp_On_Coupon';
return $methods;
}
}
Here the filter that checks if customer has inserted the chosen coupon/s and (in a "bad" way) removes all woocommerce package rates but the one I declared in the class.
function filter_woocommerce_package_rates( $array ) {
$check = FALSE;
foreach($array as $index => $value){
if(strpos($index, 'localpickup_on_coupon') !== false) { $check = TRUE; }
}
if($check){
foreach($array as $index => $value){
if(!(strpos($index, 'localpickup_on_coupon') !== false)) { unset($array[$index]); }
}
}
return $array;
};
// add the filter
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 1 );
Is there a better way to do this?
I've been endlessly trying to add a shipping method to the following code but with no luck. Could a good samaritan please offer some assistance.
I just need to add a shipping method.
Basically what the code does is add the fields to the shipping zones section in woo commerce setting | Shipping Zones.
I'd like to add a [flat rate to the zone that is created]
I've searched everywhere but with no luck. :-(
<?php
//Creating the Zone.
function DW_shipping_zone_init() {
// Getting the Shipping object
$available_zones = WC_Shipping_Zones::get_zones();
// Get all WC Countries
$all_countries = WC()->countries->get_countries();
//Array to store available names
$available_zones_names = array();
// Add each existing zone name into our array
foreach ($available_zones as $zone ) {
if( !in_array( $zone['zone_name'], $available_zones_names ) ) {
$available_zones_names[] = $zone['zone_name'];
}
}
// Check if our zone 'C' is already there
if( ! in_array( 'C', $available_zones_names ) ){
// Create an empty object
$zone_za = new stdClass();
//Add null attributes
$zone_za->zone_id = null;
$zone_za->zone_order =null;
// Add shipping zone name
$zone_za->zone_name = 'C';
// Instantiate a new shipping zone with our object
$new_zone_za = new WC_Shipping_Zone( $zone_za );
// Add South Africa as location
$new_zone_za->add_location( 'ZA', 'country' );
// Save the zone, if non existent it will create a new zone
$new_zone_za->save();
// Add our shipping method to that zone
$new_zone_za->add_shipping_method( 'some_shipping_method' );
}
add_action( 'init', 'DW_shipping_zone_init' );
You can create plugin for this:
Create one folder inside wp-content/pluigins/ with name "woocommerce-fast-delivery-system"
Inside "woocommerce-fast-delivery-system" folder, create two files as displayed below:
1. fast-delivery-shipping-method.php
<?php
/*
Plugin Name: WooCommerce Fast Delivery Shipping Method
*/
/**
* Check if WooCommerce is active
*/
$active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
if ( in_array( 'woocommerce/woocommerce.php', $active_plugins) ) {
add_filter( 'woocommerce_shipping_methods', 'add_fast_delivery_shipping_method' );
function add_fast_delivery_shipping_method( $methods ) {
$methods[] = 'WC_Fast_Delivery_Shipping_Method';
return $methods;
}
add_action( 'woocommerce_shipping_init', 'fast_delivery_shipping_method_init' );
function fast_delivery_shipping_method_init(){
require_once 'class-fast-delivery-shipping-method.php';
}
}
2. class-fast-delivery-shipping-method.php
<?php
class WC_Fast_Delivery_Shipping_Method extends WC_Shipping_Method{
public function __construct(){
$this->id = 'fast_delivery_shipping_method';
$this->method_title = __( 'Fast Delivery Shipping Method', 'woocommerce' );
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables
$this->enabled = $this->get_option( 'enabled' );
$this->title = $this->get_option( 'title' );
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function init_form_fields(){
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable Fast Delivery Shipping', 'woocommerce' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Method Tittle', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Fast Delivery Shipping', 'woocommerce' ),
)
);
}
public function is_available( $package ){
foreach ( $package['contents'] as $item_id => $values ) {
$_product = $values['data'];
$weight = $_product->get_weight();
if($weight > 10){
return false;
}
}
return true;
}
public function calculate_shipping($package){
//get the total weight and dimensions
$weight = 0;
$dimensions = 0;
foreach ( $package['contents'] as $item_id => $values ) {
$_product = $values['data'];
$weight = $weight + $_product->get_weight() * $values['quantity'];
$dimensions = $dimensions + (($_product->length * $values['quantity']) * $_product->width * $_product->height);
}
//calculate the cost according to the table
switch ($weight) {
case ($weight < 1):
switch ($dimensions) {
case ($dimensions <= 1000):
$cost = 3;
break;
case ($dimensions > 1000):
$cost = 4;
break;
}
break;
case ($weight >= 1 && $weight < 3 ):
switch ($dimensions) {
case ($dimensions <= 3000):
$cost = 10;
break;
}
break;
case ($weight >= 3 && $weight < 10):
switch ($dimensions) {
case ($dimensions <= 5000):
$cost = 25;
break;
case ($dimensions > 5000):
$cost = 50;
break;
}
break;
}
// send the final rate to the user.
$c= $this->add_rate( array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
));
}
}