Repository URL to install this package:
<?php
/**
* Cost calculation.
*
* @package Flexible Shipping PRO
*/
/**
* Can calculate shipping costs.
*/
class WPDesk_Flexible_Shipping_Pro_Costs_Calculation {
const KEY_BASED_ON = 'based_on';
const BASES_ON_NONE = 'none';
const BASED_ON_VALUE = 'value';
const BASED_ON_WEIGHT = 'weight';
const BASED_ON_ITEM = 'item';
const BASED_ON_CART_LINE_ITEM = 'cart_line_item';
/**
* Hooks.
*/
public function hooks() {
add_filter( 'flexible_shipping_calculate_shipping', array( $this, 'flexible_shipping_calculate_shipping' ), 10, 4 );
}
/**
* Get prices include tax.
*
* @param WPDesk_Flexible_Shipping $flexible_shipping .
*
* @return bool
*/
private function prices_include_tax( $flexible_shipping ) {
if ( method_exists( $flexible_shipping, 'prices_include_tax' ) ) {
return $flexible_shipping->prices_include_tax();
} else {
return WC()->cart->prices_include_tax;
}
}
/**
* Package line weight.
*
* @param array $item .
*
* @return float
*/
private function package_line_weight( $item ) {
$weight = 0;
$weight += floatval( $item['data']->get_weight() ) * floatval( $item['quantity'] );
return $weight;
}
/**
* Maybe init package shipping classes.
*
* @param array $package_shipping_classes .
*
* @return array
*/
private function maybe_init_package_shipping_classes( array $package_shipping_classes ) {
if ( empty( $package_shipping_classes ) ) {
$package_shipping_classes = array(
'none' => array(
'quantity' => 0,
'lines' => 0,
'total' => 0,
'weight' => 0,
),
'all' => array(
'quantity' => 0,
'lines' => 0,
'total' => 0,
'weight' => 0,
),
'any' => array(
'quantity' => 0,
'lines' => 0,
'total' => 0,
'weight' => 0,
),
);
}
return $package_shipping_classes;
}
/**
* Maybe convert shiping class id (WPML).
*
* @param string $shipping_class_id .
*
* @return mixed
*/
private function maybe_convert_shipping_class_id( $shipping_class_id ) {
if ( function_exists( 'icl_object_id' ) ) {
global $sitepress;
if ( ! empty( $sitepress ) ) {
$default_language = $sitepress->get_default_language();
$shipping_class_id = icl_object_id( $shipping_class_id, 'product_shipping_class', false, $default_language );
}
}
return $shipping_class_id;
}
/**
* Add item to shipping classes.
*
* @param array $package_shipping_classes .
* @param string $shipping_class_id .
* @param int $quantity .
* @param float $line_total .
* @param float $line_weight .
*
* @return array
*/
private function add_item_to_shipping_classes(
array $package_shipping_classes, $shipping_class_id, $quantity, $line_total, $line_weight
) {
$package_shipping_classes = $this->maybe_init_package_shipping_classes( $package_shipping_classes );
if ( $shipping_class_id ) {
$shipping_class_id = $this->maybe_convert_shipping_class_id( $shipping_class_id );
if ( ! isset( $package_shipping_classes[ $shipping_class_id ] ) ) {
$package_shipping_classes[ $shipping_class_id ] = array(
'quantity' => 0,
'lines' => 0,
'total' => 0,
'weight' => 0,
);
}
$package_shipping_classes[ $shipping_class_id ]['quantity'] += $quantity;
$package_shipping_classes[ $shipping_class_id ]['total'] += $line_total;
$package_shipping_classes[ $shipping_class_id ]['lines'] += 1;
$package_shipping_classes[ $shipping_class_id ]['weight'] += $line_weight;
$package_shipping_classes['any']['quantity'] += $quantity;
$package_shipping_classes['any']['total'] += $line_total;
$package_shipping_classes['any']['lines'] += 1;
$package_shipping_classes['any']['weight'] += $line_weight;
} else {
$package_shipping_classes['none']['quantity'] += $quantity;
$package_shipping_classes['none']['total'] += $line_total;
$package_shipping_classes['none']['lines'] += 1;
$package_shipping_classes['none']['weight'] += $line_weight;
}
$package_shipping_classes['all']['quantity'] += $quantity;
$package_shipping_classes['all']['total'] += $line_total;
$package_shipping_classes['all']['lines'] += 1;
$package_shipping_classes['all']['weight'] += $line_weight;
return $package_shipping_classes;
}
/**
* Get cart shipping classes.
*
* @param WPDesk_Flexible_Shipping $flexible_shipping .
*
* @return array
*/
private function get_cart_shipping_classes( $flexible_shipping ) {
$cart = WC()->cart;
$package_shipping_classes = $this->maybe_init_package_shipping_classes( array() );
foreach ( $cart->cart_contents as $item ) {
/** @var WC_Product $product */
$product = $item['data'];
$shipping_class_id = $product->get_shipping_class_id();
$line_weight = $this->package_line_weight( $item );
$line_total = 0;
if ( $this->prices_include_tax( $flexible_shipping ) ) {
if ( isset( $item['line_total'] ) ) {
$line_total = floatval( $item['line_total'] );
}
if ( isset( $item['line_tax'] ) ) {
$line_total += floatval( $item['line_tax'] );
}
} else {
if ( isset( $item['line_total'] ) ) {
$line_total = floatval( $item['line_total'] );
}
}
$package_shipping_classes = $this->add_item_to_shipping_classes(
$package_shipping_classes,
$shipping_class_id,
$item['quantity'],
$line_total,
$line_weight
);
}
return $package_shipping_classes;
}
/**
* Get package shipping classes.
*
* @param array $package .
*
* @return array
*/
private function get_package_shipping_classes( $package ) {
$package_shipping_classes = $this->maybe_init_package_shipping_classes( array() );
foreach ( $package['contents'] as $item ) {
$shipping_class_id = $item['data']->get_shipping_class_id();
$line_weight = $this->package_line_weight( $item );
$package_shipping_classes = $this->add_item_to_shipping_classes(
$package_shipping_classes,
$shipping_class_id,
$item['quantity'],
$item['line_total'],
$line_weight
);
}
return $package_shipping_classes;
}
/**
* Calculate metgod cost.
*
* @param array $shipping_method .
* @param array $rule_costs .
*
* @return float
*/
private function calculate_method_cost( $shipping_method, $rule_costs ) {
$cost = 0;
if ( 'sum' === $shipping_method['method_calculation_method'] ) {
$cost = 0;
foreach ( $rule_costs as $rule_cost ) {
$cost += $rule_cost['cost'];
}
}
if ( 'highest' === $shipping_method['method_calculation_method'] ) {
$cost = 0;
foreach ( $rule_costs as $rule_cost ) {
$cost = max( array( $cost, $rule_cost['cost'] ) );
}
}
if ( 'lowest' === $shipping_method['method_calculation_method'] ) {
$cost = INF;
foreach ( $rule_costs as $rule_cost ) {
$cost = min( array( $cost, $rule_cost['cost'] ) );
}
}
if ( isset( $shipping_method['method_max_cost'] ) && '' !== $shipping_method['method_max_cost'] ) {
$cost = min( array( $cost, floatval( wc_format_decimal( $shipping_method['method_max_cost'] ) ) ) );
}
return $cost;
}
/**
* Get compare value for rule.
*
* @param array $method_rule Method rule definition.
* @param float $contents_cost Cart contents cost.
* @param float $contents_weight Cart contents weight.
* @param int $contents_item_count Cart contents item count (all items).
* @param int $contents_line_item_count Cart lines count.
*
* @return float|int
*/
private function get_compare_value_for_rule(
array $method_rule,
$contents_cost,
$contents_weight,
$contents_item_count,
$contents_line_item_count
) {
$compare_value = $contents_cost;
if ( self::BASED_ON_VALUE === $method_rule[ self::KEY_BASED_ON ] ) {
$compare_value = $contents_cost;
}
if ( self::BASED_ON_WEIGHT === $method_rule[ self::KEY_BASED_ON ] ) {
$compare_value = $contents_weight;
}
if ( self::BASED_ON_ITEM === $method_rule[ self::KEY_BASED_ON ] ) {
$compare_value = $contents_item_count;
}
if ( self::BASED_ON_CART_LINE_ITEM === $method_rule[ self::KEY_BASED_ON ] ) {
$compare_value = $contents_line_item_count;
}
return $compare_value;
}
/**
* Is rule triggered?
*
* @param array $method_rule .
* @param array $package .
* @param float $contents_cost .
* @param float $contents_weight .
* @param float $contents_item_count .
* @param float $contents_line_item_count .
*
* @return bool
*/
private function is_rule_triggered(
$method_rule,
$package,
$contents_cost,
$contents_weight,
$contents_item_count,
$contents_line_item_count
) {
$rule_triggered = false;
if ( self::BASES_ON_NONE === $method_rule[ self::KEY_BASED_ON ] ) {
$rule_triggered = true;
}
if ( '' === trim( $method_rule['min'] ) ) {
$min = 0;
} else {
$min = floatval( $method_rule['min'] );
}
if ( '' === trim( $method_rule['max'] ) ) {
$max = INF;
} else {
$max = floatval( $method_rule['max'] );
}
$compare_value = $this->get_compare_value_for_rule(
$method_rule,
$contents_cost,
$contents_weight,
$contents_item_count,
$contents_line_item_count
);
if ( self::BASED_ON_VALUE === $method_rule[ self::KEY_BASED_ON ] ) {
if ( floatval( 0 ) !== floatval( $min ) ) {
$min = apply_filters( 'flexible_shipping_value_in_currency', $min );
}
if ( INF !== $max ) {
$max = apply_filters( 'flexible_shipping_value_in_currency', $max );
}
}
if ( $compare_value >= $min && $compare_value <= $max ) {
$rule_triggered = true;
}
if ( 0 === $contents_item_count ) {
$rule_triggered = false;
}
if ( $rule_triggered ) {
$rule_triggered = apply_filters( 'flexible_shipping_rule_triggered', $rule_triggered, $method_rule, $package );
}
return $rule_triggered;
}
/**
* Get contents values for rule.
*
* @param array $method_rule .
* @param array $cart_shipping_classes .
*
* @return array
*/
private function get_contents_values_for_rule( $method_rule, $cart_shipping_classes ) {
$shipping_classes = $method_rule['shipping_class'];
if ( ! is_array( $shipping_classes ) ) {
$shipping_classes = array( $shipping_classes );
}
$contents_cost = 0;
$contents_weight = 0;
$contents_item_count = 0;
$contents_line_item_count = 0;
$all_products = false;
$any_class = false;
foreach ( $shipping_classes as $shipping_class ) {
if ( isset( $cart_shipping_classes[ $shipping_class ] )
&& isset( $cart_shipping_classes[ $shipping_class ]['quantity'] )
&& 0 !== $cart_shipping_classes[ $shipping_class ]['quantity']
) {
if ( $all_products ) {
continue;
}
if ( $any_class ) {
continue;
}
if ( 'all' === $shipping_class ) {
$all_products = true;
}
if ( 'any' === $shipping_class ) {
$any_class = true;
}
$contents_cost += floatval( $cart_shipping_classes[ $shipping_class ]['total'] );
$contents_weight += floatval( $cart_shipping_classes[ $shipping_class ]['weight'] );
$contents_item_count += floatval( $cart_shipping_classes[ $shipping_class ]['quantity'] );
$contents_line_item_count += floatval( $cart_shipping_classes[ $shipping_class ]['lines'] );
} else {
$contents_cost = 0;
$contents_weight = 0;
$contents_item_count = 0;
$contents_line_item_count = 0;
break;
}
}
return array(
'contents_cost' => $contents_cost,
'contents_weight' => $contents_weight,
'contents_item_count' => $contents_item_count,
'contents_line_item_count' => $contents_line_item_count,
);
}
/**
* Is shipping method for logged in?
*
* @param array $shipping_method .
*
* @return bool
*/
private function is_shipping_method_for_logged_in( $shipping_method ) {
return isset( $shipping_method['method_visibility'] ) && 'yes' === $shipping_method['method_visibility'];
}
/**
* Rule contains shipping classes?
*
* @param array $method_rule .
*
* @return bool
*/
private function rule_contains_shipping_classes( $method_rule ) {
return isset( $method_rule['shipping_class'] ) && '' !== $method_rule['shipping_class'];
}
/**
* Rule contains cancel?
*
* @param array $method_rule .
*
* @return bool
*/
private function rule_contains_cancel( $method_rule ) {
return isset( $method_rule['cancel'] ) && 1 === intval( $method_rule['cancel'] );
}
/**
* Rule contains stop?
*
* @param array $method_rule .
*
* @return bool
*/
private function rule_contains_stop( $method_rule ) {
return isset( $method_rule['stop'] ) && 1 === intval( $method_rule['stop'] );
}
/**
* Calculate rule cost.
*
* @param array $method_rule Method rules definition.
* @param float $contents_cost Cart contents cost.
* @param float $contents_weight Cart contents weight.
* @param int $contents_item_count Cart contents item count (all items).
* @param int $contents_line_item_count Cart lines count.
*
* @return array
*/
private function calculate_rule_cost(
array $method_rule,
$contents_cost,
$contents_weight,
$contents_item_count,
$contents_line_item_count
) {
$rule_cost = array( 'cost' => floatval( $method_rule['cost_per_order'] ) );
$per_value = 0;
if ( isset( $method_rule['per_value'] ) && '' !== trim( $method_rule['per_value'] ) ) {
$per_value = floatval( $method_rule['per_value'] );
}
$cost_additional = 0;
if ( isset( $method_rule['cost_additional'] ) && '' !== trim( $method_rule['cost_additional'] ) ) {
$cost_additional = floatval( $method_rule['cost_additional'] );
}
if ( 0 !== $per_value ) {
$compare_value = $this->get_compare_value_for_rule(
$method_rule,
$contents_cost,
$contents_weight,
$contents_item_count,
$contents_line_item_count
);
$rule_additional_cost = ceil( $compare_value / $per_value ) * $cost_additional;
$rule_cost['cost'] += $rule_additional_cost;
}
return $rule_cost;
}
/**
* Method free shipping requires option.
*
* @param array $shipping_method .
*
* @return string
*/
private function method_free_shipping_requires( $shipping_method ) {
if ( ! isset( $shipping_method['method_free_shipping_requires'] ) ) {
return 'order_amount';
} else {
return $shipping_method['method_free_shipping_requires'];
}
}
/**
* Is free shipping coupon in cart?
*
* @return bool
*/
private function is_free_shipping_coupon_in_cart() {
$has_coupon = false;
$discounts = new WC_Discounts( WC()->cart );
$coupons = WC()->cart->get_coupons();
if ( $coupons ) {
foreach ( $coupons as $code => $coupon ) {
/** @var $coupon \WC_Coupon $coupon */
try {
$is_valid = $discounts->is_coupon_valid( $coupon );
if ( is_wp_error( $is_valid ) ) {
$is_valid = false;
}
} catch ( \Exception $e ) {
$is_valid = false;
}
if ( $is_valid && $coupon->get_free_shipping() ) {
$has_coupon = true;
break;
}
}
}
return $has_coupon;
}
/**
* Method free shipping ignore discounts?
*
* @param array $shipping_method_settings Flexible shipping method settings.
*
* @return bool
*/
private function method_free_shipping_ignore_discounts( $shipping_method_settings ) {
return isset( $shipping_method_settings[ WPDesk_Flexible_Shipping_Pro_FS_Hooks::METHOD_FREE_SHIPPING_IGNORE_DISCOUNTS ] )
&& 'yes' === $shipping_method_settings[ WPDesk_Flexible_Shipping_Pro_FS_Hooks::METHOD_FREE_SHIPPING_IGNORE_DISCOUNTS ];
}
/**
* Is free shipping?
*
* @param WC_Cart $cart .
* @param array $shipping_method_settings .
*
* @return bool
*/
private function is_free_shipping( $cart, $shipping_method_settings ) {
$is_free_shipping = false;
$cart_contents_cost = $cart->get_cart_contents_total();
if ( wc_prices_include_tax() ) {
$cart_contents_cost += $cart->get_cart_contents_tax();
}
if ( $this->method_free_shipping_ignore_discounts( $shipping_method_settings ) ) {
$cart_contents_cost = $cart_contents_cost + $cart->get_discount_total();
if ( wc_prices_include_tax() ) {
$cart_contents_cost += $cart->get_discount_tax();
}
}
$method_free_shipping_requires = $this->method_free_shipping_requires( $shipping_method_settings );
if ( 'order_amount' === $method_free_shipping_requires || 'order_amount_or_coupon' === $method_free_shipping_requires ) {
if ( isset( $shipping_method_settings['method_free_shipping'] ) && '' !== $shipping_method_settings['method_free_shipping'] ) {
if ( apply_filters( 'flexible_shipping_value_in_currency', floatval( $shipping_method_settings['method_free_shipping'] ) ) <= floatval( $cart_contents_cost ) ) {
$is_free_shipping = true;
}
}
}
if ( 'order_amount' !== $method_free_shipping_requires ) {
if ( $this->is_free_shipping_coupon_in_cart() ) {
if ( 'coupon' === $method_free_shipping_requires || 'order_amount_or_coupon' === $method_free_shipping_requires ) {
$is_free_shipping = true;
}
if ( 'order_amount_and_coupon' === $method_free_shipping_requires ) {
if ( isset( $shipping_method_settings['method_free_shipping'] ) && '' !== $shipping_method_settings['method_free_shipping'] ) {
if ( apply_filters( 'flexible_shipping_value_in_currency', floatval( $shipping_method_settings['method_free_shipping'] ) ) <= floatval( $cart_contents_cost ) ) {
$is_free_shipping = true;
}
}
}
}
}
/**
* Can modify free shipping.
*
* @param bool $is_free_shipping Current is_free_shipping value based on method settings.
* @param array $shipping_method_settings Flexible shipping method settings.
* @param float $cart_contents_cost Cart contents cost.
*
* @return bool
*/
return apply_filters( 'flexible_shipping_is_free_shipping', $is_free_shipping, $shipping_method_settings, $cart_contents_cost );
}
/**
* Calculate method cost with free shipping rules.
*
* @param float $cost .
* @param WC_Cart $cart .
* @param array $shipping_method .
*
* @return float
*/
private function calculate_method_cost_with_free_shipping_rules( $cost, $cart, $shipping_method ) {
if ( $this->is_free_shipping( $cart, $shipping_method ) ) {
$cost = 0.0;
}
return (float) $cost;
}
/**
* Is cart calculation set to package?
*
* @param array $shipping_method .
*
* @return bool
*/
private function is_cart_calculation_package( $shipping_method ) {
return isset( $shipping_method['cart_calculation'] ) && 'package' === $shipping_method['cart_calculation'];
}
/**
* Calculate shipping.
*
* @param bool $processed .
* @param WPDesk_Flexible_Shipping $flexible_shipping .
* @param array $package .
* @param int $package_id .
*
* @return bool
*/
public function flexible_shipping_calculate_shipping( $processed, $flexible_shipping, $package, $package_id ) {
$default_method_is_set = false;
$cart = WC()->cart;
$shipping_methods = $flexible_shipping->get_shipping_methods( true );
$cart_shipping_classes = $this->get_cart_shipping_classes( $flexible_shipping );
$cart_contents_cost = $cart_shipping_classes['all']['total'];
$package_shipping_classes = $this->get_package_shipping_classes( $package );
foreach ( $shipping_methods as $shipping_method ) {
$rule_costs = array();
$add_method = false;
if ( $this->is_shipping_method_for_logged_in( $shipping_method ) && ! is_user_logged_in() ) {
/* only for logged in */
continue;
}
if ( $this->is_cart_calculation_package( $shipping_method ) ) {
$shipping_classes = $package_shipping_classes;
} else {
$shipping_classes = $cart_shipping_classes;
}
foreach ( $shipping_method['method_rules'] as $rule_key => $method_rule ) {
if ( $this->rule_contains_shipping_classes( $method_rule ) ) {
$contents_values_for_rule = $this->get_contents_values_for_rule( $method_rule, $shipping_classes );
$contents_cost = $contents_values_for_rule['contents_cost'];
$contents_weight = $contents_values_for_rule['contents_weight'];
$contents_item_count = $contents_values_for_rule['contents_item_count'];
$contents_line_item_count = $contents_values_for_rule['contents_line_item_count'];
} else {
$contents_cost = $shipping_classes['all']['total'];
$contents_weight = $shipping_classes['all']['weight'];
$contents_item_count = $shipping_classes['all']['quantity'];
$contents_line_item_count = $shipping_classes['all']['lines'];
}
$rule_triggered = $this->is_rule_triggered(
$method_rule,
$package,
$contents_cost,
$contents_weight,
$contents_item_count,
$contents_line_item_count
);
if ( $rule_triggered ) {
$rule_cost = $this->calculate_rule_cost( $method_rule, $contents_cost, $contents_weight, $contents_item_count, $contents_line_item_count );
$rule_costs[ $rule_key ] = $rule_cost;
$add_method = true;
if ( $this->rule_contains_cancel( $method_rule ) ) {
$add_method = false;
break;
}
if ( $this->rule_contains_stop( $method_rule ) ) {
break;
}
}
}
$add_method = apply_filters( 'flexible_shipping_add_method', $add_method, $shipping_method, $package, $flexible_shipping );
if ( true === $add_method ) {
$cost = $this->calculate_method_cost( $shipping_method, $rule_costs );
$cost = $this->calculate_method_cost_with_free_shipping_rules( $cost, $cart, $shipping_method );
$id = $flexible_shipping->id . '_' . $flexible_shipping->instance_id . '_' . sanitize_title( $shipping_method['method_title'] );
$id = apply_filters( 'flexible_shipping_method_rate_id', $id, $shipping_method );
$method_title = wpdesk__( $shipping_method['method_title'], 'flexible-shipping' );
if ( version_compare( WC()->version, '2.6' ) >= 0 ) {
if ( 0.0 === $cost ) {
if ( ! isset( $shipping_method['method_free_shipping_label'] ) ) {
$shipping_method['method_free_shipping_label'] = __( 'Free', 'flexible-shipping-pro' );
}
if ( '' !== $shipping_method['method_free_shipping_label'] ) {
$method_title .= ' (' . wpdesk__( $shipping_method['method_free_shipping_label'], 'flexible-shipping' ) . ')';
}
}
}
$flexible_shipping->add_rate(
array(
'id' => $id,
'label' => $method_title,
'cost' => $cost,
'method' => $shipping_method,
'rule_costs' => $rule_costs,
'meta_data' => array(
'_default' => $shipping_method['method_default'],
'_fs_method' => $shipping_method,
),
)
);
if ( isset( $shipping_method['method_description'] ) ) {
WC()->session->set( 'flexible_shipping_description_' . $id, wpdesk__( $shipping_method['method_description'], 'flexible-shipping' ) );
} else {
WC()->session->set( 'flexible_shipping_description_' . $id, '' );
}
if ( ! $default_method_is_set && isset( $shipping_method['method_default'] ) && 'yes' === $shipping_method['method_default'] ) {
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', array() );
if ( ! isset( $chosen_shipping_methods[0] ) ) {
$chosen_shipping_methods[0] = $id;
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
$default_method_is_set = true;
}
}
}
}
return true;
}
}