Repository: django-oscar/django-oscar

Issue:
The Order.date_placed field shouldn't be auto_now_add=True
This changes when an order is updated later on which isn't generally the desired behaviour. Admittedly, this is rare but this field should be set explicitly.



Discussion:

Resolving patch:
diff --git a/oscar/apps/order/abstract_models.py b/oscar/apps/order/abstract_models.py
--- a/oscar/apps/order/abstract_models.py
+++ b/oscar/apps/order/abstract_models.py
@@ -9,6 +9,7 @@
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _, pgettext_lazy
 from django.utils.datastructures import SortedDict
+from django.utils.timezone import now
 
 from oscar.core.utils import get_default_currency
 from oscar.core.compat import AUTH_USER_MODEL
@@ -80,7 +81,7 @@ class AbstractOrder(models.Model):
     guest_email = models.EmailField(_("Guest email address"), blank=True)
 
     # Index added to this field for reporting
-    date_placed = models.DateTimeField(auto_now_add=True, db_index=True)
+    date_placed = models.DateTimeField(db_index=True)
 
     #: Order status pipeline.  This should be a dict where each (key, value) #:
     #: corresponds to a status and a list of possible statuses that can follow
@@ -306,6 +307,19 @@ def post_order_actions(self):
         return self.discounts.filter(
             category=AbstractOrderDiscount.DEFERRED)
 
+    def set_date_placed_default(self):
+        if self.date_placed is None:
+            self.date_placed = now()
+
+    def save(self, *args, **kwargs):
+        """
+        Overrides django's save, so the date_placed field works as if
+        auto_now_add was set to True, but with the option to modify
+        the date later if the order is updated.
+        """
+        self.set_date_placed_default()
+        super(AbstractOrder, self).save(*args, **kwargs)
+
 
 @python_2_unicode_compatible
 class AbstractOrderNote(models.Model):


Test patch:
diff --git a/tests/integration/order/model_tests.py b/tests/integration/order/model_tests.py
--- a/tests/integration/order/model_tests.py
+++ b/tests/integration/order/model_tests.py
@@ -1,4 +1,4 @@
-from datetime import timedelta
+from datetime import timedelta, datetime
 from decimal import Decimal as D
 
 from django.test import TestCase
@@ -325,3 +325,31 @@ def test_contains_voucher_details_after_voucher_is_deleted(self):
         self.assertTrue(discount.offer is None)
 
         self.assertEqual(discount.description(), voucher.code)
+
+
+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):
+        order = create_order(number='100003')
+        self.assertTupleEqual(self.get_date_tuple(order.date_placed),
+                              self.get_date_tuple())
+
+    def test_allows_date_placed_to_be_changed_and_set_explicitly(self):
+        order = create_order(number='100003')
+        tzinfo = timezone.get_current_timezone()
+        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))