Repository: saleor/saleor

Issue:
Sometimes the category page is showing the full price of discounted products.
Hello,

Fresh installation from master with demo data.

I have not been able to investigate this properly yet and find a pattern.... so I'll a bit inaccurate.
"Sometime" the category page is showing full prices of discounted products.

are you able to reproduce this issue?

![schermata del 2017-02-07 21-50-10](https://cloud.githubusercontent.com/assets/650691/22711089/2b2e60cc-ed80-11e6-8e3e-c863afa37167.png)

![schermata del 2017-02-07 21-51-37](https://cloud.githubusercontent.com/assets/650691/22711074/1d14960a-ed80-11e6-85d8-50eebbe0814f.png)
![schermata del 2017-02-07 22-06-12](https://cloud.githubusercontent.com/assets/650691/22711534/cf00775c-ed81-11e6-835f-08f02a053712.png)
![schermata del 2017-02-07 22-06-37](https://cloud.githubusercontent.com/assets/650691/22711533/ceff7e7e-ed81-11e6-830c-229d93377e78.png)






Discussion:

Resolving patch:
diff --git a/saleor/discount/models.py b/saleor/discount/models.py
--- a/saleor/discount/models.py
+++ b/saleor/discount/models.py
@@ -285,17 +285,17 @@ def _product_has_category_discount(self, product, discounted_categories):
                     return True
         return False
 
-    def modifier_for_variant(self, variant):
+    def modifier_for_product(self, product):
         discounted_products = [p.pk for p in self.products.all()]
         discounted_categories = list(self.categories.all())
-        if discounted_products and variant.product.pk not in discounted_products:
+        if discounted_products and product.pk not in discounted_products:
             raise NotApplicable(
                 pgettext(
                     'Voucher not applicable',
                     'Discount not applicable for this product'))
         if (discounted_categories and not
-                self._product_has_category_discount(
-                    variant.product, discounted_categories)):
+            self._product_has_category_discount(
+                product, discounted_categories)):
             raise NotApplicable(
                 pgettext(
                     'Voucher not applicable',
@@ -303,9 +303,18 @@ def modifier_for_variant(self, variant):
         return self.get_discount()
 
 
-def get_variant_discounts(variant, discounts, **kwargs):
+def get_product_discounts(product, discounts, **kwargs):
     for discount in discounts:
         try:
-            yield discount.modifier_for_variant(variant, **kwargs)
+            yield discount.modifier_for_product(product, **kwargs)
         except NotApplicable:
             pass
+
+
+def calculate_discounted_price(product, price, discounts, **kwargs):
+    if discounts:
+        discounts = list(
+            get_product_discounts(product, discounts, **kwargs))
+        if discounts:
+            price = min(price | discount for discount in discounts)
+    return price
diff --git a/saleor/product/models/base.py b/saleor/product/models/base.py
--- a/saleor/product/models/base.py
+++ b/saleor/product/models/base.py
@@ -16,10 +16,11 @@
 from django_prices.models import PriceField
 from mptt.managers import TreeManager
 from mptt.models import MPTTModel
+from prices import PriceRange
 from satchless.item import InsufficientStock, Item, ItemRange
 from text_unidecode import unidecode
 
-from ...discount.models import get_variant_discounts
+from ...discount.models import calculate_discounted_price
 from .utils import get_attributes_display_map
 from ...search import index
 
@@ -201,6 +202,15 @@ def get_attribute(self, pk):
     def set_attribute(self, pk, value_pk):
         self.attributes[smart_text(pk)] = smart_text(value_pk)
 
+    def get_price_range(self, discounts=None,  **kwargs):
+        if not self.variants.exists():
+            price = calculate_discounted_price(self, self.price, discounts,
+                                               **kwargs)
+            return PriceRange(price, price)
+        else:
+            return super(Product, self).get_price_range(
+                discounts=discounts, **kwargs)
+
 
 @python_2_unicode_compatible
 class ProductVariant(models.Model, Item):
@@ -240,11 +250,8 @@ def get_stock_quantity(self):
 
     def get_price_per_item(self, discounts=None, **kwargs):
         price = self.price_override or self.product.price
-        if discounts:
-            discounts = list(
-                get_variant_discounts(self, discounts, **kwargs))
-            if discounts:
-                price = min(price | discount for discount in discounts)
+        price = calculate_discounted_price(self.product, price, discounts,
+                                           **kwargs)
         return price
 
     def get_absolute_url(self):
diff --git a/saleor/product/utils.py b/saleor/product/utils.py
--- a/saleor/product/utils.py
+++ b/saleor/product/utils.py
@@ -55,15 +55,6 @@ def products_with_availability(products, discounts, local_currency):
 
 
 def get_availability(product, discounts=None, local_currency=None):
-    if not product.variants.exists():
-        return ProductAvailability(
-            available=False,
-            price_range=None,
-            price_range_undiscounted=None,
-            discount=None,
-            price_range_local_currency=None,
-            discount_local_currency=None)
-
     # In default currency
     price_range = product.get_price_range(discounts=discounts)
     undiscounted = product.get_price_range()


Test patch:
diff --git a/tests/test_discounts.py b/tests/test_discounts.py
--- a/tests/test_discounts.py
+++ b/tests/test_discounts.py
@@ -242,7 +242,7 @@ def test_sale_applies_to_correct_products(product_class):
     sale = Sale.objects.create(name='Test sale', value=5, type=Sale.FIXED)
     sale.products.add(product)
     assert product2 not in sale.products.all()
-    assert sale.modifier_for_variant(variant).amount == Price(net=5,
-                                                              currency='USD')
+    assert sale.modifier_for_product(variant.product).amount == Price(
+        net=5, currency='USD')
     with pytest.raises(NotApplicable):
-        sale.modifier_for_variant(sec_variant)
+        sale.modifier_for_product(sec_variant.product)