3

Just wondering if anyone has any experience with creating repeat orders in Cartthrob and cares to share how they achieved this (or any obvious gotchas). I ask because there doesn't appear to be anything in Cartthrob's docs with points to this functionality, so I want to make sure it's actually achievable.

To clarify what I'm trying to achieve: I have products in a number of channels (about 4 channels) which the customer can add to their basket and purchase. If they choose to create an account (or are signed in) they can then view previous orders. What I would like the customer to be able to do is view a previous order and then be able to rebuild their basket from this previous order. From the basket they would be able to do the usual stuff like change the quantity and delete individual items.

3

This is a barebones example, but hopefully it gets you on the right track:

<table>
  <caption>My Order History</caption>
  <tbody>
  {exp:channel:entries channel="orders" dynamic="no" author_id="CURRENT_USER"}
    <tr>
      <td>{title}</td>
      <td>{entry_date format="%Y-%m-%d %H:%i:%s"}</td>
      <td>
      {exp:cartthrob:multi_add_to_cart_form return="cart/basket"}
        {exp:cartthrob:order_items order_id="{entry_id}" variable_prefix="item:"}
        <input type="hidden" name="entry_id[{item:row_order}] value="{item:entry_id}" />
        <input type="hidden" name="quantity[{item:row_order}] value="{item:quantity}" />
        {/exp:cartthrob:order_items}

        <input type="submit" value="Repeat this order" />
      {/exp:cartthrob:multi_add_to_cart_form}
      </td>
    </tr>
  {/exp:channel:entries}
  </tbody>
</table>
| improve this answer | |
  • Thanks Rob for giving us a steer with this. We needed to be able to add product options into this mix, so wrote a plugin to get this from the database and return the options to the template. github.com/aarmitage/CT-Duplicate-Order – Andrew Armitage Oct 27 '14 at 18:12
2

I don't think a plugin is needed for this. You can retrieve the item options from the order itself without a plugin and I think that's maybe the better approach to avoid having to maintain an extra plugin. But maybe I am missing something.

This is a somewhat overcomplicated and not really 100% finished example I am quickly pulling from work in progress. I have two separate channels with complex variation products, but should give the idea - it's a combo display previous order details/re-order items template.

Let me know if it's not clear enough.

I am using stash models of various product details but they should be reasonably obvious.

MODEL:

{exp:channel:entries channel="orders" entry_id="{stash:e_id}" status="not closed" dynamic="no" parse="inward"}

{!-- Order Data - later: change to all one set call --}
{exp:stash:set name="order_title"}{title}{/exp:stash:set}
{exp:stash:set name="order_entry_id"}{entry_id}{/exp:stash:set}
{exp:stash:set name="order_url_title"}{url_title}{/exp:stash:set}
{exp:stash:set name="order_status"}{status}{/exp:stash:set} 
{exp:stash:set name="order_vault_id"}{order_vault_id}{/exp:stash:set}
{exp:stash:set name="order_subscription_id"}{order_subscription_id}{/exp:stash:set}
{exp:stash:set name="order_payment_gateway"}{order_payment_gateway}{/exp:stash:set}
{exp:stash:set name="order_ip_address"}{order_ip_address}{/exp:stash:set}
{exp:stash:set name="order_last_four"}{order_last_four}{/exp:stash:set}
{exp:stash:set name="order_transaction_id"}{order_transaction_id}{/exp:stash:set}
{exp:stash:set name="order_billing_last_name"}{order_billing_last_name}{/exp:stash:set}
{exp:stash:set name="order_billing_company"}{order_billing_company}{/exp:stash:set}
{exp:stash:set name="order_billing_country"}{order_billing_country}{/exp:stash:set}
{exp:stash:set name="order_country_code"}{order_country_code}{/exp:stash:set}
{exp:stash:set name="order_shipping_first_name"}{order_shipping_first_name}{/exp:stash:set}
{exp:stash:set name="order_shipping_last_name"}{order_shipping_last_name}{/exp:stash:set}
{exp:stash:set name="order_shipping_address"}{order_shipping_address}{/exp:stash:set}
{exp:stash:set name="order_shipping_address2"}{order_shipping_address2}{/exp:stash:set}
{exp:stash:set name="order_shipping_city"}{order_shipping_city}{/exp:stash:set}
{exp:stash:set name="order_customer_email"}{order_customer_email}{/exp:stash:set}
{exp:stash:set name="order_language"}{order_language}{/exp:stash:set}
{exp:stash:set name="order_full_billing_address"}{order_full_billing_address}{/exp:stash:set}
{exp:stash:set name="order_full_shipping_address"}{order_full_shipping_address}{/exp:stash:set}
{exp:stash:set name="order_billing_first_name"}{order_billing_first_name}{/exp:stash:set}
{exp:stash:set name="order_billing_address"}{order_billing_address}{/exp:stash:set}
{exp:stash:set name="order_billing_address2"}{order_billing_address2}{/exp:stash:set}
{exp:stash:set name="order_customer_phone"}{order_customer_phone}{/exp:stash:set}
{exp:stash:set name="order_discount"}{order_discount}{/exp:stash:set}
{exp:stash:set name="order_customer_full_name"}{order_customer_full_name}{/exp:stash:set}
{exp:stash:set name="order_coupons"}{order_coupons}{/exp:stash:set}
{exp:stash:set name="order_shipping_option"}{order_shipping_option}{/exp:stash:set}
{exp:stash:set name="order_tax"}{order_tax}{/exp:stash:set}
{exp:stash:set name="order_shipping_plus_tax"}{order_shipping_plus_tax}{/exp:stash:set}
{exp:stash:set name="order_shipping"}{order_shipping}{/exp:stash:set}
{exp:stash:set name="order_subtotal_plus_tax"}{order_subtotal_plus_tax}{/exp:stash:set}
{exp:stash:set name="order_subtotal"}{order_subtotal}{/exp:stash:set}
{exp:stash:set name="order_billing_city"}{order_billing_city}{/exp:stash:set}
{exp:stash:set name="order_billing_state"}{order_billing_state}{/exp:stash:set}
{exp:stash:set name="order_billing_zip"}{order_billing_zip}{/exp:stash:set}
{exp:stash:set name="order_total"}{order_total}{/exp:stash:set}
{exp:stash:set name="order_error_message"}{order_error_message}{/exp:stash:set}
{exp:stash:set name="order_shipping_country_code"}{order_shipping_country_code}{/exp:stash:set}
{exp:stash:set name="order_shipping_country"}{order_shipping_country}{/exp:stash:set}
{exp:stash:set name="order_shipping_company"}{order_shipping_company}{/exp:stash:set}
{exp:stash:set name="order_shipping_zip"}{order_shipping_zip}{/exp:stash:set}
{exp:stash:set name="order_shipping_state"}{order_shipping_state}{/exp:stash:set}
{exp:stash:set name="order_authorize_id"}{authorize_id}{/exp:stash:set}
{exp:stash:set name="order_notes"}{order_notes}{/exp:stash:set}

{/exp:channel:entries}

{!-- Line Items Data, including the auto responder text! --} {exp:stash:set_list name="order_items" parse_tags="yes" parse_conditionals="yes" parse_depth="10" debug="yes"}

{exp:cartthrob:order_items order_id="{stash:e_id}"}

            {exp:stash:append name="order_item_ids"}{entry_id}|{/exp:stash:append}
            {stash:item_id}{entry_id}{/stash:item_id}
            {stash:item_title}{title}{/stash:item_title}
            {stash:item_quantity}{quantity}{/stash:item_quantity}
            {stash:item_price}{price}{/stash:item_price}
            {stash:item_price_inc}{price_plus_tax}{/stash:item_price_inc}
            {stash:item_price_inc_numeric}{price_plus_tax_numeric}{/stash:item_price_inc_numeric}
            {stash:item_weight}{weight}{/stash:item_weight}
            {stash:item_site_id}{site_id}{/stash:item_site_id}
            {stash:item_channel}{channel}{/stash:item_channel}
            {stash:item_subtotal}{subtotal}{/stash:item_subtotal}
            {stash:item_subtotal_numeric}{subtotal_numeric}{/stash:item_subtotal_numeric}
            {stash:item_subtotal_plus_tax}{subtotal_plus_tax}{/stash:item_subtotal_plus_tax}
            {stash:item_subtotal_plus_tax_numeric}{subtotal_plus_tax_numeric}{/stash:item_subtotal_plus_tax_numeric}

            {item_options}
                 {if "{option_label}"=="Variations"}
                    {stash:item_option_variation_name}{option_name}{/stash:item_option_variation_name}
                    {stash:item_option_variation_value}{option_value}{/stash:item_option_variation_value}
                 {/if}
                 {if "{option_label}"=="Notes"}
                    {stash:item_option_notes}{option_value}{/stash:item_option_notes}
                 {/if}
            {/item_options}

{/exp:cartthrob:order_items}

{/exp:stash:set_list}

(there's also a model to get extra product info if needed like pictures etc)

VIEW:

{exp:stash:set name="cart_reorder" type="snippet" parse="no"}

{exp:cartthrob:multi_add_to_cart_form return="/cart"}

<table>

    <thead>
        <tr>
            <th></th><th>Qty.</th><th>Item</th><th>Price<th>Subtotal</th><th>Re-Order Qty.</th>
        </tr>
    </thead>

    <tbody>

{!-- this list has all the info from cartthrob order_items--}
{exp:stash:get_list name="order_items" parse_tags="yes" parse_depth="5" sort="desc" orderby="item_price_inc_numeric" sort_type="numeric"}

    <tr>
            {!-- filled by product model, has picture, link etc --}
            {exp:stash:get_list:nested name="extended_product_info" parse_tags="yes" parse_depth="10" debug="yes" match="#{item_id}#" against="product_entry_id" unique="product_entry_id"}

        <td align="center">

            {if product_master_image}
                {exp:ce_img:pair src="{product_master_image}" max="100" crop="no" parse="inward"}
                    <img src="{made_url}" width="{width}" height="{height}">
                {/exp:ce_img:pair}
            {/if}

        </td>
        <td>{item_quantity}</td>
        <td><a href="{exp:stash:get name='relative_url_{urlt}'}">{item_title}</a> ({item_id})<br>

                   {exp:switchee variable="{product_channel_short_name}" parse="inward" debug="yes"}
                        {case value="product_inkjet_paper"}
                            {item_option_variation_name}   <small>({item_option_variation_value})</small>
                        {/case}
                        {case value="service"}
                            {item_option_variation_name}   <small>({item_option_variation_value})</small>
                            <br><br>Notes (which you can change if re-ordering):<br><textarea rows="3" cols="50" name="item_options[{product_entry_id}][cf_service_notes]"  value="">{item_option_notes}</textarea>                             
                        {/case}
                        {case default="yes"}
                            <small>{product_sku}</small>
                        {/case}
                    {/exp:switchee}
               {exp:switchee variable="{product_channel_short_name}" parse="inward" debug="yes"}

                       {case value="product_package"}
                        <br><br><em>Contents:</em><br><small>

                            {exp:stash:get_list:nest2 name="pkg_items_{item_id}"}
                                {pkg_product_title} <br>
                            {/exp:stash:get_list:nest2}
                            </small>
                        {/case}
                    {/exp:switchee}
        </td>
        <td>
        {item_price_inc}
        </td>
        <td>
        {item_subtotal_plus_tax}
        </td>
        <td align="center">
           {exp:switchee variable="{product_channel_short_name}" parse="inward" debug="yes"}
                {case value="product_inkjet_paper"}
                    <input type="hidden" name="item_options[{count}][cf_paper_variations]" size="2" value="{item_option_variation_value}">
                    {/case}
                {case value="service"}
                    <input type="hidden" name="item_options[{count}][cf_service_variations]" size="2" value="{item_option_variation_value}">
                    {/case}
            {/exp:switchee}
            <input type="hidden" name="entry_id[{count}]" size="2" value="{product_entry_id}">          
            <input type="hidden" name="item_options[{count}][channel]" size="2" value="{product_channel_short_name}">           
            <span class="qty">
            <input type="text" name="quantity[{count}]" class="qty__field js-qty-field" value="0">
                <button class="qty__btn js-qty-inc">+</button>
                <button class="qty__btn js-qty-dec">-</button>
            </span>
            {!--<input type="text" name="quantity[{product_entry_id}]" size="2" value="0">--}
        </td>
        {/exp:stash:get_list:nested}

    </tr>
{/exp:stash:get_list}

<tr>
    <td></td><td></td><td></td><td>GST</td><td>{stash:order_tax}</td><td><input class="btn" type="submit" value="Add to Cart for Re-Order"></td>
</tr>
<tr>
    <td></td><td></td><td></td><td>Shipping by {stash:order_shipping_option}</td><td>${stash:order_shipping_plus_tax}</td><td></td>
</tr>
<tr>
    <td></td><td></td><td></td><td>Total</td><td>{stash:order_total}</td><td align="center">  </td>
</tr>

</tbody>

</table>

<br><br>

{/exp:cartthrob:multi_add_to_cart_form}

{/exp:stash:set} {!-- cart_reorder --}

..and let me just add one more time - this stack exchange editor component is absolutely awful!!

| improve this answer | |
  • I mean seriously, WTF is going on with those scrollbars - honestly they really need to set up an editor like github and sort this thing out. I think I'm going to jsut pastebin code from now on!! – Jeremy Daalder Oct 27 '14 at 23:29
2

This should really be added as a comment rather than an answer, but since there isn't enough space...

I've looked at our scenario again; we definitely need the plugin, but this is where different build approaches can perhaps give different requirements later on.

Our products run over a number of channels, so we never know which options will have been selected by a customer in their previous orders.

We have a product option for 'weight' which is set as {weight} in our channel.

If we use the native tags, as below...

{item_options}
  Name: {option_name}<br>
  Label: {option_label}<br>
  Value: {option_value}<br>
{/item_options}

...our output is like this:

Name: A4 | 20 sheets
Label: Packs Options
Value: A4-20
Name: 120gsm
Label: Weight (Grammage)
Value: 120gsm

The name is the option customers can select in the respective dropdown field. The label is the field label for the template and the value is the customer selected value (which could arguably be the same as the name).

What we actually need to display in the form for a repeat order would be:

<input type="hidden" name="item_options[0][packs_options]" value="A4-20">
<input type="hidden" name="item_options[0][weight]" value="120gsm">

Using the native tag, we can't access packs_options or weight, so we created the plugin to loop through all the array keys in the item options. However, in another order (or even the same order) the customer could have totally different type of product, and there are too many options to run an if statement or hard code variables on the basis they might be used.

Array
(
[0] => Array
    (
        [order_id] => 6102
        [0] => Array
            (
                [0] => Array
                    (
                        [row_order] => 0
                        [product_options] => Array
                            (
                                [discount] => 0
                                [packs_options] => A4-20
                                [weight] => 120gsm
                            )
                    )
            )
    )
)

We dropped the discount, because the discount on the original order may no longer apply.

Looking at your code example, it looks like you might know which options a customer will have selected at name="item_options[{count}][cf_paper_variations]" - maybe thats the difference between what we both need to achieve?

| improve this answer | |
  • I only know what has been selected because I pull the name/value from {item_options} in the stash model, but yep I am using an {if} statement based on the label there...I still can't really see why you can't do it natively if I am honest but yep, different approaches give different options so no harm in it! – Jeremy Daalder Oct 30 '14 at 4:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.