From 6c94eb9e91ee6f0d30c4dfd73d1edb0cf194a752 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 30 Mar 2016 15:13:50 +1100 Subject: [PATCH] Adds Registrasion support to the templates and apps. --- .gitignore | 3 + fixtures/registrasion.json | 393 ++++++++++++++++++ pinaxcon/registrasion/__init__.py | 1 + pinaxcon/registrasion/apps.py | 9 + pinaxcon/registrasion/forms.py | 10 + .../registrasion/migrations/0001_initial.py | 34 ++ .../migrations/0002_demopayment.py | 24 ++ pinaxcon/registrasion/migrations/__init__.py | 0 pinaxcon/registrasion/models.py | 82 ++++ pinaxcon/registrasion/urls.py | 8 + pinaxcon/registrasion/views.py | 47 +++ pinaxcon/settings.py | 21 +- pinaxcon/templates/dashboard.html | 74 ++++ pinaxcon/templates/registrasion/base.html | 5 + .../registrasion/checkout_errors.html | 25 ++ .../templates/registrasion/credit_note.html | 40 ++ .../templates/registrasion/discount_list.html | 7 + .../registrasion/guided_registration.html | 36 ++ .../guided_registration_complete.html | 35 ++ pinaxcon/templates/registrasion/invoice.html | 81 ++++ .../templates/registrasion/items_list.html | 7 + .../registrasion/manual_payment.html | 31 ++ .../templates/registrasion/payment_list.html | 16 + .../registrasion/product_category.html | 45 ++ .../templates/registrasion/profile_form.html | 24 ++ pinaxcon/urls.py | 7 + requirements.txt | 3 +- 27 files changed, 1060 insertions(+), 8 deletions(-) create mode 100644 fixtures/registrasion.json create mode 100644 pinaxcon/registrasion/__init__.py create mode 100644 pinaxcon/registrasion/apps.py create mode 100644 pinaxcon/registrasion/forms.py create mode 100644 pinaxcon/registrasion/migrations/0001_initial.py create mode 100644 pinaxcon/registrasion/migrations/0002_demopayment.py create mode 100644 pinaxcon/registrasion/migrations/__init__.py create mode 100644 pinaxcon/registrasion/models.py create mode 100644 pinaxcon/registrasion/urls.py create mode 100644 pinaxcon/registrasion/views.py create mode 100644 pinaxcon/templates/registrasion/base.html create mode 100644 pinaxcon/templates/registrasion/checkout_errors.html create mode 100644 pinaxcon/templates/registrasion/credit_note.html create mode 100644 pinaxcon/templates/registrasion/discount_list.html create mode 100644 pinaxcon/templates/registrasion/guided_registration.html create mode 100644 pinaxcon/templates/registrasion/guided_registration_complete.html create mode 100644 pinaxcon/templates/registrasion/invoice.html create mode 100644 pinaxcon/templates/registrasion/items_list.html create mode 100644 pinaxcon/templates/registrasion/manual_payment.html create mode 100644 pinaxcon/templates/registrasion/payment_list.html create mode 100644 pinaxcon/templates/registrasion/product_category.html create mode 100644 pinaxcon/templates/registrasion/profile_form.html diff --git a/.gitignore b/.gitignore index 9b7266b..7da754e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.pyc node_modules/ _build/ +.DS_Store +dev.db + diff --git a/fixtures/registrasion.json b/fixtures/registrasion.json new file mode 100644 index 0000000..e42d080 --- /dev/null +++ b/fixtures/registrasion.json @@ -0,0 +1,393 @@ +[ +{ + "model": "registrasion.category", + "pk": 1, + "fields": { + "name": "Ticket", + "description": "Admission to the conference", + "limit_per_user": 1, + "required": false, + "order": 1, + "render_type": 1 + } +}, +{ + "model": "registrasion.category", + "pk": 2, + "fields": { + "name": "Dinner Ticket", + "description": "Tickets for the conference dinner", + "limit_per_user": null, + "required": false, + "order": 10, + "render_type": 2 + } +}, +{ + "model": "registrasion.category", + "pk": 3, + "fields": { + "name": "T-shirt", + "description": "The conference t-shirt", + "limit_per_user": null, + "required": false, + "order": 30, + "render_type": 2 + } +}, +{ + "model": "registrasion.category", + "pk": 4, + "fields": { + "name": "Accommodation", + "description": "Accommodation in the student dormitories", + "limit_per_user": null, + "required": false, + "order": 40, + "render_type": 2 + } +}, +{ + "model": "registrasion.category", + "pk": 5, + "fields": { + "name": "Extra accommodation nights", + "description": "Extra nights at the student dorms. You will need to purchase the week block before you can add extra nights", + "limit_per_user": null, + "required": false, + "order": 50, + "render_type": 2 + } +}, +{ + "model": "registrasion.product", + "pk": 1, + "fields": { + "name": "Professional", + "description": "", + "category": 1, + "price": "1000.00", + "limit_per_user": null, + "reservation_duration": "1 00:00:00", + "order": 10 + } +}, +{ + "model": "registrasion.product", + "pk": 2, + "fields": { + "name": "Enthusiast", + "description": "", + "category": 1, + "price": "450.00", + "limit_per_user": null, + "reservation_duration": "1 00:00:00", + "order": 20 + } +}, +{ + "model": "registrasion.product", + "pk": 3, + "fields": { + "name": "Student", + "description": "", + "category": 1, + "price": "160.00", + "limit_per_user": null, + "reservation_duration": "1 00:00:00", + "order": 30 + } +}, +{ + "model": "registrasion.product", + "pk": 4, + "fields": { + "name": "Adult", + "description": "Includes a full meal and drinks", + "category": 2, + "price": "85.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 10 + } +}, +{ + "model": "registrasion.product", + "pk": 5, + "fields": { + "name": "Child", + "description": "Includes a children's meal and soft drinks", + "category": 2, + "price": "40.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 20 + } +}, +{ + "model": "registrasion.product", + "pk": 6, + "fields": { + "name": "Infant", + "description": "A seat for an infant in lap", + "category": 2, + "price": "0.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 30 + } +}, +{ + "model": "registrasion.product", + "pk": 7, + "fields": { + "name": "S", + "description": "", + "category": 3, + "price": "25.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 10 + } +}, +{ + "model": "registrasion.product", + "pk": 8, + "fields": { + "name": "M", + "description": "", + "category": 3, + "price": "25.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 20 + } +}, +{ + "model": "registrasion.product", + "pk": 9, + "fields": { + "name": "L", + "description": "", + "category": 3, + "price": "25.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 30 + } +}, +{ + "model": "registrasion.product", + "pk": 10, + "fields": { + "name": "Sunday 19-Saturday 25", + "description": "6 nights", + "category": 4, + "price": "480.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 1 + } +}, +{ + "model": "registrasion.product", + "pk": 11, + "fields": { + "name": "Friday 17", + "description": "", + "category": 5, + "price": "80.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 10 + } +}, +{ + "model": "registrasion.product", + "pk": 12, + "fields": { + "name": "Saturday 18", + "description": "", + "category": 5, + "price": "80.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 20 + } +}, +{ + "model": "registrasion.product", + "pk": 13, + "fields": { + "name": "Saturday 25", + "description": "", + "category": 5, + "price": "80.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 30 + } +}, +{ + "model": "registrasion.product", + "pk": 14, + "fields": { + "name": "Sunday 26", + "description": "", + "category": 5, + "price": "80.00", + "limit_per_user": null, + "reservation_duration": "01:00:00", + "order": 40 + } +}, +{ + "model": "registrasion.voucher", + "pk": 1, + "fields": { + "recipient": "FooCorp", + "code": "FOOCORP", + "limit": 10 + } +}, +{ + "model": "registrasion.discountbase", + "pk": 1, + "fields": { + "description": "Early bird discount" + } +}, +{ + "model": "registrasion.discountbase", + "pk": 2, + "fields": { + "description": "FooCorp complimentary sponsor ticket" + } +}, +{ + "model": "registrasion.discountbase", + "pk": 3, + "fields": { + "description": "1 Dinner Ticket included with Professional Tickets" + } +}, +{ + "model": "registrasion.discountbase", + "pk": 4, + "fields": { + "description": "1 T-Shirt included with all tickets" + } +}, +{ + "model": "registrasion.discountforproduct", + "pk": 1, + "fields": { + "discount": 1, + "product": 1, + "percentage": null, + "price": "200.00", + "quantity": 1 + } +}, +{ + "model": "registrasion.discountforproduct", + "pk": 2, + "fields": { + "discount": 1, + "product": 2, + "percentage": null, + "price": "100.00", + "quantity": 1 + } +}, +{ + "model": "registrasion.discountforproduct", + "pk": 3, + "fields": { + "discount": 2, + "product": 1, + "percentage": "100.0", + "price": null, + "quantity": 1 + } +}, +{ + "model": "registrasion.discountforcategory", + "pk": 1, + "fields": { + "discount": 3, + "category": 2, + "percentage": "100.0", + "quantity": 1 + } +}, +{ + "model": "registrasion.discountforcategory", + "pk": 2, + "fields": { + "discount": 4, + "category": 3, + "percentage": "100.0", + "quantity": 1 + } +}, +{ + "model": "registrasion.timeorstocklimitdiscount", + "pk": 1, + "fields": { + "start_time": null, + "end_time": null, + "limit": 10 + } +}, +{ + "model": "registrasion.voucherdiscount", + "pk": 2, + "fields": { + "voucher": 1 + } +}, +{ + "model": "registrasion.includedproductdiscount", + "pk": 3, + "fields": { + "enabling_products": [ + 1 + ] + } +}, +{ + "model": "registrasion.includedproductdiscount", + "pk": 4, + "fields": { + "enabling_products": [ + 1, + 2, + 3 + ] + } +}, +{ + "model": "registrasion.enablingconditionbase", + "pk": 1, + "fields": { + "description": "Extra accommodation depends on Accommodation", + "condition": 2, + "products": [], + "categories": [ + 5 + ] + } +}, +{ + "model": "registrasion.categoryflag", + "pk": 1, + "fields": { + "enabling_category": 4, + "products": [], + "categories": [ + 5 + ] + } +} +] diff --git a/pinaxcon/registrasion/__init__.py b/pinaxcon/registrasion/__init__.py new file mode 100644 index 0000000..1a3225d --- /dev/null +++ b/pinaxcon/registrasion/__init__.py @@ -0,0 +1 @@ +default_app_config = "pinaxcon.registrasion.apps.RegistrasionConfig" diff --git a/pinaxcon/registrasion/apps.py b/pinaxcon/registrasion/apps.py new file mode 100644 index 0000000..40580c3 --- /dev/null +++ b/pinaxcon/registrasion/apps.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class RegistrasionConfig(AppConfig): + name = "pinaxcon.registrasion" + label = "pinaxcon_registrasion" + verbose_name = _("Pinaxcon Registrasion") diff --git a/pinaxcon/registrasion/forms.py b/pinaxcon/registrasion/forms.py new file mode 100644 index 0000000..8cf785f --- /dev/null +++ b/pinaxcon/registrasion/forms.py @@ -0,0 +1,10 @@ +import models + +from django import forms + +class ProfileForm(forms.ModelForm): + ''' A form for requesting badge and profile information. ''' + + class Meta: + model = models.AttendeeProfile + exclude = ['attendee'] diff --git a/pinaxcon/registrasion/migrations/0001_initial.py b/pinaxcon/registrasion/migrations/0001_initial.py new file mode 100644 index 0000000..4e83ece --- /dev/null +++ b/pinaxcon/registrasion/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-04-01 10:05 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('registrasion', '0011_auto_20160401_0943'), + ] + + operations = [ + migrations.CreateModel( + name='AttendeeProfile', + fields=[ + ('attendeeprofilebase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.AttendeeProfileBase')), + ('name', models.CharField(help_text=b"Your name, as you'd like it to appear on your badge. ", max_length=64, verbose_name=b'Your name (for your conference nametag)')), + ('company', models.CharField(blank=True, help_text=b"The name of your company, as you'd like it on your badge", max_length=64)), + ('free_text_1', models.CharField(blank=True, help_text=b"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=b'Free text line 1')), + ('free_text_2', models.CharField(blank=True, max_length=64, verbose_name=b'Free text line 2')), + ('name_per_invoice', models.CharField(blank=True, help_text=b"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=b'Your legal name (for invoicing purposes)')), + ('of_legal_age', models.BooleanField(default=False, verbose_name=b'18+?')), + ('dietary_requirements', models.CharField(blank=True, max_length=256)), + ('accessibility_requirements', models.CharField(blank=True, max_length=256)), + ('gender', models.CharField(blank=True, max_length=64)), + ], + bases=('registrasion.attendeeprofilebase',), + ), + ] diff --git a/pinaxcon/registrasion/migrations/0002_demopayment.py b/pinaxcon/registrasion/migrations/0002_demopayment.py new file mode 100644 index 0000000..ccff847 --- /dev/null +++ b/pinaxcon/registrasion/migrations/0002_demopayment.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-04-07 07:41 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('registrasion', '0013_auto_20160406_2228_squashed_0015_auto_20160406_1942'), + ('pinaxcon_registrasion', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DemoPayment', + fields=[ + ('paymentbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.PaymentBase')), + ], + bases=('registrasion.paymentbase',), + ), + ] diff --git a/pinaxcon/registrasion/migrations/__init__.py b/pinaxcon/registrasion/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pinaxcon/registrasion/models.py b/pinaxcon/registrasion/models.py new file mode 100644 index 0000000..392b69d --- /dev/null +++ b/pinaxcon/registrasion/models.py @@ -0,0 +1,82 @@ +from django.db import models +from registrasion import models as rego + +class AttendeeProfile(rego.AttendeeProfileBase): + + @classmethod + def name_field(cls): + ''' This is used to pre-fill the attendee's name from the + speaker profile. If it's None, that functionality is disabled. ''' + return "name" + + def invoice_recipient(self): + if self.company: + base = "%(name_per_invoice)s c/- %(company)s" + else: + base = "%(name_per_invoice)s" + return base % self.__dict__ + + def save(self): + if not self.name_per_invoice: + self.name_per_invoice = self.name + super(AttendeeProfile, self).save() + + # 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, + ) + + +class DemoPayment(rego.PaymentBase): + ''' A subclass of PaymentBase for use in our demo payments function. ''' + + pass # No custom features here, but yours could be here. diff --git a/pinaxcon/registrasion/urls.py b/pinaxcon/registrasion/urls.py new file mode 100644 index 0000000..e609e52 --- /dev/null +++ b/pinaxcon/registrasion/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls import url, patterns + +import views + +urlpatterns = patterns( + "pinaxcon.registrasion.views", + url(r"^demopay/([0-9]+)/([A-Z0-9]+)$", views.demopay, name="demopay"), +) diff --git a/pinaxcon/registrasion/views.py b/pinaxcon/registrasion/views.py new file mode 100644 index 0000000..636707c --- /dev/null +++ b/pinaxcon/registrasion/views.py @@ -0,0 +1,47 @@ +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError +from django.http import Http404 +from django.shortcuts import get_object_or_404 +from django.shortcuts import redirect +from django.shortcuts import render + +from registrasion import models as rego +from registrasion.controllers.invoice import InvoiceController + +import models + + +def demopay(request, invoice_id, access_code): + ''' Marks the invoice with the given invoice id as paid. + ''' + invoice_id = int(invoice_id) + inv = get_object_or_404(rego.Invoice.objects,pk=invoice_id) + + invoice = InvoiceController(inv) + + if not invoice.can_view(user=request.user, access_code=access_code): + raise Http404() + + to_invoice = redirect("invoice", invoice.invoice.id, access_code) + + try: + invoice.validate_allowed_to_pay() # Verify that we're allowed to do this. + except ValidationError as ve: + messages.error(request, ve.message) + return to_invoice + + # Create the payment object + models.DemoPayment.objects.create( + invoice=invoice.invoice, + reference="Demo payment by user: " + request.user.username, + amount=invoice.invoice.value, + ) + + invoice.update_status() + + messages.success(request, "This invoice was successfully paid.") + + return to_invoice diff --git a/pinaxcon/settings.py b/pinaxcon/settings.py index b2d9a2c..c157ca3 100644 --- a/pinaxcon/settings.py +++ b/pinaxcon/settings.py @@ -9,13 +9,13 @@ BASE_DIR = PACKAGE_ROOT DEBUG = bool(int(os.environ.get("DEBUG", "1"))) DATABASES = { - "default": dj_database_url.config(default="postgres://localhost/pinaxcon") + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(PROJECT_ROOT, "dev.db"), + } } -ALLOWED_HOSTS = [ - os.environ.get("GONDOR_INSTANCE_DOMAIN"), - "conference.pinaxproject.com" -] +ALLOWED_HOSTS = [] # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name @@ -153,9 +153,16 @@ INSTALLED_APPS = [ "symposion.sponsorship", "symposion.teams", + # Registrasion + "registrasion", + + #admin - required by registrasion ?? + "nested_admin", + # project "pinaxcon", - "pinaxcon.proposals" + "pinaxcon.proposals", + "pinaxcon.registrasion", ] # A sample logging configuration. The only tangible logging @@ -212,3 +219,5 @@ PROPOSAL_FORMS = { } PINAX_PAGES_HOOKSET = "pinaxcon.hooks.PinaxPagesHookSet" PINAX_BOXES_HOOKSET = "pinaxcon.hooks.PinaxBoxesHookSet" + +ATTENDEE_PROFILE_FORM = "pinaxcon.registrasion.forms.ProfileForm" diff --git a/pinaxcon/templates/dashboard.html b/pinaxcon/templates/dashboard.html index 9e4f2de..350c9d6 100644 --- a/pinaxcon/templates/dashboard.html +++ b/pinaxcon/templates/dashboard.html @@ -4,6 +4,7 @@ {% load proposal_tags %} {% load review_tags %} {% load teams_tags %} +{% load registrasion_tags %} {% block head_title %}Dashboard{% endblock %} @@ -93,6 +94,79 @@ {% endif %} +
+
+
+ {% if not user.attendee.completed_registration %} + + Register for the conference + + {% else %} + + Edit your attendee profile + + + {% items_pending as pending %} + {% if pending %} + + Pay your registration + + {% endif %} + {% endif %} +
+

+ + {% trans "Registration" %} +

+ +
+ +
+ {% if not user.attendee.completed_registration %} +

To attend the conference, you must purchase a ticket. Use our registration form to purchase your ticket. + {% else %} +

Your registration

+ {% items_pending as pending %} + {% if pending %} +
Items pending payment
+ {% include "registrasion/items_list.html" with items=pending %} + {% endif %} + {% items_purchased as purchased %} + {% if purchased %} +
Paid items
+ {% include "registrasion/items_list.html" with items=purchased %} + {% endif %} +
Add/Update items
+ {% available_categories as categories %} + {% for category in categories %} +
  • {{ category.name }}
  • + {% endfor %} + + + {% invoices as invoices %} + {% if invoices %} +
    Invoices
    +
      + {% for invoice in invoices %} + {% if not invoice.is_void %} +
    • + Invoice {{ invoice.id }} + - ${{ invoice.value }} ({{ invoice.get_status_display }}) +
    • + {% endif %} + {% endfor %} +
    + {% endif %} + + {% available_credit as credit %} + {% if credit %} +

    You have ${{ credit }} leftover from refunded invoices. Contact the conference organisers + to put this toward other purchases, or to refund it.

    + {% endif %} + {% endif %} +
    +
    +
    diff --git a/pinaxcon/templates/registrasion/base.html b/pinaxcon/templates/registrasion/base.html new file mode 100644 index 0000000..d6a6615 --- /dev/null +++ b/pinaxcon/templates/registrasion/base.html @@ -0,0 +1,5 @@ +{% extends "site_base.html" %} + +{% block body_outer %} + {% block body %}{% endblock %} +{% endblock %} diff --git a/pinaxcon/templates/registrasion/checkout_errors.html b/pinaxcon/templates/registrasion/checkout_errors.html new file mode 100644 index 0000000..5a1bcd4 --- /dev/null +++ b/pinaxcon/templates/registrasion/checkout_errors.html @@ -0,0 +1,25 @@ +{% extends "registrasion/base.html" %} +{% load bootstrap %} +{% load registrasion_tags %} + +{% block body %} + +

    Oh No!

    + +

    We can't produce an invoice for you because of the following errors:

    + +
      + {% for error in error_list %} +
    • {{ error.message }}
    • + {% endfor %} +
    + +

    We can automatically try to remove products and vouchers that aren't available anymore, + or you can return to the dashboard and try to fix it yourself.

    + + + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/credit_note.html b/pinaxcon/templates/registrasion/credit_note.html new file mode 100644 index 0000000..5b90615 --- /dev/null +++ b/pinaxcon/templates/registrasion/credit_note.html @@ -0,0 +1,40 @@ +{% extends "site_base.html" %} +{% load bootstrap %} +{% load registrasion_tags %} +{% block body %} + +

    Credit Note

    + +{% with note_user=credit_note.invoice.user %} +
      +
    • Number: {{ credit_note.id }} +
    • Attention: {{ credit_note.invoice.user.attendee.attendeeprofilebase.invoice_recipient }}
    • +
    • Value: {{ credit_note.value }}
    • +
    • Status: {{ credit_note.status }}
    • +
    +{% endwith %} + +

    This credit note was generated from funds excess from invoice {{ credit_note.invoice.id }}.

    + +{% if credit_note.is_unclaimed %} +
    + {% csrf_token %} +

    Apply to invoice

    +

    You can apply this credit note to an unpaid invoice.

    + + {{ apply_form|bootstrap }} +
    + +
    +

    Manual refund

    +

    You can mark this credit note as refunded, and handle the refund manually. +

    + + {{ refund_form|bootstrap }} +
    + +
    +
    +{% endif %} + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/discount_list.html b/pinaxcon/templates/registrasion/discount_list.html new file mode 100644 index 0000000..577c081 --- /dev/null +++ b/pinaxcon/templates/registrasion/discount_list.html @@ -0,0 +1,7 @@ +{% if discounts %} +
      + {% for discount in discounts %} +
    • {{ discount.quantity }} × {{ discount.clause }}
    • + {% endfor %} +
    +{% endif %} diff --git a/pinaxcon/templates/registrasion/guided_registration.html b/pinaxcon/templates/registrasion/guided_registration.html new file mode 100644 index 0000000..24e22c3 --- /dev/null +++ b/pinaxcon/templates/registrasion/guided_registration.html @@ -0,0 +1,36 @@ +{% extends "registrasion/base.html" %} +{% load bootstrap %} + +{% block body %} + +

    Conference Registration – {{ title }}

    + +

    Step {{ current_step }} of {{ total_steps|add:1 }}

    + +
    + {% csrf_token %} + + {% for section in sections %} +

    {{ section.title }}

    + + {% if section.description %} +

    {{ section.description }}

    + {% endif %} + + {% if section.discounts %} +

    You have the following discounts available to you:

    + {% include "registrasion/discount_list.html" with discounts=section.discounts %} + {% endif %} + +
    + {{ section.form|bootstrap }} +
    + {% endfor %} + +
    + +
    +
    + + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/guided_registration_complete.html b/pinaxcon/templates/registrasion/guided_registration_complete.html new file mode 100644 index 0000000..2683957 --- /dev/null +++ b/pinaxcon/templates/registrasion/guided_registration_complete.html @@ -0,0 +1,35 @@ +{% extends "registrasion/base.html" %} +{% load bootstrap %} +{% load registrasion_tags %} + +{% block body %} + +

    Conference Registration – Review

    + + {% items_pending as pending %} + {% if pending %} + +

    Step 4 of 4

    + +

    You're almost done! You've selected the following items:

    + {% include "registrasion/items_list.html" with items=pending %} + +

    You can either generate an invoice and pay for your registration, or return to + the dashboard to make amendments.

    + + + + {% else %} + +

    You have no items that need to be paid.

    + + + + {% endif %} + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/invoice.html b/pinaxcon/templates/registrasion/invoice.html new file mode 100644 index 0000000..4e8612a --- /dev/null +++ b/pinaxcon/templates/registrasion/invoice.html @@ -0,0 +1,81 @@ +{% extends "site_base.html" %} +{% load bootstrap %} +{% load registrasion_tags %} +{% block body %} + + +{% if invoice.is_unpaid %} +

    NOTICE: The below invoice is automatically generated, and will be voided + if you amend your registration before payment, or if discounts or products contained in the + invoice become unavailable. The items and discounts are only reserved until + the invoice due time.

    + + {% url "invoice_access" invoice.user.attendee.access_code as access_url %} +

    Your most recent unpaid invoice will be available at + {{ request.scheme }}://{{ request.get_host }}{{ access_url }} + You can give this URL to your accounts department to pay your registration.

    + +
    + Pay this invoice + {% if user.is_staff %} + Apply manual payment + {% endif %} +
    +{% elif invoice.is_paid %} + {% if user.is_staff %} +
    + {% if user.is_staff %} + Apply manual payment/refund + {% endif %} +
    + {% endif %} +{% endif %} + +
    + +

    Invoice

    + +{% with invoice_user=invoice.cart.user %} +
      +
    • Invoice number: {{ invoice.id }} +
    • Invoice status: {{ invoice.get_status_display }}
    • +
    • Issue date: {{ invoice.issue_time|date:"DATE_FORMAT" }} + {% if not invoice.is_void %} +
    • Due: {{ invoice.due_time|date:"DATETIME_FORMAT"}}
    • + {% endif %} +
    • Attention: {{ invoice_user.attendee.attendeeprofilebase.invoice_recipient }}
    • +
    +{% endwith %} + +

    This invoice has been issued as a result of an application to attend (conference name).

    + + + + + + + + + + {% for line_item in invoice.lineitem_set.all %} + + + + + + + {% endfor %} + + + + + + +
    DescriptionQuantityPrice/UnitTotal
    {{ line_item.description }}{{ line_item.quantity }}${{ line_item.price }}${{ line_item.price|multiply:line_item.quantity }}
    TOTAL${{ invoice.value }}
    + +{% if invoice.paymentbase_set.all %} +

    Payments received

    +{% include "registrasion/payment_list.html" with payments=invoice.paymentbase_set.all %} +{% endif %} + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/items_list.html b/pinaxcon/templates/registrasion/items_list.html new file mode 100644 index 0000000..1a21424 --- /dev/null +++ b/pinaxcon/templates/registrasion/items_list.html @@ -0,0 +1,7 @@ +{% if items %} +
      + {% for item in items %} +
    • {{ item.quantity }} × {{ item.product }}
    • + {% endfor %} +
    +{% endif %} diff --git a/pinaxcon/templates/registrasion/manual_payment.html b/pinaxcon/templates/registrasion/manual_payment.html new file mode 100644 index 0000000..cdfef2c --- /dev/null +++ b/pinaxcon/templates/registrasion/manual_payment.html @@ -0,0 +1,31 @@ +{% extends "registrasion/base.html" %} +{% load bootstrap %} + +{% block body %} + +

    Invoice {{ invoice.id }} - {{ invoice.get_status_display }}

    + +

    Past payments

    + + {% include "registrasion/payment_list.html" with payments=invoice.paymentbase_set.all %} + +

    Apply manual payment

    + +

    Enter a reference and the amount of the payment. A refund is a negative + payment.

    + +
    + {% csrf_token %} + + + {{ form|bootstrap }} +
    + + +
    + + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/payment_list.html b/pinaxcon/templates/registrasion/payment_list.html new file mode 100644 index 0000000..7c2edbc --- /dev/null +++ b/pinaxcon/templates/registrasion/payment_list.html @@ -0,0 +1,16 @@ +{% if payments %} + + + + + + + {% for payment in payments %} + + + + + + {% endfor %} +
    Payment timeReferenceAmount
    {{payment.time}}{{payment.reference}}{{payment.amount}}
    +{% endif %} diff --git a/pinaxcon/templates/registrasion/product_category.html b/pinaxcon/templates/registrasion/product_category.html new file mode 100644 index 0000000..85c5a89 --- /dev/null +++ b/pinaxcon/templates/registrasion/product_category.html @@ -0,0 +1,45 @@ +{% extends "registrasion/base.html" %} +{% load bootstrap %} +{% load registrasion_tags %} +{% block body %} + +

    Product Category: {{ category.name }}

    + +
    + {% csrf_token %} + + + {{ voucher_form | bootstrap }} +
    + +
    + +
    + + {% items_purchased category as items %} + {% if items %} +

    Paid items

    +

    You have already paid for the following items:

    + {% include "registrasion/items_list.html" with items=items %} + {% endif %} + + + {% if discounts %} +

    Available Discounts

    + {% include "registrasion/discount_list.html" with discounts=discounts %} + {% endif %} + +

    Available Products

    +

    {{ category.description }}

    + + {{ form | bootstrap }} +
    + +
    + +
    + +
    + + +{% endblock %} diff --git a/pinaxcon/templates/registrasion/profile_form.html b/pinaxcon/templates/registrasion/profile_form.html new file mode 100644 index 0000000..103c2ad --- /dev/null +++ b/pinaxcon/templates/registrasion/profile_form.html @@ -0,0 +1,24 @@ +{% extends "registrasion/base.html" %} +{% load bootstrap %} + +{% block body %} + +

    Your profile

    + +

    These details will appear on your badge, your invoices, and will be used + to order catered food at the conference.

    + +
    + {% csrf_token %} + + + {{ form|bootstrap }} +
    + +
    + +
    +
    + + +{% endblock %} diff --git a/pinaxcon/urls.py b/pinaxcon/urls.py index 6797428..87de23e 100644 --- a/pinaxcon/urls.py +++ b/pinaxcon/urls.py @@ -26,6 +26,13 @@ urlpatterns = [ url(r"^boxes/", include("pinax.boxes.urls")), url(r"^", include("pinax.pages.urls")), + + # Required by registrasion + url(r'^register/', include('registrasion.urls')), + url(r'^nested_admin/', include('nested_admin.urls')), + + # Demo payment gateway and related features + url(r"^register/pinaxcon/", include("pinaxcon.registrasion.urls")), ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/requirements.txt b/requirements.txt index 89d2987..79344c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,10 +5,9 @@ metron==1.3.7 pinax-eventlog==1.1.1 dj-static==0.0.6 dj-database-url==0.4.0 -gunicorn==19.4.5 -psycopg2==2.6.1 pinax-pages==0.4.2 pinax-boxes==2.1.2 # run off of master for now git+https://github.com/pinax/symposion.git +git+https://github.com/chrisjrn/registrasion.git@demo_site_integration