4

I want to set the default countryId and a default shipping method (there is only one shipping method but contain several rules), so that the cart screen shows this and we can then present a shipping cost. I then want to build a facility for the customer to change their country on the cart screen so that they can update their country to see the new shipping costs.

Any ideas how to approach this?

If I pass in the following;

<input type="hidden" name="shippingAddress[countryId]" value="77">
<input type="hidden" name="shippingMethod" value="standard">

on the product/add to cart screen then the flash messages shows;

ERROR! CART NOT COMPLETELY UPDATED.

And whilst it sets the shippingAddress[countryId] on that product page (I checked for it) when I navigate to the cart screen the shippingAddress[countryId] is then not set at all.

6

I agree with the basic underlying premise that showing full cost - i.e. with shipping - is essential on the cart index page for a modern checkout. Customers loathe what they see as a hidden surprise if they hit shipping costs later in the process and your completion rate plummets in my experience.

Luke's answer is right about the root cause of the issue - Commerce currently requires a customer with a valid 'address' before you can set shipping on the cart, and a 'valid address' is (out of the box) at least a firstName and a lastName . You can definitely throw a countryId in a hidden field as a default, but only in addition to setting the address details.

This has become without doubt my least favourite thing about Commerce as it currently stands, and I have spent WAY more time on solving this problem than anything else in Commerce. To be fair I really should have discussed this a lot more with Luke (Holder, not Pearce above!) - as I really think this needs to be solved (by Commerce) sooner rather than later for a sophisticated checkout flow to be achievable without too much hassle. But at a certain point I was too far along to contemplate going back to scratch with it (as I will have to once a proper fix comes I guess!). So I've hacked away at it and have it all working pretty well.

So what ARE your options (I've tried all three, and none of them are great):

From easiest to hardest:

  1. Create a fake 'address' for your customer (I used Mr. Shipping Quote for this) - i.e. set firstName Shipping, lastName Quote, and have a select for your countries I guess -> so that you can then use standard methods to set shipping on the cart, and then on your shipping page basically update this with the actual details when you get them

  2. (as per Luke above) - Write an adjuster to add this shipping until such a point as you do get an address, then once you do make the adjuster adjsut to $0...as you'll then have shipping set on the cart appropriately 'for real'

  3. (And this is what we do on imagescience.com.au for a variety of reasons) - write a shipping plugin of your own and ALSO give this an option of being called by controller separate to all the regular shipping stuff in Commerce- so that you can then do a bit of fancy JS on the front end to add the result of the controller to the cart totals on your cart page. That is, fake it totally until late in your checkout when you really do have an address.

Of these, I think option 1 is about as far as you want to hack in most situations. Depending on your checkout flow, and how fancy you want to get with address handling/customer address libraries, this can potentially be a relatively simple fix for the issue. It works for basic scenarios, you basically just need, on your address input form, to test for the Shipping Quote values for those fields and if those are set, display blank fields for user input.

Option 2 was where I started and could also work, but it gets fairly fiddly fast and it seems a shame to just abandon all of Commerce's shipping stuff - seemed more future proof to not do it this way, to me. But in retrospect I'm not sure about that, this may have proved easier than option 3 in the end...

Option 3 is actually diabolical to get right. But, it can solve pretty much all the problems. However, there are MANY complications with it because if you allow a flow back and forth between your cart/shipping/payment pages, there are a bunch of scenarios that arise to do with the possibility that the cart MAY or MAY NOT not have shipping on it, and/or DOES/DOES NOT have coupons, discounts applied - to make it work consistently you have to do stuff like manually summing the lineItem totals etc. It works, but it's a right mess if you want a really flexible checkout flow - and it's a lot more complicated than it initially looks. Add to that address handling such as deleting addresses, adding new ones for billing/shipping etc along the way, and you'll end up writing well more JS than you like (well, I have). Whilst I have it working, I honestly can't recommend it as an approach.

In retrospect, I wish I had persisted with option 1 as an approach for longer, although it soon gets complicated by the same issues as option 3 really, if you allow back and forth flow between your cart pages (and how can you not?).

(Oh, and shipping currently doesn't deal with tax, which further complicated getting your totals/tax right with these approaches)

To really solve this area I'd love to see:

  • (The biggie!) Set shipping on the cart without requiring irrelevant data at that point like names - shipping should only need what it actually needs for each shipping option (including custom plugins).
  • Some basic address controllers - that are separate from the cart - add/remove (and addresses should be elements with fields etc!)
  • Tax support with shipping

I wish I could give you a 'this is how you do it' answer but it's really pretty complicated and far from ideal at this point. If you CAN live without shipping being shown in the cart (and just use e.g. a chart as Luke suggests) - your life will be vastly simpler, that's for sure!

  • Thanks for this @jeremydaalder - a great answer and has answered my fears. Having played with this last night I took it as far as option 1 you had described but I stopped in case I was heading down a wrong avenue and ending up with hack that would actually cause more issues in the future. – Terry Upton Sep 22 '16 at 12:05
  • The gripe I had with this approach is as you mentioned having to set an email address, first name and surname. I have done this with hidden fields and dummy for the purpose of changing/updating the countryID and getting the shipping. My problems then start when i proceed onto the login/guest checkout page and then subsequent address fields, as it is using this dummy data as already set. – Terry Upton Sep 22 '16 at 12:05
  • I wonder if there was a way to unset/nullify (make null) these details once you move onto the checkout process, so you essentially wipe out the name, surname and email you set with dummy data. So far I have been unable to work this out. If anyone has any ideas? – Terry Upton Sep 22 '16 at 12:05
  • Well you can use js to hide the dummy data and overwrite with the real data as you proceed... – Jeremy Daalder Sep 22 '16 at 12:20
  • Hi jeremy... Thanks for this great answer... It's a shame that this still is not possible... I've tried option one and it somehow works. I've added: name="shippingAddress[firstName]", name="shippingAddress[lastName]", name="shippingAddress[email]", name="shippingAddress[countryId]" and name="shippingMethod". It adds the content to the cart, but it does not redirect to the cart. It stays on the same page... There's no error message... Did you ran in this problem as well? Cheers Stefan – outline4 May 29 '17 at 16:37
1

A shipping address needs a First Name (firstName) and Last Name (lastName) as they are required fields - This is why your cart won't update. You will also run into issues if you write any address validation for use during the checkout process.

For these reasons it isn't advisable to set fields on an order's address for a customer. It is perhaps a better idea to list your shipping methods and costs manually somewhere on your site or your product pages.

A final solution - An adjuster could be written that adds a base shipping cost to an order without the need for a shipping address. There would be a lot of things to consider whilst implementing something like that though.

  • Is there no way to even set a default country of the commerce/site so at least a fallback shipping can be displayed? – Terry Upton Sep 22 '16 at 10:24
  • There isn't - the only "default" is the store's currency. – Luke Pearce Sep 22 '16 at 11:12
  • Did this change with Craft Commerce 2.2? – outline4 Nov 18 at 10:32
1

I've also been struggling with this a lot. I get why you have to have an address defined in order to process the shipping rules, but the lack of defaults is maddening.

My client's checkout process is very lean - ideally one page. There are no user accounts, no shipping method choice (it's enforced by cart contents and address) and no payment method choice.

It's a challenge to get Commerce to work that way without submitting hidden form fields etc. It was also important to me to show at least an informed estimate of shipping on the cart page (I fear abandoned carts otherwise).

I've explored similar options to Jeremy but in my case I found using an Adjuster by far the easier path (at least for solving shipping cost display).

My adjuster closely resembles the example from the docs. It simply iterates the cart contents and adds to $order->baseShippingCost based on my default country rules. (This is custom code shared with my Shipping Rules). The adjuster takes no action if $order->getShippingAddress() returns a value.

In my cart template I then have a {% if cart.shippingAddress %} conditional and if it doesn't I present the cart.totalShippingCost as an estimate. This works really nicely and I hope will prevent any nasty surprises for customers just before payment.

The other problems to solve related to this were auto-completion of shippingMethod and paymentMethod. I've done this using the onSaveOrder event to set the values if they are empty. You just need to watch this event as it can get into an infinite loop if you don't follow the "out-the-box" Craft checkout process (i.e. set loads of stuff at once).

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.