Fixed comparisons for ranges and wildcards

This commit is contained in:
Mike Jolley 2016-04-22 15:42:20 +01:00
parent 558211a869
commit 23271af0f6
2 changed files with 47 additions and 34 deletions

View File

@ -281,42 +281,56 @@ class WC_Tax {
// Query criteria - these will be ANDed // Query criteria - these will be ANDed
$criteria = array(); $criteria = array();
$criteria[] = $wpdb->prepare( "tax_rate_country IN ( %s, '' )", strtoupper( wc_clean( $country ) ) ); $criteria[] = $wpdb->prepare( "tax_rate_country IN ( %s, '' )", strtoupper( $country ) );
$criteria[] = $wpdb->prepare( "tax_rate_state IN ( %s, '' )", strtoupper( wc_clean( $state ) ) ); $criteria[] = $wpdb->prepare( "tax_rate_state IN ( %s, '' )", strtoupper( $state ) );
$criteria[] = $wpdb->prepare( "tax_rate_class = %s", sanitize_title( $tax_class ) ); $criteria[] = $wpdb->prepare( "tax_rate_class = %s", sanitize_title( $tax_class ) );
// Location matching criteria - ORed // Pre-query postcode ranges for PHP based matching.
$postcode_search = wc_get_wildcard_postcodes( $postcode );
$postcode_ranges = $wpdb->get_results( "SELECT tax_rate_id, location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type = 'postcode' AND location_code LIKE '%-%';" );
if ( $postcode_ranges ) {
$matches = wc_postcode_location_matcher( $postcode, $postcode_ranges, 'tax_rate_id', 'location_code' );
$postcode_search = array_unique( array_merge( $postcode_search, array_values( $matches ) ) );
}
/**
* Location matching criteria - ORed
* Needs to match:
* - rates with no postcodes and cities
* - rates with a matching postcode and city
* - rates with matching postcode, no city
* - rates with matching city, no postcode
*/
$locations_criteria = array(); $locations_criteria = array();
$locations_criteria[] = "locations.location_type IS NULL"; // No locations matches all $locations_criteria[] = "locations.location_type IS NULL";
$locations_criteria[] = $wpdb->prepare( "locations.location_type = 'city' AND locations.location_code = %s", strtoupper( wc_clean( $city ) ) ); // City match $locations_criteria[] = "
$locations_criteria[] = "0 = ( locations.location_type = 'postcode' AND locations.location_code IN ('" . implode( "','", array_map( 'esc_sql', $postcode_search ) ) . "')
SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_tax_rate_locations as sublocations AND (
WHERE sublocations.location_type = 'city' ( locations2.location_type = 'city' AND locations2.location_code = '" . esc_sql( strtoupper( $city ) ) . "' )
AND sublocations.tax_rate_id = tax_rates.tax_rate_id OR NOT EXISTS (
)"; // No cities SELECT sub.tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rate_locations as sub
WHERE sub.location_type = 'city'
$criteria[] = ' ( ' . implode( ' ) OR ( ', $locations_criteria ) . ' ) '; AND sub.tax_rate_id = tax_rates.tax_rate_id
)
// Pre-query for postcode range and wildcard matching )
$rates_with_postcodes = $wpdb->get_results( "SELECT tax_rate_id, location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type = 'postcode';" ); ";
$locations_criteria[] = "
if ( $rates_with_postcodes ) { locations.location_type = 'city' AND locations.location_code = '" . esc_sql( strtoupper( $city ) ) . "'
$rates_with_postcodes_ids = array_unique( wp_list_pluck( $rates_with_postcodes, 'tax_rate_id' ) ); AND NOT EXISTS (
$matches = wc_postcode_location_matcher( $postcode, $rates_with_postcodes, 'tax_rate_id', 'location_code' ); SELECT sub.tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rate_locations as sub
$do_not_match = array_unique( array_diff( $rates_with_postcodes_ids, $matches ) ); WHERE sub.location_type = 'postcode'
AND sub.tax_rate_id = tax_rates.tax_rate_id
// Do not match any rate with a postcode which didn't match the customer postcode )
if ( $do_not_match ) { ";
$criteria[] = "tax_rates.tax_rate_id NOT IN (" . implode( ',', array_map( 'esc_sql', $do_not_match ) ) . ")"; $criteria[] = '( ( ' . implode( ' ) OR ( ', $locations_criteria ) . ' ) )';
}
}
$found_rates = $wpdb->get_results( " $found_rates = $wpdb->get_results( "
SELECT tax_rates.* SELECT tax_rates.*
FROM {$wpdb->prefix}woocommerce_tax_rates as tax_rates FROM {$wpdb->prefix}woocommerce_tax_rates as tax_rates
LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations ON tax_rates.tax_rate_id = locations.tax_rate_id LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations ON tax_rates.tax_rate_id = locations.tax_rate_id
LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations2 ON tax_rates.tax_rate_id = locations2.tax_rate_id LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations2 ON tax_rates.tax_rate_id = locations2.tax_rate_id
WHERE 1=1 AND ( " . implode( ' ) AND ( ', $criteria ) . " ) WHERE 1=1 AND " . implode( ' AND ', $criteria ) . "
GROUP BY tax_rate_id GROUP BY tax_rate_id
ORDER BY tax_rate_priority, tax_rate_order ORDER BY tax_rate_priority, tax_rate_order
" ); " );

View File

@ -1248,19 +1248,18 @@ function wc_get_wildcard_postcodes( $postcode ) {
* @param array $objects Array of postcode objects from Database * @param array $objects Array of postcode objects from Database
* @param string $object_compare_key DB column name for the ID. * @param string $object_compare_key DB column name for the ID.
* @param string $object_compare_key DB column name for the value. * @param string $object_compare_key DB column name for the value.
* @return array Array of matching IDs * @return array Array of matching object ID and values.
*/ */
function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key ) { function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key ) {
$matches = array(); $matches = array();
$wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode ) ); $wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode ) );
$postcodes = wp_list_pluck( $objects, $object_compare_key, $object_id_key );
foreach ( $objects as $object ) { foreach ( $postcodes as $object_id => $compare_against ) {
$compare = $postcode; $compare = $postcode;
$compare_against = $object->$object_compare_key;
$object_id = absint( $object->$object_id_key );
// Handle postcodes containing ranges // Handle postcodes containing ranges
if ( strstr( '-', $compare_against ) ) { if ( strstr( $compare_against, '-' ) ) {
$range = array_map( 'trim', explode( '-', $compare_against ) ); $range = array_map( 'trim', explode( '-', $compare_against ) );
if ( 2 !== sizeof( $range ) ) { if ( 2 !== sizeof( $range ) ) {
@ -1277,12 +1276,12 @@ function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $obj
} }
if ( $compare >= $min && $compare <= $max ) { if ( $compare >= $min && $compare <= $max ) {
$matches[] = $object_id; $matches[ $object_id ] = $compare_against;
} }
// Wildcard and standard comparison // Wildcard and standard comparison
} elseif ( in_array( $compare_against, $wildcard_postcodes ) ) { } elseif ( in_array( $compare_against, $wildcard_postcodes ) ) {
$matches[] = $object_id; $matches[ $object_id ] = $compare_against;
} }
} }