您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

forms.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. from django import forms
  2. from django.conf import settings
  3. from django.db.models import get_model
  4. from django.forms.models import modelformset_factory, BaseModelFormSet
  5. from django.utils.translation import gettext_lazy as _
  6. from oscar.templatetags.currency_filters import currency
  7. Line = get_model('basket', 'line')
  8. Basket = get_model('basket', 'basket')
  9. Product = get_model('catalogue', 'product')
  10. class BasketLineForm(forms.ModelForm):
  11. save_for_later = forms.BooleanField(initial=False, required=False,
  12. label=_('Save for Later'))
  13. def clean_quantity(self):
  14. qty = self.cleaned_data['quantity']
  15. self.check_max_allowed_quantity(qty)
  16. self.check_permission(qty)
  17. return qty
  18. def check_max_allowed_quantity(self, qty):
  19. is_allowed, reason = self.instance.basket.is_quantity_allowed(qty)
  20. if not is_allowed:
  21. raise forms.ValidationError(reason)
  22. def check_permission(self, qty):
  23. product = self.instance.product
  24. is_available, reason = product.is_purchase_permitted(user=None,
  25. quantity=qty)
  26. if not is_available:
  27. raise forms.ValidationError(reason)
  28. class Meta:
  29. model = Line
  30. exclude = ('basket', 'product', 'line_reference',
  31. 'price_excl_tax', 'price_incl_tax')
  32. class SavedLineForm(forms.ModelForm):
  33. move_to_basket = forms.BooleanField(initial=False, required=False,
  34. label=_('Move to Basket'))
  35. class Meta:
  36. model = Line
  37. exclude = ('basket', 'product', 'line_reference', 'quantity',
  38. 'price_excl_tax', 'price_incl_tax')
  39. def __init__(self, user, basket, *args, **kwargs):
  40. self.user = user
  41. self.basket = basket
  42. super(SavedLineForm, self).__init__(*args, **kwargs)
  43. def clean(self):
  44. cleaned_data = super(SavedLineForm, self).clean()
  45. try:
  46. line = self.basket.lines.get(product=self.instance.product)
  47. except Line.DoesNotExist:
  48. desired_qty = self.instance.quantity
  49. else:
  50. desired_qty = self.instance.quantity + line.quantity
  51. is_available, reason = self.instance.product.is_purchase_permitted(
  52. user=self.user, quantity=desired_qty)
  53. if not is_available:
  54. raise forms.ValidationError(reason)
  55. return cleaned_data
  56. class BaseSavedLineFormSet(BaseModelFormSet):
  57. def __init__(self, user, basket, *args, **kwargs):
  58. self.user = user
  59. self.basket = basket
  60. super(BaseSavedLineFormSet, self).__init__(*args, **kwargs)
  61. def _construct_form(self, i, **kwargs):
  62. return super(BaseSavedLineFormSet, self)._construct_form(
  63. i, user=self.user, basket=self.basket, **kwargs)
  64. SavedLineFormSet = modelformset_factory(Line, form=SavedLineForm,
  65. formset=BaseSavedLineFormSet, extra=0,
  66. can_delete=True)
  67. class BasketVoucherForm(forms.Form):
  68. code = forms.CharField(max_length=128, label=_('Code'))
  69. def __init__(self, *args, **kwargs):
  70. return super(BasketVoucherForm, self).__init__(*args, **kwargs)
  71. def clean_code(self):
  72. return self.cleaned_data['code'].strip().upper()
  73. class ProductSelectionForm(forms.Form):
  74. product_id = forms.IntegerField(min_value=1, label=_("Product ID"))
  75. def clean_product_id(self):
  76. id = self.cleaned_data['product_id']
  77. try:
  78. return Product.objects.get(pk=id)
  79. except Product.DoesNotExist:
  80. raise forms.ValidationError(
  81. _("This product is not available for purchase"))
  82. class AddToBasketForm(forms.Form):
  83. # We set required=False as validation happens later on
  84. product_id = forms.IntegerField(widget=forms.HiddenInput(), required=False,
  85. min_value=1, label=_("Product ID"))
  86. quantity = forms.IntegerField(initial=1, min_value=1, label=_('Quantity'))
  87. def __init__(self, basket, user, instance, *args, **kwargs):
  88. super(AddToBasketForm, self).__init__(*args, **kwargs)
  89. self.basket = basket
  90. self.user = user
  91. self.instance = instance
  92. if instance:
  93. if instance.is_group:
  94. self._create_group_product_fields(instance)
  95. else:
  96. self._create_product_fields(instance)
  97. def cleaned_options(self):
  98. """
  99. Return submitted options in a clean format
  100. """
  101. options = []
  102. for option in self.instance.options:
  103. if option.code in self.cleaned_data:
  104. options.append({
  105. 'option': option,
  106. 'value': self.cleaned_data[option.code]})
  107. return options
  108. def clean(self):
  109. # Check product exists
  110. try:
  111. product = Product.objects.get(
  112. id=self.cleaned_data.get('product_id', None))
  113. except Product.DoesNotExist:
  114. raise forms.ValidationError(
  115. _("Please select a valid product"))
  116. current_qty = self.basket.line_quantity(product,
  117. self.cleaned_options())
  118. desired_qty = current_qty + self.cleaned_data.get('quantity', 1)
  119. is_available, reason = product.is_purchase_permitted(
  120. user=self.user, quantity=desired_qty)
  121. if not is_available:
  122. raise forms.ValidationError(reason)
  123. return self.cleaned_data
  124. def clean_quantity(self):
  125. qty = self.cleaned_data['quantity']
  126. basket_threshold = settings.OSCAR_MAX_BASKET_QUANTITY_THRESHOLD
  127. if basket_threshold:
  128. total_basket_quantity = self.basket.num_items
  129. max_allowed = basket_threshold - total_basket_quantity
  130. if qty > max_allowed:
  131. raise forms.ValidationError(
  132. _("Due to technical limitations we are not able to ship"
  133. " more than %(threshold)d items in one order. Your"
  134. " basket currently has %(basket)d items.") % {
  135. 'threshold': basket_threshold,
  136. 'basket': total_basket_quantity,
  137. })
  138. return qty
  139. def _create_group_product_fields(self, item):
  140. """
  141. Adds the fields for a "group"-type product (eg, a parent product with a
  142. list of variants.
  143. """
  144. choices = []
  145. for variant in item.variants.all():
  146. if variant.has_stockrecord:
  147. attr_summary = variant.attribute_summary()
  148. if attr_summary:
  149. attr_summary = "(%s)" % attr_summary
  150. summary = u"%s %s - %s" % (
  151. variant.get_title(), attr_summary,
  152. currency(variant.stockrecord.price_incl_tax))
  153. choices.append((variant.id, summary))
  154. self.fields['product_id'] = forms.ChoiceField(choices=tuple(choices),
  155. label=_("Variant"))
  156. def _create_product_fields(self, item):
  157. """
  158. Add the product option fields.
  159. """
  160. for option in item.options:
  161. self._add_option_field(item, option)
  162. def _add_option_field(self, item, option):
  163. """
  164. Creates the appropriate form field for the product option.
  165. This is designed to be overridden so that specific widgets can be used
  166. for certain types of options.
  167. """
  168. kwargs = {'required': option.is_required}
  169. self.fields[option.code] = forms.CharField(**kwargs)
  170. class SimpleAddToBasketForm(AddToBasketForm):
  171. quantity = forms.IntegerField(
  172. initial=1, min_value=1, widget=forms.HiddenInput, label=_('Quantity'))