Repository: django-oscar/django-oscar

Issue:
customer.utils.Dispatcher needs return values
Right now `customer.utils.Dispatcher` has a bunch of methods that return `None` where it really makes sense to have a return value. It would make hooking into or overriding `Dispatcher` a lot easier.

Might make sense to have some tests for `Dispatcher` as well.



Discussion:

Resolving patch:
diff --git a/src/oscar/apps/customer/utils.py b/src/oscar/apps/customer/utils.py
--- a/src/oscar/apps/customer/utils.py
+++ b/src/oscar/apps/customer/utils.py
@@ -24,65 +24,76 @@ def __init__(self, logger=None):
 
     def dispatch_direct_messages(self, recipient, messages):
         """
-        Dispatch one-off messages to explicitly specified recipient(s).
+        Dispatch one-off messages to explicitly specified recipient.
         """
         if messages['subject'] and (messages['body'] or messages['html']):
-            self.send_email_messages(recipient, messages)
+            return self.send_email_messages(recipient, messages)
 
-    def dispatch_order_messages(self, order, messages, event_type=None,
-                                **kwargs):
+    def dispatch_order_messages(self, order, messages, event_type=None, **kwargs):
         """
-        Dispatch order-related messages to the customer
+        Dispatch order-related messages to the customer.
         """
         if order.is_anonymous:
-            if 'email_address' in kwargs:
-                self.send_email_messages(kwargs['email_address'], messages)
-            elif order.guest_email:
-                self.send_email_messages(order.guest_email, messages)
-            else:
-                return
+            email = kwargs.get('email_address', order.guest_email)
+            dispatched_messages = self.dispatch_anonymous_messages(email, messages)
         else:
-            self.dispatch_user_messages(order.user, messages)
+            dispatched_messages = self.dispatch_user_messages(order.user, messages)
 
-        # Create order communications event for audit
-        if event_type is not None:
-            CommunicationEvent._default_manager.create(
-                order=order, event_type=event_type)
+        self.create_communication_event(order, event_type, dispatched_messages)
+
+    def dispatch_anonymous_messages(self, email, messages):
+        dispatched_messages = {}
+        if email:
+            dispatched_messages['email'] = self.send_email_messages(email, messages), None
+        return dispatched_messages
 
     def dispatch_user_messages(self, user, messages):
         """
         Send messages to a site user
         """
+        dispatched_messages = {}
         if messages['subject'] and (messages['body'] or messages['html']):
-            self.send_user_email_messages(user, messages)
+            dispatched_messages['email'] = self.send_user_email_messages(user, messages)
         if messages['sms']:
-            self.send_text_message(user, messages['sms'])
+            dispatched_messages['sms'] = self.send_text_message(user, messages['sms'])
+        return dispatched_messages
 
     # Internal
 
+    def create_communication_event(self, order, event_type, dispatched_messages):
+        """
+        Create order communications event for audit
+        """
+        if dispatched_messages and event_type is not None:
+            CommunicationEvent._default_manager.create(order=order, event_type=event_type)
+
+    def create_customer_email(self, user, messages, email):
+        """
+        Create Email instance in database for logging purposes.
+        """
+        # Is user is signed in, record the event for audit
+        if email and user.is_authenticated():
+            return Email._default_manager.create(user=user,
+                                                 email=user.email,
+                                                 subject=email.subject,
+                                                 body_text=email.body,
+                                                 body_html=messages['html'])
+
     def send_user_email_messages(self, user, messages):
         """
-        Sends message to the registered user / customer and collects data in
-        database
+        Send message to the registered user / customer and collect data in database.
         """
         if not user.email:
             self.logger.warning("Unable to send email messages as user #%d has"
                                 " no email address", user.id)
-            return
+            return None, None
 
         email = self.send_email_messages(user.email, messages)
-
-        # Is user is signed in, record the event for audit
-        if email and user_is_authenticated(user):
-            Email._default_manager.create(user=user,
-                                          email=user.email,
-                                          subject=email.subject,
-                                          body_text=email.body,
-                                          body_html=messages['html'])
+        return email, self.create_customer_email(user, messages, email)
 
     def send_email_messages(self, recipient, messages):
         """
-        Plain email sending to the specified recipient
+        Send email to recipient, HTML attachment optional.
         """
         if hasattr(settings, 'OSCAR_FROM_EMAIL'):
             from_email = settings.OSCAR_FROM_EMAIL


Test patch:
diff --git a/tests/integration/customer/test_dispatcher.py b/tests/integration/customer/test_dispatcher.py
--- a/tests/integration/customer/test_dispatcher.py
+++ b/tests/integration/customer/test_dispatcher.py
@@ -1,9 +1,10 @@
-from django.test import TestCase
 from django.core import mail
+from django.test import TestCase
 
 from oscar.core.compat import get_user_model
 from oscar.apps.customer.utils import Dispatcher, get_password_reset_url
 from oscar.apps.customer.models import CommunicationEventType, Email
+from oscar.apps.order.models import CommunicationEvent
 from oscar.test.factories import create_order, SiteFactory
 
 
@@ -12,13 +13,7 @@
 
 class TestDispatcher(TestCase):
 
-    def test_sending_a_order_related_messages(self):
-        email = 'testuser@example.com'
-        user = User.objects.create_user('testuser', email,
-                                        'somesimplepassword')
-
-        order_number = '12345'
-        order = create_order(number=order_number, user=user)
+    def _dispatch_order_messages(self, order_number, order, email=None):
         et = CommunicationEventType.objects.create(code="ORDER_PLACED",
                                                    name="Order Placed",
                                                    category=CommunicationEventType.ORDER_RELATED)
@@ -34,6 +29,8 @@ def test_sending_a_order_related_messages(self):
         dispatcher = Dispatcher()
         dispatcher.dispatch_order_messages(order, messages, et)
 
+        self.assertEquals(CommunicationEvent.objects.filter(order=order, event_type=et).count(), 1)
+
         self.assertEqual(len(mail.outbox), 1)
 
         message = mail.outbox[0]
@@ -44,10 +41,32 @@ def test_sending_a_order_related_messages(self):
         dispatcher.dispatch_direct_messages(email, messages)
         self.assertEqual(len(mail.outbox), 2)
 
-    def test_sending_user_related_message(self):
+    def _dispatch_user_messages(self, user, event_code, ctx, subject):
+        msgs = CommunicationEventType.objects.get_and_render(
+            code=event_code, context=ctx)
+        Dispatcher().dispatch_user_messages(user, msgs)
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertEquals(mail.outbox[0].subject, subject)
+        self.assertEquals(Email.objects.count(), 1)
+        email = Email.objects.last()
+        self.assertEquals(email.user.id, user.id)
+        self.assertEquals(email.email, 'testuser@example.com')
+
+    def test_dispatch_order_messages(self):
         email = 'testuser@example.com'
         user = User.objects.create_user('testuser', email,
                                         'somesimplepassword')
+        order = create_order(number='12345', user=user)
+        self.assertFalse(order.is_anonymous)
+        self._dispatch_order_messages(order_number='12345', order=order, email=email)
+
+    def test_dispatch_anonymous_order_messages(self):
+        order = create_order(number='12346', guest_email='testguest@example.com')
+        self.assertTrue(order.is_anonymous)
+        self._dispatch_order_messages(order_number='12346', order=order, email='testguest@example.com', )
+
+    def test_dispatch_email_changed_user_message(self):
+        user = User.objects.create_user('testuser', 'testuser@example.com', 'somesimplepassword')
         CommunicationEventType.objects.create(code='EMAIL_CHANGED',
                                               name='Email Changed',
                                               category=CommunicationEventType.USER_RELATED)
@@ -57,12 +76,15 @@ def test_sending_user_related_message(self):
             'reset_url': get_password_reset_url(user),
             'new_email': 'newtestuser@example.com',
         }
-        msgs = CommunicationEventType.objects.get_and_render(
-            code='EMAIL_CHANGED', context=ctx)
-        Dispatcher().dispatch_user_messages(user, msgs)
-        self.assertEqual(len(mail.outbox), 1)
-        self.assertEquals(mail.outbox[0].subject, 'Your email address has changed at Test Site.')
-        self.assertEquals(Email.objects.count(), 1)
-        email = Email.objects.last()
-        self.assertEquals(email.user.id, user.id)
-        self.assertEquals(email.email, 'testuser@example.com')
+        self._dispatch_user_messages(
+            user, 'EMAIL_CHANGED', ctx, 'Your email address has changed at Test Site.'
+        )
+
+    def test_dispatch_registration_email_message(self):
+        user = User.objects.create_user('testuser', 'testuser@example.com', 'somesimplepassword')
+        CommunicationEventType.objects.create(code='REGISTRATION',
+                                              name='Registration',
+                                              category=CommunicationEventType.USER_RELATED)
+        ctx = {'user': user,
+               'site': SiteFactory()}
+        self._dispatch_user_messages(user, 'REGISTRATION', ctx, 'Thank you for registering.')