211 lines
5.7 KiB
PHP
211 lines
5.7 KiB
PHP
<?php
|
|
namespace Sodino\Services;
|
|
|
|
use Sodino\Repositories\RuleRepository;
|
|
|
|
/**
|
|
* Pricing Service
|
|
*/
|
|
class PricingService {
|
|
private $ruleRepository;
|
|
private $rulesCache = null;
|
|
private $freeShipping = false;
|
|
|
|
public function __construct(RuleRepository $ruleRepository) {
|
|
$this->ruleRepository = $ruleRepository;
|
|
}
|
|
|
|
/**
|
|
* Apply dynamic pricing to a product price
|
|
*/
|
|
public function applyDynamicPricing($price, $product) {
|
|
$price = $this->normalizePrice($price);
|
|
$rules = $this->getEnabledRules();
|
|
$matchedRule = null;
|
|
|
|
foreach ($rules as $rule) {
|
|
if ($this->ruleMatches($rule, $product)) {
|
|
if ($matchedRule === null || $rule->priority > $matchedRule->priority) {
|
|
$matchedRule = $rule;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($matchedRule) {
|
|
$price = $this->applyActions($matchedRule, $price);
|
|
}
|
|
|
|
return max(0, $price);
|
|
}
|
|
|
|
public function shouldApplyFreeShipping() {
|
|
$rules = $this->getEnabledRules();
|
|
foreach ($rules as $rule) {
|
|
if ($this->ruleMatches($rule, null) && $this->ruleHasFreeShipping($rule)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function resetFreeShippingFlag() {
|
|
$this->freeShipping = false;
|
|
}
|
|
|
|
private function getEnabledRules() {
|
|
if ($this->rulesCache === null) {
|
|
$this->rulesCache = $this->ruleRepository->getEnabled();
|
|
}
|
|
return $this->rulesCache;
|
|
}
|
|
|
|
private function normalizePrice($price) {
|
|
if ($price === '' || $price === null) {
|
|
return 0.0;
|
|
}
|
|
|
|
return floatval($price);
|
|
}
|
|
|
|
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 ruleMatches($rule, $product = null) {
|
|
if (!$this->isRuleActive($rule)) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($rule->conditions as $condition) {
|
|
if (!$this->evaluateCondition($condition, $product)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function isRuleActive($rule) {
|
|
if (!$rule->enabled) {
|
|
return false;
|
|
}
|
|
|
|
$now = current_time('Y-m-d H:i:s');
|
|
|
|
if (!empty($rule->start_date) && $now < $rule->start_date) {
|
|
return false;
|
|
}
|
|
|
|
if (!empty($rule->end_date) && $now > $rule->end_date) {
|
|
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 getCartTotal() {
|
|
if (!WC()->cart) {
|
|
return 0;
|
|
}
|
|
|
|
return floatval(WC()->cart->get_cart_contents_total());
|
|
}
|
|
|
|
private function getCartItemCount() {
|
|
if (!WC()->cart) {
|
|
return 0;
|
|
}
|
|
|
|
return 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']);
|
|
return (bool) array_intersect($product_cats, $categories);
|
|
}
|
|
|
|
private function productIsInIds($product, $ids) {
|
|
if (!$product || empty($ids)) {
|
|
return false;
|
|
}
|
|
|
|
return in_array($product->get_id(), $ids, true);
|
|
}
|
|
|
|
private function applyActions($rule, $price) {
|
|
foreach ($rule->actions as $action) {
|
|
$price = $this->applyAction($action, $price);
|
|
if (($action['type'] ?? '') === 'free_shipping') {
|
|
$this->freeShipping = true;
|
|
}
|
|
}
|
|
return $price;
|
|
}
|
|
|
|
private function ruleHasFreeShipping($rule) {
|
|
foreach ($rule->actions as $action) {
|
|
if (($action['type'] ?? '') === 'free_shipping') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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 'free_shipping':
|
|
return $price;
|
|
default:
|
|
return $price;
|
|
}
|
|
}
|
|
}
|