def test_formatted_decimal_field(): """ Test that FormattedDecimalField doesn't return value in scientific notation. """ class TestModelForm(ModelForm): class Meta: model = Product fields = ["width"] values = [ "0E-9", "0E-30", "1E-9", "123E-10", "-123E-10", "1.12345666666666E20" ] for value in values: product = Product(width=Decimal(value)) form = TestModelForm(instance=product) rendered_form = force_text(form) rendered_value = re.search('value="(.*?)"', rendered_form).group(1) rendered_step = re.search('step="(.*?)"', rendered_form).group(1) assert rendered_value and "E" not in rendered_value assert rendered_step and "E" not in rendered_step # Extremely large exponents should raise an exception so as not to # produce excessively large files large_value = "1.23E-10000" product = Product(width=Decimal(large_value)) with pytest.raises(ValueError): form = TestModelForm(instance=product)
def create_product(sku, shop=None, supplier=None, default_price=None, **attrs): if default_price is not None: default_price = shop.create_price(default_price) product_attrs = dict( type=get_default_product_type(), tax_class=get_default_tax_class(), sku=sku, name=sku.title(), width=100, height=100, depth=100, net_weight=100, gross_weight=100, sales_unit=get_default_sales_unit(), stock_behavior=StockBehavior.UNSTOCKED ) product_attrs.update(attrs) product = Product(**product_attrs) product.full_clean() product.save() if shop: sp = ShopProduct.objects.create( product=product, shop=shop, default_price=default_price, visibility=ShopProductVisibility.ALWAYS_VISIBLE ) if supplier: sp.suppliers.add(supplier) sp.save() return product
def test_model_url(): with admin_only_urls(): with pytest.raises(NoModelUrl): get_model_url(Counter) # That's silly! p = Product() p.pk = 3 assert get_model_url(p)
def test_model_url_with_permissions(): permissions = set( ["shuup.add_product", "shuup.delete_product", "shuup.change_product"]) p = Product() p.pk = 3 # If no user is given, don't check for permissions assert get_model_url(p) # If a user is given and no permissions are provided, check for default model permissions user = StaffUser() with pytest.raises(NoModelUrl): assert get_model_url(p, user=user) # If a user is given and permissions are provided, check for those permissions assert get_model_url(p, user=user, required_permissions=()) with pytest.raises(NoModelUrl): assert get_model_url(p, user=user, required_permissions=["shuup.add_product"]) # Confirm that url is returned with correct permissions user.permissions = permissions assert get_model_url(p, user=user) assert get_model_url(p, user=user, required_permissions=permissions)
def test_applied_attributes(): product = get_default_product() for spec in ATTR_SPECS: # This loop sets each attribute twice. That's okay. attr = Attribute.objects.get(identifier=spec["identifier"]) pa, _ = ProductAttribute.objects.get_or_create(product=product, attribute=attr) _populate_applied_attribute(pa) pa.save() if not attr.is_translated: product.set_attribute_value(attr.identifier, pa.value) assert product.get_attribute_value("bogomips") == 320, "integer attribute loaded neatly" product.set_attribute_value("bogomips", 480) assert product.get_attribute_value("bogomips") == 480, "integer attribute updated neatly" Product.cache_attributes_for_targets( applied_attr_cls=ProductAttribute, targets=[product], attribute_identifiers=[a["identifier"] for a in ATTR_SPECS], language=get_language() ) assert (get_language(), "bogomips",) in product._attr_cache, "integer attribute in cache" assert product.get_attribute_value("bogomips") == 480, "integer attribute value in cache" assert product.get_attribute_value("ba:gelmips", default="Britta") == "Britta", "non-existent attributes return default value" assert product._attr_cache[(get_language(), "ba:gelmips")] is NoSuchAttributeHere, "cache miss saved" attr_info = product.get_all_attribute_info(language=get_language(), visibility_mode=AttributeVisibility.SHOW_ON_PRODUCT_PAGE) assert set(attr_info.keys()) <= set(a["identifier"] for a in ATTR_SPECS), "get_all_attribute_info gets all attribute info"
def test_modelform_persistence(): with translation.override("en"): test_product = Product(barcode="666") test_product.set_current_language("en") test_product.name = "foo" frm = MultiProductForm(languages=["en"], instance=test_product, default_language="en") assert frm["barcode"].value() == test_product.barcode assert frm.initial["name"] == test_product.name
def cache_product_things(request, products, language=None, attribute_identifiers=("author",)): # Cache necessary things for products. WARNING: This will cause queryset iteration. language = language or get_language() # TODO: Should we cache prices here? if attribute_identifiers: Product.cache_attributes_for_targets( ProductAttribute, products, attribute_identifiers=attribute_identifiers, language=language ) products = cache_translations(products, (language,)) return products
def cache_product_things(request, products, language=None, attribute_identifiers=("author",)): # Cache necessary things for products. WARNING: This will cause queryset iteration. language = language or get_language() # TODO: Should we cache prices here? if attribute_identifiers: Product.cache_attributes_for_targets( ProductAttribute, products, attribute_identifiers=attribute_identifiers, language=language) products = cache_translations(products, (language,)) return products
def test_modelform_persistence(): with translation.override("en"): test_product = Product(barcode="666", stock_behavior=StockBehavior.STOCKED) test_product.set_current_language("en") test_product.name = "foo" frm = MultiProductForm(languages=["en"], instance=test_product, default_language="en") assert frm["barcode"].value() == test_product.barcode stock_behavior_field = Product._meta.get_field_by_name("stock_behavior")[0] assert stock_behavior_field.to_python(frm["stock_behavior"].value()) is test_product.stock_behavior assert 'value="1" selected="selected"' in six.text_type(frm["stock_behavior"].as_widget()) assert frm.initial["name"] == test_product.name
def test_modelform_persistence(): with translation.override("en"): test_product = Product(barcode="666", stock_behavior=StockBehavior.STOCKED) test_product.set_current_language("en") test_product.name = "foo" frm = MultiProductForm(languages=["en"], instance=test_product, default_language="en") assert frm["barcode"].value() == test_product.barcode stock_behavior_field = Product._meta.get_field("stock_behavior") assert stock_behavior_field.to_python(frm["stock_behavior"].value()) is test_product.stock_behavior assert 'value="1" selected' in six.text_type(frm["stock_behavior"].as_widget()) assert frm.initial["name"] == test_product.name
def get_object(self, queryset=None): if not self.kwargs.get(self.pk_url_kwarg): instance = self.model() instance.shop = self.request.shop instance.product = Product() return instance return super(ProductEditView, self).get_object(queryset)
def _get_template_engine_and_context(): engine = django.template.engines['jinja2'] assert isinstance(engine, django_jinja.backend.Jinja2) request = RequestFactory().get('/') request.shop = Shop(currency='USD', prices_include_tax=False) request.customer = AnonymousContact() request.person = request.customer PriceDisplayOptions(include_taxes=False).set_for_request(request) tax = get_default_tax() create_default_tax_rule(tax) tax_class = get_default_tax_class() order, order_line = _get_order_and_order_line(request) context = { 'request': request, 'prod': Product(sku='6.0745', tax_class=tax_class), # TODO: Test also with variant products 'sline': _get_source_line(request), 'bline': _get_basket_line(request), 'oline': order_line, 'order': order } return (engine, context)
def test_formatted_decimal_field_default(): class TestModelForm(ModelForm): class Meta: model = Product fields = ["width"] rendered_form = force_text(TestModelForm(instance=Product())) rendered_value = re.search('value="(.*?)"', rendered_form).group(1) assert rendered_value == "0"
def test_pricing_module_is_active(): """ Make sure that our custom pricing module is active. """ shop = Shop(currency="USD", prices_include_tax=False) customer = AnonymousContact() product = Product(sku="6.0745") pricing_mod = get_pricing_module() pricing_ctx = pricing_mod.get_context_from_data(shop, customer) pi = product.get_price_info(pricing_ctx, quantity=2) price = shop.create_price assert pi.price == price("12.149") assert pi.base_price == price("48.596") assert pi.quantity == 2 assert pi.discounted_unit_price == price("6.0745") assert pi.base_unit_price == price("24.298") assert pi.discount_rate == Decimal("0.75")
def test_pricing_module_is_active(): """ Make sure that our custom pricing module is active. """ shop = Shop(currency='USD', prices_include_tax=False) customer = AnonymousContact() product = Product(sku='6.0745') pricing_mod = get_pricing_module() pricing_ctx = pricing_mod.get_context_from_data(shop, customer) pi = product.get_price_info(pricing_ctx, quantity=2) price = shop.create_price assert pi.price == price('12.149') assert pi.base_price == price('48.596') assert pi.quantity == 2 assert pi.discounted_unit_price == price('6.0745') assert pi.base_unit_price == price('24.298') assert pi.discount_rate == Decimal('0.75')
def test_convert_taxness_without_conversion(taxes, price_cls): request = get_request() item = Product() priceful = _get_price_info(price_cls) calcs_done_before = DummyTaxModule.calculations_done result = convert_taxness(request, item, priceful, with_taxes=taxes) calcs_done_after = DummyTaxModule.calculations_done assert result == priceful assert result.price == price_cls(480, 'USD') assert result.base_price == price_cls(660, 'USD') assert result.quantity == 2 assert calcs_done_after == calcs_done_before
def test_product_caching_object_type_validation(): with pytest.raises(TypeError): pco = ProductCachingObject() pco.product_id = "yeah" with pytest.raises(TypeError): pco = ProductCachingObject() pco.product = "yeahhh" with pytest.raises(ValueError): pco = ProductCachingObject() pco.product = Product()
def create_product(sku, shop=None, supplier=None, default_price=None, **attrs): if default_price is not None: default_price = shop.create_price(default_price) product_attrs = dict(type=get_default_product_type(), tax_class=get_default_tax_class(), sku=sku, name=sku.title(), width=100, height=100, depth=100, net_weight=100, gross_weight=100, sales_unit=get_default_sales_unit(), stock_behavior=StockBehavior.UNSTOCKED) product_attrs.update(attrs) product = Product(**product_attrs) product.full_clean() product.save() if shop: sp = ShopProduct.objects.create(product=product, shop=shop, default_price=default_price) if supplier: sp.suppliers.add(supplier) sp.save() return product
def create_product(sku, shop=None, supplier=None, default_price=None, **attrs): if default_price is not None: default_price = shop.create_price(default_price) if "fractional" in attrs: attrs.pop("fractional") get_sales_unit = get_fractional_sales_unit else: get_sales_unit = get_default_sales_unit product_attrs = dict( type=get_default_product_type(), tax_class=get_default_tax_class(), sku=sku, name=sku.title(), width=100, height=100, depth=100, net_weight=100, gross_weight=100, sales_unit=get_sales_unit(), ) product_attrs.update(attrs) product = Product(**product_attrs) product.full_clean() product.save() if shop: sp = ShopProduct.objects.create( product=product, shop=shop, default_price=default_price, visibility=ShopProductVisibility.ALWAYS_VISIBLE ) if supplier: sp.suppliers.add(supplier) sp.save() return product
def test_model_url_with_permissions(): permissions = set(["shuup.add_product", "shuup.delete_product", "shuup.change_product"]) p = Product() p.pk = 3 # If no user is given, don't check for permissions assert get_model_url(p) # If a user is given and no permissions are provided, check for default model permissions user = StaffUser() with pytest.raises(NoModelUrl): assert get_model_url(p, user=user) # If a user is given and permissions are provided, check for those permissions assert get_model_url(p, user=user, required_permissions=()) with pytest.raises(NoModelUrl): assert get_model_url(p, user=user, required_permissions=["shuup.add_product"]) # Confirm that url is returned with correct permissions user.permissions = permissions assert get_model_url(p, user=user) assert get_model_url(p, user=user, required_permissions=permissions)
def _get_order_line(request): order = Order( shop=request.shop, currency=request.shop.currency, prices_include_tax=request.shop.prices_include_tax, ) pi = _get_price_info(request.shop, Product(sku='6.0745'), quantity=2) return OrderLine( order=order, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount, quantity=pi.quantity, )
def _get_order_and_order_line(request): order = Order( shop=request.shop, currency=request.shop.currency, prices_include_tax=request.shop.prices_include_tax, ) order.taxful_total_price = TaxfulPrice("100", request.shop.currency) order.taxless_total_price = TaxlessPrice("50", request.shop.currency) pi = _get_price_info(request.shop, Product(sku='6.0745'), quantity=2) return (order, OrderLine( order=order, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount, quantity=pi.quantity, ))
def test_convert_taxness_taxless_to_taxful(): request = get_request() tax_class = TaxClass() item = Product(tax_class=tax_class) priceful = _get_price_info(TaxlessPrice) calcs_done_before = DummyTaxModule.calculations_done result = convert_taxness(request, item, priceful, with_taxes=True) calcs_done_after = DummyTaxModule.calculations_done assert result != priceful assert result.price == TaxfulPrice(576, 'USD') assert result.base_price == TaxfulPrice(792, 'USD') assert result.quantity == 2 assert result.tax_amount == Money(96, 'USD') assert result.taxful_price == result.price assert result.taxless_price == priceful.price assert calcs_done_after == calcs_done_before + 2
def test_convert_taxness_taxful_to_taxless(): request = get_request() tax_class = TaxClass() item = Product(tax_class=tax_class) priceful = _get_price_info(TaxfulPrice) calcs_done_before = DummyTaxModule.calculations_done result = convert_taxness(request, item, priceful, with_taxes=False) calcs_done_after = DummyTaxModule.calculations_done assert result != priceful assert (result.price - TaxlessPrice(400, "USD")).value < 0.00001 assert result.base_price == TaxlessPrice(550, "USD") assert result.quantity == 2 assert result.tax_amount == Money(80, "USD") assert result.taxless_price == result.price assert result.taxful_price == priceful.price assert calcs_done_after == calcs_done_before + 2
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 _get_template_engine_and_context(): engine = django.template.engines['jinja2'] assert isinstance(engine, django_jinja.backend.Jinja2) request = RequestFactory().get('/') request.shop = Shop(currency='USD', prices_include_tax=False) request.customer = AnonymousContact() request.person = request.customer context = { 'request': request, 'prod': Product(sku='6.0745'), # TODO: Test also with variant products 'sline': _get_source_line(request), 'bline': _get_basket_line(request), 'oline': _get_order_line(request), } return (engine, context)
def create_variation_product(parent_product: Product, shop: Shop, sku: str, combination: Combination, combination_hash: str) -> Product: variation_child = Product( name=get_variation_product_name(parent_product, combination), tax_class=parent_product.tax_class, sales_unit=parent_product.sales_unit, sku=sku, shipping_mode=parent_product.shipping_mode, type=parent_product.type, manufacturer=parent_product.manufacturer, height=parent_product.height, depth=parent_product.depth, net_weight=parent_product.net_weight, gross_weight=parent_product.gross_weight, ) variation_child.full_clean() variation_child.save() variation_child.link_to_parent(parent_product, combination_hash=combination_hash) return variation_child
def _get_price_info(shop, product=None, quantity=2): if not product: product = Product(sku="6.0745") # SKU of product defines the price :) price = shop.create_price(product.sku) return PriceInfo(quantity * price, quantity * 4 * price, quantity)
def _get_basket_line(request): basket = BaseBasket(request) return _create_line(basket, Product(sku="6.0745"))
def _get_source_line(request): source = OrderSource(request.shop) return _create_line(source, Product(sku="6.0745"))
def delete_variation(self, shop: Shop, supplier: Optional[Supplier], parent_product: Product, variation: Product): variation.soft_delete() ProductVariationResult.objects.filter( product=parent_product, result=variation).update( status=ProductVariationLinkStatus.INVISIBLE)
def recover_deleted_product(parent_product: Product, shop: Shop, deleted_product: Product, combination: Combination, combination_hash: str) -> Product: deleted_product.name = get_variation_product_name(parent_product, combination) deleted_product.tax_class = parent_product.tax_class deleted_product.sales_unit = parent_product.sales_unit deleted_product.shipping_mode = parent_product.shipping_mode deleted_product.type = parent_product.type deleted_product.manufacturer = parent_product.manufacturer deleted_product.height = parent_product.height deleted_product.depth = parent_product.depth deleted_product.net_weight = parent_product.net_weight deleted_product.gross_weight = parent_product.gross_weight deleted_product.deleted = False deleted_product.save() deleted_product.link_to_parent(parent_product, combination_hash=combination_hash) return deleted_product