1583 lines
43 KiB
PHP
1583 lines
43 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Discount Functions
|
||
|
*
|
||
|
* @package EDD
|
||
|
* @subpackage Functions
|
||
|
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||
|
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||
|
* @since 1.0
|
||
|
*/
|
||
|
|
||
|
// Exit if accessed directly
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* Add a discount.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 3.0 This function has been repurposed. Previously it was an internal admin callback for adding
|
||
|
* a discount via the UI. It's now used as a public function for inserting a new discount
|
||
|
* into the database.
|
||
|
*
|
||
|
* @param array $data Discount data.
|
||
|
* @return int Discount ID.
|
||
|
*/
|
||
|
function edd_add_discount( $data = array() ) {
|
||
|
|
||
|
// Juggle requirements and products.
|
||
|
$product_requirements = isset( $data['product_reqs'] ) ? $data['product_reqs'] : null;
|
||
|
$excluded_products = isset( $data['excluded_products'] ) ? $data['excluded_products'] : null;
|
||
|
$product_condition = isset( $data['product_condition'] ) ? $data['product_condition'] : null;
|
||
|
$pre_convert_args = $data;
|
||
|
unset( $data['product_reqs'], $data['excluded_products'], $data['product_condition'] );
|
||
|
|
||
|
if ( isset( $data['expiration'] ) ) {
|
||
|
$data['end_date'] = $data['expiration'];
|
||
|
unset( $data['expiration'] );
|
||
|
}
|
||
|
|
||
|
if ( isset( $data['start'] ) ) {
|
||
|
$data['start_date'] = $data['start'];
|
||
|
unset( $data['start'] );
|
||
|
}
|
||
|
|
||
|
// Setup the discounts query.
|
||
|
$discounts = new EDD\Compat\Discount_Query();
|
||
|
|
||
|
// Attempt to add the discount.
|
||
|
$discount_id = $discounts->add_item( $data );
|
||
|
|
||
|
// Maybe add requirements & exclusions.
|
||
|
if ( ! empty( $discount_id ) ) {
|
||
|
|
||
|
// Product requirements.
|
||
|
if ( ! empty( $product_requirements ) ) {
|
||
|
if ( is_string( $product_requirements ) ) {
|
||
|
$product_requirements = maybe_unserialize( $product_requirements );
|
||
|
}
|
||
|
|
||
|
if ( is_array( $product_requirements ) ) {
|
||
|
foreach ( array_filter( $product_requirements ) as $product_requirement ) {
|
||
|
edd_add_adjustment_meta( $discount_id, 'product_requirement', $product_requirement );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Excluded products.
|
||
|
if ( ! empty( $excluded_products ) ) {
|
||
|
if ( is_string( $excluded_products ) ) {
|
||
|
$excluded_products = maybe_unserialize( $excluded_products );
|
||
|
}
|
||
|
|
||
|
if ( is_array( $excluded_products ) ) {
|
||
|
foreach ( array_filter( $excluded_products ) as $excluded_product ) {
|
||
|
edd_add_adjustment_meta( $discount_id, 'excluded_product', $excluded_product );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! empty( $product_condition ) ) {
|
||
|
edd_add_adjustment_meta( $discount_id, 'product_condition', $product_condition );
|
||
|
}
|
||
|
|
||
|
// If the end date has passed, mark the discount as expired.
|
||
|
edd_is_discount_expired( $discount_id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fires after the discount code is inserted. This hook exists for
|
||
|
* backwards compatibility purposes. It uses the $pre_convert_args variable
|
||
|
* to ensure the arguments maintain backwards compatible array keys.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $pre_convert_args Discount args.
|
||
|
* @param int $return Discount ID.
|
||
|
*/
|
||
|
do_action( 'edd_post_insert_discount', $pre_convert_args, $discount_id );
|
||
|
|
||
|
// Return the new discount ID.
|
||
|
return $discount_id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete a discount.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return int
|
||
|
*/
|
||
|
function edd_delete_discount( $discount_id = 0 ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
// Do not allow for a discount to be deleted if it has been used.
|
||
|
if ( $discount && 0 < $discount->use_count ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$discounts = new EDD\Compat\Discount_Query();
|
||
|
|
||
|
// Pre-3.0 pre action.
|
||
|
do_action( 'edd_pre_delete_discount', $discount_id );
|
||
|
|
||
|
$retval = $discounts->delete_item( $discount_id );
|
||
|
|
||
|
// Pre-3.0 post action.
|
||
|
do_action( 'edd_post_delete_discount', $discount_id );
|
||
|
|
||
|
return $retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Discount.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object
|
||
|
* @since 3.0 Updated to call use new query class.
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return \EDD_Discount|bool EDD_Discount object or false if not found.
|
||
|
*/
|
||
|
function edd_get_discount( $discount_id = 0 ) {
|
||
|
$discounts = new EDD\Compat\Discount_Query();
|
||
|
|
||
|
// Return discount
|
||
|
return $discounts->get_item( $discount_id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get discount by code.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object
|
||
|
* @since 3.0 Updated to call use new query class.
|
||
|
* @since 3.0 Updated to include a filter.
|
||
|
*
|
||
|
* @param string $code Discount code.
|
||
|
* @return EDD_Discount|bool EDD_Discount object or false if not found.
|
||
|
*/
|
||
|
function edd_get_discount_by_code( $code = '' ) {
|
||
|
$discount = edd_get_discount_by( 'code', $code );
|
||
|
|
||
|
/**
|
||
|
* Filters the get discount by request.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @param \EDD_Discount $discount Discount object.
|
||
|
* @param string $code Discount code.
|
||
|
*/
|
||
|
return apply_filters( 'edd_get_discount_by_code', $discount, $code );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve discount by a given field
|
||
|
*
|
||
|
* @since 2.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object
|
||
|
* @since 3.0 Updated to call use new query class.
|
||
|
*
|
||
|
* @param string $field The field to retrieve the discount with.
|
||
|
* @param mixed $value The value for $field.
|
||
|
* @return mixed EDD_Discount|bool EDD_Discount object or false if not found.
|
||
|
*/
|
||
|
function edd_get_discount_by( $field = '', $value = '' ) {
|
||
|
$discounts = new EDD\Compat\Discount_Query();
|
||
|
|
||
|
// Return discount
|
||
|
return $discounts->get_item_by( $field, $value );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve discount by a given field
|
||
|
*
|
||
|
* @since 2.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param string $field The field to retrieve the discount with.
|
||
|
* @return mixed object|bool EDD_Discount object or false if not found.
|
||
|
*/
|
||
|
function edd_get_discount_field( $discount_id, $field = '' ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
// Check that field exists
|
||
|
return isset( $discount->{$field} )
|
||
|
? $discount->{$field}
|
||
|
: null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update a discount
|
||
|
*
|
||
|
* @since 3.0
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param array $data
|
||
|
* @return int
|
||
|
*/
|
||
|
function edd_update_discount( $discount_id = 0, $data = array() ) {
|
||
|
|
||
|
// Pre-3.0 pre action
|
||
|
do_action( 'edd_pre_update_discount', $data, $discount_id );
|
||
|
|
||
|
// Product requirements.
|
||
|
if ( isset( $data['product_reqs'] ) && ! empty( $data['product_reqs'] ) ) {
|
||
|
if ( is_string( $data['product_reqs'] ) ) {
|
||
|
$data['product_reqs'] = maybe_unserialize( $data['product_reqs'] );
|
||
|
}
|
||
|
|
||
|
if ( is_array( $data['product_reqs'] ) ) {
|
||
|
edd_delete_adjustment_meta( $discount_id, 'product_requirement' );
|
||
|
|
||
|
foreach ( $data['product_reqs'] as $product_requirement ) {
|
||
|
edd_add_adjustment_meta( $discount_id, 'product_requirement', $product_requirement );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unset( $data['product_reqs'] );
|
||
|
} elseif ( isset( $data['product_reqs'] ) ) {
|
||
|
edd_delete_adjustment_meta( $discount_id, 'product_requirement' );
|
||
|
|
||
|
// We don't have product conditions when there are no product requirements.
|
||
|
edd_delete_adjustment_meta( $discount_id, 'product_condition' );
|
||
|
unset( $data['product_condition'] );
|
||
|
}
|
||
|
|
||
|
// Excluded products are handled differently.
|
||
|
if ( isset( $data['excluded_products'] ) && ! empty( $data['excluded_products'] ) ) {
|
||
|
if ( is_string( $data['excluded_products'] ) ) {
|
||
|
$data['excluded_products'] = maybe_unserialize( $data['excluded_products'] );
|
||
|
}
|
||
|
|
||
|
if ( is_array( $data['excluded_products'] ) ) {
|
||
|
edd_delete_adjustment_meta( $discount_id, 'excluded_product' );
|
||
|
|
||
|
foreach ( $data['excluded_products'] as $excluded_product ) {
|
||
|
edd_add_adjustment_meta( $discount_id, 'excluded_product', $excluded_product );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unset( $data['excluded_products'] );
|
||
|
} elseif( isset( $data['excluded_products'] ) ) {
|
||
|
edd_delete_adjustment_meta( $discount_id, 'excluded_product' );
|
||
|
}
|
||
|
|
||
|
if ( isset( $data['product_condition'] ) ) {
|
||
|
$product_condition = sanitize_text_field( $data['product_condition'] );
|
||
|
edd_update_adjustment_meta( $discount_id, 'product_condition', $product_condition );
|
||
|
}
|
||
|
|
||
|
$discounts = new EDD\Compat\Discount_Query();
|
||
|
|
||
|
$retval = $discounts->update_item( $discount_id, $data );
|
||
|
|
||
|
// Pre-3.0 post action
|
||
|
do_action( 'edd_post_update_discount', $data, $discount_id );
|
||
|
|
||
|
return $retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Discounts
|
||
|
*
|
||
|
* Retrieves an array of all available discount codes.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @param array $args Query arguments
|
||
|
* @return mixed array if discounts exist, false otherwise
|
||
|
*/
|
||
|
function edd_get_discounts( $args = array() ) {
|
||
|
|
||
|
// Parse arguments.
|
||
|
$r = wp_parse_args( $args, array(
|
||
|
'number' => 30
|
||
|
) );
|
||
|
|
||
|
// Back compat for old query arg.
|
||
|
if ( isset( $r['posts_per_page'] ) ) {
|
||
|
$r['number'] = $r['posts_per_page'];
|
||
|
}
|
||
|
|
||
|
// Instantiate a query object.
|
||
|
$discounts = new EDD\Compat\Discount_Query();
|
||
|
|
||
|
// Return discounts
|
||
|
return $discounts->query( $r );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return total number of discounts
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @param array $args Arguments.
|
||
|
* @return int
|
||
|
*/
|
||
|
function edd_get_discount_count( $args = array() ) {
|
||
|
|
||
|
// Parse args.
|
||
|
$r = wp_parse_args( $args, array(
|
||
|
'count' => true
|
||
|
) );
|
||
|
|
||
|
// Query for count(s).
|
||
|
$discounts = new EDD\Compat\Discount_Query( $r );
|
||
|
|
||
|
// Return count(s).
|
||
|
return absint( $discounts->found_items );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Query for and return array of discount counts, keyed by status.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function edd_get_discount_counts( $args = array() ) {
|
||
|
|
||
|
// Parse arguments
|
||
|
$r = wp_parse_args( $args, array(
|
||
|
'count' => true,
|
||
|
'groupby' => 'status'
|
||
|
) );
|
||
|
|
||
|
// Query for count.
|
||
|
$counts = new EDD\Compat\Discount_Query( $r );
|
||
|
|
||
|
// Format & return
|
||
|
return edd_format_counts( $counts, $r['groupby'] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Query for discount notes.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return array Retrieved notes.
|
||
|
*/
|
||
|
function edd_get_discount_notes( $discount_id = 0 ) {
|
||
|
return edd_get_notes( array(
|
||
|
'object_id' => $discount_id,
|
||
|
'object_type' => 'discount',
|
||
|
'order' => 'asc'
|
||
|
) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if there is any active discounts, returns a boolean.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 3.0 Updated to be more efficient and make direct calls to the EDD_Discount object.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
function edd_has_active_discounts() {
|
||
|
|
||
|
// Query for active discounts.
|
||
|
$discounts = edd_get_discounts( array(
|
||
|
'number' => 10,
|
||
|
'status' => 'active'
|
||
|
) );
|
||
|
|
||
|
// Bail if none.
|
||
|
if ( empty( $discounts ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Check each discount for active status, applying filters, etc...
|
||
|
foreach ( $discounts as $discount ) {
|
||
|
/** @var $discount EDD_Discount */
|
||
|
if ( $discount->is_active( false, true ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stores a discount code. If the code already exists, it updates it, otherwise
|
||
|
* it creates a new one.
|
||
|
*
|
||
|
* @internal This method exists for backwards compatibility. `edd_add_discount()` should be used.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to use new query class.
|
||
|
*
|
||
|
* @param array $details Discount args.
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return mixed bool|int The discount ID of the discount code, or false on failure.
|
||
|
*/
|
||
|
function edd_store_discount( $details, $discount_id = null ) {
|
||
|
|
||
|
// Set default return value to false.
|
||
|
$return = false;
|
||
|
|
||
|
// Back-compat for start date.
|
||
|
if ( isset( $details['start'] ) && strstr( $details['start'], '/' ) ) {
|
||
|
$details['start_date'] = date( 'Y-m-d', strtotime( $details['start'] ) ) . ' 00:00:00';
|
||
|
unset( $details['start'] );
|
||
|
}
|
||
|
|
||
|
// Back-compat for end date.
|
||
|
if ( isset( $details['expiration'] ) && strstr( $details['expiration'], '/' ) ) {
|
||
|
$details['end_date'] = date( 'Y-m-d', strtotime( $details['expiration'] ) ) . ' 23:59:59';
|
||
|
unset( $details['expiration'] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters the args before being inserted into the database. This hook
|
||
|
* exists for backwards compatibility purposes.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $details Discount args.
|
||
|
*/
|
||
|
$details = apply_filters( 'edd_insert_discount', $details );
|
||
|
|
||
|
/**
|
||
|
* Fires before the discount has been added to the database. This hook
|
||
|
* exists for backwards compatibility purposes. It fires before the
|
||
|
* call to `EDD_Discount::convert_legacy_args` to ensure the arguments
|
||
|
* maintain backwards compatible array keys.
|
||
|
*
|
||
|
* @since 2.7
|
||
|
*
|
||
|
* @param array $details Discount args.
|
||
|
*/
|
||
|
do_action( 'edd_pre_insert_discount', $details );
|
||
|
|
||
|
// Convert legacy arguments to new ones accepted by `edd_add_discount()`.
|
||
|
$details = EDD_Discount::convert_legacy_args( $details );
|
||
|
|
||
|
if ( null === $discount_id ) {
|
||
|
$return = (int) edd_add_discount( $details );
|
||
|
} else {
|
||
|
edd_update_discount( $discount_id, $details );
|
||
|
$return = $discount_id;
|
||
|
}
|
||
|
|
||
|
return $return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Deletes a discount code.
|
||
|
*
|
||
|
* @internal This method exists for backwards compatibility. `edd_delete_discount()` should be used.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @deprecated 3.0
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
*/
|
||
|
function edd_remove_discount( $discount_id = 0 ) {
|
||
|
edd_delete_discount( $discount_id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates a discount status from one status to another.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID (default: 0)
|
||
|
* @param string $new_status New status (default: active)
|
||
|
*
|
||
|
* @return bool Whether the status has been updated or not.
|
||
|
*/
|
||
|
function edd_update_discount_status( $discount_id = 0, $new_status = 'active' ) {
|
||
|
|
||
|
// Bail if an invalid ID is passed.
|
||
|
if ( $discount_id <= 0 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Set defaults.
|
||
|
$updated = false;
|
||
|
$new_status = sanitize_key( $new_status );
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
// No change.
|
||
|
if ( $new_status === $discount->status ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Try to update status.
|
||
|
if ( ! empty( $discount->id ) ) {
|
||
|
$updated = (bool) edd_update_discount( $discount->id, array(
|
||
|
'status' => $new_status
|
||
|
) );
|
||
|
}
|
||
|
|
||
|
// Return.
|
||
|
return $updated;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks to see if a discount code already exists.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount().
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
*
|
||
|
* @return bool Whether or not the discount exists.
|
||
|
*/
|
||
|
function edd_discount_exists( $discount_id ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
return $discount instanceof EDD_Discount && $discount->exists();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a discount code is active.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.6.11 Added $update parameter.
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount().
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @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 should be set in session.
|
||
|
* @return bool Whether or not the discount is active.
|
||
|
*/
|
||
|
function edd_is_discount_active( $discount_id = 0, $update = true, $set_error = true ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
if ( ! $discount instanceof EDD_Discount ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return $discount->is_active( $update, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount code.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return string $code Discount Code.
|
||
|
*/
|
||
|
function edd_get_discount_code( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'code' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount code start date.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return string $start Discount start date.
|
||
|
*/
|
||
|
function edd_get_discount_start_date( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'start_date' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount code expiration date.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return string $expiration Discount expiration.
|
||
|
*/
|
||
|
function edd_get_discount_expiration( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'end_date' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the maximum uses that a certain discount code.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return int $max_uses Maximum number of uses for the discount code.
|
||
|
*/
|
||
|
function edd_get_discount_max_uses( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'max_uses' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve number of times a discount has been used.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field().
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return int $uses Number of times a discount has been used.
|
||
|
*/
|
||
|
function edd_get_discount_uses( $discount_id = 0 ) {
|
||
|
return (int) edd_get_discount_field( $discount_id, 'use_count' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the minimum purchase amount for a discount.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field().
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return float $min_price Minimum purchase amount.
|
||
|
*/
|
||
|
function edd_get_discount_min_price( $discount_id = 0 ) {
|
||
|
return edd_format_amount( edd_get_discount_field( $discount_id, 'min_charge_amount' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount amount.
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field().
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return float $amount Discount amount.
|
||
|
*/
|
||
|
function edd_get_discount_amount( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'amount' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount type
|
||
|
*
|
||
|
* @since 1.4
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field().
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return string $type Discount type
|
||
|
*/
|
||
|
function edd_get_discount_type( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'type' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the products the discount cannot be applied to.
|
||
|
*
|
||
|
* @since 1.9
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return array $excluded_products IDs of the required products.
|
||
|
*/
|
||
|
function edd_get_discount_excluded_products( $discount_id = 0 ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
return $discount instanceof EDD_Discount ? $discount->excluded_products : array();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount product requirements.
|
||
|
*
|
||
|
* @since 1.5
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return array $product_reqs IDs of the required products.
|
||
|
*/
|
||
|
function edd_get_discount_product_reqs( $discount_id = 0 ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
return $discount instanceof EDD_Discount ? $discount->product_reqs : array();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the product condition.
|
||
|
*
|
||
|
* @since 1.5
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
*
|
||
|
* @return string Product condition.
|
||
|
*/
|
||
|
function edd_get_discount_product_condition( $discount_id = 0 ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
return $discount instanceof EDD_Discount ? $discount->product_condition : '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the discount status label.
|
||
|
*
|
||
|
* @since 2.9
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @return string Product condition.
|
||
|
*/
|
||
|
function edd_get_discount_status_label( $discount_id = null ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
return $discount instanceof EDD_Discount ? $discount->get_status_label() : '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a discount is not global.
|
||
|
*
|
||
|
* By default discounts are applied to all products in the cart. Non global discounts are
|
||
|
* applied only to the products selected as requirements.
|
||
|
*
|
||
|
* @since 1.5
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Please use edd_get_discount_scope() instead.
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
*
|
||
|
* @return boolean Whether or not discount code is not global.
|
||
|
*/
|
||
|
function edd_is_discount_not_global( $discount_id = 0 ) {
|
||
|
return ( 'not_global' === edd_get_discount_field( $discount_id, 'scope' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the discount scope.
|
||
|
*
|
||
|
* By default this will return "global" as discounts are applied to all products in the cart. Non global discounts are
|
||
|
* applied only to the products selected as requirements.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
*
|
||
|
* @return string global or not_global.
|
||
|
*/
|
||
|
function edd_get_discount_scope( $discount_id = 0 ) {
|
||
|
return edd_get_discount_field( $discount_id, 'scope' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a discount code is expired.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.6.11 Added $update parameter.
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param bool $update Update the discount to expired if an one is found but has an active status.
|
||
|
* @return bool Whether on not the discount has expired.
|
||
|
*/
|
||
|
function edd_is_discount_expired( $discount_id = 0, $update = true ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
return ! empty( $discount->id )
|
||
|
? $discount->is_expired( $update )
|
||
|
: false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a discount code is available to use yet (start date).
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Is discount started?
|
||
|
*/
|
||
|
function edd_is_discount_started( $discount_id = 0, $set_error = true ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
return ! empty( $discount->id )
|
||
|
? $discount->is_started( $set_error )
|
||
|
: false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is Discount Maxed Out.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Is discount maxed out?
|
||
|
*/
|
||
|
function edd_is_discount_maxed_out( $discount_id = 0, $set_error = true ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
return ! empty( $discount->id )
|
||
|
? $discount->is_maxed_out( $set_error )
|
||
|
: false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks to see if the minimum purchase amount has been met.
|
||
|
*
|
||
|
* @since 1.1.7
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Whether the minimum amount has been met or not.
|
||
|
*/
|
||
|
function edd_discount_is_min_met( $discount_id = 0, $set_error = true ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
return ! empty( $discount->id )
|
||
|
? $discount->is_min_price_met( $set_error )
|
||
|
: false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is the discount limited to a single use per customer?
|
||
|
*
|
||
|
* @since 1.5
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_field()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
*
|
||
|
* @return bool Whether the discount is single use or not.
|
||
|
*/
|
||
|
function edd_discount_is_single_use( $discount_id = 0 ) {
|
||
|
return (bool) edd_get_discount_field( $discount_id, 'once_per_customer' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks to see if the required products are in the cart
|
||
|
*
|
||
|
* @since 1.5
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Are required products in the cart for the discount to hold.
|
||
|
*/
|
||
|
function edd_discount_product_reqs_met( $discount_id = 0, $set_error = true ) {
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
return $discount instanceof EDD_Discount && $discount->is_product_requirements_met( $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks to see if a user has already used a discount.
|
||
|
*
|
||
|
* @since 1.1.5
|
||
|
* @since 1.5 Added $discount_id parameter.
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount()
|
||
|
*
|
||
|
* @param string $code Discount Code.
|
||
|
* @param string $user User info.
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param bool $set_error Whether an error message be set in session
|
||
|
*
|
||
|
* @return bool $return Whether the the discount code is used.
|
||
|
*/
|
||
|
function edd_is_discount_used( $code = null, $user = '', $discount_id = 0, $set_error = true ) {
|
||
|
$discount = ( null == $code )
|
||
|
? edd_get_discount( $discount_id )
|
||
|
: edd_get_discount_by_code( $code );
|
||
|
|
||
|
return $discount instanceof EDD_Discount && $discount->is_used( $user, $set_error );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether a discount code is valid (when purchasing).
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_by_code()
|
||
|
*
|
||
|
* @param string $code Discount Code.
|
||
|
* @param string $user User info.
|
||
|
* @param bool $set_error Whether an error message be set in session.
|
||
|
* @return bool Whether the discount code is valid.
|
||
|
*/
|
||
|
function edd_is_discount_valid( $code = '', $user = '', $set_error = true ) {
|
||
|
$discount = edd_get_discount_by_code( $code );
|
||
|
|
||
|
if ( ! empty( $discount->id ) ) {
|
||
|
return $discount->is_valid( $user, $set_error );
|
||
|
} 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' ) );
|
||
|
return false;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves a discount ID from the code.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_by_code()
|
||
|
*
|
||
|
* @param string $code Discount code.
|
||
|
* @return int|bool Discount ID, or false if discount does not exist.
|
||
|
*/
|
||
|
function edd_get_discount_id_by_code( $code = '' ) {
|
||
|
$discount = edd_get_discount_by_code( $code );
|
||
|
|
||
|
return ( $discount instanceof EDD_Discount ) ? $discount->id : false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Discounted Amount.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_by_code()
|
||
|
*
|
||
|
* @param string $code Code to calculate a discount for.
|
||
|
* @param mixed string|int $base_price Price before discount.
|
||
|
* @return string Amount after discount.
|
||
|
*/
|
||
|
function edd_get_discounted_amount( $code = '', $base_price = 0 ) {
|
||
|
$discount = edd_get_discount_by_code( $code );
|
||
|
|
||
|
return ! empty( $discount->id )
|
||
|
? $discount->get_discounted_amount( $base_price )
|
||
|
: $base_price;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Increases the use count of a discount code.
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_by_code()
|
||
|
*
|
||
|
* @param string $code Discount code to be incremented.
|
||
|
* @return int New usage.
|
||
|
*/
|
||
|
function edd_increase_discount_usage( $code = '' ) {
|
||
|
$discount = edd_get_discount_by_code( $code );
|
||
|
|
||
|
// Increase if discount exists
|
||
|
return ! empty( $discount->id )
|
||
|
? (int) $discount->increase_usage()
|
||
|
: false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decreases the use count of a discount code.
|
||
|
*
|
||
|
* @since 2.5.7
|
||
|
* @since 2.7 Updated to use EDD_Discount object.
|
||
|
* @since 3.0 Updated to call edd_get_discount_by_code()
|
||
|
*
|
||
|
* @param string $code Discount code to be decremented.
|
||
|
* @return int New usage.
|
||
|
*/
|
||
|
function edd_decrease_discount_usage( $code = '' ) {
|
||
|
$discount = edd_get_discount_by_code( $code );
|
||
|
|
||
|
// Decrease if discount exists
|
||
|
return ! empty( $discount->id )
|
||
|
? (int) $discount->decrease_usage()
|
||
|
: false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Format Discount Rate
|
||
|
*
|
||
|
* @since 1.0
|
||
|
* @param string $type Discount code type
|
||
|
* @param string|int $amount Discount code amount
|
||
|
* @return string $amount Formatted amount
|
||
|
*/
|
||
|
function edd_format_discount_rate( $type = '', $amount = '' ) {
|
||
|
return ( 'flat' === $type )
|
||
|
? edd_currency_filter( edd_format_amount( $amount ) )
|
||
|
: edd_format_amount( $amount ) . '%';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves a discount amount for an item.
|
||
|
*
|
||
|
* Calculates an amount based on the context of other items.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @global float $edd_flat_discount_total Track flat rate discount total for penny adjustments.
|
||
|
* @link https://github.com/easydigitaldownloads/easy-digital-downloads/issues/2757
|
||
|
*
|
||
|
* @param array $item {
|
||
|
* Order Item data, matching Cart line item format.
|
||
|
*
|
||
|
* @type string $id Download ID.
|
||
|
* @type array $options {
|
||
|
* Download options.
|
||
|
*
|
||
|
* @type string $price_id Download Price ID.
|
||
|
* }
|
||
|
* @type int $quantity Purchase quantity.
|
||
|
* }
|
||
|
* @param array $items All items (including item being calculated).
|
||
|
* @param \EDD_Discount[]|string[] $discounts Discount to determine adjustment from.
|
||
|
* A discount code can be passed as a string.
|
||
|
* @param int $item_unit_price (Optional) Pass in a defined price for a specific context, such as the cart.
|
||
|
* @return float Discount amount. 0 if Discount is invalid or no Discount is applied.
|
||
|
*/
|
||
|
function edd_get_item_discount_amount( $item, $items, $discounts, $item_unit_price = false ) {
|
||
|
global $edd_flat_discount_total;
|
||
|
|
||
|
// Validate item.
|
||
|
if ( empty( $item ) || empty( $item['id'] ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ( ! isset( $item['quantity'] ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ( ! isset( $item['options'] ) ) {
|
||
|
$item['options'] = array();
|
||
|
|
||
|
/*
|
||
|
* Support for variable pricing when the `item_number` key is set (cart details).
|
||
|
*/
|
||
|
if ( isset( $item['item_number']['options'] ) ) {
|
||
|
$item['options'] = $item['item_number']['options'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Validate and normalize Discounts.
|
||
|
$discounts = array_map(
|
||
|
function( $discount ) {
|
||
|
// Convert a Discount code to a Discount object.
|
||
|
if ( is_string( $discount ) ) {
|
||
|
$discount = edd_get_discount_by_code( $discount );
|
||
|
}
|
||
|
|
||
|
if ( ! $discount instanceof \EDD_Discount ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return $discount;
|
||
|
},
|
||
|
$discounts
|
||
|
);
|
||
|
|
||
|
$discounts = array_filter( $discounts );
|
||
|
|
||
|
if ( false === $item_unit_price ) {
|
||
|
// Determine the price of the item.
|
||
|
if ( edd_has_variable_prices( $item['id'] ) ) {
|
||
|
// Mimics the original behavior of `\EDD_Cart::get_item_amount()` that
|
||
|
// does not fallback to the first Price ID if none is provided.
|
||
|
if ( ! isset( $item['options']['price_id'] ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
$item_unit_price = edd_get_price_option_amount( $item['id'], $item['options']['price_id'] );
|
||
|
} else {
|
||
|
$item_unit_price = edd_get_download_price( $item['id'] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$item_amount = ( $item_unit_price * $item['quantity'] );
|
||
|
$discount_amount = 0;
|
||
|
|
||
|
foreach ( $discounts as $discount ) {
|
||
|
$reqs = $discount->get_product_reqs();
|
||
|
$excluded_products = $discount->get_excluded_products();
|
||
|
|
||
|
// Make sure requirements are set and that this discount shouldn't apply to the whole cart.
|
||
|
if ( ! empty( $reqs ) && 'global' !== $discount->get_scope() ) {
|
||
|
// This is a product(s) specific discount.
|
||
|
foreach ( $reqs as $download_id ) {
|
||
|
if ( $download_id == $item['id'] && ! in_array( $item['id'], $excluded_products ) ) {
|
||
|
$discount_amount += ( $item_amount - $discount->get_discounted_amount( $item_amount ) );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// This is a global cart discount.
|
||
|
if ( ! in_array( $item['id'], $excluded_products ) ) {
|
||
|
if ( 'flat' === $discount->get_type() ) {
|
||
|
// In order to correctly record individual item amounts, global flat rate discounts
|
||
|
// are distributed across all items.
|
||
|
//
|
||
|
// The discount amount is divided by the number of items in the cart and then a
|
||
|
// portion is evenly applied to each item.
|
||
|
$items_amount = 0;
|
||
|
|
||
|
foreach ( $items as $i ) {
|
||
|
|
||
|
if ( ! in_array( $i['id'], $excluded_products ) ) {
|
||
|
$i_amount = 0;
|
||
|
|
||
|
if ( edd_has_variable_prices( $i['id'] ) ) {
|
||
|
$price_id = isset( $i['options']['price_id'] ) ? $i['options']['price_id'] : $i['item_number']['options']['price_id'];
|
||
|
$i_amount = edd_get_price_option_amount( $i['id'], $price_id );
|
||
|
} else {
|
||
|
$i_amount = edd_get_download_price( $i['id'] );
|
||
|
}
|
||
|
|
||
|
$items_amount += ( $i_amount * $i['quantity'] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$subtotal_percent = ! empty( $items_amount ) ? ( $item_amount / $items_amount ) : 0;
|
||
|
$discount_amount += ( $discount->get_amount() * $subtotal_percent );
|
||
|
|
||
|
$edd_flat_discount_total += round( $discount_amount, edd_currency_decimal_filter() );
|
||
|
|
||
|
if ( $item['id'] === end( $items )['id'] && $edd_flat_discount_total < $discount->get_amount() ) {
|
||
|
$adjustment = ( $discount->get_amount() - $edd_flat_discount_total );
|
||
|
$discount_amount += $adjustment;
|
||
|
}
|
||
|
|
||
|
if ( $discount_amount > $item_amount ) {
|
||
|
$discount_amount = $item_amount;
|
||
|
}
|
||
|
} else {
|
||
|
$discount_amount += ( $item_amount - $discount->get_discounted_amount( $item_amount ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $discount_amount;
|
||
|
}
|
||
|
|
||
|
/** Cart **********************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Set the active discount for the shopping cart
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @param string $code Discount code
|
||
|
* @return string[] All currently active discounts
|
||
|
*/
|
||
|
function edd_set_cart_discount( $code = '' ) {
|
||
|
|
||
|
// Get all active cart discounts
|
||
|
if ( edd_multiple_discounts_allowed() ) {
|
||
|
$discounts = edd_get_cart_discounts();
|
||
|
|
||
|
// Only one discount allowed per purchase, so override any existing
|
||
|
} else {
|
||
|
$discounts = false;
|
||
|
}
|
||
|
|
||
|
if ( $discounts ) {
|
||
|
$key = array_search( strtolower( $code ), array_map( 'strtolower', $discounts ) );
|
||
|
|
||
|
// Can't set the same discount more than once
|
||
|
if ( false !== $key ) {
|
||
|
unset( $discounts[ $key ] );
|
||
|
}
|
||
|
$discounts[] = $code;
|
||
|
} else {
|
||
|
$discounts = array();
|
||
|
$discounts[] = $code;
|
||
|
}
|
||
|
|
||
|
EDD()->session->set( 'cart_discounts', implode( '|', $discounts ) );
|
||
|
|
||
|
do_action( 'edd_cart_discount_set', $code, $discounts );
|
||
|
do_action( 'edd_cart_discounts_updated', $discounts );
|
||
|
|
||
|
return $discounts;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove an active discount from the shopping cart
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @param string $code Discount code
|
||
|
* @return array $discounts All remaining active discounts
|
||
|
*/
|
||
|
function edd_unset_cart_discount( $code = '' ) {
|
||
|
$discounts = edd_get_cart_discounts();
|
||
|
|
||
|
if ( $discounts ) {
|
||
|
$discounts = array_map( 'strtoupper', $discounts );
|
||
|
$key = array_search( strtoupper( $code ), $discounts );
|
||
|
|
||
|
if ( false !== $key ) {
|
||
|
unset( $discounts[ $key ] );
|
||
|
}
|
||
|
|
||
|
$discounts = implode( '|', array_values( $discounts ) );
|
||
|
// update the active discounts
|
||
|
EDD()->session->set( 'cart_discounts', $discounts );
|
||
|
}
|
||
|
|
||
|
do_action( 'edd_cart_discount_removed', $code, $discounts );
|
||
|
do_action( 'edd_cart_discounts_updated', $discounts );
|
||
|
|
||
|
return $discounts;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove all active discounts
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @return void
|
||
|
*/
|
||
|
function edd_unset_all_cart_discounts() {
|
||
|
EDD()->cart->remove_all_discounts();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the currently applied discount
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @return array $discounts The active discount codes
|
||
|
*/
|
||
|
function edd_get_cart_discounts() {
|
||
|
return EDD()->cart->get_discounts();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the cart has any active discounts applied to it
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @return bool
|
||
|
*/
|
||
|
function edd_cart_has_discounts() {
|
||
|
return EDD()->cart->has_discounts();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the total discounted amount on the cart
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
*
|
||
|
* @param bool $discounts Discount codes
|
||
|
*
|
||
|
* @return float|mixed|void Total discounted amount
|
||
|
*/
|
||
|
function edd_get_cart_discounted_amount( $discounts = false ) {
|
||
|
return EDD()->cart->get_discounted_amount( $discounts );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the discounted amount on a price
|
||
|
*
|
||
|
* @since 1.9
|
||
|
* @param array $item Cart item array
|
||
|
* @param bool|string $discount False to use the cart discounts or a string to check with a discount code
|
||
|
* @return float The discounted amount
|
||
|
*/
|
||
|
function edd_get_cart_item_discount_amount( $item = array(), $discount = false ) {
|
||
|
return EDD()->cart->get_item_discount_amount( $item, $discount );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Outputs the HTML for all discounts applied to the cart
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
function edd_cart_discounts_html() {
|
||
|
echo edd_get_cart_discounts_html();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the HTML for all discounts applied to the cart
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
*
|
||
|
* @param mixed $discounts Array of cart discounts.
|
||
|
* @return mixed|void
|
||
|
*/
|
||
|
function edd_get_cart_discounts_html( $discounts = false ) {
|
||
|
if ( ! $discounts ) {
|
||
|
$discounts = EDD()->cart->get_discounts();
|
||
|
}
|
||
|
|
||
|
if ( empty( $discounts ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$html = _n( 'Discount', 'Discounts', count( $discounts ), 'easy-digital-downloads' ) . ': ';
|
||
|
|
||
|
foreach ( $discounts as $discount ) {
|
||
|
$discount_id = edd_get_discount_id_by_code( $discount );
|
||
|
$discount_amount = 0;
|
||
|
$items = EDD()->cart->get_contents_details();
|
||
|
|
||
|
if ( is_array( $items ) && ! empty( $items ) ) {
|
||
|
foreach ( $items as $key => $item ) {
|
||
|
$discount_amount += edd_get_item_discount_amount( $item, $items, array( $discount ), $item['item_price'] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$type = edd_get_discount_type( $discount_id );
|
||
|
$rate = edd_format_discount_rate( $type, edd_get_discount_amount( $discount_id ) );
|
||
|
|
||
|
$remove_url = add_query_arg(
|
||
|
array(
|
||
|
'edd_action' => 'remove_cart_discount',
|
||
|
'discount_id' => urlencode( $discount_id ),
|
||
|
'discount_code' => urlencode( $discount ),
|
||
|
),
|
||
|
edd_get_checkout_uri()
|
||
|
);
|
||
|
|
||
|
$discount_html = '';
|
||
|
$discount_html .= "<span class=\"edd_discount\">\n";
|
||
|
$discount_amount = edd_currency_filter( edd_format_amount( $discount_amount ) );
|
||
|
$discount_html .= "<span class=\"edd_discount_total\">{$discount} – {$discount_amount}</span>\n";
|
||
|
if ( 'percent' === $type ) {
|
||
|
$discount_html .= "<span class=\"edd_discount_rate\">($rate)</span>\n";
|
||
|
}
|
||
|
$discount_html .= sprintf(
|
||
|
'<a href="%s" data-code="%s" class="edd_discount_remove"><span class="screen-reader-text">%s</span></a>',
|
||
|
esc_url( $remove_url ),
|
||
|
esc_attr( $discount ),
|
||
|
esc_attr__( 'Remove discount', 'easy-digital-downloads' )
|
||
|
);
|
||
|
$discount_html .= "</span>\n";
|
||
|
|
||
|
$html .= apply_filters( 'edd_get_cart_discount_html', $discount_html, $discount, $rate, $remove_url );
|
||
|
}
|
||
|
|
||
|
return apply_filters( 'edd_get_cart_discounts_html', $html, $discounts, $rate, $remove_url );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Show the fully formatted cart discount
|
||
|
*
|
||
|
* Note the $formatted parameter was removed from the display_cart_discount() function
|
||
|
* within EDD_Cart in 2.7 as it was a redundant parameter.
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @param bool $formatted
|
||
|
* @param bool $echo Echo?
|
||
|
* @return string $amount Fully formatted cart discount
|
||
|
*/
|
||
|
function edd_display_cart_discount( $formatted = false, $echo = false ) {
|
||
|
if ( ! $echo ) {
|
||
|
return EDD()->cart->display_cart_discount( $echo );
|
||
|
} else {
|
||
|
EDD()->cart->display_cart_discount( $echo );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Processes a remove discount from cart request
|
||
|
*
|
||
|
* @since 1.4.1
|
||
|
* @return void
|
||
|
*/
|
||
|
function edd_remove_cart_discount() {
|
||
|
|
||
|
// Get ID
|
||
|
$discount_id = isset( $_GET['discount_id'] )
|
||
|
? absint( $_GET['discount_id'] )
|
||
|
: 0;
|
||
|
|
||
|
// Get code
|
||
|
$discount_code = isset( $_GET['discount_code'] )
|
||
|
? urldecode( $_GET['discount_code'] )
|
||
|
: '';
|
||
|
|
||
|
// Bail if either ID or code are empty
|
||
|
if ( empty( $discount_id ) || empty( $discount_code ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Pre-3.0 pre action
|
||
|
do_action( 'edd_pre_remove_cart_discount', $discount_id );
|
||
|
|
||
|
edd_unset_cart_discount( $discount_code );
|
||
|
|
||
|
// Pre-3.0 post action
|
||
|
do_action( 'edd_post_remove_cart_discount', $discount_id );
|
||
|
|
||
|
// Redirect
|
||
|
edd_redirect( edd_get_checkout_uri() );
|
||
|
}
|
||
|
add_action( 'edd_remove_cart_discount', 'edd_remove_cart_discount' );
|
||
|
|
||
|
/**
|
||
|
* Checks whether discounts are still valid when removing items from the cart
|
||
|
*
|
||
|
* If a discount requires a certain product, and that product is no longer in
|
||
|
* the cart, the discount is removed.
|
||
|
*
|
||
|
* @since 1.5.2
|
||
|
*
|
||
|
* @param int $cart_key
|
||
|
*/
|
||
|
function edd_maybe_remove_cart_discount( $cart_key = 0 ) {
|
||
|
|
||
|
$discounts = edd_get_cart_discounts();
|
||
|
|
||
|
if ( empty( $discounts ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
foreach ( $discounts as $discount ) {
|
||
|
if ( ! edd_is_discount_valid( $discount ) ) {
|
||
|
edd_unset_cart_discount( $discount );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
add_action( 'edd_post_remove_from_cart', 'edd_maybe_remove_cart_discount' );
|
||
|
|
||
|
/**
|
||
|
* Checks whether multiple discounts can be applied to the same purchase
|
||
|
*
|
||
|
* @since 1.7
|
||
|
* @return bool
|
||
|
*/
|
||
|
function edd_multiple_discounts_allowed() {
|
||
|
$ret = edd_get_option( 'allow_multiple_discounts', false );
|
||
|
return (bool) apply_filters( 'edd_multiple_discounts_allowed', $ret );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Listens for a discount and automatically applies it if present and valid
|
||
|
*
|
||
|
* @since 2.0
|
||
|
* @return void
|
||
|
*/
|
||
|
function edd_listen_for_cart_discount() {
|
||
|
|
||
|
// Bail if in admin
|
||
|
if ( is_admin() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Array stops the bulk delete of discount codes from storing as a preset_discount
|
||
|
if ( empty( $_REQUEST['discount'] ) || is_array( $_REQUEST['discount'] ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$code = preg_replace('/[^a-zA-Z0-9-_]+/', '', $_REQUEST['discount'] );
|
||
|
|
||
|
EDD()->session->set( 'preset_discount', $code );
|
||
|
}
|
||
|
add_action( 'init', 'edd_listen_for_cart_discount', 0 );
|
||
|
|
||
|
/**
|
||
|
* Applies the preset discount, if any. This is separated from edd_listen_for_cart_discount() in order to allow items to be
|
||
|
* added to the cart and for it to persist across page loads if necessary
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
function edd_apply_preset_discount() {
|
||
|
|
||
|
// Bail if in admin
|
||
|
if ( is_admin() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$code = sanitize_text_field( EDD()->session->get( 'preset_discount' ) );
|
||
|
|
||
|
if ( empty( $code ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( ! edd_is_discount_valid( $code, '', false ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$code = apply_filters( 'edd_apply_preset_discount', $code );
|
||
|
|
||
|
edd_set_cart_discount( $code );
|
||
|
|
||
|
EDD()->session->set( 'preset_discount', null );
|
||
|
}
|
||
|
add_action( 'init', 'edd_apply_preset_discount', 999 );
|
||
|
|
||
|
/**
|
||
|
* Validate discount code, optionally against an array of download IDs.
|
||
|
* Note: this function does not evaluate whether a current user can use the discount,
|
||
|
* or check the discount minimum cart requirement.
|
||
|
*
|
||
|
* @param int $discount_id Discount ID.
|
||
|
* @param array $download_ids Array of download IDs.
|
||
|
*
|
||
|
* @return boolean True if discount holds, false otherwise.
|
||
|
*/
|
||
|
function edd_validate_discount( $discount_id = 0, $download_ids = array() ) {
|
||
|
|
||
|
// Bail if discount ID not passed.
|
||
|
if ( empty( $discount_id ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$discount = edd_get_discount( $discount_id );
|
||
|
|
||
|
// Bail if discount not found.
|
||
|
if ( ! $discount ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Check if discount is active, started, and not maxed out.
|
||
|
if ( ! $discount->is_active( true, false ) || ! $discount->is_started( false ) || $discount->is_maxed_out( false ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$product_requirements = $discount->get_product_reqs();
|
||
|
$excluded_products = $discount->get_excluded_products();
|
||
|
|
||
|
// Return true if there are no requirements/excluded products set.
|
||
|
if ( empty( $product_requirements ) && empty( $excluded_products ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// At this point, we assume the discount is valid.
|
||
|
$is_valid = true;
|
||
|
|
||
|
$product_requirements = array_map( 'absint', $product_requirements );
|
||
|
asort( $product_requirements );
|
||
|
$product_requirements = array_filter( array_values( $product_requirements ) );
|
||
|
|
||
|
if ( ! empty( $product_requirements ) ) {
|
||
|
|
||
|
$matches = array_intersect( $product_requirements, $download_ids );
|
||
|
|
||
|
switch ( $discount->get_product_condition() ) {
|
||
|
case 'all':
|
||
|
$is_valid = count( $matches ) === count( $product_requirements );
|
||
|
break;
|
||
|
default:
|
||
|
$is_valid = 0 < count( $matches );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$excluded_products = array_map( 'absint', $excluded_products );
|
||
|
asort( $excluded_products );
|
||
|
$excluded_products = array_filter( array_values( $excluded_products ) );
|
||
|
|
||
|
if ( ! empty( $excluded_products ) ) {
|
||
|
$is_valid = false === (bool) array_intersect( $excluded_products, $download_ids );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filters the validity of a discount.
|
||
|
*
|
||
|
* @since 3.0
|
||
|
*
|
||
|
* @param bool $is_valid True if valid, false otherwise.
|
||
|
* @param \EDD_Discount $discount Discount object.
|
||
|
* @param array $download_ids Download IDs to check against.
|
||
|
*/
|
||
|
return apply_filters( 'edd_validate_discount', $is_valid, $discount, $download_ids );
|
||
|
}
|