1 <?php
  2 
  3 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
  4 
  5 /**
  6  * WooCommerce Shipping Method Class
  7  *
  8  * Extended by shipping methods to handle shipping calculations etc.
  9  *
 10  * @class       WC_Shipping_Method
 11  * @version     1.6.4
 12  * @package     WooCommerce/Abstracts
 13  * @category    Abstract Class
 14  * @author      WooThemes
 15  */
 16 abstract class WC_Shipping_Method extends WC_Settings_API {
 17 
 18     /** @var string Unique ID for the shipping method - must be set. */
 19     var $id;
 20 
 21     /** @var int Optional instance ID. */
 22     var $number;
 23 
 24     /** @var string Method title */
 25     var $method_title;
 26 
 27     /** @var string User set title */
 28     var $title;
 29 
 30     /**  @var bool True if the method is available. */
 31     var $availability;
 32 
 33     /** @var array Array of countries this method is enabled for. */
 34     var $countries          = array();
 35 
 36     /** @var string If 'taxable' tax will be charged for this method (if applicable) */
 37     var $tax_status         = 'taxable';
 38 
 39     /** @var mixed Fees for the method */
 40     var $fee                = 0;
 41 
 42     /** @var float Minimum fee for the method */
 43     var $minimum_fee        = null;
 44 
 45     /** @var bool Enabled for disabled */
 46     var $enabled            = false;
 47 
 48     /** @var bool Whether the method has settings or not (In WooCommerce > Settings > Shipping) */
 49     var $has_settings       = true;
 50 
 51     /** @var array Features this method supports. */
 52     var $supports           = array();      // Features this method supports.
 53 
 54     /** @var array This is an array of rates - methods must populate this array to register shipping costs */
 55     var $rates              = array();
 56 
 57     /**
 58      * Whether or not we need to calculate tax on top of the shipping rate
 59      * @return boolean
 60      */
 61     public function is_taxable() {
 62         return ( get_option( 'woocommerce_calc_taxes' ) == 'yes' && $this->tax_status == 'taxable' && ! WC()->customer->is_vat_exempt() );
 63     }
 64 
 65     /**
 66      * Add a rate
 67      *
 68      * Add a shipping rate. If taxes are not set they will be calculated based on cost.
 69      *
 70      * @access public
 71      * @param array $args (default: array())
 72      * @return void
 73      */
 74     function add_rate( $args = array() ) {
 75 
 76         $defaults = array(
 77             'id'        => '',          // ID for the rate
 78             'label'     => '',          // Label for the rate
 79             'cost'      => '0',         // Amount or array of costs (per item shipping)
 80             'taxes'     => '',          // Pass taxes, nothing to have it calculated for you, or 'false' to calc no tax
 81             'calc_tax'  => 'per_order'  // Calc tax per_order or per_item. Per item needs an array of costs
 82         );
 83 
 84         $args = wp_parse_args( $args, $defaults );
 85 
 86         extract( $args );
 87 
 88         // Id and label are required
 89         if ( ! $id || ! $label ) return;
 90 
 91         // Handle cost
 92         $total_cost = ( is_array( $cost ) ) ? array_sum( $cost ) : $cost;
 93 
 94         // Taxes - if not an array and not set to false, calc tax based on cost and passed calc_tax variable
 95         // This saves shipping methods having to do complex tax calculations
 96         if ( ! is_array( $taxes ) && $taxes !== false && $total_cost > 0 && $this->is_taxable() ) {
 97 
 98             $_tax   = new WC_Tax();
 99             $taxes  = array();
100 
101             switch ( $calc_tax ) {
102 
103                 case "per_item" :
104 
105                     // If we have an array of costs we can look up each items tax class and add tax accordingly
106                     if ( is_array( $cost ) ) {
107 
108                         $cart = WC()->cart->get_cart();
109 
110                         foreach ( $cost as $cost_key => $amount ) {
111 
112                             if ( ! isset( $cart[ $cost_key ] ) )
113                                 continue;
114 
115                             $_product = $cart[  $cost_key ]['data'];
116 
117                             $rates = $_tax->get_shipping_tax_rates( $_product->get_tax_class() );
118                             $item_taxes = $_tax->calc_shipping_tax( $amount, $rates );
119 
120                             // Sum the item taxes
121                             foreach ( array_keys( $taxes + $item_taxes ) as $key )
122                                 $taxes[ $key ] = ( isset( $item_taxes[ $key ] ) ? $item_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
123 
124                         }
125 
126                         // Add any cost for the order - order costs are in the key 'order'
127                         if ( isset( $cost['order'] ) ) {
128 
129                             $rates = $_tax->get_shipping_tax_rates();
130                             $item_taxes = $_tax->calc_shipping_tax( $cost['order'], $rates );
131 
132                             // Sum the item taxes
133                             foreach ( array_keys( $taxes + $item_taxes ) as $key )
134                                 $taxes[ $key ] = ( isset( $item_taxes[ $key ] ) ? $item_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
135                         }
136 
137                     }
138 
139                 break;
140 
141                 default :
142 
143                     $rates = $_tax->get_shipping_tax_rates();
144                     $taxes = $_tax->calc_shipping_tax( $total_cost, $rates );
145 
146                 break;
147 
148             }
149 
150         }
151 
152         $this->rates[] = new WC_Shipping_Rate( $id, $label, $total_cost, $taxes, $this->id );
153     }
154 
155     /**
156      * has_settings function.
157      *
158      * @access public
159      * @return bool
160      */
161     function has_settings() {
162         return ( $this->has_settings );
163     }
164 
165     /**
166      * is_available function.
167      *
168      * @param array $package
169      * @return bool
170      */
171     public function is_available( $package ) {
172         if ( "no" == $this->enabled ) {
173             return false;
174         }
175 
176         // Country availability
177         switch ( $this->availability ) {
178             case 'specific' :
179             case 'including' :
180                 $ship_to_countries = array_intersect( $this->countries, array_keys( WC()->countries->get_shipping_countries() ) );
181             break;
182             case 'excluding' :
183                 $ship_to_countries = array_diff( array_keys( WC()->countries->get_shipping_countries() ), $this->countries );
184             break;
185             default :
186                 $ship_to_countries = array_keys( WC()->countries->get_shipping_countries() );
187             break;
188         }
189 
190         if ( ! in_array( $package['destination']['country'], $ship_to_countries ) ) {
191             return false;
192         }
193 
194         return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', true, $package );
195     }
196 
197     /**
198      * Return the gateways title
199      *
200      * @return string
201      */
202     public function get_title() {
203         return apply_filters( 'woocommerce_shipping_method_title', $this->title, $this->id );
204     }
205 
206     /**
207      * get_fee function.
208      *
209      * @access public
210      * @param mixed $fee
211      * @param mixed $total
212      * @return float
213      */
214     function get_fee( $fee, $total ) {
215         if ( strstr( $fee, '%' ) ) :
216             $fee = ( $total / 100 ) * str_replace( '%', '', $fee );
217         endif;
218         if ( ! empty( $this->minimum_fee ) && $this->minimum_fee > $fee ) $fee = $this->minimum_fee;
219         return $fee;
220     }
221 
222     /**
223      * Check if a shipping method supports a given feature.
224      *
225      * Methods should override this to declare support (or lack of support) for a feature.
226      *
227      * @param $feature string The name of a feature to test support for.
228      * @return bool True if the gateway supports the feature, false otherwise.
229      * @since 1.5.7
230      */
231     function supports( $feature ) {
232         return apply_filters( 'woocommerce_shipping_method_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this );
233     }
234 }
235 
WooCommerce API documentation generated by ApiGen 2.8.0