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.
Related
I am trying to combine the two responses from [https://stackoverflow.com/questions/62943477/set-quantity-minimum-maximum-and-step-at-product-level-in-woocommerce ] and Decimal quantity step for specific product categories in WooCommerce but I can't figure out how to edit the first lot of code so that it allows decimal quantity steps.
// Displaying quantity setting fields on admin product pages
add_action( 'woocommerce_product_options_pricing', 'wc_qty_add_product_field' );
function wc_qty_add_product_field() {
global $product_object;
$values = $product_object->get_meta('_qty_args');
woocommerce_wp_text_input( array(
'id' => 'qty_step',
'type' => 'number',
'label' => __( 'Quantity step', 'woocommerce-quantity-step' ),
'placeholder' => '',
'desc_tip' => 'true',
'description' => __( 'Optional. Set quantity step (a number greater than 0)', 'woocommerce' ),
'custom_attributes' => array( 'step' => 'any', 'min' => '1'),
'value' => isset($values['qty_step']) && $values['qty_step'] > 1 ? (int) $values['qty_step'] : 1,
) );
echo '</div>';
}
// Save quantity setting fields values
add_action( 'woocommerce_admin_process_product_object', 'wc_save_product_quantity_settings' );
function wc_save_product_quantity_settings( $product ) {
if ( isset($_POST['qty_args']) ) {
$values = $product->get_meta('_qty_args');
'qty_step' => isset($_POST['qty_step']) && $_POST['qty_step'] > 1 ? (int) wc_clean($_POST['qty_step']) : 1,
) );
} else {
$product->update_meta_data( '_qty_args', array() );
}
}
// The quantity settings in action on front end
add_filter( 'woocommerce_quantity_input_args', 'filter_wc_quantity_input_args', 99, 2 );
function filter_wc_quantity_input_args( $args, $product ) {
if ( $product->is_type('variation') ) {
$parent_product = wc_get_product( $product->get_parent_id() );
$values = $parent_product->get_meta( '_qty_args' );
} else {
$values = $product->get_meta( '_qty_args' );
}
if ( ! empty( $values ) ) {
// Step value
if ( isset( $values['qty_step'] ) && $values['qty_step'] > 1 ) {
$args['step'] = $values['qty_step'];
}
}
return $args;
}
// Ajax add to cart, set "min quantity" as quantity on shop and archives pages
add_filter( 'woocommerce_loop_add_to_cart_args', 'filter_loop_add_to_cart_quantity_arg', 10, 2 );
function filter_loop_add_to_cart_quantity_arg( $args, $product ) {
$values = $product->get_meta( '_qty_args' );
if ( ! empty( $values ) ) {
// Min value
if ( isset( $values['qty_min'] ) && $values['qty_min'] > 1 ) {
$args['quantity'] = $values['qty_min'];
}
}
return $args;
}
I've tried editing the values to a decimal unit but it's not stepping up by that value and it's not saving the value.
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.
I use WooCommerce cart quantity won't change after cart update answer code to customize some product quantity arguments (as min, max and step arguments).
Now I have a variable product with product id 27525, and I want to apply another rule to this product only, Quantity rule details: min(default) qty 1, max qty 24 and step 1.
I tried to use the code below, the problem is,
if no variations are selected, the default/min quantity will be 25, the rule is the same as the general quantity settings,
If I choose an available variation, the default quantity will be 24.
add_filter( 'woocommerce_available_variation', 'variation_quantity_input_args', 10, 2 );
function variation_quantity_input_args( $args, $product ) {
$sample_product_id = array(27525);
if (in_array( $product->get_id(), $sample_product_id)) {
$args['min_qty'] = 1;
$args['max_qty'] = 24;
return $args;
}
}
How to change the code and apply the rules to variable product 27525?
$args['min_qty'] = 1; //default and min qty
$args['max_qty'] = 24; // max qty
$args['step'] = 1; // Seems won't work use woocommerce_available_variation, but I want to change the step to 1
The woocommerce_available_variation hook has the WC_Product_Variation product object as its third parameter and not the variable product.
With the woocommerce_available_variation hook you cannot set the
product step.
The available arguments are the following (source code of the WooCommerce /includes/class-wc-product-variable.php file #version 3.0.0):
return apply_filters(
'woocommerce_available_variation',
array(
'attributes' => $variation->get_variation_attributes(),
'availability_html' => wc_get_stock_html( $variation ),
'backorders_allowed' => $variation->backorders_allowed(),
'dimensions' => $variation->get_dimensions( false ),
'dimensions_html' => wc_format_dimensions( $variation->get_dimensions( false ) ),
'display_price' => wc_get_price_to_display( $variation ),
'display_regular_price' => wc_get_price_to_display( $variation, array( 'price' => $variation->get_regular_price() ) ),
'image' => wc_get_product_attachment_props( $variation->get_image_id() ),
'image_id' => $variation->get_image_id(),
'is_downloadable' => $variation->is_downloadable(),
'is_in_stock' => $variation->is_in_stock(),
'is_purchasable' => $variation->is_purchasable(),
'is_sold_individually' => $variation->is_sold_individually() ? 'yes' : 'no',
'is_virtual' => $variation->is_virtual(),
'max_qty' => 0 < $variation->get_max_purchase_quantity() ? $variation->get_max_purchase_quantity() : '',
'min_qty' => $variation->get_min_purchase_quantity(),
'price_html' => $show_variation_price ? '<span class="price">' . $variation->get_price_html() . '</span>' : '',
'sku' => $variation->get_sku(),
'variation_description' => wc_format_content( $variation->get_description() ),
'variation_id' => $variation->get_id(),
'variation_is_active' => $variation->variation_is_active(),
'variation_is_visible' => $variation->variation_is_visible(),
'weight' => $variation->get_weight(),
'weight_html' => wc_format_weight( $variation->get_weight() ),
),
$this,
$variation
);
To set the step to product variations you will need to use the woocommerce_quantity_input_args hook.
If you want to set the same step to all product variations you can use this hook.
The available arguments are the following (source code of the WooCommerce /includes/wc-template-functions.php file #version 2.5.0):
$defaults = array(
'input_id' => uniqid( 'quantity_' ),
'input_name' => 'quantity',
'input_value' => '1',
'classes' => apply_filters( 'woocommerce_quantity_input_classes', array( 'input-text', 'qty', 'text' ), $product ),
'max_value' => apply_filters( 'woocommerce_quantity_input_max', -1, $product ),
'min_value' => apply_filters( 'woocommerce_quantity_input_min', 0, $product ),
'step' => apply_filters( 'woocommerce_quantity_input_step', 1, $product ),
'pattern' => apply_filters( 'woocommerce_quantity_input_pattern', has_filter( 'woocommerce_stock_amount', 'intval' ) ? '[0-9]*' : '' ),
'inputmode' => apply_filters( 'woocommerce_quantity_input_inputmode', has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'numeric' : '' ),
'product_name' => $product ? $product->get_title() : '',
'placeholder' => apply_filters( 'woocommerce_quantity_input_placeholder', '', $product ),
);
If you want to manage the product step, the minimum quantity and the maximum quantity as a custom meta of the product, see #LoicTheAztec's answer:
Set quantity minimum, maximum and step at product level in Woocommerce
ANSWER
To answer your question, if you want all product variations that have the parent variable product id equal to 27525 to have the following values:
Min quantity: 1
Max quantity: 24
Step: 1
You can use the following functions:
It works in the product page, in the cart and in the loop.
// set the min and max quantity for each product variation (based on the variable product id)
add_filter( 'woocommerce_available_variation', 'variation_quantity_input_args', 10, 3 );
function variation_quantity_input_args( $args, $product, $variation ) {
// set variable product ids
$sample_product_id = array( 27525 );
// if the selected variation belongs to one of the variable product ids
// set the min and max quantity
if ( in_array( $variation->get_parent_id(), $sample_product_id ) ) {
$args['min_qty'] = 1;
$args['max_qty'] = 24;
return $args;
}
return $args;
}
// set the step for specific variable products (and for each product variation)
add_filter( 'woocommerce_quantity_input_args', 'set_step_for_specific_variable_products', 10, 2 );
function set_step_for_specific_variable_products( $args, $product ) {
// set variable product ids
$sample_product_id = array( 27525 );
// on the product page
if ( ! is_cart() ) {
// if the id of the variable product is in the array
if ( in_array( $product->get_id(), $sample_product_id ) ) {
$args['input_value'] = 1;
$args['min_value'] = 1;
$args['max_value'] = 24;
$args['step'] = 1;
}
// in the cart
} else {
// if the id of the product variation belongs to a variable product present in the array
if ( in_array( $product->get_parent_id(), $sample_product_id ) ) {
$args['min_value'] = 1;
$args['step'] = 1;
}
}
return $args;
}
The code has been tested and works. Add it to your active theme's functions.php.
I've used the awesome snippet of https://jeroensormani.com/custom-stock-quantity-reduction/ to add an additional setting to variations that reduces the main inventory stock by the set amount in the variation.
The problem I'm facing now is that it doesn't check if those variations are out of stock (for example main inventory is 10, and the bundle setting is set to 12 bottles).
The code I've used to add the the multiplier for the total stock reduction is:
// For implementation instructions see: https://aceplugins.com/how-to-add-a-code-snippet/
/**
* Simple product setting.
*/
function ace_add_stock_inventory_multiplier_setting() {
?><div class='options_group'><?php
woocommerce_wp_text_input( array(
'id' => '_stock_multiplier',
'label' => __( 'Inventory reduction per quantity sold', 'woocommerce' ),
'desc_tip' => 'true',
'description' => __( 'Enter the quantity multiplier used for reducing stock levels when purchased.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'min' => '1',
'step' => '1',
),
) );
?></div><?php
}
add_action( 'woocommerce_product_options_inventory_product_data', 'ace_add_stock_inventory_multiplier_setting' );
/**
* Add variable setting.
*
* #param $loop
* #param $variation_data
* #param $variation
*/
function ace_add_variation_stock_inventory_multiplier_setting( $loop, $variation_data, $variation ) {
$variation = wc_get_product( $variation );
woocommerce_wp_text_input( array(
'id' => "stock_multiplier{$loop}",
'name' => "stock_multiplier[{$loop}]",
'value' => $variation->get_meta( '_stock_multiplier' ),
'label' => __( 'Inventory reduction per quantity sold', 'woocommerce' ),
'desc_tip' => 'true',
'description' => __( 'Enter the quantity multiplier used for reducing stock levels when purchased.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'min' => '1',
'step' => '1',
),
) );
}
add_action( 'woocommerce_variation_options_pricing', 'ace_add_variation_stock_inventory_multiplier_setting', 50, 3 );
/**
* Save the custom fields.
*
* #param WC_Product $product
*/
function ace_save_custom_stock_reduction_setting( $product ) {
if ( ! empty( $_POST['_stock_multiplier'] ) ) {
$product->update_meta_data( '_stock_multiplier', absint( $_POST['_stock_multiplier'] ) );
}
}
add_action( 'woocommerce_admin_process_product_object', 'ace_save_custom_stock_reduction_setting' );
/**
* Save custom variable fields.
*
* #param int $variation_id
* #param $i
*/
function ace_save_variable_custom_stock_reduction_setting( $variation_id, $i ) {
$variation = wc_get_product( $variation_id );
if ( ! empty( $_POST['stock_multiplier'] ) && ! empty( $_POST['stock_multiplier'][ $i ] ) ) {
$variation->update_meta_data( '_stock_multiplier', absint( $_POST['stock_multiplier'][ $i ] ) );
$variation->save();
}
}
add_action( 'woocommerce_save_product_variation', 'ace_save_variable_custom_stock_reduction_setting', 10, 2 );
The code that reduces the quantity then is the following:
// For implementation instructions see: https://aceplugins.com/how-to-add-a-code-snippet/
/**
* Reduce with custom stock quantity based on the settings.
*
* #param $quantity
* #param $order
* #param $item
* #return mixed
*/
function ace_custom_stock_reduction( $quantity, $order, $item ) {
/** #var WC_Order_Item_Product $product */
$multiplier = $item->get_product()->get_meta( '_stock_multiplier' );
if ( empty( $multiplier ) && $item->get_product()->is_type( 'variation' ) ) {
$product = wc_get_product( $item->get_product()->get_parent_id() );
$multiplier = $product->get_meta( '_stock_multiplier' );
}
if ( ! empty( $multiplier ) ) {
$quantity = $multiplier * $quantity;
}
return $quantity;
}
add_filter( 'woocommerce_order_item_quantity', 'ace_custom_stock_reduction', 10, 3 );
What I've tried to do is add an "If" Snippet to check the quantity
add_filter( ‘woocommerce_variation_is_active’, ‘my_jazzy_function’, 10, 2 );
function my_jazzy_function( $active, $variation ) {
// Get Multiplier
$multiplier = $item->get_product()->get_meta( '_stock_multiplier' );
$var_stock_count = $variation->get_stock_quantity();
// if there are 5 or less, disable the variant, could always just set to 0.
if( $var_stock_count <= $multiplier ) {
return false;
}
else {
return true;
}
}
But this doesn't work, I think it only checks the variations quantity (if you set the variation to its own quantity instead of global).
How can I compare the total stock count to the newly added setting $multiplier?
Any help would be great.
Compare the total stock quantity to the newly added setting $multiplier
Comment with explanation added to the code
function filter_woocommerce_variation_is_active( $active, $variation ) {
// Get multiplier
$multiplier = get_post_meta( $variation->get_variation_id(), '_stock_multiplier', true );
// NOT empty
if ( ! empty( $multiplier ) ) {
// Get stock quantity
$var_stock_count = $variation->get_stock_quantity();
// Stock quantity < multiplier
if( $var_stock_count < $multiplier ) {
$active = false;
}
}
return $active;
}
add_filter( 'woocommerce_variation_is_active', 'filter_woocommerce_variation_is_active', 10, 2 );
It doesn't work because:
$item variable is not defined in your code.
your custom field is defined in the parent variable product.
So you need to replace:
$multiplier = $item->get_product()->get_meta( '_stock_multiplier' );
by the folling (getting the data from the parent variable product):
$multiplier = get_post_meta( $variation->get_parent_id(), '_stock_multiplier', true );
So in your code:
add_filter( 'woocommerce_variation_is_active', 'my_jazzy_function', 10, 2 );
function my_jazzy_function( $active, $variation ) {
// Get multiplier
if( $multiplier = get_post_meta( $variation->get_parent_id(), '_stock_multiplier', true ) {
// Get stock quantity
$var_stock_count = (int) $variation->get_stock_quantity();
// if there are 5 or less, disable the variant, could always just set to 0
return $var_stock_count <= $multiplier ? false : $active;
}
return $active;
}
It should work now.
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
));
}
}