Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Sync customer data during checkout with draft orders. #4197

Merged
merged 4 commits into from May 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 23 additions & 6 deletions assets/js/base/context/hooks/use-customer-data.ts
Expand Up @@ -9,6 +9,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal';
import {
formatStoreApiErrorMessage,
pluckAddress,
pluckEmail,
} from '@woocommerce/base-utils';
import type {
CartResponseBillingAddress,
Expand All @@ -26,8 +27,16 @@ declare type CustomerData = {
import { useStoreCart } from './cart/use-store-cart';
import { useStoreNotices } from './use-store-notices';

function instanceOfCartResponseBillingAddress(
address: CartResponseBillingAddress | CartResponseShippingAddress
): address is CartResponseBillingAddress {
return 'email' in address;
}

/**
* Does a shallow compare of important address data to determine if the cart needs updating.
* Does a shallow compare of important address data to determine if the cart needs updating on the server.
*
* This takes the current and previous address into account, as well as the billing email field.
*
* @param {Object} previousAddress An object containing all previous address information
* @param {Object} address An object containing all address information
Expand All @@ -40,12 +49,20 @@ const shouldUpdateAddressStore = <
previousAddress: T,
address: T
): boolean => {
if ( ! address.country ) {
return false;
if (
instanceOfCartResponseBillingAddress( address ) &&
pluckEmail( address ) !==
pluckEmail( previousAddress as CartResponseBillingAddress )
) {
return true;
}
return ! isShallowEqual(
pluckAddress( previousAddress ),
pluckAddress( address )

return (
!! address.country &&
! isShallowEqual(
pluckAddress( previousAddress ),
pluckAddress( address )
)
);
};

Expand Down
7 changes: 4 additions & 3 deletions assets/js/base/context/tsconfig.json
Expand Up @@ -8,9 +8,10 @@
"../../settings/blocks/index.ts",
"../../base/hooks/index.js",
"../utils/type-guards.ts",
"../../base/utils/",
"../../data/",
"../../type-defs"
"../../base/utils/",
"../../data/",
"../../type-defs",
"../components"
],
"exclude": [ "**/test/**" ]
}
Expand Up @@ -3,6 +3,11 @@
*/
import { defaultAddressFields } from '@woocommerce/settings';
import prepareAddressFields from '@woocommerce/base-components/cart-checkout/address-form/prepare-address-fields';
import { isEmail } from '@wordpress/url';
import type {
CartResponseBillingAddress,
CartResponseShippingAddress,
} from '@woocommerce/types';

/**
* pluckAddress takes a full address object and returns relevant fields for calculating
Expand All @@ -21,27 +26,60 @@ export const pluckAddress = ( {
state = '',
city = '',
postcode = '',
} ) => ( {
}: CartResponseBillingAddress | CartResponseShippingAddress ): {
country: string;
state: string;
city: string;
postcode: string;
} => ( {
country: country.trim(),
state: state.trim(),
city: city.trim(),
postcode: postcode ? postcode.replace( ' ', '' ).toUpperCase() : '',
} );

/**
* pluckEmail takes a full address object and returns only the email address, if set and valid. Otherwise returns an empty string.
*
* @param {Object} address An object containing all address information
* @param {string} address.email The email address.
* @return {string} The email address.
*/
export const pluckEmail = ( {
email = '',
}: CartResponseBillingAddress ): string =>
isEmail( email ) ? email.trim() : '';

/**
* Type-guard.
*/
const isValidAddressKey = (
key: string,
address: CartResponseBillingAddress | CartResponseShippingAddress
): key is keyof typeof address => {
return key in address;
};

/**
* Sets fields to an empty string in an address if they are hidden by the settings in countryLocale.
*
* @param {Object} address The address to empty fields from.
* @return {Object} The address with hidden fields values removed.
*/
export const emptyHiddenAddressFields = ( address ) => {
export const emptyHiddenAddressFields = <
T extends CartResponseBillingAddress | CartResponseShippingAddress
>(
address: T
): T => {
const fields = Object.keys( defaultAddressFields );
const addressFields = prepareAddressFields( fields, {}, address.country );
const newAddress = Object.assign( {}, address );
addressFields.forEach( ( field ) => {
if ( field.hidden ) {
newAddress[ field.key ] = '';
const newAddress = Object.assign( {}, address ) as T;

addressFields.forEach( ( { key = '', hidden = false } ) => {
if ( hidden && isValidAddressKey( key, address ) ) {
newAddress[ key ] = '';
}
} );

return newAddress;
};
45 changes: 43 additions & 2 deletions src/StoreApi/Routes/CartUpdateCustomer.php
Expand Up @@ -119,9 +119,50 @@ protected function get_route_post_response( \WP_REST_Request $request ) {
);
wc()->customer->save();

$cart->calculate_shipping();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculate_shipping was redundant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cart class has a calculate_totals method which does the same thing.

$cart->calculate_totals();
$this->calculate_totals();
$this->maybe_update_order();

return rest_ensure_response( $this->schema->get_item_response( $cart ) );
}

/**
* If there is a draft order, update customer data there also.
*
* @return void
*/
protected function maybe_update_order() {
$draft_order_id = wc()->session->get( 'store_api_draft_order', 0 );
$draft_order = $draft_order_id ? wc_get_order( $draft_order_id ) : false;

if ( ! $draft_order ) {
return;
}

$draft_order->set_props(
[
'billing_first_name' => wc()->customer->get_billing_first_name(),
'billing_last_name' => wc()->customer->get_billing_last_name(),
'billing_company' => wc()->customer->get_billing_company(),
'billing_address_1' => wc()->customer->get_billing_address_1(),
'billing_address_2' => wc()->customer->get_billing_address_2(),
'billing_city' => wc()->customer->get_billing_city(),
'billing_state' => wc()->customer->get_billing_state(),
'billing_postcode' => wc()->customer->get_billing_postcode(),
'billing_country' => wc()->customer->get_billing_country(),
'billing_email' => wc()->customer->get_billing_email(),
'billing_phone' => wc()->customer->get_billing_phone(),
'shipping_first_name' => wc()->customer->get_shipping_first_name(),
'shipping_last_name' => wc()->customer->get_shipping_last_name(),
'shipping_company' => wc()->customer->get_shipping_company(),
'shipping_address_1' => wc()->customer->get_shipping_address_1(),
'shipping_address_2' => wc()->customer->get_shipping_address_2(),
'shipping_city' => wc()->customer->get_shipping_city(),
'shipping_state' => wc()->customer->get_shipping_state(),
'shipping_postcode' => wc()->customer->get_shipping_postcode(),
'shipping_country' => wc()->customer->get_shipping_country(),
]
);

$draft_order->save();
}
}