def update_shipping(order, shipping, contact, cart): """ In-place update of shipping details for this order""" # Set a default for when no shipping module is used order.shipping_cost = Decimal("0.00") # Save the shipping info shipper = shipping_method_by_key(shipping) shipper.calculate(cart, contact) order.shipping_description = shipper.description() order.shipping_method = shipper.method() order.shipping_cost = convert_to_currency( shipper.cost(), order.currency.iso_4217_code ) order.shipping_model = shipping if hasattr(shipper, "carrier"): shipping_details = shipper.carrier else: shipping_details = shipper # If shipping details available, set the details for the order order.shipping_signed_for = getattr(shipping_details, "signed_for", False) order.shipping_tracked = getattr(shipping_details, "tracked", False) order.shipping_postage_speed = getattr(shipping_details, "postage_speed", STANDARD) order.estimated_delivery_min_days = getattr( shipping_details, "estimated_delivery_min_days", 1 ) order.estimated_delivery_expected_days = getattr( shipping_details, "estimated_delivery_expected_days", 7 ) order.estimated_delivery_max_days = getattr( shipping_details, "estimated_delivery_max_days", 25 )
def test_round_up_is_off(self): value = Decimal("0.33") currency_code = "GBP" currency = GBPCurrencyFactory() ExchangeRateFactory(rate=Decimal("1.11"), currency=currency) Setting.objects.create(group="CURRENCY", key="ROUND_UP", value=False) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("0.37"))
def test_buffer_is_no_added_when_set_to_ignore(self): value = Decimal("1.00") currency_code = "GBP" Setting.objects.create(group="CURRENCY", key="BUFFER", value=Decimal("0.10")) test_value = convert_to_currency(value, currency_code, ignore_buffer=True) self.assertEqual(test_value, Decimal("1.00"))
def test_round_up_is_honoured__whole_number(self): value = Decimal("0.50") currency_code = "GBP" currency = GBPCurrencyFactory() ExchangeRateFactory(rate=Decimal("1.11"), currency=currency) Setting.objects.create(group="CURRENCY", key="ROUND_UP", value=True) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("1.00"))
def test_exchange_rate_doesnt_exist(self): """If the exchange rate doesn't exist, the value shouldn't change """ value = Decimal("1.00") currency_code = "GBP" Setting.objects.create(group="CURRENCY", key="BUFFER", value=Decimal("0.00")) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, value)
def test_values_are_quantized(self): value = Decimal("1.00") currency_code = "GBP" currency = GBPCurrencyFactory() ExchangeRateFactory(rate=Decimal("0.750123"), currency=currency) Setting.objects.create(group="CURRENCY", key="BUFFER", value=Decimal("0.00")) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("0.75"))
def test_buffer_is_not_added_when_value_is_zero(self): value = Decimal("0.00") currency_code = "GBP" Setting.objects.create(group="CURRENCY", key="BUFFER", value=Decimal("0.10")) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("0.00"))
def test_psychological_pricing_is_off(self): value = Decimal("1.00") currency_code = "EUR" Setting.objects.create( group="CURRENCY", key="PSYCHOLOGICAL_PRICING", value=False ) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("1.00"))
def test_psychological_pricing_is_off(self): value = Decimal("1.00") currency_code = "EUR" Setting.objects.create(group="CURRENCY", key="PSYCHOLOGICAL_PRICING", value=False) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("1.00"))
def test_psychological_pricing__not_whole_currency(self): value = Decimal("0.75") currency_code = "GBP" currency = GBPCurrencyFactory() ExchangeRateFactory(rate=Decimal("2.00"), currency=currency) Setting.objects.create(group="CURRENCY", key="PSYCHOLOGICAL_PRICING", value=True) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("1.50"))
def test_exchange_rate_exists(self): """If the exchange rate doesn't exist, the value shouldn't change """ value = Decimal("1.00") currency_code = "GBP" currency = GBPCurrencyFactory() ExchangeRateFactory(rate=Decimal("0.75"), currency=currency) Setting.objects.create(group="CURRENCY", key="BUFFER", value=Decimal("0.00")) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("0.75"))
def test_psychological_pricing__not_whole_currency(self): value = Decimal("0.75") currency_code = "GBP" currency = GBPCurrencyFactory() ExchangeRateFactory(rate=Decimal("2.00"), currency=currency) Setting.objects.create( group="CURRENCY", key="PSYCHOLOGICAL_PRICING", value=True ) test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, Decimal("1.50"))
def full_product(context, product): """ Renders a product in a way that is useful for the product detail page """ context["product"] = product current_currency = currency_for_request(context.get("request")) context["other_prices"] = [ money_format( convert_to_currency(product.unit_price, currency.iso_4217_code), currency.iso_4217_code, ) for currency in Currency.objects.filter(accepted=True).exclude( iso_4217_code=current_currency).order_by("iso_4217_code") ] return context
def discount_saved(context, product, discount): """Returns the amount saved by the discount""" request = context.get("request") currency = currency_for_request(request) if discount and discount.valid_for_product(product): unit_price = convert_to_currency(product.unit_price, currency) discounted = calc_by_percentage(unit_price, discount.percentage) saved = unit_price - discounted cents = Decimal("0.01") price = saved.quantize(cents) else: price = Decimal("0.00") return money_format(price, currency)
def discount_price(context, product, discount): """Returns the product price with the discount applied. Ex: {% discount_price product sale %} """ request = context.get("request") currency = currency_for_request(request) unit_price = convert_to_currency(product.unit_price, currency) if discount and discount.valid_for_product(product): price = calc_by_percentage(unit_price, discount.percentage) else: price = unit_price return money_format(price, currency)
def currency(context, value, currency_code=None): """Convert a value to a money formatted string. If a currency is not supplied, one will selected based on the request. Usage: {% currency val %} """ if value == "" or value is None: return value if currency_code is None: request = context.get("request") currency_code = currency_for_request(request) value = convert_to_currency(value, currency_code) return mark_safe(money_format(value, currency_code))
def test_given_currency_doesnt_exist(self): value = Decimal("1.00") currency_code = "BTC" test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, value)
def test_currency_is_primary(self): value = Decimal("1.00") currency_code = "EUR" test_value = convert_to_currency(value, currency_code) self.assertEqual(test_value, value)
def test_zero_value(self): currency_code = "EUR" test_value = convert_to_currency(Decimal("0.00"), currency_code) self.assertEqual(test_value, Decimal("0.00"))
def display(request, cart=None, error_message="", default_view_tax=NOTSET): """Display the items in the cart.""" if default_view_tax == NOTSET: default_view_tax = config_value("TAX", "DEFAULT_VIEW_TAX") if not cart: cart = Cart.objects.from_request(request) if cart.numItems > 0: products = [item.product for item in cart.cartitem_set.all()] sale = find_best_auto_discount(products) else: sale = None # Try to get the user's country from the session country_iso2_code = request.GET.get( "side-cart-country", request.session.get("shipping_country", None) ) try: country = Country.objects.get(iso2_code=country_iso2_code) except Country.DoesNotExist: country = None # Try to get the user's country from their contact (if they have one) try: contact = Contact.objects.from_request(request) if country is None: country = contact.shipping_address.country else: # We have a country set from the form, so this is a cheap # and nasty way to get a dummy contact instead... raise Contact.DoesNotExist except (AttributeError, IndexError, Contact.DoesNotExist): pass # Try to look up the user's country from their IP address if country is None and hasattr(settings, "GEOIP_PATH"): ip = get_real_ip(request) if ip: geoip = GeoIP2() try: ip_country = geoip.country(ip) except AddressNotFoundError: pass else: try: country = Country.objects.get( active=True, iso2_code=ip_country["country_code"] ) except Country.DoesNotExist: country = None # If the user doesn't have a contact, make a dummy contact so # we can work out the shipping if country is None: config = Config.objects.get_current() try: country = config.country except Country.DoesNotExist: country = None class DummyContact: if country: shipping_address = AddressBook(country_id=country.id) contact = DummyContact() if country: request.session["shipping_country"] = country.iso2_code # Calculate the shipping cost try: __, shipping_dict = _get_shipping_choices(request, {}, cart, contact) cheapest_shipping = min(shipping_dict.values()) except (AttributeError, ValueError): cheapest_shipping = Decimal("0.00") try: cheapest_shipping = convert_to_currency( cheapest_shipping, cart.currency.iso_4217_code ) except Currency.DoesNotExist: pass try: display_shipping = money_format(cheapest_shipping, cart.currency.iso_4217_code) except Currency.DoesNotExist: display_shipping = cheapest_shipping try: display_total = money_format( cart.total + cheapest_shipping, cart.currency.iso_4217_code ) except Currency.DoesNotExist: display_total = cart.total + cheapest_shipping context = { "cart": cart, "error_message": error_message, "default_view_tax": default_view_tax, "sale": sale, "cheapest_shipping": display_shipping, "display_total": display_total, "country": country, } return render(request, "base_cart.html", context)
def display(request, cart=None, error_message="", default_view_tax=NOTSET): """Display the items in the cart.""" if default_view_tax == NOTSET: default_view_tax = config_value("TAX", "DEFAULT_VIEW_TAX") if not cart: cart = Cart.objects.from_request(request) if cart.numItems > 0: products = [item.product for item in cart.cartitem_set.all()] sale = find_best_auto_discount(products) else: sale = None # Try to get the user's country from the session country_iso2_code = request.GET.get( "side-cart-country", request.session.get("shipping_country", None)) try: country = Country.objects.get(iso2_code=country_iso2_code) except Country.DoesNotExist: country = None # Try to get the user's country from their contact (if they have one) try: contact = Contact.objects.from_request(request) if country is None: country = contact.shipping_address.country else: # We have a country set from the form, so this is a cheap # and nasty way to get a dummy contact instead... raise Contact.DoesNotExist except (AttributeError, IndexError, Contact.DoesNotExist): pass # Try to look up the user's country from their IP address if country is None and hasattr(settings, "GEOIP_PATH"): ip = get_real_ip(request) if ip: geoip = GeoIP2() try: ip_country = geoip.country(ip) except AddressNotFoundError: pass else: try: country = Country.objects.get( active=True, iso2_code=ip_country["country_code"]) except Country.DoesNotExist: country = None # If the user doesn't have a contact, make a dummy contact so # we can work out the shipping if country is None: config = Config.objects.get_current() try: country = config.country except Country.DoesNotExist: country = None class DummyContact: if country: shipping_address = AddressBook(country_id=country.id) contact = DummyContact() if country: request.session["shipping_country"] = country.iso2_code # Calculate the shipping cost try: __, shipping_dict = _get_shipping_choices(request, {}, cart, contact) cheapest_shipping = min(shipping_dict.values()) except (AttributeError, ValueError): cheapest_shipping = Decimal("0.00") try: cheapest_shipping = convert_to_currency(cheapest_shipping, cart.currency.iso_4217_code) except Currency.DoesNotExist: pass try: display_shipping = money_format(cheapest_shipping, cart.currency.iso_4217_code) except Currency.DoesNotExist: display_shipping = cheapest_shipping try: display_total = money_format(cart.total + cheapest_shipping, cart.currency.iso_4217_code) except Currency.DoesNotExist: display_total = cart.total + cheapest_shipping context = { "cart": cart, "error_message": error_message, "default_view_tax": default_view_tax, "sale": sale, "cheapest_shipping": display_shipping, "display_total": display_total, "country": country, } return render(request, "base_cart.html", context)
def get_product( request, category_slug, brand_slug, product_slug, selected_options=(), include_tax=NOTSET, default_view_tax=NOTSET, ): """Basic product view""" try: product = Product.objects.get_by_site(active=True, slug=product_slug) except Product.DoesNotExist: return bad_or_missing( request, _("The product you have requested does not exist.") ) try: category = Category.objects.get(slug=category_slug) except Category.DoesNotExist: category = None brand = Brand.objects.get(slug=brand_slug) if default_view_tax == NOTSET: default_view_tax = config_value("TAX", "DEFAULT_VIEW_TAX") if default_view_tax: include_tax = True elif include_tax == NOTSET: include_tax = default_view_tax if default_view_tax: include_tax = True subtype_names = product.get_subtypes() if "ProductVariation" in subtype_names: selected_options = product.productvariation.unique_option_ids # Display the ConfigurableProduct that this ProductVariation belongs to. product = product.productvariation.parent.product subtype_names = product.get_subtypes() best_discount = find_best_auto_discount(product) context = { "product": product, "category": category, "brand": brand, "default_view_tax": default_view_tax, "sale": best_discount, } # Get the template context from the Product. context = product.add_template_context( context=context, request=request, selected_options=selected_options, include_tax=include_tax, default_view_tax=default_view_tax, ) if include_tax: tax_amt = get_tax(request.user, product, 1) context["product_tax"] = tax_amt context["price_with_tax"] = product.unit_price + tax_amt price = context["price_with_tax"] else: price = product.unit_price context["all_prices"] = [ { "currency": currency.iso_4217_code, "price": convert_to_currency(price, currency.iso_4217_code), } for currency in Currency.objects.filter(accepted=True) ] template = find_product_template(product, producttypes=subtype_names) return TemplateResponse(request, template, context)
def get_product( request, category_slug, brand_slug, product_slug, selected_options=(), include_tax=NOTSET, default_view_tax=NOTSET, ): """Basic product view""" try: product = Product.objects.active().get(slug=product_slug) except Product.DoesNotExist: return bad_or_missing( request, _("The product you have requested does not exist.")) try: category = Category.objects.get(slug=category_slug) except Category.DoesNotExist: category = None brand = Brand.objects.get(slug=brand_slug) if default_view_tax == NOTSET: default_view_tax = config_value("TAX", "DEFAULT_VIEW_TAX") if default_view_tax: include_tax = True elif include_tax == NOTSET: include_tax = default_view_tax if default_view_tax: include_tax = True subtype_names = product.get_subtypes() if "ProductVariation" in subtype_names: selected_options = product.productvariation.unique_option_ids # Display the ConfigurableProduct that this ProductVariation belongs to. product = product.productvariation.parent.product subtype_names = product.get_subtypes() best_discount = find_best_auto_discount(product) context = { "product": product, "category": category, "brand": brand, "default_view_tax": default_view_tax, "sale": best_discount, } # Get the template context from the Product. context = product.add_template_context( context=context, request=request, selected_options=selected_options, include_tax=include_tax, default_view_tax=default_view_tax, ) if include_tax: tax_amt = get_tax(request.user, product, 1) context["product_tax"] = tax_amt context["price_with_tax"] = product.unit_price + tax_amt price = context["price_with_tax"] else: price = product.unit_price context["all_prices"] = [{ "currency": currency.iso_4217_code, "price": convert_to_currency(price, currency.iso_4217_code), } for currency in Currency.objects.filter(accepted=True)] template = find_product_template(product, producttypes=subtype_names) return TemplateResponse(request, template, context)