def test_elements(self): p1 = ((self.ten_btc + self.twenty_btc) * 5).quantize('0.01') self.assertEqual( list(p1.elements()), [self.ten_btc, self.twenty_btc, 5, decimal.Decimal('0.01')]) p2 = Price(3, history=History(1, operator.__add__, 2)) self.assertEqual(list(p2.elements()), [1, 2])
def get_fixed_discount_for(self, amount): if self.discount_value_type == self.DISCOUNT_VALUE_FIXED: discount_price = Price(net=self.discount_value, currency=settings.DEFAULT_CURRENCY) discount = FixedDiscount(amount=discount_price, name=smart_text(self)) elif self.discount_value_type == self.DISCOUNT_VALUE_PERCENTAGE: discount = percentage_discount(value=self.discount_value, name=smart_text(self)) fixed_discount_value = amount - discount.apply(amount) discount = FixedDiscount(amount=fixed_discount_value, name=smart_text(self)) else: raise NotImplementedError('Unknown discount value type') if discount.amount > amount: return FixedDiscount(amount, name=smart_text(self)) else: return discount
def test_shipping_voucher_checkout_discountnot_applicable(settings, is_shipping_required, shipping_method, discount_value, discount_type, apply_to, limit, error_msg): settings.DEFAULT_CURRENCY = 'USD' checkout = Mock(is_shipping_required=is_shipping_required, shipping_method=shipping_method) voucher = Voucher( code='unique', type=Voucher.SHIPPING_TYPE, discount_value_type=discount_type, discount_value=discount_value, limit=Price(limit, currency='USD') if limit is not None else None, apply_to=apply_to) with pytest.raises(NotApplicable) as e: voucher.get_discount_for_checkout(checkout) assert str(e.value) == error_msg
def test_comparison(): price1 = Price(Amount(10, 'EUR'), Amount(15, 'EUR')) price2 = Price(Amount(30, 'EUR'), Amount(45, 'EUR')) price_range1 = PriceRange(price1, price2) price3 = Price(Amount(40, 'EUR'), Amount(60, 'EUR')) price4 = Price(Amount(80, 'EUR'), Amount(120, 'EUR')) price_range2 = PriceRange(price3, price4) assert price_range1 == PriceRange(price1, price2) assert price_range1 != price_range2 assert price_range1 != PriceRange(price1, price1) assert price_range1 != PriceRange( Price(Amount(10, 'USD'), Amount(15, 'USD')), Price(Amount(30, 'USD'), Amount(45, 'USD'))) assert price_range1 != price1
def order_details(request, order_pk): qs = (Order.objects.select_related('user', 'shipping_address', 'billing_address').prefetch_related( 'notes', 'payments', 'history', 'groups__lines')) order = get_object_or_404(qs, pk=order_pk) notes = order.notes.all() all_payments = order.payments.exclude(status=PaymentStatus.INPUT) payment = order.payments.last() groups = list(order) captured = preauthorized = Price(0, currency=order.total.currency) balance = captured - order.total if payment: can_capture = (payment.status == PaymentStatus.PREAUTH and any([ group.status != GroupStatus.CANCELLED for group in order.groups.all() ])) can_release = payment.status == PaymentStatus.PREAUTH can_refund = payment.status == PaymentStatus.CONFIRMED preauthorized = payment.get_total_price() if payment.status == PaymentStatus.CONFIRMED: captured = payment.get_captured_price() balance = captured - order.total else: can_capture = can_release = can_refund = False is_many_stock_locations = StockLocation.objects.count() > 1 ctx = { 'order': order, 'all_payments': all_payments, 'payment': payment, 'notes': notes, 'groups': groups, 'captured': captured, 'preauthorized': preauthorized, 'can_capture': can_capture, 'can_release': can_release, 'can_refund': can_refund, 'is_many_stock_locations': is_many_stock_locations, 'balance': balance } return TemplateResponse(request, 'dashboard/order/detail.html', ctx)
def test_sale_applies_to_correct_products(): product = Product.objects.create(name='Test Product', price=10, weight=1, description='', pk=10) variant = ProductVariant.objects.create(product=product, sku='firstvar') product2 = Product.objects.create(name='Second product', price=15, weight=1, description='') sec_variant = ProductVariant.objects.create(product=product2, sku='secvar', pk=10) sale = Sale.objects.create(name='Test sale', value=5, type=Sale.FIXED) sale.products.add(product) assert product2 not in sale.products.all() assert sale.modifier_for_variant(variant).amount == Price(net=5, currency='USD') with pytest.raises(NotApplicable): sale.modifier_for_variant(sec_variant)
def test_addition(): price1 = Price(Amount(10, 'EUR'), Amount(15, 'EUR')) price2 = Price(Amount(30, 'EUR'), Amount(45, 'EUR')) price_range1 = PriceRange(price1, price2) price3 = Price(Amount(40, 'EUR'), Amount(60, 'EUR')) price4 = Price(Amount(80, 'EUR'), Amount(120, 'EUR')) price_range2 = PriceRange(price3, price4) result = price_range1 + price_range2 assert result.min_price == price1 + price3 assert result.max_price == price2 + price4 result = price_range1 + price3 assert result.min_price == price1 + price3 assert result.max_price == price2 + price3 with pytest.raises(ValueError): price_range1 + PriceRange(Price(Amount(1, 'BTC'), Amount(1, 'BTC')), Price(Amount(2, 'BTC'), Amount(2, 'BTC'))) with pytest.raises(ValueError): price_range1 + Price(Amount(1, 'BTC'), Amount(1, 'BTC')) with pytest.raises(TypeError): price_range1 + 1
def get_tax_country_code(code, price: Union[Price, float]) -> Tuple[Price, float]: rate = RATES.get(code, None) if not rate: rate = RATES.get(settings.DEFAULT_TAX_RATE_COUNTRY, settings.FALLBACK_TAX_RATE) if isinstance(rate, CountryTax): rate = rate.rate if type(price) is Price: gross = price.gross currency = price.currency else: gross = price currency = settings.DEFAULT_CURRENCY taxed = _get_taxed(rate, gross) p = Price(gross=Decimal(taxed), net=gross, currency=currency) return p, rate
def test_subtraction(): price1 = Price(Amount(10, 'EUR'), Amount(15, 'EUR')) price2 = Price(Amount(30, 'EUR'), Amount(45, 'EUR')) price_range1 = PriceRange(price1, price2) price3 = Price(Amount(40, 'EUR'), Amount(60, 'EUR')) price4 = Price(Amount(80, 'EUR'), Amount(120, 'EUR')) price_range2 = PriceRange(price3, price4) result = price_range2 - price_range1 assert result.min_price == price3 - price1 assert result.max_price == price4 - price2 result = price_range2 - price1 assert result.min_price == price3 - price1 assert result.max_price == price4 - price1 with pytest.raises(ValueError): price_range2 - PriceRange(Price(Amount(1, 'BTC'), Amount(1, 'BTC')), Price(Amount(2, 'BTC'), Amount(2, 'BTC'))) with pytest.raises(ValueError): price_range2 - Price(Amount(1, 'BTC'), Amount(1, 'BTC')) with pytest.raises(TypeError): price_range2 - 1
def order_details(request, order_pk): qs = (Order.objects.select_related('user', 'shipping_address', 'billing_address').prefetch_related( 'notes', 'payments', 'history', 'groups', 'groups__items')) order = get_object_or_404(qs, pk=order_pk) notes = order.notes.all() all_payments = order.payments.all() payment = order.payments.last() groups = list(order) captured = preauthorized = Price(0, currency=order.get_total().currency) balance = captured - order.get_total() if payment: can_capture = (payment.status == 'preauth' and order.status != 'cancelled') can_release = payment.status == 'preauth' can_refund = payment.status == 'confirmed' preauthorized = payment.get_total_price() if payment.status == 'confirmed': captured = payment.get_captured_price() balance = captured - order.get_total() else: can_capture = can_release = can_refund = False ctx = { 'order': order, 'all_payments': all_payments, 'payment': payment, 'notes': notes, 'groups': groups, 'captured': captured, 'preauthorized': preauthorized, 'can_capture': can_capture, 'can_release': can_release, 'can_refund': can_refund, 'balance': balance } return TemplateResponse(request, 'dashboard/order/detail.html', ctx)
def test_undiscounted_variant_price_tag_without_asvar(self): token = mock.Mock() token.split_contents.return_value = ('variant_price', 'product', 'currency=PLN', 'discount=0') parser = mock.Mock() def side_effect(arg): return template.Variable(arg) parser.compile_filter = mock.Mock(side_effect=side_effect) node = product_prices.variant_price(parser, token) #testing simple expresion self.assertEqual(node.item.var, 'product') self.assertEqual(node.kwargs['currency'].var, 'PLN') self.assertEqual(node.kwargs['discount'].var, '0') context = {'product': MockProduct(), 'PLN': 'PLN', '0': 0} result = node.render(context) self.assertEqual( result, Price(net=5, gross=5 * decimal.Decimal('1.9'), currency=u'PLN'))
def test_variant_price_tag_with_asvar(self): token = mock.Mock() token.split_contents.return_value = ('variant_price', 'product', 'currency=PLN', 'as', 'price') parser = mock.Mock() def side_effect(arg): return template.Variable(arg) parser.compile_filter = mock.Mock(side_effect=side_effect) node = product_prices.variant_price(parser, token) self.assertEqual(node.item.var, 'product') self.assertEqual(node.kwargs['currency'].var, 'PLN') self.assertEqual(node.asvar, 'price') context = {'product': MockProduct(), 'PLN': 'PLN'} node.render(context) self.assertEqual( context['price'], Price(net=5 * decimal.Decimal('0.9'), gross=(5 * decimal.Decimal('1.9') * decimal.Decimal('0.9')), currency=u'PLN'))
def deliveries(self): """Return the cart split into delivery groups. Generates tuples consisting of a partition, its shipping cost and its total cost. Each partition is a list of tuples containing the cart line, its unit price and the line total. """ for partition in self.cart.partition(): if self.shipping_method and partition.is_shipping_required(): shipping_cost = self.shipping_method.get_total() else: shipping_cost = Price(0, currency=settings.DEFAULT_CURRENCY) total_with_shipping = partition.get_total( discounts=self.cart.discounts) + shipping_cost partition = [ (item, item.get_price_per_item(discounts=self.cart.discounts), item.get_total(discounts=self.cart.discounts)) for item in partition ] yield partition, shipping_cost, total_with_shipping
def order_details(request, pk): order = get_object_or_404(Order.objects.prefetch_related( 'notes', 'payments', 'history', 'groups'), pk=pk) notes = order.notes.all() payment = order.payments.last() groups = list(order) for group in groups: group.can_ship = (payment and payment.status == 'confirmed' and group.status == 'new') note = OrderNote(order=order, user=request.user) note_form = OrderNoteForm(request.POST or None, instance=note) if note_form.is_valid(): note_form.save() msg = _('Added note') order.create_history_entry(comment=msg, user=request.user) messages.success(request, msg) return redirect('dashboard:order-details', pk=pk) captured = preauthorized = Price(0, currency=order.get_total().currency) if payment: can_capture = (payment.status == 'preauth' and order.status != 'cancelled') can_release = payment.status == 'preauth' can_refund = payment.status == 'confirmed' preauthorized = payment.get_total_price() if payment.status == 'confirmed': captured = payment.get_captured_price() else: can_capture = can_release = can_refund = False ctx = {'order': order, 'payment': payment, 'notes': notes, 'groups': groups, 'note_form': note_form, 'captured': captured, 'preauthorized': preauthorized, 'can_capture': can_capture, 'can_release': can_release, 'can_refund': can_refund} return TemplateResponse(request, 'dashboard/order/detail.html', ctx)
def test_get_category_variants_and_prices_product_with_many_categories( cart, default_category, product_in_stock): # Test: don't duplicate percentage voucher # when product is in more than one category with discount category = Category.objects.create( name='Foobar', slug='foo', parent=default_category) product_in_stock.price = Decimal('10.00') product_in_stock.save() product_in_stock.categories.add(category) variant = product_in_stock.variants.first() cart.add(variant, check_quantity=False) discounted_products = list( get_category_variants_and_prices(cart, default_category)) assert len(discounted_products) == 1 voucher = Voucher.objects.create( category=default_category, type=Voucher.CATEGORY_TYPE, discount_value='10.0', code='foobar', discount_value_type=Voucher.DISCOUNT_VALUE_PERCENTAGE) checkout_mock = Mock(spec=Checkout, cart=cart) discount = voucher.get_discount_for_checkout(checkout_mock) # 10% of 10.00 is 1.00 assert discount.amount == Price('1.00', currency=discount.amount.currency)
def price(self): return Price(fake.pydecimal(2, 2, positive=True), currency=settings.DEFAULT_CURRENCY)
def get_cost_price(stock): zero_price = Price(0, 0, currency=settings.DEFAULT_CURRENCY) if not stock.cost_price: return zero_price return stock.cost_price
def get_captured_price(self): return Price(self.captured_amount, currency=self.currency)
def get_total_price(self): net = self.total - self.tax return Price(net, gross=self.total, currency=self.currency)
def get_price_per_item(self, **kwargs): return Price(net=self.unit_price_net, gross=self.unit_price_gross, currency=settings.DEFAULT_CURRENCY)
def get_total_shipping(self): costs = [group.shipping_price for group in self] if costs: return sum(costs[1:], costs[0]) return Price(net=0, currency=settings.DEFAULT_CURRENCY)
def get_subtotal_without_voucher(self): if self.get_items(): return super(Order, self).get_total() return Price(net=0, currency=settings.DEFAULT_CURRENCY)
def total(self, price): self.total_net = price self.total_tax = Price(price.tax, currency=price.currency)
def total(self): if self.total_net is not None: gross = self.total_net.net + self.total_tax.gross return Price(net=self.total_net.net, gross=gross, currency=settings.DEFAULT_CURRENCY)
def get_delivery_total(self): return sum([group.shipping_price for group in self.groups.all()], Price(0, currency=settings.DEFAULT_CURRENCY))
def get_total_shipping(self): zero = Price(0, currency=settings.DEFAULT_CURRENCY) cost_iterator = (shipping_cost for shipment, shipping_cost, total in self.deliveries) total = sum(cost_iterator, zero) return total
def test_total_with_discount(client, sale, request_cart, product_in_stock): sales = Sale.objects.all() variant = product_in_stock.variants.get() request_cart.add(variant, 1) line = request_cart.lines.first() assert line.get_total(discounts=sales) == Price(currency="USD", net=5)
def get_total(self): zero = Price(0, currency=settings.DEFAULT_CURRENCY) cost_iterator = (total for shipment, shipping_cost, total in self.deliveries) total = sum(cost_iterator, zero) return total if self.discount is None else self.discount.apply(total)
def get_subtotal_without_voucher(self): if self.get_lines(): return self.total return Price(net=0, currency=settings.DEFAULT_CURRENCY)
def get_price_per_item(self, **kwargs): gross = self.data['unit_price_gross'] net = self.data['unit_price_net'] return Price(net=net, gross=gross, currency=settings.DEFAULT_CURRENCY)
def create_order(self): """Create an order from the checkout session. Each order will get a private copy of both the billing and the shipping address (if shipping ). If any of the addresses is new and the user is logged in the address will also get saved to that user's address book. Current user's language is saved in the order so we can later determine which language to use when sending email. """ # FIXME: save locale along with the language voucher = self._get_voucher(vouchers=Voucher.objects.active( date=date.today()).select_for_update()) if self.voucher_code is not None and voucher is None: # Voucher expired in meantime, abort order placement return None if self.is_shipping_required: shipping_address = self._save_order_shipping_address() self._add_to_user_address_book(self.shipping_address, is_shipping=True) else: shipping_address = None billing_address = self._save_order_billing_address() self._add_to_user_address_book(self.billing_address, is_billing=True) shipping_price = (self.shipping_method.get_total() if self.shipping_method else Price( 0, currency=settings.DEFAULT_CURRENCY)) order_data = { 'language_code': get_language(), 'billing_address': billing_address, 'shipping_address': shipping_address, 'tracking_client_id': self.tracking_code, 'shipping_price': shipping_price, 'total': self.get_total() } if self.user.is_authenticated: order_data['user'] = self.user order_data['user_email'] = self.user.email else: order_data['user_email'] = self.email if voucher is not None: discount = self.discount order_data['voucher'] = voucher order_data['discount_amount'] = discount.amount order_data['discount_name'] = discount.name order = Order.objects.create(**order_data) for partition in self.cart.partition(): shipping_required = partition.is_shipping_required() shipping_method_name = (smart_text(self.shipping_method) if shipping_required else None) group = order.groups.create( shipping_method_name=shipping_method_name) group.process(partition, self.cart.discounts) group.save() if voucher is not None: increase_voucher_usage(voucher) if self.note is not None and self.note: order.notes.create(user=order.user, content=self.note) return order