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 7.5KB

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