Luke Plant avatar Luke Plant committed 9e66ab9

Added email sent to leaders for late bookings

Comments (0)

Files changed (4)

cciw/bookings/email.py

-from datetime import date
+from datetime import date, datetime
 
 from django.conf import settings
 from django.contrib.sites.models import get_current_site
 from django.utils.crypto import constant_time_compare, salted_hmac
 from django.utils.http import int_to_base36, base36_to_int
 
+from cciw.officers.email import admin_emails_for_camp
+
+
+LATE_BOOKING_THRESHOLD = 30 # days
 
 class EmailVerifyTokenGenerator(object):
     """
     subject = u"CCIW booking - place confirmed"
     mail.send_mail(subject, body, settings.SERVER_EMAIL, [account.email])
 
+    # Email leaders. Bookings could be for different camps, so send different
+    # emails.
+
+    # We don't care about timezones, or about accuracy better than 1 day,
+    # so use naive UTC datetimes, not aware datetimes.
+    today = datetime.utcnow().date()
+
+    for booking in bookings:
+        if (booking.camp.start_date - today).days < LATE_BOOKING_THRESHOLD:
+
+            c = {
+                'account': account,
+                'booking': booking,
+                'camp': booking.camp,
+                'url_start': site_address_url_start(),
+                }
+            body = loader.render_to_string('cciw/bookings/late_place_confirmed_email.txt', c)
+            subject = u"CCIW late booking: %s" % booking.name
+
+            mail.send_mail(subject, body, settings.SERVER_EMAIL,
+                           admin_emails_for_camp(booking.camp))
+
 
 def send_booking_expiry_mail(account, bookings, expired):
     if account.email == '':

cciw/bookings/models.py

             self.amount_due = self.expected_amount_due()
 
     def age_on_camp(self):
-        # Age is calculated based on shool years, i.e. age on 31st August
+        # Age is calculated based on school years, i.e. age on 31st August
         return relativedelta(date(self.camp.year, 8, 31), self.date_of_birth)
 
     def get_booking_problems(self, booking_sec=False):

cciw/bookings/tests.py

 from decimal import Decimal
 import re
 
+from django.contrib.auth.models import User
 from django.core import mail
 from django.core.urlresolvers import reverse
 from django.test import TestCase
 from cciw.bookings.models import PRICE_FULL, PRICE_2ND_CHILD, PRICE_3RD_CHILD, PRICE_CUSTOM, PRICE_SOUTH_WALES_TRANSPORT, PRICE_DEPOSIT, BOOKING_APPROVED, BOOKING_INFO_COMPLETE, BOOKING_BOOKED, BOOKING_CANCELLED, BOOKING_CANCELLED_FULL_REFUND, BOOKING_CANCELLED_HALF_REFUND
 from cciw.bookings.utils import camp_bookings_to_spreadsheet
 from cciw.cciwmain.common import get_thisyear
-from cciw.cciwmain.models import Camp
+from cciw.cciwmain.models import Camp, Person
 from cciw.cciwmain.tests.mailhelpers import read_email_url
 from cciw.officers.tests.references import OFFICER_USERNAME, OFFICER_PASSWORD, BOOKING_SEC_USERNAME, BOOKING_SEC_PASSWORD, BOOKING_SEC
 from cciw.sitecontent.models import HtmlChunk
                                         site_id=1)
 
 
+class CreateLeadersMixin(object):
+    def create_leaders(self):
+        self.leader_1 = Person.objects.create(name="Mr Leader")
+        self.leader_2 = Person.objects.create(name="Mrs Leaderess")
+
+        self.leader_1_user = User.objects.create(username="leader1",
+                                            email="leader1@mail.com")
+        self.leader_2_user = User.objects.create(username="leader2",
+                                            email="leader2@mail.com")
+
+        self.leader_1.users.add(self.leader_1_user)
+        self.leader_2.users.add(self.leader_2_user)
+
+        self.camp.leaders.add(self.leader_1)
+        self.camp.leaders.add(self.leader_2)
+
+
 class CreatePricesMixin(object):
     def add_prices(self):
         year = get_thisyear()
         self.assertEqual(resp.status_code, 200)
 
 
-class TestPaymentReceived(CreatePlaceMixin, TestCase):
+class TestPaymentReceived(CreatePlaceMixin, CreateLeadersMixin, TestCase):
 
     fixtures = ['basic.json']
 
     def test_receive_payment(self):
         self.login()
         self.create_place()
+        self.create_leaders()
         acc = self.get_account()
         book_basket_now(acc.bookings.basket(self.camp.year))
         self.assertTrue(acc.bookings.all()[0].booking_expires is not None)
 
+        mail.outbox = []
         p = Price.objects.get(year=get_thisyear(), price_type=PRICE_FULL).price
         acc.receive_payment(p)
 
         # Check we updated the bookings
         self.assertTrue(acc.bookings.all()[0].booking_expires is None)
 
+        # Check for emails sent
+        # 1 to account
+        self.assertEqual(len([m for m in mail.outbox if m.to == [self.email]]), 1)
+
+        # This is a late booking, therefore there is also:
+        # 1 to camp leaders altogether
+        self.assertEqual(len([m for m in mail.outbox
+                              if sorted(m.to) == sorted([self.leader_1_user.email,
+                                                         self.leader_2_user.email])]),
+                         1)
+
+
     def test_insufficient_receive_payment(self):
         self.login()
         self.create_place()
                                          payment_status = 'completed',
                                          )
         mail.outbox = []
-        self.assertEqual(len(mail.outbox), 0)
         paypal_payment_received(ipn_1)
 
         # Since payments are processed in a separate process, we cannot

templates/cciw/bookings/late_place_confirmed_email.txt

+{% load url from future %}{% autoescape off %}
+A booking has been received for camp {{ camp }}
+
+Name: {{ booking.name }}
+Age: {{ booking.age_on_camp.years }} years
+Sex: {{ booking.get_sex_display }}
+
+Please see the spreadsheet for more information:
+
+{{ url_start }}{% url 'cciw.officers.views.export_camper_data' year=camp.year number=camp.number %}
+
+You were sent an email about this because it was a late
+booking - within 30 days of the start of camp.
+
+Thanks,
+
+The cciw.co.uk team.
+
+{% endautoescape %}
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.