def test_product_catalog_indexing(rf, admin_user, settings):
    shop = get_default_shop()
    supplier = get_simple_supplier(shop=shop)
    supplier.stock_managed = True
    supplier.save()
    product = create_product("simple-test-product", shop, supplier)

    ProductCatalog.index_product(product)

    # no purchasable products
    catalog = ProductCatalog(
        ProductCatalogContext(shop=shop, purchasable_only=True))
    assert catalog.get_products_queryset().count() == 0

    # add 10 items to the stock
    stock_qty = 10
    request = apply_request_middleware(rf.post("/",
                                               data={
                                                   "purchase_price":
                                                   decimal.Decimal(32.00),
                                                   "delta":
                                                   stock_qty
                                               }),
                                       user=admin_user)
    response = process_stock_adjustment(request, supplier.id, product.id)
    assert response.status_code == 200
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == stock_qty

    # now there are purchasable products
    assert catalog.get_products_queryset().count() == 1
    assert product in catalog.get_products_queryset()

    # create a random order with 10 units of the product
    source = OrderSource(shop)
    source.status = get_initial_order_status()
    source.add_line(
        type=OrderLineType.PRODUCT,
        supplier=supplier,
        product=product,
        base_unit_price=source.create_price(1),
        quantity=10,
    )
    OrderCreator().create_order(source)

    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == 0

    # stocks are gone
    assert catalog.get_products_queryset().count() == 0
Exemple #2
0
def get_priced_children_for_price_range(request, product, quantity, supplier):
    catalog = ProductCatalog(
        ProductCatalogContext(
            shop=request.shop,
            user=getattr(request, "user", None),
            supplier=supplier,
            contact=getattr(request, "customer", None),
            purchasable_only=True,
        ))

    product_queryset = (catalog.get_products_queryset().filter(
        pk__in=product.variation_children.values_list(
            "pk", flat=True)).order_by("catalog_price"))

    low = product_queryset.first()

    if low is None:
        return []

    high = product_queryset.last()

    return [
        (low, _get_item_price_info(request, low, quantity, supplier)),
        (high, _get_item_price_info(request, high, quantity, supplier)),
    ]
Exemple #3
0
def _get_best_selling_products(cutoff_days, n_products, orderable_only, request, supplier=None):
    data = get_best_selling_product_info(
        shop_ids=[request.shop.pk],
        cutoff_days=cutoff_days,
        supplier=supplier,
        orderable_only=orderable_only,
        quantity=n_products,
    )
    sorted_product_ids = sorted(data, key=lambda item: item[1], reverse=True)
    product_ids = [item[0] for item in sorted_product_ids]

    catalog = ProductCatalog(
        ProductCatalogContext(
            shop=request.shop,
            user=getattr(request, "user", None),
            supplier=supplier,
            contact=getattr(request, "customer", None),
            purchasable_only=orderable_only,
            visibility=ShopProductVisibility.LISTED,
        )
    )
    valid_products_qs = (
        catalog.get_products_queryset()
        .filter(id__in=product_ids, mode__in=ProductMode.get_parent_modes())
        .distinct()[:n_products]
    )

    products = cache_product_things(request, valid_products_qs)
    # order products by the best selling order
    products = sorted(products, key=lambda product: product_ids.index(product.pk))
    return products
Exemple #4
0
def test_supplier_changed_reindex_catalog(rf, admin_user):
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier(shop)
    supplier.stock_managed = True
    supplier.save()
    product = factories.create_product("p1",
                                       shop,
                                       supplier,
                                       default_price=Decimal("10"))
    supplier.adjust_stock(product.pk, 40)  # add 40 to the stock
    ProductCatalog.index_product(product)

    catalog = ProductCatalog(ProductCatalogContext(purchasable_only=True))
    assert product in catalog.get_products_queryset()

    # disable the supplier
    edit_view = SupplierEditView.as_view()
    payload = {
        "base-name": supplier.name,
        "base-description__en": "",
        "base-type": SupplierType.INTERNAL.value,
        "base-stock_managed": True,
        "base-supplier_modules": [supplier.supplier_modules.first().pk],
        "base-shops": shop.pk,
        "base-enabled": "",
        "base-logo": "",
        "address-name": "Address Name",
        "address-email": "*****@*****.**",
        "address-phone": "23742578329",
        "address-tax_number": "ABC123",
        "address-street": "Streetz",
        "address-postal_code": "90014",
        "address-city": "Los Angeles",
        "address-region_code": "CA",
        "address-country": "US",
    }
    request = apply_request_middleware(rf.post("/", payload), user=admin_user)
    response = edit_view(request, pk=supplier.pk)
    assert response.status_code == 302

    supplier.refresh_from_db()
    assert not supplier.enabled

    # product is not available anymore
    assert product not in catalog.get_products_queryset()
def test_product_catalog_availability():
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier()
    product1 = factories.create_product("p1", shop=shop, supplier=supplier, default_price=Decimal("30"))
    product2 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=Decimal("10"))

    supplier.stock_managed = True
    supplier.save()

    # add 10 products to product1 stock
    supplier.adjust_stock(product1.pk, delta=10)

    catalog_available_only = ProductCatalog(context=ProductCatalogContext(purchasable_only=True))
    catalog_visible_only = ProductCatalog(context=ProductCatalogContext(purchasable_only=False))
    catalog_all = ProductCatalog(context=ProductCatalogContext(purchasable_only=False, visible_only=False))
    ProductCatalog.index_product(product1)
    ProductCatalog.index_product(product2)

    assert catalog_available_only.get_products_queryset().count() == 1
    assert catalog_visible_only.get_products_queryset().count() == 2
    assert catalog_all.get_products_queryset().count() == 2

    # change the product1 visibility
    ShopProduct.objects.all().update(visibility=ShopProductVisibility.NOT_VISIBLE)
    ProductCatalog.index_product(product1)
    ProductCatalog.index_product(product2)

    assert catalog_available_only.get_products_queryset().count() == 0
    assert catalog_visible_only.get_products_queryset().count() == 0
    assert catalog_all.get_products_queryset().count() == 2
Exemple #6
0
def _get_listed_products(context, n_products, ordering=None, filter_dict=None, orderable_only=True, extra_filters=None):
    """
    Returns all products marked as listed that are determined to be
    visible based on the current context.

    :param context: Rendering context
    :type context: jinja2.runtime.Context
    :param n_products: Number of products to return
    :type n_products: int
    :param ordering: String specifying ordering
    :type ordering: str
    :param filter_dict: Dictionary of filter parameters
    :type filter_dict: dict[str, object]
    :param orderable_only: Boolean limiting results to orderable products
    :type orderable_only: bool
    :param extra_filters: Extra filters to be used in Product Queryset
    :type extra_filters: django.db.models.Q
    :rtype: list[shuup.core.models.Product]
    """
    request = context["request"]
    customer = getattr(request, "customer", None)
    shop = request.shop

    catalog = ProductCatalog(
        ProductCatalogContext(
            shop=shop,
            user=getattr(request, "user", None),
            contact=customer,
            purchasable_only=orderable_only,
            visibility=ShopProductVisibility.LISTED,
        )
    )

    if not filter_dict:
        filter_dict = {}

    products_qs = (
        catalog.get_products_queryset()
        .language(get_language())
        .filter(mode__in=ProductMode.get_parent_modes(), **filter_dict)
    )

    if extra_filters:
        products_qs = products_qs.filter(extra_filters)

    if ordering:
        products_qs = products_qs.order_by(ordering)

    return products_qs.distinct()[:n_products]
Exemple #7
0
def get_all_manufacturers(context, purchasable_only=False):
    request = context["request"]
    catalog = ProductCatalog(
        ProductCatalogContext(
            shop=request.shop,
            user=getattr(request, "user", None),
            contact=getattr(request, "customer", None),
            purchasable_only=purchasable_only,
            visibility=ShopProductVisibility.LISTED,
        )
    )
    manufacturers = Manufacturer.objects.filter(
        pk__in=catalog.get_products_queryset().values_list("manufacturer_id", flat=True).distinct()
    )
    return manufacturers
Exemple #8
0
def _get_cross_sell_products(
    context,
    product: Product,
    types: Iterable[ProductCrossSellType],
    count=5,
    orderable_only=True,
    use_variation_parents=False,
):
    related_product_cross_sells = ProductCrossSell.objects.filter(
        type__in=types)
    # if this product is parent, then use all children instead
    if product.is_variation_parent():
        # Remember to exclude relations with the same parent
        related_product_cross_sells = related_product_cross_sells.filter(
            product1__in=product.variation_children.all()).exclude(
                product2__in=product.variation_children.all())
    else:
        related_product_cross_sells = ProductCrossSell.objects.filter(
            product1=product)

    if use_variation_parents:
        related_product_cross_sells = set(
            related_product_cross_sells.order_by("-weight").values_list(
                Coalesce("product2__variation_parent_id", "product2_id"),
                "weight").distinct())
    else:
        related_product_cross_sells = set(
            related_product_cross_sells.order_by("-weight").values_list(
                "product2_id", "weight").distinct())

    products_ids = [pcs[0] for pcs in related_product_cross_sells]

    request = context["request"]
    customer = get_person_contact(request.user)
    catalog = ProductCatalog(
        ProductCatalogContext(
            shop=request.shop,
            user=getattr(request, "user", None),
            contact=customer,
            purchasable_only=orderable_only,
            visibility=ShopProductVisibility.LISTED,
        ))
    products = catalog.get_products_queryset().filter(
        pk__in=products_ids).distinct()[:count]
    return sorted(products, key=lambda product: products_ids.index(product.id))
def test_product_catalog_visibilities():
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier()
    contact = factories.create_random_person()
    group = factories.create_random_contact_group(shop)
    contact.groups.add(group)
    product = factories.create_product("p", shop=shop, supplier=supplier, default_price=Decimal("10"))

    catalog_visible_only = ProductCatalog(context=ProductCatalogContext(purchasable_only=False))
    catalog_visible_contact = ProductCatalog(context=ProductCatalogContext(purchasable_only=False, contact=contact))
    catalog_all = ProductCatalog(context=ProductCatalogContext(purchasable_only=False, visible_only=False))
    ProductCatalog.index_product(product)

    assert catalog_visible_only.get_products_queryset().count() == 1
    assert catalog_visible_contact.get_products_queryset().count() == 1
    assert catalog_all.get_products_queryset().count() == 1

    # change the visibility to groups
    shop_product = product.get_shop_instance(shop)
    shop_product.visibility_limit = ProductVisibility.VISIBLE_TO_GROUPS
    shop_product.save()
    shop_product.visibility_groups.add(group)
    ProductCatalog.index_product(product)

    assert catalog_visible_only.get_products_queryset().count() == 0
    assert catalog_visible_contact.get_products_queryset().count() == 1
    assert catalog_all.get_products_queryset().count() == 1

    # change the visibility to logged in
    shop_product.visibility_limit = ProductVisibility.VISIBLE_TO_LOGGED_IN
    shop_product.save()
    ProductCatalog.index_product(product)

    assert catalog_visible_only.get_products_queryset().count() == 0
    assert catalog_visible_contact.get_products_queryset().count() == 1
    assert catalog_all.get_products_queryset().count() == 1
Exemple #10
0
    def get_queryset(self):
        if not self.form.is_valid():
            return Product.objects.none()
        data = self.form.cleaned_data
        if not (data and data.get("q")):  # pragma: no cover
            return Product.objects.none()

        catalog = ProductCatalog(
            ProductCatalogContext(
                shop=self.request.shop,
                user=self.request.user,
                contact=getattr(self.request, "customer", None),
                purchasable_only=True,
                visibility=ShopProductVisibility.SEARCHABLE,
            ))
        products = catalog.get_products_queryset().filter(
            Q(mode__in=ProductMode.get_parent_modes()),
            Q(get_query_filters(self.request, None, data=data)))
        products = get_product_queryset(products, self.request, None, data)
        products = sort_products(self.request, None, products, data)
        return products.distinct()
Exemple #11
0
    def get_context_data(self, context):
        request = context["request"]
        products = self.config.get("products")
        products_qs = Product.objects.none()

        if products:
            catalog = ProductCatalog(
                ProductCatalogContext(
                    shop=request.shop,
                    user=getattr(request, "user", None),
                    contact=getattr(request, "customer", None),
                    purchasable_only=True,
                    visibility=ShopProductVisibility.LISTED,
                ))
            products_qs = catalog.get_products_queryset().filter(
                pk__in=products, mode__in=ProductMode.get_parent_modes())

        return {
            "request": request,
            "title": self.get_translated_value("title"),
            "products": products_qs
        }
Exemple #12
0
    def get_context_data(self, context):
        context = super().get_context_data(context)
        request = context["request"]
        product_ids = [
            int(pid) for pid in request.COOKIES.get("rvp", "").split(",")
            if pid != ""
        ]

        catalog = ProductCatalog(
            ProductCatalogContext(
                shop=request.shop,
                user=getattr(request, "user", None),
                contact=getattr(request, "customer", None),
                purchasable_only=True,
                visibility=ShopProductVisibility.LISTED,
            ))
        context["products"] = sorted(
            catalog.get_products_queryset().filter(
                id__in=product_ids, mode__in=ProductMode.get_parent_modes()),
            key=lambda p: product_ids.index(p.pk),
        )
        return context
Exemple #13
0
    def get_context_data(self, context):
        request = context["request"]
        products = self.config.get("products", [])
        cache_timeout = self.config.get("cache_timeout", 0)
        products_qs = Product.objects.none()

        if request.is_ajax() and products:
            catalog = ProductCatalog(
                ProductCatalogContext(
                    shop=request.shop,
                    user=getattr(request, "user", None),
                    contact=getattr(request, "customer", None),
                    purchasable_only=True,
                    visibility=ShopProductVisibility.LISTED,
                ))
            products_qs = catalog.get_products_queryset().filter(
                pk__in=products, mode__in=ProductMode.get_parent_modes())

        return {
            "request":
            request,
            "title":
            self.get_translated_value("title"),
            "products":
            products_qs,
            "data_url":
            reverse(
                "shuup:xtheme-product-selections-highlight",
                kwargs=dict(
                    product_ids=",".join([
                        (str(prod.pk) if hasattr(prod, "pk") else str(prod))
                        for prod in products
                    ]),
                    cache_timeout=cache_timeout,
                ),
            ),
        }
Exemple #14
0
def get_context_data(context, request, category, product_filters):
    data = request.GET
    context["form"] = form = ProductListForm(request=request,
                                             shop=request.shop,
                                             category=category,
                                             data=data)
    form.full_clean()
    data = form.cleaned_data
    if "sort" in form.fields and not data.get("sort"):
        # Use first choice by default
        data["sort"] = form.fields["sort"].widget.choices[0][0]

    catalog = ProductCatalog(
        ProductCatalogContext(
            shop=request.shop,
            user=getattr(request, "user", None),
            contact=getattr(request, "customer", None),
            purchasable_only=True,
            supplier=data.get("supplier") or None,
            visibility=ShopProductVisibility.LISTED,
        ))
    products = (catalog.get_products_queryset().filter(
        **product_filters).filter(
            get_query_filters(request, category, data=data)).select_related(
                "primary_image", "sales_unit",
                "tax_class").prefetch_related("translations",
                                              "sales_unit__translations"))
    products = get_product_queryset(products, request, category, data)
    products = sort_products(request, category, products, data).distinct()
    context["page_size"] = data.get("limit", 12)
    context["products"] = products

    if "supplier" in data:
        context["supplier"] = data.get("supplier")

    return context