Plugin Directory

Changeset 3412190


Ignore:
Timestamp:
12/05/2025 12:33:27 PM (6 weeks ago)
Author:
algol.plus
Message:

dev update

Location:
advanced-dynamic-pricing-for-woocommerce/trunk
Files:
1 added
46 edited

Legend:

Unmodified
Added
Removed
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php

    r3384329 r3412190  
    9090            $settings->save();
    9191            //phpcs:ignore WordPress.Security.ValidatedSanitizedInput
    92             wp_redirect($_SERVER['HTTP_REFERER']);
     92            wp_safe_redirect($_SERVER['HTTP_REFERER']);
     93            exit();
    9394        } else if(isset($_POST['reset-options'])) {
    9495            //phpcs:ignore WordPress.Security.ValidatedSanitizedInput
     
    105106            $settings->drop();
    106107            //phpcs:ignore WordPress.Security.ValidatedSanitizedInput
    107             wp_redirect($_SERVER['HTTP_REFERER']);
     108            wp_safe_redirect($_SERVER['HTTP_REFERER']);
     109            exit();
    108110        }
    109111    }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Rules.php

    r3386161 r3412190  
    7373            }
    7474            //phpcs:ignore WordPress.Security.ValidatedSanitizedInput
    75             wp_redirect($_SERVER['HTTP_REFERER']);
     75            wp_safe_redirect($_SERVER['HTTP_REFERER']);
    7676            exit();
    7777        }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php

    r3384329 r3412190  
    55use ADP\BaseVersion\Includes\Core\Rule\Rule;
    66use ADP\BaseVersion\Includes\Helpers\Helpers;
     7use ADP\BaseVersion\Includes\Cache\CacheHelper;
    78use ADP\BaseVersion\Includes\ImportExport\Importer;
    89use ADP\BaseVersion\Includes\ImportExport\Exporter;
     
    1112use ADP\BaseVersion\Includes\AdminExtensions\AdminPage\AdminTabInterface;
    1213use ADP\BaseVersion\Includes\Database\Repository\RuleRepository;
     14use ADP\BaseVersion\Includes\Database\Repository\PersistentRuleRepository;
    1315use ADP\Factory;
    1416use Exception;
     
    127129
    128130        $repo = new RuleRepository();
    129         wp_send_json_success("Done: " . $repo->migrateSuitableCommonRulesToPersistence() . " affected");
     131        $count = $repo->migrateSuitableCommonRulesToPersistence();
     132
     133        CacheHelper::flush();
     134        wp_send_json_success("Done: $count affected");
    130135    }
    131136
     
    134139
    135140        $repo = new RuleRepository();
    136         wp_send_json_success("Done: " . $repo->migrateSuitablePersistenceRulesToCommon() . " affected");
     141        $count = $repo->migrateSuitablePersistenceRulesToCommon();
     142
     143        // should clear database cache too
     144        $persistentRuleRepository = new PersistentRuleRepository();
     145        $persistentRuleRepository->truncate();
     146        $persistentRuleRepository->clearCacheInProductMetaData();
     147
     148        CacheHelper::flush();
     149
     150        wp_send_json_success("Done: $count affected");
    137151    }
    138152
     
    182196            );
    183197            //phpcs:ignore WordPress.Security.ValidatedSanitizedInput
    184             wp_redirect($_SERVER['HTTP_REFERER']);
     198            wp_safe_redirect($_SERVER['HTTP_REFERER']);
    185199            exit();
    186200            //phpcs:ignore WordPress.Security.NonceVerification.Missing
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/AdminExtensions/Ajax.php

    r3349520 r3412190  
    613613        // phpcs:ignore WordPress.DB
    614614        $totalCount = (int)($wpdb->get_var($sql));
    615         CacheHelper::flushRulesCache();
     615        CacheHelper::flush();
    616616        wp_send_json_success(
    617617            [
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Cache/CacheHelper.php

    r3349520 r3412190  
    7575    }
    7676
     77    public static function flushRulesCache()
     78    {
     79        self::cacheDelete(self::KEY_ACTIVE_RULES_COLLECTION);
     80        self::cacheFlushGroup(self::GROUP_RULES_CACHE);
     81        self::cacheFlushGroup(self::GROUP_PROCESSED_PRODUCTS_TO_DISPLAY);
     82    }
     83
     84    public static function flushCollectionsCache()
     85    {
     86        self::cacheFlushGroup(self::GROUP_COLLECTIONS);
     87        self::flushRulesCache();
     88    }
     89
     90
    7791    /**
    7892     * @param null $deprecated
     
    261275
    262276        return $product_data ? $product_data->meta : array();
    263     }
    264 
    265     public static function flushRulesCache()
    266     {
    267         self::cacheDelete(self::KEY_ACTIVE_RULES_COLLECTION);
    268         self::cacheFlushGroup(self::GROUP_RULES_CACHE);
    269277    }
    270278
     
    327335     * @return string
    328336     */
    329     protected static function calcHashProcessedProduct(
    330         $productId,
     337    public static function calcHashProcessedProduct(
     338        $theProduct,
    331339        $variationAttributes,
    332340        $qty,
     
    335343        $calc
    336344    ) {
     345        if ($theProduct instanceof WC_Product) {
     346            $productId = $theProduct->get_id();
     347        } elseif (is_numeric($theProduct)) {
     348            $productId = $theProduct;
     349        } elseif ($theProduct instanceof \WP_Post) {
     350            $productId = $theProduct->ID;
     351        } else {
     352            $productId = $theProduct;
     353        }
     354
    337355        $parts = array($productId, $qty);
    338356
     
    361379        }
    362380
    363         $parts = apply_filters("adp_calculate_processed_product_hash", $parts);
    364 
    365         return md5(implode('_', $parts));
     381        $parts[]= md5( json_encode( $cart->getContext()->getCustomer() ) );
     382
     383        $parts = apply_filters("adp_calculate_processed_product_hash", $parts,
     384                    $theProduct, $variationAttributes, $qty, $cartItemData, $cart, $calc);
     385
     386        $hash = md5(implode('_', $parts));
     387       
     388        return $hash;
    366389    }
    367390
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/CartProcessor/CartProcessor.php

    r3384329 r3412190  
    263263        }
    264264
     265        $this->context->getContainerCompatibilityManager()->addFilters();
     266
    265267        $this->cartItemConverter = new CartItemConverter();
    266268    }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/CartProcessor/FreeAutoAddItemsController.php

    r3297317 r3412190  
    9191        }
    9292
    93         wp_redirect(wc_get_page_permalink('cart'));
    94         die();
     93        wp_safe_redirect(wc_get_page_permalink('cart'));
     94        exit();
    9595    }
    9696
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/CartProcessor/OriginalPriceCalculation.php

    r3385738 r3412190  
    9393            $this->trdPartyAdjustmentsAmount = 0.0;
    9494            $this->basePrice                 = $this->priceToAdjust;
    95         } elseif ($wcCartItem->getInitialCustomPrice() &&
    96                 !($priceMod === self::COMPARE_WC_AND_ADP && ($tmCmp = new TmExtraOptionsCmp($context)) && $tmCmp->isActive())) {
     95        } elseif ($wcCartItem->getInitialCustomPrice()) {
    9796            $this->priceToAdjust = $this->getPrice($product, $wcCartItem, $prodPropsWithFilters, true);
    9897
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Compatibility/Container/SomewhereWarmBundlesCmp.php

    r3384329 r3412190  
    6464            }, 10, 6);
    6565        }
     66
     67        add_filter('woocommerce_bundle_price_data', function($data, $product) {
     68            $discountPrice = adp_functions()->getDiscountedProductPrice($product, 1, true);
     69            if (!empty($discountPrice)) {
     70                if( is_array($discountPrice) ) {
     71                    $data['base_price'] = reset($discountPrice); //take min value
     72                } else {
     73                    $data['base_price'] = $discountPrice;
     74                }
     75            }
     76       
     77            return $data;
     78        }, 10, 2);
    6679    }
    6780
     
    110123                    $bundledProduct,
    111124                    (float)$price,
    112                     (float)$bundleItem->get_quantity("default"),
     125                    (float)$bundleItem->get_quantity("min", array( 'check_optional' => true )),
    113126                    $bundleItem->is_priced_individually()
    114127                );
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Compatibility/WcSubscriptionsCmp.php

    r3297317 r3412190  
    5959            add_action( 'woocommerce_subscription_cart_after_grouping', [$this,"setRecurringCalculationType"], 10 );
    6060            add_filter( 'woocommerce_subscriptions_calculated_total', [$this,"setNoneCalculationType"], 10 );
     61
     62            if(defined('BOS_IS_PLUGIN')) {
     63                add_filter('adp_get_wc_sale_price', [$this,"getBOSWCSalePrice"], 10, 4);
     64            }
    6165        }
    6266
     
    162166    }
    163167
     168    public function getBOSWCSalePrice($wcSalePrice, $product, $item, $prodPropsWithFilters) {
     169        if(apply_filters( 'bos_use_regular_price', false )) {
     170            return null;
     171        }
     172        return $wcSalePrice;
     173    }
     174   
    164175}
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Context/Container/ContainerCompatibilityManager.php

    r3040996 r3412190  
    8282        return null;
    8383    }
     84
     85    public function addFilters()
     86    {
     87        foreach ($this->registeredContainerCompatibilities as $compatibility) {
     88            if ($compatibility->isActive())
     89                $compatibility->addFilters();
     90        }
     91    }
    8492}
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Context/CurrencyController.php

    r3297317 r3412190  
    5959    {
    6060        $this->context         = $context;
     61        if(!$defaultCurrency) {
     62            $defaultCurrency = self::getDefaultWoocomerceCurrency();
     63        }
    6164        $this->defaultCurrency = $defaultCurrency;
    6265        $this->currentCurrency = $defaultCurrency;
     
    467470    }
    468471
     472    public static function getDefaultWoocomerceCurrency()
     473    {
     474        $currencyCode = get_option('woocommerce_currency');
     475        $symbols      = self::getDefaultCurrencySymbols();
     476
     477        $symbol       = isset($symbols[$currencyCode]) ? $symbols[$currencyCode] : '';
     478        return new Currency($currencyCode, $symbol, 1);
     479    }
     480
    469481}
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Core/Cart/CartCustomer.php

    r2852738 r3412190  
    1010defined('ABSPATH') or exit;
    1111
    12 class CartCustomer
     12class CartCustomer implements \JsonSerializable
    1313{
    1414    /**
     
    9797    }
    9898
     99    public function jsonSerialize(): mixed  {
     100        return [
     101            'id'                      => $this->getId(),
     102            'billingAddress'          => $this->getBillingAddress(),
     103            'shippingAddress'         => $this->getShippingAddress(),
     104            'selectedPaymentMethod'   => $this->getSelectedPaymentMethod(),
     105            'selectedShippingMethods' => $this->getSelectedShippingMethods(),
     106            'isVatExempt'             => $this->isVatExempt(),
     107            'roles'                   => $this->getRoles(),
     108        ];
     109    }
     110
    99111    /**
    100112     * @param array $metaArray
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Core/Cart/CartItem/CartItemConverter.php

    r3022127 r3412190  
    5757        $facade->setDiscounts($cartItem->getDiscounts());
    5858        $facade->setPriceAdjustments($cartItem->getPriceAdjustments());
    59 
    60         $facade->setOriginalPriceWithoutTax($facade->getSubtotal() / $facade->getQty());
    61         $facade->setOriginalPriceTax($facade->getExactSubtotalTax() / $facade->getQty());
     59        $qty = $facade->getQty();
     60        $facade->setOriginalPriceWithoutTax( $qty > 0 ? $facade->getSubtotal() / $facade->getQty() : 0 );
     61        $facade->setOriginalPriceTax($qty > 0 ? $facade->getExactSubtotalTax() / $facade->getQty() : 0 );
    6262        $facade->setQty($cartItem->getQty());
    6363
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Core/Cart/CartItem/Type/Base/CartItemPrices.php

    r3022127 r3412190  
    88    protected $originalPriceToDisplay;
    99    protected $minDiscountRangePrice;
     10    protected $maxDiscountRangePrice;
    1011    protected $trdPartyAdjustmentsTotal;
    1112
     
    1920        $obj->originalPriceToDisplay = $originalPriceToDisplay;
    2021        $obj->minDiscountRangePrice = null;
     22        $obj->maxDiscountRangePrice = null;
    2123        $obj->trdPartyAdjustmentsTotal = 0.0;
    2224
     
    7779
    7880    /**
     81     * @return mixed
     82     */
     83    public function getMaxDiscountRangePrice()
     84    {
     85        return $this->maxDiscountRangePrice;
     86    }
     87
     88    /**
    7989     * @param mixed $minDiscountRangePrice
    8090     * @return CartItemPrices
     
    8393    {
    8494        $this->minDiscountRangePrice = $minDiscountRangePrice;
     95        return $this;
     96    }
     97
     98    /**
     99     * @param mixed $minDiscountRangePrice
     100     * @return CartItemPrices
     101     */
     102    public function setDiscountRangePrice($discountRangePrice)
     103    {
     104        if($this->minDiscountRangePrice == null || $discountRangePrice < $this->minDiscountRangePrice) {
     105            $this->minDiscountRangePrice = $discountRangePrice;
     106        }
     107
     108        if($this->maxDiscountRangePrice == null || $discountRangePrice > $this->maxDiscountRangePrice) {
     109            $this->maxDiscountRangePrice = $discountRangePrice;
     110        }
     111
    85112        return $this;
    86113    }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Core/CartCalculator.php

    r3349520 r3412190  
    1111use ADP\BaseVersion\Includes\Core\RuleProcessor\Listener;
    1212use ADP\BaseVersion\Includes\Core\RuleProcessor\RuleProcessor;
     13use ADP\BaseVersion\Includes\Core\RuleProcessor\PersistentRuleProcessor;
    1314use ADP\BaseVersion\Includes\Database\Repository\PersistentRuleRepository;
    1415use ADP\BaseVersion\Includes\Database\Repository\PersistentRuleRepositoryInterface;
     
    280281            }
    281282        }
    282         return $wcSalePrice;
     283        return apply_filters('adp_get_wc_sale_price', $wcSalePrice, $product, $item, $prodPropsWithFilters);
    283284
    284285        //Failed code, caused infinite loop  -  some plugins add hook for 'view' context
     
    422423            }
    423424
    424             if ( ! $object || ! $object->rule || ! $object->price) {
     425            if ( ! $object || ! $object->rule || ! $object->price || !($processor instanceof PersistentRuleProcessor) ) {
    425426                continue;
    426427            }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Core/RuleProcessor/BulkDiscount/SingleItemRuleBulkDiscountProcessor.php

    r3384329 r3412190  
    295295                }
    296296
    297                 $minPrice = $item->prices()->getMinDiscountRangePrice();
    298 
    299                 if ($minPrice !== null) {
    300                     if ($price < $minPrice) {
    301                         $item->prices()->setMinDiscountRangePrice($price);
    302                     }
    303                 } else {
    304                     $item->prices()->setMinDiscountRangePrice($price);
    305                 }
     297                $item->prices()->setDiscountRangePrice($price);
    306298            }
    307299        }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php

    r3297317 r3412190  
    630630                        'choices'      => apply_filters("wdp_" . "cart" . "_discount_message_places",
    631631                            array(
    632                                 'woocommerce_cart_totals_before_shipping' => __('After subtotal',
     632                                'woocommerce_cart_totals_before_shipping' => __('Before shipping',
    633633                                    'advanced-dynamic-pricing-for-woocommerce'),
    634634                                'woocommerce_cart_totals_before_order_total' => __('Before order total',
     
    678678                                'woocommerce_review_order_after_cart_contents'  => __('After cart contents',
    679679                                    'advanced-dynamic-pricing-for-woocommerce'),
    680                                 'woocommerce_review_order_before_shipping'    => __('After subtotal',
     680                                'woocommerce_review_order_before_shipping'    => __('Before shipping',
    681681                                    'advanced-dynamic-pricing-for-woocommerce'),
    682682                                'woocommerce_review_order_before_order_total'    => __('Before order total',
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Database/Repository/PersistentRuleRepository.php

    r3384329 r3412190  
    279279                        return $key;
    280280                    };
    281                     add_filter("adp_calculate_product_hash", $customHash);
    282                     add_filter("adp_calculate_processed_product_hash", $customHash);
    283281                    add_filter("adp_calculate_persistent_rule_product_hash", $customHash);
    284282
     
    287285                    $processedProduct = $productProcessor->calculateProduct($product, 1.0, $cartItemData);
    288286                   
    289                     remove_filter("adp_calculate_product_hash", $customHash);
    290                     remove_filter("adp_calculate_processed_product_hash", $customHash);
    291287                    remove_filter("adp_calculate_persistent_rule_product_hash", $customHash);
    292288                    $customer->setRoles($initialRoles);
     
    375371                return $key;
    376372            };
    377             add_filter("adp_calculate_product_hash", $customHash);
    378             add_filter("adp_calculate_processed_product_hash", $customHash);
    379373            add_filter("adp_calculate_persistent_rule_product_hash", $customHash);
    380374
    381375            $hash[] = $this->calculateDbHash($item);
    382376
    383             remove_filter("adp_calculate_product_hash", $customHash);
    384             remove_filter("adp_calculate_processed_product_hash", $customHash);
    385377            remove_filter("adp_calculate_persistent_rule_product_hash", $customHash);
    386378        }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Helpers/Helpers.php

    r3319660 r3412190  
    366366        /** @var WC_Product[] $posts */
    367367        $posts = wc_get_products(array(
    368             'title' => $name,
     368            'name' => $name,
    369369            'type' => array_merge(array_keys(wc_get_product_types()), array('variation'))
    370370        ));
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/ImportExport/ImporterCSV.php

    r3297317 r3412190  
    124124
    125125    public static function prepareCSV($file){
    126         $separator = apply_filters("adp_import_rules_separator",",");
     126        $separator = apply_filters("adp_import_rules_separator", ",");
     127        $enclosure = apply_filters("adp_import_rules_enclosure", "\"");
     128        $escape = apply_filters("adp_import_rules_escape", "\\");
     129
    127130        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
    128131        if (($handle = fopen($file, "r")) !== false) {
    129132            $filterType = '';
    130             if (($data = fgetcsv($handle, null, $separator)) !== false && is_array($data)) {
     133            if (($data = fgetcsv($handle, null, $separator, $enclosure, $escape)) !== false && is_array($data)) {
    131134                if (str_contains(strtolower($data[0]), 'type')) {
    132135                    $filterType = self::convertSupportedValueToType($data[1]);
     
    134137            }
    135138            $discountType = '';
    136             if (($data = fgetcsv($handle, null, $separator)) !== false && is_array($data)) {
     139            if (($data = fgetcsv($handle, null, $separator, $enclosure, $escape)) !== false && is_array($data)) {
    137140                if (str_contains(strtolower($data[0]), 'type')) {
    138141                    $discountType = self::convertSupportedValueToType($data[1]);
     
    140143            }
    141144            $ruleBlocksSet = array();
    142             while (($data = fgetcsv($handle, null, $separator)) !== false) {
     145            while (($data = fgetcsv($handle, null, $separator, $enclosure, $escape)) !== false) {
    143146                $data = array_map('strtolower', $data);
    144147                if (in_array($data[0], array('filter', 'discountedprice', 'fromqty', 'toqty', 'role'))) {
     
    164167            $rules               = array();
    165168            $newRule             = array();
    166             while (($data = fgetcsv($handle, null, $separator)) !== false) {
     169            while (($data = fgetcsv($handle, null, $separator, $enclosure, $escape)) !== false) {
    167170                if (empty($data[0])) {
    168171                    for ($setIter = 1; $setIter < $ruleBlocksSetLength; $setIter++) {
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/LoadStrategies/AdminCommon.php

    r3351718 r3412190  
    6161        $orderPreview->register();
    6262
    63         $engine = Factory::get("Engine", WC()->cart);
    64         $engine->getCartProcessor()->installActionFirstProcess_Blocks(); //installs only cartCouponsProcessor
    65 
    6663        /** @see Functions::install() */
    6764        Factory::callStaticMethod("Functions", 'install');
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Loader.php

    r3384329 r3412190  
    4545    public function initPlugin()
    4646    {
     47        // must load langs ASAP
     48        // as Wordpress loads default transations on first usage __()
     49        $this->loadTextDomain();
     50
    4751        if ( ! $this->checkRequirements()) {
    4852            return;
     
    5155        $context = adp_context(); // do not remove! Required for correct initialization
    5256        $this->load($context);
     57    }
     58
     59    protected function loadTextDomain()
     60    {
     61        // do nothing if loaded free version , it reads translations from Wordpress.org
     62        if(!defined('WC_ADP_PRO_VERSION_PATH'))
     63            return;
     64
     65        $pricingDomain = 'advanced-dynamic-pricing-for-woocommerce';
     66        add_filter('load_textdomain_mofile', function ($moFile, $domain) use ($pricingDomain) {
     67            if ($domain !== $pricingDomain) {
     68                return $moFile;
     69            }
     70            $path = WP_PLUGIN_DIR . '/' . trim(basename(dirname(dirname(dirname(__FILE__)))) . '/languages/', '/');
     71            $plugin_file = $path . '/' . substr($moFile, strrpos($moFile, '/') + 1);
     72            return file_exists($plugin_file) ? $plugin_file : $moFile;
     73        }, 10, 2);
     74        load_plugin_textdomain($pricingDomain, false, basename(dirname(dirname(dirname(__FILE__)))) . '/languages/');
    5375    }
    5476
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/PriceDisplay/ConcreteProductPriceHtml/VariationProductPriceHtml.php

    r2661390 r3412190  
    44
    55use ADP\BaseVersion\Includes\PriceDisplay\ConcreteProductPriceHtml;
     6use ADP\BaseVersion\Includes\PriceDisplay\PriceFormatters\DefaultFormatter;
    67
    78defined('ABSPATH') or exit;
     
    910class VariationProductPriceHtml extends SimpleProductPriceHtml implements ConcreteProductPriceHtml
    1011{
     12    /**
     13     * @param string $priceHtml
     14     *
     15     * @return string
     16     */
     17    public function getFormattedPriceHtml($priceHtml)
     18    {
     19        $processedProduct       = $this->processedProduct;
     20        $defaultFormatter = new DefaultFormatter($this->context);
    1121
     22        if ($processedProduct->areRulesApplied()) {
     23            $priceHtml = $this->getHtml(1.0);
     24        }
     25
     26        return $defaultFormatter->isNeeded($processedProduct)
     27            ? $defaultFormatter->getHtml($priceHtml, $processedProduct)
     28            : $priceHtml;
     29    }
    1230}
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/PriceDisplay/PriceAjax.php

    r3319660 r3412190  
    259259            } elseif (!$processedProduct->areRulesAppliedAtAll()) {
    260260                return array(
    261                     'price_html' => $prodPriceDisplay->getFormattedPriceHtml($prodPriceDisplay->getPriceHtml()),
     261                    'price_html' => $prodPriceDisplay->getPriceHtmlWithoutFormatting($prodPriceDisplay->getPriceHtml()),
    262262                    'subtotal_html' => $prodPriceDisplay->getFormattedSubtotalHtml($qty),
    263263                    'total_price_html' => $totalProductPriceFormatter->getHtmlAreRulesNotApplied($product, $qty),
     
    269269            } else {
    270270                return array(
    271                     'price_html' => $prodPriceDisplay->getFormattedPriceHtml($prodPriceDisplay->getPriceHtml()),
     271                    'price_html' => $prodPriceDisplay->getPriceHtmlWithoutFormatting($prodPriceDisplay->getPriceHtml()),
    272272                    'subtotal_html' => $prodPriceDisplay->getFormattedSubtotalHtml($qty),
    273273                    'total_price_html' => $totalProductPriceFormatter->getHtmlProcessedProductSimple($processedProduct),
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/PriceDisplay/PriceFormatters/DiscountRangeFormatter.php

    r3297317 r3412190  
    100100
    101101        $minDiscountRangePriceForDisplay = null;
     102        $maxDiscountRangePriceForDisplay = null;
    102103        $initialPriceForDisplay          = null;
    103104        $minDiscountRangePrice          = null;
     105        $maxDiscountRangePrice          = null;
    104106        $regularPrice          = null;
    105107        $pos                   = null;
     108        $minMaxRange = '';
     109
    106110        if ($processedProduct instanceof ProcessedVariableProduct) {
    107111            if ($discountRangeProcessed = $processedProduct->getLowestRangeDiscountPriceProduct()) {
     
    120124                );
    121125                $pos                   = $discountRangeProcessed->getPos();
    122                 $regularPrice          = $processedProduct->getLowestRangeDiscountPriceProduct()->getProduct()->get_regular_price();
     126                $regularPrice          = $discountRangeProcessed->getProduct()->get_regular_price();
     127            }
     128
     129            if ($discountRangeProcessed = $processedProduct->getHighestRangeDiscountPriceProduct()) {
     130                $maxDiscountRangePrice = $discountRangeProcessed->getMaxDiscountRangePrice();
     131                $maxDiscountRangePriceForDisplay = $this->priceFunctions->getPriceToDisplay(
     132                    $product,
     133                    array(
     134                        'price' => $maxDiscountRangePrice,
     135                    )
     136                );
    123137            }
    124138        } else {
     
    138152            $pos                   = $processedProduct->getPos();
    139153            $regularPrice          = $product->get_regular_price();
    140         }
     154
     155            $maxDiscountRangePrice = $processedProduct->getMaxDiscountRangePrice();
     156            $maxDiscountRangePriceForDisplay = $this->priceFunctions->getPriceToDisplay(
     157                $product,
     158                array(
     159                    'price' => $maxDiscountRangePrice,
     160                )
     161            );
     162        }
     163
     164        if (!is_null($minDiscountRangePriceForDisplay)) {
     165            $minMaxRange = $this->priceFunctions->format($minDiscountRangePriceForDisplay);
     166
     167            if(!is_null($maxDiscountRangePriceForDisplay) && $minDiscountRangePriceForDisplay != $maxDiscountRangePriceForDisplay) {
     168                $minMaxRange .= " - " . $this->priceFunctions->format($maxDiscountRangePriceForDisplay);
     169            }
     170        }
     171       
    141172
    142173        $replacements = array(
     
    153184                ? '<del>' . $this->priceFunctions->format($minDiscountRangePriceForDisplay) . '</del>'
    154185                : "",
     186            'min_max_range' => $minMaxRange,
    155187        );
    156188
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/PriceDisplay/ProcessedProductSimple.php

    r3319660 r3412190  
    381381     * @param int|null $pos
    382382     *
     383     * @return float|null
     384     */
     385    public function getMaxDiscountRangePrice($pos = null)
     386    {
     387        $item = $this->getItemByPos($pos);
     388
     389        if ( ! isset($item)) {
     390            return null;
     391        }
     392
     393        return $item->prices()->getMaxDiscountRangePrice();
     394    }
     395
     396    /**
     397     * @param int|null $pos
     398     *
    383399     * @return BasicCartItem|null
    384400     */
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/PriceDisplay/ProcessedVariableProduct.php

    r3057229 r3412190  
    1515    const KEY_LOWEST_PRICE_PROD = 'min_calculated_price';
    1616    const KEY_LOWEST_RANGE_PRICE_PROD = 'min_range_calculated_price';
     17    const KEY_HIGHEST_RANGE_PRICE_PROD = 'max_range_calculated_price';
    1718    const KEY_HIGHEST_PRICE_PROD = 'max_calculated_price';
    1819    const KEY_LOWEST_INITIAL_PRICE_PROD = 'min_initial_price';
     
    186187     * @return ProcessedProductSimple|null
    187188     */
     189    public function getHighestRangeDiscountPriceProduct()
     190    {
     191        if (isset($this->childSummary[self::KEY_HIGHEST_RANGE_PRICE_PROD])) {
     192            /** @var ProcessedProductSimple $prod */
     193            $prod = $this->childSummary[self::KEY_HIGHEST_RANGE_PRICE_PROD];
     194        } else {
     195            $prod = null;
     196        }
     197
     198        return $prod;
     199    }
     200
     201    /**
     202     * @return ProcessedProductSimple|null
     203     */
    188204    public function getLowestInitialPriceProduct()
    189205    {
     
    238254
    239255        if ($product->isAffectedByRangeDiscount()) {
     256            $childLowestRangePrice = $product->getMinDiscountRangePrice();
     257            $lowestRangePrice = null;
    240258            if (isset($this->childSummary[self::KEY_LOWEST_RANGE_PRICE_PROD])) {
    241259                /** @var ProcessedProductSimple $prod */
    242260                $prod             = $this->childSummary[self::KEY_LOWEST_RANGE_PRICE_PROD];
    243261                $lowestRangePrice = $prod->getMinDiscountRangePrice();
    244             } else {
    245                 $lowestRangePrice = null;
    246             }
    247 
    248             if (is_null($lowestRangePrice) || ($childPrice < $lowestRangePrice)) {
     262            }
     263
     264            if (is_null($lowestRangePrice) || $childLowestRangePrice < $lowestRangePrice) {
    249265                $this->childSummary[self::KEY_LOWEST_RANGE_PRICE_PROD] = $product;
     266            }
     267
     268            $childHighestRangePrice = $product->getMaxDiscountRangePrice();
     269            $highestRangePrice = null;
     270            if (isset($this->childSummary[self::KEY_HIGHEST_RANGE_PRICE_PROD])) {
     271                /** @var ProcessedProductSimple $prod */
     272                $prod             = $this->childSummary[self::KEY_HIGHEST_RANGE_PRICE_PROD];
     273                $highestRangePrice = $prod->getMaxDiscountRangePrice();
     274            }
     275
     276            if (is_null($highestRangePrice) || $childHighestRangePrice > $highestRangePrice) {
     277                $this->childSummary[self::KEY_HIGHEST_RANGE_PRICE_PROD] = $product;
    250278            }
    251279
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/PriceDisplay/Processor.php

    r3349520 r3412190  
    122122
    123123        //try cache
    124         $key = array( $product->get_id() , $qty, json_encode($cartItemData) );
    125         foreach ($this->cart->getItems() as $item) {
    126             $key[] = "T".$item->getHash();
    127         }
    128         foreach ($this->calc->getRulesCollection()->getRules() as $rule) {
    129             $key[] = "R".$rule->getId();
    130         }
    131 
    132         $key = apply_filters("adp_calculate_product_hash", $key);
    133 
    134         $key = md5(implode('_', $key));
     124        // $key = array( $product->get_id() , $qty, json_encode($cartItemData) );
     125        // foreach ($this->cart->getItems() as $item) {
     126        //     $key[] = "T".$item->getHash();
     127        // }
     128        // foreach ($this->calc->getRulesCollection()->getRules() as $rule) {
     129        //     $key[] = "R".$rule->getId();
     130        // }
     131        // foreach ($this->cart->getContext()->getCustomer()->getRoles() as $role) {
     132        //     $key[] = "U".$role;
     133        // }
     134        // $key = apply_filters("adp_calculate_product_hash", $key);
     135
     136        // $key = md5(implode('_', $key));
     137
     138        $key = CacheHelper::calcHashProcessedProduct($product, [], $qty, $cartItemData, $this->cart, $this->calc);
     139
    135140        if( $key == $last_product_key )
    136141            return $last_product_result;
     
    163168            );
    164169            foreach ($children as $childId) {
    165                 $processedChild = $this->checkCacheFroProcessedProduct($childId, $qty, [], $cartItemData);
     170                $processedChild = $this->checkCacheForProcessedProduct($childId, $qty, [], $cartItemData);
    166171
    167172                if (is_null($processedChild)) {
     
    196201
    197202            foreach ($children as $childId) {
    198                 $processedChild = $this->checkCacheFroProcessedProduct($childId, $qty, [], $cartItemData);
     203                $processedChild = $this->checkCacheForProcessedProduct($childId, $qty, [], $cartItemData);
    199204
    200205                if (is_null($processedChild)) {
     
    217222        } elseif ( WcProductProcessorHelper::isCalculatingPartOfContainerProduct($product) ) {
    218223            $containerProduct = WcProductProcessorHelper::getBundleProductFromBundled($product);
    219             $processedParent = $this->checkCacheFroProcessedProduct($containerProduct, $qty, [], $cartItemData);
     224            $processedParent = $this->checkCacheForProcessedProduct($containerProduct, $qty, [], $cartItemData);
    220225
    221226            if ( is_null($processedParent) ) {
     
    235240            }
    236241        } else {
    237             $processed = $this->checkCacheFroProcessedProduct($product, $qty, [], $cartItemData);
     242            $processed = $this->checkCacheForProcessedProduct($product, $qty, [], $cartItemData);
    238243
    239244            if ( is_null($processed) ) {
     
    292297
    293298
    294     protected function checkCacheFroProcessedProduct($prodID, $qty, $variationAttributes, $cartItemData)
     299    protected function checkCacheForProcessedProduct($prodID, $qty, $variationAttributes, $cartItemData)
    295300    {
    296301        if ($prodID && $processedProduct = CacheHelper::maybeGetProcessedProductToDisplay(
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Settings/CompatibilityStoreStrategy.php

    r2804362 r3412190  
    66use ADP\Settings\Interfaces\StoreStrategyInterface;
    77use ADP\Settings\OptionsList;
     8use ADP\BaseVersion\Includes\Cache\CacheHelper;
    89
    910defined('ABSPATH') or exit;
     
    2021        if ($optionsList->getOptionsArray()) {
    2122            update_option(self::OPTION_KEY, $optionsList->getOptionsArray());
    22             wp_cache_flush();
     23            CacheHelper::flush();
    2324        }
    2425    }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Settings/OptionsInstaller.php

    r3384329 r3412190  
    480480                'clickable_bulk_rows',
    481481                false,
    482                 __('Clickable bulk rows', 'advanced-dynamic-pricing-for-woocommerce')
     482                __('Highlight rows (on hover) and set qty on click', 'advanced-dynamic-pricing-for-woocommerce')
    483483            )
    484484        );
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Settings/StoreStrategy.php

    r2661390 r3412190  
    66use ADP\Settings\Interfaces\StoreStrategyInterface;
    77use ADP\Settings\OptionsList;
     8use ADP\BaseVersion\Includes\Cache\CacheHelper;
    89
    910defined('ABSPATH') or exit;
     
    2021        if ($optionsList->getOptionsArray()) {
    2122            update_option(self::OPTION_KEY, $optionsList->getOptionsArray());
    22             wp_cache_flush();
     23            CacheHelper::flush();
    2324        }
    2425    }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Shortcodes/Products.php

    r3106428 r3412190  
    7171     */
    7272    protected function parse_attributes( $attributes ) {
     73        $this->type = 'products'; //required
    7374        $parsed_attributes = parent::parse_attributes( $attributes );
     75        $this->type = static::NAME;
     76
    7477        //parse own attrubutes
    7578        $parsed_attributes['show_wc_onsale_products'] = false;
     
    9194     */
    9295    protected function parse_query_args() {
    93         $queryArgs = parent::parse_query_args();
    94 
    9596        if ($this->attributes['rule_id'] !== false) {
    9697            $productIds = static::getCachedProductsIdsByRule($this->attributes['rule_id']);
     
    9899            $productIds = static::getCachedProductsIdsByRule(); //get all
    99100        }
    100 
    101101        if ($this->attributes["show_wc_onsale_products"]) {
    102             $queryArgs['post__in'] = array_unique(array_merge(array(0), $productIds, wc_get_product_ids_on_sale()));
     102            $post__in = array_unique(array_merge(array(0), $productIds, wc_get_product_ids_on_sale()));
    103103        } else {
    104             $queryArgs['post__in'] = array_merge(array(0), $productIds);
    105         }
     104            $post__in = array_merge(array(0), $productIds);
     105        }
     106
     107        add_filter( 'woocommerce_shortcode_products_query', function($query_args, $attributes, $type ) use ($post__in){
     108                $query_args['post__in'] = $post__in;
     109                return $query_args;
     110        },0,4);
     111        $queryArgs = parent::parse_query_args();
     112        $queryArgs['post__in'] = $post__in;//force own ids only
    106113
    107114        return $queryArgs;
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Shortcodes/SqlGeneratorPersistent.php

    r3384329 r3412190  
    309309    protected function genSqlProduct_attributes($termIds, $comparisonMethod = ComparisonMethods::IN_LIST)
    310310    {
    311         static $i =0;
    312         global $wpdb;
    313 
    314         //TODO check_lookup_table_exists
    315         $data_store      = wc_get_container()->get( LookupDataStore::class );
    316         $lookupTable     = $data_store->get_lookup_table_name();
    317 
    318         $where = $this->compareToSql("term_id", ComparisonMethods::IN_LIST, $termIds);
     311        global $wpdb;
     312
     313        $ids = implode(', ', $termIds);
     314        $where = $this->compareToSql("{$wpdb->terms}.term_id", ComparisonMethods::IN_LIST, $termIds);
    319315        // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    320         $taxanomies = $wpdb->get_results("SELECT DISTINCT term_id, taxonomy FROM {$lookupTable} WHERE {$where} ");
    321 
    322         $termIdsByTaxanomy = [];
    323         foreach ($taxanomies as $obj) {
    324             if(!isset($termIdsByTaxanomy[$obj->taxonomy])) {
    325                 $termIdsByTaxanomy[$obj->taxonomy] = [];
    326             }
    327 
    328             $termIdsByTaxanomy[$obj->taxonomy][] = $obj->term_id;
    329         }
    330 
    331         $productIds = [];
    332         foreach ($termIdsByTaxanomy as $taxanomy => $termIds) {
    333             $where = $this->compareToSql("term_id", ComparisonMethods::IN_LIST, $termIds);
    334             // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    335             $products = $wpdb->get_results("SELECT product_id, product_or_parent_id FROM {$lookupTable} WHERE {$where} ");
    336 
    337             $productIds = array_merge($productIds, wp_list_pluck($products, 'product_id'));
    338         }
    339         $productIds = array_unique($productIds);
     316        $items = $wpdb->get_results("SELECT taxonomy, {$wpdb->terms}.slug, {$wpdb->terms}.term_id
     317                                    FROM {$wpdb->term_taxonomy}
     318                                    INNER JOIN {$wpdb->terms}
     319                                    USING (term_id)
     320                                    WHERE {$where}");
     321
     322        $where = [];
     323        foreach ($items as $item) {
     324            $tmp_where   = [
     325                $this->compareToSql("meta_key", ComparisonMethods::EQ, "attribute_{$item->taxonomy}"),
     326                $this->compareToSql("meta_value", ComparisonMethods::EQ, $item->slug),
     327            ];
     328
     329            $where[] = "(" . implode(" AND ", $tmp_where) . ")";
     330        }
     331        $where = "( " . implode(' OR ', $where) . " )";
     332       
     333        // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     334        $productIds = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE {$where}");
    340335
    341336        return  $this->compareToSql('post_children.ID', $comparisonMethod, $productIds);
     
    449444                    break;
    450445                case ComparisonMethods::LATER:
    451                     $sql_method = ComparisonMethods::LT;
     446                    $sql_method = ComparisonMethods::MT;
    452447                    break;
    453448                case ComparisonMethods::EARLIER:
    454                     $sql_method = ComparisonMethods::MT;
     449                    $sql_method = ComparisonMethods::LT;
    455450                    break;
    456451                case in_array($comparisonMethod, ComparisonMethods::BASIC_METHODS):
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Updater/UpdateFunctions.php

    r3297317 r3412190  
    639639        }
    640640    }
     641
     642    public static function migrateSummaryTo_4_10_4()
     643    {
     644        global $wpdb;
     645
     646        if(!class_exists('\ADP\ProVersion\Includes\Database\Models\Collection')) {
     647            return;
     648        }
     649
     650        $table = $wpdb->prefix . \ADP\ProVersion\Includes\Database\Models\Collection::TABLE_NAME;
     651
     652        //phpcs:disable WordPress.DB.DirectDatabaseQuery
     653        $column_exists = $wpdb->get_results(
     654            //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     655            $wpdb->prepare("SHOW COLUMNS FROM {$table} LIKE %s", 'conditions_relationship')
     656        );
     657
     658        if (empty($column_exists)) {
     659            //phpcs:disable WordPress.DB.PreparedSQL, WordPress.DB.DirectDatabaseQuery
     660            $wpdb->query("ALTER TABLE {$table} ADD COLUMN conditions_relationship BOOLEAN DEFAULT TRUE");
     661            //phpcs:disable WordPress.DB.PreparedSQL, WordPress.DB.DirectDatabaseQuery
     662            $wpdb->query("UPDATE {$table} SET conditions_relationship = TRUE");
     663        }
     664
     665        //phpcs:disable WordPress.DB.DirectDatabaseQuery
     666        $column_exists = $wpdb->get_results(
     667            //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     668            $wpdb->prepare("SHOW COLUMNS FROM {$table} LIKE %s", 'summary')
     669        );
     670
     671        if (empty($column_exists)) {
     672            //phpcs:disable WordPress.DB.PreparedSQL, WordPress.DB.DirectDatabaseQuery
     673            $wpdb->query("ALTER TABLE {$table} ADD COLUMN summary TEXT DEFAULT NULL");
     674        }
     675
     676        if (!class_exists("\ADP\ProVersion\Includes\Database\Repository\CollectionRepository")) {
     677            return;
     678        }
     679
     680        $productCollections = \ADP\ProVersion\Includes\Database\Repository\CollectionRepository::getProductCollections();
     681
     682        foreach ($productCollections as $collection) {
     683            \ADP\ProVersion\Includes\Database\Repository\CollectionRepository::storeProductCollection($collection);
     684        }
     685    }
    641686}
    642687
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/Includes/Updater/Updater.php

    r3004075 r3412190  
    5555            'migratePostalCodesConditionsTo_4_5_3',
    5656            'migrateSpentConditionsTo_4_5_3'
    57         )
     57        ),
     58        '4.10.4' => array(
     59            'migrateSummaryTo_4_10_4',
     60        ),
    5861    );
    5962
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/assets/js/cache-recalculation.js

    r3384329 r3412190  
    5353            }
    5454
    55             const PAGE_SIZE = selector == 'recalculate_persistence_cache' ? 10 : 1;
     55            const PAGE_SIZE = 1;
    5656            let processedCount = 0;
    5757
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/assets/js/frontend.js

    r3384329 r3412190  
    4949                        }
    5050
    51                         jQItem.html(jQuery(response.data).children())
     51                        jQItem.html(jQuery(response.data).children()).trigger('update');
    5252                    });
    5353                    init_custom_event();
     
    5959                }
    6060
    61         DynamicPrice.prototype.clickableBulkRows()
    6261            },
    6362            error: function (response) {
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/assets/js/options.js

    r3384329 r3412190  
    139139    });
    140140
    141     $(document).on('click', function(e) {
     141    $(document).on('mousedown', function(e) {
    142142        if(!$(e.target).closest('.wdp-settings-template-wrap').length) {
    143143            $('.wdp-settings-template-wrap').removeClass('wdp-settings-template-active');
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/assets/js/rules.js

    r3384329 r3412190  
    11681168    }
    11691169
    1170     function show_buy_three_for_x(rule_type_selector, new_rule) {
    1171       let linkOnExample = $(rule_type_selector).parent().find('a');
    1172       linkOnExample.attr('href', 'https://docs.algolplus.com/algol_pricing/cart-discount-help/').show();
    1173 
    1174       var filter_data = {
    1175         qty: 3,
    1176         type: "any",
    1177         limitation: "product"
    1178       };
    1179 
    1180       var adjustment_data = {
    1181         type: 'total',
    1182         total: {
    1183           type: 'price__fixed',
    1184         }
    1185       };
    1186 
    1187       add_product_filter(new_rule.find('.wdp-filter-block'), filter_data);
    1188       add_product_adjustment(new_rule.find('.wdp-product-adjustments'), adjustment_data);
    1189     }
    1190 
    1191     function show_disc_cheapest_fifty_perc(rule_type_selector, new_rule){
     1170    function show_pro_threebytwo_discount_type(rule_type_selector, new_rule){
    11921171      let linkOnExample = $(rule_type_selector).parent().find('a');
    11931172      linkOnExample.attr('href', 'https://docs.algolplus.com/algol_pricing/cart-discount-help/').show();
     
    12061185              console.error(response.data);
    12071186              return;
     1187            } else {
     1188              new_rule.find('.wdp-select-filter-priority-temp')
     1189                .removeClass('wdp-select-filter-priority-temp')
     1190                .addClass('wdp-select-filter-priority')
     1191                .show();
     1192            }
     1193          },
     1194          'json'
     1195        );
     1196      }
     1197
     1198      var filter_data_1 = {
     1199        qty: 2,
     1200        type: "products",
     1201        limitation: "none",
     1202        select_priority: "expensive",
     1203      };
     1204
     1205      var filter_data_2 = {
     1206        qty: 1,
     1207        type: "products",
     1208        limitation: "none",
     1209        select_priority: "cheap",
     1210      };
     1211
     1212      var adjustment_data = {
     1213        type: 'split',
     1214        split: [
     1215          {
     1216            type: 'discount__amount',
     1217          },
     1218          {
     1219            type: 'discount__percentage',
     1220            value: 100
     1221          }
     1222        ]
     1223      };
     1224
     1225      add_product_filter(new_rule.find('.wdp-filter-block'), filter_data_1);
     1226      add_product_filter(new_rule.find('.wdp-filter-block'), filter_data_2);
     1227
     1228      add_product_adjustment(new_rule.find('.wdp-product-adjustments'), adjustment_data);
     1229    }
     1230
     1231    function show_buy_three_for_x(rule_type_selector, new_rule) {
     1232      let linkOnExample = $(rule_type_selector).parent().find('a');
     1233      linkOnExample.attr('href', 'https://docs.algolplus.com/algol_pricing/cart-discount-help/').show();
     1234
     1235      var filter_data = {
     1236        qty: 3,
     1237        type: "any",
     1238        limitation: "product"
     1239      };
     1240
     1241      var adjustment_data = {
     1242        type: 'total',
     1243        total: {
     1244          type: 'price__fixed',
     1245        }
     1246      };
     1247
     1248      add_product_filter(new_rule.find('.wdp-filter-block'), filter_data);
     1249      add_product_adjustment(new_rule.find('.wdp-product-adjustments'), adjustment_data);
     1250    }
     1251
     1252    function show_disc_cheapest_fifty_perc(rule_type_selector, new_rule){
     1253      let linkOnExample = $(rule_type_selector).parent().find('a');
     1254      linkOnExample.attr('href', 'https://docs.algolplus.com/algol_pricing/cart-discount-help/').show();
     1255
     1256      if(!wdp_data.options.filter_priority) {
     1257        let params = {
     1258          action: 'wdp_ajax',
     1259          method: 'check_filter_priority',
     1260        };
     1261        params[wdp_data.security_query_arg] = wdp_data.security;
     1262        $.post(
     1263          ajaxurl,
     1264          params,
     1265          function (response) {
     1266            if (!response.success) {
     1267              console.error(response.data);
     1268              return;
     1269            } else {
     1270              new_rule.find('.wdp-select-filter-priority-temp')
     1271                .removeClass('wdp-select-filter-priority-temp')
     1272                .addClass('wdp-select-filter-priority')
     1273                .show();
    12081274            }
    12091275          },
     
    13031369                show_pro_bogo_discount_type(this, new_rule);
    13041370                break;
     1371        case 'pro_threebytwo_discount':
     1372          show_pro_threebytwo_discount_type(this, new_rule);
     1373        break;
    13051374              case 'bogo_discount':
    13061375                show_bogo_discount_type(this, new_rule);
     
    21022171            /** pro version functionality */
    21032172            if ( data.select_priority ) {
     2173                $container.find('.wdp-select-filter-priority-temp select').val(data.select_priority);
    21042174                $container.find('.wdp-select-filter-priority select').val(data.select_priority);
    21052175            }
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/views/admin_page/tabs/options/replace_price_with_min_bulk_price.php

    r3349577 r3412190  
    4343            <div>
    4444                <?php esc_html_e('Available tags', 'advanced-dynamic-pricing-for-woocommerce') ?>
    45                 : <?php esc_html_e('{{price}}, {{price_suffix}}, {{price_striked}}, {{initial_price}}, {{regular_price_striked}}',
     45                : <?php esc_html_e('{{price}}, {{price_suffix}}, {{price_striked}}, {{initial_price}}, {{regular_price_striked}}, {{min_max_range}}',
    4646                    'advanced-dynamic-pricing-for-woocommerce') ?>
    4747            </div>
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/views/admin_page/tabs/rules/persistent_rule.php

    r3385738 r3412190  
    205205                        <div style="display: inline-block;margin: 0 10px 0 0;">
    206206                            <label>
    207                                 <?php esc_html_e('Limit discount to amount:', 'advanced-dynamic-pricing-for-woocommerce') ?>
     207                                <?php esc_html_e('Limit discount to amount', 'advanced-dynamic-pricing-for-woocommerce') ?>
    208208                                <input style="display: inline-block; width: 200px;"
    209209                                       name="rule[product_adjustments][max_discount_sum]" type="number"
    210                                        class="product-adjustments-max-discount" placeholder="0.00" min="0" step="any"/>
     210                                       class="product-adjustments-max-discount" placeholder="<?php esc_html_e('Unlimited', 'advanced-dynamic-pricing-for-woocommerce') ?>" min="0" step="any"/>
    211211                            </label>
    212212                        </div>
  • advanced-dynamic-pricing-for-woocommerce/trunk/BaseVersion/views/admin_page/tabs/rules/rule.php

    r3385738 r3412190  
    322322                        <div style="display: inline-block;margin: 0 10px 0 0;">
    323323                            <label>
    324                                 <?php esc_html_e('Limit discount to amount:', 'advanced-dynamic-pricing-for-woocommerce') ?>
    325                                 <input style="display: inline-block; width: 200px;" name="rule[product_adjustments][max_discount_sum]" type="number" class="product-adjustments-max-discount" placeholder="0.00" min="0" step="any"/>
     324                                <?php esc_html_e('Limit discount to amount', 'advanced-dynamic-pricing-for-woocommerce') ?>
     325                                <input style="display: inline-block; width: 200px;" name="rule[product_adjustments][max_discount_sum]" type="number" class="product-adjustments-max-discount" placeholder="<?php esc_html_e('Unlimited', 'advanced-dynamic-pricing-for-woocommerce') ?>" min="0" step="any"/>
    326326                            </label>
    327327                        </div>
  • advanced-dynamic-pricing-for-woocommerce/trunk/advanced-dynamic-pricing-for-woocommerce.php

    r3386169 r3412190  
    44 * Plugin URI:
    55 * Description: Manage WooCommerce discounts
    6  * Version: 4.10.3
     6 * Version: 4.10.4
    77 * Author: AlgolPlus
    88 * Author URI: https://algolplus.com/
     
    4444define('WC_ADP_MIN_PHP_VERSION', '7.0.0');
    4545define('WC_ADP_MIN_WC_VERSION', '3.6');
    46 define('WC_ADP_VERSION', '4.10.3');
     46define('WC_ADP_VERSION', '4.10.4');
    4747define('WC_ADP_WC_TIPTIP_SINCE_VERSION', '10.3');
    4848
  • advanced-dynamic-pricing-for-woocommerce/trunk/languages/advanced-dynamic-pricing-for-woocommerce.pot

    r3386161 r3412190  
    22msgid ""
    33msgstr ""
    4 "Project-Id-Version: Advanced Dynamic Pricing for WooCommerce (Pro)\n"
     4"Project-Id-Version: Advanced Dynamic Pricing and Discount Rules for "
     5"WooCommerce (Pro)\n"
    56"Report-Msgid-Bugs-To: \n"
    6 "POT-Creation-Date: 2025-10-29 05:01+0000\n"
     7"POT-Creation-Date: 2025-12-05 12:23+0000\n"
    78"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    89"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     
    1415"Content-Transfer-Encoding: 8bit\n"
    1516"X-Generator: Loco https://localise.biz/\n"
    16 "X-Loco-Version: 2.5.5; wp-5.8.3\n"
     17"X-Loco-Version: 2.8.0; wp-6.9-RC3; php-8.4.6\n"
    1718"X-Domain: advanced-dynamic-pricing-for-woocommerce"
    1819
     
    245246
    246247#. Name of the plugin
    247 msgid "Advanced Dynamic Pricing and Discount Rules for WooCommerce"
     248msgid "Advanced Dynamic Pricing and Discount Rules for WooCommerce (Pro)"
    248249msgstr ""
    249250
     
    252253msgstr ""
    253254
    254 #: BaseVersion/Includes/Loader.php:106
     255#: BaseVersion/Includes/Loader.php:128
    255256msgid "Advanced Dynamic Pricing for WooCommerce requires active WooCommerce!"
    256257msgstr ""
    257258
    258259#. A message about the need for a specific php version
    259 #: BaseVersion/Includes/Loader.php:100
     260#: BaseVersion/Includes/Loader.php:122
    260261#, php-format
    261262msgid ""
     
    264265
    265266#. A message about the need for a specific php version
    266 #: BaseVersion/Includes/Loader.php:113
     267#: BaseVersion/Includes/Loader.php:135
    267268#, php-format
    268269msgid ""
     
    271272msgstr ""
    272273
    273 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:261
     274#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:263
    274275msgid "Advertising"
    275276msgstr ""
     
    302303msgstr ""
    303304
    304 #: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:632
    305 #: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:680
    306 msgid "After subtotal"
    307 msgstr ""
    308 
    309305#. Author of the plugin
    310306msgid "AlgolPlus"
     
    313309#: BaseVersion/Includes/Helpers/Helpers.php:200
    314310#: BaseVersion/views/admin_page/tabs/rules.php:31
    315 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:455
     311#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:469
    316312msgid "All"
    317313msgstr ""
     
    433429msgstr ""
    434430
    435 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:135
     431#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:137
    436432msgid "Are you sure to reset all settings to default?"
    437433msgstr ""
     
    483479msgstr ""
    484480
    485 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:532
     481#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:546
    486482msgid "Backup"
    487483msgstr ""
     
    558554msgstr ""
    559555
     556#: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:632
     557#: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:680
     558msgid "Before shipping"
     559msgstr ""
     560
    560561#: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:200
    561562msgid "Below add to cart"
     
    591592msgstr ""
    592593
    593 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:213
     594#: BaseVersion/Includes/ImportExport/ImporterCSV.php:216
    594595#: BaseVersion/views/admin_page/tabs/rules/rule.php:180
    595596#: BaseVersion/views/admin_page/tabs/rules/templates/wdp-sortable-blocks.php:110
     
    659660msgstr ""
    660661
    661 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:235
     662#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:237
    662663msgid "Bulk table"
    663664msgstr ""
     
    690691msgstr ""
    691692
    692 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:181
     693#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:183
    693694msgid "Calculation"
    694695msgstr ""
     
    705706msgstr ""
    706707
    707 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:275
     708#: BaseVersion/Includes/ImportExport/ImporterCSV.php:278
    708709msgid "Can not find "
    709710msgstr ""
    710711
    711 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:201
     712#: BaseVersion/Includes/ImportExport/ImporterCSV.php:204
    712713msgid "Can't open file."
    713714msgstr ""
     
    717718#: BaseVersion/templates/reporter/main.php:20
    718719#: BaseVersion/Includes/Core/Rule/CartCondition/ConditionsLoader.php:75
    719 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:210
     720#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:212
    720721msgid "Cart"
    721722msgstr ""
     
    763764msgstr ""
    764765
    765 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:228
     766#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:230
    766767msgid "Category page"
    767768msgstr ""
     
    812813#: BaseVersion/views/admin_page/tabs/tools/backup.php:81
    813814msgid "Clear all rules before import"
    814 msgstr ""
    815 
    816 #: BaseVersion/Includes/Settings/OptionsInstaller.php:482
    817 msgid "Clickable bulk rows"
    818815msgstr ""
    819816
     
    868865msgstr ""
    869866
    870 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:538
     867#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:552
    871868msgid "Convert rules"
    872869msgstr ""
     
    907904#: BaseVersion/Includes/Debug/DebugBar.php:71
    908905#: BaseVersion/Includes/Core/Rule/CartAdjustment/CartAdjustmentsLoader.php:50
    909 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:252
     906#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:254
    910907msgid "Coupons"
    911908msgstr ""
     
    10061003msgstr ""
    10071004
    1008 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:297
     1005#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:299
    10091006msgid "Debug"
    10101007msgstr ""
     
    12221219#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Rules.php:58
    12231220#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:58
    1224 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:98
    1225 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:77
     1221#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:99
     1222#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:79
    12261223msgid "Error"
    12271224msgstr ""
     
    13071304msgstr ""
    13081305
    1309 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:155
     1306#: BaseVersion/Includes/ImportExport/ImporterCSV.php:158
    13101307msgid "File must contain one rule or more."
    13111308msgstr ""
     
    14031400msgstr ""
    14041401
    1405 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:216
     1402#: BaseVersion/Includes/ImportExport/ImporterCSV.php:219
    14061403msgid "for Roles"
    14071404msgstr ""
     
    14141411
    14151412#: BaseVersion/Includes/Helpers/Helpers.php:573
    1416 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:245
     1413#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:247
    14171414#: BaseVersion/views/admin_page/tabs/rules/persistent_rule.php:571
    14181415#: BaseVersion/views/admin_page/tabs/rules/rule.php:564
     
    15301527#: BaseVersion/views/admin_page/tabs/options/create_blank_rule.php:12
    15311528msgid "Hide rules wizard"
     1529msgstr ""
     1530
     1531#: BaseVersion/Includes/Settings/OptionsInstaller.php:482
     1532msgid "Highlight rows (on hover) and set qty on click"
    15321533msgstr ""
    15331534
     
    16081609msgstr ""
    16091610
    1610 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:519
     1611#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:533
    16111612msgid "Import rules (CSV)"
    16121613msgstr ""
     
    16161617msgstr ""
    16171618
    1618 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:229
     1619#: BaseVersion/Includes/ImportExport/ImporterCSV.php:232
    16191620msgid "Imported Rule"
    16201621msgstr ""
     
    16821683#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Rules.php:57
    16831684#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:57
    1684 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:97
    1685 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:76
     1685#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:98
     1686#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:78
    16861687msgid "Invalid nonce specified"
    16871688msgstr ""
     
    17311732#: BaseVersion/views/admin_page/tabs/rules/persistent_rule.php:207
    17321733#: BaseVersion/views/admin_page/tabs/rules/rule.php:324
    1733 msgid "Limit discount to amount:"
     1734msgid "Limit discount to amount"
    17341735msgstr ""
    17351736
     
    20062007#: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:164
    20072008#: BaseVersion/Includes/CustomizerExtensions/CustomizerExtensions.php:415
    2008 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:60
    2009 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:316
    2010 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:321
     2009#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:62
     2010#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:330
     2011#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:335
    20112012msgid "Options"
    20122013msgstr ""
     
    20162017msgstr ""
    20172018
    2018 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:266
     2019#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:268
    20192020msgid "Order"
    20202021msgstr ""
     
    22242225msgstr ""
    22252226
    2226 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:219
     2227#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:221
    22272228msgid "Product page"
    22282229msgstr ""
    22292230
    2230 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:198
     2231#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:200
    22312232msgid "Product price"
    22322233msgstr ""
     
    24362437msgstr ""
    24372438
    2438 #: BaseVersion/Includes/CartProcessor/CartProcessor.php:1081
     2439#: BaseVersion/Includes/CartProcessor/CartProcessor.php:1083
    24392440msgid "Removed {{qty}} free {{product_name}}"
    24402441msgstr ""
     
    25022503msgstr ""
    25032504
    2504 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:220
    2505 #: BaseVersion/Includes/ImportExport/ImporterCSV.php:222
     2505#: BaseVersion/Includes/ImportExport/ImporterCSV.php:223
     2506#: BaseVersion/Includes/ImportExport/ImporterCSV.php:225
    25062507msgid "Roles"
    25072508msgstr ""
     
    25332534#: BaseVersion/templates/reporter/main.php:24
    25342535#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Rules.php:97
    2535 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:273
    2536 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:61
    2537 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:467
     2536#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:275
     2537#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:63
     2538#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:481
    25382539msgid "Rules"
    25392540msgstr ""
     
    27142715
    27152716#: BaseVersion/Includes/PluginActions.php:58
    2716 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:160
     2717#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:162
    27172718#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Help.php:103
    27182719msgid "Settings"
     
    29822983msgstr ""
    29832984
    2984 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:286
     2985#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Options.php:288
    29852986msgid "System"
    29862987msgstr ""
    29872988
    2988 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:513
     2989#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:527
    29892990msgid "System report"
    29902991msgstr ""
     
    30843085msgstr ""
    30853086
    3086 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:233
     3087#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:247
    30873088#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Help.php:118
    30883089msgid "Tools"
     
    31063107msgstr ""
    31073108
     3109#: BaseVersion/views/admin_page/tabs/rules/persistent_rule.php:210
    31083110#: BaseVersion/views/admin_page/tabs/rules/persistent_rule.php:258
    31093111#: BaseVersion/views/admin_page/tabs/rules/rule.php:112
     3112#: BaseVersion/views/admin_page/tabs/rules/rule.php:325
    31103113#: BaseVersion/views/admin_page/tabs/rules/rule.php:369
    31113114msgid "Unlimited"
     
    31163119msgstr ""
    31173120
    3118 #: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:525
     3121#: BaseVersion/Includes/AdminExtensions/AdminPage/Tabs/Tools.php:539
    31193122msgid "Update bulk ranges (CSV)"
    31203123msgstr ""
     
    33043307msgid ""
    33053308"{{price}}, {{price_suffix}}, {{price_striked}}, {{initial_price}}, "
    3306 "{{regular_price_striked}}"
     3309"{{regular_price_striked}}, {{min_max_range}}"
    33073310msgstr ""
    33083311
  • advanced-dynamic-pricing-for-woocommerce/trunk/readme.txt

    r3405643 r3412190  
    184184== Changelog ==
    185185
     186= 4.10.4 - 2025-12-08 =
     187* Fixed bug - wrong prices for guests when use Redis/Memcached
     188* Fixed bug - "Amount saved" didn't show the cart/checkout for virtual products
     189* Fixed bug - internal "WDP Coupon" was visible at >Marketing>Coupons
     190* Fixed bug - fatal error after conversion "Product Only" back to "Common" rules
     191* Fixed bug - deprecation warnings during >Tools>Import Rules(CSV)
     192* Fixed bug - js error at product page when switch variations
     193* Fixed bug - "division by zero" when "Order Minimum/Maximum Amount Limits for WooCommerce" active
     194* Added tag {min_max_range} to option "Replace price with lowest bulk price"
     195* Added compatibility with "Buy once or subscribe"
     196* Updated compatibility with "WPC Product Bundles for WooCommerce"
     197* Updated compatibility with WOOCS, fatal error if default currency was not set
     198
    186199= 4.10.3 - 2025-10-29 =
    187200* Fixed critical bug - fatal error in PHP 7.4(or less)
Note: See TracChangeset for help on using the changeset viewer.