def get_price_detail(request, product_slug): """Get all price details for a product, returning the response encoded as JSON.""" results = {"success": False, "message": _("not available")} price = None if request.method == "POST": reqdata = request.POST else: reqdata = request.GET try: product = Product.objects.get_by_site(active=True, slug=product_slug) found = True if "quantity" in reqdata: quantity = int(reqdata["quantity"]) else: quantity = 1 if "ConfigurableProduct" in product.get_subtypes(): cp = product.configurableproduct chosen_options = optionids_from_post(cp, reqdata) product = cp.get_product_from_options(chosen_options) if product: price = product.get_qty_price(quantity) base_tax = get_tax(request.user, product, quantity) price_with_tax = price + base_tax currency_code = currency_for_request(request) results["slug"] = product.slug results["currency_price"] = money_format(price, currency_code) results["price"] = float(price) results["tax"] = float(base_tax) results["currency_tax"] = money_format(base_tax, currency_code) results["currency_price_with_tax"] = money_format( price_with_tax, currency_code ) results["price_with_tax"] = float(price_with_tax) results["success"] = True results["message"] = "" except Product.DoesNotExist: found = False data = json_encode(results) if found: return http.HttpResponse(data, mimetype="text/javascript") else: return http.HttpResponseNotFound(data, mimetype="text/javascript")
def get_price_detail(request, product_slug): """Get all price details for a product, returning the response encoded as JSON.""" results = {"success": False, "message": _("not available")} price = None if request.method == "POST": reqdata = request.POST else: reqdata = request.GET try: product = Product.objects.active().get(slug=product_slug) found = True if "quantity" in reqdata: quantity = int(reqdata["quantity"]) else: quantity = 1 if "ConfigurableProduct" in product.get_subtypes(): cp = product.configurableproduct chosen_options = optionids_from_post(cp, reqdata) product = cp.get_product_from_options(chosen_options) if product: price = product.get_qty_price(quantity) base_tax = get_tax(request.user, product, quantity) price_with_tax = price + base_tax currency_code = currency_for_request(request) results["slug"] = product.slug results["currency_price"] = money_format(price, currency_code) results["price"] = float(price) results["tax"] = float(base_tax) results["currency_tax"] = money_format(base_tax, currency_code) results["currency_price_with_tax"] = money_format( price_with_tax, currency_code) results["price_with_tax"] = float(price_with_tax) results["success"] = True results["message"] = "" except Product.DoesNotExist: found = False data = json_encode(results) if found: return http.HttpResponse(data, mimetype="text/javascript") else: return http.HttpResponseNotFound(data, mimetype="text/javascript")
def test_currency__does_not_exist(self): value = Decimal("1.00") currency_code = "BTC" self.assertEqual( force_str(money_format(value, currency_code)), "BTC is not accepted" )
def render(self, context): try: show_tax = self.show_tax.resolve(context) except template.VariableDoesNotExist: show_tax = self.raw_tax if show_tax: tag = CartitemLineTaxedTotalNode(self.raw_cartitem, self.raw_currency) return tag.render(context) try: cartitem = self.cartitem.resolve(context) except template.VariableDoesNotExist: log.warn("Could not resolve template variable: %s", self.cartitem) return "" try: show_currency = self.show_currency.resolve(context) except template.VariableDoesNotExist: show_currency = self.raw_currency if show_currency: request = context.get("request") currency_code = currency_for_request(request) return money_format(cartitem.line_total, currency_code) else: return cartitem.line_total
def apply_to_order(self, order): """Apply up to the full amount of the balance of this cert to the order. Returns new balance. """ amount = min(order.balance, self.balance) log.info( "applying %s from giftcert #%i [%s] to order #%i [%s]", money_format(amount, order.currency.iso_4217_code), self.id, money_format(self.balance, order.currency.iso_4217_code), order.id, money_format(order.balance, order.currency.iso_4217_code), ) config = config_get_group("PAYMENT_GIFTCERTIFICATE") orderpayment = record_payment(order, config, amount) return self.use(amount, orderpayment=orderpayment)
def _get_shipping_choices(request, paymentmodule, cart, contact, default_view_tax=False): """Iterate through legal shipping modules, building the list for display to the user. Returns the shipping choices list, along with a dictionary of shipping choices, useful for building javascript that operates on shipping choices. """ shipping_options = [] shipping_dict = {} if not cart.is_shippable: methods = [shipping_method_by_key("NoShipping")] else: methods = shipping_methods() valid_methods = [] for method in methods: method.calculate(cart, contact) if method.valid(): valid_methods.append((method, method.cost())) # sort methods by cost valid_methods.sort(key=lambda method_cost: int(method_cost[1])) for method, shipcost in valid_methods: template = lookup_template(paymentmodule, "shipping_options.html") t = loader.get_template(template) shipping_tax = None taxed_shipping_price = None if config_value("TAX", "TAX_SHIPPING"): shipping_tax = config_value("TAX", "TAX_CLASS") taxer = _get_taxprocessor(request) total = shipcost + taxer.by_price(shipping_tax, shipcost) currency_code = currency_for_request(request) taxed_shipping_price = money_format(total, currency_code) data = { "amount": shipcost, "description": method.description(), "method": method.method(), "expected_delivery": method.expectedDelivery(), "default_view_tax": default_view_tax, "shipping_tax": shipping_tax, "taxed_shipping_price": taxed_shipping_price, } if hasattr(method, "shipping_discount"): data["discount"] = method.shipping_discount() shipping_options.append((method.id, t.render(data))) shipping_dict[method.id] = shipcost return shipping_options, shipping_dict
def _get_shipping_choices( request, paymentmodule, cart, contact, default_view_tax=False ): """Iterate through legal shipping modules, building the list for display to the user. Returns the shipping choices list, along with a dictionary of shipping choices, useful for building javascript that operates on shipping choices. """ shipping_options = [] shipping_dict = {} if not cart.is_shippable: methods = [shipping_method_by_key("NoShipping")] else: methods = shipping_methods() valid_methods = [] for method in methods: method.calculate(cart, contact) if method.valid(): valid_methods.append((method, method.cost())) # sort methods by cost valid_methods.sort(key=lambda method_cost: int(method_cost[1])) for method, shipcost in valid_methods: template = lookup_template(paymentmodule, "shipping_options.html") t = loader.get_template(template) shipping_tax = None taxed_shipping_price = None if config_value("TAX", "TAX_SHIPPING"): shipping_tax = config_value("TAX", "TAX_CLASS") taxer = _get_taxprocessor(request) total = shipcost + taxer.by_price(shipping_tax, shipcost) currency_code = currency_for_request(request) taxed_shipping_price = money_format(total, currency_code) data = { "amount": shipcost, "description": method.description(), "method": method.method(), "expected_delivery": method.expectedDelivery(), "default_view_tax": default_view_tax, "shipping_tax": shipping_tax, "taxed_shipping_price": taxed_shipping_price, } if hasattr(method, "shipping_discount"): data["discount"] = method.shipping_discount() shipping_options.append((method.id, t.render(data))) shipping_dict[method.id] = shipcost return shipping_options, shipping_dict
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 get_price(request, product_slug): """Get base price for a product, returning the answer encoded as JSON.""" quantity = 1 try: product = Product.objects.get_by_site(active=True, slug=product_slug) except Product.DoesNotExist: return http.HttpResponseNotFound( json_encode(("", _("not available"))), mimetype="text/javascript" ) prod_slug = product.slug if request.method == "POST" and "quantity" in request.POST: quantity = int(request.POST["quantity"]) currency_code = currency_for_request(request) if "ConfigurableProduct" in product.get_subtypes(): cp = product.configurableproduct chosen_options = optionids_from_post(cp, request.POST) pvp = cp.get_product_from_options(chosen_options) if not pvp: return http.HttpResponse( json_encode(("", _("not available"))), mimetype="text/javascript" ) prod_slug = pvp.slug price = money_format(pvp.get_qty_price(quantity), currency_code) else: price = money_format(product.get_qty_price(quantity), currency_code) if not price: return http.HttpResponse( json_encode(("", _("not available"))), mimetype="text/javascript" ) return http.HttpResponse( json_encode((prod_slug, price)), mimetype="text/javascript" )
def order_lists(): """ Show all orders that are in status' that have display set to True """ status = [] primary_currency = Currency.objects.get_primary() for s in Status.objects.filter(display=True): value = 0 for order in s.orders(): value += order.total_in_primary_currency() if value: status.append((s, money_format(value, primary_currency.iso_4217_code))) return {"status": status}
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 render(self, context): taxer = _get_taxprocessor(context["request"]) try: item = template.resolve_variable(self.cartitem, context) except template.VariableDoesNotExist: raise template.TemplateSyntaxError("No such variable: %s", self.cartitem) total = item.line_total + taxer.by_price(item.product.taxClass, item.line_total) if self.currency: request = context.get("request") currency_code = currency_for_request(request) return money_format(total, currency_code) return total
def isValid(self, cart=None, request=None): """ Make sure this discount still has available uses and is in the current date range. If a cart has been populated, validate that it does apply to the products we have selected. """ if not self.active: return (False, ugettext("This coupon is disabled.")) if self.startDate > datetime.date.today(): return (False, ugettext("This coupon is not active yet.")) if self.endDate < datetime.date.today(): return (False, ugettext("This coupon has expired.")) if self.numUses > self.allowedUses: return ( False, ugettext( "This discount has exceeded the number of allowed uses."), ) if not cart: return (True, ugettext("Valid.")) minOrder = self.minOrder or 0 if cart.total < minOrder: currency_code = currency_for_request(request) return ( False, ugettext( "This discount only applies to orders of at least %s." % money_format(minOrder, currency_code)), ) validItems = False validproducts = self._get_valid_product_dict() if validproducts: for cart_item in cart.cartitem_set.all(): if cart_item.product.id in validproducts: validItems = True break # Once we have 1 valid item, we exit else: validItems = True if validItems: return (True, ugettext("Valid.")) else: return ( False, ugettext( "This discount cannot be applied to the products in your cart." ), )
def isValid(self, cart=None, request=None): """ Make sure this discount still has available uses and is in the current date range. If a cart has been populated, validate that it does apply to the products we have selected. """ if not self.active: return (False, ugettext("This coupon is disabled.")) if self.startDate > datetime.date.today(): return (False, ugettext("This coupon is not active yet.")) if self.endDate < datetime.date.today(): return (False, ugettext("This coupon has expired.")) if self.numUses > self.allowedUses: return ( False, ugettext("This discount has exceeded the number of allowed uses."), ) if not cart: return (True, ugettext("Valid.")) minOrder = self.minOrder or 0 if cart.total < minOrder: currency_code = currency_for_request(request) return ( False, ugettext( "This discount only applies to orders of at least %s." % money_format(minOrder, currency_code) ), ) validItems = False validproducts = self._get_valid_product_dict() if validproducts: for cart_item in cart.cartitem_set.all(): if cart_item.product.id in validproducts: validItems = True break # Once we have 1 valid item, we exit else: validItems = True if validItems: return (True, ugettext("Valid.")) else: return ( False, ugettext( "This discount cannot be applied to the products in your cart." ), )
def order_lists(): """ Show all orders that are in status' that have display set to True """ status = [] primary_currency = Currency.objects.get_primary() for s in Status.objects.filter(display=True): value = 0 for order in s.orders(): value += order.total_in_primary_currency() if value: status.append( (s, money_format(value, primary_currency.iso_4217_code))) return {"status": status}
def get_price(request, product_slug): """Get base price for a product, returning the answer encoded as JSON.""" quantity = 1 try: product = Product.objects.active().get(slug=product_slug) except Product.DoesNotExist: return http.HttpResponseNotFound(json_encode(("", _("not available"))), mimetype="text/javascript") prod_slug = product.slug if request.method == "POST" and "quantity" in request.POST: quantity = int(request.POST["quantity"]) currency_code = currency_for_request(request) if "ConfigurableProduct" in product.get_subtypes(): cp = product.configurableproduct chosen_options = optionids_from_post(cp, request.POST) pvp = cp.get_product_from_options(chosen_options) if not pvp: return http.HttpResponse(json_encode(("", _("not available"))), mimetype="text/javascript") prod_slug = pvp.slug price = money_format(pvp.get_qty_price(quantity), currency_code) else: price = money_format(product.get_qty_price(quantity), currency_code) if not price: return http.HttpResponse(json_encode(("", _("not available"))), mimetype="text/javascript") return http.HttpResponse(json_encode((prod_slug, price)), mimetype="text/javascript")
def render(self, context): taxer = _get_taxprocessor(context["request"]) try: price = template.resolve_variable(self.price, context) except template.VariableDoesNotExist: raise template.TemplateSyntaxError("No such variable: %s", self.price) total = price + taxer.by_price(self.taxclass, price) if self.currency: request = context.get("request") currency_code = currency_for_request(request) return money_format(total, currency_code) return total
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 option_total_price(context, product, option_item): """ Returns the price as (+$1.00) or (-$1.00) depending on the sign of the price change The currency format is base upon locale """ if option_item.price_change: val = product.unit_price + option_item.price_change else: val = product.unit_price request = context.get("request") currency_code = currency_for_request(request) return money_format(val, currency_code)
def option_price(context, option_item): """ Returns the price as (+$1.00) or (-$1.00) depending on the sign of the price change The currency format is base upon locale """ output = "" if option_item.price_change != 0: request = context.get("request") currency_code = currency_for_request(request) amount = money_format(abs(option_item.price_change), currency_code) if option_item.price_change < 0: output = "(- %s)" % amount elif option_item.price_change > 0: output = "(+ %s)" % amount return output
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 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 __str__(self): sb = money_format(self.start_balance, self.order.iso_4217_code) b = money_format(self.balance, self.order.iso_4217_code) return "Gift Cert: %s/%s" % (sb, b)
def productvariation_details(product, include_tax, user, request, create=False): """Build the product variation details, for conversion to javascript. Returns variation detail dictionary built like so: details = { "OPTION_KEY" : { "SLUG": "Variation Slug", "PRICE" : {"qty" : "$price", [...]}, "TAXED" : "$taxed price", # omitted if no taxed price requested "QTY" : 1 }, [...] } """ config = Config.objects.get_current() ignore_stock = config.no_stock_checkout if include_tax: taxer = get_taxprocessor(user) tax_class = product.taxClass details = {} variations = ProductPriceLookup.objects.filter(parentid=product.id) if variations.count() == 0: if create: log.debug("Creating price lookup for %s", product) ProductPriceLookup.objects.smart_create_for_product(product) variations = ProductPriceLookup.objects.filter(parentid=product.id) else: log.warning( "You must run satchmo_rebuild_pricing and add it to a cron-job to run every day, or else the product details will not work for product detail pages." ) for detl in variations: key = detl.key if key in details: detail = details[key] else: detail = {} detail["SLUG"] = detl.productslug if not detl.active: qty = -1 elif ignore_stock: qty = 10000 else: qty = detl.items_in_stock detail["QTY"] = qty detail["PRICE"] = {} if include_tax: detail["TAXED"] = {} details[key] = detail price = detl.dynamic_price currency_code = currency_for_request(request) detail["PRICE"][detl.quantity] = money_format(price, currency_code) if include_tax: tax_price = taxer.by_price(tax_class, price) + price detail["TAXED"][detl.quantity] = money_format( tax_price, currency_code) return details
def test_currency(self): EURCurrencyFactory() value = Decimal("1.00") currency_code = "EUR" self.assertEqual(money_format(value, currency_code), "€1.00 (EUR)")
def test_currency__does_not_exist(self): value = Decimal("1.00") currency_code = "BTC" self.assertEqual(force_str(money_format(value, currency_code)), "BTC is not accepted")
def test_value_is_none(self): EURCurrencyFactory() value = None currency_code = "EUR" self.assertEqual(money_format(value, currency_code), "€0.00 (EUR)")
def productvariation_details(product, include_tax, user, request, create=False): """Build the product variation details, for conversion to javascript. Returns variation detail dictionary built like so: details = { "OPTION_KEY" : { "SLUG": "Variation Slug", "PRICE" : {"qty" : "$price", [...]}, "TAXED" : "$taxed price", # omitted if no taxed price requested "QTY" : 1 }, [...] } """ config = Config.objects.get_current() ignore_stock = config.no_stock_checkout if include_tax: taxer = get_taxprocessor(user) tax_class = product.taxClass details = {} variations = ProductPriceLookup.objects.filter(parentid=product.id) if variations.count() == 0: if create: log.debug("Creating price lookup for %s", product) ProductPriceLookup.objects.smart_create_for_product(product) variations = ProductPriceLookup.objects.filter(parentid=product.id) else: log.warning( "You must run satchmo_rebuild_pricing and add it to a cron-job to run every day, or else the product details will not work for product detail pages." ) for detl in variations: key = detl.key if key in details: detail = details[key] else: detail = {} detail["SLUG"] = detl.productslug if not detl.active: qty = -1 elif ignore_stock: qty = 10000 else: qty = detl.items_in_stock detail["QTY"] = qty detail["PRICE"] = {} if include_tax: detail["TAXED"] = {} details[key] = detail price = detl.dynamic_price currency_code = currency_for_request(request) detail["PRICE"][detl.quantity] = money_format(price, currency_code) if include_tax: tax_price = taxer.by_price(tax_class, price) + price detail["TAXED"][detl.quantity] = money_format(tax_price, currency_code) return details
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)