ruleRepository = $ruleRepository; $this->trackingService = $trackingService; $this->settings = Settings::getInstance(); $this->cache = Cache::getInstance(); } public function applyDynamicPricing($price, $product) { if (!$this->settings->isPricingEnabled()) { return $price; } if (!$price || !is_numeric($price)) { return $price; } $price = $this->normalizePrice($price); if (!$this->settings->get('cart_pricing_enabled') && is_cart()) { return $price; } $originalPrice = $price; $rules = $this->getApplicableRules($product); if (empty($rules)) { return $price; } if (!$this->settings->get('allow_multiple_rules')) { $chosenRule = $this->chooseRule($rules, $price); $rules = $chosenRule ? [$chosenRule] : []; } foreach ($rules as $rule) { $oldPrice = $price; $price = $this->applyRuleActions($rule, $price); if ($price < $oldPrice) { $this->trackingService->recordDiscountApplied($product, $oldPrice, $price, $rule->id); $this->ruleRepository->incrementUsage($rule->id); } } $price = $this->enforceLimits($originalPrice, $price); return max(0, $price); } private function getApplicableRules($product) { $cache_key = 'applicable_rules_' . ($product ? $product->get_id() : 'all'); return $this->cache->remember($cache_key, function() use ($product) { $rules = $this->ruleRepository->getEnabled(); $applicable = []; foreach ($rules as $rule) { if ($this->ruleMatches($rule, $product)) { $applicable[] = $rule; } } return $applicable; }, 300, 'pricing'); } private function chooseRule(array $rules, $price) { $strategy = $this->settings->get('strategy', 'priority'); if ($strategy === 'highest_discount') { usort($rules, function ($a, $b) use ($price) { return $this->estimateRuleDiscount($b, $price) <=> $this->estimateRuleDiscount($a, $price); }); return $rules[0] ?? null; } if ($strategy === 'first_valid') { return $rules[0] ?? null; } usort($rules, function ($a, $b) { return $b->priority <=> $a->priority; }); return $rules[0] ?? null; } private function normalizePrice($price) { if ($price === '' || $price === null) { return 0.0; } return floatval($price); } private function ruleMatches($rule, $product = null) { if (!$rule->isActive()) { return false; } if ($rule->hasReachedLimit()) { return false; } if (!empty($rule->user_roles) && is_array($rule->user_roles)) { if (!$this->userHasAllowedRole($rule->user_roles)) { return false; } } if (empty($rule->conditions)) { return true; } foreach ($rule->conditions as $condition) { if (!$this->evaluateCondition($condition, $product)) { return false; } } return true; } private function evaluateCondition($condition, $product = null) { $type = $condition['type'] ?? ''; $value = $condition['value'] ?? null; switch ($type) { case 'user_type': return $this->getUserType() === $value; case 'cart_total_min': return $this->getCartTotal() >= floatval($value); case 'cart_total_max': return $this->getCartTotal() <= floatval($value); case 'cart_item_count_min': return $this->getCartItemCount() >= intval($value); case 'cart_item_count_max': return $this->getCartItemCount() <= intval($value); case 'product_category': return $this->productHasCategory($product, (array) $value); case 'product_ids': return $this->productIsInIds($product, (array) $value); default: return true; } } private function getUserType() { if (!is_user_logged_in()) { return 'guest'; } $user_id = get_current_user_id(); $order_count = wc_get_customer_order_count($user_id); return $order_count > 0 ? 'returning' : 'new'; } private function userHasAllowedRole($roles) { if (!is_user_logged_in()) { return false; } $user = wp_get_current_user(); foreach ($roles as $role) { if (in_array($role, $user->roles, true)) { return true; } } return false; } private function getCartTotal() { if (!function_exists('WC') || !WC()->cart) { return 0; } return floatval(WC()->cart->get_subtotal()); } private function getCartItemCount() { if (!function_exists('WC') || !WC()->cart) { return 0; } return intval(WC()->cart->get_cart_contents_count()); } private function productHasCategory($product, $categories) { if (!$product || empty($categories)) { return false; } $product_cats = wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'ids']); if (is_wp_error($product_cats)) { return false; } return (bool) array_intersect($product_cats, array_map('intval', $categories)); } private function productIsInIds($product, $ids) { if (!$product || empty($ids)) { return false; } return in_array($product->get_id(), array_map('intval', $ids), true); } private function applyRuleActions($rule, $price) { foreach ($rule->actions as $action) { $price = $this->applyAction($action, $price); } return $price; } private function applyAction($action, $price) { $type = $action['type'] ?? ''; $value = isset($action['value']) ? floatval($action['value']) : 0; switch ($type) { case 'discount_percent': if ($value <= 0) { return $price; } return $price * (1 - $value / 100); case 'discount_fixed': if ($value <= 0) { return $price; } return $price - $value; case 'set_price': return $value > 0 ? $value : $price; case 'free_shipping': return $price; default: return $price; } } private function enforceLimits($originalPrice, $price) { $minPrice = max(0, floatval($this->settings->get('min_product_price', 0))); $price = max($price, $minPrice); $maxDiscountPercent = floatval($this->settings->get('max_discount_percent', 100)); if ($maxDiscountPercent > 0 && $maxDiscountPercent < 100) { $maxDiscount = $originalPrice * ($maxDiscountPercent / 100); $minAllowedPrice = $originalPrice - $maxDiscount; $price = max($minAllowedPrice, $price); } return $price; } private function estimateRuleDiscount($rule, $price) { foreach ($rule->actions as $action) { if (($action['type'] ?? '') === 'discount_percent') { return $price * floatval($action['value']) / 100; } if (($action['type'] ?? '') === 'discount_fixed') { return floatval($action['value']); } } return 0; } }