diff --git a/includes/API/Customers_Controller.php b/includes/API/Customers_Controller.php index 3ba14975..2241b547 100644 --- a/includes/API/Customers_Controller.php +++ b/includes/API/Customers_Controller.php @@ -16,6 +16,7 @@ use WP_REST_Response; use WP_User; use WP_User_Query; +use WP_Error; /** * Product Tgas controller class. @@ -172,7 +173,7 @@ public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $reque $this->wcpos_register_wc_rest_api_hooks(); $params = $request->get_params(); - // Optimised query for getting all product IDs + // Optimised query for getting all user IDs. if ( isset( $params['posts_per_page'] ) && -1 == $params['posts_per_page'] && isset( $params['fields'] ) ) { $dispatch_result = $this->wcpos_get_all_posts( $params['fields'] ); } @@ -198,7 +199,7 @@ public function wcpos_register_wc_rest_api_hooks(): void { public function wcpos_customer_response( WP_REST_Response $response, WP_User $user_data, WP_REST_Request $request ): WP_REST_Response { $data = $response->get_data(); - // Add the uuid to the response + // Add the uuid to the response. $this->maybe_add_user_uuid( $user_data ); /* @@ -221,7 +222,7 @@ function ( $meta ) { } ); - // Convert to WC REST API expected format + // Convert to WC REST API expected format. $data['meta_data'] = array_map( function ( $meta ) { return array( @@ -236,7 +237,7 @@ function ( $meta ) { Logger::log( 'Error getting customer meta data: ' . $e->getMessage() ); } - // Set any changes to the response data + // Set any changes to the response data. $response->set_data( $data ); // $this->log_large_rest_response( $response, $user_data->ID ); @@ -250,30 +251,66 @@ function ( $meta ) { * multisite would return all users from all sites, not just the current site. * Also, querying by role is not as simple as querying by post type. * - * @param array $fields + * @param array $fields Fields to return. Default is ID. * * @return array|WP_Error */ public function wcpos_get_all_posts( array $fields = array() ): array { - $args = array( - 'fields' => 'ID', // Only return user IDs - ); - $roles = 'all'; // @TODO: could be an array of roles, like ['customer', 'cashier'] + global $wpdb; - if ( 'all' !== $roles ) { - $args['role__in'] = $roles; - } + $id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields; - $user_query = new WP_User_Query( $args ); + $args = array( + 'fields' => array( 'ID', 'registered' ), // Return only the ID and registered date. + // 'role__in' => 'all', // @TODO: could be an array of roles, like ['customer', 'cashier'] + ); + /** + * The user query is too complex to do a direct sql query, eg: multisite would return all users from all sites, + * not just the current site. Also, querying by role is not as simple as querying by post type. + * + * For now we get all user ids and all 'last_update' meta values, then combine them into an array of objects. + */ try { - $user_ids = $user_query->get_results(); + $user_query = new WP_User_Query( $args ); + $users = $user_query->get_results(); + $last_updates = array(); + + if ( $id_with_modified_date ) { + $last_update_results = $wpdb->get_results( + " + SELECT user_id, meta_value + FROM $wpdb->usermeta + WHERE meta_key = 'last_update'", + OBJECT_K + ); + + foreach ( $last_update_results as $user_id => $meta ) { + $last_updates[ $user_id ] = $meta->meta_value; + } + } - return array_map( array( $this, 'wcpos_format_id' ), $user_ids ); + // Merge user IDs with their corresponding 'last_updated' values or fallback to user_registered. + $user_data = array_map( + function ( $user ) use ( $last_updates, $id_with_modified_date ) { + $user_info = array( 'id' => (int) $user->ID ); + if ( $id_with_modified_date ) { + if ( isset( $last_updates[ $user->ID ] ) ) { + $user_info['date_modified_gmt'] = wc_rest_prepare_date_response( $last_updates[ $user->ID ] ); + } else { + $user_info['date_modified_gmt'] = wc_rest_prepare_date_response( $user->user_registered ); + } + } + return $user_info; + }, + $users + ); + + return $user_data; } catch ( Exception $e ) { Logger::log( 'Error fetching order IDs: ' . $e->getMessage() ); - return new \WP_Error( + return new WP_Error( 'woocommerce_pos_rest_cannot_fetch', 'Error fetching customer IDs.', array( 'status' => 500 ) @@ -281,7 +318,7 @@ public function wcpos_get_all_posts( array $fields = array() ): array { } } - /* + /** * Filter arguments, before passing to WP_User_Query, when querying users via the REST API. * * @param array $prepared_args Array of arguments for WP_User_Query. @@ -292,7 +329,7 @@ public function wcpos_get_all_posts( array $fields = array() ): array { public function wcpos_customer_query( array $prepared_args, WP_REST_Request $request ): array { $query_params = $request->get_query_params(); - // add modified_after date_modified_gmt + // add modified_after date_modified_gmt. if ( isset( $query_params['modified_after'] ) && '' !== $query_params['modified_after'] ) { $timestamp = strtotime( $query_params['modified_after'] ); $prepared_args['meta_query'] = $this->wcpos_combine_meta_queries( @@ -307,7 +344,7 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req ); } - // Handle orderby cases + // Handle orderby cases. if ( isset( $query_params['orderby'] ) ) { switch ( $query_params['orderby'] ) { case 'first_name': @@ -343,7 +380,7 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req } } - // Handle search + // Handle search. if ( isset( $query_params['search'] ) && ! empty( $query_params['search'] ) ) { $search_keyword = $query_params['search']; @@ -368,7 +405,7 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req 'value' => $search_keyword, 'compare' => 'LIKE', ), - // WooCommerce billing fields + // WooCommerce billing fields. array( 'key' => 'billing_first_name', 'value' => $search_keyword, @@ -396,11 +433,11 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req ), ); - // Combine the search meta_query with the existing meta_query + // Combine the search meta_query with the existing meta_query. $prepared_args['meta_query'] = $this->wcpos_combine_meta_queries( $search_meta_query, $prepared_args['meta_query'] ); } - // Handle include/exclude + // Handle include/exclude. if ( isset( $request['wcpos_include'] ) || isset( $request['wcpos_exclude'] ) ) { add_action( 'pre_user_query', array( $this, 'wcpos_include_exclude_users_by_id' ) ); } @@ -411,19 +448,19 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req /** * Add user_email and user_login to the user query. * - * @param WP_User_Query $query + * @param WP_User_Query $query The WP_User_Query instance (passed by reference). */ public function wcpos_search_user_table( $query ): void { global $wpdb; - // Remove the hook + // Remove the hook. remove_action( 'pre_user_query', array( $this, 'wcpos_search_user_table' ) ); - // Get the search keyword + // Get the search keyword. $query_params = $query->query_vars; $search_keyword = $query_params['_wcpos_search']; - // Prepare the LIKE statement + // Prepare the LIKE statement. $like_email = '%' . $wpdb->esc_like( $search_keyword ) . '%'; $like_login = '%' . $wpdb->esc_like( $search_keyword ) . '%'; @@ -435,10 +472,10 @@ public function wcpos_search_user_table( $query ): void { $pattern = "/\(\s*\w+\.meta_key\s*=\s*'[^']+'\s*AND\s*\w+\.meta_value\s*LIKE\s*'[^']+'\s*\)(\s*OR\s*\(\s*\w+\.meta_key\s*=\s*'[^']+'\s*AND\s*\w+\.meta_value\s*LIKE\s*'[^']+'\s*\))*\s*/"; - // Add the search keyword to the query + // Add the search keyword to the query. $modified_where = preg_replace( $pattern, "$insertion$0", $query->query_where ); - // Check if the replacement was successful and assign it back to query_where + // Check if the replacement was successful and assign it back to query_where. if ( $modified_where !== $query->query_where ) { $query->query_where = $modified_where; } @@ -447,22 +484,22 @@ public function wcpos_search_user_table( $query ): void { /** * Include or exclude users by ID. * - * @param WP_User_Query $query + * @param WP_User_Query $query The WP_User_Query instance (passed by reference). */ public function wcpos_include_exclude_users_by_id( $query ) { global $wpdb; - // Remove the hook + // Remove the hook. remove_action( 'pre_user_query', array( $this, 'wcpos_include_exclude_users_by_id' ) ); - // Handle 'wcpos_include' + // Handle 'wcpos_include'. if ( ! empty( $this->wcpos_request['wcpos_include'] ) ) { $include_ids = array_map( 'intval', (array) $this->wcpos_request['wcpos_include'] ); $ids_format = implode( ',', array_fill( 0, count( $include_ids ), '%d' ) ); $query->query_where .= $wpdb->prepare( " AND {$wpdb->users}.ID IN ($ids_format) ", $include_ids ); } - // Handle 'wcpos_exclude' + // Handle 'wcpos_exclude'. if ( ! empty( $this->wcpos_request['wcpos_exclude'] ) ) { $exclude_ids = array_map( 'intval', (array) $this->wcpos_request['wcpos_exclude'] ); $ids_format = implode( ',', array_fill( 0, count( $exclude_ids ), '%d' ) ); diff --git a/includes/API/Orders_Controller.php b/includes/API/Orders_Controller.php index b641d058..870ada1e 100644 --- a/includes/API/Orders_Controller.php +++ b/includes/API/Orders_Controller.php @@ -19,6 +19,7 @@ use WP_REST_Response; use WP_REST_Server; use Automattic\WooCommerce\Utilities\OrderUtil; +use WP_Error; use const WCPOS\WooCommercePOS\PLUGIN_NAME; @@ -595,34 +596,52 @@ public function wcpos_posts_where_order_include_exclude( string $where, $query ) /** * Returns array of all order ids. * - * @param array $fields + * @param array $fields Fields to return. * * @return array|WP_Error */ public function wcpos_get_all_posts( array $fields = array() ): array { - $args = array( - 'limit' => -1, - 'return' => 'ids', - 'status' => array_keys( wc_get_order_statuses() ), // Get valid order statuses + global $wpdb; + + $id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields; + + // Check if HPOS is enabled and custom orders table is used. + $hpos_enabled = class_exists( OrderUtil::class ) && OrderUtil::custom_orders_table_usage_is_enabled(); + $sql = ''; + + $statuses = array_map( + function ( $status ) { + return "'$status'"; + }, + array_keys( wc_get_order_statuses() ) ); - $order_query = new WC_Order_Query( $args ); + if ( $hpos_enabled ) { + $select_fields = $id_with_modified_date ? 'id, date_updated_gmt as date_modified_gmt' : 'id'; + $sql .= "SELECT DISTINCT {$select_fields} FROM {$wpdb->prefix}wc_orders WHERE type = 'shop_order'"; + $sql .= ' AND status IN (' . implode( ',', $statuses ) . ')'; - try { - $order_ids = $order_query->get_orders(); + // Order by date_created_gmt DESC to maintain order consistency. + $sql .= " ORDER BY {$wpdb->prefix}wc_orders.date_created_gmt DESC"; + } else { + $select_fields = $id_with_modified_date ? 'ID as id, post_modified_gmt as date_modified_gmt' : 'ID as id'; + $sql .= "SELECT DISTINCT {$select_fields} FROM {$wpdb->posts} WHERE post_type = 'shop_order'"; + $sql .= ' AND post_status IN (' . implode( ',', $statuses ) . ')'; - return array_map( array( $this, 'wcpos_format_id' ), $order_ids ); - } catch ( Exception $e ) { - Logger::log( 'Error fetching order IDs: ' . $e->getMessage() ); + // Order by post_date DESC to maintain order consistency. + $sql .= " ORDER BY {$wpdb->posts}.post_date DESC"; + } - return new \WP_Error( - 'woocommerce_pos_rest_cannot_fetch', - 'Error fetching order IDs.', - array( 'status' => 500 ) - ); + try { + $results = $wpdb->get_results( $sql ); + return $this->wcpos_format_all_posts_response( $results ); + } catch ( Exception $e ) { + Logger::log( 'Error fetching order data: ' . $e->getMessage() ); + return new WP_Error( 'woocommerce_pos_rest_cannot_fetch', 'Error fetching order data.', array( 'status' => 500 ) ); } } + /** * Filters all query clauses at once. * Covers the fields (SELECT), JOIN, WHERE, GROUP BY, ORDER BY, and LIMIT clauses. diff --git a/includes/API/Product_Categories_Controller.php b/includes/API/Product_Categories_Controller.php index 954bb069..da0a82ee 100644 --- a/includes/API/Product_Categories_Controller.php +++ b/includes/API/Product_Categories_Controller.php @@ -168,9 +168,15 @@ public function wcpos_get_all_posts( array $fields = array() ): array { ); try { - $product_category_ids = get_terms( $args ); - - return array_map( array( $this, 'wcpos_format_id' ), $product_category_ids ); + $results = get_terms( $args ); + + // Format the response. + return array_map( + function ( $item ) { + return array( 'id' => (int) $item ); + }, + $results + ); } catch ( Exception $e ) { Logger::log( 'Error fetching product category IDs: ' . $e->getMessage() ); diff --git a/includes/API/Product_Tags_Controller.php b/includes/API/Product_Tags_Controller.php index 73dbf92f..6015b63a 100644 --- a/includes/API/Product_Tags_Controller.php +++ b/includes/API/Product_Tags_Controller.php @@ -168,9 +168,15 @@ public function wcpos_get_all_posts( array $fields = array() ): array { ); try { - $product_tag_ids = get_terms( $args ); - - return array_map( array( $this, 'wcpos_format_id' ), $product_tag_ids ); + $results = get_terms( $args ); + + // Format the response. + return array_map( + function ( $item ) { + return array( 'id' => (int) $item ); + }, + $results + ); } catch ( Exception $e ) { Logger::log( 'Error fetching product tags IDs: ' . $e->getMessage() ); diff --git a/includes/API/Product_Variations_Controller.php b/includes/API/Product_Variations_Controller.php index 8ef1ebaa..7fb24d44 100644 --- a/includes/API/Product_Variations_Controller.php +++ b/includes/API/Product_Variations_Controller.php @@ -17,6 +17,7 @@ use WP_REST_Response; use WP_REST_Server; use WC_Product_Variation; +use WP_Error; /** * Product Tgas controller class. @@ -379,41 +380,39 @@ public function wcpos_posts_where_product_variation_include_exclude( string $whe * @return array|WP_Error */ public function wcpos_get_all_posts( array $fields = array() ): array { - $parent_id = $this->request['product_id']; + global $wpdb; + + $parent_id = (int) $this->wcpos_request->get_param( 'product_id' ); + $id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields; + + // Build the SELECT clause based on requested fields. + $select_fields = $id_with_modified_date ? 'ID as id, post_modified_gmt as date_modified_gmt' : 'ID as id'; - $args = array( - 'post_type' => 'product_variation', - 'post_status' => 'publish', - 'post_parent' => $parent_id, - 'posts_per_page' => -1, - 'fields' => 'ids', - ); + // Initialize the SQL query. + $sql = "SELECT DISTINCT {$select_fields} FROM {$wpdb->posts}"; + // Apply '_pos_visibility' condition if necessary. if ( $this->wcpos_pos_only_products_enabled() ) { - $args['meta_query'] = array( - 'relation' => 'OR', - array( - 'key' => '_pos_visibility', - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => '_pos_visibility', - 'value' => 'online_only', - 'compare' => '!=', - ), - ); + $sql .= " LEFT JOIN {$wpdb->postmeta} ON ({$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pos_visibility')"; + $sql .= " WHERE {$wpdb->posts}.post_type = 'product_variation' AND {$wpdb->posts}.post_status = 'publish' AND ({$wpdb->postmeta}.post_id IS NULL OR {$wpdb->postmeta}.meta_value != 'online_only')"; + } else { + $sql .= " WHERE {$wpdb->posts}.post_type = 'product_variation' AND {$wpdb->posts}.post_status = 'publish'"; } - $variation_query = new WP_Query( $args ); + // Dynamically add the post_parent clause if a parent ID is provided. + if ( $parent_id ) { + $sql = $wpdb->prepare( $sql . " AND {$wpdb->posts}.post_parent = %d", $parent_id ); + } try { - $variation_ids = $variation_query->posts; - - return array_map( array( $this, 'wcpos_format_id' ), $variation_ids ); + // Execute the query. + $results = $wpdb->get_results( $sql ); + // Format and return the results. + return $this->wcpos_format_all_posts_response( $results ); } catch ( Exception $e ) { Logger::log( 'Error fetching product variation IDs: ' . $e->getMessage() ); - return new \WP_Error( + return new WP_Error( 'woocommerce_pos_rest_cannot_fetch', 'Error fetching product variation IDs.', array( 'status' => 500 ) @@ -421,6 +420,7 @@ public function wcpos_get_all_posts( array $fields = array() ): array { } } + /** * Prepare objects query. * diff --git a/includes/API/Products_Controller.php b/includes/API/Products_Controller.php index 926ec480..c31ca299 100644 --- a/includes/API/Products_Controller.php +++ b/includes/API/Products_Controller.php @@ -16,6 +16,7 @@ use WP_Query; use WP_REST_Request; use WP_REST_Response; +use WP_Error; /** * Products controller class. @@ -427,47 +428,36 @@ public function wcpos_posts_where_product_include_exclude( string $where, WP_Que /** * Returns array of all product ids, name. * - * @param array $fields + * @param array $fields Fields to return. * * @return array|WP_Error */ public function wcpos_get_all_posts( array $fields = array() ): array { - $args = array( - 'post_type' => 'product', - 'post_status' => 'publish', - 'posts_per_page' => -1, - 'fields' => 'ids', - ); + global $wpdb; + + $id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields; + $select_fields = $id_with_modified_date ? 'ID as id, post_modified_gmt as date_modified_gmt' : 'ID as id'; + + // Use SELECT DISTINCT in the initial SQL statement for both cases. + $sql = "SELECT DISTINCT {$select_fields} FROM {$wpdb->posts}"; + // If the '_pos_visibility' condition needs to be applied. if ( $this->wcpos_pos_only_products_enabled() ) { - $args['meta_query'] = array( - 'relation' => 'OR', - array( - 'key' => '_pos_visibility', - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => '_pos_visibility', - 'value' => 'online_only', - 'compare' => '!=', - ), - ); + $sql .= " LEFT JOIN {$wpdb->postmeta} ON ({$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pos_visibility')"; + $sql .= " WHERE post_type = 'product' AND post_status = 'publish' AND ({$wpdb->postmeta}.post_id IS NULL OR {$wpdb->postmeta}.meta_value != 'online_only')"; + } else { + $sql .= " WHERE post_type = 'product' AND post_status = 'publish'"; } - $product_query = new WP_Query( $args ); + // Order by post_date DESC to maintain order consistency. + $sql .= " ORDER BY {$wpdb->posts}.post_date DESC"; try { - $product_ids = $product_query->posts; - - return array_map( array( $this, 'wcpos_format_id' ), $product_ids ); + $results = $wpdb->get_results( $sql ); + return $this->wcpos_format_all_posts_response( $results ); } catch ( Exception $e ) { - Logger::log( 'Error fetching product IDs: ' . $e->getMessage() ); - - return new \WP_Error( - 'woocommerce_pos_rest_cannot_fetch', - 'Error fetching product IDs.', - array( 'status' => 500 ) - ); + Logger::log( 'Error fetching product data: ' . $e->getMessage() ); + return new WP_Error( 'woocommerce_pos_rest_cannot_fetch', 'Error fetching product data.', array( 'status' => 500 ) ); } } diff --git a/includes/API/Taxes_Controller.php b/includes/API/Taxes_Controller.php index 5e3970c4..5270ff4c 100644 --- a/includes/API/Taxes_Controller.php +++ b/includes/API/Taxes_Controller.php @@ -235,14 +235,12 @@ public function wcpos_get_all_posts( array $fields = array() ): array { ARRAY_A ); - // Convert array of arrays into array of strings (ids) - $all_ids = array_map( + // Format the response. + return array_map( function ( $item ) { - return \strval( $item['id'] ); + return array( 'id' => (int) $item['id'] ); }, $results ); - - return array_map( array( $this, 'wcpos_format_id' ), $all_ids ); } } diff --git a/includes/API/Traits/WCPOS_REST_API.php b/includes/API/Traits/WCPOS_REST_API.php index 8b5c09c8..15ffbd9d 100644 --- a/includes/API/Traits/WCPOS_REST_API.php +++ b/includes/API/Traits/WCPOS_REST_API.php @@ -8,12 +8,30 @@ trait WCPOS_REST_API { /** - * @param string $id + * Formats the response for all fetched posts into associative arrays. * - * @return object + * @param array $results The raw results from the database query. + * @return array An array of associative arrays with post information. */ - public function wcpos_format_id( string $id ): object { - return (object) array( 'id' => (int) $id ); + public function wcpos_format_all_posts_response( $results ) { + $formatted_results = array_map( + function ( $result ) { + // Initialize the formatted result as an associative array. + $formatted_result = array( + 'id' => (int) $result->id, // Cast ID to integer for consistency. + ); + + // Check if post_modified_gmt exists and is not null, then set date_modified_gmt. + if ( isset( $result->date_modified_gmt ) && ! empty( $result->date_modified_gmt ) ) { + $formatted_result['date_modified_gmt'] = wc_rest_prepare_date_response( $result->date_modified_gmt ); + } + + return $formatted_result; + }, + $results + ); + + return $formatted_results; } /** diff --git a/tests/includes/API/Test_Orders_Controller.php b/tests/includes/API/Test_Orders_Controller.php index d7b13274..60f607a7 100644 --- a/tests/includes/API/Test_Orders_Controller.php +++ b/tests/includes/API/Test_Orders_Controller.php @@ -123,17 +123,48 @@ public function test_order_api_get_all_fields(): void { $this->assertEmpty( array_diff( $response_fields, $expected_response_fields ), 'These fields were not expected in the WCPOS API response: ' . print_r( array_diff( $response_fields, $expected_response_fields ), true ) ); } + /** + * + */ public function test_order_api_get_all_ids(): void { - $order = OrderHelper::create_order(); + $order1 = OrderHelper::create_order(); + $order2 = OrderHelper::create_order(); $request = $this->wp_rest_get_request( '/wcpos/v1/orders' ); $request->set_param( 'posts_per_page', -1 ); $request->set_param( 'fields', array( 'id' ) ); $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + + $this->assertEquals( array( $order1->get_id(), $order2->get_id() ), $ids ); + } + + /** + * + */ + public function test_order_api_get_all_ids_with_date_modified_gmt(): void { + $order1 = OrderHelper::create_order(); + $order2 = OrderHelper::create_order(); + $request = $this->wp_rest_get_request( '/wcpos/v1/orders' ); + $request->set_param( 'posts_per_page', -1 ); + $request->set_param( 'fields', array( 'id', 'date_modified_gmt' ) ); + $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); - $this->assertEquals( array( (object) array( 'id' => $order->get_id() ) ), $response->get_data() ); + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + + $this->assertEquals( array( $order1->get_id(), $order2->get_id() ), $ids ); + + // Verify that date_modified_gmt is present for all products and correctly formatted. + foreach ( $data as $d ) { + $this->assertArrayHasKey( 'date_modified_gmt', $d, "The 'date_modified_gmt' field is missing for product ID {$d['id']}." ); + $this->assertMatchesRegularExpression( '/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+\d{2}:\d{2}))?/', $d['date_modified_gmt'], "The 'date_modified_gmt' field for product ID {$d['id']} is not correctly formatted." ); + } } /** diff --git a/tests/includes/API/Test_Product_Variations_Controller.php b/tests/includes/API/Test_Product_Variations_Controller.php index 1996bb1a..9afc5b77 100644 --- a/tests/includes/API/Test_Product_Variations_Controller.php +++ b/tests/includes/API/Test_Product_Variations_Controller.php @@ -117,10 +117,69 @@ public function test_variation_api_get_all_fields(): void { } /** - * Test getting all customer IDs. + * Test getting all variation IDs. */ public function test_variation_api_get_all_ids(): void { + $product1 = ProductHelper::create_variation_product(); + $product2 = ProductHelper::create_variation_product(); + $variation1_ids = $product1->get_children(); + $variation2_ids = $product2->get_children(); + $this->assertEquals( 2, \count( $variation1_ids ) ); + $this->assertEquals( 2, \count( $variation2_ids ) ); + + $request = $this->wp_rest_get_request( '/wcpos/v1/products/variations' ); + $request->set_param( 'posts_per_page', -1 ); + $request->set_param( 'fields', array( 'id' ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + $this->assertEquals( 4, \count( $ids ) ); + + $this->assertEqualsCanonicalizing( array( $variation1_ids[0], $variation1_ids[1], $variation2_ids[0], $variation2_ids[1] ), $ids ); + } + + /** + * Test getting all variation IDs with modified date. + */ + public function test_variation_api_get_all_ids_with_date_modified_gmt(): void { + $product1 = ProductHelper::create_variation_product(); + $product2 = ProductHelper::create_variation_product(); + $variation1_ids = $product1->get_children(); + $variation2_ids = $product2->get_children(); + $this->assertEquals( 2, \count( $variation1_ids ) ); + $this->assertEquals( 2, \count( $variation2_ids ) ); + + $request = $this->wp_rest_get_request( '/wcpos/v1/products/variations' ); + $request->set_param( 'posts_per_page', -1 ); + $request->set_param( 'fields', array( 'id', 'date_modified_gmt' ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + $this->assertEquals( 4, \count( $ids ) ); + + $this->assertEqualsCanonicalizing( array( $variation1_ids[0], $variation1_ids[1], $variation2_ids[0], $variation2_ids[1] ), $ids ); + + // Verify that date_modified_gmt is present for all variations and correctly formatted. + foreach ( $data as $d ) { + $this->assertArrayHasKey( 'date_modified_gmt', $d, "The 'date_modified_gmt' field is missing for variation ID {$d['id']}." ); + $this->assertMatchesRegularExpression( '/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+\d{2}:\d{2}))?/', $d['date_modified_gmt'], "The 'date_modified_gmt' field for variation ID {$d['id']} is not correctly formatted." ); + } + } + + /** + * Test getting all variation IDs. + */ + public function test_variation_api_get_variable_ids(): void { $product = ProductHelper::create_variation_product(); + $product2 = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); $this->assertEquals( 2, \count( $variation_ids ) ); @@ -132,13 +191,41 @@ public function test_variation_api_get_all_ids(): void { $this->assertEquals( 200, $response->get_status() ); - $this->assertEquals( - array( - (object) array( 'id' => $variation_ids[1] ), - (object) array( 'id' => $variation_ids[0] ), - ), - $response->get_data() - ); + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + $this->assertEquals( 2, \count( $ids ) ); + + $this->assertEqualsCanonicalizing( array( $variation_ids[1], $variation_ids[0] ), $ids ); + } + + /** + * Test getting all variation IDs with modified date. + */ + public function test_variation_api_get_variable_ids_with_date_modified_gmt(): void { + $product = ProductHelper::create_variation_product(); + $product2 = ProductHelper::create_variation_product(); + $variation_ids = $product->get_children(); + $this->assertEquals( 2, \count( $variation_ids ) ); + + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $request->set_param( 'posts_per_page', -1 ); + $request->set_param( 'fields', array( 'id', 'date_modified_gmt' ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + $this->assertEquals( 2, \count( $ids ) ); + + $this->assertEqualsCanonicalizing( array( $variation_ids[1], $variation_ids[0] ), $ids ); + + // Verify that date_modified_gmt is present for all variations and correctly formatted. + foreach ( $data as $d ) { + $this->assertArrayHasKey( 'date_modified_gmt', $d, "The 'date_modified_gmt' field is missing for variation ID {$d['id']}." ); + $this->assertMatchesRegularExpression( '/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+\d{2}:\d{2}))?/', $d['date_modified_gmt'], "The 'date_modified_gmt' field for variation ID {$d['id']} is not correctly formatted." ); + } } /** diff --git a/tests/includes/API/Test_Products_Controller.php b/tests/includes/API/Test_Products_Controller.php index f4840d5b..3b7e2355 100644 --- a/tests/includes/API/Test_Products_Controller.php +++ b/tests/includes/API/Test_Products_Controller.php @@ -156,6 +156,28 @@ public function test_product_api_get_all_ids(): void { $this->assertEqualsCanonicalizing( array( $product1->get_id(), $product2->get_id() ), $ids ); } + public function test_product_api_get_all_id_with_date_modified_gmt(): void { + $product1 = ProductHelper::create_simple_product(); + $product2 = ProductHelper::create_simple_product(); + $request = $this->wp_rest_get_request( '/wcpos/v1/products' ); + $request->set_param( 'posts_per_page', -1 ); + $request->set_param( 'fields', array( 'id', 'date_modified_gmt' ) ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $ids = wp_list_pluck( $data, 'id' ); + + $this->assertEqualsCanonicalizing( array( $product1->get_id(), $product2->get_id() ), $ids ); + + // Verify that date_modified_gmt is present for all products and correctly formatted. + foreach ( $data as $d ) { + $this->assertArrayHasKey( 'date_modified_gmt', $d, "The 'date_modified_gmt' field is missing for product ID {$d['id']}." ); + $this->assertMatchesRegularExpression( '/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+\d{2}:\d{2}))?/', $d['date_modified_gmt'], "The 'date_modified_gmt' field for product ID {$d['id']} is not correctly formatted." ); + } + } + /** * Each product needs a UUID. */