You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

forms.py 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. from datetime import date, datetime
  2. from calendar import monthrange
  3. import re
  4. from django import forms
  5. from oscar.services import import_module
  6. address_models = import_module('address.models', ['Country'])
  7. order_models = import_module('order.models', ['BillingAddress'])
  8. payment_models = import_module('payment.models', ['Bankcard'])
  9. VISA, MASTERCARD, AMEX, MAESTRO, DISCOVER = ('Visa', 'Mastercard', 'American Express', 'Maestro', 'Discover')
  10. def bankcard_type(number):
  11. number = str(number)
  12. if len(number) == 13:
  13. if number[0] == "4":
  14. return VISA
  15. elif len(number) == 14:
  16. if number[:2] == "36":
  17. return MASTERCARD
  18. elif len(number) == 15:
  19. if number[:2] in ("34", "37"):
  20. return AMEX
  21. elif len(number) == 16:
  22. if number[:4] == "6011":
  23. return DISCOVER
  24. if number[:2] in ("51", "52", "53", "54", "55"):
  25. return MASTERCARD
  26. if number[0] == "4":
  27. return VISA
  28. return None
  29. def luhn(card_number):
  30. card_number = str(card_number)
  31. sum = 0
  32. num_digits = len(card_number)
  33. odd_even = num_digits & 1
  34. for i in range(0, num_digits):
  35. digit = int(card_number[i])
  36. if not (( i & 1 ) ^ odd_even ):
  37. digit = digit * 2
  38. if digit > 9:
  39. digit = digit - 9
  40. sum = sum + digit
  41. return ( (sum % 10) == 0 )
  42. class BankcardField(forms.CharField):
  43. def clean(self, value):
  44. """Check if given CC number is valid and one of the
  45. card types we accept"""
  46. non_decimal = re.compile(r'\D+')
  47. value = non_decimal.sub('', value.strip())
  48. if value and not luhn(value):
  49. raise forms.ValidationError("Please enter a valid credit card number.")
  50. return super(BankcardField, self).clean(value)
  51. class BankcardMonthWidget(forms.MultiWidget):
  52. """
  53. Widget containing two select boxes for selecting the month and year
  54. """
  55. def decompress(self, value):
  56. return [value.month, value.year] if value else [None, None]
  57. def format_output(self, rendered_widgets):
  58. html = u' '.join(rendered_widgets)
  59. return u'<span style="white-space: nowrap">%s</span>' % html
  60. class BankcardMonthField(forms.MultiValueField):
  61. """
  62. A modified version of the snippet: http://djangosnippets.org/snippets/907/
  63. """
  64. default_error_messages = {
  65. 'invalid_month': u'Enter a valid month.',
  66. 'invalid_year': u'Enter a valid year.',
  67. }
  68. def __init__(self, *args, **kwargs):
  69. errors = self.default_error_messages.copy()
  70. if 'error_messages' in kwargs:
  71. errors.update(kwargs['error_messages'])
  72. fields = (
  73. forms.ChoiceField(choices=self.month_choices(),
  74. error_messages={'invalid': errors['invalid_month']}),
  75. forms.ChoiceField(choices=self.year_choices(),
  76. error_messages={'invalid': errors['invalid_year']}),
  77. )
  78. super(BankcardMonthField, self).__init__(fields, *args, **kwargs)
  79. self.widget = BankcardMonthWidget(widgets = [fields[0].widget, fields[1].widget])
  80. def month_choices(self):
  81. return []
  82. def year_choices(self):
  83. return []
  84. class BankcardExpiryMonthField(BankcardMonthField):
  85. """
  86. Expiry month
  87. """
  88. def month_choices(self):
  89. return [("%.2d" % x, "%.2d" % x) for x in xrange(1, 13)]
  90. def year_choices(self):
  91. return [(x, x) for x in xrange( date.today().year, date.today().year+5)]
  92. def clean(self, value):
  93. expiry_date = super(BankcardExpiryMonthField, self).clean(value)
  94. if date.today() > expiry_date:
  95. raise forms.ValidationError("The expiration date you entered is in the past.")
  96. return expiry_date
  97. def compress(self, data_list):
  98. if data_list:
  99. if data_list[1] in forms.fields.EMPTY_VALUES:
  100. error = self.error_messages['invalid_year']
  101. raise forms.ValidationError(error)
  102. if data_list[0] in forms.fields.EMPTY_VALUES:
  103. error = self.error_messages['invalid_month']
  104. raise forms.ValidationError(error)
  105. year = int(data_list[1])
  106. month = int(data_list[0])
  107. # find last day of the month
  108. day = monthrange(year, month)[1]
  109. return date(year, month, day)
  110. return None
  111. class BankcardStartingMonthField(BankcardMonthField):
  112. """
  113. Starting month
  114. """
  115. def month_choices(self):
  116. months = [("%.2d" % x, "%.2d" % x) for x in xrange(1, 13)]
  117. months.insert(0, ("", "--"))
  118. return months
  119. def year_choices(self):
  120. years = [(x, x) for x in xrange( date.today().year - 5, date.today().year)]
  121. years.insert(0, ("", "--"))
  122. return years
  123. def clean(self, value):
  124. starting_date = super(BankcardMonthField, self).clean(value)
  125. if starting_date and date.today() < starting_date:
  126. raise forms.ValidationError("The starting date you entered is in the future.")
  127. return starting_date
  128. def compress(self, data_list):
  129. if data_list:
  130. if data_list[1] in forms.fields.EMPTY_VALUES:
  131. error = self.error_messages['invalid_year']
  132. raise forms.ValidationError(error)
  133. if data_list[0] in forms.fields.EMPTY_VALUES:
  134. error = self.error_messages['invalid_month']
  135. raise forms.ValidationError(error)
  136. year = int(data_list[1])
  137. month = int(data_list[0])
  138. return date(year, month, 1)
  139. return None
  140. class BankcardForm(forms.ModelForm):
  141. number = BankcardField(max_length=20, widget=forms.TextInput(attrs={'autocomplete':'off'}), label="Card number")
  142. name = forms.CharField(max_length=128, label="Name on card")
  143. ccv_number = forms.IntegerField(required=True, label="CCV Number",
  144. max_value = 99999, widget=forms.TextInput(attrs={'size': '5'}))
  145. start_month = BankcardStartingMonthField(label="Valid from", required=False)
  146. expiry_month = BankcardExpiryMonthField(required=True, label = "Valid to")
  147. class Meta:
  148. model = payment_models.Bankcard
  149. exclude = ('user', 'partner_reference')
  150. fields = ('number', 'name', 'start_month', 'expiry_month', 'ccv_number')
  151. class BillingAddressForm(forms.ModelForm):
  152. def __init__(self, *args, **kwargs):
  153. super(BillingAddressForm,self ).__init__(*args, **kwargs)
  154. self.set_country_queryset()
  155. def set_country_queryset(self):
  156. self.fields['country'].queryset = address_models.Country._default_manager.all()
  157. class Meta:
  158. model = order_models.BillingAddress