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