Commits

Luke Plant  committed fdc03bb

Implemented emails for when payment is received.

Also refactored to pull out signals and handlers

  • Participants
  • Parent commits 1217116
  • Branches bookings

Comments (0)

Files changed (7)

File cciw/bookings/email.py

 
 def check_email_verification_token(account, token):
     return EmailVerifyTokenGenerator().check_token(account, token)
+
+
+def site_address_url_start():
+    """
+    Returns start of URL (protocol and domain) for this site
+    (a guess)
+    """
+    protocol = 'https' if settings.SESSION_COOKIE_SECURE else 'http' # best guess
+    return protocol + '://' + get_current_site(None).domain
+
+
+def send_unrecognised_payment_email(ipn_obj):
+    c = {
+        'url_start': site_address_url_start(),
+        'ipn_obj': ipn_obj,
+        }
+
+    body = loader.render_to_string("cciw/bookings/unrecognised_payment_email.txt", c)
+    subject = "CCIW booking - unrecognised payment"
+    mail.send_mail(subject, body, settings.SERVER_EMAIL, [settings.WEBMASTER_EMAIL])
+
+
+def send_place_confirmed_email(booking, **kwargs):
+    account = booking.account
+    emails = list(set([e for e in [account.email, booking.email] if e.strip() != '']))
+    if not emails:
+        return
+
+    c = {
+        'url_start': site_address_url_start(),
+        'booking': booking,
+        'camp': booking.camp,
+        'payment_received': 'payment_received' in kwargs,
+        }
+    body = loader.render_to_string('cciw/bookings/place_confirmed_email.txt', c)
+    subject = "CCIW booking - place confirmed"
+    mail.send_mail(subject, body, settings.SERVER_EMAIL, emails)

File cciw/bookings/hooks.py

+import re
+
+from paypal.standard.ipn.signals import payment_was_successful
+
+from .signals import place_confirmed
+from .email import send_unrecognised_payment_email, send_place_confirmed_email
+from .models import BookingAccount
+
+#### Handlers #####
+
+### Payments ####
+
+def unrecognised_payment(ipn_obj):
+    send_unrecognised_payment_email(ipn_obj)
+
+
+def paypal_payment_received(sender, **kwargs):
+    ipn_obj = sender
+    m = re.match("account:(\d+);", ipn_obj.custom)
+    if m is None:
+        unrecognised_payment(ipn_obj)
+        return
+
+    try:
+        account = BookingAccount.objects.get(id=int(m.groups()[0]))
+        account.receive_payment(ipn_obj.mc_gross)
+    except BookingAccount.DoesNotExist:
+        unrecognised_payment(ipn_obj)
+
+
+### Place confirmation ###
+
+def place_confirmed_handler(sender, **kwargs):
+    booking = sender
+    send_place_confirmed_email(booking, **kwargs)
+
+
+#### Wiring ####
+
+payment_was_successful.connect(paypal_payment_received)
+place_confirmed.connect(place_confirmed_handler)

File cciw/bookings/models.py

 from datetime import datetime, date, timedelta
 from decimal import Decimal
 import os
-import re
 
 from dateutil.relativedelta import relativedelta
 from django.db import models
 from cciw.cciwmain.models import Camp
 from cciw.cciwmain.utils import Lock
 
+from .signals import place_confirmed
+
 # = Business rules =
 #
 # Business rules are implemented in relevant models and managers.
 #
 
 
-
 SEX_MALE, SEX_FEMALE = 'm', 'f'
 SEXES = [
     (SEX_MALE, 'Male'),
             if b.amount_due <= pot:
                 b.confirm()
                 b.save()
+                place_confirmed.send(b, payment_received=True)
                 pot -= b.amount_due
             i += 1
 
         lock.release()
 
 
-### Payments ####
-
-def unrecognised_payment(ipn_obj):
-    # If an online payment does not reference an existing BookingAccount, we accept it
-    # but complain loudly by email.
-    pass # TODO
-
-
-def paypal_payment_received(sender, **kwargs):
-    ipn_obj = sender
-    m = re.match("account:(\d+);", ipn_obj.custom)
-    if m is None:
-        unrecognised_payment(ipn_obj)
-        return
-
-    try:
-        account = BookingAccount.objects.get(id=int(m.groups()[0]))
-        account.receive_payment(ipn_obj.mc_gross)
-    except BookingAccount.DoesNotExist:
-        unrecognised_payment(ipn_obj)
-
-
-# Payment signals
-from paypal.standard.ipn.signals import payment_was_successful
-payment_was_successful.connect(paypal_payment_received)
+# Very important that the setup done in .hooks happens:
+from .hooks import *

File cciw/bookings/signals.py

+from django.dispatch import Signal
+
+place_confirmed = Signal()

File cciw/bookings/tests.py

         acc.receive_payment((p1 + p2) - p)
         self.assertTrue(acc.bookings.filter(price_type=PRICE_FULL)[0].booking_expires is None)
 
+    def test_email_for_bad_payment_1(self):
+        from cciw.bookings.models import paypal_payment_received
+
+        class IpnMock(object):
+            pass
+
+        ipn_1 = IpnMock()
+        ipn_1.id = 123
+        ipn_1.mc_gross = Decimal('1.00')
+        ipn_1.custom = "x" # wrong format
+
+        mail.outbox = []
+        self.assertEqual(len(mail.outbox), 0)
+        paypal_payment_received(ipn_1)
+
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertTrue('/admin/ipn/paypal' in mail.outbox[0].body)
+
+    def test_email_for_bad_payment_2(self):
+        from cciw.bookings.models import paypal_payment_received
+
+        class IpnMock(object):
+            pass
+
+        ipn_1 = IpnMock()
+        ipn_1.id = 123
+        ipn_1.mc_gross = Decimal('1.00')
+        ipn_1.custom = "account:1234;" # bad id
+
+        mail.outbox = []
+        self.assertEqual(len(mail.outbox), 0)
+        paypal_payment_received(ipn_1)
+
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertTrue('/admin/ipn/paypal' in mail.outbox[0].body)
+
+    def test_email_for_good_payment(self):
+        # This email could be triggered by whenever BookingAccount.distribute_funds
+        # is called, which can be from multiple routes. So we test it directly.
+
+        self.login()
+        self.create_place()
+        acc = BookingAccount.objects.get(email=self.email)
+        book_basket_now(acc.bookings.basket(self.camp.year))
+
+        mail.outbox = []
+        acc.receive_payment(acc.bookings.all()[0].amount_due)
+
+        self.assertEqual(len(mail.outbox), 1)
+
+        self.assertEqual(mail.outbox[0].subject, "CCIW booking - place confirmed")
+        self.assertEqual(mail.outbox[0].to, [self.email])
+        self.assertTrue("Thank you for your payment" in mail.outbox[0].body)
 
 class TestAjaxViews(CreatePlaceMixin, TestCase):
     # Basic tests to ensure that the views that serve AJAX return something

File templates/cciw/bookings/place_confirmed_email.txt

+{% load url from future %}{% autoescape off %}
+{% if payment_received %}Thank you for your payment{% endif %}
+The following place on a CCIW camp has been confirmed:
+
+Camper name: {{ booking.name }}
+Camp: {{ camp }}
+Dates: {{ camp.start_date|date:"d M Y" }} to {{ camp.end_date|date:"d M" }}
+
+The camp leaders will be in contact with more details in due time.
+
+Many thanks!
+
+The cciw.co.uk team.
+
+{% endautoescape %}

File templates/cciw/bookings/unrecognised_payment_email.txt

+{% load url from future %}{% autoescape off %}
+An unrecognised payment was received from Paypal. Please
+check it at:
+
+{{ url_start }}{% url 'admin:ipn_paypalipn_change' ipn_obj.id %}
+
+{% endautoescape %}
+