Commits

Chris Moffitt  committed d5bf7b6

Improving the flexibility and useability of custom product.
See ticket #209

  • Participants
  • Parent commits 7aafe0f

Comments (0)

Files changed (8)

File satchmo/payment/common/pay_ship.py

 from django.conf import settings
 from django.core.mail import send_mail
 from satchmo.configuration import config_value
-from satchmo.contact.models import OrderItem
+from satchmo.contact.models import OrderItem, OrderItemDetail
 from satchmo.shop.models import Config
 from satchmo.shop.utils import load_module
 from socket import error as SocketError
+from satchmo.product.models import CustomTextField
 import logging
 
 log = logging.getLogger('pay_ship')
         new_order_item = OrderItem(order=new_order, product=item.product, quantity=item.quantity,
         unit_price=item.unit_price, line_item_price=item.line_total)
         new_order_item.save()
-    
+        if item.has_details:
+            # Check to see if cartitem has CartItemDetails
+            # If so, add here.
+            #obj = CustomTextField.objects.get(id=item.details.values()[0]['customfield_id'])
+            #val = item.details.values()[0]['detail']
+            for detail in item.details.all():
+                new_details = OrderItemDetail(item=new_order_item, value=detail.value, name=detail.name, price_change=detail.price_change, sort_order=detail.sort_order)
+                new_details.save()
     new_order.recalculate_total()
 
 def send_order_confirmation(newOrder, template='email/order_complete.txt'):

File satchmo/product/models.py

         return True
     is_shippable = property(_get_shippable)
 
+def _cross_list(sequences):
+    """
+    Code taken from the Python cookbook v.2 (19.9 - Looping through the cross-product of multiple iterators)
+    This is used to create all the variations associated with an product
+    """
+    result =[[]]
+    for seq in sequences:
+        result = [sublist+[item] for sublist in result for item in seq]
+    return result
+    
+def get_all_options(obj):
+    """
+    Returns all possible combinations of options for this products OptionGroups as a List of Lists.
+    Ex:
+    For OptionGroups Color and Size with Options (Blue, Green) and (Large, Small) you'll get
+    [['Blue', 'Small'], ['Blue', 'Large'], ['Green', 'Small'], ['Green', 'Large']]
+    Note: the actual values will be instances of Option instead of strings
+    """
+    sublist = []
+    masterlist = []
+    #Create a list of all the options & create all combos of the options
+    for opt in obj.option_group.all():
+        for value in opt.option_set.all():
+            sublist.append(value)
+        masterlist.append(sublist)
+        sublist = []
+    return _cross_list(masterlist)
+
 class CustomProduct(models.Model):
     """
     Product which must be custom-made or ordered.
     deferred_shipping = models.BooleanField(_('Deferred Shipping'), 
         help_text=_('Do not charge shipping at checkout for this item.'), 
         default=False)
-        
+    option_group = models.ManyToManyField(OptionGroup, blank=True,)
+    
     def _is_shippable(self):
         return not self.deferred_shipping
     is_shippable = property(fget=_is_shippable)
     
     def __unicode__(self):
         return u"CustomProduct: %s" % self.product.name
+        
+    def get_valid_options(self):
+        """
+        Returns all of the valid options
+        """
+        return get_all_options(self)
     
     class Admin:
         pass
     slug = models.SlugField()
     products = models.ForeignKey(CustomProduct, verbose_name=_('Custom Fields'),
         edit_inline=models.TABULAR, num_in_admin=3, related_name='custom_text_fields')
+    sort_order = models.IntegerField(_("Sort Order"),
+        help_text=_("The display order for this group."))
+    price_change = models.DecimalField(_("Price Change"), max_digits=10, decimal_places=2, blank=True, null=True)
+        
+    class Meta:
+        ordering = ('sort_order',)
+
 
 class ConfigurableProduct(models.Model):
     """
                 sublist.append(value)
             masterlist.append(sublist)
             sublist = []
-        return self._cross_list(masterlist)
+        return _cross_list(masterlist)
 
     def get_valid_options(self):
         """
         Returns the same output as get_all_options(), but filters out Options that this
         ConfigurableProduct doesn't have a ProductVariation for.
         """
-        opts = self.get_all_options()
+        opts = get_all_options(self)
         newopts = []
         for a in opts:
             if self.get_product_count(a):

File satchmo/product/views.py

 
     if 'ConfigurableProduct' in p_types:
         options = serialize_options(product.configurableproduct, selected_options)
+    
+    if 'CustomProduct' in p_types:
+        options = serialize_options(product.customproduct, selected_options)
 
     template = find_product_template(product, producttypes=p_types)
     ctx = RequestContext(request, {'product': product, 'options': options})

File satchmo/shop/models.py

             itemToModify = CartItem(cart=self, product=chosen_item, quantity=0)
         itemToModify.quantity += number_added            
         itemToModify.save()
-        for field, val in details:
-            itemToModify.add_detail(field, val)
+        for data in details:
+            itemToModify.add_detail(data)
         
 
     def remove_item(self, chosen_item_id, number_removed):
         return self.product.name
     description = property(_get_description)
 
-    def add_detail(self, customfield, val):
-        detl = CartItemDetails(cartitem=self, customfield=customfield, detail=val)
+    def add_detail(self, data):
+        detl = CartItemDetails(cartitem=self, name=data['name'], value=data['value'], sort_order=data['sort_order'], price_change=data['price_change'])
         detl.save()
         #self.details.add(detl)
 
+    def _has_details(self):
+        """
+        Determine if this specific item has more detail
+        """
+        return (self.details.count() > 0)
+        
+    has_details = property(_has_details)
+    
     def __unicode__(self):
         currency = config_value('SHOP', 'CURRENCY')
         return u'%s - %s %s%s' % (self.quantity, self.product.name,
     """
     An arbitrary detail about a cart item.
     """
-    customfield = models.ForeignKey(CustomTextField, core=True)
     cartitem = models.ForeignKey(CartItem, related_name='details', edit_inline=True, core=True)
-    detail = models.TextField(_('detail'))
+    value = models.TextField(_('detail'))
+    name = models.CharField(_('name'), max_length=100)
+    price_change = models.DecimalField(_("Item Detail Price Change"), max_digits=6, decimal_places=2, blank=True, null=True)
+    sort_order = models.IntegerField(_("Sort Order"),
+        help_text=_("The display order for this group."))
+        
+    class Meta:
+        ordering = ('sort_order',)

File satchmo/shop/views/cart.py

 from django.utils.simplejson.encoder import JSONEncoder
 from django.utils.translation import ugettext as _
 from django.utils.datastructures import MultiValueDictKeyError
-from satchmo.product.models import Product
+from satchmo.product.models import Product, OptionManager
 from satchmo.product.views import find_product_template, optionset_from_post
-from satchmo.shop.models import Cart, CartItem
+from satchmo.shop.models import Cart, CartItem, CartItemDetails
 from satchmo.shop.views.utils import bad_or_missing
 import logging
 
                 
         if 'CustomProduct' in p_types:
             for customfield in product.customproduct.custom_text_fields.all():
-                details.append((customfield, request.POST["custom_%s" % customfield.slug]))
-            
+                data = { 'name' : customfield.name,
+                         'value' : request.POST["custom_%s" % customfield.slug],
+                         'sort_order': customfield.sort_order,
+                         'price_change': customfield.price_change }         
+                details.append(data)
+                data = {}
+            chosenOptions = optionset_from_post(product.customproduct, request.POST)
+            manager = OptionManager()
+            for choice in chosenOptions:
+                result = manager.from_unique_id(choice)
+                print result
+                data = { 'name': result.optionGroup,
+                          'value': result.name,
+                          'sort_order': result.displayOrder,
+                          'price_change': result.price_change
+                }
+                details.append(data)
+                data = {}
+                
         template = find_product_template(product)
     except (Product.DoesNotExist, MultiValueDictKeyError):
         return bad_or_missing(request, _('The product you have requested does not exist.'))

File satchmo/templates/admin/_customproduct_management.html

             {% else %}
                 [<a href="{% url satchmo_charge_remaining custom.id %}">{% trans 'Charge remaining amount' %}</a>]
             {% endifequal %}
+       
+            <li>{% trans 'Product Details' %}</li>
+            <ul>
+            {% for detail in custom.orderitemdetail_set.all %}
+                <li> {{detail.name}} - {{detail.value}} </li>
+            {% endfor %}
+            </ul>
         </li>
     {% endfor %}
 </fieldset>

File satchmo/templates/base_cart.html

                 {% endifnotequal %}
                 {% for detail in cartitem.details.all %}
                     {% if not forloop.first %}<br />{% endif %}
-                    {{ detail.customfield.name }}: 
-                    {{ detail.detail }}
+                    {{ detail.name }}: 
+                    {{ detail.value }}
                 {% endfor %}
             </td>
             <td>&nbsp;</td>

File satchmo/templates/product/detail_customproduct.html

 
 {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
 <form id="options" action="{% url satchmo_cart_add %}" method="post">
+
+{% for option_group in options %}
+ {{ option_group.name }}
+    <select name="{{ option_group.id }}" id="{{option_group.id}}" class="priced">
+    {% for choice in option_group.items %}
+     <option value="{{ choice.value }}" {% if choice.selected %}selected="selected"{% endif %}>{{ choice.name }}
+        {% if choice.price_change %}
+            {% option_price choice %}
+        {% endif %}
+     </option>
+    {% endfor %}
+      </select> 
+{% endfor %} 
+
 {% for custom in product.customproduct.custom_text_fields.all %}
 <div>
 {{ custom.name }}<br/>