Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WC_Cart often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WC_Cart, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class WC_Cart { |
||
20 | |||
21 | /** @var array Contains an array of cart items. */ |
||
22 | public $cart_contents = array(); |
||
23 | |||
24 | /** @var array Contains an array of removed cart items. */ |
||
25 | public $removed_cart_contents = array(); |
||
26 | |||
27 | /** @var array Contains an array of coupon codes applied to the cart. */ |
||
28 | public $applied_coupons = array(); |
||
29 | |||
30 | /** @var array Contains an array of coupon code discounts after they have been applied. */ |
||
31 | public $coupon_discount_amounts = array(); |
||
32 | |||
33 | /** @var array Contains an array of coupon code discount taxes. Used for tax incl pricing. */ |
||
34 | public $coupon_discount_tax_amounts = array(); |
||
35 | |||
36 | /** @var array Contains an array of coupon usage counts after they have been applied. */ |
||
37 | public $coupon_applied_count = array(); |
||
38 | |||
39 | /** @var array Array of coupons */ |
||
40 | public $coupons = array(); |
||
41 | |||
42 | /** @var float The total cost of the cart items. */ |
||
43 | public $cart_contents_total; |
||
44 | |||
45 | /** @var float Cart grand total. */ |
||
46 | public $total; |
||
47 | |||
48 | /** @var float Cart subtotal. */ |
||
49 | public $subtotal; |
||
50 | |||
51 | /** @var float Cart subtotal without tax. */ |
||
52 | public $subtotal_ex_tax; |
||
53 | |||
54 | /** @var float Total cart tax. */ |
||
55 | public $tax_total; |
||
56 | |||
57 | /** @var array An array of taxes/tax rates for the cart. */ |
||
58 | public $taxes; |
||
59 | |||
60 | /** @var array An array of taxes/tax rates for the shipping. */ |
||
61 | public $shipping_taxes; |
||
62 | |||
63 | /** @var float Discount amount before tax */ |
||
64 | public $discount_cart; |
||
65 | |||
66 | /** @var float Discounted tax amount. Used predominantly for displaying tax inclusive prices correctly */ |
||
67 | public $discount_cart_tax; |
||
68 | |||
69 | /** @var float Total for additional fees. */ |
||
70 | public $fee_total; |
||
71 | |||
72 | /** @var float Shipping cost. */ |
||
73 | public $shipping_total; |
||
74 | |||
75 | /** @var float Shipping tax. */ |
||
76 | public $shipping_tax_total; |
||
77 | |||
78 | /** @var array cart_session_data. Array of data the cart calculates and stores in the session with defaults */ |
||
79 | public $cart_session_data = array( |
||
80 | 'cart_contents_total' => 0, |
||
81 | 'total' => 0, |
||
82 | 'subtotal' => 0, |
||
83 | 'subtotal_ex_tax' => 0, |
||
84 | 'tax_total' => 0, |
||
85 | 'taxes' => array(), |
||
86 | 'shipping_taxes' => array(), |
||
87 | 'discount_cart' => 0, |
||
88 | 'discount_cart_tax' => 0, |
||
89 | 'shipping_total' => 0, |
||
90 | 'shipping_tax_total' => 0, |
||
91 | 'coupon_discount_amounts' => array(), |
||
92 | 'coupon_discount_tax_amounts' => array(), |
||
93 | 'fee_total' => 0, |
||
94 | 'fees' => array() |
||
95 | ); |
||
96 | |||
97 | /** |
||
98 | * An array of fees. |
||
99 | * |
||
100 | * @var array |
||
101 | */ |
||
102 | public $fees = array(); |
||
103 | |||
104 | /** |
||
105 | * Constructor for the cart class. Loads options and hooks in the init method. |
||
106 | */ |
||
107 | public function __construct() { |
||
114 | |||
115 | /** |
||
116 | * Auto-load in-accessible properties on demand. |
||
117 | * |
||
118 | * @param mixed $key |
||
119 | * @return mixed |
||
120 | */ |
||
121 | public function __get( $key ) { |
||
154 | |||
155 | /** |
||
156 | * Loads the cart data from the PHP session during WordPress init and hooks in other methods. |
||
157 | */ |
||
158 | public function init() { |
||
165 | |||
166 | /** |
||
167 | * Will set cart cookies if needed, once, during WP hook. |
||
168 | */ |
||
169 | public function maybe_set_cart_cookies() { |
||
178 | |||
179 | /** |
||
180 | * Set cart hash cookie and items in cart. |
||
181 | * |
||
182 | * @access private |
||
183 | * @param bool $set (default: true) |
||
184 | */ |
||
185 | private function set_cart_cookies( $set = true ) { |
||
195 | |||
196 | /*-----------------------------------------------------------------------------------*/ |
||
197 | /* Cart Session Handling */ |
||
198 | /*-----------------------------------------------------------------------------------*/ |
||
199 | |||
200 | /** |
||
201 | * Get the cart data from the PHP session and store it in class variables. |
||
202 | */ |
||
203 | public function get_cart_from_session() { |
||
264 | |||
265 | /** |
||
266 | * Sets the php session data for the cart and coupons. |
||
267 | */ |
||
268 | public function set_session() { |
||
288 | |||
289 | /** |
||
290 | * Empties the cart and optionally the persistent cart too. |
||
291 | * |
||
292 | * @param bool $clear_persistent_cart (default: true) |
||
293 | */ |
||
294 | public function empty_cart( $clear_persistent_cart = true ) { |
||
306 | |||
307 | /*-----------------------------------------------------------------------------------*/ |
||
308 | /* Persistent cart handling */ |
||
309 | /*-----------------------------------------------------------------------------------*/ |
||
310 | |||
311 | /** |
||
312 | * Save the persistent cart when the cart is updated. |
||
313 | */ |
||
314 | public function persistent_cart_update() { |
||
319 | |||
320 | /** |
||
321 | * Delete the persistent cart permanently. |
||
322 | */ |
||
323 | public function persistent_cart_destroy() { |
||
326 | |||
327 | /*-----------------------------------------------------------------------------------*/ |
||
328 | /* Cart Data Functions */ |
||
329 | /*-----------------------------------------------------------------------------------*/ |
||
330 | |||
331 | /** |
||
332 | * Coupons enabled function. Filterable. |
||
333 | * |
||
334 | * @deprecated 2.5.0 in favor to wc_coupons_enabled() |
||
335 | * |
||
336 | * @return bool |
||
337 | */ |
||
338 | public function coupons_enabled() { |
||
341 | |||
342 | /** |
||
343 | * Get number of items in the cart. |
||
344 | * @return int |
||
345 | */ |
||
346 | public function get_cart_contents_count() { |
||
349 | |||
350 | /** |
||
351 | * Get weight of items in the cart. |
||
352 | * @since 2.5.0 |
||
353 | * @return int |
||
354 | */ |
||
355 | public function get_cart_contents_weight() { |
||
364 | |||
365 | /** |
||
366 | * Checks if the cart is empty. |
||
367 | * |
||
368 | * @return bool |
||
369 | */ |
||
370 | public function is_empty() { |
||
373 | |||
374 | /** |
||
375 | * Check all cart items for errors. |
||
376 | */ |
||
377 | public function check_cart_items() { |
||
401 | |||
402 | /** |
||
403 | * Check cart coupons for errors. |
||
404 | */ |
||
405 | public function check_cart_coupons() { |
||
421 | |||
422 | /** |
||
423 | * Get cart items quantities - merged so we can do accurate stock checks on items across multiple lines. |
||
424 | * |
||
425 | * @return array |
||
426 | */ |
||
427 | public function get_cart_item_quantities() { |
||
443 | |||
444 | /** |
||
445 | * Looks through cart items and checks the posts are not trashed or deleted. |
||
446 | * |
||
447 | * @return bool|WP_Error |
||
448 | */ |
||
449 | public function check_cart_item_validity() { |
||
463 | |||
464 | /** |
||
465 | * Looks through the cart to check each item is in stock. If not, add an error. |
||
466 | * |
||
467 | * @return bool|WP_Error |
||
468 | */ |
||
469 | public function check_cart_item_stock() { |
||
540 | |||
541 | /** |
||
542 | * Gets and formats a list of cart item data + variations for display on the frontend. |
||
543 | * |
||
544 | * @param array $cart_item |
||
545 | * @param bool $flat (default: false) |
||
546 | * @return string |
||
547 | */ |
||
548 | public function get_item_data( $cart_item, $flat = false ) { |
||
618 | |||
619 | /** |
||
620 | * Gets cross sells based on the items in the cart. |
||
621 | * |
||
622 | * @return array cross_sells (item ids) |
||
623 | */ |
||
624 | public function get_cross_sells() { |
||
638 | |||
639 | /** |
||
640 | * Gets the url to the cart page. |
||
641 | * |
||
642 | * @deprecated 2.5.0 in favor to wc_get_cart_url() |
||
643 | * |
||
644 | * @return string url to page |
||
645 | */ |
||
646 | public function get_cart_url() { |
||
649 | |||
650 | /** |
||
651 | * Gets the url to the checkout page. |
||
652 | * |
||
653 | * @deprecated 2.5.0 in favor to wc_get_checkout_url() |
||
654 | * |
||
655 | * @return string url to page |
||
656 | */ |
||
657 | public function get_checkout_url() { |
||
660 | |||
661 | /** |
||
662 | * Gets the url to remove an item from the cart. |
||
663 | * |
||
664 | * @param string $cart_item_key contains the id of the cart item |
||
665 | * @return string url to page |
||
666 | */ |
||
667 | public function get_remove_url( $cart_item_key ) { |
||
671 | |||
672 | /** |
||
673 | * Gets the url to re-add an item into the cart. |
||
674 | * |
||
675 | * @param string $cart_item_key |
||
676 | * @return string url to page |
||
677 | */ |
||
678 | public function get_undo_url( $cart_item_key ) { |
||
687 | |||
688 | /** |
||
689 | * Returns the contents of the cart in an array. |
||
690 | * |
||
691 | * @return array contents of the cart |
||
692 | */ |
||
693 | public function get_cart() { |
||
702 | |||
703 | /** |
||
704 | * Returns the contents of the cart in an array without the 'data' element. |
||
705 | * |
||
706 | * @return array contents of the cart |
||
707 | */ |
||
708 | public function get_cart_for_session() { |
||
720 | |||
721 | /** |
||
722 | * Returns a specific item in the cart. |
||
723 | * |
||
724 | * @param string $item_key Cart item key. |
||
725 | * @return array Item data |
||
726 | */ |
||
727 | public function get_cart_item( $item_key ) { |
||
734 | |||
735 | /** |
||
736 | * Returns the cart and shipping taxes, merged. |
||
737 | * |
||
738 | * @return array merged taxes |
||
739 | */ |
||
740 | public function get_taxes() { |
||
750 | |||
751 | /** |
||
752 | * Get taxes, merged by code, formatted ready for output. |
||
753 | * |
||
754 | * @return array |
||
755 | */ |
||
756 | public function get_tax_totals() { |
||
757 | $taxes = $this->get_taxes(); |
||
758 | $tax_totals = array(); |
||
759 | |||
760 | foreach ( $taxes as $key => $tax ) { |
||
761 | $code = WC_Tax::get_rate_code( $key ); |
||
762 | |||
763 | if ( $code || $key === apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) ) { |
||
764 | View Code Duplication | if ( ! isset( $tax_totals[ $code ] ) ) { |
|
765 | $tax_totals[ $code ] = new stdClass(); |
||
766 | $tax_totals[ $code ]->amount = 0; |
||
767 | } |
||
768 | $tax_totals[ $code ]->tax_rate_id = $key; |
||
769 | $tax_totals[ $code ]->is_compound = WC_Tax::is_compound( $key ); |
||
770 | $tax_totals[ $code ]->label = WC_Tax::get_rate_label( $key ); |
||
771 | $tax_totals[ $code ]->amount += wc_round_tax_total( $tax ); |
||
772 | $tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ) ); |
||
773 | } |
||
774 | } |
||
775 | |||
776 | View Code Duplication | if ( apply_filters( 'woocommerce_cart_hide_zero_taxes', true ) ) { |
|
777 | $amounts = array_filter( wp_list_pluck( $tax_totals, 'amount' ) ); |
||
778 | $tax_totals = array_intersect_key( $tax_totals, $amounts ); |
||
779 | } |
||
780 | |||
781 | return apply_filters( 'woocommerce_cart_tax_totals', $tax_totals, $this ); |
||
782 | } |
||
783 | |||
784 | /** |
||
785 | * Get all tax classes for items in the cart. |
||
786 | * @return array |
||
787 | */ |
||
788 | public function get_cart_item_tax_classes() { |
||
797 | |||
798 | /** |
||
799 | * Determines the value that the customer spent and the subtotal |
||
800 | * displayed, used for things like coupon validation. |
||
801 | * |
||
802 | * Since the coupon lines are displayed based on the TAX DISPLAY value |
||
803 | * of cart, this is used to determine the spend. |
||
804 | * |
||
805 | * If cart totals are shown including tax, use the subtotal. |
||
806 | * If cart totals are shown excluding tax, use the subtotal ex tax |
||
807 | * (tax is shown after coupons). |
||
808 | * |
||
809 | * @since 2.6.0 |
||
810 | * @return string |
||
811 | */ |
||
812 | public function get_displayed_subtotal() { |
||
819 | |||
820 | /*-----------------------------------------------------------------------------------*/ |
||
821 | /* Add to cart handling */ |
||
822 | /*-----------------------------------------------------------------------------------*/ |
||
823 | |||
824 | /** |
||
825 | * Check if product is in the cart and return cart item key. |
||
826 | * |
||
827 | * Cart item key will be unique based on the item and its properties, such as variations. |
||
828 | * |
||
829 | * @param mixed id of product to find in the cart |
||
830 | * @return string cart item key |
||
831 | */ |
||
832 | public function find_product_in_cart( $cart_id = false ) { |
||
844 | |||
845 | /** |
||
846 | * Generate a unique ID for the cart item being added. |
||
847 | * |
||
848 | * @param int $product_id - id of the product the key is being generated for |
||
849 | * @param int $variation_id of the product the key is being generated for |
||
850 | * @param array $variation data for the cart item |
||
851 | * @param array $cart_item_data other cart item data passed which affects this items uniqueness in the cart |
||
852 | * @return string cart item key |
||
853 | */ |
||
854 | public function generate_cart_id( $product_id, $variation_id = 0, $variation = array(), $cart_item_data = array() ) { |
||
884 | |||
885 | /** |
||
886 | * Add a product to the cart. |
||
887 | * |
||
888 | * @param int $product_id contains the id of the product to add to the cart |
||
889 | * @param int $quantity contains the quantity of the item to add |
||
890 | * @param int $variation_id |
||
891 | * @param array $variation attribute values |
||
892 | * @param array $cart_item_data extra cart item data we want to pass into the item |
||
893 | * @return string|bool $cart_item_key |
||
894 | */ |
||
895 | public function add_to_cart( $product_id = 0, $quantity = 1, $variation_id = 0, $variation = array(), $cart_item_data = array() ) { |
||
1003 | |||
1004 | /** |
||
1005 | * Remove a cart item. |
||
1006 | * |
||
1007 | * @since 2.3.0 |
||
1008 | * @param string $cart_item_key |
||
1009 | * @return bool |
||
1010 | */ |
||
1011 | public function remove_cart_item( $cart_item_key ) { |
||
1029 | |||
1030 | /** |
||
1031 | * Restore a cart item. |
||
1032 | * |
||
1033 | * @param string $cart_item_key |
||
1034 | * @return bool |
||
1035 | */ |
||
1036 | public function restore_cart_item( $cart_item_key ) { |
||
1054 | |||
1055 | /** |
||
1056 | * Set the quantity for an item in the cart. |
||
1057 | * |
||
1058 | * @param string $cart_item_key contains the id of the cart item |
||
1059 | * @param int $quantity contains the quantity of the item |
||
1060 | * @param bool $refresh_totals whether or not to calculate totals after setting the new qty |
||
1061 | * |
||
1062 | * @return bool |
||
1063 | */ |
||
1064 | public function set_quantity( $cart_item_key, $quantity = 1, $refresh_totals = true ) { |
||
1080 | |||
1081 | /*-----------------------------------------------------------------------------------*/ |
||
1082 | /* Cart Calculation Functions */ |
||
1083 | /*-----------------------------------------------------------------------------------*/ |
||
1084 | |||
1085 | /** |
||
1086 | * Reset cart totals to the defaults. Useful before running calculations. |
||
1087 | * |
||
1088 | * @param bool $unset_session If true, the session data will be forced unset. |
||
1089 | * @access private |
||
1090 | */ |
||
1091 | private function reset( $unset_session = false ) { |
||
1100 | |||
1101 | /** |
||
1102 | * Sort by subtotal. |
||
1103 | * @param array $a |
||
1104 | * @param array $b |
||
1105 | * @return int |
||
1106 | */ |
||
1107 | private function sort_by_subtotal( $a, $b ) { |
||
1115 | |||
1116 | /** |
||
1117 | * Calculate totals for the items in the cart. |
||
1118 | */ |
||
1119 | public function calculate_totals() { |
||
1120 | $this->reset(); |
||
1121 | $this->coupons = $this->get_coupons(); |
||
1122 | |||
1123 | do_action( 'woocommerce_before_calculate_totals', $this ); |
||
1124 | |||
1125 | if ( $this->is_empty() ) { |
||
1126 | $this->set_session(); |
||
1127 | return; |
||
1128 | } |
||
1129 | |||
1130 | $tax_rates = array(); |
||
1131 | $shop_tax_rates = array(); |
||
1132 | $cart = $this->get_cart(); |
||
1133 | |||
1134 | /** |
||
1135 | * Calculate subtotals for items. This is done first so that discount logic can use the values. |
||
1136 | */ |
||
1137 | foreach ( $cart as $cart_item_key => $values ) { |
||
1138 | $_product = $values['data']; |
||
1139 | $line_price = $_product->get_price() * $values['quantity']; |
||
1140 | $line_subtotal = 0; |
||
1141 | $line_subtotal_tax = 0; |
||
1142 | |||
1143 | /** |
||
1144 | * No tax to calculate. |
||
1145 | */ |
||
1146 | if ( ! $_product->is_taxable() ) { |
||
1147 | |||
1148 | // Subtotal is the undiscounted price |
||
1149 | $this->subtotal += $line_price; |
||
1150 | $this->subtotal_ex_tax += $line_price; |
||
1151 | |||
1152 | /** |
||
1153 | * Prices include tax. |
||
1154 | * |
||
1155 | * To prevent rounding issues we need to work with the inclusive price where possible. |
||
1156 | * otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would. |
||
1157 | * be 8.325 leading to totals being 1p off. |
||
1158 | * |
||
1159 | * Pre tax coupons come off the price the customer thinks they are paying - tax is calculated. |
||
1160 | * afterwards. |
||
1161 | * |
||
1162 | * e.g. $100 bike with $10 coupon = customer pays $90 and tax worked backwards from that. |
||
1163 | */ |
||
1164 | } elseif ( $this->prices_include_tax ) { |
||
1165 | |||
1166 | // Get base tax rates |
||
1167 | if ( empty( $shop_tax_rates[ $_product->tax_class ] ) ) { |
||
1168 | $shop_tax_rates[ $_product->tax_class ] = WC_Tax::get_base_tax_rates( $_product->tax_class ); |
||
1169 | } |
||
1170 | |||
1171 | // Get item tax rates |
||
1172 | if ( empty( $tax_rates[ $_product->get_tax_class() ] ) ) { |
||
1173 | $tax_rates[ $_product->get_tax_class() ] = WC_Tax::get_rates( $_product->get_tax_class() ); |
||
1174 | } |
||
1175 | |||
1176 | $base_tax_rates = $shop_tax_rates[ $_product->tax_class ]; |
||
1177 | $item_tax_rates = $tax_rates[ $_product->get_tax_class() ]; |
||
1178 | |||
1179 | /** |
||
1180 | * ADJUST TAX - Calculations when base tax is not equal to the item tax. |
||
1181 | * |
||
1182 | * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations. |
||
1183 | * e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes. |
||
1184 | * This feature is experimental @since 2.4.7 and may change in the future. Use at your risk. |
||
1185 | */ |
||
1186 | if ( $item_tax_rates !== $base_tax_rates && apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) { |
||
1187 | |||
1188 | // Work out a new base price without the shop's base tax |
||
1189 | $taxes = WC_Tax::calc_tax( $line_price, $base_tax_rates, true, true ); |
||
1190 | |||
1191 | // Now we have a new item price (excluding TAX) |
||
1192 | $line_subtotal = $line_price - array_sum( $taxes ); |
||
1193 | |||
1194 | // Now add modified taxes |
||
1195 | $tax_result = WC_Tax::calc_tax( $line_subtotal, $item_tax_rates ); |
||
1196 | $line_subtotal_tax = array_sum( $tax_result ); |
||
1197 | |||
1198 | /** |
||
1199 | * Regular tax calculation (customer inside base and the tax class is unmodified. |
||
1200 | */ |
||
1201 | } else { |
||
1202 | |||
1203 | // Calc tax normally |
||
1204 | $taxes = WC_Tax::calc_tax( $line_price, $item_tax_rates, true ); |
||
1205 | $line_subtotal_tax = array_sum( $taxes ); |
||
1206 | $line_subtotal = $line_price - array_sum( $taxes ); |
||
1207 | } |
||
1208 | |||
1209 | /** |
||
1210 | * Prices exclude tax. |
||
1211 | * |
||
1212 | * This calculation is simpler - work with the base, untaxed price. |
||
1213 | */ |
||
1214 | } else { |
||
1215 | |||
1216 | // Get item tax rates |
||
1217 | if ( empty( $tax_rates[ $_product->get_tax_class() ] ) ) { |
||
1218 | $tax_rates[ $_product->get_tax_class() ] = WC_Tax::get_rates( $_product->get_tax_class() ); |
||
1219 | } |
||
1220 | |||
1221 | $item_tax_rates = $tax_rates[ $_product->get_tax_class() ]; |
||
1222 | |||
1223 | // Base tax for line before discount - we will store this in the order data |
||
1224 | $taxes = WC_Tax::calc_tax( $line_price, $item_tax_rates ); |
||
1225 | $line_subtotal_tax = array_sum( $taxes ); |
||
1226 | |||
1227 | $line_subtotal = $line_price; |
||
1228 | } |
||
1229 | |||
1230 | // Add to main subtotal |
||
1231 | $this->subtotal += $line_subtotal + $line_subtotal_tax; |
||
1232 | $this->subtotal_ex_tax += $line_subtotal; |
||
1233 | } |
||
1234 | |||
1235 | // Order cart items by price so coupon logic is 'fair' for customers and not based on order added to cart. |
||
1236 | uasort( $cart, array( $this, 'sort_by_subtotal' ) ); |
||
1237 | |||
1238 | /** |
||
1239 | * Calculate totals for items. |
||
1240 | */ |
||
1241 | foreach ( $cart as $cart_item_key => $values ) { |
||
1242 | |||
1243 | $_product = $values['data']; |
||
1244 | |||
1245 | // Prices |
||
1246 | $base_price = $_product->get_price(); |
||
1247 | $line_price = $_product->get_price() * $values['quantity']; |
||
1248 | |||
1249 | // Tax data |
||
1250 | $taxes = array(); |
||
1251 | $discounted_taxes = array(); |
||
1252 | |||
1253 | /** |
||
1254 | * No tax to calculate. |
||
1255 | */ |
||
1256 | if ( ! $_product->is_taxable() ) { |
||
1257 | |||
1258 | // Discounted Price (price with any pre-tax discounts applied) |
||
1259 | $discounted_price = $this->get_discounted_price( $values, $base_price, true ); |
||
1260 | $line_subtotal_tax = 0; |
||
1261 | $line_subtotal = $line_price; |
||
1262 | $line_tax = 0; |
||
1263 | $line_total = round( $discounted_price * $values['quantity'], WC_ROUNDING_PRECISION ); |
||
1264 | |||
1265 | /** |
||
1266 | * Prices include tax. |
||
1267 | */ |
||
1268 | } elseif ( $this->prices_include_tax ) { |
||
1269 | |||
1270 | $base_tax_rates = $shop_tax_rates[ $_product->tax_class ]; |
||
1271 | $item_tax_rates = $tax_rates[ $_product->get_tax_class() ]; |
||
1272 | |||
1273 | /** |
||
1274 | * ADJUST TAX - Calculations when base tax is not equal to the item tax. |
||
1275 | * |
||
1276 | * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations. |
||
1277 | * e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes. |
||
1278 | * This feature is experimental @since 2.4.7 and may change in the future. Use at your risk. |
||
1279 | */ |
||
1280 | if ( $item_tax_rates !== $base_tax_rates && apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) { |
||
1281 | |||
1282 | // Work out a new base price without the shop's base tax |
||
1283 | $taxes = WC_Tax::calc_tax( $line_price, $base_tax_rates, true, true ); |
||
1284 | |||
1285 | // Now we have a new item price (excluding TAX) |
||
1286 | $line_subtotal = round( $line_price - array_sum( $taxes ), WC_ROUNDING_PRECISION ); |
||
1287 | $taxes = WC_Tax::calc_tax( $line_subtotal, $item_tax_rates ); |
||
1288 | $line_subtotal_tax = array_sum( $taxes ); |
||
1289 | |||
1290 | // Adjusted price (this is the price including the new tax rate) |
||
1291 | $adjusted_price = ( $line_subtotal + $line_subtotal_tax ) / $values['quantity']; |
||
1292 | |||
1293 | // Apply discounts and get the discounted price FOR A SINGLE ITEM |
||
1294 | $discounted_price = $this->get_discounted_price( $values, $adjusted_price, true ); |
||
1295 | |||
1296 | // Convert back to line price and round nicely |
||
1297 | $discounted_line_price = round( $discounted_price * $values['quantity'], $this->dp ); |
||
1298 | |||
1299 | // Now use rounded line price to get taxes. |
||
1300 | $discounted_taxes = WC_Tax::calc_tax( $discounted_line_price, $item_tax_rates, true ); |
||
1301 | $line_tax = array_sum( $discounted_taxes ); |
||
1302 | $line_total = $discounted_line_price - $line_tax; |
||
1303 | |||
1304 | /** |
||
1305 | * Regular tax calculation (customer inside base and the tax class is unmodified. |
||
1306 | */ |
||
1307 | } else { |
||
1308 | |||
1309 | // Work out a new base price without the item tax |
||
1310 | $taxes = WC_Tax::calc_tax( $line_price, $item_tax_rates, true ); |
||
1311 | |||
1312 | // Now we have a new item price (excluding TAX) |
||
1313 | $line_subtotal = $line_price - array_sum( $taxes ); |
||
1314 | $line_subtotal_tax = array_sum( $taxes ); |
||
1315 | |||
1316 | // Calc prices and tax (discounted) |
||
1317 | $discounted_price = $this->get_discounted_price( $values, $base_price, true ); |
||
1318 | |||
1319 | // Convert back to line price and round nicely |
||
1320 | $discounted_line_price = round( $discounted_price * $values['quantity'], $this->dp ); |
||
1321 | |||
1322 | // Now use rounded line price to get taxes. |
||
1323 | $discounted_taxes = WC_Tax::calc_tax( $discounted_line_price, $item_tax_rates, true ); |
||
1324 | $line_tax = array_sum( $discounted_taxes ); |
||
1325 | $line_total = $discounted_line_price - $line_tax; |
||
1326 | } |
||
1327 | |||
1328 | // Tax rows - merge the totals we just got |
||
1329 | View Code Duplication | foreach ( array_keys( $this->taxes + $discounted_taxes ) as $key ) { |
|
1330 | $this->taxes[ $key ] = ( isset( $discounted_taxes[ $key ] ) ? $discounted_taxes[ $key ] : 0 ) + ( isset( $this->taxes[ $key ] ) ? $this->taxes[ $key ] : 0 ); |
||
1331 | } |
||
1332 | |||
1333 | /** |
||
1334 | * Prices exclude tax. |
||
1335 | */ |
||
1336 | } else { |
||
1337 | |||
1338 | $item_tax_rates = $tax_rates[ $_product->get_tax_class() ]; |
||
1339 | |||
1340 | // Work out a new base price without the shop's base tax |
||
1341 | $taxes = WC_Tax::calc_tax( $line_price, $item_tax_rates ); |
||
1342 | |||
1343 | // Now we have the item price (excluding TAX) |
||
1344 | $line_subtotal = $line_price; |
||
1345 | $line_subtotal_tax = array_sum( $taxes ); |
||
1346 | |||
1347 | // Now calc product rates |
||
1348 | $discounted_price = $this->get_discounted_price( $values, $base_price, true ); |
||
1349 | $discounted_taxes = WC_Tax::calc_tax( $discounted_price * $values['quantity'], $item_tax_rates ); |
||
1350 | $discounted_tax_amount = array_sum( $discounted_taxes ); |
||
1351 | $line_tax = $discounted_tax_amount; |
||
1352 | $line_total = $discounted_price * $values['quantity']; |
||
1353 | |||
1354 | // Tax rows - merge the totals we just got |
||
1355 | View Code Duplication | foreach ( array_keys( $this->taxes + $discounted_taxes ) as $key ) { |
|
1356 | $this->taxes[ $key ] = ( isset( $discounted_taxes[ $key ] ) ? $discounted_taxes[ $key ] : 0 ) + ( isset( $this->taxes[ $key ] ) ? $this->taxes[ $key ] : 0 ); |
||
1357 | } |
||
1358 | } |
||
1359 | |||
1360 | // Cart contents total is based on discounted prices and is used for the final total calculation |
||
1361 | $this->cart_contents_total += $line_total; |
||
1362 | |||
1363 | // Store costs + taxes for lines |
||
1364 | $this->cart_contents[ $cart_item_key ]['line_total'] = $line_total; |
||
1365 | $this->cart_contents[ $cart_item_key ]['line_tax'] = $line_tax; |
||
1366 | $this->cart_contents[ $cart_item_key ]['line_subtotal'] = $line_subtotal; |
||
1367 | $this->cart_contents[ $cart_item_key ]['line_subtotal_tax'] = $line_subtotal_tax; |
||
1368 | |||
1369 | // Store rates ID and costs - Since 2.2 |
||
1370 | $this->cart_contents[ $cart_item_key ]['line_tax_data'] = array( 'total' => $discounted_taxes, 'subtotal' => $taxes ); |
||
1371 | } |
||
1372 | |||
1373 | // Only calculate the grand total + shipping if on the cart/checkout |
||
1374 | if ( is_checkout() || is_cart() || defined('WOOCOMMERCE_CHECKOUT') || defined('WOOCOMMERCE_CART') ) { |
||
1375 | |||
1376 | // Calculate the Shipping |
||
1377 | $this->calculate_shipping(); |
||
1378 | |||
1379 | // Trigger the fees API where developers can add fees to the cart |
||
1380 | $this->calculate_fees(); |
||
1381 | |||
1382 | // Total up/round taxes and shipping taxes |
||
1383 | if ( $this->round_at_subtotal ) { |
||
1384 | $this->tax_total = WC_Tax::get_tax_total( $this->taxes ); |
||
1385 | $this->shipping_tax_total = WC_Tax::get_tax_total( $this->shipping_taxes ); |
||
1386 | $this->taxes = array_map( array( 'WC_Tax', 'round' ), $this->taxes ); |
||
1387 | $this->shipping_taxes = array_map( array( 'WC_Tax', 'round' ), $this->shipping_taxes ); |
||
1388 | } else { |
||
1389 | $this->tax_total = array_sum( $this->taxes ); |
||
1390 | $this->shipping_tax_total = array_sum( $this->shipping_taxes ); |
||
1391 | } |
||
1392 | |||
1393 | // VAT exemption done at this point - so all totals are correct before exemption |
||
1394 | if ( WC()->customer->is_vat_exempt() ) { |
||
1395 | $this->remove_taxes(); |
||
1396 | } |
||
1397 | |||
1398 | // Allow plugins to hook and alter totals before final total is calculated |
||
1399 | do_action( 'woocommerce_calculate_totals', $this ); |
||
1400 | |||
1401 | // Grand Total - Discounted product prices, discounted tax, shipping cost + tax |
||
1402 | $this->total = max( 0, apply_filters( 'woocommerce_calculated_total', round( $this->cart_contents_total + $this->tax_total + $this->shipping_tax_total + $this->shipping_total + $this->fee_total, $this->dp ), $this ) ); |
||
1403 | |||
1404 | } else { |
||
1405 | |||
1406 | // Set tax total to sum of all tax rows |
||
1407 | $this->tax_total = WC_Tax::get_tax_total( $this->taxes ); |
||
1408 | |||
1409 | // VAT exemption done at this point - so all totals are correct before exemption |
||
1410 | if ( WC()->customer->is_vat_exempt() ) { |
||
1411 | $this->remove_taxes(); |
||
1412 | } |
||
1413 | } |
||
1414 | |||
1415 | do_action( 'woocommerce_after_calculate_totals', $this ); |
||
1416 | |||
1417 | $this->set_session(); |
||
1418 | } |
||
1419 | |||
1420 | /** |
||
1421 | * Remove taxes. |
||
1422 | */ |
||
1423 | public function remove_taxes() { |
||
1439 | |||
1440 | /** |
||
1441 | * Looks at the totals to see if payment is actually required. |
||
1442 | * |
||
1443 | * @return bool |
||
1444 | */ |
||
1445 | public function needs_payment() { |
||
1448 | |||
1449 | /*-----------------------------------------------------------------------------------*/ |
||
1450 | /* Shipping related functions */ |
||
1451 | /*-----------------------------------------------------------------------------------*/ |
||
1452 | |||
1453 | /** |
||
1454 | * Uses the shipping class to calculate shipping then gets the totals when its finished. |
||
1455 | */ |
||
1456 | public function calculate_shipping() { |
||
1467 | |||
1468 | /** |
||
1469 | * Get packages to calculate shipping for. |
||
1470 | * |
||
1471 | * This lets us calculate costs for carts that are shipped to multiple locations. |
||
1472 | * |
||
1473 | * Shipping methods are responsible for looping through these packages. |
||
1474 | * |
||
1475 | * By default we pass the cart itself as a package - plugins can change this. |
||
1476 | * through the filter and break it up. |
||
1477 | * |
||
1478 | * @since 1.5.4 |
||
1479 | * @return array of cart items |
||
1480 | */ |
||
1481 | public function get_shipping_packages() { |
||
1506 | |||
1507 | /** |
||
1508 | * Looks through the cart to see if shipping is actually required. |
||
1509 | * |
||
1510 | * @return bool whether or not the cart needs shipping |
||
1511 | */ |
||
1512 | public function needs_shipping() { |
||
1513 | // If shipping is disabled or not yet configured, we can skip this. |
||
1514 | if ( ! wc_shipping_enabled() || 0 === wc_get_shipping_method_count( true ) ) { |
||
1515 | return false; |
||
1516 | } |
||
1517 | |||
1518 | $needs_shipping = false; |
||
1519 | |||
1520 | if ( $this->cart_contents ) { |
||
1521 | foreach ( $this->cart_contents as $cart_item_key => $values ) { |
||
1522 | $_product = $values['data']; |
||
1523 | if ( $_product->needs_shipping() ) { |
||
1524 | $needs_shipping = true; |
||
1525 | } |
||
1526 | } |
||
1527 | } |
||
1528 | |||
1529 | return apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping ); |
||
1530 | } |
||
1531 | |||
1532 | /** |
||
1533 | * Should the shipping address form be shown. |
||
1534 | * |
||
1535 | * @return bool |
||
1536 | */ |
||
1537 | public function needs_shipping_address() { |
||
1547 | |||
1548 | /** |
||
1549 | * Sees if the customer has entered enough data to calc the shipping yet. |
||
1550 | * |
||
1551 | * @return bool |
||
1552 | */ |
||
1553 | public function show_shipping() { |
||
1567 | |||
1568 | /** |
||
1569 | * Sees if we need a shipping address. |
||
1570 | * |
||
1571 | * @deprecated 2.5.0 in favor to wc_ship_to_billing_address_only() |
||
1572 | * |
||
1573 | * @return bool |
||
1574 | */ |
||
1575 | public function ship_to_billing_address_only() { |
||
1578 | |||
1579 | /** |
||
1580 | * Gets the shipping total (after calculation). |
||
1581 | * |
||
1582 | * @return string price or string for the shipping total |
||
1583 | */ |
||
1584 | public function get_cart_shipping_total() { |
||
1618 | |||
1619 | /*-----------------------------------------------------------------------------------*/ |
||
1620 | /* Coupons/Discount related functions */ |
||
1621 | /*-----------------------------------------------------------------------------------*/ |
||
1622 | |||
1623 | /** |
||
1624 | * Check for user coupons (now that we have billing email). If a coupon is invalid, add an error. |
||
1625 | * |
||
1626 | * Checks two types of coupons: |
||
1627 | * 1. Where a list of customer emails are set (limits coupon usage to those defined). |
||
1628 | * 2. Where a usage_limit_per_user is set (limits coupon usage to a number based on user ID and email). |
||
1629 | * |
||
1630 | * @param array $posted |
||
1631 | */ |
||
1632 | public function check_customer_coupons( $posted ) { |
||
1699 | |||
1700 | /** |
||
1701 | * Returns whether or not a discount has been applied. |
||
1702 | * @param string $coupon_code |
||
1703 | * @return bool |
||
1704 | */ |
||
1705 | public function has_discount( $coupon_code = '' ) { |
||
1708 | |||
1709 | /** |
||
1710 | * Applies a coupon code passed to the method. |
||
1711 | * |
||
1712 | * @param string $coupon_code - The code to apply |
||
1713 | * @return bool True if the coupon is applied, false if it does not exist or cannot be applied |
||
1714 | */ |
||
1715 | public function add_discount( $coupon_code ) { |
||
1778 | |||
1779 | /** |
||
1780 | * Get array of applied coupon objects and codes. |
||
1781 | * @return array of applied coupons |
||
1782 | */ |
||
1783 | public function get_coupons( $deprecated = null ) { |
||
1797 | |||
1798 | /** |
||
1799 | * Gets the array of applied coupon codes. |
||
1800 | * |
||
1801 | * @return array of applied coupons |
||
1802 | */ |
||
1803 | public function get_applied_coupons() { |
||
1806 | |||
1807 | /** |
||
1808 | * Get the discount amount for a used coupon. |
||
1809 | * @param string $code coupon code |
||
1810 | * @param bool $ex_tax inc or ex tax |
||
1811 | * @return float discount amount |
||
1812 | */ |
||
1813 | public function get_coupon_discount_amount( $code, $ex_tax = true ) { |
||
1822 | |||
1823 | /** |
||
1824 | * Get the discount tax amount for a used coupon (for tax inclusive prices). |
||
1825 | * @param string $code coupon code |
||
1826 | * @param bool inc or ex tax |
||
1827 | * @return float discount amount |
||
1828 | */ |
||
1829 | public function get_coupon_discount_tax_amount( $code ) { |
||
1832 | |||
1833 | /** |
||
1834 | * Remove coupons from the cart of a defined type. Type 1 is before tax, type 2 is after tax. |
||
1835 | */ |
||
1836 | public function remove_coupons( $deprecated = null ) { |
||
1842 | |||
1843 | /** |
||
1844 | * Remove a single coupon by code. |
||
1845 | * @param string $coupon_code Code of the coupon to remove |
||
1846 | * @return bool |
||
1847 | */ |
||
1848 | public function remove_coupon( $coupon_code ) { |
||
1868 | |||
1869 | /** |
||
1870 | * Function to apply discounts to a product and get the discounted price (before tax is applied). |
||
1871 | * |
||
1872 | * @param mixed $values |
||
1873 | * @param mixed $price |
||
1874 | * @param bool $add_totals (default: false) |
||
1875 | * @return float price |
||
1876 | */ |
||
1877 | public function get_discounted_price( $values, $price, $add_totals = false ) { |
||
1921 | |||
1922 | /** |
||
1923 | * Store how much discount each coupon grants. |
||
1924 | * |
||
1925 | * @access private |
||
1926 | * @param string $code |
||
1927 | * @param double $amount |
||
1928 | * @param double $tax |
||
1929 | */ |
||
1930 | private function increase_coupon_discount_amount( $code, $amount, $tax ) { |
||
1934 | |||
1935 | /** |
||
1936 | * Store how many times each coupon is applied to cart/items. |
||
1937 | * |
||
1938 | * @access private |
||
1939 | * @param string $code |
||
1940 | * @param int $count |
||
1941 | */ |
||
1942 | private function increase_coupon_applied_count( $code, $count = 1 ) { |
||
1948 | |||
1949 | /*-----------------------------------------------------------------------------------*/ |
||
1950 | /* Fees API to add additional costs to orders */ |
||
1951 | /*-----------------------------------------------------------------------------------*/ |
||
1952 | |||
1953 | /** |
||
1954 | * Add additional fee to the cart. |
||
1955 | * |
||
1956 | * @param string $name Unique name for the fee. Multiple fees of the same name cannot be added. |
||
1957 | * @param float $amount Fee amount. |
||
1958 | * @param bool $taxable (default: false) Is the fee taxable? |
||
1959 | * @param string $tax_class (default: '') The tax class for the fee if taxable. A blank string is standard tax class. |
||
1960 | */ |
||
1961 | public function add_fee( $name, $amount, $taxable = false, $tax_class = '' ) { |
||
1982 | |||
1983 | /** |
||
1984 | * Get fees. |
||
1985 | * |
||
1986 | * @return array |
||
1987 | */ |
||
1988 | public function get_fees() { |
||
1991 | |||
1992 | /** |
||
1993 | * Calculate fees. |
||
1994 | */ |
||
1995 | public function calculate_fees() { |
||
2029 | |||
2030 | /*-----------------------------------------------------------------------------------*/ |
||
2031 | /* Get Formatted Totals */ |
||
2032 | /*-----------------------------------------------------------------------------------*/ |
||
2033 | |||
2034 | /** |
||
2035 | * Gets the order total (after calculation). |
||
2036 | * |
||
2037 | * @return string formatted price |
||
2038 | */ |
||
2039 | public function get_total() { |
||
2042 | |||
2043 | /** |
||
2044 | * Gets the total excluding taxes. |
||
2045 | * |
||
2046 | * @return string formatted price |
||
2047 | */ |
||
2048 | public function get_total_ex_tax() { |
||
2055 | |||
2056 | /** |
||
2057 | * Gets the cart contents total (after calculation). |
||
2058 | * |
||
2059 | * @return string formatted price |
||
2060 | */ |
||
2061 | public function get_cart_total() { |
||
2070 | |||
2071 | /** |
||
2072 | * Gets the sub total (after calculation). |
||
2073 | * |
||
2074 | * @param bool $compound whether to include compound taxes |
||
2075 | * @return string formatted price |
||
2076 | */ |
||
2077 | public function get_cart_subtotal( $compound = false ) { |
||
2110 | |||
2111 | /** |
||
2112 | * Get the product row price per item. |
||
2113 | * |
||
2114 | * @param WC_Product $_product |
||
2115 | * @return string formatted price |
||
2116 | */ |
||
2117 | public function get_product_price( $_product ) { |
||
2126 | |||
2127 | /** |
||
2128 | * Get the product row subtotal. |
||
2129 | * |
||
2130 | * Gets the tax etc to avoid rounding issues. |
||
2131 | * |
||
2132 | * When on the checkout (review order), this will get the subtotal based on the customer's tax rate rather than the base rate. |
||
2133 | * |
||
2134 | * @param WC_Product $_product |
||
2135 | * @param int $quantity |
||
2136 | * @return string formatted price |
||
2137 | */ |
||
2138 | public function get_product_subtotal( $_product, $quantity ) { |
||
2176 | |||
2177 | /** |
||
2178 | * Gets the cart tax (after calculation). |
||
2179 | * |
||
2180 | * @return string formatted price |
||
2181 | */ |
||
2182 | public function get_cart_tax() { |
||
2187 | |||
2188 | /** |
||
2189 | * Get a tax amount. |
||
2190 | * @param string $tax_rate_id |
||
2191 | * @return float amount |
||
2192 | */ |
||
2193 | public function get_tax_amount( $tax_rate_id ) { |
||
2196 | |||
2197 | /** |
||
2198 | * Get a tax amount. |
||
2199 | * @param string $tax_rate_id |
||
2200 | * @return float amount |
||
2201 | */ |
||
2202 | public function get_shipping_tax_amount( $tax_rate_id ) { |
||
2205 | |||
2206 | /** |
||
2207 | * Get tax row amounts with or without compound taxes includes. |
||
2208 | * |
||
2209 | * @param bool $compound True if getting compound taxes |
||
2210 | * @param bool $display True if getting total to display |
||
2211 | * @return float price |
||
2212 | */ |
||
2213 | public function get_taxes_total( $compound = true, $display = true ) { |
||
2228 | |||
2229 | /** |
||
2230 | * Get the total of all cart discounts. |
||
2231 | * |
||
2232 | * @return float |
||
2233 | */ |
||
2234 | public function get_cart_discount_total() { |
||
2237 | |||
2238 | /** |
||
2239 | * Get the total of all cart tax discounts (used for discounts on tax inclusive prices). |
||
2240 | * |
||
2241 | * @return float |
||
2242 | */ |
||
2243 | public function get_cart_discount_tax_total() { |
||
2246 | |||
2247 | /** |
||
2248 | * Gets the total discount amount - both kinds. |
||
2249 | * |
||
2250 | * @return mixed formatted price or false if there are none |
||
2251 | */ |
||
2252 | public function get_total_discount() { |
||
2260 | |||
2261 | /** |
||
2262 | * Gets the total (product) discount amount - these are applied before tax. |
||
2263 | * |
||
2264 | * @deprecated Order discounts (after tax) removed in 2.3 so multiple methods for discounts are no longer required. |
||
2265 | * @return mixed formatted price or false if there are none |
||
2266 | */ |
||
2267 | public function get_discounts_before_tax() { |
||
2276 | |||
2277 | /** |
||
2278 | * Get the total of all order discounts (after tax discounts). |
||
2279 | * |
||
2280 | * @deprecated Order discounts (after tax) removed in 2.3 |
||
2281 | * @return int |
||
2282 | */ |
||
2283 | public function get_order_discount_total() { |
||
2287 | |||
2288 | /** |
||
2289 | * Function to apply cart discounts after tax. |
||
2290 | * @deprecated Coupons can not be applied after tax |
||
2291 | */ |
||
2292 | public function apply_cart_discounts_after_tax( $values, $price ) { |
||
2295 | |||
2296 | /** |
||
2297 | * Function to apply product discounts after tax. |
||
2298 | * @deprecated Coupons can not be applied after tax |
||
2299 | */ |
||
2300 | public function apply_product_discounts_after_tax( $values, $price ) { |
||
2303 | |||
2304 | /** |
||
2305 | * Gets the order discount amount - these are applied after tax. |
||
2306 | * @deprecated Coupons can not be applied after tax |
||
2307 | */ |
||
2308 | public function get_discounts_after_tax() { |
||
2311 | } |
||
2312 |
The break statement is not necessary if it is preceded for example by a return statement:
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.