From 65c31317424edd7453056649f53f4d55662e2e9e Mon Sep 17 00:00:00 2001 From: Paul Kilmurray Date: Fri, 19 Apr 2024 22:31:44 +0100 Subject: [PATCH] add fix for duplicate SKUs --- includes/API/Orders_Controller.php | 71 +++++++++++++++++------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/includes/API/Orders_Controller.php b/includes/API/Orders_Controller.php index 8cae5f3..0619f6b 100644 --- a/includes/API/Orders_Controller.php +++ b/includes/API/Orders_Controller.php @@ -214,37 +214,33 @@ public function prepare_line_items( $posted, $action = 'create', $item = null ) * * To fix this we check for a variation_id and remove the meta_data before setting the product */ - if ( $item->get_variation_id() ) { - // Get the product variation - $variation = wc_get_product( $item->get_variation_id() ); - if ( $variation ) { - // Get the valid attribute keys and remove 'attribute_' prefix - $valid_keys = array_map( - function ( $attribute_name ) { - return str_replace( 'attribute_', '', $attribute_name ); - }, - array_keys( $variation->get_variation_attributes() ) - ); + if ( $action !== 'create' && $item->get_variation_id() ) { + $attributes = wc_get_product_variation_attributes( $item->get_variation_id() ); + + // Loop through attributes and remove any duplicates + foreach ( $attributes as $key => $value ) { + $attribute = str_replace( 'attribute_', '', $key ); + $meta_data = $item->get_meta( $attribute, false ); + + if ( is_array( $meta_data ) && count( $meta_data ) > 1 ) { + $meta_to_keep = null; + + // Check each meta to find one with an ID to keep. + foreach ( $meta_data as $meta ) { + if ( isset( $meta->id ) ) { + $meta_to_keep = $meta; + break; + } + } - // Get existing meta data on the item - $meta_data = $item->get_meta_data(); - $unique_keys = array(); - - // Iterate over meta data to find and remove duplicates - foreach ( $meta_data as $index => $meta ) { - $meta_id = $meta->id; - $data = $meta->get_data(); - $key = $data['key']; - - if ( \in_array( $key, $valid_keys, true ) ) { - // If the meta data doesn't have an ID, it's considered 'new' and can be replaced by one with an ID. - if ( ! isset( $unique_keys[ $key ] ) || ( isset( $meta_id ) && ! isset( $unique_keys[ $key ]['id'] ) ) ) { - $unique_keys[ $key ] = array( - 'index' => $index, - 'id' => $meta_id, - ); - } else { - // Remove the duplicate meta data. + // If no meta with an ID is found, keep the first one. + if ( ! $meta_to_keep ) { + $meta_to_keep = $meta_data[0]; + } + + // Remove all other meta data for this attribute. + foreach ( $meta_data as $meta ) { + if ( $meta !== $meta_to_keep ) { if ( $meta->id ) { $item->delete_meta_data_by_mid( $meta->id ); } else { @@ -259,6 +255,21 @@ function ( $attribute_name ) { return $item; } + /** + * Gets the product ID from posted ID. + * NOTE: This overrides the parent method to remove the sku check, some users have products duplicated SKUs. + * + * @throws WC_REST_Exception When SKU or ID is not valid. + * @param array $posted Request data. + * @param string $action 'create' to add line item or 'update' to update it. + * @return int + */ + public function get_product_id( $posted, $action = 'create' ) { + $data = $posted; + unset( $data['sku'] ); + return parent::get_product_id( $data, $action ); + } + /** * Validate billing email. * NOTE: we have removed the format check to allow empty email addresses.