Created
October 11, 2017 14:28
-
-
Save neilbradley/e9e7c61fb39f6b8d55b2bc17822f3935 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django import forms | |
from django.contrib.auth.models import User | |
from django.forms.util import ErrorList | |
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned | |
from django.contrib.auth import authenticate, login | |
from django.utils.safestring import mark_safe | |
from django.forms.models import modelformset_factory | |
from captcha.fields import CaptchaField | |
from django.forms.extras.widgets import SelectDateWidget | |
from django.utils.translation import ugettext_lazy as _ | |
import datetime | |
from countrysites.settings import settings as countrysettings | |
from models import * | |
from basket import Basket | |
import re | |
import widgets | |
default_choice = [('', '----------')] | |
SubCategoryFormSet = modelformset_factory(SubCategory) | |
ProductCategoryFormSet = modelformset_factory(ProductCategory) | |
class SAIDForm(forms.Form): | |
id_number = forms.RegexField(regex='(((\d{2}((0[13578]|1[02])(0[1-9]|[12]\d|3[01])|(0[13456789]|1[012])(0[1-9]|[12]\d|30)|02(0[1-9]|1\d|2[0-8])))|([02468][048]|[13579][26])0229))(( |-)(\d{4})( |-)(\d{3})|(\d{7}))') | |
class DiscountForm(forms.Form): | |
discount = forms.CharField(max_length=30, required=False, label="Promotional Code") | |
def clean_discount(self): | |
discount = self.cleaned_data['discount'].strip() | |
return discount | |
class SalesReport(forms.Form): | |
# payment statuses - this should probably be migrated to the db | |
STATUS_CHOICES = ( | |
(-1, 'all'), | |
(0, 'Refunded'), | |
(1, 'Voided'), | |
(2, 'Pending'), | |
(3, 'Failed'), | |
(4, 'Authorised'), | |
(5, 'Packing'), | |
(6, 'Dispatched'), | |
(7, 'Complete'), | |
) | |
QUERY_TYPE = ( | |
('yearly', 'Yearly'), | |
('monthly', 'Monthly'), | |
('weekly', 'Weekly'), | |
('daily', 'Daily'), | |
) | |
RESULT_TYPE = ( | |
('onscreen', 'On Screen'), | |
('csv', 'CSV (for excell)'), | |
) | |
# , input_formats='%d/%m/%y' | |
start_date = forms.DateField(label="start date", input_formats=('%d/%m/%y',),initial=datetime.date.today().replace(day=1).strftime('%d/%m/%y'),required=True) | |
end_date = forms.DateField(label="end date", input_formats=('%d/%m/%y',), initial=datetime.date.today().strftime('%d/%m/%y'),required=True) | |
status = forms.ChoiceField(choices=STATUS_CHOICES, label="Select orders by status", initial=7) | |
#query_type = forms.ChoiceField(choices=QUERY_TYPE, label="Results by", initial='daily') | |
result_type = forms.ChoiceField(choices=RESULT_TYPE, label="Result type", initial='onscreen') | |
class ProductCategoryForm(forms.ModelForm): | |
""" | |
A form for the relationship between products and their parent categories | |
""" | |
class Meta: | |
model = ProductCategory | |
class ProductForm(forms.ModelForm): | |
""" | |
A form for the editing of a shop product | |
""" | |
class Meta: | |
model = Product | |
class CategoryForm(forms.ModelForm): | |
""" | |
A form for the editing of a shop category | |
""" | |
class Meta: | |
model = Category | |
class RegisterAccountForm(forms.ModelForm): | |
""" | |
A form with most of the fields needed for registering an account. | |
Note that this form is different to the UpdateAccountForm form because | |
it doesn't allow the ability to change addresses (purely because the user | |
won't yet have any at registration point). Also assumes that the customer | |
must agree to some terms and conditions at registration. | |
""" | |
# tsandcs = forms.BooleanField(help_text="I agree to the Terms and Conditions below",error_messages={'required': 'You must agree to the Terms and Conditions'}) | |
#captcha = CaptchaField(label="Verification Code") | |
class Meta: | |
model = Account | |
exclude = ('user','default_address','temporary','dob','gender','malinglist','informed','security_question') | |
''' class QuickPayAccountForm(forms.ModelForm): | |
""" """ | |
A form with most of the fields needed for registering an account ergo just | |
for the sake of ergo. | |
Note that this form is different to the UpdateAccountForm form because | |
it doesn't allow the ability to change addresses (purely because the user | |
won't yet have any at registration point). Also assumes that the customer | |
must agree to some terms and conditions at registration. | |
""" """ | |
# tsandcs = forms.BooleanField(help_text="I agree to the Terms and Conditions below",error_messages={'required': 'You must agree to the Terms and Conditions'}) | |
#captcha = CaptchaField(label="Verification Code") | |
class Meta: | |
model = Account | |
exclude = ('user','default_address','temporary','security_question','security_answer','feedback') | |
class UpdateAccountForm(forms.ModelForm): | |
""" """ | |
Partnering RegisterAccountForm, this version allows the editing of both | |
address fields and insists that the 'instance' keyword is supplied to the | |
constructor, indicating an update. Builds the list of address choices | |
dynamically, from those assigned to the related account. | |
""" """ | |
class Meta: | |
model = Account | |
exclude = ('user','temporary') | |
def __init__(self, *args, **kwargs): | |
""" """ | |
Set the address choices for this account to be those that already exist | |
against the account | |
""" """ | |
super(UpdateAccountForm, self).__init__(*args, **kwargs) | |
if not 'instance' in kwargs: | |
raise TypeError("UpdateAccountForm requires keyword paramter 'instance'") | |
address_choices = [(address.id, str(address)) for address in kwargs['instance'].address_set.all()] | |
self.fields['default_address'].choices = address_choices | |
class LoginForm(forms.Form): | |
""" """ | |
A helper form for logging in customers. This form actually performs a log in | |
when clean is called, assuming the email and password combination is valid. | |
""" """ | |
email = forms.EmailField() | |
password = forms.CharField(widget=forms.PasswordInput,min_length=5) | |
def __init__(self, request, data=None, files=None, auto_id='id_%s', prefix=None, | |
initial=None, error_class=ErrorList, label_suffix=':', | |
empty_permitted=False): | |
""" """ | |
Save a reference to the request object on this instance | |
""" """ | |
super(LoginForm, self).__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted) | |
self.request = request | |
def clean(self): | |
""" """ | |
Process the log in request. If both email and password data has been | |
entered, this function first tries to retrieve a matching account | |
object from the database which is both persistent and non-staff. If | |
successful, the username is passed through the django authenticate | |
mechanism to check whether the username and password is correct. | |
""" """ | |
cleaned_data = self.cleaned_data | |
email = cleaned_data.get('email') | |
password = cleaned_data.get('password') | |
if email and password: | |
try: | |
user = Account.objects.get(user__email=email, user__is_staff=False, temporary=False).user | |
except ObjectDoesNotExist: | |
raise forms.ValidationError("No customer exists with that email address") | |
except MultipleObjectsReturned: | |
raise forms.ValidationError("Unfortunately, there seems to be an issue. We have two accounts with that email address and cannot log you in! " | |
"This should not happen and is likely a bug with our software. Please get in touch and notify us of this issue.") | |
user = authenticate(username=user.username, password=password) | |
if user is not None: | |
if user.is_active: | |
login(self.request, user) | |
else: | |
# Possibly email admin here to notify of attempted access to locked account | |
raise forms.ValidationError("Your account has been temporarily locked.") | |
else: | |
raise forms.ValidationError("The password for that account is incorrect") | |
self.user = user | |
return cleaned_data | |
''' | |
class DiscountCodeForm(forms.Form): | |
""" | |
A form that allows the customer to enter a discount code on their basket. | |
The clean method implements the logic that inserts the discount on to the | |
basket, if the offer code validates | |
""" | |
discount_code = forms.CharField(label=_("Discount Code:"), required=False) | |
''' def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, | |
initial=None, error_class=ErrorList, label_suffix=':', | |
empty_permitted=False): | |
""" | |
If the customer has already set a discount code, then populate it's | |
initial value correctly on the form | |
""" | |
super(DiscountCodeForm, self).__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted) | |
#discount = basket.discount | |
#if discount: | |
# self.fields['discount_code'].initial = discount.code | |
def clean(self): | |
""" | |
Validate that the discount code being passed to this form exists in | |
the database. | |
""" | |
cleaned_data = self.cleaned_data | |
#discount_code = cleaned_data.get('discount_code') | |
#if discount_code: | |
# try: | |
# offer = Offer.objects.get(public=True,code=discount_code) | |
# except ObjectDoesNotExist, MultipleObjectsReturned: | |
# self._errors["discount_code"] = ErrorList([u"Discount code not found"]) | |
#return '' #cleaned_data ''' | |
class BasketRowForm(forms.Form): | |
""" | |
A form that allows quantities to be updated per row on the basket. This | |
allows for the change of quantities and the deletion of the row entirely | |
via the checkbox | |
""" | |
operate = forms.BooleanField(required=False) | |
quantity = forms.IntegerField(required=False,min_value=0) | |
def __init__(self, basket_item, data=None, files=None, auto_id='id_%s', initial=None, | |
error_class=ErrorList, label_suffix=':', empty_permitted=False): | |
""" | |
Change the initial value of this row's quantity field to be the value | |
in the basket row. | |
""" | |
prefix = basket_item.item.id | |
super(BasketRowForm, self).__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted) | |
self.fields['quantity'].initial = basket_item.quantity | |
''' | |
class PasswordResetForm(forms.Form): | |
""" | |
This form provides validation for the second step of the password reset | |
process. It validates valid passwords and that the answer is valid for the | |
given user's question | |
""" | |
answer = forms.CharField() | |
new_password = forms.CharField(widget=forms.PasswordInput,min_length=5) | |
confirm_password = forms.CharField(widget=forms.PasswordInput,min_length=5) | |
def __init__(self, password_request, data=None, files=None, auto_id='id_%s', prefix=None, | |
initial=None, error_class=ErrorList, label_suffix=':', | |
empty_permitted=False): | |
""" | |
Set the label of the answer field to be the question that the customer | |
selected and store a reference to the password request object in this | |
instance | |
""" | |
super(PasswordResetForm, self).__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted) | |
self.fields['answer'].label = str(password_request.account.security_question) | |
self.password_request = password_request | |
def clean(self): | |
""" | |
Ensure that the new passwords being chosen match and that the answer | |
is correct for this customer's chosen question. Also ensure that the | |
associated password request is still in-date and valid | |
""" | |
cleaned_data = self.cleaned_data | |
new_password = cleaned_data.get("new_password") | |
confirm_password = cleaned_data.get("confirm_password") | |
if new_password != confirm_password: | |
self._errors["new_password"] = ErrorList([u"Your passwords did not match"]) | |
answer = cleaned_data.get('answer') | |
if answer: | |
if str(answer).lower() != str(self.password_request.account.security_answer).lower(): | |
self._errors["answer"] = ErrorList([u"This answer is incorrect"]) | |
if not self.password_request.is_valid(): | |
raise forms.ValidationError("This password reset request is no longer valid.") | |
return cleaned_data | |
class ForgottenPasswordForm(forms.Form): | |
""" | |
The first step in the forgotten password process. Requests that you enter | |
your email address and complete a captcha. On success, an email should be | |
sent out with a link to the second stage of this process | |
""" | |
email = forms.EmailField() | |
#captcha = CaptchaField(label="Verification Code") | |
def clean(self): | |
""" | |
Provide validation that the email being entered exists in the database | |
for a persistent store account | |
""" | |
cleaned_data = self.cleaned_data | |
email = cleaned_data.get('email') | |
if email: | |
try: | |
user = Account.objects.get(user__email=email, user__is_staff=False, temporary=False) | |
except ObjectDoesNotExist: | |
raise forms.ValidationError("No customer exists with that email address") | |
except MultipleObjectsReturned: | |
raise forms.ValidationError("Unfortunately, there seems to be an issue. We have two accounts with that email address and cannot retreive a password! " | |
"This should not happen and is likely a bug with our software. Please get in touch and notify us of this issue.") | |
return cleaned_data | |
''' | |
class AddressForm(forms.ModelForm): | |
""" | |
Add new addresses. It is assumed that when adding an address, the customer | |
will be logged in, so we don't want, or need, them to choose the related | |
account. This will come from session data. | |
""" | |
country = forms.ModelChoiceField(queryset=Country.objects,widget=widgets.SelectCountry) | |
class Meta: | |
model = Address | |
exclude = ('account',) | |
def __init__(self, *args, **kwargs): | |
super(AddressForm, self).__init__(*args, **kwargs) | |
self.fields['country'].initial = 1 | |
if countrysettings.country == 'me': | |
self.fields['county'].label = 'State' | |
self.fields['postcode'].required = False | |
else: | |
self.fields['postcode'].required = True | |
class AddressForm_me(forms.ModelForm): | |
""" | |
Add new addresses. It is assumed that when adding an address, the customer | |
will be logged in, so we don't want, or need, them to choose the related | |
account. This will come from session data. | |
""" | |
country = forms.ModelChoiceField(queryset=Country.objects,widget=widgets.SelectCountry) | |
class Meta: | |
model = Address_me | |
exclude = ('account',) | |
def __init__(self, *args, **kwargs): | |
super(AddressForm_me, self).__init__(*args, **kwargs) | |
self.fields['country'].initial = 1 | |
''' | |
class DeliveryOptions(forms.Form): | |
""" | |
Choose your delivery method! Also provides an extra information box if the | |
customer wishes to provide extra delivery instructions | |
""" | |
method = forms.ChoiceField(widget=forms.RadioSelect(),error_messages={'required': 'Please select a shipping option.'}) | |
information = forms.CharField(widget=forms.Textarea(),required=False) | |
def has_choices(self): | |
""" | |
Return True if the number of shipping methods available to the customer | |
is at least one. False otherwise. | |
""" | |
return len(self.fields['method'].choices) > 0 | |
def num_choices(self): | |
""" | |
Return the number of shipping methods available in the customers chosen | |
region | |
""" | |
return len(self.fields['method'].choices) | |
def method_choices(self): | |
""" | |
Return the choices available for the shipping method, this will be | |
returned as a list of 2-item tuples | |
""" | |
return self.fields['method'].choices | |
def __init__(self, account, basket, data=None, files=None, auto_id='id_%s', prefix=None, | |
initial=None, error_class=ErrorList, label_suffix=':', | |
empty_permitted=False): | |
""" | |
If the customer has already chosen a delivery method then set the method | |
default to that chosen method, also, pre-populate the information field | |
if the customer has already specified some delivery instructions | |
""" | |
super(DeliveryOptions, self).__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted) | |
if basket.delivery_address: | |
bands = basket.delivery_address.country.zone.band_set.all() | |
self.fields['method'].choices = ((band.postage.id, mark_safe(band.display_line(account))) for band in bands) | |
if basket.postage: | |
self.fields['method'].initial = basket.postage.id | |
if basket.delivery_instructions: | |
self.fields['information'].initial = basket.delivery_instructions | |
class AddressSelectForm(forms.ModelForm): | |
""" | |
Select the address you want the item shipping to. It is assumed that when | |
adding an address, the customer will be logged in, so we don't want, or | |
need, them to choose the related account. This will come from session data. | |
""" | |
existing_address = forms.ChoiceField(label="Existing addresses", widget=forms.RadioSelect(), required=False) | |
class Meta: | |
model = Address | |
exclude = ('account',) | |
def __init__(self, *args, **kwargs): | |
""" | |
Store references to the account and basket that should be passed to this | |
form as keyword parameters (account, basket). | |
""" | |
self.account = kwargs.pop('account') | |
self.basket = kwargs.pop('basket') | |
super(AddressSelectForm, self).__init__(*args, **kwargs) | |
value = self.fields.pop('existing_address') | |
self.fields.insert(0, 'existing_address', value) | |
self.refresh_address_options() | |
def refresh_address_options(self): | |
""" | |
Generate a new list of valid addresses for this customer. | |
""" | |
default_addr = None | |
if self.basket.delivery_address: | |
default_addr = self.basket.delivery_address.id | |
elif self.account.default_address: | |
default_addr = self.account.default_address.id | |
addresses = [(addr.id,mark_safe("<br/>".join(addr.line_list()))) for addr in self.account.address_set.all()] + [('','Use Another')] | |
self.fields['existing_address'].choices = addresses | |
self.fields['existing_address'].initial = default_addr | |
def clean(self): | |
""" | |
Remove all errors related to the existing_address field. They won't be | |
valid anyway | |
""" | |
cleaned_data = self.cleaned_data | |
if cleaned_data.get("existing_address"): | |
for k, v in self._errors.items(): | |
del self._errors[k] | |
return cleaned_data | |
class QuickPayUserForm(forms.ModelForm): | |
""" | |
A base User form that implements logic for ensuring that the email being | |
used is not already used by a persistent account. It will however allow a | |
registration for a duplicate email address if that email is only used by | |
temporary account holders! | |
Unlike the account forms, which are separated into two distinct classes, | |
the UserForm class takes a keyword parameter in it's constructor, which, | |
if True, tells the class to apply validation as if it were an | |
update, rather than an insertion. This lets us ignore email collisions in | |
the user table. | |
""" | |
class Meta: | |
model = User | |
fields = ('first_name','last_name','email') | |
def __init__(self, *args, **kwargs): | |
""" | |
Make it so that first name, last name and email addresses are required | |
inputs, even though they're allowed to be blank in the User class | |
Django provides | |
""" | |
super(QuickPayUserForm, self).__init__(*args, **kwargs) | |
self.update = 'instance' in kwargs | |
# We really need this data | |
self.fields['first_name'].required = True | |
self.fields['last_name'].required = True | |
self.fields['email'].required = True | |
def clean(self): | |
""" | |
Check that the email address being used is not already taken by a | |
permanent member of the site | |
""" | |
cleaned_data = self.cleaned_data | |
email = cleaned_data.get('email') | |
clashes = Account.objects.filter(user__email=email, user__is_staff=False, temporary=False) | |
if self.update: | |
clashes = clashes.exclude(user__id=self.instance.id) | |
if clashes.count(): | |
self._errors["email"] = ErrorList([u"A customer with that email address already exists"]) | |
return cleaned_data | |
class UserForm(QuickPayUserForm): | |
""" | |
A extension of the QuickPayUserForm. This form also deals with passwords | |
because we're now concerning ourself with persistant users rather than | |
temporary ones. | |
""" | |
password = forms.CharField(widget=forms.PasswordInput,min_length=5) | |
confirm_password = forms.CharField(widget=forms.PasswordInput,min_length=5) | |
def __init__(self, *args, **kwargs): | |
""" | |
Make it so that the password and confirm_password fields are not | |
required if we're performing an update rather than an insert on the | |
User | |
""" | |
super(UserForm, self).__init__(*args, **kwargs) | |
if self.update: | |
self.fields['password'].required = False | |
self.fields['confirm_password'].required = False | |
def clean(self): | |
""" | |
Check that the passwords match | |
""" | |
super(UserForm, self).clean() | |
cleaned_data = self.cleaned_data | |
password = cleaned_data.get("password") | |
confirm_password = cleaned_data.get("confirm_password") | |
self.set_password = password and confirm_password | |
if self.set_password and password != confirm_password: | |
self._errors["password"] = ErrorList([u"Your passwords did not match"]) | |
return cleaned_data | |
''' | |
CARD_TYPES = ( | |
(0, 'VISA', 'Visa'), | |
(1, 'MC', 'Master Card'), | |
(2, 'DELTA', 'Delta'), | |
(3, 'SOLO', 'Solo'), | |
(4, 'MAESTRO', 'Maestro / Switch'), | |
(5, 'UKE', 'Visa Electron'), | |
# (6, 'JCB', 'JCB'), | |
#(7, 'AMEX', 'Amex'), | |
# (8, 'DC', 'Diners Club'), | |
) | |
CARD_TYPES_CHOICES = [(aid, adesc) for aid, vpsid, adesc in CARD_TYPES] | |
class PaymentForm(forms.Form): | |
""" | |
A payment form that requests credit card details off the customer | |
for forwarding on to payment gateways such as protx | |
""" | |
card_holder = forms.CharField(label=_('Name on Card'), max_length=300) | |
card_number = forms.RegexField(label=_('Card Number'), max_length=26, regex=r'^[0-9\ ]{12,26}$', error_messages={'invalid': _('Please enter a valid card number')}) | |
expiry_month = forms.IntegerField(label='MM', min_value=1, max_value=12, widget=forms.TextInput(attrs={'size': '3', 'maxlength': '2'})) | |
expiry_year = forms.IntegerField(label='YY', min_value=1, max_value=99, widget=forms.TextInput(attrs={'size': '3', 'maxlength': '2'})) | |
ccv = forms.CharField(label=_('Security Number'), widget=forms.TextInput(attrs={'size': '5', 'maxlength': '4'})) | |
card_type = forms.ChoiceField(label=_('Card Type'), widget=forms.Select, choices=CARD_TYPES_CHOICES) | |
start_month = forms.IntegerField(required=False, label='MM', min_value=1, max_value=12, widget=forms.TextInput(attrs={'size': '3', 'maxlength': '2'})) | |
start_year = forms.IntegerField(required=False, label='YY', min_value=1, max_value=99, widget=forms.TextInput(attrs={'size': '3', 'maxlength': '2'})) | |
issue_number = forms.CharField(required=False, label=_('Issue Number'), widget=forms.TextInput(attrs={'size': '3', 'maxlength': '3'})) | |
def clean(self): | |
""" | |
Remove white spaces from the card number | |
""" | |
cleaned_data = self.cleaned_data | |
if 'card_number' in cleaned_data: | |
cleaned_data['card_number'] = re.sub("\s+", "", cleaned_data['card_number']) | |
return cleaned_data | |
class BuyerForm(forms.ModelForm): | |
""" | |
A payment form that requests credit card details off the customer | |
for forwarding on to payment gateways such as protx | |
""" | |
class Meta: | |
model = User | |
fields = ('first_name','last_name','email') | |
#exclude = ('is_staff','is_active','last_login','date_joined','username','password','user_permissions','groups','is_superuser') | |
first_name = forms.CharField(label=_('First name'), max_length=200,required=True) | |
last_name = forms.CharField(label=_('Surname'), max_length=200,required=True) | |
#dob = forms.DateField(label='DOB', widget=SelectDateWidget(years=range(2006,1900,-1))) | |
#gender = forms.CharField(label='Gender', widget=forms.RadioSelect(choices=(('Male','Male'),('Female','Female'))), required=True) | |
#phone = forms.CharField(label='Phone number', max_length=50,required=True) | |
email = forms.EmailField(label=_('Email'), max_length=500,required=True) | |
def clean(self): | |
cleaned_data = self.cleaned_data | |
return cleaned_data | |
class AccountForm(forms.ModelForm): | |
""" | |
A payment form that requests credit card details off the customer | |
for forwarding on to payment gateways such as protx | |
""" | |
class Meta: | |
model = Account | |
fields = ('dob','gender','telephone') | |
#exclude = ('is_staff','is_active','last_login','date_joined','username','password','user_permissions','groups','is_superuser') | |
dob = forms.DateField(label=_('DOB'), widget=SelectDateWidget(years=range(2006,1900,-1))) | |
gender = forms.CharField(label=_('Gender'), widget=forms.RadioSelect(choices=(('Male',_('Male')),('Female',_('Female')))), required=True) | |
telephone = forms.CharField(label=_('Phone number'), max_length=50,required=True) | |
def clean(self): | |
cleaned_data = self.cleaned_data | |
return cleaned_data | |
class TermsForm(forms.Form): | |
# informed = forms.BooleanField(label='Keeping you informed',required=False) | |
mailing = forms.BooleanField(label=_('Add to mailing list'),required=False) | |
terms = forms.BooleanField(label=_('I accept the T&C\'s'), required=True) | |
def clean(self): | |
""" | |
Remove white spaces from the card number | |
""" | |
cleaned_data = self.cleaned_data | |
return cleaned_data |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment