Allow third party methods to appear in local pickup area (https://github.com/woocommerce/woocommerce-blocks/pull/8256)
* Add get_collectible_method_ids function * Add collectibleMethodIds to asset data registry * Check whether method id is pickup_location/in collectibleMethodIds * Allow selectShippingRate to be called without a package id * Prevent collectible methods showing in the main shipping area * Remove unnecessary pluck and add pickup_location to returned array * No longer insert pickup_location in collectibleMethodIds * Allow third party methods to influence low/high collection price * Update useShippingData to consider any collectible method * Add hasSelectedLocalPickup to shipping types * Add dependency to selectShippingRate in useShippingData * Register collectibleMethodIds as a callback This is so the shipping methods get change to register before this is called. Passing a callback to `add` means it won't be called until just before it is output. * Update supports key to 'local_pickup' * Rename utils/shipping-rates to TS * Convert to TS, add isPackageRateCollectible & hasCollectableRate * Add tests for hasCollectableRate and isPackageRateCollectible * Update shipping controller to output only method names * Make PickupLocation shipping method support local_pickup * Set prefersCollection based on rate ID being collectible * Remove need to retrieve settings and use helper function instead * rename hasCollectableRate to hasCollectibleRate * Use array_reduce and update comments in get_local_pickup_method_ids * Switch order of array_unique and array_values * Remove unneeded dependency * Hyphenate local-pickup so it follows the same format as other features * Update use of collectible to collectable * Change supports feature to be hyphenated
This commit is contained in:
parent
093ec68061
commit
130827a2c3
|
@ -12,10 +12,12 @@ export interface ShippingData {
|
|||
// Returns a function that accepts a shipping rate ID and a package ID.
|
||||
selectShippingRate: (
|
||||
newShippingRateId: string,
|
||||
packageId: string | number
|
||||
packageId?: string | number | undefined
|
||||
) => void;
|
||||
// Only true when ALL packages support local pickup. If true, we can show the collection/delivery toggle
|
||||
isCollectable: boolean;
|
||||
// True when a rate is currently being selected and persisted to the server.
|
||||
isSelectingRate: boolean;
|
||||
|
||||
hasSelectedLocalPickup: boolean;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,10 @@ import {
|
|||
import { useSelect, useDispatch } from '@wordpress/data';
|
||||
import { isObject } from '@woocommerce/types';
|
||||
import { useEffect, useRef, useCallback } from '@wordpress/element';
|
||||
import { deriveSelectedShippingRates } from '@woocommerce/base-utils';
|
||||
import {
|
||||
hasCollectableRate,
|
||||
deriveSelectedShippingRates,
|
||||
} from '@woocommerce/base-utils';
|
||||
import isShallowEqual from '@wordpress/is-shallow-equal';
|
||||
import { previewCart } from '@woocommerce/resource-previews';
|
||||
|
||||
|
@ -43,9 +46,8 @@ export const useShippingData = (): ShippingData => {
|
|||
isLoadingRates: isEditor ? false : store.isCustomerDataUpdating(),
|
||||
isCollectable: rates.every(
|
||||
( { shipping_rates: packageShippingRates } ) =>
|
||||
packageShippingRates.find(
|
||||
( { method_id: methodId } ) =>
|
||||
methodId === 'pickup_location'
|
||||
packageShippingRates.find( ( { method_id: methodId } ) =>
|
||||
hasCollectableRate( methodId )
|
||||
)
|
||||
),
|
||||
isSelectingRate: isEditor
|
||||
|
@ -78,6 +80,12 @@ export const useShippingData = (): ShippingData => {
|
|||
) => Promise< unknown >;
|
||||
};
|
||||
|
||||
const hasSelectedLocalPickup = hasCollectableRate(
|
||||
Object.values( selectedRates.current ).map(
|
||||
( rate ) => rate.split( ':' )[ 0 ]
|
||||
)
|
||||
);
|
||||
|
||||
// Selects a shipping rate, fires an event, and catch any errors.
|
||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||
const selectShippingRate = useCallback(
|
||||
|
@ -92,12 +100,8 @@ export const useShippingData = (): ShippingData => {
|
|||
*
|
||||
* Forces pickup location to be selected for all packages since we don't allow a mix of shipping and pickup.
|
||||
*/
|
||||
const hasSelectedLocalPickup = !! Object.values(
|
||||
selectedRates.current
|
||||
).find( ( rate ) => rate.includes( 'pickup_location:' ) );
|
||||
|
||||
if (
|
||||
newShippingRateId.includes( 'pickup_location:' ) ||
|
||||
hasCollectableRate( newShippingRateId.split( ':' )[ 0 ] ) ||
|
||||
hasSelectedLocalPickup
|
||||
) {
|
||||
selectPromise = dispatchSelectShippingRate( newShippingRateId );
|
||||
|
@ -117,7 +121,11 @@ export const useShippingData = (): ShippingData => {
|
|||
processErrorResponse( error );
|
||||
} );
|
||||
},
|
||||
[ dispatchSelectShippingRate, dispatchCheckoutEvent, selectedRates ]
|
||||
[
|
||||
hasSelectedLocalPickup,
|
||||
dispatchSelectShippingRate,
|
||||
dispatchCheckoutEvent,
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -129,8 +137,6 @@ export const useShippingData = (): ShippingData => {
|
|||
hasCalculatedShipping,
|
||||
isLoadingRates,
|
||||
isCollectable,
|
||||
hasSelectedLocalPickup: !! Object.values( selectedRates.current ).find(
|
||||
( rate ) => rate.includes( 'pickup_location:' )
|
||||
),
|
||||
hasSelectedLocalPickup,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Get the number of packages in a shippingRates array.
|
||||
*
|
||||
* @param {Array} shippingRates Shipping rates and packages array.
|
||||
*/
|
||||
export const getShippingRatesPackageCount = ( shippingRates ) => {
|
||||
return shippingRates.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of rates in a shippingRates array.
|
||||
*
|
||||
* @param {Array} shippingRates Shipping rates and packages array.
|
||||
*/
|
||||
export const getShippingRatesRateCount = ( shippingRates ) => {
|
||||
return shippingRates.reduce( function ( count, shippingPackage ) {
|
||||
return count + shippingPackage.shipping_rates.length;
|
||||
}, 0 );
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
CartShippingPackageShippingRate,
|
||||
CartShippingRate,
|
||||
} from '@woocommerce/type-defs/cart';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Get the number of packages in a shippingRates array.
|
||||
*
|
||||
* @param {Array} shippingRates Shipping rates and packages array.
|
||||
*/
|
||||
export const getShippingRatesPackageCount = (
|
||||
shippingRates: CartShippingRate[]
|
||||
) => {
|
||||
return shippingRates.length;
|
||||
};
|
||||
|
||||
const collectableMethodIds = getSetting< string[] >(
|
||||
'collectableMethodIds',
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* If the package rate's method_id is in the collectableMethodIds array, return true.
|
||||
*/
|
||||
export const isPackageRateCollectable = (
|
||||
rate: CartShippingPackageShippingRate
|
||||
): boolean => collectableMethodIds.includes( rate.method_id );
|
||||
|
||||
/**
|
||||
* Check if the specified rates are collectable. Accepts either an array of rate names, or a single string.
|
||||
*/
|
||||
export const hasCollectableRate = (
|
||||
chosenRates: string[] | string
|
||||
): boolean => {
|
||||
if ( Array.isArray( chosenRates ) ) {
|
||||
return !! chosenRates.find( ( rate ) =>
|
||||
collectableMethodIds.includes( rate )
|
||||
);
|
||||
}
|
||||
return collectableMethodIds.includes( chosenRates );
|
||||
};
|
||||
/**
|
||||
* Get the number of rates in a shippingRates array.
|
||||
*
|
||||
* @param {Array} shippingRates Shipping rates and packages array.
|
||||
*/
|
||||
export const getShippingRatesRateCount = (
|
||||
shippingRates: CartShippingRate[]
|
||||
) => {
|
||||
return shippingRates.reduce( function ( count, shippingPackage ) {
|
||||
return count + shippingPackage.shipping_rates.length;
|
||||
}, 0 );
|
||||
};
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
hasCollectableRate,
|
||||
isPackageRateCollectable,
|
||||
} from '@woocommerce/base-utils';
|
||||
import { CartShippingRate } from '@woocommerce/type-defs/cart';
|
||||
|
||||
jest.mock( '@woocommerce/settings', () => {
|
||||
return {
|
||||
...jest.requireActual( '@woocommerce/settings' ),
|
||||
getSetting: ( setting: string ) => {
|
||||
if ( setting === 'collectableMethodIds' ) {
|
||||
return [ 'local_pickup' ];
|
||||
}
|
||||
return jest
|
||||
.requireActual( '@woocommerce/settings' )
|
||||
.getSetting( setting );
|
||||
},
|
||||
};
|
||||
} );
|
||||
describe( 'hasCollectableRate', () => {
|
||||
it( 'correctly identifies if an array contains a collectable rate', () => {
|
||||
const ratesToTest = [ 'flat_rate', 'local_pickup' ];
|
||||
expect( hasCollectableRate( ratesToTest ) ).toBe( true );
|
||||
const ratesToTest2 = [ 'flat_rate', 'free_shipping' ];
|
||||
expect( hasCollectableRate( ratesToTest2 ) ).toBe( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'isPackageRateCollectable', () => {
|
||||
it( 'correctly identifies if a package rate is collectable or not', () => {
|
||||
const testPackage: CartShippingRate = {
|
||||
package_id: 0,
|
||||
name: 'Shipping',
|
||||
destination: {
|
||||
address_1: '',
|
||||
address_2: '',
|
||||
city: '',
|
||||
state: '',
|
||||
postcode: '',
|
||||
country: '',
|
||||
},
|
||||
items: [],
|
||||
shipping_rates: [
|
||||
{
|
||||
rate_id: 'flat_rate:1',
|
||||
name: 'Flat rate',
|
||||
description: '',
|
||||
delivery_time: '',
|
||||
price: '10',
|
||||
taxes: '0',
|
||||
instance_id: 1,
|
||||
method_id: 'flat_rate',
|
||||
meta_data: [],
|
||||
selected: true,
|
||||
currency_code: 'USD',
|
||||
currency_symbol: '$',
|
||||
currency_minor_unit: 2,
|
||||
currency_decimal_separator: '.',
|
||||
currency_thousand_separator: ',',
|
||||
currency_prefix: '$',
|
||||
currency_suffix: '',
|
||||
},
|
||||
{
|
||||
rate_id: 'local_pickup:2',
|
||||
name: 'Local pickup',
|
||||
description: '',
|
||||
delivery_time: '',
|
||||
price: '0',
|
||||
taxes: '0',
|
||||
instance_id: 2,
|
||||
method_id: 'local_pickup',
|
||||
meta_data: [],
|
||||
selected: false,
|
||||
currency_code: 'USD',
|
||||
currency_symbol: '$',
|
||||
currency_minor_unit: 2,
|
||||
currency_decimal_separator: '.',
|
||||
currency_thousand_separator: ',',
|
||||
currency_prefix: '$',
|
||||
currency_suffix: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(
|
||||
isPackageRateCollectable( testPackage.shipping_rates[ 0 ] )
|
||||
).toBe( false );
|
||||
expect(
|
||||
isPackageRateCollectable( testPackage.shipping_rates[ 1 ] )
|
||||
).toBe( true );
|
||||
} );
|
||||
} );
|
|
@ -17,6 +17,7 @@ import { Icon, mapMarker } from '@wordpress/icons';
|
|||
import RadioControl from '@woocommerce/base-components/radio-control';
|
||||
import type { RadioControlOption } from '@woocommerce/base-components/radio-control/types';
|
||||
import { CartShippingPackageShippingRate } from '@woocommerce/types';
|
||||
import { isPackageRateCollectable } from '@woocommerce/base-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -118,7 +119,7 @@ const Block = (): JSX.Element | null => {
|
|||
|
||||
// Get pickup locations from the first shipping package.
|
||||
const pickupLocations = ( shippingRates[ 0 ]?.shipping_rates || [] ).filter(
|
||||
( { method_id: methodId } ) => methodId === 'pickup_location'
|
||||
isPackageRateCollectable
|
||||
);
|
||||
|
||||
const [ selectedOption, setSelectedOption ] = useState< string >(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart';
|
||||
import { hasCollectableRate } from '@woocommerce/base-utils';
|
||||
|
||||
export interface minMaxPrices {
|
||||
min: CartShippingPackageShippingRate | undefined;
|
||||
|
@ -25,7 +26,7 @@ export function getShippingPrices(
|
|||
lowestRate: CartShippingPackageShippingRate | undefined,
|
||||
currentRate: CartShippingPackageShippingRate
|
||||
) => {
|
||||
if ( currentRate.method_id === 'pickup_location' ) {
|
||||
if ( hasCollectableRate( currentRate.method_id ) ) {
|
||||
return lowestRate;
|
||||
}
|
||||
if (
|
||||
|
@ -44,7 +45,7 @@ export function getShippingPrices(
|
|||
highestRate: CartShippingPackageShippingRate | undefined,
|
||||
currentRate: CartShippingPackageShippingRate
|
||||
) => {
|
||||
if ( currentRate.method_id === 'pickup_location' ) {
|
||||
if ( hasCollectableRate( currentRate.method_id ) ) {
|
||||
return highestRate;
|
||||
}
|
||||
if (
|
||||
|
@ -83,7 +84,7 @@ export function getLocalPickupPrices(
|
|||
lowestRate: CartShippingPackageShippingRate | undefined,
|
||||
currentRate: CartShippingPackageShippingRate
|
||||
) => {
|
||||
if ( currentRate.method_id !== 'pickup_location' ) {
|
||||
if ( hasCollectableRate( currentRate.method_id ) ) {
|
||||
return lowestRate;
|
||||
}
|
||||
if (
|
||||
|
@ -101,7 +102,7 @@ export function getLocalPickupPrices(
|
|||
highestRate: CartShippingPackageShippingRate | undefined,
|
||||
currentRate: CartShippingPackageShippingRate
|
||||
) => {
|
||||
if ( currentRate.method_id !== 'pickup_location' ) {
|
||||
if ( hasCollectableRate( currentRate.method_id ) ) {
|
||||
return highestRate;
|
||||
}
|
||||
if (
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { useShippingData } from '@woocommerce/base-context/hooks';
|
||||
import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout';
|
||||
import { getShippingRatesPackageCount } from '@woocommerce/base-utils';
|
||||
import {
|
||||
getShippingRatesPackageCount,
|
||||
hasCollectableRate,
|
||||
} from '@woocommerce/base-utils';
|
||||
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
|
||||
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
|
||||
import { useEditorContext, noticeContexts } from '@woocommerce/base-context';
|
||||
|
@ -66,8 +69,9 @@ const Block = (): JSX.Element | null => {
|
|||
...shippingRatesPackage,
|
||||
shipping_rates: shippingRatesPackage.shipping_rates.filter(
|
||||
( shippingRatesPackageRate ) =>
|
||||
shippingRatesPackageRate.method_id !==
|
||||
'pickup_location'
|
||||
! hasCollectableRate(
|
||||
shippingRatesPackageRate.method_id
|
||||
)
|
||||
),
|
||||
};
|
||||
} )
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { select } from '@wordpress/data';
|
||||
import { hasCollectableRate } from '@woocommerce/base-utils';
|
||||
import { isString, objectHasProp } from '@woocommerce/types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -83,7 +85,12 @@ export const prefersCollection = ( state: CheckoutState ) => {
|
|||
const selectedRate = shippingRates[ 0 ].shipping_rates.find(
|
||||
( rate ) => rate.selected
|
||||
);
|
||||
return selectedRate?.method_id === 'pickup_location';
|
||||
if (
|
||||
objectHasProp( selectedRate, 'method_id' ) &&
|
||||
isString( selectedRate.method_id )
|
||||
) {
|
||||
return hasCollectableRate( selectedRate?.method_id );
|
||||
}
|
||||
}
|
||||
return state.prefersCollection;
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@ class PickupLocation extends WC_Shipping_Method {
|
|||
$this->title = $this->get_option( 'title' );
|
||||
$this->tax_status = $this->get_option( 'tax_status' );
|
||||
$this->cost = $this->get_option( 'cost' );
|
||||
$this->supports = [ 'local-pickup' ];
|
||||
$this->pickup_locations = get_option( $this->id . '_pickup_locations', [] );
|
||||
add_filter( 'woocommerce_attribute_label', array( $this, 'translate_meta_data' ), 10, 3 );
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ class ShippingController {
|
|||
true
|
||||
);
|
||||
}
|
||||
$this->asset_data_registry->add( 'collectableMethodIds', array( $this, 'get_local_pickup_method_ids' ), true );
|
||||
add_action( 'rest_api_init', [ $this, 'register_settings' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'hydrate_client_settings' ] );
|
||||
|
@ -60,6 +61,32 @@ class ShippingController {
|
|||
add_filter( 'pre_update_option_pickup_location_pickup_locations', array( $this, 'flush_cache' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of payment method ids that support the 'local-pickup' feature.
|
||||
*
|
||||
* @return string[] List of payment method ids that support the 'local-pickup' feature.
|
||||
*/
|
||||
public function get_local_pickup_method_ids() {
|
||||
$all_methods_supporting_local_pickup = array_reduce(
|
||||
WC()->shipping()->get_shipping_methods(),
|
||||
function( $methods, $method ) {
|
||||
if ( $method->supports( 'local-pickup' ) ) {
|
||||
$methods[] = $method->id;
|
||||
}
|
||||
return $methods;
|
||||
},
|
||||
array()
|
||||
);
|
||||
|
||||
// We use array_values because this will be used in JS, so we don't need the (numerical) keys.
|
||||
return array_values(
|
||||
// This array_unique is necessary because WC()->shipping()->get_shipping_methods() can return duplicates.
|
||||
array_unique(
|
||||
$all_methods_supporting_local_pickup
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Local Pickup settings for rest api.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue