Third in line after:
#3022850: [Addressbook, part 1] Rework the ownership model for customer profiles
#2910193: Allow reusing profile values from another inline form ("Billing same as shipping")

The first two issues do most of the heavy lifting logic-wise.
#3022850: [Addressbook, part 1] Rework the ownership model for customer profiles adds the "Save to my address book" checkbox and prefills the checkout profile form with the default profile values. However, the customer can't choose between multiple profiles. That's the job for this issue.

A still open question is which UX to take:
- Commerce Addressbook 7.x-2.x (select box above the fields which prefills the data)
- Commerce Addressbook 7.x-3.x (select box above a rendered profile, Edit button that shows fields)

First one currently exists in an experimental state. Once we implement the second, we'll be able to choose.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bojanz created an issue. See original summary.

bojanz’s picture

Assigned: Unassigned » bojanz

Spun out a portion to #3055035: Finalize the AddressBookManager service. Going to land that first.

EDIT: Done.

bojanz’s picture

Status: Active » Needs review
FileSize
22.42 KB
55.72 KB

Here's an initial implementation of the 2.x UX. Screenshot also attached.

The UX is simple, there is a "Select an address" dropdown that pre-fills the form. Selecting "+ Enter a new address" gives you an empty form.
The dropdown is not there if the profile type only allows a single profile per customer.

The point of this patch is to help the Commerce team choose the right UX. It is not commit ready.

The 3.x UX patch will follow shortly.

Status: Needs review » Needs work

The last submitted patch, 3: 3053165-2-2x.patch, failed testing. View results

bojanz’s picture

For the sake of history, I am posting the last 2.x UI patch that we tested. Compared to the one in #3 it contains a few code cleanups, bug fixes, and makes the "copy" checkbox also visible for the "edit" use case (when an existing address book profile is selected).

We have decided against proceeding with the 2.x UI because it makes it difficult to differentiate between the "edit" and "add" operations.
Consider this:
- Customer hits checkout. Previous address is selected.
- Customer enters new address, without choosing "Enter a new address" from the dropdown
- Customer completes checkout.

There are two outcomes of this, depending on whether the customer checked the "Update my stored address" checkbox:
a) The new address has replaced the other one in the address book.
b) The new address wasn't entered into the address book at all.
Neither feels optimal. Yes, we could decide to treat the "edit" as "add" based on some magic criteria (did the address_line1 change? etc), but now we're guessing.

This is why we're proceeding with the 3.x UI, which is very similar code-wise. More on that in another comment.

bojanz’s picture

I committed #3065400: Allow AddressBook::copy() to update address book profiles separately to reduce the size of this patch, but it's still huge.
It has gone through several major iterations. Most of the unplanned, additional work came from applying the address book to the admin pages, which is something that the D7 version didn't support.

This is easily the hardest work I've done in the 2.x cycle. The number of code paths through the UI is huge (anonymous VS authenticated customer, admin VS checkout, single VS multiple mode, add VS edit, etc), so it required extensive test coverage (mostly done in CustomerProfileTest). We repeat a lot of that coverage in PaymentCheckoutTest, ensuring that in a real checkout an address can be reused, edited, or replaced.

Any site running Shipping will need to update to today's -dev, because I just committed #3054351: Add support for the Commerce 2.14 address book there.
You also need a recent -dev of Profile.

bojanz credited jsacksick.

bojanz credited mglaman.

bojanz credited rszrama.

bojanz’s picture

Crediting jsacksick, mglaman, rszrama, who have spent significant time discussing and reviewing the patches.

bojanz’s picture

Title: [Addressbook part 3] Complete the UI by allowing choice between multiple addressbook profiles » [Addressbook part 2] Complete the UI by allowing choice between multiple addressbook profiles

Status: Needs review » Needs work

The last submitted patch, 6: 3053165-6-address-book.patch, failed testing. View results

bojanz’s picture

Here is a green patch, along with some screenshots.

We have two modes, activated via the profile type checkbox "Allow multiple profiles per user".
1) Single - Provided in D7 by the Commerce Single Address module. The customer always has a single address book profile. This address book profile always reflects the last entered address (there is no checkbox to opt out of copying). Commonly used for billing information on some sites, especially ones that sell digital products (e.g. Platform.sh).

Single mode in checkout
Single mode in checkout - edit

When in admin mode (admin order/shipment UIs), the copy checkbox is shown and unchecked: "Save to the customer's address book" / "Also update the address in the customer's address book". This allows admins to create orders with one-off addresses.

Single mode on the admin pages
Single mode on the admin pages - edit

2) Multiple - Default. Provided in D7 by the Commerce Address Book module. The customer can have multiple address book profiles, selected via dropdown. When the "+ Enter a new address" option is selected, a "Save to my address book" checkbox is shown, to allow the customer to add a one-off address, or add it to the address book. The "edit" flow will of course update the address book profile, no checkbox is shown in that case cause the intent is obvious.

Multiple mode on checkout
Multiple mode on checkout - edit

When in admin mode (admin order/shipment UIs), the copy checkbox shown when adding a new address will say "Save to the customer's address book", and unchecked. If editing a profile that was already added to the address book, and the two profiles are still identical, the address book profile will be updated.

Multiple mode on the admin pages - new address

Additional touches:
- Assume that a profile was added to the address book for the address "9 Drupal Ave". After that, the address book profile was edited via the user pages, and its phone number was updated. Editing the main profile (on the order / payment method) will now show two options: "9 Drupal Ave (original version)" and "9 Drupal Ave (updated version)". This allows the customer to retain the previous information (and even update it further), or to switch to the latest version of the profile (thus removing the original version).

Address dropdown in multiple mode

bojanz’s picture

The main thing to note in the screenshots above is that once you click "Edit", you get a form, with no Save / Cancel buttons.
We did this to (significantly) simplify the code and to reduce the number of interactions the customer can have. I experimented with having just a Cancel button, like the D7 address book had, but it felt potentially dangerous (someone enters an address and clicks the button below out of habit, boom, changes lost).

If you select a different address in the dropdown, that resets the UI to render mode.

That's the main thing we need to agree on, whether it is okay to not have Save / Cancel buttons.
Naturally, people who end up not liking this UI will be able to swap out the CustomerProfile inline form plugin, but I still want the default UX to have a broad consensus.

czigor’s picture

This looks great!

If I don't have a Cancel button and I want to revert the billing information changes only, my only chance is reloading the page, right?

bojanz’s picture

In multiple mode you can also toggle the dropdown (select a different address, then the previous one again).
However, the profile form is generally short enough that I don't see reverting as a major requirement.

agoradesign’s picture

Here's my partial review. I haven't tried single mode (but the screenshot looks good) and only quickly looked into admin view (never ever created an order that way).

First of all: the implementation really seems to be solid. I've continued several time to next step and then went back, did some changes, etc. And all seemed fine.

I personally like the UI. It's what I would expect as a customer: having my default address pre-selected and easily be able to change to any other address from my address book, as well as enter a new one. Most of the time I do not want to change anything. So this is perfectly cleaned up and saves me from thinking about entering/editing already pre-filled fields and lets me go quickly through checkout

There are only some minor thoughts, where some of them are a matter of taste:

  • when I select an address from my address book and then edit it - should we add a checkbox on whether to sync changes to the stored address? ok, it's in the patch I see ("Also update the address in my address book").. hmm now I'm unsure, if I've just overseen this, or if it was missing!? Could someone plz test this?
  • in the dropdown, should we add postal code + locality to the label, or keep only the street address? Of course, it won't happen often, that you have stored addresses in different cities with the same street name, but it felt a little bit unusual to only choose a street name from a list. but that's a matter of taste for sure
  • we have added an additional field to the profile (phone number). The copy checkbox was placed after the address, but before the phone number field, which is bad. Maybe we should add quite a high weight to the checkbox, so that this won't happen under normal circumstances?
bojanz’s picture

Thanks for the review!

First of all: the implementation really seems to be solid. I've continued several time to next step and then went back, did some changes, etc. And all seemed fine.

That's great to hear. We've done a lot of testing and debugging internally before posting the patch.

when I select an address from my address book and then edit it - should we add a checkbox on whether to sync changes to the stored address? ok, it's in the patch I see ("Also update the address in my address book").. hmm now I'm unsure, if I've just overseen this, or if it was missing!? Could someone plz test this?

The checkbox is there, checked but hidden. If you're editing an address book profile we assume that clicking "Edit" communicates enough intent that we can assume we're always copying. Of course, if the edited profile has drifted from the address book profile (and has an "original value" suffix), the copy checkbox is off (but still hidden). That way we don't ask questions we know the answer to.

in the dropdown, should we add postal code + locality to the label, or keep only the street address? Of course, it won't happen often, that you have stored addresses in different cities with the same street name, but it felt a little bit unusual to only choose a street name from a list. but that's a matter of taste for sure

Good question. This was also raised in internal review. Opened #3067030: Expand the default profile label (currently: address_line1) with some initial thoughts.

we have added an additional field to the profile (phone number). The copy checkbox was placed after the address, but before the phone number field, which is bad. Maybe we should add quite a high weight to the checkbox, so that this won't happen under normal circumstances?

Nice catch. I'll add a large #weight in the next/final reroll.

Jons’s picture

Not tried the code yet, but we'd want to stop some roles (B2B) from editing at all, but just select from a separately-managed list of addresses in their address book. I guess we'll be able to do via an alter hook?

bojanz’s picture

@Jons
You'll probably want to copy the CustomerProfile inline form class into your own module, and customize it there (point the plugin definition to it via hook_commerce_inline_form_info_alter()). I'm expecting you'd need/want to do significant changes to the logic, which would be tricky to do from a form alter hook.

Jons’s picture

Ok - thanks

rhovland’s picture

I have given the new address book functionality some testing. I think the new UI looks great. It keeps things simple for repeat customers by not showing a bunch of forms they have to think about and then skip to the next step.

There was two minor issues I could see evaluating the forms from the viewpoint of our customers.
The label for the address select box does not describe what it contains. The current label of "Select an address" hides the fact that you want to open the list of options if you want to enter a new address. Customers might instead click the edit button instead to enter a new address if they don't think to open the select list. I would suggest changing it to something like "Select or enter a new address".

address-selection-language.png

I feel that the edit button should read "Edit address" to be clear about it's function.

rhovland’s picture

Jons’s picture

A thought about Stripe - this creates a new profile for each card entry ('payment method'). Ideally it should point to a selected existing profile or the one to be created, if new?
Either way its helpful to adapt PaymentMethodListBuilder to show more profile info.

bojanz’s picture

@Jons
Each parent entity (order, payment method) must get its own profile to protect against modification. That's what allows you to delete an address book profile without affecting existing orders and payment methods. And that's what allows you to modify one order's address without modifying all of them. Our payment methods should map 1-1 to Stripe payment methods, where addresses aren't a standalone object, so we should be fine.

Londova’s picture

How to manage the Shipping profile, when Shipping method is "Self-Collectiion" from Store address?
It looks that I need to select first the Shipping metod, and after that the Profile (which is against the existing logic).

mglaman’s picture

Londova - there is a shipping issue to not require a shipping profile, which I assume is what you need. We require the same for in-store pickups.

  • bojanz committed 0d56b0d on 8.x-2.x
    Issue #3053165 by bojanz, rhovland, mglaman, jsacksick, rszrama,...
bojanz’s picture

Status: Needs review » Fixed

Okay, since the initial reviews were positive, I'm going ahead and committing #13, with two additional changes:
1) Require a stable version of Profile again, now that we've tagged RC6
2) Add #weight to the select_address and copy_to_address_book elements.

We discussed #22 and thought that the feedback was valid but were unsure about the naming changes, so we decided to wait for more feedback.

Let's continue testing -dev. I won't tag 2.14 for another week. Thanks, everyone!

Immediate next focus: Finishing #3059633: Provide a better addressbook UI for the user pages.

Jons’s picture

Status: Fixed » Needs review

I think getShippingProfile(OrderInterface $order) in commerce_shipping/src/EventSubscriber/AddressBookSubscriber.php needs to check if 'shipments' field is present on $order and skip getting the ShipingProfile if not. This is to accommodate non-shipping related checkout flows. On an existing site I otherwise get: EntityStorageException: Field shipments is unknown.

bojanz’s picture

Status: Needs review » Fixed

Why report it here? Wrong module

NicolasGraph’s picture

Hi, the patch seems to work well, even if I would really like to see a submit button on edit.
I already tried to create an alternative CustomProfile class to change the UI (I mean, not just one or two titles) ; it sort of works but for now I just overrides the existing class and, in fact, I wouldn't like to see my changes applied the backend.
I tried to alter the checkout form on the frontend by doing something like so but I'm missing something :

  if (isset($form['shipping_information']['shipping_profile'])) {
    $customer_profile = $form['shipping_information']['shipping_profile']['#inline_form'];
    $custom_customer_profile_form = \Drupal::service('plugin.manager.commerce_inline_form')
      ->createInstance(
        'custom_' . $customer_profile->getPluginId(),
        $customer_profile->getConfiguration(),
        $customer_profile->getEntity(),
      );
    $form['shipping_information']['shipping_profile']['#inline_form'] = $custom_customer_profile_form;
  }

Does anyone could point me too the right direction for this ?

bojanz’s picture

@NicolasGraph
Never tried it, but my gut tells me that a form alter is too late for swapping out the inline form. You'd need to override the checkout pane and do it there.

Personally I'd still swap out the CustomerProfile inline form, inherit from it, check for $this->configuration['admin'], which indicates backend usage, and in that case fall back to the parent implementation:

$inline_form = parent::buildInlineForm($inline_form, $form_state);
if ($this->configuration['admin']) {
  // Use the parent logic.
  return $inline_form;  
}
else {
  // Do our thing.
}
NicolasGraph’s picture

@bojanz
Thanks for the tip ; using the kind of fallback you mentioned seems good and simple enough to me, I'll give it a try.

NicolasGraph’s picture

Not sure how I should report this...
In the CustomerProfile at line 170, $triggering_element is not defined.
Also, shouldn't we unset the 'revision_log_message' item of the profile forms on the front-end ?

bojanz’s picture

In the CustomerProfile at line 170, $triggering_element is not defined.

Thanks for catching this, opened #3068649: Undefined variable $triggering_element in CustomerProfile::buildInlineForm().

Also, shouldn't we unset the 'revision_log_message' item of the profile forms on the front-end ?

Yes: #3061017: Remove the revision_log_message field in CustomerProfile.

NicolasGraph’s picture

Thanks for your reply and for the credit @bojanz.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.