I have the following sum
$item_shipping_cost += (float) $this->get_fee( $this->fee, $item_shipping_cost ) * (int) $product_data['quantity'];
This adds up like the this:
Product Price + Item Shipping Cost + Fee * Qty
So if the product price is set at £1.00 and Item Shipping is at £1.00 and the fee is £0.50 and I want 2 items the cost will be a total of £4.00. However, I would like to subtract the value of 'fee' as I only want the fee to be charge on addition products. How would I do this?
This line of code belongs to a WooCommerce Shipping Per Product plugin. The full code is:
class WC_Shipping_Per_Product extends WC_Shipping_Method {
const METHOD_ID = 'per_product';
/**
* Default product shipping cost.
*
* #var int
*/
private $cost;
/**
* Handling fee applied to entire order.
*
* #var int
*/
private $order_fee;
/**
* Constructor.
*
* #param int $instance_id Instance Id for method in zone config.
*/
public function __construct( $instance_id = 0 ) {
parent::__construct( $instance_id );
$this->id = self::METHOD_ID;
$this->method_title = __( 'Per-product', 'woocommerce-shipping-per-product' );
$this->method_description = __( 'Per product shipping allows you to define different shipping costs for products, based on customer location. These costs will be displayed and charged separately from any other shipping methods.', 'woocommerce-shipping-per-product' );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
// Load the form fields.
$this->init_form_fields();
// Define user set variables.
$this->title = $this->get_option( 'title' );
$this->tax_status = $this->get_option( 'tax_status' );
$this->cost = $this->get_option( 'cost' );
$this->fee = $this->get_option( 'fee' );
$this->order_fee = $this->get_option( 'order_fee' );
// Actions.
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Initialise Gateway Settings Form Fields.
*/
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Method Title', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-shipping-per-product' ),
'default' => __( 'Product Shipping', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
'tax_status' => array(
'title' => __( 'Tax Status', 'woocommerce-shipping-per-product' ),
'type' => 'select',
'description' => '',
'default' => 'taxable',
'options' => array(
'taxable' => __( 'Taxable', 'woocommerce-shipping-per-product' ),
'none' => __( 'None', 'woocommerce-shipping-per-product' ),
),
),
'cost' => array(
'title' => __( 'Default Product Cost', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'Cost excluding tax (per product) for products without defined costs. Enter an amount, e.g. 2.50. Entering an amount here will apply a global shipping cost for all products, effectively disabling all other shipping methods', 'woocommerce-shipping-per-product' ),
'default' => '',
'placeholder' => __( 'Disabled, Enter an amount, e.g. 2.50.', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
'fee' => array(
'title' => __( 'Handling Fee (per product)', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'Fee excluding tax. Enter an amount, e.g. 2.50, or a percentage, e.g. 5%. Leave blank to disable.', 'woocommerce-shipping-per-product' ),
'default' => '',
'placeholder' => __( 'Disabled, Enter an amount, e.g. 2.50, or a percentage, e.g. 5%.', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
'order_fee' => array(
'title' => __( 'Handling Fee (per order)', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'Fee excluding tax. Enter an amount, e.g. 2.50, or a percentage, e.g. 5%. Leave blank to disable.', 'woocommerce-shipping-per-product' ),
'default' => '',
'placeholder' => __( 'Disabled, Enter an amount, e.g. 2.50, or a percentage, e.g. 5%.', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
);
}
/**
* Check is per product shipping is enabled for the product.
*
* #param array $product_data The product data form the package array.
* #param array $package Shipping package array.
*
* #return bool
*/
public function is_per_product_shipping_product( array $product_data, array $package ) {
if ( $product_data['quantity'] > 0 ) {
if ( $product_data['data']->needs_shipping() ) {
if ( false !== $this->calculate_product_shipping_cost( $product_data, $package ) ) {
return true;
}
}
}
return false;
}
/**
* Calculate the per product shipping cost if enabled for the product.
*
* #param array $product_data The product data form the package array.
* #param array $package Shipping package array.
*
* #return float|bool
*/
private function calculate_product_shipping_cost( array $product_data, array $package ) {
$rule = false;
$item_shipping_cost = 0;
if ( $product_data['variation_id'] ) {
$rule = woocommerce_per_product_shipping_get_matching_rule( $product_data['variation_id'], $package );
}
if ( false === $rule ) {
$rule = woocommerce_per_product_shipping_get_matching_rule( $product_data['product_id'], $package );
}
if ( $rule ) {
$item_shipping_cost += (float) $rule->rule_item_cost * (int) $product_data['quantity'];
$item_shipping_cost += (float) $rule->rule_cost;
} elseif ( '0' === $this->cost || $this->cost > 0 ) {
// Use default shipping cost.
$item_shipping_cost += (float) $this->cost * (int) $product_data['quantity'];
} else {
// NO default and nothing found - abort.
return false;
}
// Fee.
$item_shipping_cost += (float) $this->get_fee( $this->fee, $item_shipping_cost ) * (int) $product_data['quantity'];
return $item_shipping_cost;
}
/**
* Calculate shipping when this method is used standalone.
*
* #param array $package information.
*/
public function calculate_shipping( $package = array() ) {
$_tax = new WC_Tax();
$taxes = array();
$shipping_cost = 0;
if ( empty( $package['ship_via'] ) || ! in_array( $this->id, $package['ship_via'], true ) ) {
return; // must be a package mark as per product shipping in split_shipping_packages_per_product.
}
// This shipping method loops through products, adding up the cost.
if ( count( $package['contents'] ) > 0 ) {
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['quantity'] > 0 ) {
if ( $values['data']->needs_shipping() ) {
$item_shipping_cost = $this->calculate_product_shipping_cost( $values, $package );
$shipping_cost += $item_shipping_cost;
if ( 'yes' === get_option( 'woocommerce_calc_taxes' ) && 'taxable' === $this->tax_status ) {
$rates = $_tax->get_shipping_tax_rates( $values['data']->get_tax_class() );
$item_taxes = $_tax->calc_shipping_tax( $item_shipping_cost, $rates );
// Sum the item taxes.
foreach ( array_keys( $taxes + $item_taxes ) as $key ) {
$taxes[ $key ] = ( isset( $item_taxes[ $key ] ) ? $item_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
}
}
}
}
}
}
// Add order shipping cost + tax.
if ( $this->order_fee ) {
$order_fee = (float) $this->get_fee( $this->order_fee, $shipping_cost );
$shipping_cost += $order_fee;
if ( 'yes' === get_option( 'woocommerce_calc_taxes' ) && 'taxable' === $this->tax_status ) {
$rates = $_tax->get_shipping_tax_rates();
$item_taxes = $_tax->calc_shipping_tax( $order_fee, $rates );
// Sum the item taxes.
foreach ( array_keys( $taxes + $item_taxes ) as $key ) {
$taxes[ $key ] = ( isset( $item_taxes[ $key ] ) ? $item_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
}
}
}
// Add rate.
$this->add_rate(
array(
'id' => $this->id,
'label' => $this->title,
'cost' => $shipping_cost,
'taxes' => $taxes, // We calc tax in the method.
)
);
}
}
I suppose you mean:
$item_shipping_cost -= ($product_data['quantity'] - 1) * $this->fee;
This will remove fees from all products except one.
Add my line of code after your line:
$item_shipping_cost += (float) $this->get_fee( $this->fee, $item_shipping_cost ) * (int) $product_data['quantity'];
When there is only 1 product, then no fee will be subtracted.
I'm working on my personnal website, and i'm struggling with weight units.... can you help me please ?
First; i have this code that allows me to display item weight subtotal in cart and checkout pages.
// Display the cart item weight in cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'display_custom_item_data', 10, 2 );
function display_custom_item_data( $cart_item_data, $cart_item ) {
if ( $cart_item['data']->get_weight() > 0 ){
$cart_item_data[] = array(
'name' => __( 'Weight subtotal', 'woocommerce' ),
'value' => ( $cart_item['quantity'] * $cart_item['data']->get_weight() ) . ' ' . get_option('woocommerce_weight_unit')
);
}
return $cart_item_data;
}
But i would like to display the subtotals based on country automatically. For exemple if the customer is from USA or Canada i have to convert subtotals to lbs and inch instead of kg and cm.
I have already succeeded in the product page in the "additional information" area, using this code :
// For Weight
add_filter( 'woocommerce_format_weight', 'imperial_format_weight', 20, 2 );
function imperial_format_weight( $weight_string, $weight ) {
$country = WC()->customer->get_shipping_country(); // Customer country
$countries = array( 'US', 'LR', 'MM' ); // Imperial measurement countries
if ( ! in_array( $country, $countries ) ) return $weight_string; // Exit
$weight_unit = get_option( 'woocommerce_weight_unit' );
$weight_string = wc_format_localized_decimal( $weight );
if ( empty( $weight_string ) )
return __( 'N/A', 'woocommerce' ); // No values
if ( $weight_unit == 'kg' ) {
// conversion rate for 'kg' to 'lbs'
$rate = 2.20462;
$label = ' lbs';
} elseif ( $weight_unit == 'g' ) {
// conversion rate for 'g' to 'oz'
$rate = 0.035274;
$label = ' oz';
}
return round( $weight * $rate, 2 ) . $label;
}
and now i would like to know how to display correctly this subtotal in cart and check out pages with the appropriate units per country.
thank you for your help !
I want to display the cost of the product after a discount coupon on the invoice.
I have the following code, but it displays only base price, not the cost after coupon code:
add_filter( 'wpo_wcpdf_woocommerce_totals', 'wpo_wcpdf_woocommerce_totals_custom', 10, 2 );
function wpo_wcpdf_woocommerce_totals_custom( $totals, $order ) {
$totals = array(
'subtotal' => array(
'label' => __('Subtotal', 'wpo_wcpdf'),
'value' => $order->get_subtotal_to_display(),
),
);
return $totals;
}
I tred to change $totals to $discount, but it didn't work.
Your actual code is just removing all totals, displaying the subtotal only. Please try the following hooked function, that will display the discounted subtotal after the discount amount:
add_filter( 'wpo_wcpdf_woocommerce_totals', 'add_discounted_subtotal_to_pdf_invoices', 10, 2 );
function add_discounted_subtotal_to_pdf_invoices( $totals, $order ) {
// Get 'subtotal' raw amount value
$subtotal = strip_tags($totals['cart_subtotal']['value']);
$subtotal = (float) preg_replace('/[^0-9.]+/', '', $subtotal);
// Get 'discount' raw amount value
$discount = strip_tags($totals['discount']['value']);
$discount = (float) preg_replace('/[^0-9.]+/', '', $discount);
$new_totals = array();
// Loop through totals lines
foreach( $totals as $key => $values ){
$new_totals[$key] = $totals[$key];
// Inset new calculated 'Subtotal discounted' after total discount
if( $key == 'discount' && $discount != 0 && !empty($discount) ){
$new_totals['subtotal_discounted'] = array(
'label' => __('Subtotal discounted', 'wpo_wcpdf'),
'value' => wc_price($subtotal - $discount)
);
}
}
return $new_totals;
}
Code goes in function.php file of your active child theme (or theme).
Tested and works. It should work for you too.
I create a custom shipping method.
(https://docs.woocommerce.com/document/shipping-method-api/)
In the calculate_shipping function i want to set the price of my shipping method based on the shipping zone:
If shipping zone is 'Zone 1' set price to 15
else set price to 20
So i want to know, how can i get the current shipping zone ?
I already try to see this doc: https://docs.woocommerce.com/wc-apidocs/class-WC_Shipping_Zone.html
Maybe i don't understand...
public function calculate_shipping( $package=Array()) {
global $woocommerce;
$cart_total=$woocommerce->cart->cart_contents_total;
$current_hour=date('His');
$start_hour='110000';
$end_hour='210000';
// CONDITIONAL CHECK
// OPEN HOUR
if($current_hour >= $start_hour && $current_hour <= $end_hour){
$is_open=true;
}else{
$is_open=false;
}
// price PER ZONE
$zone=""; // need to know how to get zone name or zone id
if($zone=='Zone 1' && $cart_total>='60'){
$min_order=true;
$cost = 6;
}elseif($zone=='Zone 2' && $cart_total>='120'){
$min_order=true;
$cost = 8;
}elseif($zone=='Zone 3' && $cart_total>='180'){
$min_order=true;
$cost = 9;
}else{
$min_order=false;
$cost = 0;
}
if($is_open==true && $min_order==true){
$allowed=true;
}else{
$allowed=false;
}
$rate = array(
'id' => $this->id,
'label' => 'Livraison Express T '.wc_get_shipping_zone().' T',
'cost' => $cost,
);
// Register the rate
if($allowed==true){
$this->add_rate( $rate );
}
}
I find a solution myself
Like that:
$shipping_zone = WC_Shipping_Zones::get_zone_matching_package( $package );
$zone=$shipping_zone->get_zone_name();
Complete function:
public function calculate_shipping( $package=Array()) {
global $woocommerce;
$shipping_zone = WC_Shipping_Zones::get_zone_matching_package( $package );
$zone=$shipping_zone->get_zone_name();
if($zone=='Zone 1'){
$cost = 6;
}else{
$cost=12;
}
$rate = array(
'id' => $this->id,
'label' => 'Delivery Name',
'cost' => $cost,
);
$this->add_rate( $rate );
}
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
));
}
}