Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

forms.py 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. from datetime import date
  2. from calendar import monthrange
  3. import re
  4. from django import forms
  5. from django.db.models import get_model
  6. from django.utils.translation import ugettext_lazy as _
  7. from oscar.apps.address.forms import AbstractAddressForm
  8. from oscar.core.loading import get_class
  9. from . import bankcards
  10. Country = get_model('address', 'Country')
  11. BillingAddress = get_model('order', 'BillingAddress')
  12. BankcardModel = get_model('payment', 'Bankcard')
  13. Bankcard = get_class('payment.utils', 'Bankcard')
  14. class BankcardNumberField(forms.CharField):
  15. def clean(self, value):
  16. """Check if given CC number is valid and one of the
  17. card types we accept"""
  18. non_decimal = re.compile(r'\D+')
  19. value = non_decimal.sub('', value.strip())
  20. if value and not bankcards.luhn(value):
  21. raise forms.ValidationError(_("Please enter a valid credit card number."))
  22. return super(BankcardNumberField, self).clean(value)
  23. class BankcardMonthWidget(forms.MultiWidget):
  24. """
  25. Widget containing two select boxes for selecting the month and year
  26. """
  27. def decompress(self, value):
  28. return [value.month, value.year] if value else [None, None]
  29. def format_output(self, rendered_widgets):
  30. html = u' '.join(rendered_widgets)
  31. return u'<span style="white-space: nowrap">%s</span>' % html
  32. class BankcardMonthField(forms.MultiValueField):
  33. """
  34. A modified version of the snippet: http://djangosnippets.org/snippets/907/
  35. """
  36. default_error_messages = {
  37. 'invalid_month': _('Enter a valid month.'),
  38. 'invalid_year': _('Enter a valid year.'),
  39. }
  40. num_years = 5
  41. def __init__(self, *args, **kwargs):
  42. # Allow the number of years to be specified
  43. if 'num_years' in kwargs:
  44. self.num_years = kwargs['num_years']
  45. del kwargs['num_years']
  46. errors = self.default_error_messages.copy()
  47. if 'error_messages' in kwargs:
  48. errors.update(kwargs['error_messages'])
  49. fields = (
  50. forms.ChoiceField(choices=self.month_choices(),
  51. error_messages={'invalid': errors['invalid_month']}),
  52. forms.ChoiceField(choices=self.year_choices(),
  53. error_messages={'invalid': errors['invalid_year']}),
  54. )
  55. super(BankcardMonthField, self).__init__(fields, *args, **kwargs)
  56. self.widget = BankcardMonthWidget(widgets = [fields[0].widget, fields[1].widget])
  57. def month_choices(self):
  58. return []
  59. def year_choices(self):
  60. return []
  61. class BankcardExpiryMonthField(BankcardMonthField):
  62. """
  63. Expiry month
  64. """
  65. def month_choices(self):
  66. return [("%.2d" % x, "%.2d" % x) for x in xrange(1, 13)]
  67. def year_choices(self):
  68. return [(x, x) for x in xrange(date.today().year, date.today().year+self.num_years)]
  69. def clean(self, value):
  70. expiry_date = super(BankcardExpiryMonthField, self).clean(value)
  71. if date.today() > expiry_date:
  72. raise forms.ValidationError(_("The expiration date you entered is in the past."))
  73. return expiry_date
  74. def compress(self, data_list):
  75. if data_list:
  76. if data_list[1] in forms.fields.EMPTY_VALUES:
  77. error = self.error_messages['invalid_year']
  78. raise forms.ValidationError(error)
  79. if data_list[0] in forms.fields.EMPTY_VALUES:
  80. error = self.error_messages['invalid_month']
  81. raise forms.ValidationError(error)
  82. year = int(data_list[1])
  83. month = int(data_list[0])
  84. # find last day of the month
  85. day = monthrange(year, month)[1]
  86. return date(year, month, day)
  87. return None
  88. class BankcardStartingMonthField(BankcardMonthField):
  89. def month_choices(self):
  90. months = [("%.2d" % x, "%.2d" % x) for x in xrange(1, 13)]
  91. months.insert(0, ("", "--"))
  92. return months
  93. def year_choices(self):
  94. years = [(x, x) for x in xrange(date.today().year - self.num_years, date.today().year + 1)]
  95. years.insert(0, ("", "--"))
  96. return years
  97. def clean(self, value):
  98. starting_date = super(BankcardMonthField, self).clean(value)
  99. if starting_date and date.today() < starting_date:
  100. raise forms.ValidationError(_("The starting date you entered is in the future."))
  101. return starting_date
  102. def compress(self, data_list):
  103. if data_list:
  104. if data_list[1] in forms.fields.EMPTY_VALUES:
  105. error = self.error_messages['invalid_year']
  106. raise forms.ValidationError(error)
  107. if data_list[0] in forms.fields.EMPTY_VALUES:
  108. error = self.error_messages['invalid_month']
  109. raise forms.ValidationError(error)
  110. year = int(data_list[1])
  111. month = int(data_list[0])
  112. return date(year, month, 1)
  113. return None
  114. class BankcardForm(forms.ModelForm):
  115. number = BankcardNumberField(max_length=20, widget=forms.TextInput(attrs={'autocomplete':'off'}),
  116. label=_("Card number"))
  117. # Name is not normally needed by payment gateways so we hide it by default
  118. name = forms.CharField(max_length=128, label=_("Name on card"),
  119. widget=forms.widgets.HiddenInput, required=False)
  120. cvv_number = forms.RegexField(
  121. required=True, label=_("CVV Number"),
  122. regex=r'^\d{3,4}$',
  123. widget=forms.TextInput(attrs={'size': '5'}),
  124. help_text=_("This is the 3 or 4 digit security number on the back of your bankcard"))
  125. start_month = BankcardStartingMonthField(label=_("Valid from"), required=False)
  126. expiry_month = BankcardExpiryMonthField(required=True, label=_("Valid to"))
  127. def __init__(self, *args, **kwargs):
  128. if 'initial' not in kwargs:
  129. # Set the initial expiry month to be current month
  130. today = date.today()
  131. kwargs['initial'] = {'expiry_month': ["%.2d" % today.month,
  132. today.year]}
  133. super(BankcardForm, self).__init__(*args, **kwargs)
  134. class Meta:
  135. model = BankcardModel
  136. exclude = ('user', 'partner_reference')
  137. fields = ('number', 'name', 'start_month', 'expiry_month', 'cvv_number')
  138. def get_bankcard_obj(self):
  139. """
  140. Returns a Bankcard object for use in payment processing.
  141. """
  142. kwargs = {
  143. 'name': self.cleaned_data['name'],
  144. 'card_number': self.cleaned_data['number'],
  145. 'expiry_date': self.cleaned_data['expiry_month'].strftime("%m/%y"),
  146. 'cvv': self.cleaned_data['cvv_number'],
  147. }
  148. if self.cleaned_data['start_month']:
  149. kwargs['start_date'] = self.cleaned_data['start_month'].strftime("%m/%y")
  150. return Bankcard(**kwargs)
  151. class BillingAddressForm(AbstractAddressForm):
  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 = Country._default_manager.all()
  157. class Meta:
  158. model = BillingAddress
  159. exclude = ('search_text',)