types = Array( 'percent_total_amount' => 'Percentage of cart total amount', 'percent_product_price' => 'Percentage of product price', 'fixed_product_price' => 'Fixed price of product price', 'fixed_cart_amount' => 'Fixed price of cart total amount', ); } /** * * Ensures only one instance of AWDP is loaded or can be loaded. * * @since 1.0.0 * @static * @see WordPress_Plugin_Template() * @return Main AWDP instance */ public static function instance($file = '', $version = '1.0.0') { if (is_null(self::$_instance)) { self::$_instance = new self($file, $version); } return self::$_instance; } /** * @return bool */ public function isActive() { return $this->_active; } public function cart_discount_items( $item_price, $cart_item ) { $prod_ID = $cart_item['data']->get_data()['slug']; $discounted_price = abs(wc_remove_number_precision($this->discounted_price[$prod_ID])); $actual_price = $cart_item['data']->get_data()['price']; $quantity = $cart_item['quantity']; if( $discounted_price > 0.0000000001 && ( $discounted_price < $actual_price ) ) { $item_price = ''; $item_price .= ''; $item_price .= ''.wc_price($actual_price).''; $item_price .= ''. wc_price($discounted_price).''; } else if( $discounted_price > $actual_price ) { $item_price = ''; $item_price .= ''; $item_price .= ''.wc_price($actual_price).''; $item_price .= ''. wc_price($discounted_price).''; } return $item_price; } public function get_product_price_html($item_price, $product) { // $latest_price = $product->get_data()['price'] - wc_remove_number_precision($_discounts[$product->get_data()['slug']]); $prod_ID = $product->get_data()['slug']; $latest_price = abs(wc_remove_number_precision($this->discounted_price[$prod_ID])); $actual_price = $product->get_data()['price']; if( $latest_price > 0.0000000001 && ( $latest_price < $actual_price ) ) { $item_price = ''; $item_price .= ''; $item_price .= ''.wc_price($actual_price).''; $item_price .= ''. wc_price($latest_price).''; } else if( $latest_price > $actual_price ) { $item_price = ''; $item_price .= ''; $item_price .= ''.wc_price($actual_price).''; $item_price .= ''. wc_price($latest_price).''; } return $item_price; } public function get_product_price($price, $product) { if($product) { $id = $product->get_id(); if (isset($this->product_lists[$id])) { return $this->product_lists[$id]['price']; } return $this->calculate_discount($price, $product); } } public function calculate_discount( $price, $product ) { $this->load_rules(); $products_with_discount = []; global $woocommerce; $this->discounted_price[$product->get_data()['slug']] = -0.0000000001; foreach ($this->discount_rules as $k => $rule) { //validate cart based rules if (!$this->validate_discount_rules($product, $rule, ['cart_total_amount', 'cart_items', 'cust_prev_order_count'])) { continue; } if(!$this->get_items_to_apply_discount($product, $rule)){ continue; } if (!isset($this->discounts[$rule['id']])) { $this->discounts[$rule['id']] = ['label' => $rule['label'], 'discount_type' => $rule['type'], 'discount_remainder' => -1]; } // calculating discount if('percent_product_price' == $rule['type']) $this->apply_discount_percent_product_price($rule, $product); else if('fixed_product_price' == $rule['type']) $this->apply_discount_fixed_product_price($rule, $product); else if ('percent_total_amount' == $rule['type']) $this->apply_discount_percent_total_amount($rule, $product); else if ('fixed_cart_amount' == $rule['type']) $this->apply_discount_fixed_price_total_amount($rule, $product); } $_discounts = array(); foreach ($this->discounts as $discounts) { if($discounts['discount_type'] == 'percent_product_price' || $discounts['discount_type'] == 'fixed_product_price'){ foreach ($discounts['discounts'] as $key => $discount) { if (!isset($_discounts[$key])) { $_discounts[$key] = 0.0; } $_discounts[$key] += $discount; } } } if($product->get_data()['price'] >= wc_remove_number_precision($_discounts[$product->get_data()['slug']])) $latest_price = $product->get_data()['price'] - wc_remove_number_precision($_discounts[$product->get_data()['slug']]); else $latest_price = 0; return $latest_price; } public function load_rules() { if ($this->discount_rules === false) { $datenow = date("Y-m-d"); $awdp_discount_args = array( 'post_type' => AWDP_POST_TYPE, 'post_status' => 'publish', 'posts_per_page' => -1, 'meta_key' => 'discount_priority', //setting the meta_key which will be used to order 'orderby' => 'meta_value_num', //if the meta_key (population) is numeric use meta_value_num instead 'order' => 'ASC', //setting order direction 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'discount_status', 'value' => 1, 'compare' => '=', 'type' => 'NUMERIC' ), array( 'key' => 'discount_start_date', 'value' => $datenow, 'compare' => '<=', 'type' => 'DATE' ), array( 'relation' => 'OR', array( 'key' => 'discount_end_date', 'value' => $datenow, 'compare' => '>=', 'type' => 'DATE' ), array( 'key' => 'discount_end_date', 'compare' => 'NOT EXISTS', ), array( 'key' => 'discount_end_date', 'value' => '', 'compare' => '=', ), ) ) ); $awdp_discount_rules = get_posts($awdp_discount_args); $discount_rules = array(); foreach ($awdp_discount_rules as $rule) { $rule_type = get_post_meta($rule->ID, 'discount_type', true); $discount_config = get_post_meta($rule->ID, 'discount_config', true); $discount_rules[] = array( 'id' => $rule->ID, 'label' => ($discount_config['label'] != '') ? $discount_config['label'] : $rule->post_title, 'discount' => get_post_meta($rule->ID, 'discount_value', true), 'inc_tax' => $discount_config['inc_tax'], 'disable_on_sale' => $discount_config['disable_on_sale'], 'sequentially' => $discount_config['sequentially'], 'product_list' => get_post_meta($rule->ID, 'discount_product_list', true), 'rules' => unserialize(base64_decode($discount_config['rules'])), 'type' => $rule_type, ); } $this->discount_rules = $discount_rules; } } public function validate_discount_rules($cart_obj, $rule, $rules_to_validate = array(), $item = false) { $evel_str = ''; // $rules_to_validate = ['cart_total_amount', 'cart_items', 'cust_prev_order_count']; $result = true;// if no rules, the validation must be true if (isset($rule['rules']) && is_array($rule['rules']) && !empty($rule['rules'])) { foreach ($rule['rules'] as $val) { if (is_array($val['rules']) && count($val['rules']) && !empty($val['rules'])) { // echo 'rule'; $evel_str .= '('; foreach ($val['rules'] as $rul) { $evel_str .= '('; if (in_array($rul['rule']['item'], $rules_to_validate)) { if ($this->eval_rule($rul['rule'], $cart_obj, $item)) { $evel_str .= ' true '; } else { $evel_str .= ' false '; } } else { $evel_str .= ' true '; } $evel_str .= ') ' . (($rul['operator'] !== false) ? $rul['operator'] : '') . ' '; } if (count($val['rules']) > 0 && !empty($val['rules'])) { preg_match_all('/\(.*\)/', $evel_str, $match); $evel_str = $match[0][0] . ' '; } $evel_str .= ') ' . (($val['operator'] !== false) ? $val['operator'] : '') . ' '; } } if (count($rule['rules']) > 0 && !empty($rule['rules']) && $evel_str != '') { preg_match_all('/\(.*\)/', $evel_str, $match); $evel_str = $match[0][0] . ' '; } $evel_str = str_replace(['and', 'or'], ['&&', '||'], $evel_str); if ($evel_str !== '') { $result = eval('return ' . $evel_str . ';'); } } return $result; } public function eval_rule($rule, $cart_obj, $item = false) { if ('cart_total_amount' == $rule['item']) { // $item_val = $cart_obj->get_total(); $item_val = (float)WC()->cart->total; $rel_val = (float)$rule['value']; } else if ('product_price' == $rule['item']) { // $item_val = $item['data']->get_price('edit'); $item_val = (float)$item->get_data()['price']; $rel_val = (float)$rule['value']; } else { return false; } switch ($rule['condition']) { case 'equal_to': if (@abs(($item_val - $rel_val) / $item_val) < 0.00001) { return true; } break; case 'less_than': if ($item_val < $rel_val) { return true; } break; case 'less_than_eq': if ($item_val < $rel_val || abs(($item_val - $rel_val) / $item_val) < 0.0001) { return true; } break; case 'greater_than': if ($item_val > $rel_val) { return true; } break; case 'greater_than_eq': if ($item_val > $rel_val || abs(($item_val - $rel_val) / $item_val) < 0.0001) { return true; } break; } return false; } public function get_items_to_apply_discount($product, $rule) { $items = array(); global $woocommerce; //validate with $rule if (!$this->check_in_product_list($product, $rule)) { return false; } if (!$this->validate_discount_rules($product, $rule, ['product_price'], $product)) { return false; } if (isset($rule['disable_on_sale']) && $rule['disable_on_sale'] && $product->is_on_sale('edit')) { return true; } return true; } public function check_in_product_list($product, $rule) { if (0 == $rule['product_list']) { return true; } else { $this->set_product_list(); $pro_id = $product->get_data()['parent']; // in case of variation if ($pro_id == 0) { $pro_id = $product->get_id(); } return isset($this->product_lists[$rule['product_list']]) && in_array($pro_id, $this->product_lists[$rule['product_list']]); } } public function set_product_list() { if (false == $this->product_lists) { if (false === ($product_lists = get_transient(AWDP_PRODUCTS_TRANSIENT_KEY))) { $post_type = AWDP_PRODUCT_LIST; global $wpdb; $product_lists = array(); $lists = $wpdb->get_col($wpdb->prepare(" SELECT pm.meta_value FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '%s' AND p.post_status = '%s' AND p.post_type = '%s' ", 'discount_product_list', 'publish', AWDP_POST_TYPE)); $post_ids = get_posts( array( 'fields' => 'ids', 'post_type' => AWDP_PRODUCT_LIST, 'post__in' => $lists, 'posts_per_page' => -1,hu ) ); foreach ($post_ids as $id) { $list_type = get_post_meta($id, 'list_type', true); $other_config = get_post_meta($id, 'product_list_config', true); // $other_config = array( // 'selectedProducts' => serialize($data['selectedProducts']), // 'productAuthor' => serialize($data['productAuthor']), // 'excludedProducts' => serialize($data['excludedProducts']), // 'taxRelation' => serialize($data['taxRelation']), // 'rules' => serialize($data['rules']), // ); $product_lists[$id] = array(); if ('products_selection' == $list_type) { $product_lists[$id] = ($other_config['selectedProducts']); } else { $tax_rules = ($other_config['rules']); $tax_rules = ($tax_rules && is_array($tax_rules) && !empty($tax_rules)) ? $tax_rules : false; $excludedProducts = ($other_config['excludedProducts']); $args = array( 'post_type' => 'product', 'posts_per_page' => -1, 'post__not_in' => $excludedProducts ); if (false !== $tax_rules) { if (isset($tax_rules[0]['rules']) && is_array($tax_rules[0]['rules'])) { $tax_query = array( 'relation' => ('or' == strtolower($other_config['taxRelation'])) ? 'OR' : 'AND' ); foreach ($tax_rules[0]['rules'] as $tr) { $tax_query[] = array( 'taxonomy' => $tr['rule']['item'], 'field' => 'term_id', 'terms' => $tr['rule']['value'], ); } $args['tax_query'] = $tax_query; } } $query = new WP_Query($args); $product_lists[$id] = wp_list_pluck($query->posts, 'ID'); } } set_transient(AWDP_PRODUCTS_TRANSIENT_KEY, $product_lists, 7 * 24 * HOUR_IN_SECONDS); } $this->product_lists = $product_lists; } } // public function validate_product_rules($cart_obj, $item, $rule) // { // $evel_str = ''; // $rules_to_validate = ['product_price']; // $result = true;// if no rules, the validation must be true // if (isset($rule['rules']) && is_array($rule['rules'])) { // foreach ($rule['rules'] as $val) { // $evel_str .= '('; // if (is_array($val['rules']) && count($val['rules'])) { // foreach ($val['rules'] as $rul) { // $evel_str .= '('; // if (in_array($rul['rule']['item'], $rules_to_validate)) { // if ($this->eval_rule($rul['rule'], $cart_obj, $item)) { // $evel_str .= ' true '; // } else { // $evel_str .= ' false '; // } // } else { // $evel_str .= ' true '; // } // // $evel_str .= ') ' . (($rul['operator'] !== false) ? $rul['operator'] : '') . ' '; // } // // if (count($val['rules']) > 0) { // preg_match_all('/\(.*\)/', $evel_str, $match); // $evel_str = $match[0][0] . ' '; // } // // $evel_str .= ') ' . (($val['operator'] !== false) ? $val['operator'] : '') . ' '; // } // } // // if (count($val['rules']) > 0) { // preg_match_all('/\(.*\)/', $evel_str, $match); // $evel_str = $match[0][0] . ' '; // } // // $evel_str = str_replace(['and', 'or'], ['&&', '||'], $evel_str); // $result = eval('return ' . $evel_str . ';'); // } // return $result; // } // Cart Total Discount /////////////////////////////// public function apply_discount_percent_total_amount($rule, $item) { $total_discount = 0; $cart_total = 0; $prod_ID = $item->get_data()['slug']; //var_dump($item->get_data()['slug']); if($this->discounted_price[$prod_ID] != '' && $this->discounted_price[$prod_ID] >= 0) { $price_to_discount = $this->discounted_price[$prod_ID]; } else { // Find out how much price is available to discount for the item. // $discounted_price = $this->get_discounted_price_in_cents($item, $rule['inc_tax'], true); // $actual_price = $this->get_discounted_price_in_cents($item, $rule['inc_tax'], false); // $price_to_discount = ($rule['sequentially']) ? $discounted_price : $actual_price; // check if apply_ sequential $price_to_discount = wc_add_number_precision($item->get_data()['price']); } $discount = $price_to_discount * ((int)$rule['discount'] / 100); $discount = min($price_to_discount, $discount); // Store code and discount amount per item. $cart_total = $cart_total + $price_to_discount; $total_discount = $total_discount + $discount; // $prod_ID = $item->get_data()['slug']; if(!$this->discounts[$rule['id']]['discount_applied'][$prod_ID]) { $this->discounts[$rule['id']]['discounts'][$prod_ID] += $discount; $this->discounts[$rule['id']]['discount_applied'][$prod_ID] = true; } // $cart_total_discount = wc_round_discount($cart_total * ($rule['discount'] / 100), 0); // if ($total_discount < $cart_total_discount) { // $this->apply_discount_remainder($rule, $items_to_apply, $cart_total_discount - $total_discount); // } } public function apply_discount_fixed_price_total_amount($rule, $item) { $total_discount = 0; $cart_total = 0; $prod_ID = $item->get_data()['slug']; if($this->discounted_price[$prod_ID] != '' && $this->discounted_price[$prod_ID] >= 0) { $price_to_discount = $this->discounted_price[$prod_ID]; } else { // $price_to_discount = wc_add_number_precision($this->get_discounted_price_in_cents($item, $rule['inc_tax'], false)); $price_to_discount = wc_add_number_precision($item->get_data()['price']); } $discount = wc_add_number_precision($rule['discount']); if($this->discounts[$rule['id']]['discount_remainder'] >= 0){ $discount = $this->discounts[$rule['id']]['discount_remainder']; } if(intval($price_to_discount) >= $discount && $discount > 0) { $discounted_price = $price_to_discount - $discount; $product_discount = $discount; $this->discounts[$rule['id']]['discount_remainder'] = 0; } else if(intval($price_to_discount) < $discount && $discount > 0){ $discounted_price = $price_to_discount; $product_discount = $discounted_price; $this->discounts[$rule['id']]['discount_remainder'] = $discount - $price_to_discount; } else { $product_discount = 0; } if(!$this->discounts[$rule['id']]['discount_applied'][$prod_ID]) { $this->discounts[$rule['id']]['discounts'][$prod_ID] += $product_discount; $this->discounts[$rule['id']]['discount_applied'][$prod_ID] = true; } } // Product Based Discounts //////////////////////////////// public function apply_discount_fixed_product_price($rule, $item) { // $total_discount = 0; // $cart_total = 0; // $single_discount_amount = 0; // global $woocommerce; $prod_ID = $item->get_data()['slug']; if($this->discounted_price[$prod_ID] != '' && $this->discounted_price[$prod_ID] >= 0) { $product_original_price = $this->discounted_price[$prod_ID]; } else { // $product_original_price = $this->get_individual_discounted_price_in_cents($item, $rule['inc_tax'], false);] // $product_original_price = wc_add_number_precision($item->get_data()['price']); $product_original_price = wc_add_number_precision($item->get_data()['price']); } $discount_amount = wc_add_number_precision($rule['discount']); if($product_original_price >= $discount_amount) { $updated_product_price = $product_original_price - $discount_amount; $single_discount_amount = $discount_amount; } else { $updated_product_price = 0; $single_discount_amount = $product_original_price; } if(!$this->discounts[$rule['id']]['discount_applied'][$prod_ID]) { $this->discounts[$rule['id']]['discounts'][$prod_ID] += $single_discount_amount; $this->discounts[$rule['id']]['discount_applied'][$prod_ID] = true; } $this->discounted_price[$prod_ID] = $updated_product_price; // $cart_total = $cart_total + $discounted_price; } public function apply_discount_percent_product_price($rule, $item) { $total_discount = 0; $cart_total = 0; global $woocommerce; $prod_ID = $item->get_data()['slug']; if($this->discounted_price[$prod_ID] != '' && $this->discounted_price[$prod_ID] >= 0) { $product_original_price = $this->discounted_price[$prod_ID]; } else { // Find out how much price is available to discount for the item. // $discounted_price = $this->get_individual_discounted_price_in_cents($item, $rule['inc_tax'], true); // $actual_price = $this->get_individual_discounted_price_in_cents($item, $rule['inc_tax'], false); // sequential // $product_original_price = ($rule['sequentially']) ? $discounted_price : $actual_price; // check if apply_ sequential $product_original_price = wc_add_number_precision($item->get_data()['price']); } $discount = floor($product_original_price * ($rule['discount'] / 100)); // $discount = min($discounted_price, $discount); if($product_original_price >=$discount) $updated_product_price = $product_original_price - $discount; else $updated_product_price = 0; if(!$this->discounts[$rule['id']]['discount_applied'][$prod_ID]) { $this->discounts[$rule['id']]['discounts'][$prod_ID] += $discount; $this->discounts[$rule['id']]['discount_applied'][$prod_ID] = true; } $this->discounted_price[$prod_ID] = $updated_product_price; } ///////////////////////////// public function get_discounted_price_in_cents($item, $include_tax = true, $sequential = false) { $actual_price = $item->get_data()['price']; if ($include_tax) { $price = wc_get_price_including_tax($item, array( 'price' => $actual_price, )); } else { $price = wc_get_price_excluding_tax($item, array( 'price' => $actual_price, )); } if($sequential) return absint(round($price - wc_remove_number_precision($this->get_discount($item->get_id(), true)))); else return $price; } public function get_individual_discounted_price_in_cents($item, $include_tax = true, $sequential = false) { $latest_price = ''; if ($include_tax) { $price = wc_get_price_including_tax($item, array( 'price' => $item->get_data()['price'], )); } else { $price = wc_get_price_excluding_tax($item, array( 'price' => $item->get_data()['price'], )); } if($sequential) return absint(round($price - $this->get_discount($item->get_id(), true))); else return $price; } public function get_discount($key, $in_cents = false) { $item_discount_totals = $this->get_discounts_by_item($in_cents); return isset($item_discount_totals[$key]) ? $item_discount_totals[$key] : 0; } public function get_discounts_by_item($in_cents = false) { $discounts = $this->discounts; $item_discount_totals = array(); foreach ($discounts as $item_discounts) { if($item_discounts['discounts']){ foreach ($item_discounts['discounts'] as $item_key => $item_discount) { if (!isset($item_discount_totals[$item_key])) { $item_discount_totals[$item_key] = 0.0; } $item_discount_totals[$item_key] += $item_discount; } } } return $in_cents ? $item_discount_totals : $item_discount_totals; } protected function apply_discount_remainder($rule, $items_to_apply, $amount) { $total_discount = 0; foreach ($items_to_apply as $item) { for ($i = 0; $i < $item->quantity; $i++) { // Find out how much price is available to discount for the item. $discounted_price = $this->get_discounted_price_in_cents($item); // $price_to_discount = (false) ? $discounted_price : $item->price;// check if apply_ sequential $discount = min($discounted_price, 1); // Store totals. $total_discount += $discount; // Store code and discount amount per item. $this->discounts[$rule['id']]['discounts'][$item->key] += $discount; if ($total_discount >= $amount) { break 2; } } if ($total_discount >= $amount) { break; } } return $total_discount; } public function show_discounts($cart_obj) { if (is_admin() && !defined('DOING_AJAX')) return; $cart_contents = $cart_obj->get_cart(); $prod_QNT = []; foreach($cart_contents as $cart_content){ $prod_QNT[$cart_content['data']->get_data()['slug']] = $cart_content['quantity']; } global $woocommerce; $_discounts = array(); foreach ($this->discounts as $discounts) { if($discounts['discount_type'] == 'percent_total_amount' || $discounts['discount_type'] == 'fixed_cart_amount'){ $total = 0; foreach ($discounts['discounts'] as $key => $discount) { $total = $total + ( wc_remove_number_precision($discount) * $prod_QNT[$key] ); } if ( $total > 0 ) { $woocommerce->cart->add_fee($discounts['label'], $total * -1); } } } } /** * Cloning is forbidden. * * @since 1.0.0 */ public function __clone() { _doing_it_wrong(__FUNCTION__, __('Cheatin’ huh?'), $this->_version); } /** * Unserializing instances of this class is forbidden. * * @since 1.0.0 */ public function __wakeup() { _doing_it_wrong(__FUNCTION__, __('Cheatin’ huh?'), $this->_version); } }