3

We're selling a mix of both tangible and digital products with Commerce.

If an order doesn't require shipping, I'd rather just take them to directly to the final one page checkout/payment form.

Is there a way to determine this natively or do I need to add a "RequireShipping" field and loop through the line items to see if one exists in the user's cart?

2 Answers 2

1

Commerce_OrderModel has a getShippingMethod() that will return a Commerce_ShippingMethodModel class of the current shipping method, or null if nothing has been set.

If you've already got the order in the template is should be as simple as:

{% if order.getShippingMethod() %}
    Shipping.
{% else %}
    No shipping.
{% endif %}
2
  • As a follow up question, Is there a way to check for this similar thing at the cart level? For example, I have not checked out yet, but all products in my cart have freeShipping selected. At this point, I don't know if my whole order qualifies for free shipping so I am checking each line item. Is cart.totalShippingCost accurate at this point in the checkout process?
    – Damon
    Commented Apr 9, 2017 at 19:29
  • A cart is just an OrderModel with its isCompleted property set to false.
    – Brad Bell
    Commented Apr 9, 2017 at 23:22
1

Well, I think somewhere in-between Brad's answer and this is the solution...

I too have a mix of tangible and tangible products on my site. In the CP I have checked the "Free Shipping" checkbox for all non-tangible products when I create them.

Because I eventually will have products that require shipping, I didn't want to completely do away with the shipping screen. Here is what I came up with.

EDIT/UPDATE

After working through this more, I wanted to provide more insight for others who might come across this thread. As always if there is room for improvement, please let me know so it can be updated.

/shop/checkout/addresses/

{% set freeShipping  = false %}

{% for item in cart.lineItems %}
    {% set variant = item.purchasable %}
    {% set product = variant.product %}

    {% if product.freeShipping %}
        {% set freeShipping = true %}
    {% else %}
        {% set freeShipping = false %}
    {% endif %}
{% endfor %}

...

<form method="post" accept-charset="utf-8">
<input type="hidden" name="action" value="commerce/cart/updateCart">

{% if freeShipping %}
    <input type="hidden" name="redirect" value="shop/checkout/payment"/>
{% else %}
    <input type="hidden" name="redirect" value="shop/checkout/shipping"/>
{% endif %}


...

{% if freeShipping %}
    <input type="hidden" name="sameAddress" value="1">
{% else %}
    <input type="hidden" name="sameAddress" value="0">
    <input type="checkbox" name="sameAddress" id="sameAddress"
                                               checked="checked">Use same address for shipping
{% endif %}


...

{% if freeShipping %}
    <button type="submit" class="btn btn-primary">Continue To Payment</button>
{% else %}
    <button type="submit" class="btn btn-primary">Continue To Shipping</button>
{% endif %}

<span>or <a href="{{ url('/shop/cart') }}">Return to cart</a></span>

What I'm doing here is looping through my cart to see if freeShipping has been selected for every product (see above). Later I check the same var and handle the form redirect appropriately. If all products in my cart are set to freeShipping I don't really want to make the user have to enter a shipping address. So I will set the sameAddress for them. This way we still have a shipping address should we need it in the future. Then at the end changing the next button accordingly as well.

/shop/checkout/payment

{% set freeShipping  = false %}
{% for item in cart.lineItems %}
    {% set variant = item.purchasable %}
    {% set product = variant.product %}

    {% if product.freeShipping %}
        {% set freeShipping = true %}
    {% else %}
        {% set freeShipping = false %}
    {% endif %}
{% endfor %}


...

{% for adjustment in cart.adjustments %}
    <div class="adjustment">
        {% if adjustment.name == "Free Shipping" %}
            {% if not freeShipping %}
                <h4>{{ adjustment.name }}</h4>
                <p>{{ adjustment.amount|commerceCurrency(cart.currency) }}
                                    ({{ adjustment.description }})</p>
            {% endif %}
        {% else %}
            <h4>{{ adjustment.name }}</h4>
            <p>{{ adjustment.amount|commerceCurrency(cart.currency) }}
            ({{ adjustment.description }})</p>
        {% endif %}
    </div>
{% endfor %}


...


{% if not cart.shippingMethod %}
    {#
    If the order requires a shipping method be selected, you could redirect back to the shipping page until one is applied to the order.
    {% redirect "shop/checkout/shipping" %}
    You might also want to add a php plugin hook to require
    #}
    {% if freeShipping %}
        {# Shipping not applicable. #}
    {% else %}
        <p><strong>Shipping</strong> None selected. <a href="{{ url('shop/checkout/shipping') }}">
        Choose a shipping method.</a>
        </p>
    {% endif %}
{% endif %}

Same thing here, checking for freeShipping on all items. The biggest change here is (purely cosmetic) I didn't want to show "Free Shipping" since we aren't shipping anything. The last part is just updating existing shipping logic to accommodate the free shipping.

/shop/checkout/shipping

{% set freeShipping  = false %}

{% for item in cart.lineItems %}
    {% set variant = item.purchasable %}
    {% set product = variant.product %}

    {% if product.freeShipping %}
        {% set freeShipping = true %}
    {% else %}
        {% set freeShipping = false %}
    {% endif %}
{% endfor %}

...

{% if freeShipping %}
    <p>None of the items in your cart require shipping.</p>
    <form method="post" accept-charset="utf-8">
        <input type="hidden" name="action" value="commerce/cart/updateCart">
        <input type="hidden" name="redirect" value="shop/checkout/payment">
        <input type="hidden" name="shippingMethod" value="freeShipping">
        {{ getCsrfInput() }}

        <button type="submit" class="btn btn-primary">Continue to payment</button>         
    </form>
{% else %}
    <h3>Shipping Method</h3>
    <p>Select a shipping method below.</p>

    ...
{% endif %}

All we are doing here is checking (again) for freeShipping. If everything qualifies, I only show a button that tells the customer they don't have anything to ship and I send them to the /payment screen. I suppose you could just set a {% redirect %} as well.

2
  • Thanks. I may end up doing something like what you've suggested. I'm still getting my head around the way shipping works...Shipping Categories and Methods and Zones, Oh my...
    – casey
    Commented Apr 10, 2017 at 17:14
  • What I struggled with was I wanted to check if the cart had anything that qualified for shipping before actually getting to the shipping screen. So I don't think checking cart.totalShippingCost would be accurate since I haven't even gotten to that screen yet. Hope this helps, I'd be interested to know what you come up with!
    – Damon
    Commented Apr 10, 2017 at 20:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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