Coupon usage limits per user (using email + ID). Closes #3314
This commit is contained in:
parent
088f565b98
commit
c20a44c423
|
@ -145,6 +145,12 @@ class WC_Meta_Box_Coupon_Data {
|
|||
'min' => '0'
|
||||
) ) );
|
||||
|
||||
// Usage limit
|
||||
woocommerce_wp_text_input( array( 'id' => 'usage_limit_per_user', 'label' => __( 'Usage limit per user', 'woocommerce' ), 'placeholder' => _x( 'Unlimited usage', 'placeholder', 'woocommerce' ), 'description' => __( 'How many times this coupon can be used by an invidual user. Uses billing email for guests, and user ID for logged in users.', 'woocommerce' ), 'type' => 'number', 'custom_attributes' => array(
|
||||
'step' => '1',
|
||||
'min' => '0'
|
||||
) ) );
|
||||
|
||||
// Expiry date
|
||||
woocommerce_wp_text_input( array( 'id' => 'expiry_date', 'label' => __( 'Expiry date', 'woocommerce' ), 'placeholder' => _x('Never expire', 'placeholder', 'woocommerce'), 'description' => __( 'The date this coupon will expire, <code>YYYY-MM-DD</code>.', 'woocommerce' ), 'class' => 'short date-picker', 'custom_attributes' => array( 'pattern' => "[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" ) ) );
|
||||
|
||||
|
@ -180,16 +186,17 @@ class WC_Meta_Box_Coupon_Data {
|
|||
WC_Admin_Meta_Boxes::add_error( __( 'Coupon code already exists - customers will use the latest coupon with this code.', 'woocommerce' ) );
|
||||
|
||||
// Add/Replace data to array
|
||||
$type = woocommerce_clean( $_POST['discount_type'] );
|
||||
$amount = woocommerce_clean( $_POST['coupon_amount'] );
|
||||
$usage_limit = empty( $_POST['usage_limit'] ) ? '' : absint( $_POST['usage_limit'] );
|
||||
$individual_use = isset( $_POST['individual_use'] ) ? 'yes' : 'no';
|
||||
$expiry_date = woocommerce_clean( $_POST['expiry_date'] );
|
||||
$apply_before_tax = isset( $_POST['apply_before_tax'] ) ? 'yes' : 'no';
|
||||
$free_shipping = isset( $_POST['free_shipping'] ) ? 'yes' : 'no';
|
||||
$exclude_sale_items = isset( $_POST['exclude_sale_items'] ) ? 'yes' : 'no';
|
||||
$minimum_amount = woocommerce_clean( $_POST['minimum_amount'] );
|
||||
$customer_email = array_filter( array_map( 'trim', explode( ',', woocommerce_clean( $_POST['customer_email'] ) ) ) );
|
||||
$type = woocommerce_clean( $_POST['discount_type'] );
|
||||
$amount = woocommerce_clean( $_POST['coupon_amount'] );
|
||||
$usage_limit = empty( $_POST['usage_limit'] ) ? '' : absint( $_POST['usage_limit'] );
|
||||
$usage_limit_per_user = empty( $_POST['usage_limit_per_user'] ) ? '' : absint( $_POST['usage_limit_per_user'] );
|
||||
$individual_use = isset( $_POST['individual_use'] ) ? 'yes' : 'no';
|
||||
$expiry_date = woocommerce_clean( $_POST['expiry_date'] );
|
||||
$apply_before_tax = isset( $_POST['apply_before_tax'] ) ? 'yes' : 'no';
|
||||
$free_shipping = isset( $_POST['free_shipping'] ) ? 'yes' : 'no';
|
||||
$exclude_sale_items = isset( $_POST['exclude_sale_items'] ) ? 'yes' : 'no';
|
||||
$minimum_amount = woocommerce_clean( $_POST['minimum_amount'] );
|
||||
$customer_email = array_filter( array_map( 'trim', explode( ',', woocommerce_clean( $_POST['customer_email'] ) ) ) );
|
||||
|
||||
if ( isset( $_POST['product_ids'] ) ) {
|
||||
$product_ids = implode( ',', array_filter( array_map( 'intval', (array) $_POST['product_ids'] ) ) );
|
||||
|
@ -213,6 +220,7 @@ class WC_Meta_Box_Coupon_Data {
|
|||
update_post_meta( $post_id, 'product_ids', $product_ids );
|
||||
update_post_meta( $post_id, 'exclude_product_ids', $exclude_product_ids );
|
||||
update_post_meta( $post_id, 'usage_limit', $usage_limit );
|
||||
update_post_meta( $post_id, 'usage_limit_per_user', $usage_limit_per_user );
|
||||
update_post_meta( $post_id, 'expiry_date', $expiry_date );
|
||||
update_post_meta( $post_id, 'apply_before_tax', $apply_before_tax );
|
||||
update_post_meta( $post_id, 'free_shipping', $free_shipping );
|
||||
|
|
|
@ -393,6 +393,10 @@ class WC_Cart {
|
|||
/**
|
||||
* Check for user coupons (now that we have billing email). If a coupon is invalid, add an error.
|
||||
*
|
||||
* Checks two types of coupons:
|
||||
* 1. Where a list of customer emails are set (limits coupon usage to those defined)
|
||||
* 2. Where a usage_limit_per_user is set (limits coupon usage to a number based on user ID and email)
|
||||
*
|
||||
* @access public
|
||||
* @param array $posted
|
||||
*/
|
||||
|
@ -401,26 +405,55 @@ class WC_Cart {
|
|||
foreach ( $this->applied_coupons as $key => $code ) {
|
||||
$coupon = new WC_Coupon( $code );
|
||||
|
||||
if ( $coupon->is_valid() && is_array( $coupon->customer_email ) && sizeof( $coupon->customer_email ) > 0 ) {
|
||||
if ( $coupon->is_valid() ) {
|
||||
|
||||
$coupon->customer_email = array_map( 'sanitize_email', $coupon->customer_email );
|
||||
// Limit to defined email addresses
|
||||
if ( is_array( $coupon->customer_email ) && sizeof( $coupon->customer_email ) > 0 ) {
|
||||
$coupon->customer_email = array_map( 'sanitize_email', $coupon->customer_email );
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
$current_user = wp_get_current_user();
|
||||
$check_emails[] = $current_user->user_email;
|
||||
if ( is_user_logged_in() ) {
|
||||
$current_user = wp_get_current_user();
|
||||
$check_emails[] = $current_user->user_email;
|
||||
}
|
||||
$check_emails[] = $posted['billing_email'];
|
||||
$check_emails = array_map( 'sanitize_email', array_map( 'strtolower', $check_emails ) );
|
||||
|
||||
if ( 0 == sizeof( array_intersect( $check_emails, $coupon->customer_email ) ) ) {
|
||||
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED );
|
||||
|
||||
// Remove the coupon
|
||||
unset( $this->applied_coupons[ $key ] );
|
||||
|
||||
WC()->session->set( 'coupon_codes', $this->applied_coupons );
|
||||
WC()->session->set( 'refresh_totals', true );
|
||||
}
|
||||
}
|
||||
$check_emails[] = $posted['billing_email'];
|
||||
|
||||
$check_emails = array_map( 'sanitize_email', array_map( 'strtolower', $check_emails ) );
|
||||
// Usage limits per user - check against billing and user email and user ID
|
||||
if ( $coupon->usage_limit_per_user > 0 ) {
|
||||
$used_by = get_post_meta( $this->id, '_used_by' );
|
||||
|
||||
if ( 0 == sizeof( array_intersect( $check_emails, $coupon->customer_email ) ) ) {
|
||||
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED );
|
||||
if ( is_user_logged_in() ) {
|
||||
$current_user = wp_get_current_user();
|
||||
$check_emails[] = $current_user->user_email;
|
||||
}
|
||||
$check_emails[] = $posted['billing_email'];
|
||||
$check_emails = array_map( 'sanitize_email', array_map( 'strtolower', $check_emails ) );
|
||||
|
||||
// Remove the coupon
|
||||
unset( $this->applied_coupons[ $key ] );
|
||||
$usage_count = sizeof( array_keys( $used_by, get_current_user_id() ) );
|
||||
|
||||
WC()->session->set( 'coupon_codes', $this->applied_coupons );
|
||||
WC()->session->set( 'refresh_totals', true );
|
||||
foreach ( $check_emails as $check_email )
|
||||
$usage_count = $usage_count + sizeof( array_keys( $used_by, $check_email ) );
|
||||
|
||||
if ( $usage_count >= $coupon->usage_limit_per_user ) {
|
||||
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED );
|
||||
|
||||
// Remove the coupon
|
||||
unset( $this->applied_coupons[ $key ] );
|
||||
|
||||
WC()->session->set( 'coupon_codes', $this->applied_coupons );
|
||||
WC()->session->set( 'refresh_totals', true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,9 @@ class WC_Coupon {
|
|||
/** @public int Coupon usage limit. */
|
||||
public $usage_limit;
|
||||
|
||||
/** @public int Coupon usage limit per user. */
|
||||
public $usage_limit_per_user;
|
||||
|
||||
/** @public int Coupon usage count. */
|
||||
public $usage_count;
|
||||
|
||||
|
@ -104,22 +107,23 @@ class WC_Coupon {
|
|||
|
||||
if ( $coupon_data ) {
|
||||
|
||||
$this->id = absint( $coupon_data['id'] );
|
||||
$this->type = esc_html( $coupon_data['type'] );
|
||||
$this->amount = esc_html( $coupon_data['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_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->customer_email = esc_html( $coupon_data['customer_email'] );
|
||||
$this->id = absint( $coupon_data['id'] );
|
||||
$this->type = esc_html( $coupon_data['type'] );
|
||||
$this->amount = esc_html( $coupon_data['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 = absint( $coupon_data['usage_limit_per_user'] );
|
||||
$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->customer_email = esc_html( $coupon_data['customer_email'] );
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -138,21 +142,22 @@ class WC_Coupon {
|
|||
$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_count' => '',
|
||||
'expiry_date' => '',
|
||||
'apply_before_tax' => 'yes',
|
||||
'free_shipping' => 'no',
|
||||
'product_categories' => array(),
|
||||
'exclude_product_categories' => array(),
|
||||
'exclude_sale_items' => 'no',
|
||||
'minimum_amount' => '',
|
||||
'customer_email' => array()
|
||||
'discount_type' => 'fixed_cart',
|
||||
'coupon_amount' => 0,
|
||||
'individual_use' => 'no',
|
||||
'product_ids' => '',
|
||||
'exclude_product_ids' => '',
|
||||
'usage_limit' => '',
|
||||
'usage_limit_per_user' => '',
|
||||
'usage_count' => '',
|
||||
'expiry_date' => '',
|
||||
'apply_before_tax' => 'yes',
|
||||
'free_shipping' => 'no',
|
||||
'product_categories' => array(),
|
||||
'exclude_product_categories' => array(),
|
||||
'exclude_sale_items' => 'no',
|
||||
'minimum_amount' => '',
|
||||
'customer_email' => array()
|
||||
);
|
||||
|
||||
foreach ( $load_data as $key => $default )
|
||||
|
@ -213,11 +218,15 @@ class WC_Coupon {
|
|||
* Increase usage count fo current coupon.
|
||||
*
|
||||
* @access public
|
||||
* @param string $used_by Either user ID or billing email
|
||||
* @return void
|
||||
*/
|
||||
public function inc_usage_count() {
|
||||
public function inc_usage_count( $used_by = '' ) {
|
||||
$this->usage_count++;
|
||||
update_post_meta( $this->id, 'usage_count', $this->usage_count );
|
||||
|
||||
if ( $used_by )
|
||||
add_post_meta( $this->id, '_used_by', strtolower( $used_by ) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -225,11 +234,19 @@ class WC_Coupon {
|
|||
* Decrease usage count fo current coupon.
|
||||
*
|
||||
* @access public
|
||||
* @param string $used_by Either user ID or billing email
|
||||
* @return void
|
||||
*/
|
||||
public function dcr_usage_count() {
|
||||
public function dcr_usage_count( $used_by = '' ) {
|
||||
global $wpdb;
|
||||
|
||||
$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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,6 +284,18 @@ class WC_Coupon {
|
|||
}
|
||||
}
|
||||
|
||||
// Per user usage limit - check here if user is logged in (aginst 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 = 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 ) {
|
||||
|
|
|
@ -1364,7 +1364,12 @@ class WC_Order {
|
|||
continue;
|
||||
|
||||
$coupon = new WC_Coupon( $code );
|
||||
$coupon->inc_usage_count();
|
||||
|
||||
$used_by = $this->user_id;
|
||||
if ( ! $used_by )
|
||||
$used_by = $this->billing_email;
|
||||
|
||||
$coupon->inc_usage_count( $used_by );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1390,7 +1395,12 @@ class WC_Order {
|
|||
continue;
|
||||
|
||||
$coupon = new WC_Coupon( $code );
|
||||
$coupon->dcr_usage_count();
|
||||
|
||||
$used_by = $this->user_id;
|
||||
if ( ! $used_by )
|
||||
$used_by = $this->billing_email;
|
||||
|
||||
$coupon->dcr_usage_count( $used_by );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,6 +185,7 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
|
|||
* Feature - woocommerce_get_featured_product_ids function.
|
||||
* Feature - WOOCOMMERCE_DELIMITER to customise the pipes for attributes
|
||||
* Feature - Standardized, default credit card form for gateways to use if they support 'default_credit_card_form'.
|
||||
* Feature - Coupon usage limits per user (using email + ID).
|
||||
* Tweak - Added pagination to tax rate screens.
|
||||
* Tweak - Added filter to check the 'Create account' checkbox on checkout by default.
|
||||
* Tweak - Update CPT parameters for 'product_variation' and 'shop_coupon' to be no longer public.
|
||||
|
|
Loading…
Reference in New Issue