def test_units_match(): class XxxMoney(int): currency = 'XXX' m1 = Money(1, 'EUR') m2 = Money(2, 'EUR') m3 = Money(3, 'XXX') m4 = XxxMoney(4) assert m1.unit_matches_with(m2) assert not m1.unit_matches_with(m3) assert m3.unit_matches_with(m4)
def test_tax_special_cases1(): all_tax_line1 = Line(base_unit_price=TaxfulPrice(25, 'EUR'), quantity=5, discount_amount=TaxfulPrice(25, 'EUR'), tax_amount=Money(100, 'EUR')) assert all_tax_line1.taxful_price == TaxfulPrice(100, 'EUR') assert all_tax_line1.taxless_price == TaxlessPrice(0, 'EUR') assert all_tax_line1.taxful_discount_amount == TaxfulPrice(25, 'EUR') assert all_tax_line1.taxless_discount_amount == TaxlessPrice(0, 'EUR') assert all_tax_line1.tax_rate == 0 assert all_tax_line1.taxful_base_unit_price == TaxfulPrice(25, 'EUR') assert all_tax_line1.taxless_base_unit_price == TaxlessPrice(0, 'EUR')
def get_data(self, **kwargs): orders = super(RefundedSalesReport, self).get_objects().filter( lines__type=OrderLineType.REFUND).distinct() total_refunded = Money(0, self.shop.currency) for order in orders: total_refunded += order.get_total_refunded_amount() data = [{ "refunded_orders": len(orders), "total_refunded": total_refunded.value }] return self.get_return_data(data, has_totals=False)
def test_can_create_refund(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() assert order.can_create_payment() # Partially refunded orders can create refunds order.create_refund([ {"line": order.lines.first(), "quantity": 1, "amount": Money(200, order.currency), "restock": False}]) assert order.can_create_refund() # But fully refunded orders can't order.create_refund([ {"line": order.lines.first(), "quantity": 1, "amount": Money(200, order.currency), "restock": False}]) assert not order.can_create_refund()
def test_max_refundable_amount(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, ) order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() assert len(order.lines.all()) == 1 line = order.lines.first() assert line.max_refundable_amount == line.taxful_price.amount partial_refund_amount = Money(10, order.currency) assert partial_refund_amount.value > 0 order.create_refund([{"line": line, "quantity": 1, "amount": partial_refund_amount}]) assert line.max_refundable_amount == line.taxful_price.amount - partial_refund_amount assert order.get_total_tax_amount() == Money( order.taxful_total_price_value - order.taxless_total_price_value, order.currency)
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 form_valid(self, form): order = self.object amount = Money(form.cleaned_data["amount"], order.currency) if amount.value == 0: messages.error(self.request, _("Payment amount cannot be 0")) return self.form_invalid(form) try: payment = order.create_payment(amount, description="Manual payment") messages.success( self.request, _("Payment %s created.") % payment.payment_identifier) except NoPaymentToCreateException: messages.error(self.request, _("Order has already been paid")) return self.form_invalid(form) else: return HttpResponseRedirect(get_model_url(order))
def test_mixed_chart(): labels = ["One", "Two", "Three"] locale = "pt_br" currency = "BRL" chart = MixedChart("ma biultiful xart", labels, data_type=ChartDataType.CURRENCY, locale=locale, currency=currency) dataset1 = OrderedDict({ "type": ChartType.BAR, "label": "some bars #1", "data": [1, 2, 3] }) dataset2 = OrderedDict({ "type": ChartType.BAR, "label": "some bars #2", "data": [2, 3, 4] }) dataset3 = OrderedDict({ "type": ChartType.LINE, "label": "some lines #1", "data": [5, 6, 7] }) dataset4 = OrderedDict({ "type": ChartType.LINE, "label": "some lines #2", "data": [8, 9, 10] }) datasets = [dataset1, dataset2, dataset3, dataset4] for dataset in datasets: chart.add_data(dataset["label"], dataset["data"], dataset["type"]) chart_config = chart.get_config() assert chart_config["type"] == "mixed" assert chart_config["labels"] == labels for i in range(len(chart_config["data"])): for j in range(len(chart_config["data"][i]["data"])): assert chart_config["data"][i]["data"][j] == datasets[i]["data"][j] formatted_data = chart_config["data"][i]["formatted_data"][j] assert formatted_data == format_money( Money(datasets[i]["data"][j], currency=currency).as_rounded())
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False): shop = get_shop(prices_include_tax=prices_include_tax) supplier = get_simple_supplier() if include_basket_campaign: _add_basket_campaign(shop) if include_catalog_campaign: _add_catalog_campaign(shop) _add_taxes() source = BasketishOrderSource(shop) source.status = get_initial_order_status() ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact()) for product_data in _get_product_data(): quantity = product_data.pop("quantity") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, stock_behavior=StockBehavior.STOCKED, tax_class=get_default_tax_class(), **product_data) shop_product = product.get_shop_instance(shop) shop_product.categories.add(get_default_category()) shop_product.save() supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY) pi = product.get_price_info(ctx) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount ) oc = OrderCreator() order = oc.create_order(source) order.create_payment(Money("1", "EUR")) assert not order.has_refunds() assert order.can_create_refund() assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.payment_status == PaymentStatus.PARTIALLY_PAID return order
def post(self, request, *args, **kwargs): order = self.object = self.get_object() error = False if order.payment_status not in (PaymentStatus.DEFERRED, PaymentStatus.NOT_PAID): error = True messages.error( self.request, _("Only orders which are not paid or deferred can be set as paid." )) if order.taxful_total_price: error = True messages.error( self.request, _("Only zero price orders can be set as paid without creating a payment." )) if not error: amount = Money(0, order.shop.currency) order.create_payment(amount, description=_("Zero amount payment")) messages.success(self.request, _("Order marked as paid.")) return HttpResponseRedirect(get_model_url(self.get_object()))
def _get_refund_line_info(self, order, data): refund_line_info = {} amount_value = data.get("amount", 0) or 0 line_number = data.get("line_number") quantity = data.get("quantity", 0) or 1 restock_products = data.get("restock_products") if line_number != "amount": line = order.lines.filter(ordering=line_number).first() if not line: return None refund_line_info["line"] = line refund_line_info["quantity"] = quantity refund_line_info["restock_products"] = bool(restock_products) else: refund_line_info["line"] = "amount" refund_line_info["text"] = data.get("text") refund_line_info["quantity"] = 1 refund_line_info["amount"] = Money(amount_value, order.currency) return refund_line_info
def add_data(self, name, data, chart_type): """ Add data to this chart :param name: the name of the dataset :type name: str :param data: the list of data :type data: list[int|float|Decimal] :param chart_type: the chart type - tells how data should be rendered. This data type must be available in the `supported_chart_type` attribute of this instance :type chart_type: ChartType """ assert chart_type in self.supported_chart_types formatted_data = [] # format value for each data point if self.data_type == ChartDataType.CURRENCY: for value in data: formatted_data.append( format_money( Money(value, currency=self.currency).as_rounded())) elif self.data_type == ChartDataType.PERCENT: for value in data: formatted_data.append(format_percent(value, locale=self.locale)) # self.data_type == ChartDataType.NUMBER else: for value in data: formatted_data.append(format_decimal(value, locale=self.locale)) self.datasets.append({ "type": chart_type, "label": name, "data": data, "formatted_data": formatted_data })
def test_money_without_currency(): with pytest.raises(TypeError): Money(42)
def money_round(value): return Money(value, shop.currency).as_rounded(2)
def test_repr(): assert repr(Money(42, 'EUR')) == "Money('42', 'EUR')" assert repr(Money('42.123', 'EUR')) == "Money('42.123', 'EUR')" assert repr(Money('42.0', 'EUR')) == "Money('42.0', 'EUR')" assert repr(Money('42.123', 'EUR')) == "Money('42.123', 'EUR')" assert repr(Money('42.123', 'USD')) == "Money('42.123', 'USD')"
def usd(value): """ Get Money with USD currency for given value. """ return Money(value, "USD")
def test_refunds(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, ) tax_rate = Decimal("0.1") taxless_base_unit_price = shop.create_price(200) order = create_order_with_product(product, supplier, 3, taxless_base_unit_price, tax_rate, shop=shop) order.payment_status = PaymentStatus.DEFERRED order.cache_prices() order.save() assert order.get_total_refunded_amount().value == 0 assert order.get_total_unrefunded_amount().value == order.taxful_total_price.value assert not order.can_edit() assert len(order.lines.all()) == 1 product_line = order.lines.first() assert product_line.ordering == 0 assert order.can_create_refund() assert not order.has_refunds() order.create_refund([{"line": product_line, "quantity": 1, "amount": (product_line.taxful_price.amount / 3)}]) assert len(order.lines.all()) == 2 assert order.lines.last().ordering == 1 assert order.has_refunds() # Confirm the value of the refund assert order.lines.last().taxful_price == -product_line.base_unit_price assert order.lines.last().tax_amount == -(product_line.taxless_base_unit_price * tax_rate).amount # Create a refund with a parent line and amount order.create_refund([{"line": product_line, "quantity": 1, "amount": product_line.taxful_price.amount / 3}]) assert len(order.lines.all()) == 3 assert order.lines.last().ordering == 2 assert order.lines.last().taxful_price.amount == -taxless_base_unit_price.amount * (1 + tax_rate) assert order.lines.last().tax_amount == -taxless_base_unit_price.amount * tax_rate assert order.taxless_total_price.amount == taxless_base_unit_price.amount assert order.taxful_total_price.amount == taxless_base_unit_price.amount * (1 + tax_rate) assert order.can_create_refund() assert order.get_total_tax_amount() == Money( (order.taxful_total_price_value - order.taxless_total_price_value), order.currency) # Try to refunding remaining amount without a parent line with pytest.raises(AssertionError): order.create_refund([{"amount": taxless_base_unit_price}]) # refund remaining amount order.create_refund([{"line": product_line, "quantity": 1, "amount": product_line.taxful_price.amount / 3}]) assert len(order.lines.all()) == 4 assert order.lines.last().ordering == 3 assert order.lines.last().taxful_price.amount == -taxless_base_unit_price.amount * (1 + tax_rate) assert not order.taxful_total_price.amount assert not order.can_create_refund() assert order.get_total_tax_amount() == Money( (order.taxful_total_price_value - order.taxless_total_price_value), order.currency) with pytest.raises(RefundExceedsAmountException): order.create_refund([{"line": product_line, "quantity": 1, "amount": taxless_base_unit_price.amount}])
def create_refund(): order.create_refund([ {"line": product_line, "quantity": 1, "amount": Money(1, order.currency), "restock_products": restock}])
def test_money_init_does_not_call_settings(): def guarded_getattr(self, name): assert False, 'nobody should read settings yet' with patch.object(type(settings), '__getattr__', guarded_getattr): Money(42, 'EUR')
base_unit_price = None quantity = None discount_amount = None tax_amount = None def __init__(self, base_unit_price, quantity, discount_amount, tax_amount): self.base_unit_price = base_unit_price self.quantity = quantity self.discount_amount = discount_amount self.tax_amount = tax_amount line = Line(base_unit_price=TaxfulPrice(5, 'EUR'), quantity=9, discount_amount=TaxfulPrice(12, 'EUR'), tax_amount=Money(3, 'EUR')) line2 = Line(base_unit_price=TaxfulPrice(10, 'EUR'), quantity=123, discount_amount=TaxfulPrice(0, 'EUR'), tax_amount=Money(123, 'EUR')) def setup_module(module): # uses the get_precision to avoiding db hits set_precision_provider(babel_precision_provider.get_precision) def test_price(): assert line.price == TaxfulPrice(33, 'EUR') # 5 * 9 - 12
def test_money_property_set_invalid_unit(): w = get_wallet() with pytest.raises(UnitMixupError): w.amount = Money(3, 'USD')
def test_money_property_get(): w = get_wallet() assert w.amount == Money(42, 'EUR')
def test_str(): assert str(Money('42.25', 'EUR')) == '42.25 EUR' assert str(Money('100', 'USD')) == '100 USD' assert str(Money(42, 'EUR')) == '42 EUR' assert str(Money('12.345', 'EUR')) == '12.345 EUR'
def tax_amount(self): """ :rtype: wshop.utils.money.Money """ zero = Money(0, self.order.currency) return sum((x.amount for x in self.taxes.all()), zero)
def test_format_money(): assert format_money(Money("3.6", "EUR"), locale="fi") == "3,60\xa0\u20ac" assert format_money(Money("3.6", "EUR"), widen=2, locale="fi") == "3,6000\xa0\u20ac" assert format_money(Money("3.6", "EUR"), digits=0, locale="fi") == "4\xa0\u20ac"
def test_money_init_from_value_with_currency(): class Dollar(int): currency = 'USD' assert Money(Dollar(42)) == Money(42, 'USD')
def test_refunds(admin_user): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, ) tax_rate = Decimal("0.1") taxless_base_unit_price = shop.create_price(200) order = create_order_with_product(product, supplier, 3, taxless_base_unit_price, tax_rate, shop=shop) order.payment_status = PaymentStatus.DEFERRED order.cache_prices() order.save() assert len(order.lines.all()) == 1 assert order.can_create_refund() assert not order.has_refunds() client = _get_client(admin_user) # Refund first and the only order line in 3 parts refund_url = "/api/wshop/order/%s/create_refund/" % order.id product_line = order.lines.first() data = { "refund_lines": [{ "line": product_line.id, "quantity": 1, "amount": (product_line.taxful_price.amount.value / 3), "restock_products": False }] } # First refund response = client.post(refund_url, data, format="json") assert response.status_code == status.HTTP_201_CREATED order.refresh_from_db() assert order.lines.count() == 2 assert order.has_refunds() # Second refund response = client.post(refund_url, data, format="json") assert response.status_code == status.HTTP_201_CREATED order.refresh_from_db() assert order.lines.count() == 3 assert order.can_create_refund() # Third refund response = client.post(refund_url, data, format="json") assert response.status_code == status.HTTP_201_CREATED order.refresh_from_db() assert order.lines.count() == 4 assert not order.can_create_refund() assert not order.taxful_total_price.amount assert order.get_total_tax_amount() == Money( (order.taxful_total_price_value - order.taxless_total_price_value), order.currency) # Test error message response = client.post(refund_url, data, format="json") assert response.status_code == status.HTTP_400_BAD_REQUEST error_msg = json.loads(response.content.decode("utf-8"))["error"] assert error_msg == "Order can not be refunded at the moment."
def money_sum(iterable): return sum(iterable, Money(0, price.currency))
def test_broken_order(admin_user): """ """ quantities = [44, 23, 65] expected = sum(quantities) * 50 expected_based_on = expected / 1.5 # Wshop is calculating taxes per line so there will be some "errors" expected_based_on = ensure_decimal_places( Decimal("%s" % (expected_based_on + 0.01))) shop = get_default_shop() supplier = get_default_supplier() product1 = create_product("simple-test-product1", shop, supplier, 50) product2 = create_product("simple-test-product2", shop, supplier, 50) product3 = create_product("simple-test-product3", shop, supplier, 50) tax = get_default_tax() source = BasketishOrderSource(get_default_shop()) billing_address = get_address(country="US") shipping_address = get_address(name="Test street", country="US") source.status = get_initial_order_status() source.billing_address = billing_address source.shipping_address = shipping_address source.customer = create_random_person() source.payment_method = get_default_payment_method() source.shipping_method = get_default_shipping_method() source.add_line( type=OrderLineType.PRODUCT, product=product1, supplier=get_default_supplier(), quantity=quantities[0], base_unit_price=source.create_price(50), ) source.add_line( type=OrderLineType.PRODUCT, product=product2, supplier=get_default_supplier(), quantity=quantities[1], base_unit_price=source.create_price(50), ) source.add_line( type=OrderLineType.PRODUCT, product=product3, supplier=get_default_supplier(), quantity=quantities[2], base_unit_price=source.create_price(50), ) currency = "EUR" summary = source.get_tax_summary() assert len(summary) == 1 summary = summary[0] assert summary.taxful == Money(expected, "EUR") assert summary.based_on == Money(expected_based_on, "EUR") # originally non-rounded value assert bankers_round(source.get_total_tax_amount()) == summary.tax_amount assert source.taxless_total_price.value == expected_based_on assert summary.taxful.value == source.taxful_total_price.value assert summary.tax_amount == Money( bankers_round(source.taxful_total_price.value - source.taxless_total_price.value), currency) assert summary.taxful == summary.raw_based_on + summary.tax_amount assert summary.tax_rate == tax.rate assert summary.taxful.value == ( summary.based_on + summary.tax_amount).value - Decimal("%s" % 0.01) # create order from basket creator = OrderCreator() order = creator.create_order(source) assert order.taxless_total_price.value == expected_based_on # originally non-rounded value assert bankers_round(order.get_total_tax_amount()) == summary.tax_amount
def test_money_init_from_money(): assert Money(Money(123, 'GBP')) == Money(123, 'GBP')