Repository: saleor/saleor

Issue:
Attribute filters are not available in subcategories
### What I'm trying to achieve

I'm trying to filter products in subcategories by attributes of this products

### Steps to reproduce the problem
1.  Create category and then create 2 or more subcategory
2.  Add product to last subcategory
3. Select category, than subcategory and try to filter products by attributes

### What I expected to happen

Attribute filters are not available in subcategories! Filters by attributes available only in last subcategory. For example, i have category "Phones" with subcategories by companies names -> phone model. If i select last subcategory "Iphone 8", i got all filters, but if i select subcategory "Apple", i got filter only by price range.

So, how to enable these filters? Can i modify some template or python code by myself, or you guys will do it some later?

### Screenshots
![nofitlers](https://user-images.githubusercontent.com/16798475/44631772-945c1600-a979-11e8-992b-7726533c37b3.png)
![filters](https://user-images.githubusercontent.com/16798475/44632055-33364180-a97d-11e8-97a0-289e9e960a1b.png)




Discussion:
https://github.com/mirumee/saleor/blob/master/saleor/product/filters.py#L91-L100
Those lines should be fixed, as `category` is the parent category that has no products assigned to it  
@pitero92 is the brave one to handle this 
Am I understand correctly?
parent is `self.category = kwargs.pop('category')`
children are `_get_product_attributes_lookup` and` _get_variant_attributes_lookup`?
@pitero92 
Functions `_get_product_attributes_lookup`/ `_get_variant_attributes_lookup` are returning django's Q object https://docs.djangoproject.com/en/2.1/ref/models/querysets/#django.db.models.Q

`self.category` is the parent category. In Saleor categories are built with `django-mptt` package, for creating tree-like structure. 
so `self.category` can have a `child category`(so `self.category` is the parent of the `child category`)
this `child category` can have `grandchild category`
this `grandchild category` ...
etc

Lookup that happens in the above functions is taking product/variant attributes from products/variants assigned to `self.category`

But as self.category can have descendants categories, we should change the lookup, to take product/variant attributes from products/variants assigned to the `self.category` or its descendants. 
Proper solution was already implemented [here](https://github.com/mirumee/saleor/pull/2949/files#diff-1302fef4f54970a2c047714bdc3d5634), but this PR wasn't ready to merge as it contained also unwanted changes.

Resolving patch:
diff --git a/saleor/product/filters.py b/saleor/product/filters.py
--- a/saleor/product/filters.py
+++ b/saleor/product/filters.py
@@ -88,10 +88,12 @@ def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
     def _get_product_attributes_lookup(self):
-        return Q(product_type__products__category=self.category)
+        categories = self.category.get_descendants(include_self=True)
+        return Q(product_type__products__category__in=categories)
 
     def _get_variant_attributes_lookup(self):
-        return Q(product_variant_type__products__category=self.category)
+        categories = self.category.get_descendants(include_self=True)
+        return Q(product_variant_type__products__category__in=categories)
 
 
 class ProductCollectionFilter(ProductFilter):


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -255,11 +255,22 @@ def category_with_image(db, image):  # pylint: disable=W0613
 
 
 @pytest.fixture
-def categories_tree(db):
+def categories_tree(db, product_type):  # pylint: disable=W0613
     parent = Category.objects.create(name='Parent', slug='parent')
     parent.children.create(name='Child', slug='child')
+    child = parent.children.first()
+
+    product_attr = product_type.product_attributes.first()
+    attr_value = product_attr.values.first()
+    attributes = {smart_text(product_attr.pk): smart_text(attr_value.pk)}
+
+    Product.objects.create(
+        name='Test product', price=Money('10.00', 'USD'),
+        product_type=product_type, attributes=attributes, category=child)
+
     return parent
 
+
 @pytest.fixture
 def non_default_category(db):  # pylint: disable=W0613
     return Category.objects.create(name='Not default', slug='not-default')
diff --git a/tests/test_category.py b/tests/test_category.py
new file mode 100644
--- /dev/null
+++ b/tests/test_category.py
@@ -0,0 +1,13 @@
+from saleor.product.filters import ProductCategoryFilter
+
+
+def test_product_category_filter_filters_from_child_category(
+        product_type, categories_tree):
+    product_filter = ProductCategoryFilter(data={}, category=categories_tree)
+    (product_attributes, variant_attributes) = product_filter._get_attributes()
+
+    attribut = product_type.product_attributes.get()
+    variant = product_type.variant_attributes.get()
+
+    assert attribut in product_attributes
+    assert variant in variant_attributes