Merge branch 'guided_registration'
This commit is contained in:
commit
4069d4bb32
17 changed files with 453 additions and 30 deletions
|
@ -211,6 +211,7 @@ class CartController(object):
|
|||
# Get the count of past uses of this discount condition
|
||||
# as this affects the total amount we're allowed to use now.
|
||||
past_uses = rego.DiscountItem.objects.filter(
|
||||
cart__user=self.cart.user,
|
||||
discount=discount.discount,
|
||||
)
|
||||
agg = past_uses.aggregate(Sum("quantity"))
|
||||
|
|
|
@ -122,7 +122,7 @@ class InvoiceController(object):
|
|||
)
|
||||
payment.save()
|
||||
|
||||
payments = rego.Payment.objects .filter(invoice=self.invoice)
|
||||
payments = rego.Payment.objects.filter(invoice=self.invoice)
|
||||
agg = payments.aggregate(Sum("amount"))
|
||||
total = agg["amount__sum"]
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import models as rego
|
||||
|
||||
from controllers.product import ProductController
|
||||
|
||||
from django import forms
|
||||
|
||||
|
||||
|
@ -34,6 +36,14 @@ def CategoryForm(category):
|
|||
''' Removes a given product from this form. '''
|
||||
del self.fields[field_name(product)]
|
||||
|
||||
def disable_products_for_user(self, user):
|
||||
for product in products:
|
||||
# Remove fields that do not have an enabling condition.
|
||||
prod = ProductController(product)
|
||||
if not prod.can_add_with_enabling_conditions(user, 0):
|
||||
self.disable_product(product)
|
||||
|
||||
|
||||
products = rego.Product.objects.filter(category=category).order_by("order")
|
||||
for product in products:
|
||||
|
||||
|
@ -47,9 +57,18 @@ def CategoryForm(category):
|
|||
|
||||
return _CategoryForm
|
||||
|
||||
|
||||
class ProfileForm(forms.ModelForm):
|
||||
''' A form for requesting badge and profile information. '''
|
||||
|
||||
class Meta:
|
||||
model = rego.BadgeAndProfile
|
||||
exclude = ['attendee']
|
||||
|
||||
|
||||
class VoucherForm(forms.Form):
|
||||
voucher = forms.CharField(
|
||||
label="Voucher code",
|
||||
help_text="If you have a voucher code, enter it here",
|
||||
required=True,
|
||||
required=False,
|
||||
)
|
||||
|
|
54
registrasion/migrations/0002_auto_20160323_2029.py
Normal file
54
registrasion/migrations/0002_auto_20160323_2029.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registrasion', '0001_squashed_0002_auto_20160304_1723'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='accessibility_requirements',
|
||||
field=models.CharField(max_length=256, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='dietary_requirements',
|
||||
field=models.CharField(max_length=256, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='free_text_1',
|
||||
field=models.CharField(help_text="A line of free text that will appear on your badge. Use this for your Twitter handle, IRC nick, your preferred pronouns or anything else you'd like people to see on your badge.", max_length=64, verbose_name='Free text line 1', blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='free_text_2',
|
||||
field=models.CharField(max_length=64, verbose_name='Free text line 2', blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='gender',
|
||||
field=models.CharField(max_length=64, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='of_legal_age',
|
||||
field=models.BooleanField(default=False, verbose_name='18+?'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='badge',
|
||||
name='company',
|
||||
field=models.CharField(help_text="The name of your company, as you'd like it on your badge", max_length=64, blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='badge',
|
||||
name='name',
|
||||
field=models.CharField(help_text="Your name, as you'd like it on your badge", max_length=64),
|
||||
),
|
||||
]
|
24
registrasion/migrations/0003_auto_20160323_2044.py
Normal file
24
registrasion/migrations/0003_auto_20160323_2044.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registrasion', '0002_auto_20160323_2029'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='badge',
|
||||
name='name_per_invoice',
|
||||
field=models.CharField(help_text="If your legal name is different to the name on your badge, fill this in, and we'll put it on your invoice. Otherwise, leave it blank.", max_length=64, verbose_name='Your legal name (for invoicing purposes)', blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='badge',
|
||||
name='name',
|
||||
field=models.CharField(help_text="Your name, as you'd like it to appear on your badge. ", max_length=64, verbose_name='Your name (for your conference nametag)'),
|
||||
),
|
||||
]
|
55
registrasion/migrations/0004_auto_20160323_2137.py
Normal file
55
registrasion/migrations/0004_auto_20160323_2137.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('registrasion', '0003_auto_20160323_2044'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Attendee',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('completed_registration', models.BooleanField(default=False)),
|
||||
('highest_complete_category', models.IntegerField(default=0)),
|
||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BadgeAndProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(help_text="Your name, as you'd like it to appear on your badge. ", max_length=64, verbose_name='Your name (for your conference nametag)')),
|
||||
('company', models.CharField(help_text="The name of your company, as you'd like it on your badge", max_length=64, blank=True)),
|
||||
('free_text_1', models.CharField(help_text="A line of free text that will appear on your badge. Use this for your Twitter handle, IRC nick, your preferred pronouns or anything else you'd like people to see on your badge.", max_length=64, verbose_name='Free text line 1', blank=True)),
|
||||
('free_text_2', models.CharField(max_length=64, verbose_name='Free text line 2', blank=True)),
|
||||
('name_per_invoice', models.CharField(help_text="If your legal name is different to the name on your badge, fill this in, and we'll put it on your invoice. Otherwise, leave it blank.", max_length=64, verbose_name='Your legal name (for invoicing purposes)', blank=True)),
|
||||
('of_legal_age', models.BooleanField(default=False, verbose_name='18+?')),
|
||||
('dietary_requirements', models.CharField(max_length=256, blank=True)),
|
||||
('accessibility_requirements', models.CharField(max_length=256, blank=True)),
|
||||
('gender', models.CharField(max_length=64, blank=True)),
|
||||
('profile', models.OneToOneField(to='registrasion.Attendee')),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='badge',
|
||||
name='profile',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='profile',
|
||||
name='user',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Badge',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Profile',
|
||||
),
|
||||
]
|
19
registrasion/migrations/0005_auto_20160323_2141.py
Normal file
19
registrasion/migrations/0005_auto_20160323_2141.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registrasion', '0004_auto_20160323_2137'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='badgeandprofile',
|
||||
old_name='profile',
|
||||
new_name='attendee',
|
||||
),
|
||||
]
|
20
registrasion/migrations/0006_category_required.py
Normal file
20
registrasion/migrations/0006_category_required.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('registrasion', '0005_auto_20160323_2141'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='required',
|
||||
field=models.BooleanField(default=False),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
|||
import datetime
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models import F, Q
|
||||
|
@ -15,29 +16,100 @@ from model_utils.managers import InheritanceManager
|
|||
# User models
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Profile(models.Model):
|
||||
class Attendee(models.Model):
|
||||
''' Miscellaneous user-related data. '''
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % self.user
|
||||
|
||||
@staticmethod
|
||||
def get_instance(user):
|
||||
''' Returns the instance of attendee for the given user, or creates
|
||||
a new one. '''
|
||||
attendees = Attendee.objects.filter(user=user)
|
||||
if len(attendees) > 0:
|
||||
return attendees[0]
|
||||
else:
|
||||
attendee = Attendee(user=user)
|
||||
attendee.save()
|
||||
return attendee
|
||||
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
# Badge is linked
|
||||
# Badge/profile is linked
|
||||
completed_registration = models.BooleanField(default=False)
|
||||
highest_complete_category = models.IntegerField(default=0)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Badge(models.Model):
|
||||
''' Information for an attendee's badge. '''
|
||||
class BadgeAndProfile(models.Model):
|
||||
''' Information for an attendee's badge and related preferences '''
|
||||
|
||||
def __str__(self):
|
||||
return "Badge for: %s of %s" % (self.name, self.company)
|
||||
|
||||
profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
|
||||
@staticmethod
|
||||
def get_instance(attendee):
|
||||
''' Returns either None, or the instance that belongs
|
||||
to this attendee. '''
|
||||
try:
|
||||
return BadgeAndProfile.objects.get(attendee=attendee)
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
|
||||
name = models.CharField(max_length=256)
|
||||
company = models.CharField(max_length=256)
|
||||
attendee = models.OneToOneField(Attendee, on_delete=models.CASCADE)
|
||||
|
||||
# Things that appear on badge
|
||||
name = models.CharField(
|
||||
verbose_name="Your name (for your conference nametag)",
|
||||
max_length=64,
|
||||
help_text="Your name, as you'd like it to appear on your badge. ",
|
||||
)
|
||||
company = models.CharField(
|
||||
max_length=64,
|
||||
help_text="The name of your company, as you'd like it on your badge",
|
||||
blank=True,
|
||||
)
|
||||
free_text_1 = models.CharField(
|
||||
max_length=64,
|
||||
verbose_name="Free text line 1",
|
||||
help_text="A line of free text that will appear on your badge. Use "
|
||||
"this for your Twitter handle, IRC nick, your preferred "
|
||||
"pronouns or anything else you'd like people to see on "
|
||||
"your badge.",
|
||||
blank=True,
|
||||
)
|
||||
free_text_2 = models.CharField(
|
||||
max_length=64,
|
||||
verbose_name="Free text line 2",
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# Other important Information
|
||||
name_per_invoice = models.CharField(
|
||||
verbose_name="Your legal name (for invoicing purposes)",
|
||||
max_length=64,
|
||||
help_text="If your legal name is different to the name on your badge, "
|
||||
"fill this in, and we'll put it on your invoice. Otherwise, "
|
||||
"leave it blank.",
|
||||
blank=True,
|
||||
)
|
||||
of_legal_age = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="18+?",
|
||||
blank=True,
|
||||
)
|
||||
dietary_requirements = models.CharField(
|
||||
max_length=256,
|
||||
blank=True,
|
||||
)
|
||||
accessibility_requirements = models.CharField(
|
||||
max_length=256,
|
||||
blank=True,
|
||||
)
|
||||
gender = models.CharField(
|
||||
max_length=64,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
|
||||
# Inventory Models
|
||||
|
@ -60,6 +132,7 @@ class Category(models.Model):
|
|||
name = models.CharField(max_length=65, verbose_name=_("Name"))
|
||||
description = models.CharField(max_length=255,
|
||||
verbose_name=_("Description"))
|
||||
required = models.BooleanField(blank=True)
|
||||
order = models.PositiveIntegerField(verbose_name=("Display order"))
|
||||
render_type = models.IntegerField(choices=CATEGORY_RENDER_TYPES,
|
||||
verbose_name=_("Render type"))
|
||||
|
|
|
@ -33,5 +33,19 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Payment time</th>
|
||||
<th>Reference</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
{% for payment in invoice.payment_set.all %}
|
||||
<tr>
|
||||
<td>{{payment.time}}</td>
|
||||
<td>{{payment.reference}}</td>
|
||||
<td>{{payment.amount}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
|
22
registrasion/templates/profile_form.html
Normal file
22
registrasion/templates/profile_form.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
<!--- Sample template. Move elsewhere once it's ready to go. -->
|
||||
|
||||
{% extends "site_base.html" %}
|
||||
{% block body %}
|
||||
|
||||
<h1>Attendee Profile</h1>
|
||||
|
||||
<p>Something something fill in your attendee details here!</p>
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
|
||||
<table>
|
||||
{{ form }}
|
||||
</table>
|
||||
|
||||
<input type="submit">
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
{% endblock %}
|
0
registrasion/templatetags/__init__.py
Normal file
0
registrasion/templatetags/__init__.py
Normal file
10
registrasion/templatetags/registrasion_tags.py
Normal file
10
registrasion/templatetags/registrasion_tags.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from registrasion import models as rego
|
||||
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.assignment_tag(takes_context=True)
|
||||
def available_categories(context):
|
||||
''' Returns all of the available product categories '''
|
||||
return rego.Category.objects.all()
|
|
@ -37,6 +37,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
|||
description="This is a test category",
|
||||
order=10,
|
||||
render_type=rego.Category.RENDER_TYPE_RADIO,
|
||||
required=False,
|
||||
)
|
||||
cls.CAT_1.save()
|
||||
|
||||
|
@ -45,6 +46,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
|||
description="This is a test category",
|
||||
order=10,
|
||||
render_type=rego.Category.RENDER_TYPE_RADIO,
|
||||
required=False,
|
||||
)
|
||||
cls.CAT_2.save()
|
||||
|
||||
|
|
|
@ -200,3 +200,17 @@ class DiscountTestCase(RegistrationCartTestCase):
|
|||
# There is one discount, and it should apply to the more expensive.
|
||||
self.assertEqual(1, len(discount_items))
|
||||
self.assertEqual(self.PROD_3, discount_items[0].product)
|
||||
|
||||
def test_discount_quantity_is_per_user(self):
|
||||
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
||||
|
||||
# Both users should be able to apply the same discount
|
||||
# in the same way
|
||||
for user in (self.USER_1, self.USER_2):
|
||||
cart = CartController.for_user(user)
|
||||
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
||||
cart.add_to_cart(self.PROD_3, 1)
|
||||
|
||||
discount_items = list(cart.cart.discountitem_set.all())
|
||||
# The discount is applied.
|
||||
self.assertEqual(1, len(discount_items))
|
||||
|
|
|
@ -6,4 +6,7 @@ urlpatterns = patterns(
|
|||
url(r"^checkout$", "checkout", name="checkout"),
|
||||
url(r"^invoice/([0-9]+)$", "invoice", name="invoice"),
|
||||
url(r"^invoice/([0-9]+)/pay$", "pay_invoice", name="pay_invoice"),
|
||||
url(r"^profile$", "edit_profile", name="profile"),
|
||||
url(r"^register$", "guided_registration", name="guided_registration"),
|
||||
url(r"^register/([0-9]+)$", "guided_registration", name="guided_registration"),
|
||||
)
|
||||
|
|
|
@ -12,9 +12,81 @@ from django.shortcuts import redirect
|
|||
from django.shortcuts import render
|
||||
|
||||
|
||||
@login_required
|
||||
def guided_registration(request, page_id=0):
|
||||
''' Goes through the registration process in order,
|
||||
making sure user sees all valid categories.
|
||||
|
||||
WORK IN PROGRESS: the finalised version of this view will allow
|
||||
grouping of categories into a specific page. Currently, it just goes
|
||||
through each category one by one
|
||||
'''
|
||||
|
||||
dashboard = redirect("dashboard")
|
||||
next_step = redirect("guided_registration")
|
||||
|
||||
attendee = rego.Attendee.get_instance(request.user)
|
||||
if attendee.completed_registration:
|
||||
return dashboard
|
||||
|
||||
# Step 1: Fill in a badge
|
||||
profile = rego.BadgeAndProfile.get_instance(attendee)
|
||||
|
||||
if profile is None:
|
||||
ret = edit_profile(request)
|
||||
profile_new = rego.BadgeAndProfile.get_instance(attendee)
|
||||
if profile_new is None:
|
||||
# No new profile was created
|
||||
return ret
|
||||
else:
|
||||
return next_step
|
||||
|
||||
# Step 2: Go through each of the categories in order
|
||||
category = attendee.highest_complete_category
|
||||
|
||||
# Get the next category
|
||||
cats = rego.Category.objects
|
||||
cats = cats.filter(id__gt=category).order_by("order")
|
||||
|
||||
if len(cats) == 0:
|
||||
# We've filled in every category
|
||||
attendee.completed_registration = True
|
||||
attendee.save()
|
||||
return dashboard
|
||||
|
||||
ret = product_category(request, cats[0].id)
|
||||
attendee_new = rego.Attendee.get_instance(request.user)
|
||||
if attendee_new.highest_complete_category == category:
|
||||
# We've not yet completed this category
|
||||
return ret
|
||||
else:
|
||||
return next_step
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_profile(request):
|
||||
attendee = rego.Attendee.get_instance(request.user)
|
||||
|
||||
try:
|
||||
profile = rego.BadgeAndProfile.objects.get(attendee=attendee)
|
||||
except ObjectDoesNotExist:
|
||||
profile = None
|
||||
|
||||
form = forms.ProfileForm(request.POST or None, instance=profile)
|
||||
|
||||
if request.POST and form.is_valid():
|
||||
form.instance.attendee = attendee
|
||||
form.save()
|
||||
|
||||
data = {
|
||||
"form": form,
|
||||
}
|
||||
return render(request, "profile_form.html", data)
|
||||
|
||||
@login_required
|
||||
def product_category(request, category_id):
|
||||
''' Registration selections form for a specific category of items '''
|
||||
''' Registration selections form for a specific category of items.
|
||||
'''
|
||||
|
||||
PRODUCTS_FORM_PREFIX = "products"
|
||||
VOUCHERS_FORM_PREFIX = "vouchers"
|
||||
|
@ -25,14 +97,17 @@ def product_category(request, category_id):
|
|||
|
||||
CategoryForm = forms.CategoryForm(category)
|
||||
|
||||
attendee = rego.Attendee.get_instance(request.user)
|
||||
|
||||
products = rego.Product.objects.filter(category=category)
|
||||
products = products.order_by("order")
|
||||
|
||||
if request.method == "POST":
|
||||
cat_form = CategoryForm(request.POST, request.FILES, prefix=PRODUCTS_FORM_PREFIX)
|
||||
cat_form.disable_products_for_user(request.user)
|
||||
voucher_form = forms.VoucherForm(request.POST, prefix=VOUCHERS_FORM_PREFIX)
|
||||
|
||||
if voucher_form.is_valid():
|
||||
if voucher_form.is_valid() and voucher_form.cleaned_data["voucher"].strip():
|
||||
# Apply voucher
|
||||
# leave
|
||||
voucher = voucher_form.cleaned_data["voucher"]
|
||||
|
@ -40,23 +115,35 @@ def product_category(request, category_id):
|
|||
current_cart.apply_voucher(voucher)
|
||||
except Exception as e:
|
||||
voucher_form.add_error("voucher", e)
|
||||
# Re-visit current page.
|
||||
elif cat_form.is_valid():
|
||||
try:
|
||||
with transaction.atomic():
|
||||
for product_id, quantity, field_name \
|
||||
in cat_form.product_quantities():
|
||||
product = rego.Product.objects.get(pk=product_id)
|
||||
try:
|
||||
current_cart.set_quantity(
|
||||
product, quantity, batched=True)
|
||||
except ValidationError as ve:
|
||||
cat_form.add_error(field_name, ve)
|
||||
if cat_form.errors:
|
||||
raise ValidationError("Cannot add that stuff")
|
||||
current_cart.end_batch()
|
||||
handle_valid_cat_form(cat_form, current_cart)
|
||||
except ValidationError as ve:
|
||||
pass
|
||||
|
||||
# If category is required, the user must have at least one
|
||||
# in an active+valid cart
|
||||
|
||||
if category.required:
|
||||
carts = rego.Cart.reserved_carts()
|
||||
carts = carts.filter(user=request.user)
|
||||
items = rego.ProductItem.objects.filter(
|
||||
product__category=category,
|
||||
cart=carts,
|
||||
)
|
||||
if len(items) == 0:
|
||||
cat_form.add_error(
|
||||
None,
|
||||
"You must have at least one item from this category",
|
||||
)
|
||||
|
||||
if not cat_form.errors:
|
||||
if category_id > attendee.highest_complete_category:
|
||||
attendee.highest_complete_category = category_id
|
||||
attendee.save()
|
||||
return redirect("dashboard")
|
||||
|
||||
else:
|
||||
# Create initial data for each of products in category
|
||||
items = rego.ProductItem.objects.filter(
|
||||
|
@ -75,15 +162,10 @@ def product_category(request, category_id):
|
|||
|
||||
initial = CategoryForm.initial_data(quantities)
|
||||
cat_form = CategoryForm(prefix=PRODUCTS_FORM_PREFIX, initial=initial)
|
||||
cat_form.disable_products_for_user(request.user)
|
||||
|
||||
voucher_form = forms.VoucherForm(prefix=VOUCHERS_FORM_PREFIX)
|
||||
|
||||
for product in products:
|
||||
# Remove fields that do not have an enabling condition.
|
||||
prod = ProductController(product)
|
||||
if not prod.can_add_with_enabling_conditions(request.user, 0):
|
||||
cat_form.disable_product(product)
|
||||
|
||||
|
||||
data = {
|
||||
"category": category,
|
||||
|
@ -93,6 +175,17 @@ def product_category(request, category_id):
|
|||
|
||||
return render(request, "product_category.html", data)
|
||||
|
||||
@transaction.atomic
|
||||
def handle_valid_cat_form(cat_form, current_cart):
|
||||
for product_id, quantity, field_name in cat_form.product_quantities():
|
||||
product = rego.Product.objects.get(pk=product_id)
|
||||
try:
|
||||
current_cart.set_quantity(product, quantity, batched=True)
|
||||
except ValidationError as ve:
|
||||
cat_form.add_error(field_name, ve)
|
||||
if cat_form.errors:
|
||||
raise ValidationError("Cannot add that stuff")
|
||||
current_cart.end_batch()
|
||||
|
||||
@login_required
|
||||
def checkout(request):
|
||||
|
@ -129,7 +222,7 @@ def pay_invoice(request, invoice_id):
|
|||
invoice_id = int(invoice_id)
|
||||
inv = rego.Invoice.objects.get(pk=invoice_id)
|
||||
current_invoice = InvoiceController(inv)
|
||||
if not inv.paid and not current_invoice.is_valid():
|
||||
if not inv.paid and current_invoice.is_valid():
|
||||
current_invoice.pay("Demo invoice payment", inv.value)
|
||||
|
||||
return redirect("invoice", current_invoice.invoice.id)
|
||||
|
|
Loading…
Reference in a new issue