Merge branch 'chrisjrn/20161006'

This commit is contained in:
Christopher Neugebauer 2016-10-06 12:50:33 -07:00
commit dec0a759ca
5 changed files with 200 additions and 6 deletions

View file

@ -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

View file

@ -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

View file

@ -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,
)

View file

@ -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$",

View file

@ -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"])