1 <?php
2 3 4 5 6 7 8 9 10 11 12 13
14 class WC_Cart {
15
16
17 public $cart_contents = array();
18
19
20 public $applied_coupons = array();
21
22
23 public $coupon_discount_amounts = array();
24
25
26 public $coupon_applied_count = array();
27
28
29 public $cart_contents_total;
30
31
32 public $cart_contents_weight;
33
34
35 public $cart_contents_count;
36
37
38 public $cart_contents_tax;
39
40
41 public $total;
42
43
44 public $subtotal;
45
46
47 public $subtotal_ex_tax;
48
49
50 public $tax_total;
51
52
53 public $taxes;
54
55
56 public $shipping_taxes;
57
58
59 public $discount_cart;
60
61
62 public $discount_total;
63
64
65 public $fee_total;
66
67
68 public $shipping_total;
69
70
71 public $shipping_tax_total;
72
73
74 public $tax;
75
76
77 public $cart_session_data = array();
78
79
80 public $fees = array();
81
82 83 84 85 86 87
88 public function __construct() {
89 $this->tax = new WC_Tax();
90 $this->prices_include_tax = get_option( 'woocommerce_prices_include_tax' ) == 'yes';
91 $this->round_at_subtotal = get_option('woocommerce_tax_round_at_subtotal') == 'yes';
92 $this->tax_display_cart = get_option( 'woocommerce_tax_display_cart' );
93 $this->dp = absint( get_option( 'woocommerce_price_num_decimals' ) );
94 $this->display_totals_ex_tax = $this->tax_display_cart == 'excl';
95 $this->display_cart_ex_tax = $this->tax_display_cart == 'excl';
96
97
98 $this->cart_session_data = array(
99 'cart_contents_total' => 0,
100 'cart_contents_weight' => 0,
101 'cart_contents_count' => 0,
102 'cart_contents_tax' => 0,
103 'total' => 0,
104 'subtotal' => 0,
105 'subtotal_ex_tax' => 0,
106 'tax_total' => 0,
107 'taxes' => array(),
108 'shipping_taxes' => array(),
109 'discount_cart' => 0,
110 'discount_total' => 0,
111 'shipping_total' => 0,
112 'shipping_tax_total' => 0,
113 'coupon_discount_amounts' => array(),
114 );
115
116 add_action( 'init', array( $this, 'init' ), 5 );
117 }
118
119 120 121 122 123 124
125 public function init() {
126 $this->get_cart_from_session();
127
128 add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ), 1 );
129 add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_coupons' ), 1 );
130 add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_customer_coupons' ), 1 );
131 }
132
133
134
135
136
137 138 139 140 141 142
143 public function get_cart_from_session() {
144
145
146 foreach ( $this->cart_session_data as $key => $default ) {
147 $this->$key = WC()->session->get( $key, $default );
148 }
149
150
151 $this->applied_coupons = array_filter( WC()->session->get( 'applied_coupons', array() ) );
152
153
154 $cart = WC()->session->get( 'cart', array() );
155
156 $update_cart_session = false;
157
158 if ( is_array( $cart ) ) {
159 foreach ( $cart as $key => $values ) {
160 $_product = get_product( $values['variation_id'] ? $values['variation_id'] : $values['product_id'] );
161
162 if ( ! empty( $_product ) && $_product->exists() && $values['quantity'] > 0 ) {
163
164 if ( ! $_product->is_purchasable() ) {
165
166
167 $update_cart_session = true;
168 wc_add_notice( sprintf( __( '%s has been removed from your cart because it can no longer be purchased. Please contact us if you need assistance.', 'woocommerce' ), $_product->get_title() ), 'error' );
169
170 } else {
171
172
173 $this->cart_contents[ $key ] = apply_filters( 'woocommerce_get_cart_item_from_session', array(
174 'product_id' => $values['product_id'],
175 'variation_id' => $values['variation_id'],
176 'variation' => $values['variation'],
177 'quantity' => $values['quantity'],
178 'data' => $_product
179 ), $values, $key );
180
181 }
182 }
183 }
184 }
185
186 if ( $update_cart_session ) {
187 WC()->session->cart = $this->get_cart_for_session();
188 }
189
190 $this->set_cart_cookies( sizeof( $this->cart_contents ) > 0 );
191
192
193 do_action( 'woocommerce_cart_loaded_from_session', $this );
194
195
196 if ( ( ! $this->subtotal && sizeof( $this->cart_contents ) > 0 ) || $update_cart_session ) {
197 $this->calculate_totals();
198 }
199 }
200
201 202 203
204 public function set_session() {
205
206 $cart_session = $this->get_cart_for_session();
207
208 WC()->session->set( 'cart', $cart_session );
209 WC()->session->set( 'applied_coupons', $this->applied_coupons );
210 WC()->session->set( 'coupon_discount_amounts', $this->coupon_discount_amounts );
211
212 foreach ( $this->cart_session_data as $key => $default ) {
213 WC()->session->set( $key, $this->$key );
214 }
215
216 if ( get_current_user_id() ) {
217 $this->persistent_cart_update();
218 }
219
220 do_action( 'woocommerce_cart_updated' );
221 }
222
223 224 225 226 227 228 229
230 public function empty_cart( $clear_persistent_cart = true ) {
231 $this->cart_contents = array();
232 $this->reset();
233
234 unset( WC()->session->order_awaiting_payment, WC()->session->applied_coupons, WC()->session->coupon_discount_amounts, WC()->session->cart );
235
236 if ( $clear_persistent_cart && get_current_user_id() ) {
237 $this->persistent_cart_destroy();
238 }
239
240 do_action( 'woocommerce_cart_emptied' );
241 }
242
243
244
245
246
247 248 249 250 251 252
253 public function persistent_cart_update() {
254 update_user_meta( get_current_user_id(), '_woocommerce_persistent_cart', array(
255 'cart' => WC()->session->cart,
256 ) );
257 }
258
259 260 261 262 263 264
265 public function persistent_cart_destroy() {
266 delete_user_meta( get_current_user_id(), '_woocommerce_persistent_cart' );
267 }
268
269
270
271
272
273 274 275 276 277 278
279 public function coupons_enabled() {
280 return apply_filters( 'woocommerce_coupons_enabled', get_option( 'woocommerce_enable_coupons' ) == 'yes' );
281 }
282
283 284 285 286 287 288
289 public function get_cart_contents_count() {
290 return apply_filters( 'woocommerce_cart_contents_count', $this->cart_contents_count );
291 }
292
293 294 295 296 297 298
299 public function check_cart_items() {
300 $result = $this->check_cart_item_validity();
301
302 if ( is_wp_error( $result ) ) {
303 wc_add_notice( $result->get_error_message(), 'error' );
304 }
305
306
307 $result = $this->check_cart_item_stock();
308
309 if ( is_wp_error( $result ) ) {
310 wc_add_notice( $result->get_error_message(), 'error' );
311 }
312 }
313
314 315 316 317 318 319
320 public function check_cart_coupons() {
321 foreach ( $this->applied_coupons as $code ) {
322 $coupon = new WC_Coupon( $code );
323
324 if ( ! $coupon->is_valid() ) {
325
326 $coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_INVALID_REMOVED );
327
328
329 $this->remove_coupon( $code );
330
331
332 WC()->session->set( 'refresh_totals', true );
333 }
334 }
335 }
336
337 338 339 340 341 342
343 public function get_cart_item_quantities() {
344 $quantities = array();
345
346 foreach ( $this->get_cart() as $cart_item_key => $values ) {
347 if ( $values['variation_id'] > 0 && $values['data']->variation_has_stock ) {
348
349 $quantities[ $values['variation_id'] ] = isset( $quantities[ $values['variation_id'] ] ) ? $quantities[ $values['variation_id'] ] + $values['quantity'] : $values['quantity'];
350 } else {
351 $quantities[ $values['product_id'] ] = isset( $quantities[ $values['product_id'] ] ) ? $quantities[ $values['product_id'] ] + $values['quantity'] : $values['quantity'];
352 }
353 }
354
355 return $quantities;
356 }
357
358 359 360 361
362 public function check_cart_item_validity() {
363 foreach ( $this->get_cart() as $cart_item_key => $values ) {
364
365 $_product = $values['data'];
366
367 if ( ! $_product || ! $_product->exists() || $_product->post->post_status == 'trash' ) {
368 $this->set_quantity( $cart_item_key, 0 );
369
370 return new WP_Error( 'invalid', __( 'An item which is no longer available was removed from your cart.', 'woocommerce' ) );
371 }
372 }
373
374 return true;
375 }
376
377 378 379 380 381 382
383 public function check_cart_item_stock() {
384 global $wpdb;
385
386 $error = new WP_Error();
387
388 $product_qty_in_cart = $this->get_cart_item_quantities();
389
390
391 foreach ( $this->get_cart() as $cart_item_key => $values ) {
392
393 $_product = $values['data'];
394
395 396 397
398 if ( $_product->managing_stock() ) {
399
400 401 402
403 if ( ! $_product->is_in_stock() || ! $_product->has_enough_stock( $values['quantity'] ) ) {
404 $error->add( 'out-of-stock', sprintf(__( 'Sorry, we do not have enough "%s" in stock to fulfill your order (%s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $_product->get_title(), $_product->stock ) );
405 return $error;
406 }
407
408
409 $key = '_product_id';
410 $value = $values['product_id'];
411 $in_cart = $values['quantity'];
412
413 414 415
416 if ( $values['variation_id'] && $_product->variation_has_stock && isset( $product_qty_in_cart[ $values['variation_id'] ] ) ) {
417
418 $key = '_variation_id';
419 $value = $values['variation_id'];
420 $in_cart = $product_qty_in_cart[ $values['variation_id'] ];
421
422 if ( ! $_product->has_enough_stock( $product_qty_in_cart[ $values['variation_id'] ] ) ) {
423 $error->add( 'out-of-stock', sprintf(__( 'Sorry, we do not have enough "%s" in stock to fulfill your order (%s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $_product->get_title(), $_product->stock ) );
424 return $error;
425 }
426
427 } elseif ( isset( $product_qty_in_cart[ $values['product_id'] ] ) ) {
428
429 $in_cart = $product_qty_in_cart[ $values['product_id'] ];
430
431 if ( ! $_product->has_enough_stock( $product_qty_in_cart[ $values['product_id'] ] ) ) {
432 $error->add( 'out-of-stock', sprintf(__( 'Sorry, we do not have enough "%s" in stock to fulfill your order (%s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $_product->get_title(), $_product->stock ) );
433 return $error;
434 }
435
436 }
437
438 439 440
441 if ( get_option( 'woocommerce_hold_stock_minutes' ) > 0 && ! $_product->backorders_allowed() ) {
442
443 $order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0;
444
445 $held_stock = $wpdb->get_var( $wpdb->prepare( "
446 SELECT SUM( order_item_meta.meta_value ) AS held_qty
447
448 FROM {$wpdb->posts} AS posts
449
450 LEFT JOIN {$wpdb->prefix}woocommerce_order_items as order_items ON posts.ID = order_items.order_id
451 LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
452 LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta2 ON order_items.order_item_id = order_item_meta2.order_item_id
453 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
454 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
455 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
456
457 WHERE order_item_meta.meta_key = '_qty'
458 AND order_item_meta2.meta_key = %s AND order_item_meta2.meta_value = %d
459 AND posts.post_type = 'shop_order'
460 AND posts.post_status = 'publish'
461 AND tax.taxonomy = 'shop_order_status'
462 AND term.slug IN ('pending')
463 AND posts.ID != %d
464 ", $key, $value, $order_id ) );
465
466 if ( $_product->stock < ( $held_stock + $in_cart ) ) {
467 $error->add( 'out-of-stock', sprintf(__( 'Sorry, we do not have enough "%s" in stock to fulfill your order right now. Please try again in %d minutes or edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $_product->get_title(), get_option( 'woocommerce_hold_stock_minutes' ) ) );
468 return $error;
469 }
470 }
471
472 473 474
475 } else {
476 if ( ! $_product->is_in_stock() ) {
477 $error->add( 'out-of-stock', sprintf(__( 'Sorry, "%s" is not in stock. Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $_product->get_title() ) );
478 return $error;
479 }
480 }
481 }
482
483 return true;
484 }
485
486 487 488 489 490 491 492 493
494 public function get_item_data( $cart_item, $flat = false ) {
495 $item_data = array();
496
497
498 if ( ! empty( $cart_item['data']->variation_id ) && is_array( $cart_item['variation'] ) ) {
499
500 $variation_list = array();
501
502 foreach ( $cart_item['variation'] as $name => $value ) {
503
504 if ( '' === $value )
505 continue;
506
507 $taxonomy = wc_attribute_taxonomy_name( str_replace( 'attribute_pa_', '', urldecode( $name ) ) );
508
509
510 if ( taxonomy_exists( $taxonomy ) ) {
511 $term = get_term_by( 'slug', $value, $taxonomy );
512 if ( ! is_wp_error( $term ) && $term && $term->name ) {
513 $value = $term->name;
514 }
515 $label = wc_attribute_label( $taxonomy );
516
517
518 } else {
519 $value = apply_filters( 'woocommerce_variation_option_name', $value );
520 $product_attributes = $cart_item['data']->get_attributes();
521 if ( isset( $product_attributes[ str_replace( 'attribute_', '', $name ) ] ) ) {
522 $label = wc_attribute_label( $product_attributes[ str_replace( 'attribute_', '', $name ) ]['name'] );
523 } else {
524 $label = $name;
525 }
526 }
527
528 $item_data[] = array(
529 'key' => $label,
530 'value' => $value
531 );
532 }
533 }
534
535
536 $other_data = apply_filters( 'woocommerce_get_item_data', array(), $cart_item );
537
538 if ( $other_data && is_array( $other_data ) && sizeof( $other_data ) > 0 ) {
539
540 foreach ( $other_data as $data ) {
541
542 if ( empty( $data['hidden'] ) ) {
543 $display_value = ! empty( $data['display'] ) ? $data['display'] : $data['value'];
544
545 $item_data[] = array(
546 'key' => $data['name'],
547 'value' => $display_value
548 );
549 }
550 }
551 }
552
553
554 if ( sizeof( $item_data ) > 0 ) {
555
556 ob_start();
557
558 if ( $flat ) {
559 foreach ( $item_data as $data ) {
560 echo esc_html( $data['key'] ) . ': ' . wp_kses_post( $data['value'] ) . "\n";
561 }
562 } else {
563 wc_get_template( 'cart/cart-item-data.php', array( 'item_data' => $item_data ) );
564 }
565
566 return ob_get_clean();
567 }
568
569 return '';
570 }
571
572 573 574 575 576
577 public function get_cross_sells() {
578 $cross_sells = array();
579 $in_cart = array();
580 if ( sizeof( $this->get_cart() ) > 0 ) {
581 foreach ( $this->get_cart() as $cart_item_key => $values ) {
582 if ( $values['quantity'] > 0 ) {
583 $cross_sells = array_merge( $values['data']->get_cross_sells(), $cross_sells );
584 $in_cart[] = $values['product_id'];
585 }
586 }
587 }
588 $cross_sells = array_diff( $cross_sells, $in_cart );
589 return $cross_sells;
590 }
591
592 593 594 595 596
597 public function get_cart_url() {
598 $cart_page_id = wc_get_page_id( 'cart' );
599 return apply_filters( 'woocommerce_get_cart_url', $cart_page_id ? get_permalink( $cart_page_id ) : '' );
600 }
601
602 603 604 605 606
607 public function get_checkout_url() {
608 $checkout_page_id = wc_get_page_id( 'checkout' );
609 $checkout_url = '';
610 if ( $checkout_page_id ) {
611 if ( is_ssl() || get_option('woocommerce_force_ssl_checkout') == 'yes' ) {
612 $checkout_url = str_replace( 'http:', 'https:', get_permalink( $checkout_page_id ) );
613 } else {
614 $checkout_url = get_permalink( $checkout_page_id );
615 }
616 }
617 return apply_filters( 'woocommerce_get_checkout_url', $checkout_url );
618 }
619
620 621 622 623 624 625
626 public function get_remove_url( $cart_item_key ) {
627 $cart_page_id = wc_get_page_id('cart');
628 return apply_filters( 'woocommerce_get_remove_url', $cart_page_id ? wp_nonce_url( add_query_arg( 'remove_item', $cart_item_key, get_permalink( $cart_page_id ) ), 'woocommerce-cart' ) : '' );
629 }
630
631 632 633 634 635
636 public function get_cart() {
637 return array_filter( (array) $this->cart_contents );
638 }
639
640 641 642 643 644
645 private function get_cart_for_session() {
646
647 $cart_session = array();
648
649 if ( $this->get_cart() ) {
650 foreach ( $this->get_cart() as $key => $values ) {
651 $cart_session[ $key ] = $values;
652 unset( $cart_session[ $key ]['data'] );
653 }
654 }
655
656 return $cart_session;
657 }
658
659 660 661 662 663
664 public function get_taxes() {
665 $taxes = array();
666
667
668 foreach ( array_keys( $this->taxes + $this->shipping_taxes ) as $key ) {
669 $taxes[ $key ] = ( isset( $this->shipping_taxes[ $key ] ) ? $this->shipping_taxes[ $key ] : 0 ) + ( isset( $this->taxes[ $key ] ) ? $this->taxes[ $key ] : 0 );
670 }
671
672 return apply_filters( 'woocommerce_cart_get_taxes', $taxes, $this );
673 }
674
675 676 677 678 679 680
681 public function get_tax_totals() {
682 $taxes = $this->get_taxes();
683 $tax_totals = array();
684
685 foreach ( $taxes as $key => $tax ) {
686
687 $code = $this->tax->get_rate_code( $key );
688
689 if ( $code ) {
690 if ( ! isset( $tax_totals[ $code ] ) ) {
691 $tax_totals[ $code ] = new stdClass();
692 $tax_totals[ $code ]->amount = 0;
693 }
694
695 $tax_totals[ $code ]->tax_rate_id = $key;
696 $tax_totals[ $code ]->is_compound = $this->tax->is_compound( $key );
697 $tax_totals[ $code ]->label = $this->tax->get_rate_label( $key );
698 $tax_totals[ $code ]->amount += wc_round_tax_total( $tax );
699 $tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ) );
700 }
701 }
702
703 return apply_filters( 'woocommerce_cart_tax_totals', $tax_totals, $this );
704 }
705
706
707
708
709
710 711 712 713 714 715 716 717
718 public function find_product_in_cart( $cart_id = false ) {
719 if ( $cart_id !== false ) {
720 if ( is_array( $this->cart_contents ) ) {
721 foreach ( $this->cart_contents as $cart_item_key => $cart_item ) {
722 if ( $cart_item_key == $cart_id ) {
723 return $cart_item_key;
724 }
725 }
726 }
727 }
728 return '';
729 }
730
731 732 733 734 735 736 737 738 739
740 public function generate_cart_id( $product_id, $variation_id = 0, $variation = array(), $cart_item_data = array() ) {
741 $id_parts = array( $product_id );
742
743 if ( $variation_id && 0 != $variation_id )
744 $id_parts[] = $variation_id;
745
746 if ( is_array( $variation ) && ! empty( $variation ) ) {
747 $variation_key = '';
748 foreach ( $variation as $key => $value ) {
749 $variation_key .= trim( $key ) . trim( $value );
750 }
751 $id_parts[] = $variation_key;
752 }
753
754 if ( is_array( $cart_item_data ) && ! empty( $cart_item_data ) ) {
755 $cart_item_data_key = '';
756 foreach ( $cart_item_data as $key => $value ) {
757 if ( is_array( $value ) ) $value = http_build_query( $value );
758 $cart_item_data_key .= trim($key) . trim($value);
759 }
760 $id_parts[] = $cart_item_data_key;
761 }
762
763 return md5( implode( '_', $id_parts ) );
764 }
765
766 767 768 769 770 771 772 773 774 775
776 public function add_to_cart( $product_id, $quantity = 1, $variation_id = '', $variation = '', $cart_item_data = array() ) {
777
778 if ( $quantity <= 0 ) {
779 return false;
780 }
781
782
783 $cart_item_data = (array) apply_filters( 'woocommerce_add_cart_item_data', $cart_item_data, $product_id, $variation_id );
784
785
786 $cart_id = $this->generate_cart_id( $product_id, $variation_id, $variation, $cart_item_data );
787
788
789 $cart_item_key = $this->find_product_in_cart( $cart_id );
790
791
792 if ( 'product_variation' == get_post_type( $product_id ) ) {
793 $variation_id = $product_id;
794 $product_id = wp_get_post_parent_id( $variation_id );
795 }
796
797
798 $product_data = get_product( $variation_id ? $variation_id : $product_id );
799
800 if ( ! $product_data )
801 return false;
802
803
804 if ( $product_data->is_sold_individually() )
805 $quantity = 1;
806
807
808 if ( ! $product_data->is_purchasable() ) {
809 wc_add_notice( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ), 'error' );
810 return false;
811 }
812
813
814 if ( ! $product_data->is_in_stock() ) {
815
816 wc_add_notice( sprintf( __( 'You cannot add "%s" to the cart because the product is out of stock.', 'woocommerce' ), $product_data->get_title() ), 'error' );
817
818 return false;
819 } elseif ( ! $product_data->has_enough_stock( $quantity ) ) {
820
821 wc_add_notice( sprintf(__( 'You cannot add that amount of "%s" to the cart because there is not enough stock (%s remaining).', 'woocommerce' ), $product_data->get_title(), $product_data->get_stock_quantity() ), 'error' );
822
823 return false;
824 }
825
826
827 if ( $product_data->is_sold_individually() ) {
828 $in_cart_quantity = $cart_item_key ? $this->cart_contents[$cart_item_key]['quantity'] : 0;
829
830
831 if ( $in_cart_quantity > 0 ) {
832 wc_add_notice( sprintf(
833 '<a href="%s" class="button wc-forward">%s</a> %s',
834 $this->get_cart_url(),
835 __( 'View Cart', 'woocommerce' ),
836 sprintf( __( 'You cannot add another "%s" to your cart.', 'woocommerce' ), $product_data->get_title() )
837 ), 'error' );
838 return false;
839 }
840 }
841
842
843 $product_qty_in_cart = $this->get_cart_item_quantities();
844
845 if ( $product_data->managing_stock() ) {
846
847
848 if ( $variation_id && $product_data->variation_has_stock ) {
849
850 if ( isset( $product_qty_in_cart[ $variation_id ] ) && ! $product_data->has_enough_stock( $product_qty_in_cart[ $variation_id ] + $quantity ) ) {
851 wc_add_notice( sprintf(
852 '<a href="%s" class="button wc-forward">%s</a> %s',
853 $this->get_cart_url(),
854 __( 'View Cart', 'woocommerce' ),
855 sprintf( __( 'You cannot add that amount to the cart — we have %s in stock and you already have %s in your cart.', 'woocommerce' ), $product_data->get_stock_quantity(), $product_qty_in_cart[ $variation_id ] )
856 ), 'error' );
857 return false;
858 }
859
860
861 } else {
862
863 if ( isset( $product_qty_in_cart[ $product_id ] ) && ! $product_data->has_enough_stock( $product_qty_in_cart[ $product_id ] + $quantity ) ) {
864 wc_add_notice( sprintf(
865 '<a href="%s" class="button wc-forward">%s</a> %s',
866 $this->get_cart_url(),
867 __( 'View Cart', 'woocommerce' ),
868 sprintf( __( 'You cannot add that amount to the cart — we have %s in stock and you already have %s in your cart.', 'woocommerce' ), $product_data->get_stock_quantity(), $product_qty_in_cart[ $product_id ] )
869 ), 'error' );
870 return false;
871 }
872
873 }
874
875 }
876
877
878 if ( $cart_item_key ) {
879
880 $new_quantity = $quantity + $this->cart_contents[$cart_item_key]['quantity'];
881
882 $this->set_quantity( $cart_item_key, $new_quantity, false );
883
884 } else {
885
886 $cart_item_key = $cart_id;
887
888
889 $this->cart_contents[$cart_item_key] = apply_filters( 'woocommerce_add_cart_item', array_merge( $cart_item_data, array(
890 'product_id' => $product_id,
891 'variation_id' => $variation_id,
892 'variation' => $variation,
893 'quantity' => $quantity,
894 'data' => $product_data
895 ) ), $cart_item_key );
896
897 }
898
899 do_action( 'woocommerce_add_to_cart', $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data );
900
901 $this->set_cart_cookies();
902 $this->calculate_totals();
903
904 return true;
905 }
906
907 908 909 910 911 912 913
914 public function set_quantity( $cart_item_key, $quantity = 1, $refresh_totals = true ) {
915 if ( $quantity == 0 || $quantity < 0 ) {
916 do_action( 'woocommerce_before_cart_item_quantity_zero', $cart_item_key );
917 unset( $this->cart_contents[ $cart_item_key ] );
918 } else {
919 $this->cart_contents[ $cart_item_key ]['quantity'] = $quantity;
920 do_action( 'woocommerce_after_cart_item_quantity_update', $cart_item_key, $quantity );
921 }
922
923 if ( $refresh_totals ) {
924 $this->calculate_totals();
925 }
926 }
927
928 929 930 931 932 933 934
935 private function set_cart_cookies( $set = true ) {
936 if ( $set ) {
937 wc_setcookie( 'woocommerce_items_in_cart', 1 );
938 wc_setcookie( 'woocommerce_cart_hash', md5( json_encode( $this->get_cart() ) ) );
939 } elseif ( isset( $_COOKIE['woocommerce_items_in_cart'] ) ) {
940 wc_setcookie( 'woocommerce_items_in_cart', 0, time() - 3600 );
941 wc_setcookie( 'woocommerce_cart_hash', '', time() - 3600 );
942 }
943
944 do_action( 'woocommerce_set_cart_cookies', $set );
945 }
946
947
948
949
950
951 952 953 954 955 956
957 private function reset() {
958 foreach ( $this->cart_session_data as $key => $default ) {
959 $this->$key = $default;
960 unset( WC()->session->$key );
961 }
962 }
963
964 965 966 967 968
969 public function calculate_totals() {
970
971 $this->reset();
972
973 do_action( 'woocommerce_before_calculate_totals', $this );
974
975 if ( sizeof( $this->get_cart() ) == 0 ) {
976 $this->set_session();
977 return;
978 }
979
980 $tax_rates = array();
981 $shop_tax_rates = array();
982
983 984 985
986 foreach ( $this->get_cart() as $cart_item_key => $values ) {
987
988 $_product = $values['data'];
989
990
991 $this->cart_contents_weight += $_product->get_weight() * $values['quantity'];
992 $this->cart_contents_count += $values['quantity'];
993
994
995 $base_price = $_product->get_price();
996 $line_price = $_product->get_price() * $values['quantity'];
997
998 $line_subtotal = 0;
999 $line_subtotal_tax = 0;
1000
1001 1002 1003
1004 if ( ! $_product->is_taxable() ) {
1005
1006
1007 $this->subtotal += $line_price;
1008 $this->subtotal_ex_tax += $line_price;
1009
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
1022 } elseif ( $this->prices_include_tax ) {
1023
1024
1025 if ( empty( $shop_tax_rates[ $_product->tax_class ] ) )
1026 $shop_tax_rates[ $_product->tax_class ] = $this->tax->get_shop_base_rate( $_product->tax_class );
1027
1028
1029 if ( empty( $tax_rates[ $_product->get_tax_class() ] ) )
1030 $tax_rates[ $_product->get_tax_class() ] = $this->tax->get_rates( $_product->get_tax_class() );
1031
1032 $base_tax_rates = $shop_tax_rates[ $_product->tax_class ];
1033 $item_tax_rates = $tax_rates[ $_product->get_tax_class() ];
1034
1035 1036 1037
1038 if ( $item_tax_rates !== $base_tax_rates ) {
1039
1040
1041 $taxes = $this->tax->calc_tax( $line_price, $base_tax_rates, true, true );
1042
1043
1044 $line_subtotal = $line_price - array_sum( $taxes );
1045
1046
1047 $tax_result = $this->tax->calc_tax( $line_subtotal, $item_tax_rates );
1048 $line_subtotal_tax = array_sum( $tax_result );
1049
1050 1051 1052
1053 } else {
1054
1055
1056 $taxes = $this->tax->calc_tax( $line_price, $item_tax_rates, true );
1057 $line_subtotal_tax = array_sum( $taxes );
1058 $line_subtotal = $line_price - array_sum( $taxes );
1059 }
1060
1061 1062 1063 1064 1065
1066 } else {
1067
1068
1069 if ( empty( $tax_rates[ $_product->get_tax_class() ] ) )
1070 $tax_rates[ $_product->get_tax_class() ] = $this->tax->get_rates( $_product->get_tax_class() );
1071
1072 $item_tax_rates = $tax_rates[ $_product->get_tax_class() ];
1073
1074
1075 $taxes = $this->tax->calc_tax( $line_price, $item_tax_rates );
1076 $line_subtotal_tax = array_sum( $taxes );
1077 $line_subtotal = $line_price;
1078 }
1079
1080
1081 $this->subtotal += $line_subtotal + $line_subtotal_tax;
1082 $this->subtotal_ex_tax += $line_subtotal;
1083 }
1084
1085 1086 1087
1088 foreach ( $this->get_cart() as $cart_item_key => $values ) {
1089
1090 $_product = $values['data'];
1091
1092
1093 $base_price = $_product->get_price();
1094 $line_price = $_product->get_price() * $values['quantity'];
1095
1096 1097 1098
1099 if ( ! $_product->is_taxable() ) {
1100
1101
1102 $discounted_price = $this->get_discounted_price( $values, $base_price, true );
1103 $discounted_tax_amount = 0;
1104 $tax_amount = 0;
1105 $line_subtotal_tax = 0;
1106 $line_subtotal = $line_price;
1107 $line_tax = 0;
1108 $line_total = $this->tax->round( $discounted_price * $values['quantity'] );
1109
1110 1111 1112
1113 } elseif ( $this->prices_include_tax ) {
1114
1115 $base_tax_rates = $shop_tax_rates[ $_product->tax_class ];
1116 $item_tax_rates = $tax_rates[ $_product->get_tax_class() ];
1117
1118 1119 1120
1121 if ( $item_tax_rates !== $base_tax_rates ) {
1122
1123
1124 $taxes = $this->tax->calc_tax( $line_price, $base_tax_rates, true, true );
1125
1126
1127 $line_subtotal = round( $line_price - array_sum( $taxes ), WC_ROUNDING_PRECISION );
1128
1129
1130 $taxes = $this->tax->calc_tax( $line_subtotal, $item_tax_rates );
1131 $line_subtotal_tax = array_sum( $taxes );
1132
1133
1134 $adjusted_price = ( $line_subtotal + $line_subtotal_tax ) / $values['quantity'];
1135
1136
1137 $discounted_price = $this->get_discounted_price( $values, $adjusted_price, true );
1138 $discounted_taxes = $this->tax->calc_tax( $discounted_price * $values['quantity'], $item_tax_rates, true );
1139 $line_tax = array_sum( $discounted_taxes );
1140 $line_total = ( $discounted_price * $values['quantity'] ) - $line_tax;
1141
1142 1143 1144
1145 } else {
1146
1147
1148 $taxes = $this->tax->calc_tax( $line_price, $item_tax_rates, true );
1149
1150
1151 $line_subtotal = $line_price - array_sum( $taxes );
1152 $line_subtotal_tax = array_sum( $taxes );
1153
1154
1155 $discounted_price = $this->get_discounted_price( $values, $base_price, true );
1156 $discounted_taxes = $this->tax->calc_tax( $discounted_price * $values['quantity'], $item_tax_rates, true );
1157 $line_tax = array_sum( $discounted_taxes );
1158 $line_total = ( $discounted_price * $values['quantity'] ) - $line_tax;
1159 }
1160
1161
1162 foreach ( array_keys( $this->taxes + $discounted_taxes ) as $key ) {
1163 $this->taxes[ $key ] = ( isset( $discounted_taxes[ $key ] ) ? $discounted_taxes[ $key ] : 0 ) + ( isset( $this->taxes[ $key ] ) ? $this->taxes[ $key ] : 0 );
1164 }
1165
1166 1167 1168
1169 } else {
1170
1171 $item_tax_rates = $tax_rates[ $_product->get_tax_class() ];
1172
1173
1174 $taxes = $this->tax->calc_tax( $line_price, $item_tax_rates );
1175
1176
1177 $line_subtotal = $line_price;
1178 $line_subtotal_tax = array_sum( $taxes );
1179
1180
1181 $discounted_price = $this->get_discounted_price( $values, $base_price, true );
1182 $discounted_taxes = $this->tax->calc_tax( $discounted_price * $values['quantity'], $item_tax_rates );
1183 $discounted_tax_amount = array_sum( $discounted_taxes );
1184 $line_tax = $discounted_tax_amount;
1185 $line_total = $discounted_price * $values['quantity'];
1186
1187
1188 foreach ( array_keys( $this->taxes + $discounted_taxes ) as $key ) {
1189 $this->taxes[ $key ] = ( isset( $discounted_taxes[ $key ] ) ? $discounted_taxes[ $key ] : 0 ) + ( isset( $this->taxes[ $key ] ) ? $this->taxes[ $key ] : 0 );
1190 }
1191 }
1192
1193
1194 $this->apply_product_discounts_after_tax( $values, $line_total + $line_tax );
1195
1196
1197 $this->cart_contents_total += $line_total;
1198
1199
1200 $this->cart_contents[ $cart_item_key ]['line_total'] = $line_total;
1201 $this->cart_contents[ $cart_item_key ]['line_tax'] = $line_tax;
1202 $this->cart_contents[ $cart_item_key ]['line_subtotal'] = $line_subtotal;
1203 $this->cart_contents[ $cart_item_key ]['line_subtotal_tax'] = $line_subtotal_tax;
1204 }
1205
1206
1207 if ( is_checkout() || is_cart() || defined('WOOCOMMERCE_CHECKOUT') || defined('WOOCOMMERCE_CART') ) {
1208
1209
1210 $this->calculate_shipping();
1211
1212
1213 $this->calculate_fees();
1214
1215
1216 if ( $this->round_at_subtotal ) {
1217 $this->tax_total = $this->tax->get_tax_total( $this->taxes );
1218 $this->shipping_tax_total = $this->tax->get_tax_total( $this->shipping_taxes );
1219 $this->taxes = array_map( array( $this->tax, 'round' ), $this->taxes );
1220 $this->shipping_taxes = array_map( array( $this->tax, 'round' ), $this->shipping_taxes );
1221 } else {
1222 $this->tax_total = array_sum( $this->taxes );
1223 $this->shipping_tax_total = array_sum( $this->shipping_taxes );
1224 }
1225
1226
1227 if ( WC()->customer->is_vat_exempt() ) {
1228 $this->remove_taxes();
1229 }
1230
1231
1232 $this->apply_cart_discounts_after_tax();
1233
1234
1235 do_action( 'woocommerce_calculate_totals', $this );
1236
1237
1238 $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->discount_total + $this->fee_total, $this->dp ), $this ) );
1239
1240 } else {
1241
1242
1243 $this->tax_total = $this->tax->get_tax_total( $this->taxes );
1244
1245
1246 if ( WC()->customer->is_vat_exempt() ) {
1247 $this->remove_taxes();
1248 }
1249
1250
1251 $this->apply_cart_discounts_after_tax();
1252 }
1253
1254 $this->set_session();
1255 }
1256
1257 1258 1259 1260 1261 1262
1263 public function remove_taxes() {
1264 $this->shipping_tax_total = $this->tax_total = 0;
1265 $this->subtotal = $this->subtotal_ex_tax;
1266
1267 foreach ( $this->cart_contents as $cart_item_key => $item ) {
1268 $this->cart_contents[ $cart_item_key ]['line_subtotal_tax'] = $this->cart_contents[ $cart_item_key ]['line_tax'] = 0;
1269 }
1270
1271
1272 if ( apply_filters( 'woocommerce_cart_remove_taxes_apply_zero_rate', true ) ) {
1273 $this->taxes = $this->shipping_taxes = array( apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) => 0 );
1274 } else {
1275 $this->taxes = $this->shipping_taxes = array();
1276 }
1277 }
1278
1279 1280 1281 1282 1283
1284 public function needs_payment() {
1285 return apply_filters( 'woocommerce_cart_needs_payment', $this->total > 0, $this );
1286 }
1287
1288
1289
1290
1291
1292 1293 1294 1295 1296 1297
1298 public function calculate_shipping() {
1299 if ( $this->needs_shipping() && $this->show_shipping() ) {
1300 WC()->shipping->calculate_shipping( $this->get_shipping_packages() );
1301 } else {
1302 WC()->shipping->reset_shipping();
1303 }
1304
1305
1306 $this->shipping_total = WC()->shipping->shipping_total;
1307 $this->shipping_taxes = WC()->shipping->shipping_taxes;
1308 }
1309
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
1324 public function get_shipping_packages() {
1325
1326 $packages = array();
1327
1328 $packages[0]['contents'] = $this->get_cart();
1329 $packages[0]['contents_cost'] = 0;
1330 $packages[0]['applied_coupons'] = $this->applied_coupons;
1331 $packages[0]['destination']['country'] = WC()->customer->get_shipping_country();
1332 $packages[0]['destination']['state'] = WC()->customer->get_shipping_state();
1333 $packages[0]['destination']['postcode'] = WC()->customer->get_shipping_postcode();
1334 $packages[0]['destination']['city'] = WC()->customer->get_shipping_city();
1335 $packages[0]['destination']['address'] = WC()->customer->get_shipping_address();
1336 $packages[0]['destination']['address_2'] = WC()->customer->get_shipping_address_2();
1337
1338 foreach ( $this->get_cart() as $item )
1339 if ( $item['data']->needs_shipping() )
1340 if ( isset( $item['line_total'] ) )
1341 $packages[0]['contents_cost'] += $item['line_total'];
1342
1343 return apply_filters( 'woocommerce_cart_shipping_packages', $packages );
1344 }
1345
1346 1347 1348 1349 1350
1351 public function needs_shipping() {
1352 if ( get_option('woocommerce_calc_shipping') == 'no' )
1353 return false;
1354
1355 $needs_shipping = false;
1356
1357 if ( $this->cart_contents ) {
1358 foreach ( $this->cart_contents as $cart_item_key => $values ) {
1359 $_product = $values['data'];
1360 if ( $_product->needs_shipping() ) {
1361 $needs_shipping = true;
1362 }
1363 }
1364 }
1365
1366 return apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
1367 }
1368
1369 1370 1371 1372 1373
1374 function needs_shipping_address() {
1375
1376 $needs_shipping_address = false;
1377
1378 if ( WC()->cart->needs_shipping() === true && ! WC()->cart->ship_to_billing_address_only() ) {
1379 $needs_shipping_address = true;
1380 }
1381
1382 return apply_filters( 'woocommerce_cart_needs_shipping_address', $needs_shipping_address );
1383 }
1384
1385 1386 1387 1388 1389
1390 public function show_shipping() {
1391 if ( get_option('woocommerce_calc_shipping') == 'no' || ! is_array( $this->cart_contents ) )
1392 return false;
1393
1394 if ( get_option( 'woocommerce_shipping_cost_requires_address' ) == 'yes' ) {
1395 if ( ! WC()->customer->has_calculated_shipping() ) {
1396 if ( ! WC()->customer->get_shipping_country() || ( ! WC()->customer->get_shipping_state() && ! WC()->customer->get_shipping_postcode() ) )
1397 return false;
1398 }
1399 }
1400
1401 $show_shipping = true;
1402
1403 return apply_filters( 'woocommerce_cart_ready_to_calc_shipping', $show_shipping );
1404
1405 }
1406
1407 1408 1409 1410 1411
1412 public function ship_to_billing_address_only() {
1413 return get_option('woocommerce_ship_to_billing_address_only') == 'yes';
1414 }
1415
1416 1417 1418 1419 1420
1421 public function get_cart_shipping_total() {
1422 if ( isset( $this->shipping_total ) ) {
1423 if ( $this->shipping_total > 0 ) {
1424
1425
1426 if ( $this->tax_display_cart == 'excl' ) {
1427
1428 $return = wc_price( $this->shipping_total );
1429
1430 if ( $this->shipping_tax_total > 0 && $this->prices_include_tax ) {
1431 $return .= ' <small>' . WC()->countries->ex_tax_or_vat() . '</small>';
1432 }
1433
1434 return $return;
1435
1436 } else {
1437
1438 $return = wc_price( $this->shipping_total + $this->shipping_tax_total );
1439
1440 if ( $this->shipping_tax_total > 0 && ! $this->prices_include_tax ) {
1441 $return .= ' <small>' . WC()->countries->inc_tax_or_vat() . '</small>';
1442 }
1443
1444 return $return;
1445
1446 }
1447
1448 } else {
1449 return __( 'Free!', 'woocommerce' );
1450 }
1451 }
1452
1453 return '';
1454 }
1455
1456
1457
1458
1459
1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
1470 public function check_customer_coupons( $posted ) {
1471 if ( ! empty( $this->applied_coupons ) ) {
1472 foreach ( $this->applied_coupons as $code ) {
1473 $coupon = new WC_Coupon( $code );
1474
1475 if ( $coupon->is_valid() ) {
1476
1477
1478 if ( is_array( $coupon->customer_email ) && sizeof( $coupon->customer_email ) > 0 ) {
1479 $check_emails = array();
1480 $coupon->customer_email = array_map( 'sanitize_email', $coupon->customer_email );
1481
1482 if ( is_user_logged_in() ) {
1483 $current_user = wp_get_current_user();
1484 $check_emails[] = $current_user->user_email;
1485 }
1486 $check_emails[] = $posted['billing_email'];
1487 $check_emails = array_map( 'sanitize_email', array_map( 'strtolower', $check_emails ) );
1488
1489 if ( 0 == sizeof( array_intersect( $check_emails, $coupon->customer_email ) ) ) {
1490 $coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED );
1491
1492
1493 $this->remove_coupon( $code );
1494
1495
1496 WC()->session->set( 'refresh_totals', true );
1497 }
1498 }
1499
1500
1501 if ( $coupon->usage_limit_per_user > 0 ) {
1502 $check_emails = array();
1503 $used_by = array_filter( (array) get_post_meta( $coupon->id, '_used_by' ) );
1504
1505 if ( is_user_logged_in() ) {
1506 $current_user = wp_get_current_user();
1507 $check_emails[] = sanitize_email( $current_user->user_email );
1508 $usage_count = sizeof( array_keys( $used_by, get_current_user_id() ) );
1509 } else {
1510 $check_emails[] = sanitize_email( $posted['billing_email'] );
1511 $user = get_user_by( 'email', $posted['billing_email'] );
1512 if ( $user ) {
1513 $usage_count = sizeof( array_keys( $used_by, $user->ID ) );
1514 } else {
1515 $usage_count = 0;
1516 }
1517 }
1518
1519 foreach ( $check_emails as $check_email ) {
1520 $usage_count = $usage_count + sizeof( array_keys( $used_by, $check_email ) );
1521 }
1522
1523 if ( $usage_count >= $coupon->usage_limit_per_user ) {
1524 $coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED );
1525
1526
1527 $this->remove_coupon( $code );
1528
1529
1530 WC()->session->set( 'refresh_totals', true );
1531 }
1532 }
1533 }
1534 }
1535 }
1536 }
1537
1538 1539 1540 1541 1542
1543 public function has_discount( $coupon_code ) {
1544 return in_array( apply_filters( 'woocommerce_coupon_code', $coupon_code ), $this->applied_coupons );
1545 }
1546
1547 1548 1549 1550 1551 1552
1553 public function add_discount( $coupon_code ) {
1554
1555 if ( ! $this->coupons_enabled() )
1556 return false;
1557
1558
1559 $coupon_code = apply_filters( 'woocommerce_coupon_code', $coupon_code );
1560
1561
1562 $the_coupon = new WC_Coupon( $coupon_code );
1563
1564 if ( $the_coupon->id ) {
1565
1566
1567 if ( ! $the_coupon->is_valid() ) {
1568 wc_add_notice( $the_coupon->get_error_message(), 'error' );
1569 return false;
1570 }
1571
1572
1573 if ( $this->has_discount( $coupon_code ) ) {
1574 $the_coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_ALREADY_APPLIED );
1575 return false;
1576 }
1577
1578
1579 if ( $the_coupon->individual_use == 'yes' ) {
1580 $this->applied_coupons = apply_filters( 'woocommerce_apply_individual_use_coupon', array(), $the_coupon, $this->applied_coupons );
1581 }
1582
1583 if ( $this->applied_coupons ) {
1584 foreach ( $this->applied_coupons as $code ) {
1585 $coupon = new WC_Coupon( $code );
1586
1587 if ( $coupon->individual_use == 'yes' && false === apply_filters( 'woocommerce_apply_with_individual_use_coupon', false, $the_coupon, $coupon, $this->applied_coupons ) ) {
1588
1589
1590 $coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY );
1591
1592 return false;
1593 }
1594 }
1595 }
1596
1597 $this->applied_coupons[] = $coupon_code;
1598
1599
1600 if ( $the_coupon->enable_free_shipping() ) {
1601 $packages = WC()->shipping->get_packages();
1602 $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
1603
1604 foreach ( $packages as $i => $package ) {
1605 $chosen_shipping_methods[ $i ] = 'free_shipping';
1606 }
1607
1608 WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
1609 }
1610
1611 $this->calculate_totals();
1612
1613 $the_coupon->add_coupon_message( WC_Coupon::WC_COUPON_SUCCESS );
1614
1615 do_action( 'woocommerce_applied_coupon', $coupon_code );
1616
1617 return true;
1618
1619 } else {
1620 $the_coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_EXIST );
1621 return false;
1622 }
1623 return false;
1624 }
1625
1626 1627 1628 1629 1630
1631 public function get_coupons( $type = null ) {
1632 $coupons = array();
1633
1634 if ( 'cart' == $type || is_null( $type ) ) {
1635 if ( $this->applied_coupons ) {
1636 foreach ( $this->applied_coupons as $code ) {
1637 $coupon = new WC_Coupon( $code );
1638
1639 if ( $coupon->apply_before_tax() )
1640 $coupons[ $code ] = $coupon;
1641 }
1642 }
1643 }
1644
1645 if ( 'order' == $type || is_null( $type ) ) {
1646 if ( $this->applied_coupons ) {
1647 foreach ( $this->applied_coupons as $code ) {
1648 $coupon = new WC_Coupon( $code );
1649
1650 if ( ! $coupon->apply_before_tax() )
1651 $coupons[ $code ] = $coupon;
1652 }
1653 }
1654 }
1655
1656 return $coupons;
1657 }
1658
1659 1660 1661 1662 1663
1664 public function get_applied_coupons() {
1665 return $this->applied_coupons;
1666 }
1667
1668 1669 1670 1671 1672
1673 public function remove_coupons( $type = null ) {
1674
1675 if ( 'cart' == $type || 1 == $type ) {
1676 if ( $this->applied_coupons ) {
1677 foreach ( $this->applied_coupons as $code ) {
1678 $coupon = new WC_Coupon( $code );
1679
1680 if ( $coupon->apply_before_tax() )
1681 $this->remove_coupon( $code );
1682 }
1683 }
1684 } elseif ( 'order' == $type || 2 == $type ) {
1685 if ( $this->applied_coupons ) {
1686 foreach ( $this->applied_coupons as $code ) {
1687 $coupon = new WC_Coupon( $code );
1688
1689 if ( ! $coupon->apply_before_tax() )
1690 $this->remove_coupon( $code );
1691 }
1692 }
1693 } else {
1694 $this->applied_coupons = $this->coupon_discount_amounts = $this->coupon_applied_count = array();
1695 WC()->session->set( 'applied_coupons', array() );
1696 WC()->session->set( 'coupon_discount_amounts', array() );
1697 }
1698 }
1699
1700 1701 1702 1703 1704
1705 public function remove_coupon( $coupon_code ) {
1706
1707 if ( ! $this->coupons_enabled() )
1708 return false;
1709
1710
1711 $coupon_code = apply_filters( 'woocommerce_coupon_code', $coupon_code );
1712 $position = array_search( $coupon_code, $this->applied_coupons );
1713
1714 if ( $position !== false )
1715 unset( $this->applied_coupons[ $position ] );
1716
1717 WC()->session->set( 'applied_coupons', $this->applied_coupons );
1718
1719 return true;
1720 }
1721
1722 1723 1724 1725 1726 1727 1728 1729 1730
1731 public function get_discounted_price( $values, $price, $add_totals = false ) {
1732 if ( ! $price )
1733 return $price;
1734
1735 if ( ! empty( $this->applied_coupons ) ) {
1736 foreach ( $this->applied_coupons as $code ) {
1737 $coupon = new WC_Coupon( $code );
1738
1739 if ( $coupon->apply_before_tax() && $coupon->is_valid() ) {
1740 if ( $coupon->is_valid_for_product( $values['data'] ) || $coupon->is_valid_for_cart() ) {
1741
1742 $discount_amount = $coupon->get_discount_amount( $price, $values, $single = true );
1743 $price = max( $price - $discount_amount, 0 );
1744
1745 if ( $add_totals ) {
1746 $this->discount_cart += $discount_amount * $values['quantity'];
1747 $this->increase_coupon_discount_amount( $code, $discount_amount * $values['quantity'] );
1748 $this->increase_coupon_applied_count( $code, $values['quantity'] );
1749 }
1750 }
1751 }
1752 }
1753 }
1754
1755 return apply_filters( 'woocommerce_get_discounted_price', $price, $values, $this );
1756 }
1757
1758 1759 1760 1761 1762
1763 public function apply_cart_discounts_after_tax() {
1764 $pre_discount_total = round( $this->cart_contents_total + $this->tax_total + $this->shipping_tax_total + $this->shipping_total + $this->fee_total, $this->dp );
1765
1766 if ( $this->applied_coupons ) {
1767 foreach ( $this->applied_coupons as $code ) {
1768 $coupon = new WC_Coupon( $code );
1769
1770 do_action( 'woocommerce_cart_discount_after_tax_' . $coupon->type, $coupon );
1771
1772 if ( $coupon->is_valid() && ! $coupon->apply_before_tax() && $coupon->is_valid_for_cart() ) {
1773 $discount_amount = $coupon->get_discount_amount( $pre_discount_total );
1774 $pre_discount_total = $pre_discount_total - $discount_amount;
1775 $this->discount_total += $discount_amount;
1776 $this->increase_coupon_discount_amount( $code, $discount_amount );
1777 $this->increase_coupon_applied_count( $code );
1778 }
1779 }
1780 }
1781 }
1782
1783 1784 1785 1786 1787 1788 1789
1790 public function apply_product_discounts_after_tax( $values, $price ) {
1791 if ( ! empty( $this->applied_coupons ) ) {
1792 foreach ( $this->applied_coupons as $code ) {
1793 $coupon = new WC_Coupon( $code );
1794
1795 do_action( 'woocommerce_product_discount_after_tax_' . $coupon->type, $coupon, $values, $price );
1796
1797 if ( $coupon->is_valid() && ! $coupon->apply_before_tax() && $coupon->is_valid_for_product( $values['data'] ) ) {
1798 $discount_amount = $coupon->get_discount_amount( $price, $values );
1799 $this->discount_total += $discount_amount;
1800 $this->increase_coupon_discount_amount( $code, $discount_amount );
1801 $this->increase_coupon_applied_count( $code, $values['quantity'] );
1802 }
1803 }
1804 }
1805 }
1806
1807 1808 1809 1810 1811 1812 1813
1814 private function increase_coupon_discount_amount( $code, $amount ) {
1815 if ( empty( $this->coupon_discount_amounts[ $code ] ) )
1816 $this->coupon_discount_amounts[ $code ] = 0;
1817
1818 $this->coupon_discount_amounts[ $code ] += $amount;
1819 }
1820
1821 1822 1823 1824 1825 1826 1827
1828 private function increase_coupon_applied_count( $code, $count = 1 ) {
1829 if ( empty( $this->coupon_applied_count[ $code ] ) )
1830 $this->coupon_applied_count[ $code ] = 0;
1831
1832 $this->coupon_applied_count[ $code ] += $count;
1833 }
1834
1835
1836
1837
1838
1839 1840 1841 1842 1843 1844 1845 1846
1847 public function add_fee( $name, $amount, $taxable = false, $tax_class = '' ) {
1848
1849 $new_fee_id = sanitize_title( $name );
1850
1851
1852 foreach ( $this->fees as $fee ) {
1853 if ( $fee->id == $new_fee_id ) {
1854 return;
1855 }
1856 }
1857
1858 $new_fee = new stdClass();
1859 $new_fee->id = $new_fee_id;
1860 $new_fee->name = esc_attr( $name );
1861 $new_fee->amount = (float) esc_attr( $amount );
1862 $new_fee->tax_class = $tax_class;
1863 $new_fee->taxable = $taxable ? true : false;
1864 $new_fee->tax = 0;
1865 $this->fees[] = $new_fee;
1866 }
1867
1868 1869 1870 1871 1872 1873
1874 public function get_fees() {
1875 return array_filter( (array) $this->fees );
1876 }
1877
1878 1879 1880
1881 public function calculate_fees() {
1882
1883
1884 do_action( 'woocommerce_cart_calculate_fees', $this );
1885
1886
1887 if ( ! empty( $this->fees ) ) {
1888 foreach ( $this->fees as $fee_key => $fee ) {
1889 $this->fee_total += $fee->amount;
1890
1891 if ( $fee->taxable ) {
1892
1893 $tax_rates = $this->tax->get_rates( $fee->tax_class );
1894 $fee_taxes = $this->tax->calc_tax( $fee->amount, $tax_rates, false );
1895
1896 if ( ! empty( $fee_taxes ) ) {
1897
1898 $this->fees[ $fee_key ]->tax = array_sum( $fee_taxes );
1899
1900
1901 foreach ( array_keys( $this->taxes + $fee_taxes ) as $key ) {
1902 $this->taxes[ $key ] = ( isset( $fee_taxes[ $key ] ) ? $fee_taxes[ $key ] : 0 ) + ( isset( $this->taxes[ $key ] ) ? $this->taxes[ $key ] : 0 );
1903 }
1904 }
1905 }
1906 }
1907 }
1908 }
1909
1910
1911
1912
1913
1914 1915 1916 1917 1918
1919 public function get_order_discount_total() {
1920 return $this->discount_total;
1921 }
1922
1923 1924 1925 1926 1927
1928 public function get_cart_discount_total() {
1929 return $this->discount_cart;
1930 }
1931
1932 1933 1934 1935 1936
1937 public function get_total() {
1938 return apply_filters( 'woocommerce_cart_total', wc_price( $this->total ) );
1939 }
1940
1941 1942 1943 1944 1945
1946 public function get_total_ex_tax() {
1947 $total = $this->total - $this->tax_total - $this->shipping_tax_total;
1948 if ( $total < 0 )
1949 $total = 0;
1950 return apply_filters( 'woocommerce_cart_total_ex_tax', wc_price( $total ) );
1951 }
1952
1953 1954 1955 1956 1957
1958 public function get_cart_total() {
1959 if ( ! $this->prices_include_tax ) {
1960 $cart_contents_total = wc_price( $this->cart_contents_total );
1961 } else {
1962 $cart_contents_total = wc_price( $this->cart_contents_total + $this->tax_total );
1963 }
1964
1965 return apply_filters( 'woocommerce_cart_contents_total', $cart_contents_total );
1966 }
1967
1968 1969 1970 1971 1972 1973
1974 public function get_cart_subtotal( $compound = false ) {
1975
1976
1977
1978 if ( $compound ) {
1979
1980 $cart_subtotal = wc_price( $this->cart_contents_total + $this->shipping_total + $this->get_taxes_total( false, false ) );
1981
1982
1983 } else {
1984
1985
1986 if ( $this->tax_display_cart == 'excl' ) {
1987
1988 $cart_subtotal = wc_price( $this->subtotal_ex_tax );
1989
1990 if ( $this->tax_total > 0 && $this->prices_include_tax ) {
1991 $cart_subtotal .= ' <small>' . WC()->countries->ex_tax_or_vat() . '</small>';
1992 }
1993
1994 } else {
1995
1996 $cart_subtotal = wc_price( $this->subtotal );
1997
1998 if ( $this->tax_total > 0 && !$this->prices_include_tax ) {
1999 $cart_subtotal .= ' <small>' . WC()->countries->inc_tax_or_vat() . '</small>';
2000 }
2001
2002 }
2003 }
2004
2005 return apply_filters( 'woocommerce_cart_subtotal', $cart_subtotal, $compound, $this );
2006 }
2007
2008 2009 2010 2011 2012 2013
2014 public function get_product_price( $_product ) {
2015 if ( $this->tax_display_cart == 'excl' )
2016 $product_price = $_product->get_price_excluding_tax();
2017 else
2018 $product_price = $_product->get_price_including_tax();
2019
2020 return apply_filters( 'woocommerce_cart_product_price', wc_price( $product_price ), $_product );
2021 }
2022
2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033
2034 public function get_product_subtotal( $_product, $quantity ) {
2035
2036 $price = $_product->get_price();
2037 $taxable = $_product->is_taxable();
2038
2039
2040 if ( $taxable ) {
2041
2042 if ( $this->tax_display_cart == 'excl' ) {
2043
2044 $row_price = $_product->get_price_excluding_tax( $quantity );
2045 $product_subtotal = wc_price( $row_price );
2046
2047 if ( $this->prices_include_tax && $this->tax_total > 0 )
2048 $product_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
2049
2050 } else {
2051
2052 $row_price = $_product->get_price_including_tax( $quantity );
2053 $product_subtotal = wc_price( $row_price );
2054
2055 if ( ! $this->prices_include_tax && $this->tax_total > 0 )
2056 $product_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
2057
2058 }
2059
2060
2061 } else {
2062
2063 $row_price = $price * $quantity;
2064 $product_subtotal = wc_price( $row_price );
2065
2066 }
2067
2068 return apply_filters( 'woocommerce_cart_product_subtotal', $product_subtotal, $_product, $quantity, $this );
2069 }
2070
2071 2072 2073 2074 2075
2076 public function get_cart_tax() {
2077 $cart_total_tax = wc_round_tax_total( $this->tax_total + $this->shipping_tax_total );
2078
2079 return apply_filters( 'woocommerce_get_cart_tax', $cart_total_tax ? wc_price( $cart_total_tax ) : '' );
2080 }
2081
2082 2083 2084 2085 2086 2087 2088
2089 public function get_taxes_total( $compound = true, $display = true ) {
2090 $total = 0;
2091 foreach ( $this->taxes as $key => $tax ) {
2092 if ( ! $compound && $this->tax->is_compound( $key ) ) continue;
2093 $total += $tax;
2094 }
2095 foreach ( $this->shipping_taxes as $key => $tax ) {
2096 if ( ! $compound && $this->tax->is_compound( $key ) ) continue;
2097 $total += $tax;
2098 }
2099 if ( $display ) {
2100 $total = wc_round_tax_total( $total );
2101 }
2102 return apply_filters( 'woocommerce_cart_taxes_total', $total, $compound, $display, $this );
2103 }
2104
2105 2106 2107 2108 2109
2110 public function get_discounts_before_tax() {
2111 if ( $this->discount_cart ) {
2112 $discounts_before_tax = wc_price( $this->discount_cart );
2113 } else {
2114 $discounts_before_tax = false;
2115 }
2116 return apply_filters( 'woocommerce_cart_discounts_before_tax', $discounts_before_tax, $this );
2117 }
2118
2119 2120 2121 2122 2123
2124 public function get_discounts_after_tax() {
2125 if ( $this->discount_total ) {
2126 $discounts_after_tax = wc_price( $this->discount_total );
2127 } else {
2128 $discounts_after_tax = false;
2129 }
2130 return apply_filters( 'woocommerce_cart_discounts_after_tax', $discounts_after_tax, $this );
2131 }
2132
2133 2134 2135 2136 2137
2138 public function get_total_discount() {
2139 if ( $this->discount_total || $this->discount_cart ) {
2140 $total_discount = wc_price( $this->discount_total + $this->discount_cart );
2141 } else {
2142 $total_discount = false;
2143 }
2144 return apply_filters( 'woocommerce_cart_total_discount', $total_discount, $this );
2145 }
2146 }
2147