Before I found this module I'd been sketching out a model where the customization itself is also a product.

It looks like I need to go down this avenue -- the question is whether this is something I can build into/on top of this module.

I have products that can take one or more customization options, such as shirts that can have:

* name & number
* sleeve badge
* game dates

The customer can choose one or more of these, entering data for each one. Each one adds to the total cost, thus:

* name & number, £x
* sleeve badge, £y
* game dates, £z

To further complicate things, whether all 3 of these are available or only a subset varies per product. So shirt A can have:

* name & number, £x

while shirt B can have:

* name & number, £x
* sleeve badge, £y

(see #1798006: Add to Cart Line Item type should be determined by product type or product instance for some work going towards allowing this on the product level).

Given that each customization is a thing that can be chosen and carries a price, it really seems the right thing to do to make this a product. (Furthermore, I know of a couple of EPOS systems that treat customization as a separate line item too.)

The system would then be structured like this:

- the product 'shirt A' has a multi-valued product reference field, where the store admin selects the customization products it can take. In this case, just 'name & number'.
- the 'name & number' product needs to be linked up to a line item type that allows the extra fields. That's what this module can already do -- though not quite, as the relationship from product type -> line item type is baked into display settings on the node. There is work on this over at #1798006: Add to Cart Line Item type should be determined by product type or product instance.
- the add to cart form for 'shirt A' needs to then be expanded to allow the line item form for the customization product to be in there too, along with the price implications.[*]
- if the customer chooses to customize, the 'name & number' line item gets added to the cart as well as the 'product A' line item.

[*] You could redirect them to a new form to optionally pick customizations after they have added product A to the cart, but that goes against doing things like nifty JS previews of the product image + the customer's customization options. Those should be previewable before you add anything to your cart.

Comments

malberts’s picture

Here's the functionality I am working towards. Sorry, this is long and without a TL;DR section.

The store sells customisable computers (desktops and laptops). The components, like RAM, CPU, etc., will be actual products with SKU's. The resulting customised product, however, will have the base product's SKU with all the customisations specified in the line item.

Product   Product Type    Line Item Type
-------   ------------    --------------
DESK1     Desktop         Desktop
DESK2     Desktop         Desktop
DESK3     Desktop         Desktop
LAP1      Laptop          Laptop
LAP2      Laptop          Laptop
LAP3      Laptop          Laptop

In most cases the products of a certain type will need the same customisations.

Example fields on line item bundle:

Laptop    Desktop
------    -------
CPU       CPU
RAM       RAM
Drive1    Drive1
Drive2    Drive2

In this example the field labels will be the same but they need to be different fields if I need to enforce a distinction between referencing laptop components and incompatible desktop components. So instead of having one line item type for 'Customisable computer' I will have one for Laptop and one for Desktop. This is not an issue, though.

My line item item types will resemble master blueprints where all possible customisation fields are present. However, all products using a specific line item do not need to use all the customisation fields. For example, LAP3 might be a small laptop that can only take Drive1 and not Drive2. All the other fields might be the same, so I would like to only disable #access for that one field instead of creating a new line item type just for that one product.

Discretion will be needed to determine if a product requires such a vastly different line item type that adding all its one-of-a-kind customisation fields to the master line item would be impractical. In this case it would be justified to add a new master line item type 'Super Laptop' which requires completely different fields. The product would be the same type as the others, but it would then just have the custom line item type associated with that specific product, as opposed to using the default 'Laptop' line item type linked to the 'Laptop' product type.

In addition to that, I would also need to select a subset of allowed values for the fields on the line item. For example, my Laptop line item's CPU field will list all possible mobile CPU's, but LAP3 can maybe only take Intel Ivy Bridge series. So then again, instead of creating a new line item type just for this simple deviation, I'd rather use the master line item and all its existing fields and then just disable some of the allowed values.

To summarise, in my specific use case the line item types for a specific type of product will be mostly the same, except for minor field and field value variations.

As a comparison, Ubercart's Attribute system allows you to add customisations on a per-product basis. So while you could have a default set for a product class, you were able to add extra attributes. This would be a big difference compared to my model where you can only add extra customisation fields to the master line item type and then dis/enable it on a per-product basis. Ubercart allows additive customisations whereas mine would be subtractive (if those terms are correct). Thus, the master line item type will always be a conglomeration of all possible customisation fields. In my case, this not an issue since the set of allowed sub-variations within a certain product type is low.

[*] You could redirect them to a new form to optionally pick customizations after they have added product A to the cart, but that goes against doing things like nifty JS previews of the product image + the customer's customization options. Those should be previewable before you add anything to your cart.

A while ago I started with some code that would add a relative price adjustment for each line item field option in applicable option-based widgets. These price adjustments would be re-adjusted via AJAX on a change. For example, on an Add to Cart form:
CPU field:
- (selected) Intel i3 0123
- Intel i5 1234 +$100.00
- Intel i7 5678 + $200.00

When changing a value it would update to:
CPU field:
- Intel i3 0123 -$100.00
- (selected) Intel i5 1234
- Intel i7 5678 + $100.00

and it would re-calculate the resulting price of the base product plus all these adjustments. However, doing this via AJAX when none of my price adjustments needed to be calculated in PHP turned out to be a bit slow. I intend to add some custom Javascript that will rather pick out something like the 'rel' attribute from each option to get the price adjustment. I scrapped my original code since it was a massive hack, but I'll get back to this at a later stage.

This is just a nice to have, but it makes customisation more user friendly :).


I think a big difference between our customisation requirements is that I've got many component slots where each component is a product with a price. I'll most likely be using those price field values to display the +$123.45 labels and for the actual adjustment. I'll also be checking and updating stock levels when selecting the components.

If I understand it correctly, your use case is like having only one 'component slot' (in my terminology) but where the selected product needs some more input which mine does not. To map your needs onto what I need, you could have a master Shirt line item type with one Product/Entity reference field referencing all the 'Customisations' products. On a per-product basis you could then dis/enable the allowed customisations (subset of allowed field values). Up to that point I think your solution is compatible with mine. What you do after that, like rendering the extra input fields and adding the separate line items, should not conflict either. The main product (e.g. SHIRT123) and its associated line item will then be added to the cart by default with the base price. The line item will contain all the selected customisation values. You'd just have to add an extra submit function to create the new line items. However, if you've got a limited amount of Shirt customisations, you could possibly add the input fields to the Shirt master line item and hide them with Conditional fields depending on the 'Customisations' values selected. That way it'll be similar to what I'm doing (except for the input fields).

In my case, though, I want to avoid separate line items as I'd have to track line item relationships and it might enable the user to remove components and make a mess in the cart unless I modify the cart interface.