Version Description
- (PRO) FEATURE: Allow customer to choose a free product
- (PRO) FEATURE: Setting the priority of auto coupons (Useful for Individual use coupons)
- (PRO) FEATURE: Display extra columns on the Coupon Admin page (auto coupon, individual use, priority, free products)
- (PRO) TWEAK: Free products: Display 'Free!' as subtotal for free products, (adaptable with filter 'wjecf_free_cart_item_subtotal' )
- (PRO) FIX: Free products: Plugin wouldn't always detect the free products in cart and kept appending free products
- (PRO) Introduction of the API for developers, see wjecf-pro-api.php
- FEATURE: Filter to only display Auto Coupons on the Coupon Admin page
- FIX: Compatibilty PHP 5.4
- FIX: Rewritten and simplified Autocoupon removal/addition routine making it more robust
- FIX: Multiplier value calculation (as for now only used for Free Products)
- FIX: Coupon must never be valid for free products (wjecf_free_product_coupon set in cartitem)
- INTERNAL: Refactoring of all classes
- INTERNAL: New log for debugging
Download this release
Release Info
Developer | josk79 |
Plugin | WooCommerce Extended Coupon Features |
Version | 2.3.0 |
Comparing to | |
See all releases |
Code changes from version 2.2.5.1 to 2.3.0
- assets/screenshot-1.png +0 -0
- assets/screenshot-2.png +0 -0
- assets/screenshot-3.png +0 -0
- assets/screenshot-4.png +0 -0
- assets/wjecf.css +18 -0
- includes/abstract-wjecf-plugin.php +13 -0
- includes/{wjecf-coupon-extensions.php → admin/wjecf-admin.php} +13 -450
- includes/wjecf-autocoupon.php +232 -88
- includes/wjecf-controller.php +609 -0
- readme.txt +20 -3
- woocommerce-jos-autocoupon.php +34 -31
assets/screenshot-1.png
DELETED
Binary file
|
assets/screenshot-2.png
DELETED
Binary file
|
assets/screenshot-3.png
DELETED
Binary file
|
assets/screenshot-4.png
DELETED
Binary file
|
assets/wjecf.css
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
WJECF columns system
|
3 |
+
*/
|
4 |
+
ul.wjecf-cols { list-style-type: none;margin:0}
|
5 |
+
ul.wjecf-cols > li { float:left;margin-right:3%;margin-left:0; }
|
6 |
+
.woocommerce-cart table.cart ul.wjecf-cols > li > img { width:auto; }
|
7 |
+
|
8 |
+
ul.wjecf-cols.cols-2 li { width:48.5%; }
|
9 |
+
ul.wjecf-cols.cols-3 li { width:31.3%; }
|
10 |
+
ul.wjecf-cols.cols-4 li { width:22.7%; }
|
11 |
+
ul.wjecf-cols.cols-5 li { width:17.6%; }
|
12 |
+
ul.wjecf-cols.cols-6 li { width:14.1%; }
|
13 |
+
|
14 |
+
ul.wjecf-cols.cols-2 li:nth-child(2n+2),
|
15 |
+
ul.wjecf-cols.cols-3 li:nth-child(3n+3),
|
16 |
+
ul.wjecf-cols.cols-4 li:nth-child(4n+4),
|
17 |
+
ul.wjecf-cols.cols-5 li:nth-child(5n+5),
|
18 |
+
ul.wjecf-cols.cols-6 li:nth-child(6n+6) { margin-right:0!important; }
|
includes/abstract-wjecf-plugin.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Abstract_WJECF_Plugin {
|
4 |
+
/**
|
5 |
+
* Log a message (for debugging)
|
6 |
+
*
|
7 |
+
* @param string $message The message to log
|
8 |
+
*
|
9 |
+
*/
|
10 |
+
protected function log ( $message ) {
|
11 |
+
WJECF()->log( $message, 1 );
|
12 |
+
}
|
13 |
+
}
|
includes/{wjecf-coupon-extensions.php → admin/wjecf-admin.php}
RENAMED
@@ -2,103 +2,25 @@
|
|
2 |
|
3 |
defined('ABSPATH') or die();
|
4 |
|
5 |
-
if ( class_exists('
|
6 |
return;
|
7 |
}
|
8 |
|
9 |
-
class
|
10 |
-
private $options = false;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Singleton Instance
|
14 |
-
*
|
15 |
-
* @static
|
16 |
-
* @return Singleton Instance
|
17 |
-
*/
|
18 |
-
public static function instance() {
|
19 |
-
if ( is_null( self::$_instance ) ) {
|
20 |
-
self::$_instance = new self();
|
21 |
-
}
|
22 |
-
return self::$_instance;
|
23 |
-
}
|
24 |
-
protected static $_instance = null;
|
25 |
-
|
26 |
|
27 |
public function __construct() {
|
28 |
-
|
29 |
-
}
|
30 |
-
|
31 |
-
public function controller_init() {
|
32 |
-
if ( ! class_exists('WC_Coupon') ) {
|
33 |
-
return;
|
34 |
-
}
|
35 |
-
|
36 |
-
$this->init_options();
|
37 |
-
|
38 |
-
//Admin hooks
|
39 |
-
add_action( 'admin_init', array( &$this, 'admin_init' ) );
|
40 |
-
|
41 |
-
//Frontend hooks
|
42 |
-
add_filter('woocommerce_coupon_is_valid', array( &$this, 'coupon_is_valid' ), 10, 2 );
|
43 |
-
add_action('woocommerce_coupon_loaded', array( $this, 'woocommerce_coupon_loaded' ), 10, 1);
|
44 |
-
add_filter('woocommerce_coupon_get_discount_amount', array( $this, 'woocommerce_coupon_get_discount_amount' ), 10, 5);
|
45 |
-
}
|
46 |
-
|
47 |
-
public function init_options() {
|
48 |
-
$this->options = get_option( 'wjecf_options' );
|
49 |
-
if (false === $this->options) {
|
50 |
-
$this->options = array( 'db_version' => 0 );
|
51 |
-
}
|
52 |
-
}
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Check whether WooCommerce version is greater or equal than $req_version
|
56 |
-
* @param string @req_version The version to compare to
|
57 |
-
* @return bool true if WooCommerce is at least the given version
|
58 |
-
*/
|
59 |
-
public static function check_woocommerce_version( $req_version ) {
|
60 |
-
return version_compare( self::get_woocommerce_version(), $req_version, '>=' );
|
61 |
}
|
62 |
|
63 |
-
private static $wc_version = null;
|
64 |
-
/**
|
65 |
-
* Get the WooCommerce version number
|
66 |
-
* @return string|bool WC Version number or false if WC not detected
|
67 |
-
*/
|
68 |
-
public static function get_woocommerce_version() {
|
69 |
-
if (self::$wc_version === null) {
|
70 |
-
// If get_plugins() isn't available, require it
|
71 |
-
if ( ! function_exists( 'get_plugins' ) ) {
|
72 |
-
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
73 |
-
}
|
74 |
-
|
75 |
-
// Create the plugins folder and file variables
|
76 |
-
$plugin_folder = get_plugins( '/woocommerce' );
|
77 |
-
$plugin_file = 'woocommerce.php';
|
78 |
-
|
79 |
-
// If the plugin version number is set, return it
|
80 |
-
if ( isset( $plugin_folder[$plugin_file]['Version'] ) ) {
|
81 |
-
self::$wc_version = $plugin_folder[$plugin_file]['Version'];
|
82 |
-
} else {
|
83 |
-
self::$wc_version = false; // Not found
|
84 |
-
}
|
85 |
-
|
86 |
-
//echo "<pre>";var_dump( get_site_option( 'active_sitewide_plugins' ) );die();
|
87 |
-
//echo "<pre>";var_dump( get_plugins() );die();
|
88 |
-
//die(self::$wc_version);
|
89 |
-
}
|
90 |
-
return self::$wc_version;
|
91 |
-
}
|
92 |
-
|
93 |
//Upgrade options on version change
|
94 |
public function auto_upgrade_options() {
|
95 |
$current_db_version = 1;
|
96 |
//Check version
|
97 |
-
if (
|
98 |
global $wpdb;
|
99 |
|
100 |
//DB_VERSION 1: Since 2.1.0-b5
|
101 |
-
if (
|
102 |
//RENAME meta_key _wjecf_matching_product_qty TO _wjecf_min_matching_product_qty
|
103 |
$where = array("meta_key" => "_wjecf_matching_product_qty");
|
104 |
$set = array('meta_key' => "_wjecf_min_matching_product_qty");
|
@@ -109,11 +31,11 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
109 |
$set = array('meta_key' => "_wjecf_is_auto_coupon");
|
110 |
$wpdb->update( _get_meta_table('post'), $set, $where );
|
111 |
//Now we're version 1
|
112 |
-
|
113 |
}
|
114 |
|
115 |
// Write options to database
|
116 |
-
update_option( 'wjecf_options' ,
|
117 |
}
|
118 |
}
|
119 |
|
@@ -166,7 +88,7 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
166 |
}
|
167 |
private function admin_coupon_data_footer() {
|
168 |
$documentation_url = plugins_url( 'docs/index.html', dirname( __FILE__ ) );
|
169 |
-
if ( !
|
170 |
$documentation_url = 'http://www.soft79.nl/documentation/wjecf';
|
171 |
?>
|
172 |
<h3><?php _e( 'Do you find WooCommerce Extended Coupon Features useful?', 'woocommerce-jos-autocoupon'); ?></h3>
|
@@ -313,7 +235,7 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
313 |
<p class="form-field"><label for="wjecf_shipping_methods"><?php _e( 'Shipping methods', 'woocommerce-jos-autocoupon' ); ?></label>
|
314 |
<select id="wjecf_shipping_methods" name="wjecf_shipping_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any shipping method', 'woocommerce-jos-autocoupon' ); ?>">
|
315 |
<?php
|
316 |
-
$coupon_shipping_method_ids =
|
317 |
$shipping_methods = WC()->shipping->load_shipping_methods();
|
318 |
|
319 |
if ( $shipping_methods ) foreach ( $shipping_methods as $shipping_method ) {
|
@@ -329,7 +251,7 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
329 |
<p class="form-field"><label for="wjecf_payment_methods"><?php _e( 'Payment methods', 'woocommerce-jos-autocoupon' ); ?></label>
|
330 |
<select id="wjecf_payment_methods" name="wjecf_payment_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any payment method', 'woocommerce-jos-autocoupon' ); ?>">
|
331 |
<?php
|
332 |
-
$coupon_payment_method_ids =
|
333 |
//DONT USE WC()->payment_gateways->available_payment_gateways() AS IT CAN CRASH IN UNKNOWN OCCASIONS
|
334 |
$payment_methods = WC()->payment_gateways->payment_gateways();
|
335 |
if ( $payment_methods ) foreach ( $payment_methods as $payment_method ) {
|
@@ -354,7 +276,7 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
354 |
?>
|
355 |
<p class="form-field"><label><?php _e( 'Allowed Customers', 'woocommerce-jos-autocoupon' ); ?></label>
|
356 |
<input type="hidden" class="wc-customer-search" data-multiple="true" style="width: 50%;" name="wjecf_customer_ids" data-placeholder="<?php _e( 'Any customer', 'woocommerce-jos-autocoupon' ); ?>" data-action="woocommerce_json_search_customers" data-selected="<?php
|
357 |
-
$coupon_customer_ids =
|
358 |
$json_ids = array();
|
359 |
|
360 |
foreach ( $coupon_customer_ids as $customer_id ) {
|
@@ -376,7 +298,7 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
376 |
<p class="form-field"><label for="wjecf_customer_roles"><?php _e( 'Allowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
|
377 |
<select id="wjecf_customer_roles" name="wjecf_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
|
378 |
<?php
|
379 |
-
$coupon_customer_roles =
|
380 |
|
381 |
$available_customer_roles = array_reverse( get_editable_roles() );
|
382 |
foreach ( $available_customer_roles as $role_id => $role ) {
|
@@ -398,7 +320,7 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
398 |
<p class="form-field"><label for="wjecf_excluded_customer_roles"><?php _e( 'Disallowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
|
399 |
<select id="wjecf_customer_roles" name="wjecf_excluded_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
|
400 |
<?php
|
401 |
-
$coupon_excluded_customer_roles =
|
402 |
|
403 |
foreach ( $available_customer_roles as $role_id => $role ) {
|
404 |
$role_name = translate_user_role($role['name'] );
|
@@ -467,365 +389,6 @@ class WC_Jos_Extended_Coupon_Features_Controller {
|
|
467 |
|
468 |
}
|
469 |
|
470 |
-
/* FRONTEND HOOKS */
|
471 |
-
|
472 |
-
//2.2.2
|
473 |
-
function woocommerce_coupon_loaded ( $coupon ) {
|
474 |
-
if ( ! is_admin() ) {
|
475 |
-
//2.2.2 Allow coupon even if minimum spend not reached
|
476 |
-
if ( get_post_meta( $coupon->id, '_wjecf_allow_below_minimum_spend', true ) == 'yes' ) {
|
477 |
-
//HACK: Overwrite the minimum amount with 0 so WooCommerce will allow the coupon
|
478 |
-
$coupon->wjecf_minimum_amount_for_discount = $coupon->minimum_amount;
|
479 |
-
$coupon->minimum_amount = 0;
|
480 |
-
}
|
481 |
-
}
|
482 |
-
}
|
483 |
-
|
484 |
-
//2.2.2
|
485 |
-
function woocommerce_coupon_get_discount_amount ( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
|
486 |
-
//2.2.2 No value if minimum spend not reached
|
487 |
-
if (isset( $coupon->wjecf_minimum_amount_for_discount ) ) {
|
488 |
-
if ( wc_format_decimal( $coupon->wjecf_minimum_amount_for_discount ) > wc_format_decimal( WC()->cart->subtotal ) ) {
|
489 |
-
return 0;
|
490 |
-
};
|
491 |
-
}
|
492 |
-
return $discount;
|
493 |
-
}
|
494 |
-
|
495 |
-
|
496 |
-
/* Extra validation rules for coupons */
|
497 |
-
function coupon_is_valid ( $valid, $coupon ) {
|
498 |
-
//Not valid? Then it will never validate, so get out of here
|
499 |
-
if ( ! $valid ) {
|
500 |
-
return false;
|
501 |
-
}
|
502 |
-
|
503 |
-
//============================
|
504 |
-
//Test if ALL products are in the cart (if AND-operator selected in stead of the default OR)
|
505 |
-
$products_and = get_post_meta( $coupon->id, '_wjecf_products_and', true ) == 'yes';
|
506 |
-
if ( $products_and && sizeof( $coupon->product_ids ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
|
507 |
-
//Get array of all cart product and variation ids
|
508 |
-
$cart_item_ids = array();
|
509 |
-
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
510 |
-
$cart_item_ids[] = $cart_item['product_id'];
|
511 |
-
$cart_item_ids[] = $cart_item['variation_id'];
|
512 |
-
}
|
513 |
-
//check if every single product is in the cart
|
514 |
-
foreach( $coupon->product_ids as $product_id ) {
|
515 |
-
if ( ! in_array( $product_id, $cart_item_ids ) ) {
|
516 |
-
return false;
|
517 |
-
}
|
518 |
-
}
|
519 |
-
}
|
520 |
-
|
521 |
-
//============================
|
522 |
-
//Test if products form ALL categories are in the cart (if AND-operator selected in stead of the default OR)
|
523 |
-
$categories_and = get_post_meta( $coupon->id, '_wjecf_categories_and', true ) == 'yes';
|
524 |
-
if ( $categories_and && sizeof( $coupon->product_categories ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
|
525 |
-
//Get array of all cart product and variation ids
|
526 |
-
$cart_product_cats = array();
|
527 |
-
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
528 |
-
$cart_product_cats = array_merge ( $cart_product_cats, wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) ) );
|
529 |
-
}
|
530 |
-
//check if every single category is in the cart
|
531 |
-
foreach( $coupon->product_categories as $cat_id ) {
|
532 |
-
if ( ! in_array( $cat_id, $cart_product_cats ) ) {
|
533 |
-
return false;
|
534 |
-
}
|
535 |
-
}
|
536 |
-
|
537 |
-
}
|
538 |
-
|
539 |
-
//============================
|
540 |
-
//Test min/max quantity of matching products
|
541 |
-
//
|
542 |
-
//For all items in the cart:
|
543 |
-
// If coupon contains both a product AND category inclusion filter: the item is counted if it matches either one of them
|
544 |
-
// If coupon contains either a product OR category exclusion filter: the item will NOT be counted if it matches either one of them
|
545 |
-
// If sale items are excluded by the coupon: the item will NOT be counted if it is a sale item
|
546 |
-
// If no filter exist, all items will be counted
|
547 |
-
|
548 |
-
$multiplier = null; //null = not initialized
|
549 |
-
|
550 |
-
//Validate quantity
|
551 |
-
$min_matching_product_qty = intval(get_post_meta( $coupon->id, '_wjecf_min_matching_product_qty', true ));
|
552 |
-
$max_matching_product_qty = intval(get_post_meta( $coupon->id, '_wjecf_max_matching_product_qty', true ));
|
553 |
-
if ( $min_matching_product_qty > 0 || $max_matching_product_qty > 0 ) {
|
554 |
-
//Count the products
|
555 |
-
$qty = $this->get_quantity_of_matching_products( $coupon );
|
556 |
-
if ($min_matching_product_qty > 0 && $qty < $min_matching_product_qty) return false;
|
557 |
-
if ($max_matching_product_qty > 0 && $qty > $max_matching_product_qty) return false;
|
558 |
-
$multiplier = self::min_value( floor( $qty / $min_matching_product_qty ), $multiplier );
|
559 |
-
}
|
560 |
-
|
561 |
-
//Validate subtotal (2.2.2)
|
562 |
-
$min_matching_product_subtotal = intval(get_post_meta( $coupon->id, '_wjecf_min_matching_product_subtotal', true ));
|
563 |
-
$max_matching_product_subtotal = intval(get_post_meta( $coupon->id, '_wjecf_max_matching_product_subtotal', true ));
|
564 |
-
if ( $min_matching_product_subtotal > 0 || $max_matching_product_subtotal > 0 ) {
|
565 |
-
$subtotal = $this->get_subtotal_of_matching_products( $coupon );
|
566 |
-
if ($min_matching_product_subtotal > 0 && $subtotal < $min_matching_product_subtotal) return false;
|
567 |
-
if ($max_matching_product_subtotal > 0 && $subtotal > $max_matching_product_subtotal) return false;
|
568 |
-
$multiplier = self::min_value( floor( $subtotal / $min_matching_product_subtotal ), $multiplier );
|
569 |
-
}
|
570 |
-
|
571 |
-
//============================
|
572 |
-
//Test restricted shipping methods
|
573 |
-
$shipping_method_ids = $this->get_coupon_shipping_method_ids( $coupon->id );
|
574 |
-
if ( sizeof( $shipping_method_ids ) > 0 ) {
|
575 |
-
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
|
576 |
-
$chosen_shipping = $chosen_shipping_methods[0];
|
577 |
-
|
578 |
-
if ( ! in_array( $chosen_shipping, $shipping_method_ids ) ) {
|
579 |
-
return false;
|
580 |
-
}
|
581 |
-
}
|
582 |
-
|
583 |
-
//============================
|
584 |
-
//Test restricted payment methods
|
585 |
-
$payment_method_ids = $this->get_coupon_payment_method_ids( $coupon->id );
|
586 |
-
if ( sizeof( $payment_method_ids ) > 0 ) {
|
587 |
-
$chosen_payment_method = isset( WC()->session->chosen_payment_method ) ? WC()->session->chosen_payment_method : array();
|
588 |
-
|
589 |
-
if ( ! in_array( $chosen_payment_method, $payment_method_ids ) ) {
|
590 |
-
return false;
|
591 |
-
}
|
592 |
-
}
|
593 |
-
|
594 |
-
|
595 |
-
//============================
|
596 |
-
//Test restricted user ids and roles
|
597 |
-
//NOTE: If both customer id and role restrictions are provided, the coupon matches if either the id or the role matches
|
598 |
-
$coupon_customer_ids = $this->get_coupon_customer_ids( $coupon->id );
|
599 |
-
$coupon_customer_roles = $this->get_coupon_customer_roles( $coupon->id );
|
600 |
-
if ( sizeof( $coupon_customer_ids ) > 0 || sizeof( $coupon_customer_roles ) > 0 ) {
|
601 |
-
$user = wp_get_current_user();
|
602 |
-
|
603 |
-
//If both fail we invalidate. Otherwise it's ok
|
604 |
-
if ( ! in_array( $user->ID, $coupon_customer_ids ) && ! array_intersect( $user->roles, $coupon_customer_roles ) ) {
|
605 |
-
return false;
|
606 |
-
}
|
607 |
-
}
|
608 |
-
|
609 |
-
//============================
|
610 |
-
//Test excluded user roles
|
611 |
-
$coupon_excluded_customer_roles = $this->get_coupon_excluded_customer_roles( $coupon->id );
|
612 |
-
if ( sizeof( $coupon_excluded_customer_roles ) > 0 ) {
|
613 |
-
$user = wp_get_current_user();
|
614 |
-
|
615 |
-
//Excluded customer roles
|
616 |
-
if ( array_intersect( $user->roles, $coupon_excluded_customer_roles ) ) {
|
617 |
-
return false;
|
618 |
-
}
|
619 |
-
}
|
620 |
-
|
621 |
-
if ( $coupon->minimum_amount ) {
|
622 |
-
$multiplier = self::min_value( floor( WC()->cart->subtotal / $coupon->minimum_amount ), $multiplier );
|
623 |
-
}
|
624 |
-
|
625 |
-
|
626 |
-
$this->coupon_multiplier_values[ $coupon->code ] = $multiplier;
|
627 |
-
|
628 |
-
return true;
|
629 |
-
}
|
630 |
-
|
631 |
-
/**
|
632 |
-
* Return the lowest multiplier value
|
633 |
-
*/
|
634 |
-
private static function min_value( $value, $current_multiplier_value = null ) {
|
635 |
-
return ( $current_multiplier_value === null || $value < $current_multiplier_value ) ? $value : $current_multiplier_value;
|
636 |
-
}
|
637 |
-
|
638 |
-
/**
|
639 |
-
* The amount of times the minimum spend / quantity / subtotal values are reached
|
640 |
-
* @return int 1 or more
|
641 |
-
*/
|
642 |
-
public function get_coupon_multiplier_value( $coupon ) {
|
643 |
-
$coupon = $this->get_coupon( $coupon );
|
644 |
-
|
645 |
-
if ( ! isset( $this->coupon_multiplier_values[ $coupon->code ] ) || ! $this->coupon_multiplier_values[ $coupon->code ] ) {
|
646 |
-
return 1;
|
647 |
-
}
|
648 |
-
|
649 |
-
return $this->coupon_multiplier_values[ $coupon->code ];
|
650 |
-
}
|
651 |
-
|
652 |
-
//Temporary storage
|
653 |
-
private $coupon_multiplier_values = array();
|
654 |
-
|
655 |
-
|
656 |
-
/**
|
657 |
-
* PUBLIC USAGE OF THIS FUNCTION IS ALLOWED!
|
658 |
-
* The total amount of the products in the cart that match the coupon restrictions
|
659 |
-
* since 2.2.2-b3
|
660 |
-
*/
|
661 |
-
public function get_quantity_of_matching_products( $coupon ) {
|
662 |
-
$coupon = $this->get_coupon( $coupon );
|
663 |
-
|
664 |
-
$qty = 0;
|
665 |
-
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
666 |
-
$_product = $cart_item['data'];
|
667 |
-
if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
|
668 |
-
$qty += $cart_item['quantity'];
|
669 |
-
}
|
670 |
-
}
|
671 |
-
return $qty;
|
672 |
-
}
|
673 |
-
|
674 |
-
/**
|
675 |
-
* PUBLIC USAGE OF THIS FUNCTION IS ALLOWED!
|
676 |
-
* The total value of the products in the cart that match the coupon restrictions
|
677 |
-
* since 2.2.2-b3
|
678 |
-
*/
|
679 |
-
public function get_subtotal_of_matching_products( $coupon ) {
|
680 |
-
$coupon = $this->get_coupon( $coupon );
|
681 |
-
|
682 |
-
$subtotal = 0;
|
683 |
-
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
684 |
-
$_product = $cart_item['data'];
|
685 |
-
if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
|
686 |
-
$subtotal += $_product->get_price() * $cart_item['quantity'];
|
687 |
-
}
|
688 |
-
}
|
689 |
-
return $subtotal;
|
690 |
-
}
|
691 |
-
|
692 |
-
//Test if coupon is valid for the product
|
693 |
-
// (this function is used to count the quantity of matching products)
|
694 |
-
public function coupon_is_valid_for_product( $coupon, $product, $values = array() ) {
|
695 |
-
//Ugly hack: $coupon->is_valid_for_product only works for these types
|
696 |
-
$original_discount_type = $coupon->discount_type;
|
697 |
-
if ( ! $this->coupon_is_type( $coupon, array( 'fixed_product', 'percent_product' ) ) ) {
|
698 |
-
$coupon->discount_type = 'fixed_product';
|
699 |
-
}
|
700 |
-
$valid = $coupon->is_valid_for_product( $product, $values );
|
701 |
-
$coupon->discount_type = $original_discount_type;
|
702 |
-
|
703 |
-
return $valid;
|
704 |
-
}
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
// =====================
|
709 |
-
|
710 |
-
/**
|
711 |
-
* Retrieve the id of the product or the variation id if it's a variant.
|
712 |
-
* @Returns int|bool The variation or product id. False if not a valid product
|
713 |
-
*/
|
714 |
-
public function get_product_or_variation_id( $product ) {
|
715 |
-
if ( is_a( $product, 'WC_Product_Variation' ) ) {
|
716 |
-
return $product->variation_id;
|
717 |
-
} elseif ( is_a( $product, 'WC_Product' ) ) {
|
718 |
-
return $product->id;
|
719 |
-
} else {
|
720 |
-
return false;
|
721 |
-
}
|
722 |
-
}
|
723 |
-
|
724 |
-
/**
|
725 |
-
* Get a WC_Coupon object
|
726 |
-
* @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
|
727 |
-
* @return WC_Coupon The coupon object
|
728 |
-
*/
|
729 |
-
public function get_coupon( $coupon ) {
|
730 |
-
if ( ! ( $coupon instanceof WC_Coupon ) ) {
|
731 |
-
$coupon = new WC_Coupon( $coupon );
|
732 |
-
}
|
733 |
-
return $coupon;
|
734 |
-
}
|
735 |
-
|
736 |
-
/**
|
737 |
-
* Check the type of the coupon
|
738 |
-
* @param WC_Coupon $coupon The coupon to check
|
739 |
-
* @param string|array $type The type(s) we want to check for
|
740 |
-
* @return bool True if the coupon is of the type
|
741 |
-
*/
|
742 |
-
private function coupon_is_type( $coupon, $type ) {
|
743 |
-
//Backwards compatibility 2.2.11
|
744 |
-
if ( method_exists( $coupon, 'is_type' ) ) {
|
745 |
-
return $coupon->is_type( $type );
|
746 |
-
} else {
|
747 |
-
return ( $coupon->discount_type == $type || ( is_array( $type ) && in_array( $coupon->discount_type, $type ) ) ) ? true : false;
|
748 |
-
}
|
749 |
-
}
|
750 |
-
|
751 |
-
/**
|
752 |
-
* Get array of the selected shipping methods ids.
|
753 |
-
* @param int $coupon_id The coupon id
|
754 |
-
* @return array Id's of the shipping methods or an empty array.
|
755 |
-
*/
|
756 |
-
private function get_coupon_shipping_method_ids($coupon_id) {
|
757 |
-
$v = get_post_meta( $coupon_id, '_wjecf_shipping_methods', true );
|
758 |
-
if ($v == '') {
|
759 |
-
$v = array();
|
760 |
-
}
|
761 |
-
|
762 |
-
return $v;
|
763 |
-
}
|
764 |
-
|
765 |
-
/**
|
766 |
-
* Get array of the selected payment method ids.
|
767 |
-
* @param int $coupon_id The coupon id
|
768 |
-
* @return array Id's of the payment methods or an empty array.
|
769 |
-
*/
|
770 |
-
private function get_coupon_payment_method_ids($coupon_id) {
|
771 |
-
$v = get_post_meta( $coupon_id, '_wjecf_payment_methods', true );
|
772 |
-
if ($v == '') {
|
773 |
-
$v = array();
|
774 |
-
}
|
775 |
-
|
776 |
-
return $v;
|
777 |
-
}
|
778 |
-
|
779 |
-
/**
|
780 |
-
* Get array of the selected customer ids.
|
781 |
-
* @param int $coupon_id The coupon id
|
782 |
-
* @return array Id's of the customers (users) or an empty array.
|
783 |
-
*/
|
784 |
-
private function get_coupon_customer_ids($coupon_id) {
|
785 |
-
$v = get_post_meta( $coupon_id, '_wjecf_customer_ids', true );
|
786 |
-
//$v = array_map( 'intval', explode(",", get_post_meta( $coupon_id, '_wjecf_customer_ids', true ) ) );
|
787 |
-
if ($v == '') {
|
788 |
-
$v = array();
|
789 |
-
} else {
|
790 |
-
$v = array_map( 'intval', explode(",", $v ) );
|
791 |
-
}
|
792 |
-
|
793 |
-
return $v;
|
794 |
-
}
|
795 |
-
|
796 |
-
/**
|
797 |
-
* Get array of the selected customer role ids.
|
798 |
-
* @param int $coupon_id The coupon id
|
799 |
-
* @return array Id's (string) of the customer roles or an empty array.
|
800 |
-
*/
|
801 |
-
private function get_coupon_customer_roles($coupon_id) {
|
802 |
-
$v = get_post_meta( $coupon_id, '_wjecf_customer_roles', true );
|
803 |
-
if ($v == '') {
|
804 |
-
$v = array();
|
805 |
-
}
|
806 |
-
|
807 |
-
return $v;
|
808 |
-
}
|
809 |
-
|
810 |
-
/**
|
811 |
-
* Get array of the excluded customer role ids.
|
812 |
-
* @param int $coupon_id The coupon id
|
813 |
-
* @return array Id's (string) of the excluded customer roles or an empty array.
|
814 |
-
*/
|
815 |
-
private function get_coupon_excluded_customer_roles($coupon_id) {
|
816 |
-
$v = get_post_meta( $coupon_id, '_wjecf_excluded_customer_roles', true );
|
817 |
-
if ($v == '') {
|
818 |
-
$v = array();
|
819 |
-
}
|
820 |
-
|
821 |
-
return $v;
|
822 |
-
}
|
823 |
-
|
824 |
-
public function is_pro() {
|
825 |
-
return class_exists( 'WC_Jos_Pro_Controller' );
|
826 |
-
}
|
827 |
-
|
828 |
-
|
829 |
public static function get_donate_url() {
|
830 |
return "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQBCS2QHRY&lc=NL&item_name=Jos%20Koenis&item_number=wordpress%2dplugin¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted";
|
831 |
}
|
2 |
|
3 |
defined('ABSPATH') or die();
|
4 |
|
5 |
+
if ( class_exists('WJECF_Admin') ) {
|
6 |
return;
|
7 |
}
|
8 |
|
9 |
+
class WJECF_Admin extends Abstract_WJECF_Plugin {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
public function __construct() {
|
12 |
+
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
}
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
//Upgrade options on version change
|
16 |
public function auto_upgrade_options() {
|
17 |
$current_db_version = 1;
|
18 |
//Check version
|
19 |
+
if ( WJECF()->options['db_version'] < $current_db_version ) {
|
20 |
global $wpdb;
|
21 |
|
22 |
//DB_VERSION 1: Since 2.1.0-b5
|
23 |
+
if (WJECF()->options['db_version'] < 1) {
|
24 |
//RENAME meta_key _wjecf_matching_product_qty TO _wjecf_min_matching_product_qty
|
25 |
$where = array("meta_key" => "_wjecf_matching_product_qty");
|
26 |
$set = array('meta_key' => "_wjecf_min_matching_product_qty");
|
31 |
$set = array('meta_key' => "_wjecf_is_auto_coupon");
|
32 |
$wpdb->update( _get_meta_table('post'), $set, $where );
|
33 |
//Now we're version 1
|
34 |
+
WJECF()->options['db_version'] = 1;
|
35 |
}
|
36 |
|
37 |
// Write options to database
|
38 |
+
update_option( 'wjecf_options' , WJECF()->options, false );
|
39 |
}
|
40 |
}
|
41 |
|
88 |
}
|
89 |
private function admin_coupon_data_footer() {
|
90 |
$documentation_url = plugins_url( 'docs/index.html', dirname( __FILE__ ) );
|
91 |
+
if ( ! WJECF()->is_pro() ) {
|
92 |
$documentation_url = 'http://www.soft79.nl/documentation/wjecf';
|
93 |
?>
|
94 |
<h3><?php _e( 'Do you find WooCommerce Extended Coupon Features useful?', 'woocommerce-jos-autocoupon'); ?></h3>
|
235 |
<p class="form-field"><label for="wjecf_shipping_methods"><?php _e( 'Shipping methods', 'woocommerce-jos-autocoupon' ); ?></label>
|
236 |
<select id="wjecf_shipping_methods" name="wjecf_shipping_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any shipping method', 'woocommerce-jos-autocoupon' ); ?>">
|
237 |
<?php
|
238 |
+
$coupon_shipping_method_ids = WJECF()->get_coupon_shipping_method_ids( $thepostid );
|
239 |
$shipping_methods = WC()->shipping->load_shipping_methods();
|
240 |
|
241 |
if ( $shipping_methods ) foreach ( $shipping_methods as $shipping_method ) {
|
251 |
<p class="form-field"><label for="wjecf_payment_methods"><?php _e( 'Payment methods', 'woocommerce-jos-autocoupon' ); ?></label>
|
252 |
<select id="wjecf_payment_methods" name="wjecf_payment_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any payment method', 'woocommerce-jos-autocoupon' ); ?>">
|
253 |
<?php
|
254 |
+
$coupon_payment_method_ids = WJECF()->get_coupon_payment_method_ids( $thepostid );
|
255 |
//DONT USE WC()->payment_gateways->available_payment_gateways() AS IT CAN CRASH IN UNKNOWN OCCASIONS
|
256 |
$payment_methods = WC()->payment_gateways->payment_gateways();
|
257 |
if ( $payment_methods ) foreach ( $payment_methods as $payment_method ) {
|
276 |
?>
|
277 |
<p class="form-field"><label><?php _e( 'Allowed Customers', 'woocommerce-jos-autocoupon' ); ?></label>
|
278 |
<input type="hidden" class="wc-customer-search" data-multiple="true" style="width: 50%;" name="wjecf_customer_ids" data-placeholder="<?php _e( 'Any customer', 'woocommerce-jos-autocoupon' ); ?>" data-action="woocommerce_json_search_customers" data-selected="<?php
|
279 |
+
$coupon_customer_ids = WJECF()->get_coupon_customer_ids( $thepostid );
|
280 |
$json_ids = array();
|
281 |
|
282 |
foreach ( $coupon_customer_ids as $customer_id ) {
|
298 |
<p class="form-field"><label for="wjecf_customer_roles"><?php _e( 'Allowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
|
299 |
<select id="wjecf_customer_roles" name="wjecf_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
|
300 |
<?php
|
301 |
+
$coupon_customer_roles = WJECF()->get_coupon_customer_roles( $thepostid );
|
302 |
|
303 |
$available_customer_roles = array_reverse( get_editable_roles() );
|
304 |
foreach ( $available_customer_roles as $role_id => $role ) {
|
320 |
<p class="form-field"><label for="wjecf_excluded_customer_roles"><?php _e( 'Disallowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
|
321 |
<select id="wjecf_customer_roles" name="wjecf_excluded_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
|
322 |
<?php
|
323 |
+
$coupon_excluded_customer_roles = WJECF()->get_coupon_excluded_customer_roles( $thepostid );
|
324 |
|
325 |
foreach ( $available_customer_roles as $role_id => $role ) {
|
326 |
$role_name = translate_user_role($role['name'] );
|
389 |
|
390 |
}
|
391 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
392 |
public static function get_donate_url() {
|
393 |
return "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQBCS2QHRY&lc=NL&item_name=Jos%20Koenis&item_number=wordpress%2dplugin¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted";
|
394 |
}
|
includes/wjecf-autocoupon.php
CHANGED
@@ -2,13 +2,9 @@
|
|
2 |
|
3 |
defined('ABSPATH') or die();
|
4 |
|
5 |
-
|
6 |
-
return;
|
7 |
-
}
|
8 |
|
9 |
-
|
10 |
-
|
11 |
-
private $_autocoupon_codes = null;
|
12 |
|
13 |
private $_user_emails = null;
|
14 |
|
@@ -28,9 +24,9 @@ class WC_Jos_AutoCoupon_Controller {
|
|
28 |
|
29 |
//Frontend hooks - logic
|
30 |
add_action( 'woocommerce_after_calculate_totals', array( &$this, 'update_matched_autocoupons' ) );
|
31 |
-
add_action( 'woocommerce_check_cart_items', array( &$this, '
|
32 |
//Last check for coupons with restricted_emails
|
33 |
-
add_action( 'woocommerce_checkout_update_order_review', array(
|
34 |
|
35 |
//Frontend hooks - visualisation
|
36 |
add_filter('woocommerce_cart_totals_coupon_label', array( &$this, 'coupon_label' ), 10, 2 );
|
@@ -49,8 +45,79 @@ class WC_Jos_AutoCoupon_Controller {
|
|
49 |
public function admin_init() {
|
50 |
add_action( 'wjecf_woocommerce_coupon_options_extended_features', array( $this, 'admin_coupon_options_extended_features' ), 20, 2 );
|
51 |
add_action( 'woocommerce_process_shop_coupon_meta', array( $this, 'process_shop_coupon_meta' ), 10, 2 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
}
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
public function admin_coupon_options_extended_features( $thepostid, $post ) {
|
55 |
|
56 |
//=============================
|
@@ -66,6 +133,19 @@ class WC_Jos_AutoCoupon_Controller {
|
|
66 |
'description' => __( "Automatically add the coupon to the cart if the restrictions are met. Please enter a description when you check this box, the description will be shown in the customer's cart if the coupon is applied.", 'woocommerce-jos-autocoupon' )
|
67 |
) );
|
68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
//=============================
|
70 |
// Apply without notice
|
71 |
woocommerce_wp_checkbox( array(
|
@@ -73,6 +153,7 @@ class WC_Jos_AutoCoupon_Controller {
|
|
73 |
'label' => __( 'Apply silently', 'woocommerce-jos-autocoupon' ),
|
74 |
'description' => __( "Don't display a message when this coupon is automatically applied.", 'woocommerce-jos-autocoupon' ),
|
75 |
) );
|
|
|
76 |
|
77 |
?>
|
78 |
<script type="text/javascript">
|
@@ -81,9 +162,9 @@ class WC_Jos_AutoCoupon_Controller {
|
|
81 |
if ( animation === undefined ) animation = 'slow';
|
82 |
|
83 |
if (jQuery("#_wjecf_is_auto_coupon").prop('checked')) {
|
84 |
-
jQuery(".
|
85 |
} else {
|
86 |
-
jQuery(".
|
87 |
}
|
88 |
}
|
89 |
update_wjecf_apply_silently_field( 0 );
|
@@ -95,8 +176,13 @@ class WC_Jos_AutoCoupon_Controller {
|
|
95 |
}
|
96 |
|
97 |
public function process_shop_coupon_meta( $post_id, $post ) {
|
98 |
-
|
|
|
|
|
99 |
update_post_meta( $post_id, '_wjecf_apply_silently', isset( $_POST['_wjecf_apply_silently'] ) ? 'yes' : 'no' );
|
|
|
|
|
|
|
100 |
}
|
101 |
|
102 |
/* FRONTEND HOOKS */
|
@@ -175,42 +261,84 @@ class WC_Jos_AutoCoupon_Controller {
|
|
175 |
}
|
176 |
}
|
177 |
|
178 |
-
|
179 |
-
|
180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
/**
|
182 |
* Apply matched autocoupons and remove unmatched autocoupons.
|
183 |
* @return void
|
184 |
*/
|
185 |
function update_matched_autocoupons() {
|
186 |
-
if ( $this->
|
187 |
-
$this->log ( 'update_matched_autocoupons counter reached limit 20' );
|
188 |
return;
|
189 |
}
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
}
|
206 |
}
|
207 |
-
|
|
|
|
|
208 |
if ( $calc_needed ) {
|
209 |
WC()->cart->calculate_totals();
|
210 |
}
|
211 |
|
212 |
}
|
213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
/**
|
215 |
* Test whether the coupon is valid and has a discount > 0
|
216 |
* @return bool
|
@@ -221,23 +349,11 @@ class WC_Jos_AutoCoupon_Controller {
|
|
221 |
//Test validity
|
222 |
if ( ! $coupon->is_valid() ) {
|
223 |
$can_be_applied = false;
|
224 |
-
//$this->log( "Not valid. ");
|
225 |
-
//echo "<pre>";
|
226 |
-
//print_r (WC()->cart->get_cart());
|
227 |
-
//echo "</pre>";
|
228 |
-
}
|
229 |
-
//Test individual use
|
230 |
-
elseif ( $coupon->individual_use == 'yes' ) {
|
231 |
-
if ( count( WC()->cart->applied_coupons ) > 1 ) {
|
232 |
-
$can_be_applied = false;
|
233 |
-
} elseif ( count( WC()->cart->applied_coupons ) == 1 && ! in_array( $coupon->code, WC()->cart->applied_coupons ) ) {
|
234 |
-
$can_be_applied = false;
|
235 |
-
}
|
236 |
}
|
237 |
|
238 |
//Test restricted emails
|
239 |
//See WooCommerce: class-wc-cart.php function check_customer_coupons
|
240 |
-
if ( $can_be_applied && is_array( $coupon->customer_email ) && sizeof( $coupon->customer_email ) > 0 ) {
|
241 |
$user_emails = array_map( 'sanitize_email', array_map( 'strtolower', $this->get_user_emails() ) );
|
242 |
$coupon_emails = array_map( 'sanitize_email', array_map( 'strtolower', $coupon->customer_email ) );
|
243 |
|
@@ -279,28 +395,6 @@ class WC_Jos_AutoCoupon_Controller {
|
|
279 |
}
|
280 |
|
281 |
|
282 |
-
/**
|
283 |
-
* Remove unmatched autocoupons. No message will be shown.
|
284 |
-
* NOTE: This function must be called before WooCommerce removes the coupon, to inhibit WooCommerces "coupon not valid"-message!
|
285 |
-
* @return bool True if coupons were removed, otherwise False;
|
286 |
-
*/
|
287 |
-
function remove_unmatched_autocoupons() {
|
288 |
-
global $woocommerce;
|
289 |
-
|
290 |
-
$calc_needed = false;
|
291 |
-
foreach ( $this->get_all_auto_coupons() as $coupon_code ) {
|
292 |
-
if ( $woocommerce->cart->has_discount( $coupon_code ) ) {
|
293 |
-
$coupon = new WC_Coupon($coupon_code);
|
294 |
-
if ( ! $this->coupon_can_be_applied($coupon) ) {
|
295 |
-
//$this->log( sprintf( "Removing %s", $coupon_code ) );
|
296 |
-
WC()->cart->remove_coupon( $coupon_code );
|
297 |
-
$calc_needed = true;
|
298 |
-
}
|
299 |
-
}
|
300 |
-
}
|
301 |
-
return $calc_needed;
|
302 |
-
}
|
303 |
-
|
304 |
/**
|
305 |
* Overwrite the default "Coupon added" notice with a more descriptive message.
|
306 |
* @param WC_Coupon $coupon The coupon data
|
@@ -361,6 +455,16 @@ class WC_Jos_AutoCoupon_Controller {
|
|
361 |
private function is_auto_coupon($coupon) {
|
362 |
return get_post_meta( $coupon->id, '_wjecf_is_auto_coupon', true ) == 'yes';
|
363 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
364 |
|
365 |
|
366 |
/**
|
@@ -419,44 +523,84 @@ class WC_Jos_AutoCoupon_Controller {
|
|
419 |
$this->append_user_emails( $posted['billing_email'] );
|
420 |
}
|
421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
422 |
}
|
423 |
|
424 |
/**
|
425 |
* Get a list of all auto coupon codes
|
426 |
* @return array All auto coupon codes
|
427 |
*/
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
$this->_autocoupon_codes = array();
|
432 |
|
433 |
$query_args = array(
|
434 |
'posts_per_page' => -1,
|
435 |
'post_type' => 'shop_coupon',
|
436 |
'post_status' => 'publish',
|
437 |
-
'orderby' => 'title',
|
438 |
-
'meta_query' => array(
|
439 |
-
array(
|
440 |
-
'key' => '_wjecf_is_auto_coupon',
|
441 |
-
'value' => 'yes',
|
442 |
-
'compare' => '=',
|
443 |
-
),
|
444 |
-
)
|
445 |
);
|
446 |
|
447 |
$query = new WP_Query($query_args);
|
448 |
foreach ($query->posts as $post) {
|
449 |
$coupon = new WC_Coupon($post->post_title);
|
450 |
if ( $this->is_auto_coupon($coupon) ) {
|
451 |
-
$this->
|
452 |
}
|
453 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
454 |
}
|
455 |
-
return $this->_autocoupon_codes;
|
456 |
-
}
|
457 |
|
458 |
-
|
459 |
-
|
460 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
461 |
}
|
462 |
}
|
2 |
|
3 |
defined('ABSPATH') or die();
|
4 |
|
5 |
+
class WJECF_AutoCoupon extends Abstract_WJECF_Plugin {
|
|
|
|
|
6 |
|
7 |
+
private $_autocoupons = null;
|
|
|
|
|
8 |
|
9 |
private $_user_emails = null;
|
10 |
|
24 |
|
25 |
//Frontend hooks - logic
|
26 |
add_action( 'woocommerce_after_calculate_totals', array( &$this, 'update_matched_autocoupons' ) );
|
27 |
+
add_action( 'woocommerce_check_cart_items', array( &$this, 'remove_unmatched_autocoupons' ) , 0, 0 ); //Remove coupon before WC does it and shows a message
|
28 |
//Last check for coupons with restricted_emails
|
29 |
+
add_action( 'woocommerce_checkout_update_order_review', array( &$this, 'fetch_billing_email' ), 10 ); // AJAX One page checkout
|
30 |
|
31 |
//Frontend hooks - visualisation
|
32 |
add_filter('woocommerce_cart_totals_coupon_label', array( &$this, 'coupon_label' ), 10, 2 );
|
45 |
public function admin_init() {
|
46 |
add_action( 'wjecf_woocommerce_coupon_options_extended_features', array( $this, 'admin_coupon_options_extended_features' ), 20, 2 );
|
47 |
add_action( 'woocommerce_process_shop_coupon_meta', array( $this, 'process_shop_coupon_meta' ), 10, 2 );
|
48 |
+
|
49 |
+
//Inject columns
|
50 |
+
if ( WJECF()->is_pro() ) {
|
51 |
+
WJECF()->inject_coupon_column(
|
52 |
+
'_wjecf_auto_coupon',
|
53 |
+
__( 'Auto coupon', 'woocommerce-jos-autocoupon' ),
|
54 |
+
array( $this, 'admin_render_shop_coupon_columns' ), 'coupon_code'
|
55 |
+
);
|
56 |
+
WJECF()->inject_coupon_column(
|
57 |
+
'_wjecf_individual_use',
|
58 |
+
__( 'Individual use', 'woocommerce-jos-autocoupon' ),
|
59 |
+
array( $this, 'admin_render_shop_coupon_columns' ), 'coupon_code'
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
add_filter( 'views_edit-shop_coupon', array( $this, 'admin_views_edit_coupon' ) );
|
64 |
+
add_filter( 'request', array( $this, 'admin_request_query' ) );
|
65 |
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Output a coupon custom column value
|
69 |
+
*
|
70 |
+
* @param string $column
|
71 |
+
* @param WP_Post The coupon post object
|
72 |
+
*/
|
73 |
+
public function admin_render_shop_coupon_columns( $column, $post ) {
|
74 |
+
|
75 |
+
switch ( $column ) {
|
76 |
+
case '_wjecf_auto_coupon' :
|
77 |
+
$is_auto_coupon = get_post_meta( $post->ID, '_wjecf_is_auto_coupon', true ) == 'yes';
|
78 |
+
echo $is_auto_coupon ? __( 'Yes', 'woocommerce' ) : __( 'No', 'woocommerce' );
|
79 |
+
if ( $is_auto_coupon ) {
|
80 |
+
$prio = get_post_meta( $post->ID, '_wjecf_coupon_priority', true );
|
81 |
+
if ( $prio ) echo " (" . intval( $prio ) . ")";
|
82 |
+
}
|
83 |
+
break;
|
84 |
+
case '_wjecf_individual_use' :
|
85 |
+
$individual = get_post_meta( $post->ID, 'individual_use', true ) == 'yes';
|
86 |
+
echo $individual ? __( 'Yes', 'woocommerce' ) : __( 'No', 'woocommerce' );
|
87 |
+
break;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
public function admin_views_edit_coupon( $views ) {
|
92 |
+
global $post_type, $wp_query;
|
93 |
+
|
94 |
+
$class = ( isset( $wp_query->query['meta_key'] ) && $wp_query->query['meta_key'] == '_wjecf_is_auto_coupon' ) ? 'current' : '';
|
95 |
+
$query_string = remove_query_arg(array( 'wjecf_is_auto_coupon' ));
|
96 |
+
$query_string = add_query_arg( 'wjecf_is_auto_coupon', '1', $query_string );
|
97 |
+
$views['wjecf_is_auto_coupon'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Auto coupons', 'woocommerce' ) . '</a>';
|
98 |
+
|
99 |
+
return $views;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Filters and sorting handler
|
104 |
+
*
|
105 |
+
* @param array $vars
|
106 |
+
* @return array
|
107 |
+
*/
|
108 |
+
public function admin_request_query( $vars ) {
|
109 |
+
global $typenow, $wp_query, $wp_post_statuses;
|
110 |
+
|
111 |
+
if ( 'shop_coupon' === $typenow ) {
|
112 |
+
if ( isset( $_GET['wjecf_is_auto_coupon'] ) ) {
|
113 |
+
$vars['meta_key'] = '_wjecf_is_auto_coupon';
|
114 |
+
$vars['meta_value'] = $_GET['wjecf_is_auto_coupon'] == '1' ? 'yes' : 'no';
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
return $vars;
|
119 |
+
}
|
120 |
+
|
121 |
public function admin_coupon_options_extended_features( $thepostid, $post ) {
|
122 |
|
123 |
//=============================
|
133 |
'description' => __( "Automatically add the coupon to the cart if the restrictions are met. Please enter a description when you check this box, the description will be shown in the customer's cart if the coupon is applied.", 'woocommerce-jos-autocoupon' )
|
134 |
) );
|
135 |
|
136 |
+
echo '<div class="_wjecf_show_if_autocoupon">';
|
137 |
+
if ( WJECF()->is_pro() ) {
|
138 |
+
// Maximum quantity of matching products (product/category)
|
139 |
+
woocommerce_wp_text_input( array(
|
140 |
+
'id' => '_wjecf_coupon_priority',
|
141 |
+
'label' => __( 'Priority', 'woocommerce-jos-autocoupon' ),
|
142 |
+
'placeholder' => __( 'No priority', 'woocommerce' ),
|
143 |
+
'description' => __( 'When \'individual use\' is checked, auto coupons with a higher value will have priority over other auto coupons.', 'woocommerce-jos-autocoupon' ),
|
144 |
+
'data_type' => 'decimal',
|
145 |
+
'desc_tip' => true
|
146 |
+
) );
|
147 |
+
}
|
148 |
+
|
149 |
//=============================
|
150 |
// Apply without notice
|
151 |
woocommerce_wp_checkbox( array(
|
153 |
'label' => __( 'Apply silently', 'woocommerce-jos-autocoupon' ),
|
154 |
'description' => __( "Don't display a message when this coupon is automatically applied.", 'woocommerce-jos-autocoupon' ),
|
155 |
) );
|
156 |
+
echo '</div>';
|
157 |
|
158 |
?>
|
159 |
<script type="text/javascript">
|
162 |
if ( animation === undefined ) animation = 'slow';
|
163 |
|
164 |
if (jQuery("#_wjecf_is_auto_coupon").prop('checked')) {
|
165 |
+
jQuery("._wjecf_show_if_autocoupon").show( animation );
|
166 |
} else {
|
167 |
+
jQuery("._wjecf_show_if_autocoupon").hide( animation );
|
168 |
}
|
169 |
}
|
170 |
update_wjecf_apply_silently_field( 0 );
|
176 |
}
|
177 |
|
178 |
public function process_shop_coupon_meta( $post_id, $post ) {
|
179 |
+
$autocoupon = isset( $_POST['_wjecf_is_auto_coupon'] );
|
180 |
+
|
181 |
+
update_post_meta( $post_id, '_wjecf_is_auto_coupon', $autocoupon ? 'yes' : 'no' );
|
182 |
update_post_meta( $post_id, '_wjecf_apply_silently', isset( $_POST['_wjecf_apply_silently'] ) ? 'yes' : 'no' );
|
183 |
+
if ( WJECF()->is_pro() ) {
|
184 |
+
update_post_meta( $post_id, '_wjecf_coupon_priority', intval( $_POST['_wjecf_coupon_priority'] ) );
|
185 |
+
}
|
186 |
}
|
187 |
|
188 |
/* FRONTEND HOOKS */
|
261 |
}
|
262 |
}
|
263 |
|
264 |
+
|
265 |
+
function remove_unmatched_autocoupons( $valid_coupon_codes = null ) {
|
266 |
+
if ( $valid_coupon_codes === null ) {
|
267 |
+
//Get the coupons that should be in the cart
|
268 |
+
$valid_coupons = $this->valid_auto_coupons();
|
269 |
+
$valid_coupons = $this->individual_use_filter( $valid_coupons );
|
270 |
+
$valid_coupon_codes = array();
|
271 |
+
foreach ( $valid_coupons as $coupon ) {
|
272 |
+
$valid_coupon_codes[] = $coupon->code;
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
+
//Remove invalids
|
277 |
+
$calc_needed = false;
|
278 |
+
foreach ( $this->get_all_auto_coupons() as $coupon ) {
|
279 |
+
if ( WC()->cart->has_discount( $coupon->code ) && ! in_array( $coupon->code, $valid_coupon_codes ) ) {
|
280 |
+
$this->log( sprintf( "Removing %s", $coupon->code ) );
|
281 |
+
WC()->cart->remove_coupon( $coupon->code );
|
282 |
+
$calc_needed = true;
|
283 |
+
}
|
284 |
+
}
|
285 |
+
return $calc_needed;
|
286 |
+
}
|
287 |
+
|
288 |
+
private $update_matched_autocoupons_executed = false;
|
289 |
+
|
290 |
/**
|
291 |
* Apply matched autocoupons and remove unmatched autocoupons.
|
292 |
* @return void
|
293 |
*/
|
294 |
function update_matched_autocoupons() {
|
295 |
+
if ( $this->update_matched_autocoupons_executed ) {
|
|
|
296 |
return;
|
297 |
}
|
298 |
+
$this->update_matched_autocoupons_executed = true;
|
299 |
+
$this->log( "()" );
|
300 |
+
|
301 |
+
//Get the coupons that should be in the cart
|
302 |
+
$valid_coupons = $this->valid_auto_coupons();
|
303 |
+
$valid_coupons = $this->individual_use_filter( $valid_coupons );
|
304 |
+
$valid_coupon_codes = array();
|
305 |
+
foreach ( $valid_coupons as $coupon ) {
|
306 |
+
$valid_coupon_codes[] = $coupon->code;
|
307 |
+
}
|
308 |
+
|
309 |
+
//$this->log( sprintf( "Coupons that should be in cart: %s", implode( ', ', $valid_coupon_codes ) ) );
|
310 |
+
|
311 |
+
$calc_needed = $this->remove_unmatched_autocoupons( $valid_coupon_codes );
|
312 |
+
|
313 |
+
//Add valids
|
314 |
+
foreach( $valid_coupons as $coupon ) {
|
315 |
+
if ( ! WC()->cart->has_discount( $coupon->code ) ) {
|
316 |
+
$this->log( sprintf( "Applying %s", $coupon->code ) );
|
317 |
+
WC()->cart->add_discount( $coupon->code ); //Causes calculation and will remove other coupons if it's a individual coupon
|
318 |
+
$calc_needed = false; //Already done by adding the discount
|
319 |
+
$apply_silently = get_post_meta( $coupon->id, '_wjecf_apply_silently', true ) == 'yes';
|
320 |
+
$this->overwrite_success_message( $coupon, $apply_silently );
|
321 |
}
|
322 |
}
|
323 |
+
|
324 |
+
$this->log( 'Coupons in cart: ' . implode( ', ', WC()->cart->applied_coupons ) . ($calc_needed ? ". RECALC" : "") );
|
325 |
+
|
326 |
if ( $calc_needed ) {
|
327 |
WC()->cart->calculate_totals();
|
328 |
}
|
329 |
|
330 |
}
|
331 |
+
|
332 |
+
private function valid_auto_coupons( ) {
|
333 |
+
$valid_coupons = array();
|
334 |
+
foreach ( $this->get_all_auto_coupons() as $coupon ) {
|
335 |
+
if ( $this->coupon_can_be_applied( $coupon ) && $this->coupon_has_a_value( $coupon ) ) {
|
336 |
+
$valid_coupons[] = $coupon;
|
337 |
+
}
|
338 |
+
}
|
339 |
+
return $valid_coupons;
|
340 |
+
}
|
341 |
+
|
342 |
/**
|
343 |
* Test whether the coupon is valid and has a discount > 0
|
344 |
* @return bool
|
349 |
//Test validity
|
350 |
if ( ! $coupon->is_valid() ) {
|
351 |
$can_be_applied = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
352 |
}
|
353 |
|
354 |
//Test restricted emails
|
355 |
//See WooCommerce: class-wc-cart.php function check_customer_coupons
|
356 |
+
else if ( $can_be_applied && is_array( $coupon->customer_email ) && sizeof( $coupon->customer_email ) > 0 ) {
|
357 |
$user_emails = array_map( 'sanitize_email', array_map( 'strtolower', $this->get_user_emails() ) );
|
358 |
$coupon_emails = array_map( 'sanitize_email', array_map( 'strtolower', $coupon->customer_email ) );
|
359 |
|
395 |
}
|
396 |
|
397 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
398 |
/**
|
399 |
* Overwrite the default "Coupon added" notice with a more descriptive message.
|
400 |
* @param WC_Coupon $coupon The coupon data
|
455 |
private function is_auto_coupon($coupon) {
|
456 |
return get_post_meta( $coupon->id, '_wjecf_is_auto_coupon', true ) == 'yes';
|
457 |
}
|
458 |
+
|
459 |
+
private function get_coupon_priority($coupon) {
|
460 |
+
if ( WJECF()->is_pro() ) {
|
461 |
+
$prio = get_post_meta( $coupon->id, '_wjecf_coupon_priority', true );
|
462 |
+
if ( ! empty( $prio ) ) {
|
463 |
+
return intval( $prio );
|
464 |
+
}
|
465 |
+
}
|
466 |
+
return 0;
|
467 |
+
}
|
468 |
|
469 |
|
470 |
/**
|
523 |
$this->append_user_emails( $posted['billing_email'] );
|
524 |
}
|
525 |
|
526 |
+
}
|
527 |
+
|
528 |
+
/**
|
529 |
+
* Return an array of WC_Coupons with coupons that shouldn't cause individual use conflicts
|
530 |
+
*/
|
531 |
+
private function individual_use_filter( $valid_auto_coupons ) {
|
532 |
+
$filtered = array();
|
533 |
+
|
534 |
+
//Any individual use non-autocoupons in the cart?
|
535 |
+
foreach ( WC()->cart->get_applied_coupons() as $coupon_code ) {
|
536 |
+
$coupon = new WC_Coupon( $coupon_code );
|
537 |
+
if ( $coupon->individual_use == 'yes' && ! $this->is_auto_coupon( $coupon ) ) {
|
538 |
+
return $filtered; //Don't allow any auto coupon
|
539 |
+
}
|
540 |
+
}
|
541 |
+
foreach ( $valid_auto_coupons as $coupon ) {
|
542 |
+
if ( $coupon->individual_use != 'yes' || empty( $filtered ) ) {
|
543 |
+
$filtered[] = $coupon;
|
544 |
+
if ( $coupon->individual_use == 'yes' ) {
|
545 |
+
break;
|
546 |
+
}
|
547 |
+
}
|
548 |
+
}
|
549 |
+
return $filtered;
|
550 |
}
|
551 |
|
552 |
/**
|
553 |
* Get a list of all auto coupon codes
|
554 |
* @return array All auto coupon codes
|
555 |
*/
|
556 |
+
public function get_all_auto_coupons() {
|
557 |
+
if ( ! is_array( $this->_autocoupons ) ) {
|
558 |
+
$this->_autocoupons = array();
|
|
|
559 |
|
560 |
$query_args = array(
|
561 |
'posts_per_page' => -1,
|
562 |
'post_type' => 'shop_coupon',
|
563 |
'post_status' => 'publish',
|
564 |
+
'orderby' => array( 'title' => 'ASC' ),
|
565 |
+
'meta_query' => array( 'key' => '_wjecf_is_auto_coupon', 'compare' => '=', 'value' => 'yes')
|
|
|
|
|
|
|
|
|
|
|
|
|
566 |
);
|
567 |
|
568 |
$query = new WP_Query($query_args);
|
569 |
foreach ($query->posts as $post) {
|
570 |
$coupon = new WC_Coupon($post->post_title);
|
571 |
if ( $this->is_auto_coupon($coupon) ) {
|
572 |
+
$this->_autocoupons[$coupon->code] = $coupon;
|
573 |
}
|
574 |
+
}
|
575 |
+
|
576 |
+
//Sort by prio
|
577 |
+
@uasort( $this->_autocoupons , array( $this, 'sort_auto_coupons' ) ); //Ignore error PHP Bug #50688
|
578 |
+
|
579 |
+
$coupon_codes = array();
|
580 |
+
foreach( $this->_autocoupons as $coupon ) {
|
581 |
+
$coupon_codes[] = $coupon->code;
|
582 |
+
}
|
583 |
+
|
584 |
+
$this->log( "Autocoupons: " . implode(", ", $coupon_codes ) );
|
585 |
}
|
|
|
|
|
586 |
|
587 |
+
return $this->_autocoupons;
|
588 |
+
}
|
589 |
+
|
590 |
+
/**
|
591 |
+
* Compare function to sort coupons by priority
|
592 |
+
* @param type $a
|
593 |
+
* @param type $b
|
594 |
+
* @return type
|
595 |
+
*/
|
596 |
+
private function sort_auto_coupons( $coupon_a, $coupon_b ) {
|
597 |
+
$prio_a = $this->get_coupon_priority( $coupon_a );
|
598 |
+
$prio_b = $this->get_coupon_priority( $coupon_b );
|
599 |
+
$this->log("A: $prio_a B: $prio_b ");
|
600 |
+
if ( $prio_a == $prio_b ) {
|
601 |
+
return $a->code < $b->code ? -1 : 1; //By title ASC
|
602 |
+
} else {
|
603 |
+
return $prio_a > $prio_b ? -1 : 1; //By prio DESC
|
604 |
+
}
|
605 |
}
|
606 |
}
|
includes/wjecf-controller.php
ADDED
@@ -0,0 +1,609 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') or die();
|
4 |
+
|
5 |
+
/**
|
6 |
+
* The main controller for WooCommerce Extended Coupon Features
|
7 |
+
*/
|
8 |
+
class WJECF_Controller {
|
9 |
+
|
10 |
+
protected $debug_mode = false;
|
11 |
+
protected $log = array();
|
12 |
+
public $options = false;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Singleton Instance
|
16 |
+
*
|
17 |
+
* @static
|
18 |
+
* @return Singleton Instance
|
19 |
+
*/
|
20 |
+
public static function instance() {
|
21 |
+
if ( is_null( self::$_instance ) ) {
|
22 |
+
self::$_instance = new self();
|
23 |
+
}
|
24 |
+
return self::$_instance;
|
25 |
+
}
|
26 |
+
protected static $_instance = null;
|
27 |
+
|
28 |
+
|
29 |
+
public function __construct() {
|
30 |
+
add_action('init', array( &$this, 'controller_init' ));
|
31 |
+
}
|
32 |
+
|
33 |
+
public function controller_init() {
|
34 |
+
if ( ! class_exists('WC_Coupon') ) {
|
35 |
+
return;
|
36 |
+
}
|
37 |
+
$this->debug_mode = false; //defined( 'WP_DEBUG' ) && WP_DEBUG;
|
38 |
+
$this->log( "INIT " . ( is_ajax() ? "AJAX" : is_admin() ? "ADMIN" : "FRONTEND" ) . " " . $_SERVER['REQUEST_URI'] );
|
39 |
+
|
40 |
+
$this->init_options();
|
41 |
+
|
42 |
+
//Frontend hooks
|
43 |
+
add_filter('woocommerce_coupon_is_valid', array( &$this, 'coupon_is_valid' ), 10, 2 );
|
44 |
+
add_action('woocommerce_coupon_loaded', array( $this, 'woocommerce_coupon_loaded' ), 10, 1);
|
45 |
+
add_filter('woocommerce_coupon_get_discount_amount', array( $this, 'woocommerce_coupon_get_discount_amount' ), 10, 5);
|
46 |
+
add_action( 'wp_footer', array( &$this, 'render_log' ) ); //Log
|
47 |
+
}
|
48 |
+
|
49 |
+
protected $plugins = array();
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Load a WJECF Plugin (class name)
|
53 |
+
* @param string $class_name The class name of the plugin
|
54 |
+
* @return bool True if succeeded, otherwise false
|
55 |
+
*/
|
56 |
+
public function add_plugin( $class_name ) {
|
57 |
+
if ( class_exists( $class_name ) ) {
|
58 |
+
$this->plugins[ $class_name ] = new $class_name();
|
59 |
+
return true;
|
60 |
+
}
|
61 |
+
return false;
|
62 |
+
}
|
63 |
+
|
64 |
+
public function get_plugins() {
|
65 |
+
return $this->plugins;
|
66 |
+
}
|
67 |
+
|
68 |
+
public function get_plugin( $class_name ) {
|
69 |
+
return $this->plugins[ $class_name ];
|
70 |
+
}
|
71 |
+
|
72 |
+
public function init_options() {
|
73 |
+
$this->options = get_option( 'wjecf_options' );
|
74 |
+
if (false === $this->options) {
|
75 |
+
$this->options = array( 'db_version' => 0 );
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Check whether WooCommerce version is greater or equal than $req_version
|
81 |
+
* @param string @req_version The version to compare to
|
82 |
+
* @return bool true if WooCommerce is at least the given version
|
83 |
+
*/
|
84 |
+
public static function check_woocommerce_version( $req_version ) {
|
85 |
+
return version_compare( self::get_woocommerce_version(), $req_version, '>=' );
|
86 |
+
}
|
87 |
+
|
88 |
+
private static $wc_version = null;
|
89 |
+
/**
|
90 |
+
* Get the WooCommerce version number
|
91 |
+
* @return string|bool WC Version number or false if WC not detected
|
92 |
+
*/
|
93 |
+
public static function get_woocommerce_version() {
|
94 |
+
if (self::$wc_version === null) {
|
95 |
+
// If get_plugins() isn't available, require it
|
96 |
+
if ( ! function_exists( 'get_plugins' ) ) {
|
97 |
+
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
98 |
+
}
|
99 |
+
|
100 |
+
// Create the plugins folder and file variables
|
101 |
+
$plugin_folder = get_plugins( '/woocommerce' );
|
102 |
+
$plugin_file = 'woocommerce.php';
|
103 |
+
|
104 |
+
// If the plugin version number is set, return it
|
105 |
+
if ( isset( $plugin_folder[$plugin_file]['Version'] ) ) {
|
106 |
+
self::$wc_version = $plugin_folder[$plugin_file]['Version'];
|
107 |
+
} else {
|
108 |
+
self::$wc_version = false; // Not found
|
109 |
+
}
|
110 |
+
|
111 |
+
}
|
112 |
+
return self::$wc_version;
|
113 |
+
}
|
114 |
+
|
115 |
+
/* FRONTEND HOOKS */
|
116 |
+
|
117 |
+
//2.2.2
|
118 |
+
public function woocommerce_coupon_loaded ( $coupon ) {
|
119 |
+
if ( ! is_admin() ) {
|
120 |
+
//2.2.2 Allow coupon even if minimum spend not reached
|
121 |
+
if ( get_post_meta( $coupon->id, '_wjecf_allow_below_minimum_spend', true ) == 'yes' ) {
|
122 |
+
//HACK: Overwrite the minimum amount with 0 so WooCommerce will allow the coupon
|
123 |
+
$coupon->wjecf_minimum_amount_for_discount = $coupon->minimum_amount;
|
124 |
+
$coupon->minimum_amount = 0;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
//2.2.2
|
130 |
+
public function woocommerce_coupon_get_discount_amount ( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
|
131 |
+
//2.2.2 No value if minimum spend not reached
|
132 |
+
if (isset( $coupon->wjecf_minimum_amount_for_discount ) ) {
|
133 |
+
if ( wc_format_decimal( $coupon->wjecf_minimum_amount_for_discount ) > wc_format_decimal( WC()->cart->subtotal ) ) {
|
134 |
+
return 0;
|
135 |
+
};
|
136 |
+
}
|
137 |
+
return $discount;
|
138 |
+
}
|
139 |
+
|
140 |
+
|
141 |
+
/* Extra validation rules for coupons */
|
142 |
+
public function coupon_is_valid ( $valid, $coupon ) {
|
143 |
+
//error_log("is valid? " . $coupon->code );
|
144 |
+
//Not valid? Then it will never validate, so get out of here
|
145 |
+
if ( ! $valid ) {
|
146 |
+
return false;
|
147 |
+
}
|
148 |
+
|
149 |
+
//============================
|
150 |
+
//Test if ALL products are in the cart (if AND-operator selected in stead of the default OR)
|
151 |
+
$products_and = get_post_meta( $coupon->id, '_wjecf_products_and', true ) == 'yes';
|
152 |
+
if ( $products_and && sizeof( $coupon->product_ids ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
|
153 |
+
//Get array of all cart product and variation ids
|
154 |
+
$cart_item_ids = array();
|
155 |
+
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
156 |
+
$cart_item_ids[] = $cart_item['product_id'];
|
157 |
+
$cart_item_ids[] = $cart_item['variation_id'];
|
158 |
+
}
|
159 |
+
//check if every single product is in the cart
|
160 |
+
foreach( $coupon->product_ids as $product_id ) {
|
161 |
+
if ( ! in_array( $product_id, $cart_item_ids ) ) {
|
162 |
+
return false;
|
163 |
+
}
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
//============================
|
168 |
+
//Test if products form ALL categories are in the cart (if AND-operator selected in stead of the default OR)
|
169 |
+
$categories_and = get_post_meta( $coupon->id, '_wjecf_categories_and', true ) == 'yes';
|
170 |
+
if ( $categories_and && sizeof( $coupon->product_categories ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
|
171 |
+
//Get array of all cart product and variation ids
|
172 |
+
$cart_product_cats = array();
|
173 |
+
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
174 |
+
$cart_product_cats = array_merge ( $cart_product_cats, wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) ) );
|
175 |
+
}
|
176 |
+
//check if every single category is in the cart
|
177 |
+
foreach( $coupon->product_categories as $cat_id ) {
|
178 |
+
if ( ! in_array( $cat_id, $cart_product_cats ) ) {
|
179 |
+
return false;
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
}
|
184 |
+
|
185 |
+
//============================
|
186 |
+
//Test min/max quantity of matching products
|
187 |
+
//
|
188 |
+
//For all items in the cart:
|
189 |
+
// If coupon contains both a product AND category inclusion filter: the item is counted if it matches either one of them
|
190 |
+
// If coupon contains either a product OR category exclusion filter: the item will NOT be counted if it matches either one of them
|
191 |
+
// If sale items are excluded by the coupon: the item will NOT be counted if it is a sale item
|
192 |
+
// If no filter exist, all items will be counted
|
193 |
+
|
194 |
+
$multiplier = null; //null = not initialized
|
195 |
+
|
196 |
+
//Validate quantity
|
197 |
+
$min_matching_product_qty = intval(get_post_meta( $coupon->id, '_wjecf_min_matching_product_qty', true ));
|
198 |
+
$max_matching_product_qty = intval(get_post_meta( $coupon->id, '_wjecf_max_matching_product_qty', true ));
|
199 |
+
if ( $min_matching_product_qty > 0 || $max_matching_product_qty > 0 ) {
|
200 |
+
//Count the products
|
201 |
+
$qty = $this->get_quantity_of_matching_products( $coupon );
|
202 |
+
if ($min_matching_product_qty > 0 && $qty < $min_matching_product_qty) return false;
|
203 |
+
if ($max_matching_product_qty > 0 && $qty > $max_matching_product_qty) return false;
|
204 |
+
|
205 |
+
if ($min_matching_product_qty > 0) {
|
206 |
+
$multiplier = self::min_value( floor( $qty / $min_matching_product_qty ), $multiplier );
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
//Validate subtotal (2.2.2)
|
211 |
+
$min_matching_product_subtotal = intval(get_post_meta( $coupon->id, '_wjecf_min_matching_product_subtotal', true ));
|
212 |
+
$max_matching_product_subtotal = intval(get_post_meta( $coupon->id, '_wjecf_max_matching_product_subtotal', true ));
|
213 |
+
if ( $min_matching_product_subtotal > 0 || $max_matching_product_subtotal > 0 ) {
|
214 |
+
$subtotal = $this->get_subtotal_of_matching_products( $coupon );
|
215 |
+
if ($min_matching_product_subtotal > 0 && $subtotal < $min_matching_product_subtotal) return false;
|
216 |
+
if ($max_matching_product_subtotal > 0 && $subtotal > $max_matching_product_subtotal) return false;
|
217 |
+
|
218 |
+
if ($min_matching_product_subtotal > 0) {
|
219 |
+
$multiplier = self::min_value( floor( $subtotal / $min_matching_product_subtotal ), $multiplier );
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
//============================
|
224 |
+
//Test restricted shipping methods
|
225 |
+
$shipping_method_ids = $this->get_coupon_shipping_method_ids( $coupon->id );
|
226 |
+
if ( sizeof( $shipping_method_ids ) > 0 ) {
|
227 |
+
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
|
228 |
+
$chosen_shipping = $chosen_shipping_methods[0];
|
229 |
+
|
230 |
+
if ( ! in_array( $chosen_shipping, $shipping_method_ids ) ) {
|
231 |
+
return false;
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
//============================
|
236 |
+
//Test restricted payment methods
|
237 |
+
$payment_method_ids = $this->get_coupon_payment_method_ids( $coupon->id );
|
238 |
+
if ( sizeof( $payment_method_ids ) > 0 ) {
|
239 |
+
$chosen_payment_method = isset( WC()->session->chosen_payment_method ) ? WC()->session->chosen_payment_method : array();
|
240 |
+
|
241 |
+
if ( ! in_array( $chosen_payment_method, $payment_method_ids ) ) {
|
242 |
+
return false;
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
|
247 |
+
//============================
|
248 |
+
//Test restricted user ids and roles
|
249 |
+
//NOTE: If both customer id and role restrictions are provided, the coupon matches if either the id or the role matches
|
250 |
+
$coupon_customer_ids = $this->get_coupon_customer_ids( $coupon->id );
|
251 |
+
$coupon_customer_roles = $this->get_coupon_customer_roles( $coupon->id );
|
252 |
+
if ( sizeof( $coupon_customer_ids ) > 0 || sizeof( $coupon_customer_roles ) > 0 ) {
|
253 |
+
$user = wp_get_current_user();
|
254 |
+
|
255 |
+
//If both fail we invalidate. Otherwise it's ok
|
256 |
+
if ( ! in_array( $user->ID, $coupon_customer_ids ) && ! array_intersect( $user->roles, $coupon_customer_roles ) ) {
|
257 |
+
return false;
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
//============================
|
262 |
+
//Test excluded user roles
|
263 |
+
$coupon_excluded_customer_roles = $this->get_coupon_excluded_customer_roles( $coupon->id );
|
264 |
+
if ( sizeof( $coupon_excluded_customer_roles ) > 0 ) {
|
265 |
+
$user = wp_get_current_user();
|
266 |
+
|
267 |
+
//Excluded customer roles
|
268 |
+
if ( array_intersect( $user->roles, $coupon_excluded_customer_roles ) ) {
|
269 |
+
return false;
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
if ( $coupon->minimum_amount ) {
|
274 |
+
$multiplier = self::min_value( floor( WC()->cart->subtotal / $coupon->minimum_amount ), $multiplier );
|
275 |
+
}
|
276 |
+
|
277 |
+
|
278 |
+
$this->coupon_multiplier_values[ $coupon->code ] = $multiplier;
|
279 |
+
//error_log("multiplier " . $coupon->code . " = " . $multiplier );
|
280 |
+
|
281 |
+
return true;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Return the lowest multiplier value
|
286 |
+
*/
|
287 |
+
private static function min_value( $value, $current_multiplier_value = null ) {
|
288 |
+
return ( $current_multiplier_value === null || $value < $current_multiplier_value ) ? $value : $current_multiplier_value;
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* The amount of times the minimum spend / quantity / subtotal values are reached
|
293 |
+
* @return int 1 or more if coupon is valid, otherwise 0
|
294 |
+
*/
|
295 |
+
public function get_coupon_multiplier_value( $coupon ) {
|
296 |
+
$coupon = $this->get_coupon( $coupon );
|
297 |
+
|
298 |
+
//If coupon validation was not executed, the value is unknown
|
299 |
+
if ( ! isset( $this->coupon_multiplier_values[ $coupon->code ] ) ) {
|
300 |
+
if ( ! $this->coupon_is_valid( true, $coupon ) ) {
|
301 |
+
return 0;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
$multiplier = $this->coupon_multiplier_values[ $coupon->code ];
|
305 |
+
|
306 |
+
//error_log("get multiplier " . $coupon->code . " = " . $multiplier );
|
307 |
+
return $multiplier;
|
308 |
+
}
|
309 |
+
|
310 |
+
//Temporary storage
|
311 |
+
private $coupon_multiplier_values = array();
|
312 |
+
|
313 |
+
|
314 |
+
/**
|
315 |
+
* (API FUNCTION)
|
316 |
+
* The total amount of the products in the cart that match the coupon restrictions
|
317 |
+
* since 2.2.2-b3
|
318 |
+
*/
|
319 |
+
public function get_quantity_of_matching_products( $coupon ) {
|
320 |
+
$coupon = $this->get_coupon( $coupon );
|
321 |
+
|
322 |
+
$qty = 0;
|
323 |
+
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
324 |
+
$_product = $cart_item['data'];
|
325 |
+
if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
|
326 |
+
$qty += $cart_item['quantity'];
|
327 |
+
}
|
328 |
+
}
|
329 |
+
return $qty;
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* (API FUNCTION)
|
334 |
+
* The total value of the products in the cart that match the coupon restrictions
|
335 |
+
* since 2.2.2-b3
|
336 |
+
*/
|
337 |
+
public function get_subtotal_of_matching_products( $coupon ) {
|
338 |
+
$coupon = $this->get_coupon( $coupon );
|
339 |
+
|
340 |
+
$subtotal = 0;
|
341 |
+
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
342 |
+
$_product = $cart_item['data'];
|
343 |
+
if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
|
344 |
+
$subtotal += $_product->get_price() * $cart_item['quantity'];
|
345 |
+
}
|
346 |
+
}
|
347 |
+
return $subtotal;
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* (API FUNCTION)
|
352 |
+
* Test if coupon is valid for the product
|
353 |
+
* (this function is used to count the quantity of matching products)
|
354 |
+
*/
|
355 |
+
public function coupon_is_valid_for_product( $coupon, $product, $values = array() ) {
|
356 |
+
//Do not count the free products
|
357 |
+
if ( isset( $values['_wjecf_free_product_coupon'] ) ) {
|
358 |
+
return false;
|
359 |
+
}
|
360 |
+
|
361 |
+
//Ugly hack: $coupon->is_valid_for_product only works for these types
|
362 |
+
$original_discount_type = $coupon->discount_type;
|
363 |
+
if ( ! $this->coupon_is_type( $coupon, array( 'fixed_product', 'percent_product' ) ) ) {
|
364 |
+
$coupon->discount_type = 'fixed_product';
|
365 |
+
}
|
366 |
+
$valid = $coupon->is_valid_for_product( $product, $values );
|
367 |
+
$coupon->discount_type = $original_discount_type;
|
368 |
+
|
369 |
+
return $valid;
|
370 |
+
}
|
371 |
+
|
372 |
+
|
373 |
+
|
374 |
+
// =====================
|
375 |
+
|
376 |
+
/**
|
377 |
+
* Retrieve the id of the product or the variation id if it's a variant.
|
378 |
+
* @Returns int|bool The variation or product id. False if not a valid product
|
379 |
+
*/
|
380 |
+
public function get_product_or_variation_id( $product ) {
|
381 |
+
if ( $product instanceof WC_Product_Variation ) {
|
382 |
+
return $product->variation_id;
|
383 |
+
} elseif ( $product instanceof WC_Product ) {
|
384 |
+
return $product->id;
|
385 |
+
} else {
|
386 |
+
return false;
|
387 |
+
}
|
388 |
+
}
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Get a WC_Coupon object
|
392 |
+
* @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
|
393 |
+
* @return WC_Coupon The coupon object
|
394 |
+
*/
|
395 |
+
public function get_coupon( $coupon ) {
|
396 |
+
if ( is_int( $coupon ) ) {
|
397 |
+
global $wpdb;
|
398 |
+
$coupon = $wpdb->get_var( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE id = %s AND post_type = 'shop_coupon' AND post_status = 'publish'", $coupon ) );
|
399 |
+
}
|
400 |
+
if ( ! ( $coupon instanceof WC_Coupon ) ) {
|
401 |
+
$coupon = new WC_Coupon( $coupon );
|
402 |
+
}
|
403 |
+
return $coupon;
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Check the type of the coupon
|
408 |
+
* @param WC_Coupon $coupon The coupon to check
|
409 |
+
* @param string|array $type The type(s) we want to check for
|
410 |
+
* @return bool True if the coupon is of the type
|
411 |
+
*/
|
412 |
+
public function coupon_is_type( $coupon, $type ) {
|
413 |
+
//Backwards compatibility 2.2.11
|
414 |
+
if ( method_exists( $coupon, 'is_type' ) ) {
|
415 |
+
return $coupon->is_type( $type );
|
416 |
+
} else {
|
417 |
+
return ( $coupon->discount_type == $type || ( is_array( $type ) && in_array( $coupon->discount_type, $type ) ) ) ? true : false;
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
/**
|
422 |
+
* Get array of the selected shipping methods ids.
|
423 |
+
* @param int $coupon_id The coupon id
|
424 |
+
* @return array Id's of the shipping methods or an empty array.
|
425 |
+
*/
|
426 |
+
public function get_coupon_shipping_method_ids($coupon_id) {
|
427 |
+
$v = get_post_meta( $coupon_id, '_wjecf_shipping_methods', true );
|
428 |
+
if ($v == '') {
|
429 |
+
$v = array();
|
430 |
+
}
|
431 |
+
|
432 |
+
return $v;
|
433 |
+
}
|
434 |
+
|
435 |
+
/**
|
436 |
+
* Get array of the selected payment method ids.
|
437 |
+
* @param int $coupon_id The coupon id
|
438 |
+
* @return array Id's of the payment methods or an empty array.
|
439 |
+
*/
|
440 |
+
public function get_coupon_payment_method_ids($coupon_id) {
|
441 |
+
$v = get_post_meta( $coupon_id, '_wjecf_payment_methods', true );
|
442 |
+
if ($v == '') {
|
443 |
+
$v = array();
|
444 |
+
}
|
445 |
+
|
446 |
+
return $v;
|
447 |
+
}
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Get array of the selected customer ids.
|
451 |
+
* @param int $coupon_id The coupon id
|
452 |
+
* @return array Id's of the customers (users) or an empty array.
|
453 |
+
*/
|
454 |
+
public function get_coupon_customer_ids($coupon_id) {
|
455 |
+
$v = get_post_meta( $coupon_id, '_wjecf_customer_ids', true );
|
456 |
+
//$v = array_map( 'intval', explode(",", get_post_meta( $coupon_id, '_wjecf_customer_ids', true ) ) );
|
457 |
+
if ($v == '') {
|
458 |
+
$v = array();
|
459 |
+
} else {
|
460 |
+
$v = array_map( 'intval', explode(",", $v ) );
|
461 |
+
}
|
462 |
+
|
463 |
+
return $v;
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Get array of the selected customer role ids.
|
468 |
+
* @param int $coupon_id The coupon id
|
469 |
+
* @return array Id's (string) of the customer roles or an empty array.
|
470 |
+
*/
|
471 |
+
public function get_coupon_customer_roles($coupon_id) {
|
472 |
+
$v = get_post_meta( $coupon_id, '_wjecf_customer_roles', true );
|
473 |
+
if ($v == '') {
|
474 |
+
$v = array();
|
475 |
+
}
|
476 |
+
|
477 |
+
return $v;
|
478 |
+
}
|
479 |
+
|
480 |
+
/**
|
481 |
+
* Get array of the excluded customer role ids.
|
482 |
+
* @param int $coupon_id The coupon id
|
483 |
+
* @return array Id's (string) of the excluded customer roles or an empty array.
|
484 |
+
*/
|
485 |
+
public function get_coupon_excluded_customer_roles($coupon_id) {
|
486 |
+
$v = get_post_meta( $coupon_id, '_wjecf_excluded_customer_roles', true );
|
487 |
+
if ($v == '') {
|
488 |
+
$v = array();
|
489 |
+
}
|
490 |
+
|
491 |
+
return $v;
|
492 |
+
}
|
493 |
+
|
494 |
+
public function is_pro() {
|
495 |
+
return $this instanceof WJECF_Pro_Controller;
|
496 |
+
}
|
497 |
+
|
498 |
+
|
499 |
+
private $_session_data = null;
|
500 |
+
/**
|
501 |
+
* Read something from the session
|
502 |
+
* @param string $key The key for identification
|
503 |
+
* @param any $default The default value (Default: false)
|
504 |
+
*
|
505 |
+
* @return The saved value if found, otherwise the default value
|
506 |
+
*/
|
507 |
+
public function get_session( $key, $default = false ) {
|
508 |
+
if ( $this->_session_data == null) {
|
509 |
+
$this->_session_data = WC()->session->get( '_wjecf_session_data', array() );
|
510 |
+
}
|
511 |
+
return isset( $this->_session_data[ $key ] ) ? $this->_session_data[ $key ] : $default;
|
512 |
+
}
|
513 |
+
|
514 |
+
/**
|
515 |
+
* Save something in the session
|
516 |
+
*
|
517 |
+
* @param string $key The key for identification
|
518 |
+
* @param anything $value The value to store
|
519 |
+
*/
|
520 |
+
public function set_session( $key, $value ) {
|
521 |
+
if ( $this->_session_data == null) {
|
522 |
+
$this->_session_data = WC()->session->get( '_wjecf_session_data', array() );
|
523 |
+
}
|
524 |
+
$this->_session_data[ $key ] = $value;
|
525 |
+
if ( $value !== null ) {
|
526 |
+
WC()->session->set( '_wjecf_session_data', $this->_session_data );
|
527 |
+
} else {
|
528 |
+
|
529 |
+
}
|
530 |
+
}
|
531 |
+
|
532 |
+
|
533 |
+
/**
|
534 |
+
* Get overwritable template filename
|
535 |
+
* @param string $template_name
|
536 |
+
* @return string Template filename
|
537 |
+
*/
|
538 |
+
public function get_template_filename( $template_name ) {
|
539 |
+
$template_path = 'woocommerce-auto-added-coupons';
|
540 |
+
|
541 |
+
$plugin_template_path = plugin_dir_path( dirname(__FILE__) ) . 'templates/';
|
542 |
+
|
543 |
+
//Get template overwritten file
|
544 |
+
$template = locate_template( trailingslashit( $template_path ) . $template_name );
|
545 |
+
|
546 |
+
// Get default template
|
547 |
+
if ( ! $template ) {
|
548 |
+
$template = $plugin_template_path . $template_name;
|
549 |
+
}
|
550 |
+
|
551 |
+
return $template;
|
552 |
+
}
|
553 |
+
|
554 |
+
/**
|
555 |
+
* Include a template file, either from this plugins directory or overwritten in the themes directory
|
556 |
+
* @param type $template_name
|
557 |
+
* @return type
|
558 |
+
*/
|
559 |
+
public function include_template( $template_name, $variables = array() ) {
|
560 |
+
extract( $variables );
|
561 |
+
include( $this->get_template_filename( $template_name ) );
|
562 |
+
}
|
563 |
+
|
564 |
+
/**
|
565 |
+
* Log message for debugging
|
566 |
+
* @param string $string The message to log
|
567 |
+
* @param int $skip_backtrace Defaults to 0, amount of items to skip in backtrace to fetch class and method name
|
568 |
+
*/
|
569 |
+
public function log( $string, $skip_backtrace = 0) {
|
570 |
+
if ( $this->debug_mode ) {
|
571 |
+
$nth = 1 + $skip_backtrace;
|
572 |
+
$bt = debug_backtrace();
|
573 |
+
$class = $bt[$nth]['class'];
|
574 |
+
$function = $bt[$nth]['function'];
|
575 |
+
|
576 |
+
$row = array(
|
577 |
+
'time' => time(),
|
578 |
+
'class' => $class,
|
579 |
+
'function' => $function,
|
580 |
+
'filter' => current_filter(),
|
581 |
+
'message' => $string,
|
582 |
+
);
|
583 |
+
$this->log[] = $row;
|
584 |
+
error_log( $row['filter'] . ' ' . $row['class'] . '::' . $row['function'] . ' ' . $row['message'] );
|
585 |
+
}
|
586 |
+
}
|
587 |
+
|
588 |
+
/**
|
589 |
+
* Output the log as html
|
590 |
+
*/
|
591 |
+
public function render_log() {
|
592 |
+
if ( $this->debug_mode && current_user_can( 'manage_options' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) {
|
593 |
+
echo "<table class='soft79_wjecf_log'>";
|
594 |
+
foreach( $this->log as $row ) {
|
595 |
+
$cells = array(
|
596 |
+
date("H:i:s", $row['time']),
|
597 |
+
esc_html( $row['filter'] ),
|
598 |
+
esc_html( $row['class'] . '::' . $row['function'] ),
|
599 |
+
esc_html( $row['message'] ),
|
600 |
+
);
|
601 |
+
echo "<tr><td>" . implode( "</td><td>", $cells ) . "</td></tr>";
|
602 |
+
}
|
603 |
+
$colspan = isset( $cells ) ? count( $cells ) : 1;
|
604 |
+
echo "<tr><td colspan='" . $colspan . "'>Current coupons in cart: " . implode( ", ", WC()->cart->applied_coupons ) . "</td></tr>";
|
605 |
+
echo "</table>";
|
606 |
+
}
|
607 |
+
}
|
608 |
+
|
609 |
+
}
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQ
|
|
4 |
Tags: woocommerce, coupons, discount
|
5 |
Requires at least: 4.0.0
|
6 |
Tested up to: 4.3.1
|
7 |
-
Stable tag: 2.
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
@@ -17,13 +17,15 @@ Very easy to use, the functionality is conveniently integrated to the WooCommerc
|
|
17 |
|
18 |
Full documentation is available at [www.soft79.nl](http://www.soft79.nl/documentation/wjecf).
|
19 |
|
20 |
-
* (PRO) Add *free products* to the customer's cart based on coupon rules
|
21 |
-
* (PRO) Allow a cart discount to be applied based on quantity / subtotal of matching products
|
22 |
* *Auto coupons*: Allow coupons to be automatically added to the users cart if it's restrictions are met,
|
23 |
* Apply coupon via an url,
|
24 |
* Restrict coupon by shipping method,
|
25 |
* Restrict coupon by payment method,
|
26 |
* Restrict coupon by a combination of products
|
|
|
|
|
|
|
|
|
27 |
|
28 |
For more information or the PRO version please visit [www.soft79.nl](http://www.soft79.nl)
|
29 |
|
@@ -100,6 +102,21 @@ Sure! [This](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQ
|
|
100 |
4. Additionals restrictions based on shipping or payment method or the customer
|
101 |
|
102 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
= 2.2.5.1 =
|
104 |
* FIX: When checkbox 'Individual use' was ticked, Autocoupons would be removed/added multiple times
|
105 |
|
4 |
Tags: woocommerce, coupons, discount
|
5 |
Requires at least: 4.0.0
|
6 |
Tested up to: 4.3.1
|
7 |
+
Stable tag: 2.3.0
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
17 |
|
18 |
Full documentation is available at [www.soft79.nl](http://www.soft79.nl/documentation/wjecf).
|
19 |
|
|
|
|
|
20 |
* *Auto coupons*: Allow coupons to be automatically added to the users cart if it's restrictions are met,
|
21 |
* Apply coupon via an url,
|
22 |
* Restrict coupon by shipping method,
|
23 |
* Restrict coupon by payment method,
|
24 |
* Restrict coupon by a combination of products
|
25 |
+
* (PRO) Add *free products* to the customer's cart based on coupon rules
|
26 |
+
* (PRO) Allow a cart discount to be applied based on quantity / subtotal of matching products
|
27 |
+
* (PRO) Set Auto Coupon priorities (Useful for 'Individual Use Only'-coupons)
|
28 |
+
* (PRO) API to allow developers to use functions of this plugin
|
29 |
|
30 |
For more information or the PRO version please visit [www.soft79.nl](http://www.soft79.nl)
|
31 |
|
102 |
4. Additionals restrictions based on shipping or payment method or the customer
|
103 |
|
104 |
== Changelog ==
|
105 |
+
= 2.3.0 =
|
106 |
+
* (PRO) FEATURE: Allow customer to choose a free product
|
107 |
+
* (PRO) FEATURE: Setting the priority of auto coupons (Useful for Individual use coupons)
|
108 |
+
* (PRO) FEATURE: Display extra columns on the Coupon Admin page (auto coupon, individual use, priority, free products)
|
109 |
+
* (PRO) TWEAK: Free products: Display 'Free!' as subtotal for free products, (adaptable with filter 'wjecf_free_cart_item_subtotal' )
|
110 |
+
* (PRO) FIX: Free products: Plugin wouldn't always detect the free products in cart and kept appending free products
|
111 |
+
* (PRO) Introduction of the API for developers, see wjecf-pro-api.php
|
112 |
+
* FEATURE: Filter to only display Auto Coupons on the Coupon Admin page
|
113 |
+
* FIX: Compatibilty PHP 5.4
|
114 |
+
* FIX: Rewritten and simplified Autocoupon removal/addition routine making it more robust
|
115 |
+
* FIX: Multiplier value calculation (as for now only used for Free Products)
|
116 |
+
* FIX: Coupon must never be valid for free products (_wjecf_free_product_coupon set in cart_item)
|
117 |
+
* INTERNAL: Refactoring of all classes
|
118 |
+
* INTERNAL: New log for debugging
|
119 |
+
|
120 |
= 2.2.5.1 =
|
121 |
* FIX: When checkbox 'Individual use' was ticked, Autocoupons would be removed/added multiple times
|
122 |
|
woocommerce-jos-autocoupon.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: WooCommerce Extended Coupon Features
|
4 |
* Plugin URI: http://www.soft79.nl
|
5 |
* Description: Additional functionality for WooCommerce Coupons: Apply certain coupons automatically, allow applying coupons via an url, etc...
|
6 |
-
* Version: 2.
|
7 |
* Author: Jos Koenis
|
8 |
* License: GPL2
|
9 |
*/
|
@@ -13,44 +13,47 @@
|
|
13 |
|
14 |
defined('ABSPATH') or die();
|
15 |
|
16 |
-
|
17 |
-
require_once( 'includes/wjecf-coupon-extensions.php' );
|
18 |
-
@include_once( 'includes/wjecf-pro.php' );
|
19 |
-
@include_once( 'includes/wjecf-pro-free-products.php' );
|
20 |
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
} else {
|
31 |
-
if ( ! function_exists( 'wjecf_load_plugin_textdomain' ) ) {
|
32 |
-
/**
|
33 |
-
* Get the instance if the WC_Jos_Extended_Coupon_Features_Controller
|
34 |
-
*/
|
35 |
-
function WJECF() {
|
36 |
-
return WC_Jos_Extended_Coupon_Features_Controller::instance();
|
37 |
-
}
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
function wjecf_load_plugin_textdomain() {
|
40 |
load_plugin_textdomain('woocommerce-jos-autocoupon', false, basename(dirname(__FILE__)) . '/languages/' );
|
41 |
}
|
42 |
add_action('plugins_loaded', 'wjecf_load_plugin_textdomain');
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
}
|
53 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
}
|
@@ -61,7 +64,7 @@ if ( WC_Jos_Extended_Coupon_Features_Controller::get_woocommerce_version() == fa
|
|
61 |
if ( ! function_exists( 'wjecf_plugin_meta' ) ) {
|
62 |
function wjecf_plugin_meta( $links, $file ) {
|
63 |
if ( strpos( $file, 'woocommerce-jos-autocoupon.php' ) !== false ) {
|
64 |
-
$links = array_merge( $links, array( '<a href="' .
|
65 |
}
|
66 |
return $links;
|
67 |
}
|
3 |
* Plugin Name: WooCommerce Extended Coupon Features
|
4 |
* Plugin URI: http://www.soft79.nl
|
5 |
* Description: Additional functionality for WooCommerce Coupons: Apply certain coupons automatically, allow applying coupons via an url, etc...
|
6 |
+
* Version: 2.3.0
|
7 |
* Author: Jos Koenis
|
8 |
* License: GPL2
|
9 |
*/
|
13 |
|
14 |
defined('ABSPATH') or die();
|
15 |
|
16 |
+
if ( ! function_exists( 'wjecf_load_plugin_textdomain' ) ) {
|
|
|
|
|
|
|
17 |
|
18 |
+
require_once( 'includes/wjecf-controller.php' );
|
19 |
+
require_once( 'includes/abstract-wjecf-plugin.php' );
|
20 |
+
require_once( 'includes/admin/wjecf-admin.php' );
|
21 |
+
//Optional
|
22 |
+
@include_once( 'includes/wjecf-autocoupon.php' );
|
23 |
+
//PRO
|
24 |
+
@include_once( 'includes/wjecf-pro-controller.php' );
|
25 |
+
@include_once( 'includes/wjecf-pro-free-products.php' );
|
26 |
+
@include_once( 'includes/wjecf-pro-api.php' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
+
// Only Initiate the plugin if WooCommerce is active
|
29 |
+
if ( WJECF_Controller::get_woocommerce_version() == false ) {
|
30 |
+
add_action( 'admin_notices', 'wjecf_admin_notice' );
|
31 |
+
function wjecf_admin_notice() {
|
32 |
+
$msg = __( 'WooCommerce Extended Coupon Features is disabled because WooCommerce could not be detected.', 'woocommerce-jos-autocoupon' );
|
33 |
+
echo '<div class="error"><p>' . $msg . '</p></div>';
|
34 |
+
}
|
35 |
+
} else {
|
36 |
function wjecf_load_plugin_textdomain() {
|
37 |
load_plugin_textdomain('woocommerce-jos-autocoupon', false, basename(dirname(__FILE__)) . '/languages/' );
|
38 |
}
|
39 |
add_action('plugins_loaded', 'wjecf_load_plugin_textdomain');
|
40 |
|
41 |
+
/**
|
42 |
+
* Get the instance if the WJECF_Controller
|
43 |
+
*/
|
44 |
+
function WJECF() {
|
45 |
+
if ( class_exists( 'WJECF_Pro_Controller' ) ) {
|
46 |
+
return WJECF_Pro_Controller::instance();
|
47 |
+
} else {
|
48 |
+
return WJECF_Controller::instance();
|
49 |
}
|
50 |
}
|
51 |
+
|
52 |
+
|
53 |
+
$wjecf_extended_coupon_features = WJECF();
|
54 |
+
WJECF()->add_plugin('WJECF_Admin');
|
55 |
+
WJECF()->add_plugin('WJECF_AutoCoupon');
|
56 |
+
WJECF()->add_plugin('WJECF_Pro_Free_Products');
|
57 |
}
|
58 |
|
59 |
}
|
64 |
if ( ! function_exists( 'wjecf_plugin_meta' ) ) {
|
65 |
function wjecf_plugin_meta( $links, $file ) {
|
66 |
if ( strpos( $file, 'woocommerce-jos-autocoupon.php' ) !== false ) {
|
67 |
+
$links = array_merge( $links, array( '<a href="' . WJECF_Admin::get_donate_url() . '" title="Support the development" target="_blank">Donate</a>' ) );
|
68 |
}
|
69 |
return $links;
|
70 |
}
|