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
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)), ]
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
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
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]
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
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
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()
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 }
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
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, ), ), }
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