Repository: django-oscar/django-oscar

Issue:
Basket.line_tax returns None
Due to the change in ad1094c1e8, if a line's tax is set to `Decimal('0.00')`, `Line.is_tax_known` will return `True`, but `Line.line_tax` will return `None`. Formerly in this case, `Line.line_tax` returned `Decimal('0.00')` instead of `None`. I'd suggest making `line_tax` check `is_tax_known` instead of doing it's own check.

```
 @property
     def line_tax(self):
-         if self.unit_tax:
+         if self.is_tax_known:
             return self.quantity * self.unit_tax
``` 

See [ad1094c1e8:src/oscar/apps/basket/abstract_models.py:828](https://github.com/django-oscar/django-oscar/commit/ad1094c1e89c0314c0a883b0b0a4e618a443da22#diff-519bc8e8997795240c2a90d8d63b66baL828)


Discussion:

Resolving patch:
diff --git a/src/oscar/apps/basket/abstract_models.py b/src/oscar/apps/basket/abstract_models.py
--- a/src/oscar/apps/basket/abstract_models.py
+++ b/src/oscar/apps/basket/abstract_models.py
@@ -828,7 +828,7 @@ def line_price_incl_tax_incl_discounts(self):
 
     @property
     def line_tax(self):
-        if self.unit_tax:
+        if self.is_tax_known:
             return self.quantity * self.unit_tax
 
     @property


Test patch:
diff --git a/tests/integration/basket/test_models.py b/tests/integration/basket/test_models.py
--- a/tests/integration/basket/test_models.py
+++ b/tests/integration/basket/test_models.py
@@ -78,6 +78,33 @@ def test_basket_lines_queryset_is_ordered(self):
         queryset = basket.all_lines()
         self.assertTrue(queryset.ordered)
 
+    def test_line_tax_for_zero_tax_strategies(self):
+        basket = Basket()
+        basket.strategy = strategy.Default()
+        product = factories.create_product()
+        # Tax for the default strategy will be 0
+        factories.create_stockrecord(
+            product, price_excl_tax=D('75.00'), num_in_stock=10)
+        basket.add(product, 1)
+
+        self.assertEqual(basket.lines.first().line_tax, D('0'))
+
+    def test_line_tax_for_unknown_tax_strategies(self):
+
+        class UnknownTaxStrategy(strategy.Default):
+            """ A test strategy where the tax is not known """
+
+            def pricing_policy(self, product, stockrecord):
+                return prices.FixedPrice('GBP', stockrecord.price_excl_tax, tax=None)
+
+        basket = Basket()
+        basket.strategy = UnknownTaxStrategy()
+        product = factories.create_product()
+        factories.create_stockrecord(product, num_in_stock=10)
+        basket.add(product, 1)
+
+        self.assertEqual(basket.lines.first().line_tax, None)
+
 
 class TestAddingAProductToABasket(TestCase):
 
diff --git a/tests/integration/order/test_models.py b/tests/integration/order/test_models.py
--- a/tests/integration/order/test_models.py
+++ b/tests/integration/order/test_models.py
@@ -345,22 +345,14 @@ def test_contains_voucher_details_after_voucher_is_deleted(self):
 
 
 class OrderTests(TestCase):
-    def get_date_tuple(self, date=None):
-        """
-        Returns a tuple like (year, month, day, hour, minute) for
-        datetime comparisons.
-        We probably don't want to assert datetime objects have the same
-        number of miliseconds etc. just in case the object in the test
-        differs by some insignificant amount.
-        """
-        if date is None:
-            date = timezone.now()
-        return date.timetuple()[:-4]
 
-    def test_sets_date_placed_to_now_by_default(self):
+    @mock.patch('oscar.apps.order.abstract_models.now')
+    def test_sets_date_placed_to_now_by_default(self, mock_now):
+        tzinfo = timezone.get_current_timezone()
+        mock_now.return_value = datetime(2017, 6, 23, 16, 14, tzinfo=tzinfo)
         order = create_order(number='100003')
-        self.assertTupleEqual(self.get_date_tuple(order.date_placed),
-                              self.get_date_tuple())
+        self.assertEqual(order.date_placed,
+                              datetime(2017, 6, 23, 16, 14, tzinfo=tzinfo))
 
     def test_allows_date_placed_to_be_changed_and_set_explicitly(self):
         order = create_order(number='100003')
@@ -368,8 +360,8 @@ def test_allows_date_placed_to_be_changed_and_set_explicitly(self):
         order.date_placed = datetime(2012, 8, 11, 16, 14, tzinfo=tzinfo)
         order.save()
 
-        self.assertTupleEqual(self.get_date_tuple(order.date_placed),
-                              (2012, 8, 11, 16, 14))
+        self.assertEqual(order.date_placed,
+                            datetime(2012, 8, 11, 16, 14, tzinfo=tzinfo))
 
     def test_shipping_status(self):
         order = OrderFactory()