From 458019f0f6f957bd4fbdecd7cd03219addd90f6d Mon Sep 17 00:00:00 2001 From: Paul Kilmurray Date: Wed, 29 Nov 2023 14:26:26 +0100 Subject: [PATCH] add test for pos visibility --- .../API/Product_Variations_Controller.php | 126 +++++++- includes/Products.php | 94 ++++-- .../Test_Product_Variations_Controller.php | 302 +++++++++++++----- tests/includes/Test_Products.php | 67 ++++ 4 files changed, 460 insertions(+), 129 deletions(-) create mode 100644 tests/includes/Test_Products.php diff --git a/includes/API/Product_Variations_Controller.php b/includes/API/Product_Variations_Controller.php index fa5d6ce..5f765f4 100644 --- a/includes/API/Product_Variations_Controller.php +++ b/includes/API/Product_Variations_Controller.php @@ -2,9 +2,9 @@ namespace WCPOS\WooCommercePOS\API; -\defined('ABSPATH') || die; +\defined( 'ABSPATH' ) || die; -if ( ! class_exists('WC_REST_Product_Variations_Controller') ) { +if ( ! class_exists( 'WC_REST_Product_Variations_Controller' ) ) { return; } @@ -15,6 +15,8 @@ use WP_Query; use WP_REST_Request; use WP_REST_Response; +use WP_REST_Server; +use WC_Product_Variation; /** * Product Tgas controller class. @@ -41,6 +43,20 @@ public function __construct() { if ( method_exists( parent::class, '__construct' ) ) { parent::__construct(); + + register_rest_route( + $this->namespace, + '/products/variations', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'wcpos_get_all_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); } } @@ -49,21 +65,21 @@ public function __construct() { */ public function get_item_schema() { $schema = parent::get_item_schema(); - + // Add the 'barcode' property if 'properties' exists and is an array - if (isset($schema['properties']) && \is_array($schema['properties'])) { + if ( isset( $schema['properties'] ) && \is_array( $schema['properties'] ) ) { $schema['properties']['barcode'] = array( - 'description' => __('Barcode', 'woocommerce-pos'), + 'description' => __( 'Barcode', 'woocommerce-pos' ), 'type' => 'string', - 'context' => array('view', 'edit'), + 'context' => array( 'view', 'edit' ), 'readonly' => false, ); } // Check for 'stock_quantity' and allow decimal - if ($this->wcpos_allow_decimal_quantities() && - isset($schema['properties']['stock_quantity']) && - \is_array($schema['properties']['stock_quantity'])) { + if ( $this->wcpos_allow_decimal_quantities() && + isset( $schema['properties']['stock_quantity'] ) && + \is_array( $schema['properties']['stock_quantity'] ) ) { $schema['properties']['stock_quantity']['type'] = 'string'; } @@ -76,14 +92,14 @@ public function get_item_schema() { */ public function get_collection_params() { $params = parent::get_collection_params(); - + // Check if 'per_page' parameter exists and has a 'minimum' key before modifying - if (isset($params['per_page']) && \is_array($params['per_page'])) { + if ( isset( $params['per_page'] ) && \is_array( $params['per_page'] ) ) { $params['per_page']['minimum'] = -1; } // Ensure 'orderby' is set and is an array before attempting to modify it - if (isset($params['orderby']['enum']) && \is_array($params['orderby']['enum'])) { + if ( isset( $params['orderby']['enum'] ) && \is_array( $params['orderby']['enum'] ) ) { // Define new sorting options $new_sort_options = array( 'sku', @@ -92,9 +108,9 @@ public function get_collection_params() { 'stock_status', ); // Merge new options, avoiding duplicates - $params['orderby']['enum'] = array_unique(array_merge($params['orderby']['enum'], $new_sort_options)); + $params['orderby']['enum'] = array_unique( array_merge( $params['orderby']['enum'], $new_sort_options ) ); } - + return $params; } @@ -155,7 +171,7 @@ public function wcpos_variation_response( WP_REST_Response $response, WC_Data $v $data['parent_id'] = $variation->get_parent_id(); } if ( ! isset( $data['name'] ) ) { - $data['name'] = \function_exists('wc_get_formatted_variation') ? wc_get_formatted_variation( $variation, true, false, false ) : ''; + $data['name'] = \function_exists( 'wc_get_formatted_variation' ) ? wc_get_formatted_variation( $variation, true, false, false ) : ''; } // Make sure we parse the meta data before returning the response @@ -201,6 +217,21 @@ public function wcpos_get_all_posts( array $fields = array() ): array { 'fields' => 'ids', ); + 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' => '!=', + ), + ); + } + $variation_query = new WP_Query( $args ); try { @@ -255,6 +286,71 @@ protected function prepare_objects_query( $request ) { } } + // Add online_only check + if ( $this->wcpos_pos_only_products_enabled() ) { + $default_meta_query = array( + 'relation' => 'OR', + array( + 'key' => '_pos_visibility', + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => '_pos_visibility', + 'value' => 'online_only', + 'compare' => '!=', + ), + ); + + if ( isset( $args['meta_query'] ) ) { + if ( ! isset( $args['meta_query']['relation'] ) ) { + $args['meta_query']['relation'] = 'AND'; + } + $args['meta_query'] = array_merge_recursive( $args['meta_query'], $default_meta_query ); + } else { + $args['meta_query'] = $default_meta_query; + } + }; + return $args; } + + /** + * Endpoint for searching all product variations. + * + * @param WP_REST_Request $request Full details about the request. + */ + public function wcpos_get_all_items( $request ) { + // Prepare arguments for the product query + $args = array( + 'post_type' => 'product_variation', + 'posts_per_page' => 10, // Limit to 10 items per page + 'orderby' => 'ID', // Default ordering + 'order' => 'ASC', + 'paged' => ! empty( $request['page'] ) ? $request['page'] : 1, // Handle pagination + ); + + // Check if 'search' param is set for SKU search + if ( ! empty( $request['search'] ) ) { + $args['meta_query'] = array( + array( + 'key' => '_sku', + 'value' => $request['search'], + 'compare' => 'LIKE', + ), + ); + } + + // Get product variations + $query = new WP_Query( $args ); + $variations = array(); + + foreach ( $query->posts as $variation ) { + $object = new WC_Product_Variation( $variation->ID ); + $response = $this->prepare_object_for_response( $object, $request ); + $variations[] = $this->prepare_response_for_collection( $response ); + } + + // Return the response + return new WP_REST_Response( $variations, 200 ); + } } diff --git a/includes/Products.php b/includes/Products.php index eea5f68..5381a2e 100644 --- a/includes/Products.php +++ b/includes/Products.php @@ -17,8 +17,14 @@ class Products { public function __construct() { add_action( 'woocommerce_product_set_stock', array( $this, 'product_set_stock' ) ); add_action( 'woocommerce_variation_set_stock', array( $this, 'product_set_stock' ) ); - add_action( 'pre_get_posts', array( $this, 'hide_pos_products' ) ); -// add_filter( 'woocommerce_get_product_subcategories_args', array( $this, 'filter_category_count_exclude_pos_only' ) ); + + $pos_only_products = woocommerce_pos_get_settings( 'general', 'pos_only_products' ); + + if ( $pos_only_products ) { + add_action( 'woocommerce_product_query', array( $this, 'hide_pos_only_products' ) ); + add_filter( 'woocommerce_variation_is_visible', array( $this, 'hide_pos_only_variations' ), 10, 4 ); + // add_filter( 'woocommerce_get_product_subcategories_args', array( $this, 'filter_category_count_exclude_pos_only' ) ); + } /** * Allow decimal quantities for products and variations. @@ -41,42 +47,76 @@ public function __construct() { public function product_set_stock( $product ): void { $post_modified = current_time( 'mysql' ); $post_modified_gmt = current_time( 'mysql', 1 ); - wp_update_post(array( - 'ID' => $product->get_id(), - 'post_modified' => $post_modified, - 'post_modified_gmt' => $post_modified_gmt, - )); + wp_update_post( + array( + 'ID' => $product->get_id(), + 'post_modified' => $post_modified, + 'post_modified_gmt' => $post_modified_gmt, + ) + ); } /** * Hide POS Only products from the shop and category pages. * * @TODO - this should be improved so that admin users can see the product, but get a message + * @TODO - should I use the 'woocommerce_product_query' action instead? I found it doesn't work correctly + * + * @param WP_Query $query Query instance. * - * @param mixed $query + * @return void */ - public function hide_pos_products( $query ): void { - if ( ! is_admin() && $query->is_main_query() && ( 'product' === $query->get( 'post_type' ) || $query->is_tax( 'product_cat' ) ) ) { - $pos_only_products = woocommerce_pos_get_settings( 'general', 'pos_only_products' ); + public function hide_pos_only_products( $query ) { + $meta_query = $query->get( 'meta_query' ); - if ( $pos_only_products ) { - $meta_query = $query->get( 'meta_query' ); - if ( ! \is_array( $meta_query ) ) { - $meta_query = array(); - } - $meta_query['relation'] = 'OR'; - $meta_query[] = array( - 'key' => '_pos_visibility', - 'value' => 'pos_only', - 'compare' => '!=', - ); - $meta_query[] = array( - 'key' => '_pos_visibility', - 'compare' => 'NOT EXISTS', - ); - $query->set( 'meta_query', $meta_query ); + // Define your default meta query. + $default_meta_query = array( + 'relation' => 'OR', + array( + 'key' => '_pos_visibility', + 'value' => 'pos_only', + 'compare' => '!=', + ), + array( + 'key' => '_pos_visibility', + 'compare' => 'NOT EXISTS', + ), + ); + + // Check if an existing meta query exists. + if ( is_array( $meta_query ) ) { + if ( ! isset( $meta_query ['relation'] ) ) { + $meta_query['relation'] = 'AND'; } + $meta_query[] = $default_meta_query; + } else { + $meta_query = $default_meta_query; } + + // Set the updated meta query back to the query. + $query->set( 'meta_query', $meta_query ); + } + + /** + * Remove POS Only variations from the storefront. + * + * @param bool $visible Whether the variation is visible. + * @param int $variation_id The variation ID. + * @param int $product_id The product ID. + * @param \WC_Product_Variation $variation The variation object. + */ + public function hide_pos_only_variations( $visible, $variation_id, $product_id, $variation ) { + if ( \is_shop() || \is_product_category() || \is_product() ) { + // Get the _pos_visibility meta value for the variation. + $pos_visibility = get_post_meta( $variation_id, '_pos_visibility', true ); + + // Check if _pos_visibility is 'pos_only' for this variation. + if ( $pos_visibility === 'pos_only' ) { + return false; + } + } + + return $visible; } /** diff --git a/tests/includes/API/Test_Product_Variations_Controller.php b/tests/includes/API/Test_Product_Variations_Controller.php index 9c6283e..fe79365 100644 --- a/tests/includes/API/Test_Product_Variations_Controller.php +++ b/tests/includes/API/Test_Product_Variations_Controller.php @@ -22,15 +22,15 @@ public function tearDown(): void { } public function test_namespace_property(): void { - $namespace = $this->get_reflected_property_value('namespace'); + $namespace = $this->get_reflected_property_value( 'namespace' ); - $this->assertEquals('wcpos/v1', $namespace ); + $this->assertEquals( 'wcpos/v1', $namespace ); } public function test_rest_base(): void { - $rest_base = $this->get_reflected_property_value('rest_base'); + $rest_base = $this->get_reflected_property_value( 'rest_base' ); - $this->assertEquals('products/(?P[\d]+)/variations', $rest_base); + $this->assertEquals( 'products/(?P[\d]+)/variations', $rest_base ); } /** @@ -121,11 +121,10 @@ public function test_variation_api_get_all_ids(): void { $product = 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') ); + $request->set_param( 'fields', array( 'id' ) ); $response = $this->server->dispatch( $request ); @@ -146,38 +145,41 @@ public function test_variation_api_get_all_ids(): void { public function test_variation_response_contains_uuid_meta_data(): void { $product = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0]); - $response = $this->server->dispatch($request); + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0] ); + $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertEquals(200, $response->get_status()); + $this->assertEquals( 200, $response->get_status() ); $found = false; $uuid_value = ''; $count = 0; // Look for the _woocommerce_pos_uuid key in meta_data - foreach ($data['meta_data'] as $meta) { - if ('_woocommerce_pos_uuid' === $meta['key']) { + foreach ( $data['meta_data'] as $meta ) { + if ( '_woocommerce_pos_uuid' === $meta['key'] ) { $count++; $uuid_value = $meta['value']; } } - $this->assertEquals(1, $count, 'There should only be one _woocommerce_pos_uuid.'); - $this->assertTrue(Uuid::isValid($uuid_value), 'The UUID value is not valid.'); + $this->assertEquals( 1, $count, 'There should only be one _woocommerce_pos_uuid.' ); + $this->assertTrue( Uuid::isValid( $uuid_value ), 'The UUID value is not valid.' ); } /** * Barcode. */ public function test_variation_get_barcode(): void { - add_filter( 'woocommerce_pos_general_settings', function() { - return array( - 'barcode_field' => 'foo', - ); - }); + add_filter( + 'woocommerce_pos_general_settings', + function () { + return array( + 'barcode_field' => 'foo', + ); + } + ); $product = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); @@ -190,58 +192,66 @@ public function test_variation_response_contains_sku_barcode(): void { $product = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0]); - $response = $this->server->dispatch($request); - + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0] ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); - - $this->assertEquals(200, $response->get_status()); - + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'DUMMY SKU VARIABLE SMALL', $data['barcode'] ); } public function test_variation_response_contains_barcode(): void { - add_filter( 'woocommerce_pos_general_settings', function() { - return array( - 'barcode_field' => '_some_field', - ); - }); + add_filter( + 'woocommerce_pos_general_settings', + function () { + return array( + 'barcode_field' => '_some_field', + ); + } + ); $product = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); update_post_meta( $variation_ids[0], '_some_field', 'some_string' ); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0]); - $response = $this->server->dispatch($request); - + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0] ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); - - $this->assertEquals(200, $response->get_status()); - + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'some_string', $data['barcode'] ); } public function test_variation_update_barcode(): void { - add_filter( 'woocommerce_pos_general_settings', function() { - return array( - 'barcode_field' => 'barcode', - ); - }); + add_filter( + 'woocommerce_pos_general_settings', + function () { + return array( + 'barcode_field' => 'barcode', + ); + } + ); $product = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); update_post_meta( $variation_ids[0], '_sku', 'sku-12345' ); - $request = $this->wp_rest_patch_request('/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0]); - $request->set_body_params( array( - 'barcode' => 'foo-12345', - ) ); - $response = $this->server->dispatch($request); - + $request = $this->wp_rest_patch_request( '/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0] ); + $request->set_body_params( + array( + 'barcode' => 'foo-12345', + ) + ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); - - $this->assertEquals(200, $response->get_status()); - + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'foo-12345', $data['barcode'] ); } @@ -250,8 +260,13 @@ public function test_variation_update_barcode(): void { */ public function test_variation_orderby_sku(): void { $product = ProductHelper::create_variation_product(); - $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations'); - $request->set_query_params( array( 'orderby' => 'sku', 'order' => 'asc' ) ); + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $request->set_query_params( + array( + 'orderby' => 'sku', + 'order' => 'asc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'sku' ); @@ -259,7 +274,12 @@ public function test_variation_orderby_sku(): void { $this->assertEquals( $skus, array( 'DUMMY SKU VARIABLE LARGE', 'DUMMY SKU VARIABLE SMALL' ) ); // reverse order - $request->set_query_params( array( 'orderby' => 'sku', 'order' => 'desc' ) ); + $request->set_query_params( + array( + 'orderby' => 'sku', + 'order' => 'desc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'sku' ); @@ -268,19 +288,27 @@ public function test_variation_orderby_sku(): void { } public function test_variation_orderby_barcode(): void { - add_filter( 'woocommerce_pos_general_settings', function() { - return array( - 'barcode_field' => '_barcode', - ); - }); + add_filter( + 'woocommerce_pos_general_settings', + function () { + return array( + 'barcode_field' => '_barcode', + ); + } + ); $product = ProductHelper::create_variation_product(); $variation_ids = $product->get_children(); update_post_meta( $variation_ids[0], '_barcode', 'alpha' ); update_post_meta( $variation_ids[1], '_barcode', 'zeta' ); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations'); - $request->set_query_params( array( 'orderby' => 'barcode', 'order' => 'asc' ) ); + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $request->set_query_params( + array( + 'orderby' => 'barcode', + 'order' => 'asc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $barcodes = wp_list_pluck( $data, 'barcode' ); @@ -288,7 +316,12 @@ public function test_variation_orderby_barcode(): void { $this->assertEquals( $barcodes, array( 'alpha', 'zeta' ) ); // reverse order - $request->set_query_params( array( 'orderby' => 'barcode', 'order' => 'desc' ) ); + $request->set_query_params( + array( + 'orderby' => 'barcode', + 'order' => 'desc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $barcodes = wp_list_pluck( $data, 'barcode' ); @@ -301,9 +334,14 @@ public function test_variation_orderby_stock_status(): void { $variation_ids = $product->get_children(); update_post_meta( $variation_ids[0], '_stock_status', 'instock' ); update_post_meta( $variation_ids[1], '_stock_status', 'outofstock' ); - - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations'); - $request->set_query_params( array( 'orderby' => 'stock_status', 'order' => 'asc' ) ); + + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $request->set_query_params( + array( + 'orderby' => 'stock_status', + 'order' => 'asc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'stock_status' ); @@ -311,7 +349,12 @@ public function test_variation_orderby_stock_status(): void { $this->assertEquals( $skus, array( 'instock', 'outofstock' ) ); // reverse order - $request->set_query_params( array( 'orderby' => 'stock_status', 'order' => 'desc' ) ); + $request->set_query_params( + array( + 'orderby' => 'stock_status', + 'order' => 'desc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'stock_status' ); @@ -327,8 +370,13 @@ public function test_variation_orderby_stock_quantity(): void { update_post_meta( $variation_ids[1], '_stock', 2 ); update_post_meta( $variation_ids[1], '_manage_stock', 'yes' ); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations'); - $request->set_query_params( array( 'orderby' => 'stock_quantity', 'order' => 'asc' ) ); + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $request->set_query_params( + array( + 'orderby' => 'stock_quantity', + 'order' => 'asc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'stock_quantity' ); @@ -336,7 +384,12 @@ public function test_variation_orderby_stock_quantity(): void { $this->assertEquals( $skus, array( 1, 2 ) ); // reverse order - $request->set_query_params( array( 'orderby' => 'stock_quantity', 'order' => 'desc' ) ); + $request->set_query_params( + array( + 'orderby' => 'stock_quantity', + 'order' => 'desc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'stock_quantity' ); @@ -369,13 +422,13 @@ public function test_variation_response_with_decimal_quantities(): void { $variation->set_stock_quantity( 1.5 ); $variation->save(); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0]); - $response = $this->server->dispatch($request); - + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0] ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); - - $this->assertEquals(200, $response->get_status()); - + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 1.5, $data['stock_quantity'] ); } @@ -390,16 +443,15 @@ public function test_variation_update_decimal_quantities(): void { $variation->set_manage_stock( true ); $variation->save(); - $request = $this->wp_rest_patch_request('/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0]); - $request->set_body_params( array( - 'stock_quantity' => '3.85', - ) ); - $response = $this->server->dispatch($request); - + $request = $this->wp_rest_patch_request( '/wcpos/v1/products/' . $product->get_id() . '/variations/' . $variation_ids[0] ); + $request->set_body_params( + array( + 'stock_quantity' => '3.85', + ) + ); + $response = $this->server->dispatch( $request ); $data = $response->get_data(); - - $this->assertEquals(200, $response->get_status()); - + $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 3.85, $data['stock_quantity'] ); } @@ -413,8 +465,13 @@ public function test_variation_orderby_decimal_stock_quantity(): void { update_post_meta( $variation_ids[0], '_manage_stock', 'yes' ); update_post_meta( $variation_ids[1], '_stock', '3.5' ); update_post_meta( $variation_ids[1], '_manage_stock', 'yes' ); - $request = $this->wp_rest_get_request('/wcpos/v1/products/' . $product->get_id() . '/variations'); - $request->set_query_params( array( 'orderby' => 'stock_quantity', 'order' => 'asc' ) ); + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $request->set_query_params( + array( + 'orderby' => 'stock_quantity', + 'order' => 'asc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'stock_quantity' ); @@ -422,7 +479,12 @@ public function test_variation_orderby_decimal_stock_quantity(): void { $this->assertEquals( $skus, array( 3.5, 11.2 ) ); // reverse order - $request->set_query_params( array( 'orderby' => 'stock_quantity', 'order' => 'desc' ) ); + $request->set_query_params( + array( + 'orderby' => 'stock_quantity', + 'order' => 'desc', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $skus = wp_list_pluck( $data, 'stock_quantity' ); @@ -430,9 +492,75 @@ public function test_variation_orderby_decimal_stock_quantity(): void { $this->assertEquals( $skus, array( 11.2, 3.5 ) ); } + /** + * Online Only products. + */ + public function test_pos_only_product_variations(): void { + add_filter( + 'woocommerce_pos_general_settings', + function () { + return array( + 'pos_only_products' => true, + ); + } + ); + + // create a variable product with 4 variations + $product = ProductHelper::create_variation_product(); // has two variations + $variation_3 = new \WC_Product_Variation(); + $variation_3->set_props( + array( + 'parent_id' => $product->get_id(), + 'sku' => 'DUMMY SKU VARIABLE MEDIUM', + 'regular_price' => 10, + ) + ); + $variation_3->set_attributes( array( 'pa_size' => 'medium' ) ); + $variation_3->update_meta_data( '_pos_visibility', 'online_only' ); + $variation_3->save_meta_data(); + $variation_3->save(); + + $variation_4 = new \WC_Product_Variation(); + $variation_4->set_props( + array( + 'parent_id' => $product->get_id(), + 'sku' => 'DUMMY SKU VARIABLE EXTRA LARGE', + 'regular_price' => 15, + ) + ); + $variation_4->set_attributes( array( 'pa_size' => 'x-large' ) ); + $variation_4->update_meta_data( '_pos_visibility', 'pos_only' ); + $variation_4->save_meta_data(); + $variation_4->save(); + + // test get all 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' ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 3, \count( $data ) ); + $ids = wp_list_pluck( $data, 'id' ); + + $this->assertNotContains( $variation_3->get_id(), $ids ); + $this->assertContains( $variation_4->get_id(), $ids ); + + // test products response + $request = $this->wp_rest_get_request( '/wcpos/v1/products/' . $product->get_id() . '/variations' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 3, \count( $data ) ); + $ids = wp_list_pluck( $data, 'id' ); + + $this->assertNotContains( $variation_3->get_id(), $ids ); + $this->assertContains( $variation_4->get_id(), $ids ); + } + // /** - // * Variations should order by menu_order by default. - // */ + // * Variations should order by menu_order by default. + // */ // public function test_variation_orderby_menu_order(): void { // } } diff --git a/tests/includes/Test_Products.php b/tests/includes/Test_Products.php new file mode 100644 index 0000000..dcf1a66 --- /dev/null +++ b/tests/includes/Test_Products.php @@ -0,0 +1,67 @@ + true, + ); + } + ); + new Products(); // reinstantiate the class to apply the filter + + // Create a visible product + $visible_product = ProductHelper::create_simple_product(); + + // Create a product with _pos_visibility set to 'pos_only' + $hidden_product = ProductHelper::create_simple_product(); + update_post_meta( $hidden_product->get_id(), '_pos_visibility', 'pos_only' ); + + // Verify that the meta value is set correctly + $pos_visibility = get_post_meta( $hidden_product->get_id(), '_pos_visibility', true ); + $this->assertEquals( 'pos_only', $pos_visibility, 'Meta value for _pos_visibility not set correctly' ); + + // Mimic the main WooCommerce query + $query_args = array( + 'post_type' => 'product', + 'post_status' => 'publish', + 'posts_per_page' => -1, // Get all products for testing + ); + + $query = new WP_Query( $query_args ); + WC()->query->product_query( $query ); + $queried_ids = wp_list_pluck( $query->posts, 'ID' ); + + // Assert that the visible product is in the query + $this->assertContains( $visible_product->get_id(), $queried_ids ); + + // Assert that the hidden product is not in the query + $this->assertNotContains( $hidden_product->get_id(), $queried_ids ); + } +}