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