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 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 best_selling(self, request): data = get_best_selling_product_info( shop_ids=[request.GET.get("shop", request.shop.pk)], cutoff_days=30, orderable_only=True, ) 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.GET.get("shop", request.shop.pk), user=getattr(request, "user", None), contact=getattr(request, "customer", None), purchasable_only=True, )) valid_shop_products_qs = catalog.get_shop_products_queryset().filter( product__in=product_ids, product__mode__in=ProductMode.get_parent_modes()) shop_products_qs = self.filter_queryset( valid_shop_products_qs).distinct() page = self.paginate_queryset(shop_products_qs) if page is not None: serializer = self.get_serializer_class()( page, many=True, context=self.get_serializer_context()) return self.get_paginated_response(serializer.data) serializer = self.get_serializer_class()( shop_products_qs, many=True, context=self.get_serializer_context()) return Response(serializer.data)
def test_admin_catalog_discount_contact_group_filter(rf, admin_user): shop, supplier, contact, group, category = _init_test() default_price = Decimal("15.0") discounted_price_value = Decimal("12") product = factories.create_product("p1", shop=shop, supplier=supplier, default_price=default_price) product.get_shop_instance(shop).categories.add(category) ProductCatalog.index_product(product) view = DiscountEditView.as_view() # create a discounted price for the contact group payload = _get_default_discount_data( {"contact_group": group.pk, "discounted_price_value": str(discounted_price_value)} ) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=None) assert response.status_code == 302 anon_catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False)) customer_catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False, contact=contact)) _assert_products_queryset(anon_catalog, [(product.pk, default_price, None)]) _assert_products_queryset(customer_catalog, [(product.pk, default_price, discounted_price_value)]) # remove the group from the discount discount = Discount.objects.last() payload = _get_default_discount_data({"discounted_price_value": str(discounted_price_value)}) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=discount.pk) assert response.status_code == 302 _assert_products_queryset(anon_catalog, [(product.pk, default_price, discounted_price_value)]) _assert_products_queryset(customer_catalog, [(product.pk, default_price, discounted_price_value)])
def reindex_catalog(apps, schema_editor): ShopProduct = apps.get_model("shuup", "ShopProduct") from shuup.core.models import ProductMode for shop_product in ShopProduct.objects.exclude( product__mode=ProductMode.VARIATION_CHILD): ProductCatalog.index_shop_product(shop_product.pk)
def reindex_catalog(apps, schema_editor): activate(settings.PARLER_DEFAULT_LANGUAGE_CODE) ShopProduct = apps.get_model("shuup", "ShopProduct") from shuup.core.models import ProductMode for shop_product in ShopProduct.objects.exclude( product__mode=ProductMode.VARIATION_CHILD): ProductCatalog.index_shop_product(shop_product.pk)
def postsave_hook(self, sess): # noqa (C901) # get all the special values shop_product = ShopProduct.objects.get_or_create(product=sess.instance, shop=sess.shop)[0] matched_fields = [] for k, v in six.iteritems(sess.importer.extra_matches): if k.startswith("ShopProduct:"): x, field = k.split(":") matched_fields.append(field) val = sess.row.get(v) setattr(shop_product, field, val) for k, v in sess.importer.data_map.items(): field_name = v.get("id") if field_name in self.fields_to_skip: continue if hasattr(shop_product, field_name): field = getattr(shop_product, field_name) value = sess.row.get(k, None) if isinstance(field, PriceProperty): value = sess.shop.create_price(value) value, is_related = self._find_related_values( field_name, sess, value) if is_related and isinstance(value, six.string_types): continue if field_name in [ "suppliers", "visibility_groups", "shipping_methods", "payment_methods", "categories", ]: getattr(shop_product, field_name).set(value) else: setattr(shop_product, field_name, value) shop_product.full_clean() shop_product.save() # add shop relation to the manufacturer if sess.instance.manufacturer: sess.instance.manufacturer.shops.add(sess.shop) # add shop relation to all categories for category in shop_product.categories.all(): category.shops.add(sess.shop) ProductCatalog.index_shop_product(shop_product)
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_simple_list(): 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")) product3 = factories.create_product("p3", shop=shop, supplier=supplier, default_price=Decimal("20")) catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False)) ProductCatalog.index_product(product1) ProductCatalog.index_product(product2) ProductCatalog.index_product(product3) _assert_products_queryset( catalog, [ (product2.pk, Decimal("10"), None), (product3.pk, Decimal("20"), None), (product1.pk, Decimal("30"), None), ], ) _assert_shop_products_queryset( catalog, [ (product2.get_shop_instance(shop).pk, Decimal("10"), None), (product3.get_shop_instance(shop).pk, Decimal("20"), None), (product1.get_shop_instance(shop).pk, Decimal("30"), None), ], ) _assert_price(product1, shop, Decimal("30"), Decimal("30")) _assert_price(product2, shop, Decimal("10"), Decimal("10")) _assert_price(product3, shop, Decimal("20"), Decimal("20"))
def test_product_catalog_purchasable(): 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 = ProductCatalog(context=ProductCatalogContext(purchasable_only=True)) ProductCatalog.index_product(product1) ProductCatalog.index_product(product2) _assert_products_queryset(catalog, [(product1.pk, Decimal("30"), None)]) _assert_shop_products_queryset(catalog, [(product1.get_shop_instance(shop).pk, Decimal("30"), None)])
def test_product_catalog_product_discount(): shop = factories.get_default_shop() supplier = factories.get_default_supplier() product1 = factories.create_product("p1", shop=shop, supplier=supplier, default_price=Decimal("10")) product2 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=Decimal("20")) # create a $5 discount for the product Discount.objects.create( shop=shop, product=product1, discount_amount_value=Decimal(5), start_datetime=timezone.now(), end_datetime=timezone.now() + timedelta(days=1), ) catalog = ProductCatalog(context=ProductCatalogContext( purchasable_only=False)) ProductCatalog.index_product(product1) ProductCatalog.index_product(product2) _assert_products_queryset( catalog, [ (product1.pk, Decimal("10"), Decimal("5")), (product2.pk, Decimal("20"), None), ], ) _assert_shop_products_queryset( catalog, [ (product1.get_shop_instance(shop).pk, Decimal("10"), Decimal("5")), (product2.get_shop_instance(shop).pk, Decimal("20"), None), ], ) _assert_price(product1, shop, Decimal("5"), Decimal("10")) _assert_price(product2, shop, Decimal("20"), Decimal("20"))
def test_product_catalog_category_discount(): shop = factories.get_default_shop() supplier = factories.get_default_supplier() contact = factories.create_random_person() group = PersonContact.get_default_group() category = factories.get_default_category() contact.groups.add(group) product1 = factories.create_product("p1", shop=shop, supplier=supplier, default_price=Decimal("10")) product2 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=Decimal("20")) product3 = factories.create_product("p3", shop=shop, supplier=supplier, default_price=Decimal("30")) product1.get_shop_instance(shop).categories.add(category) product3.get_shop_instance(shop).categories.add(category) # create a 10% discount for the category Discount.objects.create( shop=shop, category=category, discount_percentage=Decimal(0.1), start_datetime=timezone.now(), end_datetime=timezone.now() + timedelta(days=1), ) catalog = ProductCatalog(context=ProductCatalogContext( purchasable_only=False)) ProductCatalog.index_product(product1) ProductCatalog.index_product(product2) ProductCatalog.index_product(product3) _assert_products_queryset( catalog, [ (product1.pk, Decimal("10"), Decimal("9")), (product2.pk, Decimal("20"), None), (product3.pk, Decimal("30"), Decimal("27")), ], ) _assert_shop_products_queryset( catalog, [ (product1.get_shop_instance(shop).pk, Decimal("10"), Decimal("9")), (product2.get_shop_instance(shop).pk, Decimal("20"), None), (product3.get_shop_instance(shop).pk, Decimal("30"), Decimal("27")), ], ) _assert_price(product1, shop, Decimal("9"), Decimal("10")) _assert_price(product2, shop, Decimal("20"), Decimal("20")) _assert_price(product3, shop, Decimal("27"), Decimal("30"))
def test_admin_catalog_discount_category_filter(rf, admin_user): shop, supplier, contact, group, category = _init_test() default_price = Decimal("15.0") discount_amount = Decimal("5") product = factories.create_product("p1", shop=shop, supplier=supplier, default_price=default_price) product.get_shop_instance(shop).categories.add(category) ProductCatalog.index_product(product) view = DiscountEditView.as_view() # create a $5 discount for the category payload = _get_default_discount_data({"category": category.pk, "discount_amount_value": str(discount_amount)}) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=None) assert response.status_code == 302 anon_catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False)) customer_catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False, contact=contact)) # discount is indexed discounted_price = (default_price - discount_amount).quantize(Decimal("0.01")) _assert_products_queryset(anon_catalog, [(product.pk, default_price, discounted_price)]) _assert_products_queryset(customer_catalog, [(product.pk, default_price, discounted_price)]) # make the exclude_selected_category flag be True discount = Discount.objects.last() payload = _get_default_discount_data( { "category": category.pk, "exclude_selected_category": "on", "discount_amount_value": str(discount_amount), } ) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=discount.pk) assert response.status_code == 302 # discounts removed from the product _assert_products_queryset(anon_catalog, [(product.pk, default_price, None)]) _assert_products_queryset(customer_catalog, [(product.pk, default_price, None)])
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 _visible(self, shop, customer, language=None, purchasable_only=False, visibility: "ShopProductVisibility" = None): root = self.language(language) if language else self from shuup.core.catalog import ProductCatalog, ProductCatalogContext catalog = ProductCatalog( ProductCatalogContext(shop=shop, contact=customer, purchasable_only=purchasable_only, visibility=visibility)) qs = catalog.annotate_products_queryset(root.all()) qs = qs.select_related( *Product.COMMON_SELECT_RELATED).prefetch_related( *Product.COMMON_PREFETCH_RELATED) return qs.exclude(type__isnull=True)
def test_admin_catalog_discount_product_filter(rf, admin_user): shop, supplier, contact, group, category = _init_test() default_price = Decimal("15.0") discount_percentage = Decimal("10") product = factories.create_product("p1", shop=shop, supplier=supplier, default_price=default_price) product2 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=default_price) ProductCatalog.index_product(product) ProductCatalog.index_product(product2) view = DiscountEditView.as_view() # create a 10% discount for the product payload = _get_default_discount_data({"product": product.pk, "discount_percentage": str(discount_percentage)}) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=None) assert response.status_code == 302 anon_catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False)) customer_catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False, contact=contact)) # discount is indexed discounted_price = (default_price - (default_price * discount_percentage * Decimal(0.01))).quantize(Decimal("0.01")) _assert_products_queryset( anon_catalog, [ (product.pk, default_price, discounted_price), (product2.pk, default_price, None), ], ) _assert_products_queryset( customer_catalog, [ (product.pk, default_price, discounted_price), (product2.pk, default_price, None), ], ) # changed the discount product discount = Discount.objects.last() payload = _get_default_discount_data({"product": product2.pk, "discount_percentage": str(discount_percentage)}) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=discount.pk) assert response.status_code == 302 _assert_products_queryset( anon_catalog, [ (product.pk, default_price, None), (product2.pk, default_price, discounted_price), ], ) _assert_products_queryset( customer_catalog, [ (product.pk, default_price, None), (product2.pk, default_price, discounted_price), ], )
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_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 test_product_catalog_variations(): shop = factories.get_default_shop() supplier = factories.get_default_supplier() parent = factories.create_product("p1", shop=shop, supplier=supplier, default_price=Decimal("10")) child1 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=Decimal("20")) child2 = factories.create_product("p3", shop=shop, supplier=supplier, default_price=Decimal("40")) child3 = factories.create_product("p4", shop=shop, supplier=supplier, default_price=Decimal("50")) child1.link_to_parent(parent) child2.link_to_parent(parent) child3.link_to_parent(parent) catalog = ProductCatalog(context=ProductCatalogContext(purchasable_only=False)) ProductCatalog.index_product(parent) _assert_products_queryset( catalog, [ (parent.pk, Decimal("10"), None), (child1.pk, Decimal("20"), None), (child2.pk, Decimal("40"), None), (child3.pk, Decimal("50"), None), ], ) _assert_shop_products_queryset( catalog, [ (parent.get_shop_instance(shop).pk, Decimal("10"), None), (child1.get_shop_instance(shop).pk, Decimal("20"), None), (child2.get_shop_instance(shop).pk, Decimal("40"), None), (child3.get_shop_instance(shop).pk, Decimal("50"), None), ], ) _assert_price(parent, shop, Decimal("10"), Decimal("10")) _assert_price(child1, shop, Decimal("20"), Decimal("20")) _assert_price(child2, shop, Decimal("40"), Decimal("40")) _assert_price(child3, shop, Decimal("50"), Decimal("50"))
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
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_admin_custom_customer_price_updates(rf, admin_user): shop = factories.get_default_shop() supplier = factories.get_default_supplier() contact = factories.create_random_person() group = PersonContact.get_default_group() contact.groups.add(group) product_type = factories.get_default_product_type() tax_class = factories.get_default_tax_class() sales_unit = factories.get_default_sales_unit() view = ProductEditView.as_view() group_price = "10.0" default_price = "15.0" payload = { "base-name__en": "My Product", "base-type": product_type.pk, "base-sku": "p1", "base-shipping_mode": ShippingMode.NOT_SHIPPED.value, "base-tax_class": tax_class.pk, "base-sales_unit": sales_unit.pk, "base-width": "0", "base-height": "0", "base-depth": "0", "base-net_weight": "0", "base-gross_weight": "0", f"shop{shop.pk}-default_price_value": default_price, f"shop{shop.pk}-visibility": ShopProductVisibility.ALWAYS_VISIBLE.value, f"shop{shop.pk}-visibility_limit": ProductVisibility.VISIBLE_TO_ALL.value, f"shop{shop.pk}-minimum_purchase_quantity": "1", f"shop{shop.pk}-purchase_multiple": "1", f"shop{shop.pk}-suppliers": [supplier.pk], f"customer_group_pricing-s_{shop.pk}_g_{group.pk}": group_price, # set price for the group } # create a new product request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=None) assert response.status_code == 302 anon_catalog = ProductCatalog(context=ProductCatalogContext( purchasable_only=False)) customer_catalog = ProductCatalog( context=ProductCatalogContext(purchasable_only=False, contact=contact)) product = Product.objects.first() _assert_products_queryset(anon_catalog, [(product.pk, Decimal(default_price), None)]) _assert_products_queryset(customer_catalog, [(product.pk, Decimal(group_price), None)]) payload.update({ # remove the customer group price f"customer_group_pricing-s_{shop.pk}_g_{group.pk}": "", "media-TOTAL_FORMS": 0, "media-INITIAL_FORMS": 0, "media-MIN_NUM_FORMS": 0, "media-MAX_NUM_FORMS": 1000, "images-TOTAL_FORMS": 0, "images-INITIAL_FORMS": 0, "images-MIN_NUM_FORMS": 0, "images-MAX_NUM_FORMS": 1000, }) request = apply_request_middleware(rf.post("/", data=payload), shop=shop, user=admin_user) with patch("django.db.transaction.on_commit", new=atomic_commit_mock): response = view(request, pk=product.get_shop_instance(shop).pk) assert response.status_code == 302 # default price for both _assert_products_queryset(anon_catalog, [(product.pk, Decimal(default_price), None)]) _assert_products_queryset(customer_catalog, [(product.pk, Decimal(default_price), None)])
def handle(self, *args, **options): for shop_product in ShopProduct.objects.exclude( product__mode=ProductMode.VARIATION_CHILD): ProductCatalog.index_shop_product(shop_product)
def test_product_catalog_cgp_with_variations(): shop = factories.get_default_shop() supplier = factories.get_default_supplier() contact = factories.create_random_person() group = PersonContact.get_default_group() contact.groups.add(group) parent = factories.create_product("p1", shop=shop, supplier=supplier, default_price=Decimal("10")) child1 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=Decimal("20")) child2 = factories.create_product("p3", shop=shop, supplier=supplier, default_price=Decimal("40")) child3 = factories.create_product("p4", shop=shop, supplier=supplier, default_price=Decimal("50")) child1.link_to_parent(parent) child2.link_to_parent(parent) child3.link_to_parent(parent) # set a price for child2 CgpPrice.objects.create(shop=shop, product=child2, group=group, price_value=Decimal("5")) # create a discount for child3 CgpDiscount.objects.create(shop=shop, product=child3, group=group, discount_amount_value=Decimal("35")) catalog = ProductCatalog( context=ProductCatalogContext(purchasable_only=False, contact=contact)) ProductCatalog.index_product(parent) _assert_products_queryset( catalog, [ (child2.pk, Decimal("5"), None), (parent.pk, Decimal("10"), None), (child1.pk, Decimal("20"), None), (child3.pk, Decimal("50"), Decimal("15")), ], ) _assert_shop_products_queryset( catalog, [ (child2.get_shop_instance(shop).pk, Decimal("5"), None), (parent.get_shop_instance(shop).pk, Decimal("10"), None), (child1.get_shop_instance(shop).pk, Decimal("20"), None), (child3.get_shop_instance(shop).pk, Decimal("50"), Decimal("15")), ], ) # no customer _assert_price(parent, shop, Decimal("10"), Decimal("10")) _assert_price(child1, shop, Decimal("20"), Decimal("20")) _assert_price(child2, shop, Decimal("40"), Decimal("40")) _assert_price(child3, shop, Decimal("50"), Decimal("50")) # with the customer in the group _assert_price(parent, shop, Decimal("10"), Decimal("10"), customer=contact) _assert_price(child1, shop, Decimal("20"), Decimal("20"), customer=contact) _assert_price(child2, shop, Decimal("5"), Decimal("40"), customer=contact) _assert_price(child3, shop, Decimal("15"), Decimal("50"), customer=contact)
def test_product_catalog_discounted_price(): shop = factories.get_default_shop() supplier = factories.get_default_supplier() contact = factories.create_random_person() group = PersonContact.get_default_group() contact.groups.add(group) product1 = factories.create_product("p1", shop=shop, supplier=supplier, default_price=Decimal("50")) product2 = factories.create_product("p2", shop=shop, supplier=supplier, default_price=Decimal("30")) # set price for product2 CgpPrice.objects.create(shop=shop, product=product2, group=group, price_value=Decimal(25)) # create a discount for product2 CgpDiscount.objects.create(shop=shop, product=product2, group=group, discount_amount_value=Decimal(7)) anon_catalog = ProductCatalog(context=ProductCatalogContext( purchasable_only=False)) customer_catalog = ProductCatalog( context=ProductCatalogContext(purchasable_only=False, contact=contact)) ProductCatalog.index_product(product1) ProductCatalog.index_product(product2) _assert_products_queryset( anon_catalog, [ (product2.pk, Decimal("30"), None), (product1.pk, Decimal("50"), None), ], ) _assert_products_queryset( customer_catalog, [ (product2.pk, Decimal("25"), Decimal("23")), (product1.pk, Decimal("50"), None), ], ) _assert_shop_products_queryset( anon_catalog, [ (product2.get_shop_instance(shop).pk, Decimal("30"), None), (product1.get_shop_instance(shop).pk, Decimal("50"), None), ], ) _assert_shop_products_queryset( customer_catalog, [ (product2.get_shop_instance(shop).pk, Decimal("25"), Decimal("23")), (product1.get_shop_instance(shop).pk, Decimal("50"), None), ], ) # no customer _assert_price(product1, shop, Decimal("50"), Decimal("50")) _assert_price(product2, shop, Decimal("30"), Decimal("30")) # with the customer in the group _assert_price(product1, shop, Decimal("50"), Decimal("50"), customer=contact) _assert_price(product2, shop, Decimal("18"), Decimal("30"), customer=contact)
def index_product(product_id: int): """ Task to call the ProductCatalog to index the product. This util function can be used by a task runner to index this asynchronously. """ ProductCatalog.index_product(product_id)