Merge branch 'chrisjrn/20161006'
This commit is contained in:
		
						commit
						dec0a759ca
					
				
					 5 changed files with 200 additions and 6 deletions
				
			
		|  | @ -76,7 +76,14 @@ class CartController(object): | ||||||
|         determine whether the cart has reserved the items and discounts it |         determine whether the cart has reserved the items and discounts it | ||||||
|         holds. ''' |         holds. ''' | ||||||
| 
 | 
 | ||||||
|         reservations = [datetime.timedelta()] |         time = timezone.now() | ||||||
|  | 
 | ||||||
|  |         # Calculate the residual of the _old_ reservation duration | ||||||
|  |         # if it's greater than what's in the cart now, keep it. | ||||||
|  |         time_elapsed_since_updated = (time - self.cart.time_last_updated) | ||||||
|  |         residual = self.cart.reservation_duration - time_elapsed_since_updated | ||||||
|  | 
 | ||||||
|  |         reservations = [datetime.timedelta(0), residual] | ||||||
| 
 | 
 | ||||||
|         # If we have vouchers, we're entitled to an hour at minimum. |         # If we have vouchers, we're entitled to an hour at minimum. | ||||||
|         if len(self.cart.vouchers.all()) >= 1: |         if len(self.cart.vouchers.all()) >= 1: | ||||||
|  | @ -90,7 +97,7 @@ class CartController(object): | ||||||
|         if product_max is not None: |         if product_max is not None: | ||||||
|             reservations.append(product_max) |             reservations.append(product_max) | ||||||
| 
 | 
 | ||||||
|         self.cart.time_last_updated = timezone.now() |         self.cart.time_last_updated = time | ||||||
|         self.cart.reservation_duration = max(reservations) |         self.cart.reservation_duration = max(reservations) | ||||||
| 
 | 
 | ||||||
|     def end_batch(self): |     def end_batch(self): | ||||||
|  | @ -117,6 +124,32 @@ class CartController(object): | ||||||
|         self.cart.revision += 1 |         self.cart.revision += 1 | ||||||
|         self.cart.save() |         self.cart.save() | ||||||
| 
 | 
 | ||||||
|  |     def extend_reservation(self, timedelta): | ||||||
|  |         ''' Extends the reservation on this cart by the given timedelta. | ||||||
|  |         This can only be done if the current state of the cart is valid (i.e | ||||||
|  |         all items and discounts in the cart are still available.) | ||||||
|  | 
 | ||||||
|  |         Arguments: | ||||||
|  |             timedelta (timedelta): The amount of time to extend the cart by. | ||||||
|  |                 The resulting reservation_duration will be now() + timedelta, | ||||||
|  |                 unless the requested extension is *LESS* than the current | ||||||
|  |                 reservation deadline. | ||||||
|  | 
 | ||||||
|  |         ''' | ||||||
|  | 
 | ||||||
|  |         self.validate_cart() | ||||||
|  |         cart = self.cart | ||||||
|  |         cart.refresh_from_db() | ||||||
|  | 
 | ||||||
|  |         elapsed = (timezone.now() - cart.time_last_updated) | ||||||
|  | 
 | ||||||
|  |         if cart.reservation_duration - elapsed > timedelta: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         cart.time_last_updated = timezone.now() | ||||||
|  |         cart.reservation_duration = timedelta | ||||||
|  |         cart.save() | ||||||
|  | 
 | ||||||
|     @_modifies_cart |     @_modifies_cart | ||||||
|     def set_quantities(self, product_quantities): |     def set_quantities(self, product_quantities): | ||||||
|         ''' Sets the quantities on each of the products on each of the |         ''' Sets the quantities on each of the products on each of the | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ from django.db.models import Case, When, Value | ||||||
| from django.db.models.fields.related import RelatedField | from django.db.models.fields.related import RelatedField | ||||||
| from django.shortcuts import render | from django.shortcuts import render | ||||||
| 
 | 
 | ||||||
|  | from registrasion.controllers.cart import CartController | ||||||
| from registrasion.controllers.item import ItemController | from registrasion.controllers.item import ItemController | ||||||
| from registrasion.models import commerce | from registrasion.models import commerce | ||||||
| from registrasion.models import people | from registrasion.models import people | ||||||
|  | @ -404,19 +405,32 @@ def attendee(request, form, user_id=None): | ||||||
|     reports = [] |     reports = [] | ||||||
| 
 | 
 | ||||||
|     profile_data = [] |     profile_data = [] | ||||||
|     profile = people.AttendeeProfileBase.objects.get_subclass( |     try: | ||||||
|         attendee=attendee |         profile = people.AttendeeProfileBase.objects.get_subclass( | ||||||
|     ) |             attendee=attendee | ||||||
|  |         ) | ||||||
|  |         fields = profile._meta.get_fields() | ||||||
|  |     except people.AttendeeProfileBase.DoesNotExist: | ||||||
|  |         fields = [] | ||||||
|  | 
 | ||||||
|     exclude = set(["attendeeprofilebase_ptr", "id"]) |     exclude = set(["attendeeprofilebase_ptr", "id"]) | ||||||
|     for field in profile._meta.get_fields(): |     for field in fields: | ||||||
|         if field.name in exclude: |         if field.name in exclude: | ||||||
|             # Not actually important |             # Not actually important | ||||||
|             continue |             continue | ||||||
|         if not hasattr(field, "verbose_name"): |         if not hasattr(field, "verbose_name"): | ||||||
|             continue  # Not a publicly visible field |             continue  # Not a publicly visible field | ||||||
|         value = getattr(profile, field.name) |         value = getattr(profile, field.name) | ||||||
|  | 
 | ||||||
|  |         if isinstance(field, models.ManyToManyField): | ||||||
|  |             value = ", ".join(str(i) for i in value.all()) | ||||||
|  | 
 | ||||||
|         profile_data.append((field.verbose_name, value)) |         profile_data.append((field.verbose_name, value)) | ||||||
| 
 | 
 | ||||||
|  |     cart = CartController.for_user(attendee.user) | ||||||
|  |     reservation = cart.cart.reservation_duration + cart.cart.time_last_updated | ||||||
|  |     profile_data.append(("Current cart reserved until", reservation)) | ||||||
|  | 
 | ||||||
|     reports.append(ListReport("Profile", ["", ""], profile_data)) |     reports.append(ListReport("Profile", ["", ""], profile_data)) | ||||||
| 
 | 
 | ||||||
|     links = [] |     links = [] | ||||||
|  | @ -424,6 +438,11 @@ def attendee(request, form, user_id=None): | ||||||
|         reverse(views.amend_registration, args=[user_id]), |         reverse(views.amend_registration, args=[user_id]), | ||||||
|         "Amend current cart", |         "Amend current cart", | ||||||
|     )) |     )) | ||||||
|  |     links.append(( | ||||||
|  |         reverse(views.extend_reservation, args=[user_id]), | ||||||
|  |         "Extend reservation", | ||||||
|  |     )) | ||||||
|  | 
 | ||||||
|     reports.append(Links("Actions for " + name, links)) |     reports.append(Links("Actions for " + name, links)) | ||||||
| 
 | 
 | ||||||
|     # Paid and pending  products |     # Paid and pending  products | ||||||
|  |  | ||||||
|  | @ -423,3 +423,130 @@ class BasicCartTests(RegistrationCartTestCase): | ||||||
|         self.assertEqual(0, count_1) |         self.assertEqual(0, count_1) | ||||||
|         self.assertEqual(0, count_2) |         self.assertEqual(0, count_2) | ||||||
|         self.assertEqual(1, count_3) |         self.assertEqual(1, count_3) | ||||||
|  | 
 | ||||||
|  |     def test_reservation_duration_forwards(self): | ||||||
|  |         ''' Reservation duration should be the maximum of the durations (small) | ||||||
|  |         ''' | ||||||
|  | 
 | ||||||
|  |         new_res = self.RESERVATION * 2 | ||||||
|  |         self.PROD_2.reservation_duration = new_res | ||||||
|  |         self.PROD_2.save() | ||||||
|  | 
 | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, self.RESERVATION) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_2, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res) | ||||||
|  | 
 | ||||||
|  |     def test_reservation_duration_backwards(self): | ||||||
|  |         ''' Reservation duration should be the maximum of the durations (big) | ||||||
|  |         ''' | ||||||
|  | 
 | ||||||
|  |         new_res = self.RESERVATION * 2 | ||||||
|  |         self.PROD_2.reservation_duration = new_res | ||||||
|  |         self.PROD_2.save() | ||||||
|  | 
 | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_2, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def test_reservation_duration_removals(self): | ||||||
|  |         ''' Reservation duration should update with removals | ||||||
|  |         ''' | ||||||
|  | 
 | ||||||
|  |         new_res = self.RESERVATION * 2 | ||||||
|  |         self.PROD_2.reservation_duration = new_res | ||||||
|  |         self.PROD_2.save() | ||||||
|  | 
 | ||||||
|  |         self.set_time(datetime.datetime(2015, 1, 1, tzinfo=UTC)) | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         one_third = new_res / 3 | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_2, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res) | ||||||
|  | 
 | ||||||
|  |         # Reservation duration should not decrease if time hasn't decreased | ||||||
|  |         cart.set_quantity(self.PROD_2, 0) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res) | ||||||
|  | 
 | ||||||
|  |         # Adding a new product should not reset the reservation duration below | ||||||
|  |         # the old one | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res) | ||||||
|  | 
 | ||||||
|  |         self.add_timedelta(one_third) | ||||||
|  | 
 | ||||||
|  |         # The old reservation duration is still longer than PROD_1's | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, new_res - one_third) | ||||||
|  | 
 | ||||||
|  |         self.add_timedelta(one_third) | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, self.RESERVATION) | ||||||
|  | 
 | ||||||
|  |     def test_reservation_extension_less_than_current(self): | ||||||
|  |         ''' Reservation extension should have no effect if it's too small | ||||||
|  |         ''' | ||||||
|  | 
 | ||||||
|  |         self.set_time(datetime.datetime(2015, 1, 1, tzinfo=UTC)) | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, self.RESERVATION) | ||||||
|  | 
 | ||||||
|  |         cart.extend_reservation(datetime.timedelta(minutes=30)) | ||||||
|  | 
 | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, self.RESERVATION) | ||||||
|  | 
 | ||||||
|  |     def test_reservation_extension(self): | ||||||
|  |         ''' Test various reservation extension bits. | ||||||
|  |         ''' | ||||||
|  | 
 | ||||||
|  |         self.set_time(datetime.datetime(2015, 1, 1, tzinfo=UTC)) | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, self.RESERVATION) | ||||||
|  | 
 | ||||||
|  |         hours = datetime.timedelta(hours=1) | ||||||
|  |         cart.extend_reservation(24 * hours) | ||||||
|  | 
 | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, 24 * hours) | ||||||
|  | 
 | ||||||
|  |         self.add_timedelta(1 * hours) | ||||||
|  | 
 | ||||||
|  |         # PROD_1's reservation is less than what we've added to the cart | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual(cart.cart.reservation_duration, 23 * hours) | ||||||
|  | 
 | ||||||
|  |         # Now the extension should only have 59 minutes remaining | ||||||
|  |         # so the autoextend behaviour should kick in | ||||||
|  |         self.add_timedelta(datetime.timedelta(hours=22, minutes=1)) | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  |         cart.cart.refresh_from_db() | ||||||
|  |         self.assertEqual( | ||||||
|  |             cart.cart.reservation_duration, | ||||||
|  |             self.PROD_1.reservation_duration, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ from .views import ( | ||||||
|     checkout, |     checkout, | ||||||
|     credit_note, |     credit_note, | ||||||
|     edit_profile, |     edit_profile, | ||||||
|  |     extend_reservation, | ||||||
|     guided_registration, |     guided_registration, | ||||||
|     invoice, |     invoice, | ||||||
|     invoice_access, |     invoice_access, | ||||||
|  | @ -24,6 +25,7 @@ public = [ | ||||||
|     url(r"^checkout$", checkout, name="checkout"), |     url(r"^checkout$", checkout, name="checkout"), | ||||||
|     url(r"^checkout/([0-9]+)$", checkout, name="checkout"), |     url(r"^checkout/([0-9]+)$", checkout, name="checkout"), | ||||||
|     url(r"^credit_note/([0-9]+)$", credit_note, name="credit_note"), |     url(r"^credit_note/([0-9]+)$", credit_note, name="credit_note"), | ||||||
|  |     url(r"^extend/([0-9]+)$", extend_reservation, name="extend_reservation"), | ||||||
|     url(r"^invoice/([0-9]+)$", invoice, name="invoice"), |     url(r"^invoice/([0-9]+)$", invoice, name="invoice"), | ||||||
|     url(r"^invoice/([0-9]+)/([A-Z0-9]+)$", invoice, name="invoice"), |     url(r"^invoice/([0-9]+)/([A-Z0-9]+)$", invoice, name="invoice"), | ||||||
|     url(r"^invoice/([0-9]+)/manual_payment$", |     url(r"^invoice/([0-9]+)/manual_payment$", | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | import datetime | ||||||
| import sys | import sys | ||||||
| import util | import util | ||||||
| 
 | 
 | ||||||
|  | @ -903,3 +904,15 @@ def amend_registration(request, user_id): | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return render(request, "registrasion/amend_registration.html", data) |     return render(request, "registrasion/amend_registration.html", data) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @user_passes_test(_staff_only) | ||||||
|  | def extend_reservation(request, user_id, days=7): | ||||||
|  |     ''' Allows staff to extend the reservation on a given user's cart. | ||||||
|  |     ''' | ||||||
|  | 
 | ||||||
|  |     user = User.objects.get(id=int(user_id)) | ||||||
|  |     cart = CartController.for_user(user) | ||||||
|  |     cart.extend_reservation(datetime.timedelta(days=days)) | ||||||
|  | 
 | ||||||
|  |     return redirect(request.META["HTTP_REFERER"]) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer