Merge branch 'master' of github.com:woothemes/woocommerce

This commit is contained in:
claudiosmweb 2014-11-14 23:15:16 -02:00
commit 03c16229e0
3 changed files with 419 additions and 385 deletions

View File

@ -916,7 +916,7 @@ class WC_Cart {
$cart_item_key = $cart_id;
// Add item after merging with $cart_item_data - hook to allow plugins to modify cart item
$this->cart_contents[$cart_item_key] = apply_filters( 'woocommerce_add_cart_item', array_merge( $cart_item_data, array(
$this->cart_contents[ $cart_item_key ] = apply_filters( 'woocommerce_add_cart_item', array_merge( $cart_item_data, array(
'product_id' => $product_id,
'variation_id' => $variation_id,
'variation' => $variation,
@ -978,6 +978,7 @@ class WC_Cart {
$this->$key = $default;
unset( WC()->session->$key );
}
do_action( 'woocommerce_cart_reset', $this );
}
/**
@ -986,6 +987,7 @@ class WC_Cart {
public function calculate_totals() {
$this->reset();
$this->coupons = $this->get_coupons();
do_action( 'woocommerce_before_calculate_totals', $this );
@ -1663,8 +1665,9 @@ class WC_Cart {
foreach ( $this->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code );
if ( $coupon->apply_before_tax() )
if ( $coupon->apply_before_tax() ) {
$coupons[ $code ] = $coupon;
}
}
}
}
@ -1674,8 +1677,9 @@ class WC_Cart {
foreach ( $this->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code );
if ( ! $coupon->apply_before_tax() )
if ( ! $coupon->apply_before_tax() ) {
$coupons[ $code ] = $coupon;
}
}
}
}
@ -1765,13 +1769,12 @@ class WC_Cart {
* @return float price
*/
public function get_discounted_price( $values, $price, $add_totals = false ) {
if ( ! $price )
if ( ! $price ) {
return $price;
}
if ( ! empty( $this->applied_coupons ) ) {
foreach ( $this->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code );
if ( ! empty( $this->coupons ) ) {
foreach ( $this->coupons as $code => $coupon ) {
if ( $coupon->apply_before_tax() && $coupon->is_valid() ) {
if ( $coupon->is_valid_for_product( $values['data'], $values ) || $coupon->is_valid_for_cart() ) {
@ -1799,10 +1802,8 @@ class WC_Cart {
public function apply_cart_discounts_after_tax() {
$pre_discount_total = round( $this->cart_contents_total + $this->tax_total + $this->shipping_tax_total + $this->shipping_total + $this->fee_total, $this->dp );
if ( $this->applied_coupons ) {
foreach ( $this->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code );
if ( $this->coupons ) {
foreach ( $this->coupons as $code => $coupon ) {
do_action( 'woocommerce_cart_discount_after_tax_' . $coupon->type, $coupon );
if ( $coupon->is_valid() && ! $coupon->apply_before_tax() && $coupon->is_valid_for_cart() ) {
@ -1824,10 +1825,8 @@ class WC_Cart {
* @param double $price
*/
public function apply_product_discounts_after_tax( $values, $price ) {
if ( ! empty( $this->applied_coupons ) ) {
foreach ( $this->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code );
if ( ! empty( $this->coupons ) ) {
foreach ( $this->coupons as $code => $coupon ) {
do_action( 'woocommerce_product_discount_after_tax_' . $coupon->type, $coupon, $values, $price );
if ( $coupon->is_valid() && ! $coupon->apply_before_tax() && $coupon->is_valid_for_product( $values['data'] ) ) {
@ -1848,8 +1847,9 @@ class WC_Cart {
* @param double $amount
*/
private function increase_coupon_discount_amount( $code, $amount ) {
if ( empty( $this->coupon_discount_amounts[ $code ] ) )
if ( empty( $this->coupon_discount_amounts[ $code ] ) ) {
$this->coupon_discount_amounts[ $code ] = 0;
}
$this->coupon_discount_amounts[ $code ] += $amount;
}
@ -1862,8 +1862,9 @@ class WC_Cart {
* @param integer $count
*/
private function increase_coupon_applied_count( $code, $count = 1 ) {
if ( empty( $this->coupon_applied_count[ $code ] ) )
if ( empty( $this->coupon_applied_count[ $code ] ) ) {
$this->coupon_applied_count[ $code ] = 0;
}
$this->coupon_applied_count[ $code ] += $count;
}

View File

@ -5,6 +5,7 @@
* The WooCommerce coupons class gets coupon data from storage and checks coupon validity
*
* @class WC_Coupon
* @version 2.3.0
* @package WooCommerce/Classes
* @category Class
* @author WooThemes
@ -31,73 +32,13 @@ class WC_Coupon {
const WC_COUPON_REMOVED = 201;
/** @public string Coupon code. */
public $code;
public $code = '';
/** @public int Coupon ID. */
public $id;
public $id = 0;
/** @public string Type of discount. */
public $type;
/** @public string Type of discount (alias). */
public $discount_type;
/** @public string Coupon amount. */
public $amount;
/** @public string "Yes" if for individual use. */
public $individual_use;
/** @public array Array of product IDs. */
public $product_ids;
/** @public int Coupon usage limit. */
public $usage_limit;
/** @public int Coupon usage limit per user. */
public $usage_limit_per_user;
/** @public int Coupon usage limit per item. */
public $limit_usage_to_x_items;
/** @public int Coupon usage count. */
public $usage_count;
/** @public string Expiry date. */
public $expiry_date;
/** @public string "yes" if applied before tax. */
public $apply_before_tax;
/** @public string "yes" if coupon grants free shipping. */
public $free_shipping;
/** @public array Array of category ids. */
public $product_categories;
/** @public array Array of category ids. */
public $exclude_product_categories;
/** @public string "yes" if coupon does NOT apply to items on sale. */
public $exclude_sale_items;
/** @public string Minimum cart amount. */
public $minimum_amount;
/** @public string Maximum cart amount. */
public $maximum_amount;
/** @public string Coupon owner's email. */
public $customer_email;
/** @public array Post meta. */
public $coupon_custom_fields;
/** @public string How much the coupon is worth. */
public $coupon_amount;
/** @public string Error message. */
public $error_message;
/** @public bool Coupon exists */
public $exists = false;
/**
* Coupon constructor. Loads coupon data.
@ -106,127 +47,181 @@ class WC_Coupon {
* @param mixed $code code of the coupon to load
*/
public function __construct( $code ) {
global $wpdb;
$this->exists = $this->get_coupon( $code );
}
/**
* __isset function.
*
* @param mixed $key
* @return bool
*/
public function __isset( $key ) {
if ( in_array( $key, array( 'coupon_custom_fields', 'type', 'amount' ) ) ) {
return true;
}
return false;
}
/**
* __get function.
*
* @param mixed $key
* @return mixed
*/
public function __get( $key ) {
// Get values or default if not set
if ( 'coupon_custom_fields' === $key ) {
$value = $this->id ? get_post_meta( $this->id ) : array();
} elseif ( 'type' === $key ) {
$value = $this->discount_type;
} elseif ( 'amount' === $key ) {
$value = $this->coupon_amount;
} else {
$value = '';
}
return $value;
}
/**
* Checks the coupon type.
*
* @param string $type Array or string of types
* @return bool
*/
public function is_type( $type ) {
return ( $this->discount_type == $type || ( is_array( $type ) && in_array( $this->discount_type, $type ) ) ) ? true : false;
}
/**
* Gets an coupon from the database.
*
* @param string $code
* @return bool
*/
private function get_coupon( $code ) {
$this->code = apply_filters( 'woocommerce_coupon_code', $code );
// Coupon data lets developers create coupons through code
$coupon_data = apply_filters( 'woocommerce_get_shop_coupon_data', false, $code );
if ( $coupon = apply_filters( 'woocommerce_get_shop_coupon_data', false, $code ) ) {
$this->populate( $coupon );
return true;
} elseif ( ( $this->id = $this->get_coupon_id_from_code( $code ) ) && $this->code === get_the_title( $this->id ) ) {
$this->populate();
return true;
}
if ( $coupon_data ) {
return false;
}
$this->id = absint( $coupon_data['id'] );
$this->type = esc_html( $coupon_data['type'] );
$this->amount = esc_html( $coupon_data['amount'] ? $coupon_data['amount'] : $coupon_data['coupon_amount'] ) ;
$this->coupon_amount = $this->amount;
$this->individual_use = esc_html( $coupon_data['individual_use'] );
$this->product_ids = is_array( $coupon_data['product_ids'] ) ? $coupon_data['product_ids'] : array();
$this->exclude_product_ids = is_array( $coupon_data['exclude_product_ids'] ) ? $coupon_data['exclude_product_ids'] : array();
$this->usage_limit = absint( $coupon_data['usage_limit'] );
$this->usage_limit_per_user = isset( $coupon_data['usage_limit_per_user'] ) ? absint( $coupon_data['usage_limit_per_user'] ) : 0;
$this->limit_usage_to_x_items = isset( $coupon_data['limit_usage_to_x_items'] ) ? absint( $coupon_data['limit_usage_to_x_items'] ) : '';
$this->usage_count = absint( $coupon_data['usage_count'] );
$this->expiry_date = esc_html( $coupon_data['expiry_date'] );
$this->apply_before_tax = esc_html( $coupon_data['apply_before_tax'] );
$this->free_shipping = esc_html( $coupon_data['free_shipping'] );
$this->product_categories = is_array( $coupon_data['product_categories'] ) ? $coupon_data['product_categories'] : array();
$this->exclude_product_categories = is_array( $coupon_data['exclude_product_categories'] ) ? $coupon_data['exclude_product_categories'] : array();
$this->exclude_sale_items = esc_html( $coupon_data['exclude_sale_items'] );
$this->minimum_amount = esc_html( $coupon_data['minimum_amount'] );
$this->maximum_amount = esc_html( $coupon_data['maximum_amount'] );
$this->customer_email = esc_html( $coupon_data['customer_email'] );
/**
* Get a coupon ID from it's code
* @param string $code
* @return int
*/
private function get_coupon_id_from_code( $code ) {
global $wpdb;
} else {
return absint( $wpdb->get_var( $wpdb->prepare( apply_filters( 'woocommerce_coupon_code_query', "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish'" ), $this->code ) ) );
}
$coupon_id = $wpdb->get_var( $wpdb->prepare( apply_filters( 'woocommerce_coupon_code_query', "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish'" ), $this->code ) );
/**
* Populates an order from the loaded post data.
*/
private function populate( $data = array() ) {
$defaults = array(
'discount_type' => 'fixed_cart',
'coupon_amount' => 0,
'individual_use' => 'no',
'product_ids' => array(),
'exclude_product_ids' => array(),
'usage_limit' => '',
'usage_limit_per_user' => '',
'limit_usage_to_x_items' => '',
'usage_count' => '',
'expiry_date' => '',
'apply_before_tax' => 'yes',
'free_shipping' => 'no',
'product_categories' => array(),
'exclude_product_categories' => array(),
'exclude_sale_items' => 'no',
'minimum_amount' => '',
'maximum_amount' => '',
'customer_email' => array()
);
if ( ! $coupon_id )
return;
foreach ( $defaults as $key => $value ) {
// Try to load from meta if an ID is present
if ( $this->id ) {
$this->$key = get_post_meta( $this->id, $key, true );
} else {
$this->$key = ! empty( $data[ $key ] ) ? wc_clean( $data[ $key ] ) : '';
$coupon = get_post( $coupon_id );
$this->post_title = apply_filters( 'woocommerce_coupon_code', $coupon->post_title );
// Backwards compat field names @deprecated
if ( 'coupon_amount' === $key ) {
$this->coupon_amount = ! empty( $data[ 'amount' ] ) ? wc_clean( $data[ 'amount' ] ) : $this->coupon_amount;
} elseif ( 'discount_type' === $key ) {
$this->discount_type = ! empty( $data[ 'type' ] ) ? wc_clean( $data[ 'type' ] ) : $this->discount_type;
}
}
if ( empty( $coupon ) || $this->code !== $this->post_title )
return;
$this->id = $coupon->ID;
$this->coupon_custom_fields = get_post_meta( $this->id );
$load_data = array(
'discount_type' => 'fixed_cart',
'coupon_amount' => 0,
'individual_use' => 'no',
'product_ids' => '',
'exclude_product_ids' => '',
'usage_limit' => '',
'usage_limit_per_user' => '',
'limit_usage_to_x_items' => '',
'usage_count' => '',
'expiry_date' => '',
'apply_before_tax' => 'yes',
'free_shipping' => 'no',
'product_categories' => array(),
'exclude_product_categories' => array(),
'exclude_sale_items' => 'no',
'minimum_amount' => '',
'maximum_amount' => '',
'customer_email' => array()
);
foreach ( $load_data as $key => $default )
$this->$key = isset( $this->coupon_custom_fields[ $key ][0] ) && $this->coupon_custom_fields[ $key ][0] !== '' ? $this->coupon_custom_fields[ $key ][0] : $default;
// Alias
$this->type = $this->discount_type;
$this->amount = $this->coupon_amount;
// Formatting
$this->product_ids = array_filter( array_map( 'trim', explode( ',', $this->product_ids ) ) );
$this->exclude_product_ids = array_filter( array_map( 'trim', explode( ',', $this->exclude_product_ids ) ) );
$this->expiry_date = $this->expiry_date ? strtotime( $this->expiry_date ) : '';
$this->product_categories = array_filter( array_map( 'trim', (array) maybe_unserialize( $this->product_categories ) ) );
$this->exclude_product_categories = array_filter( array_map( 'trim', (array) maybe_unserialize( $this->exclude_product_categories ) ) );
$this->customer_email = array_filter( array_map( 'trim', array_map( 'strtolower', (array) maybe_unserialize( $this->customer_email ) ) ) );
if ( empty( $this->$key ) ) {
$this->$key = $value;
} elseif ( in_array( $key, array( 'product_ids', 'exclude_product_ids', 'product_categories', 'exclude_product_categories', 'customer_email' ) ) ) {
$this->$key = $this->format_array( $this->$key );
} elseif ( in_array( $key, array( 'usage_limit', 'usage_limit_per_user', 'limit_usage_to_x_items', 'usage_count' ) ) ) {
$this->$key = absint( $this->$key );
} elseif( 'expiry_date' === $key ) {
$this->expiry_date = $this->expiry_date && ! is_numeric( $this->expiry_date ) ? strtotime( $this->expiry_date ) : $this->expiry_date;
}
}
do_action( 'woocommerce_coupon_loaded', $this );
}
/**
* Format loaded data as array
* @param string|array $array
* @return array
*/
public function format_array( $array ) {
if ( ! is_array( $array ) ) {
if ( is_serialized( $array ) ) {
$array = maybe_unserialize( $array );
} else {
$array = explode( ',', $array );
}
}
return array_filter( array_map( 'trim', array_map( 'strtolower', $array ) ) );
}
/**
* Check if coupon needs applying before tax.
*
* @access public
* @return bool
*/
public function apply_before_tax() {
return $this->apply_before_tax == 'yes' ? true : false;
return 'yes' === $this->apply_before_tax;
}
/**
* Check if a coupon enables free shipping.
*
* @access public
* @return bool
*/
public function enable_free_shipping() {
return $this->free_shipping == 'yes' ? true : false;
return 'yes' === $this->free_shipping;
}
/**
* Check if a coupon excludes sale items.
*
* @access public
* @return bool
*/
public function exclude_sale_items() {
return $this->exclude_sale_items == 'yes' ? true : false;
return 'yes' === $this->exclude_sale_items;
}
/**
* Increase usage count for current coupon.
*
@ -235,15 +230,15 @@ class WC_Coupon {
* @return void
*/
public function inc_usage_count( $used_by = '' ) {
$this->usage_count++;
update_post_meta( $this->id, 'usage_count', $this->usage_count );
if ( $this->id ) {
update_post_meta( $this->id, 'usage_count', ( $this->usage_count ++ ) );
if ( $used_by ) {
add_post_meta( $this->id, '_used_by', strtolower( $used_by ) );
if ( $used_by ) {
add_post_meta( $this->id, '_used_by', strtolower( $used_by ) );
}
}
}
/**
* Decrease usage count for current coupon.
*
@ -252,15 +247,16 @@ class WC_Coupon {
* @return void
*/
public function dcr_usage_count( $used_by = '' ) {
global $wpdb;
if ( $this->id ) {
global $wpdb;
$this->usage_count--;
update_post_meta( $this->id, 'usage_count', $this->usage_count );
update_post_meta( $this->id, 'usage_count', ( $this->usage_count -- ) );
// Delete 1 used by meta
$meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;", $used_by, $this->id ) );
if ( $meta_id ) {
delete_metadata_by_mid( 'post', $meta_id );
// Delete 1 used by meta
$meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1;", $used_by, $this->id ) );
if ( $meta_id ) {
delete_metadata_by_mid( 'post', $meta_id );
}
}
}
@ -275,190 +271,229 @@ class WC_Coupon {
}
/**
* is_valid function.
* Ensure coupon exists or throw exception
*/
private function validate_exists() {
if ( ! $this->exists ) {
throw new Exception( self::E_WC_COUPON_NOT_EXIST );
}
}
/**
* Ensure coupon usage limit is valid or throw exception
*/
private function validate_usage_limit() {
if ( $this->usage_limit > 0 && $this->usage_count >= $this->usage_limit ) {
throw new Exception( self::E_WC_COUPON_USAGE_LIMIT_REACHED );
}
}
/**
* Ensure coupon user usage limit is valid or throw exception
*
* Check if a coupon is valid. Return a reason code if invalid. Reason codes:
* Per user usage limit - check here if user is logged in (against user IDs)
* Checked again for emails later on in WC_Cart::check_customer_coupons()
*/
private function validate_user_usage_limit() {
if ( $this->usage_limit_per_user > 0 && is_user_logged_in() && $this->id ) {
$used_by = (array) get_post_meta( $this->id, '_used_by' );
$usage_count = sizeof( array_keys( $used_by, get_current_user_id() ) );
if ( $usage_count >= $this->usage_limit_per_user ) {
throw new Exception( self::E_WC_COUPON_USAGE_LIMIT_REACHED );
}
}
}
/**
* Ensure coupon date is valid or throw exception
*/
private function validate_expiry_date() {
if ( $this->expiry_date && current_time( 'timestamp' ) > $this->expiry_date ) {
throw new Exception( $error_code = self::E_WC_COUPON_EXPIRED );
}
}
/**
* Ensure coupon amount is valid or throw exception
*/
private function validate_minimum_amount() {
if ( $this->minimum_amount > 0 && $this->minimum_amount > WC()->cart->subtotal ) {
throw new Exception( self::E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET );
}
}
/**
* Ensure coupon amount is valid or throw exception
*/
private function validate_maximum_amount() {
if ( $this->maximum_amount > 0 && $this->maximum_amount < WC()->cart->subtotal ) {
throw new Exception( self::E_WC_COUPON_MAX_SPEND_LIMIT_MET );
}
}
/**
* Ensure coupon is valid for products in the cart is valid or throw exception
*/
private function validate_product_ids() {
if ( sizeof( $this->product_ids ) > 0 ) {
$valid_for_cart = false;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->product_ids ) || in_array( $cart_item['variation_id'], $this->product_ids ) || in_array( $cart_item['data']->get_parent(), $this->product_ids ) ) {
$valid_for_cart = true;
}
}
}
if ( ! $valid_for_cart ) {
throw new Exception( self::E_WC_COUPON_NOT_APPLICABLE );
}
}
}
/**
* Ensure coupon is valid for product categories in the cart is valid or throw exception
*/
private function validate_product_categories() {
if ( sizeof( $this->product_categories ) > 0 ) {
$valid_for_cart = false;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_cats = wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) );
if ( sizeof( array_intersect( $product_cats, $this->product_categories ) ) > 0 ) {
$valid_for_cart = true;
}
}
}
if ( ! $valid_for_cart ) {
throw new Exception( self::E_WC_COUPON_NOT_APPLICABLE );
}
}
}
/**
* Ensure coupon is valid for sale items in the cart is valid or throw exception
*/
private function validate_sale_items() {
if ( 'yes' === $this->exclude_sale_items && $this->is_type( array( 'fixed_product', 'percent_product' ) ) ) {
$valid_for_cart = false;
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( sizeof( array_intersect( array( absint( $cart_item['product_id'] ), absint( $cart_item['variation_id'] ), $cart_item['data']->get_parent() ), $product_ids_on_sale ) ) === 0 ) {
// not on sale
$valid_for_cart = true;
}
}
}
if ( ! $valid_for_cart ) {
throw new Exception( self::E_WC_COUPON_NOT_VALID_SALE_ITEMS );
}
}
}
/**
* Cart discounts cannot be added if non-eligble product is found in cart
*/
private function validate_cart_excluded_items() {
if ( ! $this->is_type( array( 'fixed_product', 'percent_product' ) ) ) {
$this->validate_cart_excluded_product_ids();
$this->validate_cart_excluded_product_categories();
$this->validate_cart_excluded_sale_items();
}
}
/**
* Exclude products from cart
*/
private function validate_cart_excluded_product_ids() {
// Exclude Products
if ( sizeof( $this->exclude_product_ids ) > 0 ) {
$valid_for_cart = true;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->exclude_product_ids ) || in_array( $cart_item['variation_id'], $this->exclude_product_ids ) || in_array( $cart_item['data']->get_parent(), $this->exclude_product_ids ) ) {
$valid_for_cart = false;
}
}
}
if ( ! $valid_for_cart ) {
throw new Exception( self::E_WC_COUPON_EXCLUDED_PRODUCTS );
}
}
}
/**
* Exclude categories from cart
*/
private function validate_cart_excluded_product_categories() {
if ( sizeof( $this->exclude_product_categories ) > 0 ) {
$valid_for_cart = true;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_cats = wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) );
if ( sizeof( array_intersect( $product_cats, $this->exclude_product_categories ) ) > 0 ) {
$valid_for_cart = false;
}
}
}
if ( ! $valid_for_cart ) {
throw new Exception( self::E_WC_COUPON_EXCLUDED_CATEGORIES );
}
}
}
/**
* Exclude sale items from cart
*/
private function validate_cart_excluded_sale_items() {
if ( $this->exclude_sale_items == 'yes' ) {
$valid_for_cart = true;
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids_on_sale, true ) || in_array( $cart_item['variation_id'], $product_ids_on_sale, true ) || in_array( $cart_item['data']->get_parent(), $product_ids_on_sale, true ) ) {
$valid_for_cart = false;
}
}
}
if ( ! $valid_for_cart ) {
throw new Exception( self::E_WC_COUPON_NOT_VALID_SALE_ITEMS );
}
}
}
/**
* Check if a coupon is valid.
*
* @access public
* @return boolean validity or a WP_Error if not valid
* @return boolean validity
*/
public function is_valid() {
try {
$this->validate_exists();
$this->validate_usage_limit();
$this->validate_user_usage_limit();
$this->validate_expiry_date();
$this->validate_minimum_amount();
$this->validate_maximum_amount();
$this->validate_product_ids();
$this->validate_product_categories();
$this->validate_sale_items();
$this->validate_cart_excluded_items();
$error_code = null;
$valid = true;
$error = false;
if ( $this->id ) {
// Usage Limit
if ( $this->usage_limit > 0 ) {
if ( $this->usage_count >= $this->usage_limit ) {
$valid = false;
$error_code = self::E_WC_COUPON_USAGE_LIMIT_REACHED;
}
if ( ! apply_filters( 'woocommerce_coupon_is_valid', true, $this ) ) {
throw new Exception( self::E_WC_COUPON_INVALID_FILTERED );
}
// Per user usage limit - check here if user is logged in (against user IDs)
// Checked again for emails later on in WC_Cart::check_customer_coupons()
if ( $this->usage_limit_per_user > 0 && is_user_logged_in() ) {
$used_by = (array) get_post_meta( $this->id, '_used_by' );
$usage_count = sizeof( array_keys( $used_by, get_current_user_id() ) );
if ( $usage_count >= $this->usage_limit_per_user ) {
$valid = false;
$error_code = self::E_WC_COUPON_USAGE_LIMIT_REACHED;
}
}
// Expired
if ( $this->expiry_date ) {
if ( current_time( 'timestamp' ) > $this->expiry_date ) {
$valid = false;
$error_code = self::E_WC_COUPON_EXPIRED;
}
}
// Minimum spend
if ( $this->minimum_amount > 0 ) {
if ( $this->minimum_amount > WC()->cart->subtotal ) {
$valid = false;
$error_code = self::E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET;
}
}
// Maximum spend
if ( $this->maximum_amount > 0 ) {
if ( $this->maximum_amount < WC()->cart->subtotal ) {
$valid = false;
$error_code = self::E_WC_COUPON_MAX_SPEND_LIMIT_MET;
}
}
// Product ids - If a product included is found in the cart then its valid
if ( sizeof( $this->product_ids ) > 0 ) {
$valid_for_cart = false;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->product_ids ) || in_array( $cart_item['variation_id'], $this->product_ids ) || in_array( $cart_item['data']->get_parent(), $this->product_ids ) )
$valid_for_cart = true;
}
}
if ( ! $valid_for_cart ) {
$valid = false;
$error_code = self::E_WC_COUPON_NOT_APPLICABLE;
}
}
// Category ids - If a product included is found in the cart then its valid
if ( sizeof( $this->product_categories ) > 0 ) {
$valid_for_cart = false;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_cats = wp_get_post_terms($cart_item['product_id'], 'product_cat', array("fields" => "ids"));
if ( sizeof( array_intersect( $product_cats, $this->product_categories ) ) > 0 )
$valid_for_cart = true;
}
}
if ( ! $valid_for_cart ) {
$valid = false;
$error_code = self::E_WC_COUPON_NOT_APPLICABLE;
}
}
// Exclude Sale Items check for product coupons - valid if a non-sale item is present
if ( 'yes' === $this->exclude_sale_items && in_array( $this->type, array( 'fixed_product', 'percent_product' ) ) ) {
$valid_for_cart = false;
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( sizeof( array_intersect( array( absint( $cart_item['product_id'] ), absint( $cart_item['variation_id'] ), $cart_item['data']->get_parent() ), $product_ids_on_sale ) ) === 0 ) {
// not on sale
$valid_for_cart = true;
}
}
}
if ( ! $valid_for_cart ) {
$valid = false;
$error_code = self::E_WC_COUPON_NOT_VALID_SALE_ITEMS;
}
}
// Cart discounts cannot be added if non-eligble product is found in cart
if ( $this->type != 'fixed_product' && $this->type != 'percent_product' ) {
// Exclude Products
if ( sizeof( $this->exclude_product_ids ) > 0 ) {
$valid_for_cart = true;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->exclude_product_ids ) || in_array( $cart_item['variation_id'], $this->exclude_product_ids ) || in_array( $cart_item['data']->get_parent(), $this->exclude_product_ids ) ) {
$valid_for_cart = false;
}
}
}
if ( ! $valid_for_cart ) {
$valid = false;
$error_code = self::E_WC_COUPON_EXCLUDED_PRODUCTS;
}
}
// Exclude Sale Items
if ( $this->exclude_sale_items == 'yes' ) {
$valid_for_cart = true;
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids_on_sale, true ) || in_array( $cart_item['variation_id'], $product_ids_on_sale, true ) || in_array( $cart_item['data']->get_parent(), $product_ids_on_sale, true ) ) {
$valid_for_cart = false;
}
}
}
if ( ! $valid_for_cart ) {
$valid = false;
$error_code = self::E_WC_COUPON_NOT_VALID_SALE_ITEMS;
}
}
// Exclude Categories
if ( sizeof( $this->exclude_product_categories ) > 0 ) {
$valid_for_cart = true;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_cats = wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) );
if ( sizeof( array_intersect( $product_cats, $this->exclude_product_categories ) ) > 0 ) {
$valid_for_cart = false;
}
}
}
if ( ! $valid_for_cart ) {
$valid = false;
$error_code = self::E_WC_COUPON_EXCLUDED_CATEGORIES;
}
}
}
$valid = apply_filters( 'woocommerce_coupon_is_valid', $valid, $this );
if ( $valid ) {
return true;
} else {
if ( is_null( $error_code ) )
$error_code = self::E_WC_COUPON_INVALID_FILTERED;
}
} else {
$error_code = self::E_WC_COUPON_NOT_EXIST;
} catch ( Exception $e ) {
$this->error_message = $this->get_coupon_error( $e->getMessage() );
return false;
}
if ( $error_code )
$this->error_message = $this->get_coupon_error( $error_code );
return false;
return true;
}
/**
@ -467,8 +502,7 @@ class WC_Coupon {
* @return bool
*/
public function is_valid_for_cart() {
$valid = $this->type != 'fixed_cart' && $this->type != 'percent' ? false : true;
return apply_filters( 'woocommerce_coupon_is_valid_for_cart', $valid, $this );
return apply_filters( 'woocommerce_coupon_is_valid_for_cart', $this->is_type( array( 'fixed_cart', 'percent' ) ), $this );
}
/**
@ -478,8 +512,9 @@ class WC_Coupon {
* @return boolean
*/
public function is_valid_for_product( $product, $values = array() ) {
if ( $this->type != 'fixed_product' && $this->type != 'percent_product' )
if ( ! $this->is_type( array( 'fixed_product', 'percent_product' ) ) ) {
return apply_filters( 'woocommerce_coupon_is_valid_for_product', false, $product, $this, $values );
}
$valid = false;
$product_cats = wp_get_post_terms( $product->id, 'product_cat', array( "fields" => "ids" ) );
@ -487,14 +522,16 @@ class WC_Coupon {
// Specific products get the discount
if ( sizeof( $this->product_ids ) > 0 ) {
if ( in_array( $product->id, $this->product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $this->product_ids ) ) || in_array( $product->get_parent(), $this->product_ids ) )
if ( in_array( $product->id, $this->product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $this->product_ids ) ) || in_array( $product->get_parent(), $this->product_ids ) ) {
$valid = true;
}
// Category discounts
} elseif ( sizeof( $this->product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $this->product_categories ) ) > 0 )
if ( sizeof( array_intersect( $product_cats, $this->product_categories ) ) > 0 ) {
$valid = true;
}
} else {
// No product ids - all items discounted
@ -502,21 +539,26 @@ class WC_Coupon {
}
// Specific product ID's excluded from the discount
if ( sizeof( $this->exclude_product_ids ) > 0 )
if ( in_array( $product->id, $this->exclude_product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $this->exclude_product_ids ) ) || in_array( $product->get_parent(), $this->exclude_product_ids ) )
if ( sizeof( $this->exclude_product_ids ) > 0 ) {
if ( in_array( $product->id, $this->exclude_product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $this->exclude_product_ids ) ) || in_array( $product->get_parent(), $this->exclude_product_ids ) ) {
$valid = false;
}
}
// Specific categories excluded from the discount
if ( sizeof( $this->exclude_product_categories ) > 0 )
if ( sizeof( array_intersect( $product_cats, $this->exclude_product_categories ) ) > 0 )
if ( sizeof( $this->exclude_product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $this->exclude_product_categories ) ) > 0 ) {
$valid = false;
}
}
// Sale Items excluded from discount
if ( $this->exclude_sale_items == 'yes' ) {
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( in_array( $product->id, $product_ids_on_sale, true ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $product_ids_on_sale, true ) ) || in_array( $product->get_parent(), $product_ids_on_sale, true ) )
if ( in_array( $product->id, $product_ids_on_sale, true ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $product_ids_on_sale, true ) ) || in_array( $product->get_parent(), $product_ids_on_sale, true ) ) {
$valid = false;
}
}
return apply_filters( 'woocommerce_coupon_is_valid_for_product', $valid, $product, $this, $values );
@ -533,9 +575,9 @@ class WC_Coupon {
public function get_discount_amount( $discounting_amount, $cart_item = null, $single = false ) {
$discount = 0;
if ( $this->type == 'fixed_product') {
if ( $this->is_type( 'fixed_product' ) ) {
$discount = $discounting_amount < $this->amount ? $discounting_amount : $this->amount;
$discount = $discounting_amount < $this->coupon_amount ? $discounting_amount : $this->coupon_amount;
// If dealing with a line and not a single item, we need to multiple fixed discount by cart item qty.
if ( ! $single && ! is_null( $cart_item ) ) {
@ -543,11 +585,11 @@ class WC_Coupon {
$discount = $discount * $cart_item['quantity'];
}
} elseif ( $this->type == 'percent_product' || $this->type == 'percent' ) {
} elseif ( $this->is_type( array( 'percent_product', 'percent' ) ) ) {
$discount = round( ( $discounting_amount / 100 ) * $this->amount, WC()->cart->dp );
$discount = round( ( $discounting_amount / 100 ) * $this->coupon_amount, WC()->cart->dp );
} elseif ( $this->type == 'fixed_cart' ) {
} elseif ( $this->is_type( 'fixed_cart' ) ) {
if ( ! is_null( $cart_item ) ) {
/**
* This is the most complex discount - we need to divide the discount between rows based on their price in
@ -562,15 +604,18 @@ class WC_Coupon {
$discount_percent = ( $cart_item['data']->get_price_excluding_tax() * $cart_item['quantity'] ) / WC()->cart->subtotal_ex_tax;
}
$discount = min( ( $this->amount * $discount_percent ) / $cart_item['quantity'], $discounting_amount );
$discount = min( ( $this->coupon_amount * $discount_percent ) / $cart_item['quantity'], $discounting_amount );
} else {
$discount = min( $this->amount, $discounting_amount );
$discount = min( $this->coupon_amount, $discounting_amount );
}
}
// Handle the limit_usage_to_x_items option
if ( in_array( $this->type, array( 'percent_product', 'fixed_product' ) ) && ! is_null( $cart_item ) ) {
$qty = empty( $this->limit_usage_to_x_items ) ? $cart_item['quantity'] : min( $this->limit_usage_to_x_items, $cart_item['quantity'] );
if ( $this->is_type( array( 'percent_product', 'fixed_product' ) ) && ! is_null( $cart_item ) ) {
$qty = '' === $this->limit_usage_to_x_items ? $cart_item['quantity'] : min( $this->limit_usage_to_x_items, $cart_item['quantity'] );
// Reduce limits
$this->limit_usage_to_x_items = max( 0, $this->limit_usage_to_x_items - $qty );
if ( $single ) {
$discount = ( $discount * $qty ) / $cart_item['quantity'];
@ -586,27 +631,24 @@ class WC_Coupon {
* Converts one of the WC_Coupon message/error codes to a message string and
* displays the message/error.
*
* @access public
* @param int $msg_code Message/error code.
* @return void
*/
public function add_coupon_message( $msg_code ) {
if ( $msg_code < 200 )
if ( $msg_code < 200 ) {
wc_add_notice( $this->get_coupon_error( $msg_code ), 'error' );
else
} else {
wc_add_notice( $this->get_coupon_message( $msg_code ) );
}
}
/**
* Map one of the WC_Coupon message codes to a message string
*
* @access public
* @param integer $msg_code
* @return string| Message/error string
*/
public function get_coupon_message( $msg_code ) {
switch ( $msg_code ) {
case self::WC_COUPON_SUCCESS :
$msg = __( 'Coupon code applied successfully.', 'woocommerce' );
@ -618,19 +660,16 @@ class WC_Coupon {
$msg = '';
break;
}
return apply_filters( 'woocommerce_coupon_message', $msg, $msg_code, $this );
}
/**
* Map one of the WC_Coupon error codes to a message string
*
* @access public
* @param int $err_code Message/error code.
* @return string| Message/error string
*/
public function get_coupon_error( $err_code ) {
switch ( $err_code ) {
case self::E_WC_COUPON_INVALID_FILTERED:
$err = __( 'Coupon is not valid.', 'woocommerce' );
@ -666,7 +705,6 @@ class WC_Coupon {
$err = __( 'Sorry, this coupon is not applicable to your cart contents.', 'woocommerce' );
break;
case self::E_WC_COUPON_EXCLUDED_PRODUCTS:
// Store excluded products that are in cart in $products
$products = array();
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
@ -680,7 +718,6 @@ class WC_Coupon {
$err = sprintf( __( 'Sorry, this coupon is not applicable to the products: %s.', 'woocommerce' ), implode( ', ', $products ) );
break;
case self::E_WC_COUPON_EXCLUDED_CATEGORIES:
// Store excluded categories that are in cart in $categories
$categories = array();
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
@ -709,7 +746,6 @@ class WC_Coupon {
$err = '';
break;
}
return apply_filters( 'woocommerce_coupon_error', $err, $err_code, $this );
}
@ -718,12 +754,10 @@ class WC_Coupon {
* No coupon instance will be available where a coupon does not exist,
* so this static method exists.
*
* @access public
* @param int $err_code Error code
* @return string| Error string
*/
public static function get_generic_coupon_error( $err_code ) {
switch ( $err_code ) {
case self::E_WC_COUPON_NOT_EXIST:
$err = __( 'Coupon does not exist!', 'woocommerce' );
@ -735,9 +769,7 @@ class WC_Coupon {
$err = '';
break;
}
// When using this static method, there is no $this to pass to filter
return apply_filters( 'woocommerce_coupon_error', $err, $err_code, null );
}
}

View File

@ -133,6 +133,7 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
= 2.3.0 =
* Feature - Made tax importer expand postcode ranges.
* Feature - Print styles for reports.
* Feature - Remove products from the cart in the widget.
* Refactor - Removed deprecated methods from WC_Frontend_Scripts and rewrote script registration and localization to run once.
* Refactor - Routing all email functionality through one send() method.
* Refactor - Replaced existing email css inliner with Emogrifier.