from decimal import Decimal as D from django.test import TestCase from oscar.apps.offer.applicator import Applicator from oscar.apps.offer.models import Benefit from oscar.apps.shipping.models import OrderAndItemCharges, WeightBased from oscar.apps.shipping.repository import Repository from oscar.core.compat import get_user_model from oscar.test import factories User = get_user_model() class TestOrderAndItemCharges(TestCase): def setUp(self): self.method = OrderAndItemCharges( price_per_order=D('5.00'), price_per_item=D('1.00')) def test_tax_is_known(self): basket = factories.create_basket(empty=True) charge = self.method.calculate(basket) self.assertTrue(charge.is_tax_known) def test_returns_order_level_charge_for_empty_basket(self): basket = factories.create_basket(empty=True) charge = self.method.calculate(basket) self.assertEqual(D('5.00'), charge.incl_tax) def test_single_item_basket(self): basket = factories.create_basket(empty=False) charge = self.method.calculate(basket) self.assertEqual(D('5.00') + D('1.00'), charge.incl_tax) def test_single_item_basket_that_doesnt_require_shipping(self): # Create a product that doesn't require shipping record = factories.create_stockrecord() product = record.product product.product_class.requires_shipping = False product.product_class.save() basket = factories.create_basket(empty=True) basket.add_product(record.product) charge = self.method.calculate(basket) self.assertEqual(D('5.00'), charge.incl_tax) def test_multi_item_basket(self): basket = factories.create_basket(empty=True) record = factories.create_stockrecord() basket.add_product(record.product, 7) charge = self.method.calculate(basket) self.assertEqual(D('5.00') + 7 * D('1.00'), charge.incl_tax) class ZeroFreeThresholdTest(TestCase): def setUp(self): self.method = OrderAndItemCharges( price_per_order=D('10.00'), free_shipping_threshold=D('0.00')) self.basket = factories.create_basket(empty=True) def test_free_shipping_with_empty_basket(self): charge = self.method.calculate(self.basket) self.assertEqual(D('0.00'), charge.incl_tax) def test_free_shipping_with_nonempty_basket(self): record = factories.create_stockrecord(price=D('5.00')) self.basket.add_product(record.product) charge = self.method.calculate(self.basket) self.assertEqual(D('0.00'), charge.incl_tax) class TestNonZeroFreeThreshold(TestCase): def setUp(self): self.method = OrderAndItemCharges( price_per_order=D('10.00'), free_shipping_threshold=D('20.00')) self.basket = factories.create_basket(empty=True) def test_basket_below_threshold(self): record = factories.create_stockrecord(price=D('5.00')) self.basket.add_product(record.product) charge = self.method.calculate(self.basket) self.assertEqual(D('10.00'), charge.incl_tax) def test_basket_on_threshold(self): record = factories.create_stockrecord(price=D('5.00')) self.basket.add_product(record.product, quantity=4) charge = self.method.calculate(self.basket) self.assertEqual(D('0.00'), charge.incl_tax) def test_basket_above_threshold(self): record = factories.create_stockrecord(price=D('5.00')) self.basket.add_product(record.product, quantity=8) charge = self.method.calculate(self.basket) self.assertEqual(D('0.00'), charge.incl_tax) class WeightBasedMethodTests(TestCase): def setUp(self): self.standard = WeightBased.objects.create(name='Standard') self.express = WeightBased.objects.create(name='Express') def test_zero_weight_baskets_can_have_a_charge(self): self.standard.bands.create(upper_limit=1, charge=D('4.00')) charge = self.standard.get_charge(0) self.assertEqual(D('4.00'), charge) def test_zero_weight_baskets_can_have_no_charge(self): self.standard.bands.create(upper_limit=0, charge=D('0.00')) self.standard.bands.create(upper_limit=1, charge=D('4.00')) charge = self.standard.get_charge(0) self.assertEqual(D('0.00'), charge) def test_get_band_for_zero_weight(self): self.standard.bands.create(upper_limit=1, charge=D('4.00')) charge = self.standard.get_charge(0) self.assertEqual(D('4.00'), charge) def test_get_band_for_lower_weight(self): band = self.standard.bands.create(upper_limit=1, charge=D('4.00')) fetched_band = self.standard.get_band_for_weight(0.5) self.assertEqual(band.id, fetched_band.id) def test_get_band_for_higher_weight(self): self.standard.bands.create(upper_limit=1, charge=D('4.00')) fetched_band = self.standard.get_band_for_weight(1.5) self.assertIsNone(fetched_band) def test_get_band_for_matching_weight(self): band = self.standard.bands.create(upper_limit=1, charge=D('4.00')) fetched_band = self.standard.get_band_for_weight(1) self.assertEqual(band.id, fetched_band.id) def test_weight_to_is_upper_bound(self): band = self.standard.bands.create(upper_limit=1, charge=D('4.00')) self.assertEqual(1, band.weight_to) def test_weight_from_for_single_band(self): band = self.standard.bands.create(upper_limit=1, charge=D('4.00')) self.assertEqual(0, band.weight_from) def test_weight_from_for_multiple_bands(self): self.standard.bands.create(upper_limit=1, charge=D('4.00')) band = self.express.bands.create(upper_limit=2, charge=D('8.00')) self.assertEqual(0, band.weight_from) def test_get_band_for_series_of_bands(self): self.standard.bands.create(upper_limit=1, charge=D('4.00')) self.standard.bands.create(upper_limit=2, charge=D('8.00')) self.standard.bands.create(upper_limit=3, charge=D('12.00')) self.assertEqual(D('4.00'), self.standard.get_band_for_weight(0.5).charge) self.assertEqual(D('8.00'), self.standard.get_band_for_weight(1.5).charge) self.assertEqual(D('12.00'), self.standard.get_band_for_weight(2.5).charge) def test_get_band_for_series_of_bands_from_different_methods(self): self.express.bands.create(upper_limit=2, charge=D('8.00')) self.standard.bands.create(upper_limit=1, charge=D('4.00')) self.standard.bands.create(upper_limit=3, charge=D('12.00')) self.assertEqual(D('12.00'), self.standard.get_band_for_weight(2.5).charge) def test_for_smoke_with_basket_charge(self): basket = factories.create_basket(empty=True) charge = self.standard.calculate(basket) self.assertEqual(D('0.00'), charge.incl_tax) self.assertTrue(charge.is_tax_known) def test_simple_shipping_cost_scenario_handled_correctly(self): basket = factories.BasketFactory() product_attribute_value = factories.ProductAttributeValueFactory( value_float=2.5) basket.add_product(product_attribute_value.product) expected_charge = D('3.00') self.standard.bands.create(upper_limit=3, charge=expected_charge) charge = self.standard.calculate(basket) self.assertEqual(expected_charge, charge.excl_tax) def test_overflow_shipping_cost_scenario_handled_correctly(self): basket = factories.BasketFactory() product_attribute_value = factories.ProductAttributeValueFactory( value_float=2.5) basket.add_product(product_attribute_value.product) self.standard.bands.create(upper_limit=1, charge=D('1.00')) self.standard.bands.create(upper_limit=2, charge=D('2.00')) charge = self.standard.calculate(basket) self.assertEqual(D('1.00') + D('2.00'), charge.excl_tax) def test_zero_charge_discount(self): # Since Repository.apply_shipping_offer() returns the original # shipping method object on a free shipping charge, it's discount() # method should be callable and also indicate it won't add discounts. basket = factories.create_basket() self.assertEqual(D('0.00'), self.standard.discount(basket)) def test_zero_charge_with_shipping_discount(self): offer = factories.create_offer( benefit=Benefit.objects.create( type=Benefit.SHIPPING_FIXED_PRICE, value=1), ) basket = factories.create_basket() Applicator().apply_offers(basket, [offer]) # Similar to test_zero_charge_discount(), # but now test how the repository deals with it. method = Repository().apply_shipping_offer( basket, self.standard, offer) self.assertEqual(D('0.00'), method.discount(basket)) def test_get_charge_when_weight_is_divided_by_top_band_upper_limit_without_remainder(self): # In case we have next bands # Upper limit, kg Charge, USD # 1 2.00 # 2 6.00 self.standard.bands.create(upper_limit=1, charge=D('2.00')) self.standard.bands.create(upper_limit=2, charge=D('6.00')) # for weight 100 kg we should get charge 300 USD: # 100 kg / 2 kg * 6 USD = 300 USD self.assertEqual(D('300.00'), self.standard.get_charge(100)) # for weight 100.01 kg we should get charge 302 USD: # (100 kg / 2 kg * 6 USD = 300 USD) + (2 USD for remainder 0.01 kg) = 302 USD self.assertEqual(D('302.00'), self.standard.get_charge(100.01)) # for weight 2 kg we should get charge 6 USD self.assertEqual(D('6.00'), self.standard.get_charge(2)) # for weight 2.01 kg we should get charge 8 USD: # (2 kg / 2 kg * 6 USD = 6 USD) + (2 USD for remainder 0.01 kg) = 8 USD self.assertEqual(D('8.00'), self.standard.get_charge(2.01))