1911 lines
47 KiB
PHP
1911 lines
47 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Discount Object
|
||
|
*
|
||
|
* @package EDD
|
||
|
* @subpackage Discounts
|
||
|
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||
|
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||
|
* @since 2.7
|
||
|
*/
|
||
|
|
||
|
use EDD\Database\Rows\Adjustment;
|
||
|
|
||
|
// Exit if accessed directly
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* EDD_Discount Class
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @since 3.0 Extends EDD\Database\Rows\Adjustment instead of EDD_DB_Discount
|
||
|
*
|
||
|
* @property int $id
|
||
|
* @property string $name
|
||
|
* @property string $code
|
||
|
* @property string $status
|
||
|
* @property string $amount_type
|
||
|
* @property float $amount
|
||
|
* @property array $product_reqs
|
||
|
* @property string $scope
|
||
|
* @property array $excluded_products
|
||
|
* @property string $product_condition
|
||
|
* @property string $date_created
|
||
|
* @property string $date_modified
|
||
|
* @property string $start_date
|
||
|
* @property string $end_date
|
||
|
* @property int $use_count
|
||
|
* @property int $max_uses
|
||
|
* @property float $min_charge_amount
|
||
|
* @property bool $once_per_customer
|
||
|
*/
|
||
|
class EDD_Discount extends Adjustment {
|
||
|
|
||
|
/**
|
||
|
* Flat discount.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @var string
|
||
|
*/
|
||
|
const FLAT = 'flat';
|
||
|
|
||
|
/**
|
||
|
* Percent discount.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @var string
|
||
|
*/
|
||
|
const PERCENT = 'percent';
|
||
|
|
||
|
/**
|
||
|
* Discount ID.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var int
|
||
|
*/
|
||
|
protected $id = 0;
|
||
|
|
||
|
/**
|
||
|
* Discount Name.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $name = null;
|
||
|
|
||
|
/**
|
||
|
* Discount Code.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $code = null;
|
||
|
|
||
|
/**
|
||
|
* Discount Status (Active or Inactive).
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $status = null;
|
||
|
|
||
|
/**
|
||
|
* Discount Type (Percentage or Flat Amount).
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $amount_type = null;
|
||
|
|
||
|
/**
|
||
|
* Discount Amount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var mixed float|int
|
||
|
*/
|
||
|
protected $amount = null;
|
||
|
|
||
|
/**
|
||
|
* Download Requirements.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $product_reqs = array();
|
||
|
|
||
|
/**
|
||
|
* Scope of the discount.
|
||
|
*
|
||
|
* global - Applies to all products in the cart, save for those explicitly excluded through excluded_products
|
||
|
* not_global - Applies only to the products set in product_reqs
|
||
|
*
|
||
|
* This used to be called "is_not_global" but was changed to "scope" in 3.0.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $scope = null;
|
||
|
|
||
|
/**
|
||
|
* Excluded Downloads.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $excluded_products = array();
|
||
|
|
||
|
/**
|
||
|
* Product Condition
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $product_condition = null;
|
||
|
|
||
|
/**
|
||
|
* Created Date.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $date_created = null;
|
||
|
|
||
|
/**
|
||
|
* Modified Date.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $date_modified = null;
|
||
|
|
||
|
/**
|
||
|
* Start Date.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $start_date = null;
|
||
|
|
||
|
/**
|
||
|
* End Date.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $end_date = null;
|
||
|
|
||
|
/**
|
||
|
* Uses.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var int
|
||
|
*/
|
||
|
protected $use_count = null;
|
||
|
|
||
|
/**
|
||
|
* Maximum Uses.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var int
|
||
|
*/
|
||
|
protected $max_uses = null;
|
||
|
|
||
|
/**
|
||
|
* Minimum Amount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var mixed int|float
|
||
|
*/
|
||
|
protected $min_charge_amount = null;
|
||
|
|
||
|
/**
|
||
|
* Is Single Use per customer?
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
* @var bool
|
||
|
*/
|
||
|
protected $once_per_customer = null;
|
||
|
|
||
|
/**
|
||
|
* Constructor.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access protected
|
||
|
*
|
||
|
* @param mixed int|string $_id_or_code_or_name Discount id/code/name.
|
||
|
* @param bool $by_code Whether identifier passed was a discount code.
|
||
|
* @param bool $by_name Whether identifier passed was a discount name.
|
||
|
*/
|
||
|
public function __construct( $_id_or_code_or_name = false, $by_code = false, $by_name = false ) {
|
||
|
|
||
|
// Bail if no id or code
|
||
|
if ( empty( $_id_or_code_or_name ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Already an object
|
||
|
if ( is_object( $_id_or_code_or_name ) ) {
|
||
|
$discount = $_id_or_code_or_name;
|
||
|
|
||
|
// Code
|
||
|
} elseif ( $by_code ) {
|
||
|
$discount = $this->find_by_code( $_id_or_code_or_name );
|
||
|
|
||
|
// Name
|
||
|
} elseif ( $by_name ) {
|
||
|
$discount = $this->find_by_name( $_id_or_code_or_name );
|
||
|
|
||
|
// Default to ID
|
||
|
} else {
|
||
|
$discount = edd_get_discount( absint( $_id_or_code_or_name ) );
|
||
|
}
|
||
|
|
||
|
// Setup or bail
|
||
|
if ( ! empty( $discount ) ) {
|
||
|
$this->setup_discount( $discount );
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Magic __get method to dispatch a call to retrieve a protected property.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param mixed $key
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function __get( $key = '' ) {
|
||
|
$key = sanitize_key( $key );
|
||
|
|
||
|
// Back compat for ID
|
||
|
if ( 'discount_id' === $key || 'ID' === $key ) {
|
||
|
return (int) $this->id;
|
||
|
|
||
|
// Method
|
||
|
} elseif ( method_exists( $this, "get_{$key}" ) ) {
|
||
|
return call_user_func( array( $this, "get_{$key}" ) );
|
||
|
|
||
|
// Property
|
||
|
} elseif ( property_exists( $this, $key ) ) {
|
||
|
return $this->{$key};
|
||
|
|
||
|
// Other...
|
||
|
} else {
|
||
|
|
||
|
// Account for old property keys from pre 3.0
|
||
|
switch ( $key ) {
|
||
|
case 'post_author':
|
||
|
break;
|
||
|
|
||
|
case 'post_date':
|
||
|
case 'post_date_gmt':
|
||
|
return $this->date_created;
|
||
|
|
||
|
case 'post_modified':
|
||
|
case 'post_modified_gmt':
|
||
|
return $this->date_modified;
|
||
|
|
||
|
case 'post_content':
|
||
|
case 'post_title':
|
||
|
return $this->name;
|
||
|
|
||
|
case 'post_excerpt':
|
||
|
case 'post_status':
|
||
|
return $this->status;
|
||
|
|
||
|
case 'comment_status':
|
||
|
case 'ping_status':
|
||
|
case 'post_password':
|
||
|
case 'post_name':
|
||
|
case 'to_ping':
|
||
|
case 'pinged':
|
||
|
case 'post_modified':
|
||
|
case 'post_modified_gmt':
|
||
|
case 'post_content_filtered':
|
||
|
case 'post_parent':
|
||
|
case 'guid':
|
||
|
case 'menu_order':
|
||
|
case 'post_mime_type':
|
||
|
case 'comment_count':
|
||
|
case 'filter':
|
||
|
return '';
|
||
|
|
||
|
case 'post_type':
|
||
|
return 'edd_discount';
|
||
|
|
||
|
case 'expiration':
|
||
|
return $this->get_expiration();
|
||
|
|
||
|
case 'start':
|
||
|
return $this->start_date;
|
||
|
|
||
|
case 'min_price':
|
||
|
return $this->min_charge_amount;
|
||
|
|
||
|
case 'use_once':
|
||
|
case 'is_single_use':
|
||
|
case 'once_per_customer':
|
||
|
return $this->get_is_single_use();
|
||
|
|
||
|
case 'uses':
|
||
|
return $this->use_count;
|
||
|
|
||
|
case 'not_global':
|
||
|
case 'is_not_global':
|
||
|
return 'global' === $this->scope ? false : true;
|
||
|
}
|
||
|
|
||
|
return new WP_Error( 'edd-discount-invalid-property', sprintf( __( 'Can\'t get property %s', 'easy-digital-downloads' ), $key ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Magic __set method to dispatch a call to update a protected property.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @see set()
|
||
|
*
|
||
|
* @param string $key Property name.
|
||
|
* @param mixed $value Property value.
|
||
|
*
|
||
|
* @return mixed Value of setter being dispatched to.
|
||
|
*/
|
||
|
public function __set( $key, $value ) {
|
||
|
$key = sanitize_key( $key );
|
||
|
|
||
|
// Only real properties can be saved.
|
||
|
$keys = array_keys( get_class_vars( get_called_class() ) );
|
||
|
$old_keys = array(
|
||
|
'is_single_use',
|
||
|
'uses',
|
||
|
'expiration',
|
||
|
'start',
|
||
|
'min_price',
|
||
|
'use_once',
|
||
|
'is_not_global',
|
||
|
);
|
||
|
|
||
|
if ( ! in_array( $key, $keys, true ) && ! in_array( $key, $old_keys, true ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Dispatch to setter method if value needs to be sanitized
|
||
|
if ( method_exists( $this, 'set_' . $key ) ) {
|
||
|
return call_user_func( array( $this, 'set_' . $key ), $key, $value );
|
||
|
} elseif ( in_array( $key, $old_keys, true ) ) {
|
||
|
switch ( $key ) {
|
||
|
case 'expiration':
|
||
|
$this->end_date = $value;
|
||
|
break;
|
||
|
case 'start':
|
||
|
$this->start_date = $value;
|
||
|
break;
|
||
|
case 'min_price':
|
||
|
$this->min_charge_amount = $value;
|
||
|
break;
|
||
|
case 'use_once':
|
||
|
case 'is_single_use':
|
||
|
$this->once_per_customer = $value;
|
||
|
break;
|
||
|
case 'uses':
|
||
|
$this->use_count = $value;
|
||
|
break;
|
||
|
case 'not_global':
|
||
|
case 'is_not_global':
|
||
|
$this->scope = $value ? 'not_global' : 'global';
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
$this->{$key} = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle method dispatch dynamically.
|
||
|
*
|
||
|
* @param string $method Method name.
|
||
|
* @param array $args Arguments to be passed to method.
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function __call( $method, $args ) {
|
||
|
$property = strtolower( str_replace( array( 'setup_', 'get_' ), '', $method ) );
|
||
|
if ( ! method_exists( $this, $method ) && property_exists( $this, $property ) ) {
|
||
|
return $this->{$property};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Magic __toString method.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*/
|
||
|
public function __toString() {
|
||
|
return $this->code;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts the instance of the EDD_Discount object into an array for special cases.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return array EDD_Discount object as an array.
|
||
|
*/
|
||
|
public function array_convert() {
|
||
|
return get_object_vars( $this );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find a discount in the database with the code supplied.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access private
|
||
|
*
|
||
|
* @param string $code Discount code.
|
||
|
* @return object WP_Post instance of the discount.
|
||
|
*/
|
||
|
private function find_by_code( $code = '' ) {
|
||
|
return edd_get_discount_by( 'code', $code );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find a discount in the database with the name supplied.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access private
|
||
|
*
|
||
|
* @param string $name Discount name.
|
||
|
* @return object WP_Post instance of the discount.
|
||
|
*/
|
||
|
private function find_by_name( $name = '' ) {
|
||
|
return edd_get_discount_by( 'name', $name );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Setup object vars with discount WP_Post object.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access private
|
||
|
*
|
||
|
* @param object $discount WP_Post instance of the discount.
|
||
|
* @return bool Object initialization successful or not.
|
||
|
*/
|
||
|
private function setup_discount( $discount = null ) {
|
||
|
if ( is_null( $discount ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( ! is_object( $discount ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( is_wp_error( $discount ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fires before the instance of the EDD_Discount object is set up.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param object EDD_Discount EDD_Discount instance of the discount object.
|
||
|
* @param object WP_Post $discount WP_Post instance of the discount object.
|
||
|
*/
|
||
|
do_action( 'edd_pre_setup_discount', $this, $discount );
|
||
|
|
||
|
$vars = get_object_vars( $discount );
|
||
|
|
||
|
foreach ( $vars as $key => $value ) {
|
||
|
switch ( $key ) {
|
||
|
case 'start_date':
|
||
|
case 'end_date':
|
||
|
if ( '0000-00-00 00:00:00' === $value || is_null( $value ) ) {
|
||
|
$this->{$key} = false;
|
||
|
break;
|
||
|
}
|
||
|
case 'notes':
|
||
|
if ( ! empty( $value ) ) {
|
||
|
$this->{$key} = $value;
|
||
|
}
|
||
|
break;
|
||
|
case 'id':
|
||
|
$this->{$key} = (int) $value;
|
||
|
break;
|
||
|
case 'min_charge_amount':
|
||
|
$this->min_charge_amount = $value;
|
||
|
break;
|
||
|
default:
|
||
|
if ( is_string( $value ) ) {
|
||
|
@json_decode( $value );
|
||
|
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||
|
$this->{$key} = json_decode( $value );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->{$key} = $value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Some object vars need to be setup manually as the values need to be
|
||
|
* pulled in from the `edd_adjustmentmeta` table.
|
||
|
*/
|
||
|
$this->excluded_products = (array) edd_get_adjustment_meta( $this->id, 'excluded_product', false );
|
||
|
$this->product_reqs = (array) edd_get_adjustment_meta( $this->id, 'product_requirement', false );
|
||
|
$this->product_condition = (string) edd_get_adjustment_meta( $this->id, 'product_condition', true );
|
||
|
|
||
|
/**
|
||
|
* Fires after the instance of the EDD_Discount object is set up. Allows extensions to add items to this object via hook.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param object EDD_Discount EDD_Discount instance of the discount object.
|
||
|
* @param object WP_Post $discount WP_Post instance of the discount object.
|
||
|
*/
|
||
|
do_action( 'edd_setup_discount', $this, $discount );
|
||
|
|
||
|
if ( ! empty( $this->id ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Helper method to retrieve meta data associated with the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $key Meta key.
|
||
|
* @param bool $single Return single item or array.
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function get_meta( $key = '', $single = true ) {
|
||
|
return edd_get_adjustment_meta( $this->id, $key, $single );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Helper method to update meta data associated with the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $key Meta key to update.
|
||
|
* @param string $value New meta value to set.
|
||
|
* @param string $prev_value Optional. Previous meta value.
|
||
|
*
|
||
|
* @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
|
||
|
*/
|
||
|
public function update_meta( $key, $value = '', $prev_value = '' ) {
|
||
|
$filter_key = '_edd_discount_' . $key;
|
||
|
|
||
|
/**
|
||
|
* Filters the meta value being updated.
|
||
|
* The key is prefixed with `_edd_discount_` for 2.9 backwards compatibility.
|
||
|
*
|
||
|
* @param mixed $value Value being set.
|
||
|
* @param int $id Discount ID.
|
||
|
*/
|
||
|
$value = apply_filters( 'edd_update_discount_meta_' . $filter_key, $value, $this->id );
|
||
|
|
||
|
return edd_update_adjustment_meta( $this->id, $key, $value, $prev_value );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the code used to apply the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string Discount code.
|
||
|
*/
|
||
|
public function get_code() {
|
||
|
/**
|
||
|
* Filters the discount code.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $code Discount code.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_code', $this->code, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the status of the discount
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string Discount code status (active/inactive).
|
||
|
*/
|
||
|
public function get_status() {
|
||
|
/**
|
||
|
* Filters the discount status.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $code Discount status (active or inactive).
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_status', $this->status, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the status label of the discount.
|
||
|
*
|
||
|
* This method exists as a helper, until legitimate Status classes can be
|
||
|
* registered that will contain an array of status-specific labels.
|
||
|
*
|
||
|
* @since 2.9
|
||
|
*
|
||
|
* @return string Status label for the current discount.
|
||
|
*/
|
||
|
public function get_status_label() {
|
||
|
|
||
|
// Default label
|
||
|
$label = ucwords( $this->status );
|
||
|
|
||
|
// Specific labels
|
||
|
switch ( $this->status ) {
|
||
|
case '':
|
||
|
$label = __( 'None', 'easy-digital-downloads' );
|
||
|
break;
|
||
|
case 'draft':
|
||
|
$label = __( 'Draft', 'easy-digital-downloads' );
|
||
|
break;
|
||
|
case 'expired':
|
||
|
$label = __( 'Expired', 'easy-digital-downloads' );
|
||
|
break;
|
||
|
case 'inactive':
|
||
|
$label = __( 'Inactive', 'easy-digital-downloads' );
|
||
|
break;
|
||
|
case 'active':
|
||
|
$label = __( 'Active', 'easy-digital-downloads' );
|
||
|
break;
|
||
|
case 'inherit':
|
||
|
if ( ! empty( $this->parent ) ) {
|
||
|
$parent = edd_get_discount( $this->parent );
|
||
|
$label = $parent->get_status_label();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters the discount status.
|
||
|
*
|
||
|
* @since 2.9
|
||
|
*
|
||
|
* @param string $label Discount status label.
|
||
|
* @param string $status Discount status (active or inactive).
|
||
|
* @param int $id Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_status_label', $label, $this->status, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the type of discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string Discount type (percent or flat amount).
|
||
|
*/
|
||
|
public function get_type() {
|
||
|
/**
|
||
|
* Filters the discount type.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $code Discount type (percent or flat amount).
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_type', $this->amount_type, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount amount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return mixed float Discount amount.
|
||
|
*/
|
||
|
public function get_amount() {
|
||
|
/**
|
||
|
* Filters the discount amount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param float $amount Discount amount.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (float) apply_filters( 'edd_get_discount_amount', $this->amount, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount requirements for the discount to be satisfied.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return array IDs of required downloads.
|
||
|
*/
|
||
|
public function get_product_reqs() {
|
||
|
|
||
|
/**
|
||
|
* Filters the download requirements.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $product_reqs IDs of required products.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (array) apply_filters( 'edd_get_discount_product_reqs', $this->product_reqs, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount scope.
|
||
|
*
|
||
|
* This used to be called "is_not_global". That filter is still here for backwards compatibility.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @return string The scope, i.e. "global".
|
||
|
*/
|
||
|
public function get_scope() {
|
||
|
$legacy_value = apply_filters( 'edd_discount_is_not_global', null, $this->id );
|
||
|
|
||
|
if ( ! is_null( $legacy_value ) ) {
|
||
|
$this->scope = $legacy_value ? 'global' : 'not_global';
|
||
|
}
|
||
|
|
||
|
return apply_filters( 'edd_get_discount_scope', $this->scope, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the product condition.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string Product condition
|
||
|
*/
|
||
|
public function get_product_condition() {
|
||
|
/**
|
||
|
* Filters the product condition.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $product_condition Product condition.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_discount_product_condition', $this->product_condition, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the downloads that are excluded from having this discount code applied.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return array IDs of excluded downloads.
|
||
|
*/
|
||
|
public function get_excluded_products() {
|
||
|
/**
|
||
|
* Filters the excluded downloads.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $excluded_products IDs of excluded products.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (array) apply_filters( 'edd_get_discount_excluded_products', $this->excluded_products, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the start date.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string Start date.
|
||
|
*/
|
||
|
public function get_start_date() {
|
||
|
/**
|
||
|
* Filters the start date.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $start Discount start date.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_start', $this->start_date, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the end date.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string End date.
|
||
|
*/
|
||
|
public function get_expiration() {
|
||
|
/**
|
||
|
* Filters the end date.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $expiration Discount expiration date.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_expiration', $this->end_date, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the uses for the discount code.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return int Uses.
|
||
|
*/
|
||
|
public function get_uses() {
|
||
|
/**
|
||
|
* Filters the maximum uses.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param int $max_uses Maximum uses.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (int) apply_filters( 'edd_get_discount_uses', $this->use_count, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the maximum uses for the discount code.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return int Maximum uses.
|
||
|
*/
|
||
|
public function get_max_uses() {
|
||
|
/**
|
||
|
* Filters the maximum uses.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param int $max_uses Maximum uses.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (int) apply_filters( 'edd_get_discount_max_uses', $this->max_uses, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the minimum spend required for the discount to be satisfied.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return mixed float Minimum spend.
|
||
|
*/
|
||
|
public function get_min_price() {
|
||
|
/**
|
||
|
* Filters the minimum price.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param float $min_price Minimum price.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (float) apply_filters( 'edd_get_discount_min_price', $this->min_charge_amount, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the usage limit per limit (if the discount can only be used once per customer).
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return bool Once use per customer?
|
||
|
*/
|
||
|
public function get_is_single_use() {
|
||
|
return $this->get_once_per_customer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the usage limit per limit (if the discount can only be used once per customer).
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @return bool Once use per customer?
|
||
|
*/
|
||
|
public function get_once_per_customer() {
|
||
|
/**
|
||
|
* Filters the single use meta value.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $is_single_use Is the discount only allowed to be used once per customer.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (bool) apply_filters( 'edd_is_discount_single_use', $this->once_per_customer, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a discount exists.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return bool Discount exists.
|
||
|
*/
|
||
|
public function exists() {
|
||
|
if ( ! $this->id > 0 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Once object variables has been set, an update is needed to persist them to the database.
|
||
|
*
|
||
|
* This is now simply a wrapper to the add() method which handles creating new discounts and updating existing ones.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return bool True if the save was successful, false if it failed or wasn't needed.
|
||
|
*/
|
||
|
public function save() {
|
||
|
$args = get_object_vars( $this );
|
||
|
$saved = $this->add( $args );
|
||
|
|
||
|
return $saved;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new discount. If the discount already exists in the database, update it.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount details.
|
||
|
* @return mixed bool|int false if data isn't passed and class not instantiated for creation, or post ID for the new discount.
|
||
|
*/
|
||
|
public function add( $args = array() ) {
|
||
|
|
||
|
// If no code is provided, return early with false
|
||
|
if ( empty( $args['code'] ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( ! empty( $this->id ) && $this->exists() ) {
|
||
|
return $this->update( $args );
|
||
|
|
||
|
} else {
|
||
|
$args = self::convert_legacy_args( $args );
|
||
|
|
||
|
if ( ! empty( $args['start_date'] ) ) {
|
||
|
$args['start_date'] = date( 'Y-m-d H:i:s', strtotime( $args['start_date'], current_time( 'timestamp' ) ) );
|
||
|
}
|
||
|
|
||
|
if ( ! empty( $args['end_date'] ) ) {
|
||
|
$args['end_date'] = date( 'Y-m-d H:i:s', strtotime( $args['end_date'], current_time( 'timestamp' ) ) );
|
||
|
|
||
|
if ( strtotime( $args['end_date'], current_time( 'timestamp' ) ) < current_time( 'timestamp' ) ) {
|
||
|
$args['status'] = 'expired';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! empty( $args['start_date'] ) && ! empty( $args['end_date'] ) ) {
|
||
|
$start_timestamp = strtotime( $args['start_date'], current_time( 'timestamp' ) );
|
||
|
$end_timestamp = strtotime( $args['end_date'], current_time( 'timestamp' ) );
|
||
|
|
||
|
if ( $start_timestamp > $end_timestamp ) {
|
||
|
// Set the expiration date to the start date if start is later than expiration
|
||
|
$args['end_date'] = $args['start_date'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Assume discount status is "active" if it has not been set
|
||
|
if ( ! isset( $args['status'] ) ) {
|
||
|
$args['status'] = 'active';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a new discount to the database.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Filters the args before being inserted into the database.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount args.
|
||
|
*/
|
||
|
$args = apply_filters( 'edd_insert_discount', $args );
|
||
|
|
||
|
/**
|
||
|
* Filters the args before being inserted into the database (kept for backwards compatibility purposes)
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @since 3.0 Updated parameters to pass $args twice for backwards compatibility.
|
||
|
*
|
||
|
* @param array $args Discount args.
|
||
|
*/
|
||
|
$args = apply_filters( 'edd_insert_discount_args', $args, $args );
|
||
|
|
||
|
$args = $this->sanitize_columns( $args );
|
||
|
|
||
|
/**
|
||
|
* Fires before the discount has been added to the database.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount args.
|
||
|
*/
|
||
|
do_action( 'edd_pre_insert_discount', $args );
|
||
|
|
||
|
foreach ( $args as $key => $value ) {
|
||
|
$this->{$key} = $value;
|
||
|
}
|
||
|
|
||
|
// We have to ensure an ID is not passed to edd_add_discount()
|
||
|
unset( $args['id'] );
|
||
|
|
||
|
$id = edd_add_discount( $args );
|
||
|
|
||
|
// The DB class 'add' implies an update if the discount being asked to be created already exists
|
||
|
if ( ! empty( $id ) ) {
|
||
|
|
||
|
// We need to update the ID of the instance of the object in order to add meta
|
||
|
$this->id = $id;
|
||
|
|
||
|
if ( isset( $args['excluded_products'] ) ) {
|
||
|
if ( is_array( $args['excluded_products'] ) ) {
|
||
|
foreach ( $args['excluded_products'] as $product ) {
|
||
|
edd_add_adjustment_meta( $this->id, 'excluded_product', absint( $product ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( isset( $args['product_reqs'] ) ) {
|
||
|
if ( is_array( $args['product_reqs'] ) ) {
|
||
|
foreach ( $args['product_reqs'] as $product ) {
|
||
|
edd_add_adjustment_meta( $this->id, 'product_requirement', absint( $product ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fires after the discount code is inserted.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $meta {
|
||
|
* The discount details.
|
||
|
*
|
||
|
* @type string $code The discount code.
|
||
|
* @type string $name The name of the discount.
|
||
|
* @type string $status The discount status. Defaults to active.
|
||
|
* @type int $uses The current number of uses.
|
||
|
* @type int $max_uses The max number of uses.
|
||
|
* @type string $start The start date.
|
||
|
* @type int $min_price The minimum price required to use the discount code.
|
||
|
* @type array $product_reqs The product IDs required to use the discount code.
|
||
|
* @type string $product_condition The conditions in which a product(s) must meet to use the discount code.
|
||
|
* @type array $excluded_products Product IDs excluded from this discount code.
|
||
|
* @type bool $is_not_global If the discount code is not globally applied to all products. Defaults to false.
|
||
|
* @type bool $is_single_use If the code cannot be used more than once per customer. Defaults to false.
|
||
|
* }
|
||
|
* @param int $ID The ID of the discount that was inserted.
|
||
|
*/
|
||
|
do_action( 'edd_post_insert_discount', $args, $this->id );
|
||
|
|
||
|
// Discount code created
|
||
|
return $id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update an existing discount in the database.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount details.
|
||
|
* @return bool True if update is successful, false otherwise.
|
||
|
*/
|
||
|
public function update( $args = array() ) {
|
||
|
$args = self::convert_legacy_args( $args );
|
||
|
$ret = false;
|
||
|
|
||
|
/**
|
||
|
* Filter the data being updated
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount args.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
$args = apply_filters( 'edd_update_discount', $args, $this->id );
|
||
|
$args = $this->sanitize_columns( $args );
|
||
|
|
||
|
// Get current time once to avoid inconsistencies
|
||
|
$current_time = current_time( 'timestamp' );
|
||
|
|
||
|
if ( ! empty( $args['start_date'] ) && ! empty( $args['end_date'] ) ) {
|
||
|
$start_timestamp = strtotime( $args['start_date'], $current_time );
|
||
|
$end_timestamp = strtotime( $args['end_date'], $current_time );
|
||
|
|
||
|
// Set the expiration date to the start date if start is later than expiration
|
||
|
if ( $start_timestamp > $end_timestamp ) {
|
||
|
$args['end_date'] = $args['start_date'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Start date
|
||
|
if ( ! empty( $args['start_date'] ) ) {
|
||
|
$args['start_date'] = date( 'Y-m-d H:i:s', strtotime( $args['start_date'], $current_time ) );
|
||
|
}
|
||
|
|
||
|
// End date
|
||
|
if ( ! empty( $args['end_date'] ) ) {
|
||
|
$args['end_date'] = date( 'Y-m-d H:i:s', strtotime( $args['end_date'], $current_time ) );
|
||
|
}
|
||
|
|
||
|
if ( isset( $args['excluded_products'] ) ) {
|
||
|
// Reset meta
|
||
|
edd_delete_adjustment_meta( $this->id, 'excluded_product' );
|
||
|
|
||
|
if ( is_array( $args['excluded_products'] ) ) {
|
||
|
// Now add each newly excluded product
|
||
|
foreach ( $args['excluded_products'] as $product ) {
|
||
|
edd_add_adjustment_meta( $this->id, 'excluded_product', absint( $product ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( isset( $args['product_reqs'] ) ) {
|
||
|
// Reset meta
|
||
|
edd_delete_adjustment_meta( $this->id, 'product_requirement' );
|
||
|
|
||
|
if ( is_array( $args['product_reqs'] ) ) {
|
||
|
// Now add each newly required product
|
||
|
foreach ( $args['product_reqs'] as $product ) {
|
||
|
edd_add_adjustment_meta( $this->id, 'product_requirement', absint( $product ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Switch `type` to `amount_type`
|
||
|
if ( ! isset( $args['amount_type'] ) && ! empty( $args['type'] ) && 'discount' !== $args['type'] ) {
|
||
|
$args['amount_type'] = $args['type'];
|
||
|
}
|
||
|
|
||
|
// Force `type` to `discount`
|
||
|
$args['type'] = 'discount';
|
||
|
|
||
|
/**
|
||
|
* Fires before the discount has been updated in the database.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount args.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
do_action( 'edd_pre_update_discount', $args, $this->id );
|
||
|
|
||
|
// If we are using the discounts DB
|
||
|
if ( edd_update_discount( $this->id, $args ) ) {
|
||
|
$discount = edd_get_discount( $this->id );
|
||
|
$this->setup_discount( $discount );
|
||
|
$ret = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fires after the discount has been updated in the database.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $args Discount args.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
do_action( 'edd_post_update_discount', $args, $this->id );
|
||
|
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the status of the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $new_status New status (default: active)
|
||
|
* @return bool If the status been updated or not.
|
||
|
*/
|
||
|
public function update_status( $new_status = 'active' ) {
|
||
|
|
||
|
/**
|
||
|
* Fires before the status of the discount is updated.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $new_status New status.
|
||
|
* @param string $post_status Post status.
|
||
|
*/
|
||
|
do_action( 'edd_pre_update_discount_status', $this->id, $new_status, $this->status );
|
||
|
|
||
|
$ret = $this->update( array( 'status' => $new_status ) );
|
||
|
|
||
|
/**
|
||
|
* Fires after the status of the discount is updated.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $new_status New status.
|
||
|
* @param string $status Post status.
|
||
|
*/
|
||
|
do_action( 'edd_post_update_discount_status', $this->id, $new_status, $this->status );
|
||
|
|
||
|
return (bool) $ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the discount has started.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Is discount started?
|
||
|
*/
|
||
|
public function is_started( $set_error = true ) {
|
||
|
$return = false;
|
||
|
|
||
|
if ( $this->start_date ) {
|
||
|
$start_date = strtotime( $this->start_date );
|
||
|
|
||
|
if ( $start_date < time() ) {
|
||
|
// Discount has pased the start date
|
||
|
$return = true;
|
||
|
} elseif ( $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', _x( 'This discount is invalid.', 'error shown when attempting to use a discount before its start date', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
} else {
|
||
|
// No start date for this discount, so has to be true
|
||
|
$return = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters if the discount has started or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return Has the discount started or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_started', $return, $this->id, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the discount has expired.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $update Update the discount to expired if an one is found but has an active status
|
||
|
* @return bool Has the discount expired?
|
||
|
*/
|
||
|
public function is_expired( $update = true ) {
|
||
|
$return = false;
|
||
|
|
||
|
if ( empty( $this->end_date ) || '0000-00-00 00:00:00' === $this->end_date ) {
|
||
|
return $return;
|
||
|
}
|
||
|
|
||
|
$end_date = strtotime( $this->end_date );
|
||
|
|
||
|
if ( $end_date < time() ) {
|
||
|
if ( $update ) {
|
||
|
$this->update_status( 'expired' );
|
||
|
}
|
||
|
$return = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters if the discount has expired or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return Has the discount expired or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_expired', $return, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the discount has maxed out.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Is discount maxed out?
|
||
|
*/
|
||
|
public function is_maxed_out( $set_error = true ) {
|
||
|
$return = false;
|
||
|
|
||
|
if ( $this->uses >= $this->max_uses && ! empty( $this->max_uses ) ) {
|
||
|
if ( $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', __( 'This discount has reached its maximum usage.', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
|
||
|
$return = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters if the discount is maxed out or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return Is the discount maxed out or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_maxed_out', $return, $this->id, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the minimum cart amount is satisfied for the discount to hold.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Is the minimum cart amount met?
|
||
|
*/
|
||
|
public function is_min_price_met( $set_error = true ) {
|
||
|
$return = false;
|
||
|
|
||
|
$cart_amount = edd_get_cart_discountable_subtotal( $this->id );
|
||
|
|
||
|
if ( (float) $cart_amount >= (float) $this->min_charge_amount ) {
|
||
|
$return = true;
|
||
|
} elseif ( $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', sprintf( __( 'Minimum order of %s not met.', 'easy-digital-downloads' ), edd_currency_filter( edd_format_amount( $this->min_charge_amount ) ) ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters if the minimum cart amount has been met to satisfy the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return Is the minimum cart amount met or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_min_met', $return, $this->id, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is the discount single use or not?
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return bool Is the discount single use or not?
|
||
|
*/
|
||
|
public function is_single_use() {
|
||
|
/**
|
||
|
* Filters if the discount is single use or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $single_use Is the discount is single use or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
*/
|
||
|
return (bool) apply_filters( 'edd_is_discount_single_use', $this->once_per_customer, $this->id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Are the product requirements met for the discount to hold.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Are required products in the cart?
|
||
|
*/
|
||
|
public function is_product_requirements_met( $set_error = true ) {
|
||
|
$product_reqs = $this->get_product_reqs();
|
||
|
$excluded_ps = $this->get_excluded_products();
|
||
|
$cart_items = edd_get_cart_contents();
|
||
|
$cart_ids = $cart_items ? wp_list_pluck( $cart_items, 'id' ) : null;
|
||
|
$is_met = true;
|
||
|
|
||
|
/**
|
||
|
* Normalize our data for product requirements, exclusions and cart data.
|
||
|
*/
|
||
|
|
||
|
// First absint the items, then sort, and reset the array keys
|
||
|
$product_reqs = array_map( 'absint', $product_reqs );
|
||
|
asort( $product_reqs );
|
||
|
$product_reqs = array_filter( array_values( $product_reqs ) );
|
||
|
|
||
|
$cart_ids = array_map( 'absint', $cart_ids );
|
||
|
asort( $cart_ids );
|
||
|
$cart_ids = array_values( $cart_ids );
|
||
|
|
||
|
// Ensure we have requirements before proceeding
|
||
|
if ( ! empty( $product_reqs ) ) {
|
||
|
$matches = array_intersect( $product_reqs, $cart_ids );
|
||
|
|
||
|
switch ( $this->get_product_condition() ) {
|
||
|
case 'all':
|
||
|
$is_met = count( $matches ) === count( $product_reqs );
|
||
|
break;
|
||
|
default:
|
||
|
$is_met = 0 < count( $matches );
|
||
|
}
|
||
|
|
||
|
if ( ! $is_met && $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', __( 'The product requirements for this discount are not met.', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$excluded_ps = array_map( 'absint', $excluded_ps );
|
||
|
asort( $excluded_ps );
|
||
|
$excluded_ps = array_filter( array_values( $excluded_ps ) );
|
||
|
|
||
|
if ( ! empty( $excluded_ps ) ) {
|
||
|
if ( count( array_intersect( $cart_ids, $excluded_ps ) ) === count( $cart_ids ) ) {
|
||
|
$is_met = false;
|
||
|
|
||
|
if ( $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', __( 'This discount is not valid for the cart contents.', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters whether the product requirements are met for the discount to hold.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $is_met Are the product requirements met or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $product_condition Product condition.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return (bool) apply_filters( 'edd_is_discount_products_req_met', $is_met, $this->id, $this->product_condition, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Has the discount code been used.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @since 3.0 Refactored to use new query methods.
|
||
|
*
|
||
|
* @param string $user User info.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*
|
||
|
* @return bool Whether the discount has been used or not.
|
||
|
*/
|
||
|
public function is_used( $user = '', $set_error = true ) {
|
||
|
$return = false;
|
||
|
|
||
|
if ( $this->is_single_use ) {
|
||
|
$payments = array();
|
||
|
|
||
|
if ( edd_get_component_interface( 'customer', 'table' )->exists() ) {
|
||
|
$by_user_id = ! is_email( $user );
|
||
|
|
||
|
$customer = new EDD_Customer( $user, $by_user_id );
|
||
|
|
||
|
$payments = explode( ',', $customer->payment_ids );
|
||
|
} else {
|
||
|
$user_found = false;
|
||
|
|
||
|
if ( is_email( $user ) ) {
|
||
|
$user_found = true; // All we need is the email
|
||
|
$key = '_edd_payment_user_email';
|
||
|
$value = $user;
|
||
|
} else {
|
||
|
$user_data = get_user_by( 'login', $user );
|
||
|
|
||
|
if ( $user_data ) {
|
||
|
$user_found = true;
|
||
|
$key = '_edd_payment_user_id';
|
||
|
$value = $user_data->ID;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( $user_found ) {
|
||
|
$query_args = array(
|
||
|
'post_type' => 'edd_payment',
|
||
|
'meta_query' => array(
|
||
|
array(
|
||
|
'key' => $key,
|
||
|
'value' => $value,
|
||
|
'compare' => '=',
|
||
|
),
|
||
|
),
|
||
|
'fields' => 'ids',
|
||
|
);
|
||
|
|
||
|
$payments = get_posts( $query_args ); // Get all payments with matching email
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( $payments ) {
|
||
|
foreach ( $payments as $payment ) {
|
||
|
$payment = new EDD_Payment( $payment );
|
||
|
|
||
|
if ( empty( $payment->discounts ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( in_array( $payment->status, edd_get_incomplete_order_statuses(), true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$discounts = explode( ',', $payment->discounts );
|
||
|
|
||
|
if ( is_array( $discounts ) ) {
|
||
|
$discounts = array_map( 'strtoupper', $discounts );
|
||
|
$key = array_search( strtoupper( $this->code ), $discounts, true );
|
||
|
|
||
|
if ( false !== $key ) {
|
||
|
if ( $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', __( 'This discount has already been redeemed.', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
|
||
|
$return = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters if the discount is used or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return If the discount is used or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $user User info.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_used', $return, $this->id, $user, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a discount holds at the time of purchase.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string $user User info.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Is the discount valid or not?
|
||
|
*/
|
||
|
public function is_valid( $user = '', $set_error = true ) {
|
||
|
$return = false;
|
||
|
$user = trim( $user );
|
||
|
|
||
|
if ( edd_get_cart_contents() && $this->id ) {
|
||
|
if (
|
||
|
$this->is_active( true, $set_error ) &&
|
||
|
$this->is_started( $set_error ) &&
|
||
|
! $this->is_maxed_out( $set_error ) &&
|
||
|
! $this->is_used( $user, $set_error ) &&
|
||
|
$this->is_product_requirements_met( $set_error ) &&
|
||
|
$this->is_min_price_met( $set_error )
|
||
|
) {
|
||
|
$return = true;
|
||
|
}
|
||
|
} elseif ( $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', _x( 'This discount is invalid.', 'error for when a discount is invalid based on its configuration', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters whether the discount is valid or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return If the discount is used or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $code Discount code.
|
||
|
* @param string $user User info.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_valid', $return, $this->id, $this->code, $user, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a discount code is active.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $update Update the discount to expired if an one is found but has an active status.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool If the discount is active or not.
|
||
|
*/
|
||
|
public function is_active( $update = true, $set_error = true ) {
|
||
|
$return = false;
|
||
|
|
||
|
if ( $this->exists() ) {
|
||
|
|
||
|
if ( $this->is_expired( $update ) ) {
|
||
|
if ( edd_doing_ajax() && $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', __( 'This discount is expired.', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
} elseif ( 'active' === $this->status ) {
|
||
|
$return = true;
|
||
|
} elseif ( edd_doing_ajax() && $set_error ) {
|
||
|
edd_set_error( 'edd-discount-error', __( 'This discount is not active.', 'easy-digital-downloads' ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters if the discount is active or not.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param bool $return Is the discount active or not.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
*/
|
||
|
return apply_filters( 'edd_is_discount_active', $return, $this->id, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Discounted Amount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param string|int $base_price Price before discount.
|
||
|
* @return float $discounted_price Amount after discount.
|
||
|
*/
|
||
|
public function get_discounted_amount( $base_price ) {
|
||
|
$base_price = floatval( $base_price );
|
||
|
|
||
|
if ( 'flat' === $this->amount_type ) {
|
||
|
$amount = $base_price - floatval( $this->amount );
|
||
|
|
||
|
if ( $amount < 0 ) {
|
||
|
$amount = 0;
|
||
|
}
|
||
|
} else {
|
||
|
// Percentage discount
|
||
|
$amount = $base_price - ( $base_price * ( floatval( $this->amount ) / 100 ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filter the discounted amount calculated.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
* @access public
|
||
|
*
|
||
|
* @param float $amount Calculated discounted amount.
|
||
|
* @param EDD_Discount $this Discount object.
|
||
|
*/
|
||
|
return apply_filters( 'edd_discounted_amount', $amount, $this );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Increment the usage of the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return int New discount usage.
|
||
|
*/
|
||
|
public function increase_usage() {
|
||
|
if ( $this->get_uses() ) {
|
||
|
$this->use_count++;
|
||
|
} else {
|
||
|
$this->use_count = 1;
|
||
|
}
|
||
|
|
||
|
$args = array( 'use_count' => $this->use_count );
|
||
|
|
||
|
$this->max_uses = absint( $this->max_uses );
|
||
|
|
||
|
if ( 0 !== $this->max_uses && $this->max_uses <= $this->use_count ) {
|
||
|
$args['status'] = 'inactive';
|
||
|
}
|
||
|
|
||
|
$this->update( $args );
|
||
|
|
||
|
/**
|
||
|
* Fires after the usage count has been increased.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param int $use_count Discount usage.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $code Discount code.
|
||
|
*/
|
||
|
do_action( 'edd_discount_increase_use_count', $this->use_count, $this->id, $this->code );
|
||
|
|
||
|
return (int) $this->use_count;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrement the usage of the discount.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return int New discount usage.
|
||
|
*/
|
||
|
public function decrease_usage() {
|
||
|
if ( $this->get_uses() ) {
|
||
|
$this->use_count--;
|
||
|
}
|
||
|
|
||
|
if ( $this->use_count < 0 ) {
|
||
|
$this->use_count = 0;
|
||
|
}
|
||
|
|
||
|
$args = array( 'use_count' => $this->use_count );
|
||
|
|
||
|
if ( 0 !== $this->max_uses && $this->max_uses > $this->use_count ) {
|
||
|
$args['status'] = 'active';
|
||
|
}
|
||
|
|
||
|
$this->update( $args );
|
||
|
|
||
|
/**
|
||
|
* Fires after the usage count has been decreased.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param int $use_count Discount usage.
|
||
|
* @param int $ID Discount ID.
|
||
|
* @param string $code Discount code.
|
||
|
*/
|
||
|
do_action( 'edd_discount_decrease_use_count', $this->use_count, $this->id, $this->code );
|
||
|
|
||
|
return (int) $this->use_count;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Edit Discount Link.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @return string Link to the `Edit Discount` page.
|
||
|
*/
|
||
|
public function edit_url() {
|
||
|
return esc_url(
|
||
|
edd_get_admin_url(
|
||
|
array(
|
||
|
'page' => 'edd-discounts',
|
||
|
'edd-action' => 'edit_discount',
|
||
|
'discount' => absint( $this->id ),
|
||
|
)
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sanitize the data for update/create
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @param array $data The data to sanitize
|
||
|
* @return array The sanitized data, based off column defaults
|
||
|
*/
|
||
|
private function sanitize_columns( $data ) {
|
||
|
$default_values = array();
|
||
|
|
||
|
foreach ( $data as $key => $type ) {
|
||
|
|
||
|
// Only sanitize data that we were provided
|
||
|
if ( ! array_key_exists( $key, $data ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch ( $type ) {
|
||
|
|
||
|
case '%s':
|
||
|
if ( 'email' === $key ) {
|
||
|
$data[ $key ] = sanitize_email( $data[ $key ] );
|
||
|
} elseif ( 'notes' === $key ) {
|
||
|
$data[ $key ] = strip_tags( $data[ $key ] );
|
||
|
} else {
|
||
|
if ( is_array( $data[ $key ] ) ) {
|
||
|
$data[ $key ] = json_encode( $data[ $key ] );
|
||
|
} else {
|
||
|
$data[ $key ] = sanitize_text_field( $data[ $key ] );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case '%d':
|
||
|
if ( ! is_numeric( $data[ $key ] ) || absint( $data[ $key ] ) !== (int) $data[ $key ] ) {
|
||
|
$data[ $key ] = $default_values[ $key ];
|
||
|
} else {
|
||
|
$data[ $key ] = absint( $data[ $key ] );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case '%f':
|
||
|
// Convert what was given to a float
|
||
|
$value = floatval( $data[ $key ] );
|
||
|
|
||
|
if ( ! is_float( $value ) ) {
|
||
|
$data[ $key ] = $default_values[ $key ];
|
||
|
} else {
|
||
|
$data[ $key ] = $value;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
$data[ $key ] = ! is_array( $data[ $key ] )
|
||
|
? sanitize_text_field( $data[ $key ] )
|
||
|
: maybe_serialize( array_map( 'sanitize_text_field', $data[ $key ] ) );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts pre-3.0 arguments to the 3.0+ version.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @static
|
||
|
*
|
||
|
* @param $args array Arguments to be converted.
|
||
|
* @return array The converted arguments.
|
||
|
*/
|
||
|
public static function convert_legacy_args( $args = array() ) {
|
||
|
|
||
|
// Loop through arguments provided and adjust old key names for the new schema introduced in 3.0
|
||
|
$old = array(
|
||
|
'uses' => 'use_count',
|
||
|
'max' => 'max_uses',
|
||
|
'start' => 'start_date',
|
||
|
'expiration' => 'end_date',
|
||
|
'min_price' => 'min_charge_amount',
|
||
|
'products' => 'product_reqs',
|
||
|
'excluded-products' => 'excluded_products',
|
||
|
'not_global' => 'scope',
|
||
|
'is_not_global' => 'scope',
|
||
|
'use_once' => 'once_per_customer',
|
||
|
'is_single_use' => 'once_per_customer',
|
||
|
);
|
||
|
|
||
|
foreach ( $old as $old_key => $new_key ) {
|
||
|
if ( isset( $args[ $old_key ] ) ) {
|
||
|
if ( in_array( $old_key, array( 'not_global', 'is_not_global' ), true ) && ! array_key_exists( 'scope', $args ) ) {
|
||
|
$args[ $new_key ] = ! empty( $args[ $old_key ] )
|
||
|
? 'not_global'
|
||
|
: 'global';
|
||
|
} else {
|
||
|
$args[ $new_key ] = $args[ $old_key ];
|
||
|
}
|
||
|
}
|
||
|
unset( $args[ $old_key ] );
|
||
|
}
|
||
|
|
||
|
// Default status needs to be active for regression purposes.
|
||
|
// See https://github.com/easydigitaldownloads/easy-digital-downloads/issues/6806
|
||
|
if ( ! isset( $args['status'] ) ) {
|
||
|
$args['status'] = 'active';
|
||
|
}
|
||
|
|
||
|
return $args;
|
||
|
}
|
||
|
}
|