def test_price_property_set(): m = get_market() m.price = TaxfulPrice(321, 'GBP') assert m.price == TaxfulPrice(321, 'GBP') assert m.value == 321 assert type(m.value) == Decimal assert m.currency == 'GBP'
def test_taxless_and_taxful_price_properties(): class Foo(object): taxful_value = 110 taxless_value = 100 currency = 'USD' taxful_price = TaxfulPriceProperty('taxful_value', 'currency') taxless_price = TaxlessPriceProperty('taxless_value', 'currency') foo = Foo() assert foo.taxful_price == TaxfulPrice(110, 'USD') foo.taxful_price = TaxfulPrice(220, 'USD') assert foo.taxful_price == TaxfulPrice(220, 'USD') assert foo.taxful_value == 220 assert foo.taxless_price == TaxlessPrice(100, 'USD') foo.taxless_price = TaxlessPrice(200, 'USD') assert foo.taxless_price == TaxlessPrice(200, 'USD') assert foo.taxless_value == 200 with pytest.raises(UnitMixupError): foo.taxful_price = TaxlessPrice(220, 'USD') with pytest.raises(UnitMixupError): foo.taxful_price = TaxfulPrice(220, 'EUR') with pytest.raises(UnitMixupError): foo.taxless_price = TaxfulPrice(220, 'USD') with pytest.raises(UnitMixupError): foo.taxless_price = TaxlessPrice(220, 'EUR')
def get_data(self): orders = self.get_objects().order_by("-order_date") data = [] for order_date, orders_group in itertools.groupby(orders, key=self.extract_date): taxless_total = TaxlessPrice(0, currency=self.shop.currency) taxful_total = TaxfulPrice(0, currency=self.shop.currency) paid_total = TaxfulPrice(0, currency=self.shop.currency) product_count = 0 order_count = 0 for order in orders_group: taxless_total += order.taxless_total_price taxful_total += order.taxful_total_price product_count += sum(order.get_product_ids_and_quantities().values()) order_count += 1 if order.payment_date: paid_total += order.taxful_total_price data.append({ "date": format_date(order_date, format="short", locale=get_current_babel_locale()), "order_count": order_count, "product_count": int(product_count), "taxless_total": taxless_total, "taxful_total": taxful_total, }) return self.get_return_data(data)
def test_stacked_tax_taxful_price(): shop = get_shop(prices_include_tax=True, currency='EUR') source = OrderSource(shop) assert source.prices_include_tax source.add_line(type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(20)) with override_provides("tax_module", TAX_MODULE_SPEC): with override_settings(WSHOP_TAX_MODULE="irvine"): source.shipping_address = MutableAddress( street="16215 Alton Pkwy", postal_code="92602", ) line = source.get_final_lines(with_taxes=True)[0] assert isinstance(line, SourceLine) assert line.taxes assert line.taxful_price == TaxfulPrice(20, 'EUR') assert_almost_equal(line.taxless_price, TaxlessPrice("18.52", 'EUR')) source.uncache() # Let's move out to a taxless location. source.shipping_address.postal_code = "11111" line = source.get_final_lines(with_taxes=True)[0] assert isinstance(line, SourceLine) assert not line.taxes assert line.taxful_price == TaxfulPrice(20, source.currency) assert line.taxless_price.value == Decimal("20")
def test_add_with_currency(): for c in ['EUR', 'USD', 'GBP']: summed = TaxfulPrice(1, c) + TaxfulPrice(2, c) assert summed.value == 3 assert summed.includes_tax == True assert summed.currency == c assert summed == TaxfulPrice(3, c)
def get_data(self): data = [] # group products by id - que queryset must be ordered by id to make this work for key, groups in itertools.groupby(self.get_objects(), lambda pl: pl.product_id): quantity = 0 taxful_total = TaxfulPrice(0, self.shop.currency) taxless_total = TaxlessPrice(0, self.shop.currency) product = None for order_line in groups: quantity += order_line.quantity taxful_total += order_line.taxful_price taxless_total += order_line.taxless_price if not product: product = order_line.product data.append({ "product": product.name, "sku": product.sku, "quantity": quantity, "taxful_total": taxful_total.as_rounded().value, "taxless_total": taxless_total.as_rounded().value, }) order_by = self.options.get("order_by") if order_by: data = sorted(data, key=itemgetter(order_by), reverse=True) return self.get_return_data(data)
def test_refunds_for_discounted_order_lines(): 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) discount_line = OrderLine( order_id=order.id, type=OrderLineType.DISCOUNT, quantity=1, discount_amount_value=Decimal("0.54321")) discount_line.save() order.lines.add(discount_line) product_line = order.lines.filter(type=OrderLineType.PRODUCT).first() product_line.discount_amount = TaxfulPrice(100, order.currency) product_line.save() taxful_price_with_discount = product_line.taxful_price order.cache_prices() order.save() assert product_line.base_price == TaxfulPrice(400, order.currency) assert taxful_price_with_discount == TaxfulPrice(300, order.currency) # try to refund only the product line - should fail since this would result in a negative total with pytest.raises(RefundExceedsAmountException): order.create_refund([{"line": product_line, "quantity": 2, "amount": taxful_price_with_discount.amount}]) # try to refund the product line with a negative amount with pytest.raises(InvalidRefundAmountException): order.create_refund([{"line": product_line, "quantity": 1, "amount": -taxful_price_with_discount.amount}]) # try to refund the discount line with a positive amount with pytest.raises(InvalidRefundAmountException): order.create_refund([{"line": discount_line, "quantity": 1, "amount": -discount_line.taxful_price.amount}]) order.create_refund([ {"line": discount_line, "quantity": 1, "amount": discount_line.taxful_price.amount}, {"line": product_line, "quantity": 2, "amount": taxful_price_with_discount.amount} ]) assert product_line.max_refundable_amount.value == 0 assert discount_line.max_refundable_amount.value == 0 assert order.taxful_total_price.value == 0 order = create_order_with_product(product, supplier, 2, 200, shop=shop) discount_line = OrderLine( order_id=order.id, type=OrderLineType.DISCOUNT, quantity=1, discount_amount_value=Decimal("0.54321")) discount_line.save() order.lines.add(discount_line) product_line = order.lines.filter(type=OrderLineType.PRODUCT).first() product_line.discount_amount = TaxfulPrice(100, order.currency) product_line.save() order.cache_prices() order.save() order.create_full_refund(restock_products=False) assert order.taxful_total_price.value == 0
def _calc_compounded_added_taxes_from_taxful(amount, tax_groups): base_price = TaxfulPrice(amount) reversed_line_taxes = [] for taxes in reversed(tax_groups): taxed_price = stacked_value_added_taxes(base_price, taxes) base_price = TaxfulPrice(taxed_price.taxless) reversed_line_taxes.extend(reversed(taxed_price.taxes)) line_taxes = list(reversed(reversed_line_taxes)) return TaxedPrice(taxful=TaxfulPrice(amount), taxless=TaxlessPrice(base_price), taxes=line_taxes)
def test_init(): TaxfulPrice(42, 'EUR') TaxlessPrice(42, 'EUR') assert TaxfulPrice(currency='EUR').value == 0 assert TaxlessPrice(currency='EUR').value == 0 with pytest.raises(TypeError): Price() with pytest.raises(TypeError): Price(10) with pytest.raises(TypeError): Price(10, 'EUR')
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_shop_overview_block(request, currency, for_date=None): end = to_aware(for_date, time=time.max) if for_date else local_now() start_of_day = to_aware(end.date(), time=time.min) start_of_month = start_of_day.replace(day=1) start_of_year = start_of_day.replace(month=1, day=1) shop = request.shop if not currency: currency = shop.currency daily = get_order_overview_for_date_range(currency, start_of_day, end, shop=shop) mtd = get_order_overview_for_date_range(currency, start_of_month, end, shop=shop) ytd = get_order_overview_for_date_range(currency, start_of_year, end) totals = get_orders_by_currency(currency).complete().aggregate( num_orders=Count("id"), num_customers=Count("customer", distinct=True), sales=Sum("taxful_total_price_value") ) anon_orders = get_orders_by_currency(currency).complete().filter(customer__isnull=True, shop=shop).aggregate( num_orders=Count("id")) totals["num_customers"] += anon_orders["num_orders"] totals["sales"] = TaxfulPrice(totals["sales"] or 0, currency) block = DashboardContentBlock.by_rendering_template( "store_overview", request, "wshop/admin/sales_dashboard/_store_overview_dashboard_block.jinja", { "daily": daily, "mtd": mtd, "ytd": ytd, "totals": totals }) block.size = "small" return block
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_custom_payment_processor_cash_service(choice_identifier, expected_payment_status): shop = get_default_shop() product = get_default_product() supplier = get_default_supplier() processor = CustomPaymentProcessor.objects.create() payment_method = PaymentMethod.objects.create( shop=shop, payment_processor=processor, choice_identifier=choice_identifier, tax_class=get_default_tax_class()) order = create_order_with_product( product=product, supplier=supplier, quantity=1, taxless_base_unit_price=Decimal('5.55'), shop=shop) order.taxful_total_price = TaxfulPrice(Decimal('5.55'), u'EUR') order.payment_method = payment_method order.save() assert order.payment_status == PaymentStatus.NOT_PAID processor.process_payment_return_request(choice_identifier, order, None) assert order.payment_status == expected_payment_status processor.process_payment_return_request(choice_identifier, order, None) assert order.payment_status == expected_payment_status
def test_line_discount_more(): order = create_empty_order() order.save() ol = OrderLine(order=order, type=OrderLineType.OTHER) ol.quantity = 5 ol.base_unit_price = order.shop.create_price(30) ol.discount_amount = order.shop.create_price(50) ol.save() currency = order.shop.currency assert ol.taxless_base_unit_price == TaxlessPrice(30, currency) assert ol.taxless_discount_amount == TaxlessPrice(50, currency) assert ol.taxless_price == TaxlessPrice(5 * 30 - 50, currency) order_line_tax = OrderLineTax.from_tax( get_default_tax(), ol.taxless_price.amount, order_line=ol) order_line_tax.save() ol.taxes.add(order_line_tax) assert ol.taxless_discount_amount == TaxlessPrice(50, currency) assert ol.taxful_discount_amount == TaxfulPrice(75, currency) assert ol.taxless_price == TaxlessPrice(100, currency) assert ol.taxful_price == TaxfulPrice(150, currency) assert ol.taxless_base_unit_price == TaxlessPrice(30, currency) assert ol.taxful_base_unit_price == TaxfulPrice(45, currency)
def stacked_value_added_taxes(price, taxes): """ Stack added taxes on the given price without compounding. Note that this will not take compound taxation (Quebec) into account. :param price: Taxful or taxless price to calculate taxes for :type price: wshop.core.pricing.Price :param taxes: List of Tax objects :type taxes: list[wshop.core.models.Tax] :return: TaxedPrice with the calculated taxes. :rtype: TaxedPrice """ def money_sum(iterable): return sum(iterable, Money(0, price.currency)) if not taxes: return TaxedPrice(TaxfulPrice(price), TaxlessPrice(price), []) if price.includes_tax: taxful = price rate_sum = sum(tax.rate for tax in taxes if tax.rate) amount_sum = money_sum(tax.amount for tax in taxes if tax.amount) taxless = TaxlessPrice((taxful.amount - amount_sum) / (1 + rate_sum)) else: taxful = None # will be calculated below taxless = price line_taxes = [ SourceLineTax.from_tax(tax=tax, base_amount=taxless.amount) for tax in taxes ] if taxful is None: total_tax_amount = money_sum(x.amount for x in line_taxes) taxful = TaxfulPrice(taxless.amount + total_tax_amount) return TaxedPrice(taxful, taxless, line_taxes)
def create_price(self, value): """ Create a price with given value and settings of this shop. Takes the ``prices_include_tax`` and ``currency`` settings of this Shop into account. :type value: decimal.Decimal|int|str :rtype: wshop.core.pricing.Price """ if self.prices_include_tax: return TaxfulPrice(value, self.currency) else: return TaxlessPrice(value, self.currency)
def get_taxed_price(self, context, price, tax_class): if price.includes_tax: taxful = price taxless = TaxlessPrice(price.amount / Decimal('1.2')) else: taxful = TaxfulPrice(price.amount * Decimal('1.2')) taxless = price tax_amount = taxful.amount - taxless.amount base_amount = taxless.amount taxes = [ SourceLineTax(Tax(), 'fifth', tax_amount, base_amount), ] DummyTaxModule.calculations_done += 1 return TaxedPrice(taxful, taxless, taxes)
def test_basic_order(): PRODUCTS_TO_SEND = 10 product = get_default_product() supplier = get_default_supplier() order = create_order_with_product( product, supplier=supplier, quantity=PRODUCTS_TO_SEND, taxless_base_unit_price=10, tax_rate=Decimal("0.5") ) assert order.shop.prices_include_tax is False price = order.shop.create_price currency = order.currency discount_order_line = OrderLine(order=order, quantity=1, type=OrderLineType.OTHER) discount_order_line.discount_amount = price(30) assert discount_order_line.price == price(-30) discount_order_line.save() order.cache_prices() order.check_all_verified() order.save() assert order.taxful_total_price == TaxfulPrice(PRODUCTS_TO_SEND * (10 + 5) - 30, currency) shipment = order.create_shipment_of_all_products(supplier=supplier) assert shipment.total_products == PRODUCTS_TO_SEND, "All products were shipped" assert shipment.weight == product.gross_weight * PRODUCTS_TO_SEND / 1000, "Gravity works" assert not order.get_unshipped_products(), "Nothing was left in the warehouse" order.shipping_status = ShippingStatus.FULLY_SHIPPED order.create_payment(order.taxful_total_price) assert order.payments.exists(), "A payment was created" with pytest.raises(NoPaymentToCreateException): order.create_payment(Money(6, currency)) assert order.is_paid(), "Order got paid" assert order.can_set_complete(), "Finalization is possible" order.status = OrderStatus.objects.get_default_complete() assert order.is_complete(), "Finalization done" summary = order.get_tax_summary() assert len(summary) == 2 assert summary[0].tax_rate * 100 == 50 assert summary[0].based_on == Money(100, currency) assert summary[0].tax_amount == Money(50, currency) assert summary[0].taxful == summary[0].based_on + summary[0].tax_amount assert summary[1].tax_id is None assert summary[1].tax_code == '' assert summary[1].tax_amount == Money(0, currency) assert summary[1].tax_rate == 0 assert order.get_total_tax_amount() == Money(50, currency)
def test_line_discount(): order = create_empty_order(prices_include_tax=False) order.save() currency = order.shop.currency ol = OrderLine( order=order, type=OrderLineType.OTHER, quantity=5, text="Thing" ) ol.discount_amount = order.shop.create_price(50) ol.base_unit_price = order.shop.create_price(40) ol.save() order_line_tax = OrderLineTax.from_tax( get_default_tax(), ol.taxless_price.amount, order_line=ol) order_line_tax.save() ol.taxes.add(order_line_tax) assert ol.taxless_discount_amount == order.shop.create_price(50) assert ol.taxful_discount_amount == TaxfulPrice(75, currency) assert ol.taxless_price == order.shop.create_price(150) assert ol.taxful_price == TaxfulPrice(150 + 75, currency) assert ol.taxless_base_unit_price == order.shop.create_price(40) assert ol.taxful_base_unit_price == TaxfulPrice(60, currency) assert "Thing" in six.text_type(ol)
def get_order_overview_for_date_range(currency, start_date, end_date, shop=None): if not shop: shop = Shop.objects.first() orders = get_orders_by_currency(currency).filter(shop=shop).complete() orders_in_range = orders.in_date_range(start_date, end_date) q = orders_in_range.aggregate( num_orders=Count("id"), num_customers=Count("customer", distinct=True), sales=Sum("taxful_total_price_value")) anon_orders = orders_in_range.filter(customer__isnull=True).aggregate( num_orders=Count("id")) q["num_customers"] += anon_orders["num_orders"] q["sales"] = TaxfulPrice(q["sales"] or 0, currency) return q
def get_data(self): orders = self.get_objects().order_by("-order_date") data = [] # TODO: maybe make raw sql query in future for order_date, orders_group in itertools.groupby(orders, key=self.extract_date): taxless_total = TaxlessPrice(0, currency=self.shop.currency) taxful_total = TaxfulPrice(0, currency=self.shop.currency) product_count = 0 order_count = 0 for order in orders_group: taxless_total += order.taxless_total_price taxful_total += order.taxful_total_price product_count += sum(order.get_product_ids_and_quantities().values()) order_count += 1 data.append({ "date": format_date(order_date, locale=get_current_babel_locale()), "order_count": order_count, "product_count": int(product_count), "taxless_total": taxless_total.as_rounded().value, "taxful_total": taxful_total.as_rounded().value, }) return self.get_return_data(data)
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 get_open_orders_block(request, currency=None): orders = get_orders_for_shop(request) if not currency: shop = request.shop currency = shop.currency # Open orders / open orders value open_order_data = ( orders.incomplete() .aggregate(count=Count("id"), sum=Sum("taxful_total_price_value"))) return DashboardMoneyBlock( id="open_orders_sum", color="orange", title=_("Open Orders Value"), value=TaxfulPrice((open_order_data.get("sum") or 0), currency), currency=currency, icon="fa fa-inbox", subtitle=get_subtitle(open_order_data.get("count")) )
def test_taxed_base_unit_prices(): assert_almost_equal(line.taxless_base_unit_price, TaxlessPrice(5, 'EUR') / Decimal('1.1')) assert line.taxful_base_unit_price == TaxfulPrice(5, 'EUR')
def test_tax_special_cases3(): taxless_line = Line(base_unit_price=TaxfulPrice(0, 'EUR'), quantity=0, discount_amount=TaxfulPrice(0, 'EUR'), tax_amount=Money(0, 'EUR')) assert taxless_line.taxful_price == TaxfulPrice(0, 'EUR') assert taxless_line.taxless_price == TaxlessPrice(0, 'EUR') assert taxless_line.base_unit_price == TaxfulPrice(0, 'EUR') assert taxless_line.taxful_base_unit_price == TaxfulPrice(0, 'EUR') assert taxless_line.taxless_base_unit_price == TaxlessPrice(0, 'EUR') assert taxless_line.discount_amount == TaxfulPrice(0, 'EUR') assert taxless_line.taxful_discount_amount == TaxfulPrice(0, 'EUR') assert taxless_line.taxless_discount_amount == TaxlessPrice(0, 'EUR') assert taxless_line.price == TaxfulPrice(0, 'EUR') assert taxless_line.taxful_price == TaxfulPrice(0, 'EUR') assert taxless_line.taxless_price == TaxlessPrice(0, 'EUR') assert taxless_line.base_price == TaxfulPrice(0, 'EUR') assert taxless_line.taxful_base_price == TaxfulPrice(0, 'EUR') assert taxless_line.taxless_base_price == TaxlessPrice(0, 'EUR') assert taxless_line.unit_discount_amount == TaxfulPrice(0, 'EUR') assert taxless_line.taxful_unit_discount_amount == TaxfulPrice(0, 'EUR') assert taxless_line.taxless_unit_discount_amount == TaxlessPrice(0, 'EUR') assert taxless_line.discount_rate == 0 assert taxless_line.tax_rate == 0
def test_price(): assert line.price == TaxfulPrice(33, 'EUR') # 5 * 9 - 12
def test_taxed_discount_amounts(): assert_almost_equal(line.taxless_discount_amount, TaxlessPrice(12, 'EUR') / Decimal('1.1')) assert line.taxful_discount_amount == TaxfulPrice(12, 'EUR')
def test_taxed_discounted_unit_prices(): assert_almost_equal(line.taxless_discounted_unit_price, TaxlessPrice(33, 'EUR') / Decimal('1.1') / 9) assert line.taxful_discounted_unit_price == TaxfulPrice(33, 'EUR') / 9
def test_unit_discount_amount(): assert line.unit_discount_amount == TaxfulPrice(12, 'EUR') / 9
def test_discounted_unit_price(): assert line.discounted_unit_price == TaxfulPrice(33, 'EUR') / 9