From 6abfd1538d929a600b7422bf1159964a83c6ab07 Mon Sep 17 00:00:00 2001 From: sgillot Date: Fri, 13 Sep 2024 18:57:34 +0200 Subject: [PATCH 01/11] issue/74034 : wip --- src/Feed/Generator.php | 21 +++++++++++++++ src/Products/Product.php | 55 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/Feed/Generator.php b/src/Feed/Generator.php index dddd3f5..49dcb60 100644 --- a/src/Feed/Generator.php +++ b/src/Feed/Generator.php @@ -142,6 +142,18 @@ function ( $product->setWeight( (float) $sf_product->get_weight() ); } + if ( ! empty( $sf_product->get_length() ) ) { + $product->setAttribute( 'length', (float) $sf_product->get_length() ); + } + + if ( ! empty( $sf_product->get_width() ) ) { + $product->setAttribute('width', (float) $sf_product->get_width() ); + } + + if ( ! empty( $sf_product->get_height() ) ) { + $product->setAttribute('height', (float) $sf_product->get_height() ); + } + if ( ! empty( $sf_product->get_category_name() ) ) { $product->setCategory( $sf_product->get_category_name(), $sf_product->get_category_link() ); } @@ -213,6 +225,15 @@ function ( if ( ! empty( $variation_images ) ) { $variation->setAdditionalImages( $variation_images ); } + if ( ! empty ( $sf_product_variation['width'] ) ){ + $variation->setAttribute( 'width', (float) $sf_product_variation['width'] ); + } + if ( ! empty ( $sf_product_variation['length'] ) ){ + $variation->setAttribute( 'length', (float) $sf_product_variation['length'] ); + } + if ( ! empty ( $sf_product_variation['height'] ) ){ + $variation->setAttribute( 'height', (float) $sf_product_variation['height'] ); + } } } ); diff --git a/src/Products/Product.php b/src/Products/Product.php index 0dc8d53..5773ff2 100644 --- a/src/Products/Product.php +++ b/src/Products/Product.php @@ -33,7 +33,17 @@ class Product { /** * @var string */ - private $weight; + private $weight; /** + * @var string + */ + + private $length; /** + * @var string + */ + private $width; /** + * @var string + */ + private $height; /** * @var bool|mixed|\WP_Term @@ -52,6 +62,9 @@ public function __construct( $product ) { $this->brand = $this->set_brand(); $this->category = $this->set_category(); $this->weight = $this->product->get_weight(); + $this->length = $this->product->get_length(); + $this->width = $this->product->get_width(); + $this->height = $this->product->get_height(); } /** @@ -218,6 +231,39 @@ public function get_weight() { return $this->weight; } + /** + * @return string + */ + public function get_length() { + if ( empty( $this->length ) ) { + return ''; + } + + return $this->length; + } + + /** + * @return string + */ + public function get_width() { + if ( empty( $this->width ) ) { + return ''; + } + + return $this->width; + } + + /** + * @return string + */ + public function get_height() { + if ( empty( $this->height ) ) { + return ''; + } + + return $this->height; + } + /** * @return string */ @@ -408,10 +454,16 @@ public function get_variations( $for_feed = false ) { $variation_data['quantity'] = $this->_get_quantity( $variation ); $variation_data['price'] = ! is_null( $variation->get_regular_price() ) ? $variation->get_regular_price() : $variation->get_price(); $variation_data['discount'] = $variation->get_sale_price(); + $variation_data['width'] = $variation->get_width(); + $variation_data['height'] = $variation->get_height(); + $variation_data['length'] = $variation->get_length(); + + if ( ! empty( get_the_post_thumbnail_url( $variation->get_id(), 'full' ) ) ) { $variation_data['image_main'] = get_the_post_thumbnail_url( $variation->get_id(), 'full' ); } + $variation_data['attributes'] = $this->get_variation_attributes( $variation ); $variations[] = $variation_data; } @@ -453,6 +505,7 @@ public function get_variation_attributes( $variation ) { } } + return apply_filters( 'shopping_feed_extra_variation_attributes', $attribute_names, $variation ); } From 3722746e2db73593300a4908520d8c1f242e5da1 Mon Sep 17 00:00:00 2001 From: sgillot Date: Tue, 24 Sep 2024 17:26:04 +0200 Subject: [PATCH 02/11] add tests --- tests/wpunit/Feed/ProductFeedTest.php | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/wpunit/Feed/ProductFeedTest.php b/tests/wpunit/Feed/ProductFeedTest.php index 54876b3..befdc34 100644 --- a/tests/wpunit/Feed/ProductFeedTest.php +++ b/tests/wpunit/Feed/ProductFeedTest.php @@ -161,6 +161,10 @@ function ( $value ) { ); } + /** + * @covers + */ + /** * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_quantity */ @@ -209,4 +213,57 @@ public function test_get_product_quantity_outofstock_manage_stock() { $p = new Product( wc_get_product( 13 ) ); $this->assertEquals( 0, $p->get_quantity() ); } + + /** + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_length + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_height + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_width + * + * @author Stéphane Gillot + */ + public function test_get_simple_product_dimensions_when_defined(){ + $wc_product = wc_get_product( 32 ); + $wc_product->set_length( 5 ); + $wc_product->set_height( 10 ); + $wc_product->set_width( 15 ); + $wc_product->save(); + + $p = new Product( wc_get_product( 32 ) ); + + $this->assertEquals( 5, $p->get_length(), 'Product length should be 5.' ); + $this->assertEquals( 10, $p->get_height(), 'Product height should be 10.' ); + $this->assertEquals( 15, $p->get_width(), 'Product width should be 15.' ); + } + + /** + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_length + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_height + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_width + * + * @author Stéphane Gillot + */ + public function test_get_simple_product_dimensions_when_not_defined(){ + $wc_product = wc_get_product( 32 ); + $wc_product->set_length('' ); + $wc_product->set_height( '' ); + $wc_product->set_width( '' ); + $wc_product->save(); + + $p = new Product( wc_get_product( 32 ) ); + + $this->assertEquals( '', $p->get_length(), 'Product length should be an empty string.' ); + $this->assertEquals( '', $p->get_height(), 'Product height should be Product length should be an empty string.' ); + $this->assertEquals( '', $p->get_width(), 'Product width should be Product length should be an empty string.' ); + } + + public function test_get_variation_dimensions_when_it_defaults_to_parent_dimensions(){ + //TODO + } + + public function test_get_variation_dimensions_when_it_overrides_parent_dimensions(){ + //TODO + } + + + } From 3b067ba89066d55658180f09dc278314873bfc40 Mon Sep 17 00:00:00 2001 From: sgillot Date: Wed, 25 Sep 2024 18:54:50 +0200 Subject: [PATCH 03/11] issue/74026 : add attribute to parent if not visible on variations --- src/Feed/Generator.php | 2 +- src/Products/Product.php | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Feed/Generator.php b/src/Feed/Generator.php index ae17000..8180117 100644 --- a/src/Feed/Generator.php +++ b/src/Feed/Generator.php @@ -147,7 +147,7 @@ function ( } // For variable products, don't include attributes. They will be available in the variations. - if ( ! $sf_product->has_variations() && ! empty( $sf_product->get_attributes() ) ) { + if ( ! empty( $sf_product->get_attributes() ) ) { $product->setAttributes( $sf_product->get_attributes() ); } diff --git a/src/Products/Product.php b/src/Products/Product.php index 50e6c85..4f47739 100644 --- a/src/Products/Product.php +++ b/src/Products/Product.php @@ -327,7 +327,15 @@ public function get_attributes() { $wc_attributes = $this->product->get_attributes(); - $attributes = array(); + if ( 'variable' === $this->product->get_type() && is_array( $wc_attributes ) && !empty( $wc_attributes ) ) { + foreach ( $wc_attributes as $key => $attribute ) { + if ( $attribute->get_variation() ) { + unset( $wc_attributes[ $key ] ); + } + } + } + + $attributes = []; if ( ! empty( $wc_attributes ) ) { foreach ( $wc_attributes as $taxonomy => $attribute_obj ) { $attribute = reset( $attribute_obj ); From ee4d5ae139ffeb49c3bc796cda3a8634a946d08e Mon Sep 17 00:00:00 2001 From: sgillot Date: Thu, 26 Sep 2024 16:11:03 +0200 Subject: [PATCH 04/11] issue/74026 : add tests for atributes --- tests/wpunit/Feed/ProductFeedTest.php | 99 +++++- tests/wpunit/WC_Helper_Product.php | 441 ++++++++++++++++++++++++++ 2 files changed, 534 insertions(+), 6 deletions(-) create mode 100644 tests/wpunit/WC_Helper_Product.php diff --git a/tests/wpunit/Feed/ProductFeedTest.php b/tests/wpunit/Feed/ProductFeedTest.php index 54876b3..a9c72aa 100644 --- a/tests/wpunit/Feed/ProductFeedTest.php +++ b/tests/wpunit/Feed/ProductFeedTest.php @@ -5,6 +5,7 @@ use ShoppingFeed\ShoppingFeedWC\Products\Product; use ShoppingFeed\ShoppingFeedWC\Products\Products; use ShoppingFeed\ShoppingFeedWC\ShoppingFeedHelper; +use ShoppingFeed\ShoppingFeedWC\Tests\wpunit\WC_Helper_Product; class ProductFeedTest extends \Codeception\TestCase\WPTestCase { /** @@ -129,13 +130,13 @@ function ( $value ) { public function test_get_products_for_feed_query_args() { $products_query_args = Products::get_instance()->get_list_args(); $this->assertEqualSets( - array( + [ 'limit' => - 1, 'orderby' => 'date', 'order' => 'DESC', 'status' => 'publish', 'stock_status' => 'instock', - ), + ], $products_query_args ); @@ -150,13 +151,13 @@ function ( $value ) { $products_query_args = Products::get_instance()->get_list_args(); $this->assertEqualSets( - array( + [ 'limit' => - 1, 'orderby' => 'date', 'order' => 'DESC', 'status' => 'publish', 'stock_status' => [ 'instock', 'outofstock' ], - ), + ], $products_query_args ); } @@ -166,7 +167,7 @@ function ( $value ) { */ public function test_get_product_quantity_instock() { $wc_product = wc_get_product( 13 ); - $wc_product->set_stock_status('instock'); + $wc_product->set_stock_status( 'instock' ); $wc_product->save(); $p = new Product( wc_get_product( 13 ) ); $this->assertEquals( ShoppingFeedHelper::get_default_product_quantity(), $p->get_quantity() ); @@ -177,7 +178,7 @@ public function test_get_product_quantity_instock() { */ public function test_get_product_quantity_outofstock() { $wc_product = wc_get_product( 13 ); - $wc_product->set_stock_status('outofstock'); + $wc_product->set_stock_status( 'outofstock' ); $wc_product->save(); $p = new Product( wc_get_product( 13 ) ); @@ -209,4 +210,90 @@ public function test_get_product_quantity_outofstock_manage_stock() { $p = new Product( wc_get_product( 13 ) ); $this->assertEquals( 0, $p->get_quantity() ); } + + /** + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_attributes + * + * @author Stéphane Gillot, Clément Boirie + */ + public function test_attribute_on_variable_product_is_applied_to_variations() { + // Prepare the attribute object + $attribute = new \WC_Product_Attribute(); + $attribute->set_name( 'material' ); + $attribute->set_options( [ 'Coton', 'Linen' ] ); + $attribute->set_variation( 'true' ); + $attribute->set_visible( 'true' ); + + // Prepare the variable product object + $wc_variable_product = WC_Helper_Product::create_variation_product(); + $wc_variable_product->set_attributes( [ $attribute ] ); + $wc_variable_product->save(); + + // Prepare the sf product object + $sf_product = new Product( $wc_variable_product->get_id() ); + + $this->assertEquals( [], $sf_product->get_attributes() ); + } + + /** + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_attributes + * + * @author Stéphane Gillot, Clément Boirie + */ + public function test_attribute_on_variable_product_is_not_applied_to_variations() { + // Prepare the attribute object + $attribute = new \WC_Product_Attribute(); + $attribute->set_name( 'material' ); + $attribute->set_options( [ 'Coton', 'Linen' ] ); + $attribute->set_variation( 'false' ); + $attribute->set_visible( 'true' ); + + // Prepare the variable product object + $wc_variable_product = WC_Helper_Product::create_variation_product(); + $wc_variable_product->set_attributes( [ $attribute ] ); + $wc_variable_product->save(); + + // Prepare the sf product object + $sf_product = new Product( $wc_variable_product ); + + $this->assertEquals( [ 'material' => 'Coton,Linen' ], $sf_product->get_attributes() ); + } + + /** + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_attributes + * + * @author Stéphane Gillot, Clément Boirie + */ + public function test_attribute_on_simple_product_exists() { + // Prepare the attribute object + $attribute = new \WC_Product_Attribute(); + $attribute->set_name( 'material' ); + $attribute->set_options( [ 'Coton' ] ); + $attribute->set_visible( 'true' ); + + // Prepare the variable product object + $wc_simple_product = WC_Helper_Product::create_simple_product(); + $wc_simple_product->set_attributes( [ $attribute ] ); + $wc_simple_product->save(); + + // Prepare the sf product object + $sf_product = new Product( $wc_simple_product ); + + $this->assertEquals( [ 'material' => 'Coton' ], $sf_product->get_attributes() ); + } + + /** + * @covers \ShoppingFeed\ShoppingFeedWC\Products\Product::get_attributes + * + * @author Stéphane Gillot, Clément Boirie + */ + public function test_attribute_on_simple_product_does_not_exist() { + // Prepare the variable product object + $wc_simple_product = WC_Helper_Product::create_simple_product(); + + // Prepare the sf product object + $sf_product = new Product( $wc_simple_product ); + + $this->assertEquals( [], $sf_product->get_attributes() ); + } } diff --git a/tests/wpunit/WC_Helper_Product.php b/tests/wpunit/WC_Helper_Product.php new file mode 100644 index 0000000..9d4d032 --- /dev/null +++ b/tests/wpunit/WC_Helper_Product.php @@ -0,0 +1,441 @@ +delete( true ); + } + } + + /** + * Create simple product. + * + * @since 2.3 + * @param bool $save Save or return object. + * @param array $props Properties to be set in the new product, as an associative array. + * @return \WC_Product_Simple + */ + public static function create_simple_product( $save = true, $props = array() ) { + $product = new \WC_Product_Simple(); + $default_props = + array( + 'name' => 'Dummy Product', + 'regular_price' => 10, + 'price' => 10, + 'sku' => 'DUMMY SKU' . self::$sku_counter, + 'manage_stock' => false, + 'tax_status' => 'taxable', + 'downloadable' => false, + 'virtual' => false, + 'stock_status' => 'instock', + 'weight' => '1.1', + ); + + ++self::$sku_counter; + + $product->set_props( array_merge( $default_props, $props ) ); + + if ( $save ) { + $product->save(); + return wc_get_product( $product->get_id() ); + } else { + return $product; + } + } + + /** + * Create a downloadable product. + * + * @since 6.4.0 + * + * @param array $downloads An array of arrays (each containing a 'name' and 'file' key) or WC_Product_Download objects. + * @param bool $save Save or return object. + * + * @return \WC_Product_Simple|false + */ + public static function create_downloadable_product( array $downloads = array(), $save = true ) { + $product = new \WC_Product_Simple(); + $product->set_props( + array( + 'name' => 'Downloadable Product', + 'regular_price' => 10, + 'price' => 10, + 'manage_stock' => false, + 'tax_status' => 'taxable', + 'downloadable' => true, + 'virtual' => false, + 'stock_status' => 'instock', + ) + ); + + $product->set_downloads( $downloads ); + + if ( $save ) { + $product->save(); + return \wc_get_product( $product->get_id() ); + } else { + return $product; + } + } + + /** + * Create external product. + * + * @since 3.0.0 + * @return \WC_Product_External + */ + public static function create_external_product() { + $product = new \WC_Product_External(); + $product->set_props( + array( + 'name' => 'Dummy External Product', + 'regular_price' => 10, + 'sku' => 'DUMMY EXTERNAL SKU', + 'product_url' => 'https://woocommerce.com', + 'button_text' => 'Buy external product', + ) + ); + $product->save(); + + return wc_get_product( $product->get_id() ); + } + + /** + * Create grouped product. + * + * @since 3.0.0 + * @return WC_Product_Grouped + */ + public static function create_grouped_product() { + $simple_product_1 = self::create_simple_product(); + $simple_product_2 = self::create_simple_product(); + $product = new \WC_Product_Grouped(); + $product->set_props( + array( + 'name' => 'Dummy Grouped Product', + 'sku' => 'DUMMY GROUPED SKU', + ) + ); + $product->set_children( array( $simple_product_1->get_id(), $simple_product_2->get_id() ) ); + $product->save(); + + return wc_get_product( $product->get_id() ); + } + + /** + * Create a dummy variation product or configure an existing product object with dummy data. + * + * + * @since 2.3 + * @param \WC_Product_Variable|null $product Product object to configure, or null to create a new one. + * @return \WC_Product_Variable + */ + public static function create_variation_product( $product = null ) { + $is_new_product = is_null( $product ); + if ( $is_new_product ) { + $product = new \WC_Product_Variable(); + } + + $product->set_props( + array( + 'name' => 'Dummy Variable Product', + 'sku' => 'DUMMY VARIABLE SKU', + ) + ); + + $attributes = array(); + + $attributes[] = self::create_product_attribute_object( 'size', array( 'small', 'large', 'huge' ) ); + $attributes[] = self::create_product_attribute_object( 'colour', array( 'red', 'blue' ) ); + $attributes[] = self::create_product_attribute_object( 'number', array( '0', '1', '2' ) ); + + $product->set_attributes( $attributes ); + $product->save(); + + $variations = array(); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE SMALL', + 10, + array( 'pa_size' => 'small' ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE LARGE', + 15, + array( 'pa_size' => 'large' ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE RED 0', + 16, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'red', + 'pa_number' => '0', + ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE RED 2', + 17, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'red', + 'pa_number' => '2', + ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE BLUE 2', + 18, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'blue', + 'pa_number' => '2', + ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE BLUE ANY NUMBER', + 19, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'blue', + 'pa_number' => '', + ) + ); + + if ( $is_new_product ) { + return wc_get_product( $product->get_id() ); + } + + $variation_ids = array_map( + function( $variation ) { + return $variation->get_id(); + }, + $variations + ); + $product->set_children( $variation_ids ); + return $product; + } + + /** + * Creates an instance of WC_Product_Variation with the supplied parameters, optionally persisting it to the database. + * + * @param string $parent_id Parent product id. + * @param string $sku SKU for the variation. + * @param int $price Price of the variation. + * @param array $attributes Attributes that define the variation, e.g. ['pa_color'=>'red']. + * @param bool $save If true, the object will be saved to the database after being created and configured. + * + * @return \WC_Product_Variation The created object. + */ + public static function create_product_variation_object( $parent_id, $sku, $price, $attributes, $save = true ) { + $variation = new \WC_Product_Variation(); + $variation->set_props( + array( + 'parent_id' => $parent_id, + 'sku' => $sku, + 'regular_price' => $price, + ) + ); + $variation->set_attributes( $attributes ); + if ( $save ) { + $variation->save(); + } + return $variation; + } + + /** + * Creates an instance of WC_Product_Attribute with the supplied parameters. + * + * @param string $raw_name Attribute raw name (without 'pa_' prefix). + * @param array $terms Possible values for the attribute. + * + * @return \WC_Product_Attribute The created attribute object. + */ + public static function create_product_attribute_object( $raw_name = 'size', $terms = array( 'small' ) ) { + $attribute = new \WC_Product_Attribute(); + $attribute_data = self::create_attribute( $raw_name, $terms ); + $attribute->set_id( $attribute_data['attribute_id'] ); + $attribute->set_name( $attribute_data['attribute_taxonomy'] ); + $attribute->set_options( $attribute_data['term_ids'] ); + $attribute->set_position( 1 ); + $attribute->set_visible( true ); + $attribute->set_variation( true ); + return $attribute; + } + + /** + * Create a dummy attribute. + * + * @since 2.3 + * + * @param string $raw_name Name of attribute to create. + * @param array(string) $terms Terms to create for the attribute. + * @return array + */ + public static function create_attribute( $raw_name = 'size', $terms = array( 'small' ) ) { + global $wpdb, $wc_product_attributes; + + // Make sure caches are clean. + delete_transient( 'wc_attribute_taxonomies' ); + \WC_Cache_Helper::invalidate_cache_group( 'woocommerce-attributes' ); + + // These are exported as labels, so convert the label to a name if possible first. + $attribute_labels = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_label', 'attribute_name' ); + $attribute_name = array_search( $raw_name, $attribute_labels, true ); + + if ( ! $attribute_name ) { + $attribute_name = wc_sanitize_taxonomy_name( $raw_name ); + } + + $attribute_id = wc_attribute_taxonomy_id_by_name( $attribute_name ); + + if ( ! $attribute_id ) { + $taxonomy_name = wc_attribute_taxonomy_name( $attribute_name ); + + // Degister taxonomy which other tests may have created... + unregister_taxonomy( $taxonomy_name ); + + $attribute_id = wc_create_attribute( + array( + 'name' => $raw_name, + 'slug' => $attribute_name, + 'type' => 'select', + 'order_by' => 'menu_order', + 'has_archives' => 0, + ) + ); + + // Register as taxonomy. + register_taxonomy( + $taxonomy_name, + apply_filters( 'woocommerce_taxonomy_objects_' . $taxonomy_name, array( 'product' ) ), + apply_filters( + 'woocommerce_taxonomy_args_' . $taxonomy_name, + array( + 'labels' => array( + 'name' => $raw_name, + ), + 'hierarchical' => false, + 'show_ui' => false, + 'query_var' => true, + 'rewrite' => false, + ) + ) + ); + + // Set product attributes global. + $wc_product_attributes = array(); + + foreach ( wc_get_attribute_taxonomies() as $taxonomy ) { + $wc_product_attributes[ wc_attribute_taxonomy_name( $taxonomy->attribute_name ) ] = $taxonomy; + } + } + + $attribute = wc_get_attribute( $attribute_id ); + $return = array( + 'attribute_name' => $attribute->name, + 'attribute_taxonomy' => $attribute->slug, + 'attribute_id' => $attribute_id, + 'term_ids' => array(), + ); + + foreach ( $terms as $term ) { + $result = term_exists( $term, $attribute->slug ); + + if ( ! $result ) { + $result = wp_insert_term( $term, $attribute->slug ); + $return['term_ids'][] = $result['term_id']; + } else { + $return['term_ids'][] = $result['term_id']; + } + } + + return $return; + } + + /** + * Delete an attribute. + * + * @param int $attribute_id ID to delete. + * + * @since 2.3 + */ + public static function delete_attribute( $attribute_id ) { + global $wpdb; + + $attribute_id = absint( $attribute_id ); + + $wpdb->query( + $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d", $attribute_id ) + ); + } + + /** + * Creates a new product review on a specific product. + * + * @since 3.0 + * @param int $product_id integer Product ID that the review is for. + * @param string $review_content string Content to use for the product review. + * @return integer Product Review ID. + */ + public static function create_product_review( $product_id, $review_content = 'Review content here' ) { + $data = array( + 'comment_post_ID' => $product_id, + 'comment_author' => 'admin', + 'comment_author_email' => 'woo@woo.local', + 'comment_author_url' => '', + 'comment_date' => '2016-01-01T11:11:11', + 'comment_content' => $review_content, + 'comment_approved' => 1, + 'comment_type' => 'review', + ); + return wp_insert_comment( $data ); + } + + /** + * A helper function for hooking into save_post during the test_product_meta_save_post test. + * @since 3.0.1 + * + * @param int $id ID to update. + */ + public static function save_post_test_update_meta_data_direct( $id ) { + update_post_meta( $id, '_test2', 'world' ); + } +} \ No newline at end of file From 8224cce36639f101fb93dbc0d80f73fa086db573 Mon Sep 17 00:00:00 2001 From: sgillot Date: Tue, 1 Oct 2024 13:05:48 +0200 Subject: [PATCH 05/11] issue/74034 : add test for dimensions --- tests/wpunit/Feed/ProductFeedTest.php | 58 +++- tests/wpunit/WC_Helper_Product.php | 441 ++++++++++++++++++++++++++ 2 files changed, 487 insertions(+), 12 deletions(-) create mode 100644 tests/wpunit/WC_Helper_Product.php diff --git a/tests/wpunit/Feed/ProductFeedTest.php b/tests/wpunit/Feed/ProductFeedTest.php index befdc34..5f79c75 100644 --- a/tests/wpunit/Feed/ProductFeedTest.php +++ b/tests/wpunit/Feed/ProductFeedTest.php @@ -5,6 +5,7 @@ use ShoppingFeed\ShoppingFeedWC\Products\Product; use ShoppingFeed\ShoppingFeedWC\Products\Products; use ShoppingFeed\ShoppingFeedWC\ShoppingFeedHelper; +use ShoppingFeed\ShoppingFeedWC\Tests\wpunit\WC_Helper_Product; class ProductFeedTest extends \Codeception\TestCase\WPTestCase { /** @@ -222,13 +223,13 @@ public function test_get_product_quantity_outofstock_manage_stock() { * @author Stéphane Gillot */ public function test_get_simple_product_dimensions_when_defined(){ - $wc_product = wc_get_product( 32 ); + $wc_product = WC_Helper_Product::create_simple_product(); $wc_product->set_length( 5 ); $wc_product->set_height( 10 ); $wc_product->set_width( 15 ); $wc_product->save(); - $p = new Product( wc_get_product( 32 ) ); + $p = new Product( $wc_product->get_id() ); $this->assertEquals( 5, $p->get_length(), 'Product length should be 5.' ); $this->assertEquals( 10, $p->get_height(), 'Product height should be 10.' ); @@ -243,27 +244,60 @@ public function test_get_simple_product_dimensions_when_defined(){ * @author Stéphane Gillot */ public function test_get_simple_product_dimensions_when_not_defined(){ - $wc_product = wc_get_product( 32 ); + $wc_product = WC_Helper_Product::create_simple_product(); $wc_product->set_length('' ); $wc_product->set_height( '' ); $wc_product->set_width( '' ); $wc_product->save(); - $p = new Product( wc_get_product( 32 ) ); + $p = new Product( $wc_product->get_id() ); $this->assertEquals( '', $p->get_length(), 'Product length should be an empty string.' ); - $this->assertEquals( '', $p->get_height(), 'Product height should be Product length should be an empty string.' ); - $this->assertEquals( '', $p->get_width(), 'Product width should be Product length should be an empty string.' ); + $this->assertEquals( '', $p->get_height(), 'Product height should be an empty string.' ); + $this->assertEquals( '', $p->get_width(), 'Product width should be an empty string.' ); } - public function test_get_variation_dimensions_when_it_defaults_to_parent_dimensions(){ - //TODO - } + public function test_get_variation_dimensions_when_it_not_defined() { - public function test_get_variation_dimensions_when_it_overrides_parent_dimensions(){ - //TODO - } + // Prepare the variable product object + $wc_variable_product = New \WC_Product_Variable(); + $wc_variable_product->set_length( 5 ); + $wc_variable_product->set_height( 10 ); + $wc_variable_product->set_width( 15 ); + $wc_variable_product->save(); + $variation = WC_Helper_Product::create_variation_product(); + $variation->set_parent_id( $wc_variable_product->get_id() ); + $variation->save(); + // Create an SF variation + $sf_product = new Product( $variation ); + $this->assertEquals( '', $sf_product->get_length(), 'Product length should be null.' ); + $this->assertEquals( '', $sf_product->get_height(), 'Product height should be null.' ); + $this->assertEquals( '', $sf_product->get_width(), 'Product width should be null.' ); + } + public function test_get_variation_dimensions_when_it_overrides_parent_dimensions(){ + + // Prepare the variable product object + $wc_variable_product = New \WC_Product_Variable(); + $wc_variable_product->set_length( 5 ); + $wc_variable_product->set_height( 10 ); + $wc_variable_product->set_width( 15 ); + $wc_variable_product->save(); + + $variation = WC_Helper_Product::create_variation_product(); + $variation->set_parent_id( $wc_variable_product->get_id() ); + $variation->set_length(20); + $variation->set_height(30); + $variation->set_width(40); + $variation->save(); + + + // Create an SF variation + $sf_product = new Product( $variation ); + $this->assertEquals( 20, $sf_product->get_length(), 'Product length should be 20.' ); + $this->assertEquals( 30, $sf_product->get_height(), 'Product height should be 30.' ); + $this->assertEquals( 40, $sf_product->get_width(), 'Product width should be 40.' ); + } } diff --git a/tests/wpunit/WC_Helper_Product.php b/tests/wpunit/WC_Helper_Product.php new file mode 100644 index 0000000..9d4d032 --- /dev/null +++ b/tests/wpunit/WC_Helper_Product.php @@ -0,0 +1,441 @@ +delete( true ); + } + } + + /** + * Create simple product. + * + * @since 2.3 + * @param bool $save Save or return object. + * @param array $props Properties to be set in the new product, as an associative array. + * @return \WC_Product_Simple + */ + public static function create_simple_product( $save = true, $props = array() ) { + $product = new \WC_Product_Simple(); + $default_props = + array( + 'name' => 'Dummy Product', + 'regular_price' => 10, + 'price' => 10, + 'sku' => 'DUMMY SKU' . self::$sku_counter, + 'manage_stock' => false, + 'tax_status' => 'taxable', + 'downloadable' => false, + 'virtual' => false, + 'stock_status' => 'instock', + 'weight' => '1.1', + ); + + ++self::$sku_counter; + + $product->set_props( array_merge( $default_props, $props ) ); + + if ( $save ) { + $product->save(); + return wc_get_product( $product->get_id() ); + } else { + return $product; + } + } + + /** + * Create a downloadable product. + * + * @since 6.4.0 + * + * @param array $downloads An array of arrays (each containing a 'name' and 'file' key) or WC_Product_Download objects. + * @param bool $save Save or return object. + * + * @return \WC_Product_Simple|false + */ + public static function create_downloadable_product( array $downloads = array(), $save = true ) { + $product = new \WC_Product_Simple(); + $product->set_props( + array( + 'name' => 'Downloadable Product', + 'regular_price' => 10, + 'price' => 10, + 'manage_stock' => false, + 'tax_status' => 'taxable', + 'downloadable' => true, + 'virtual' => false, + 'stock_status' => 'instock', + ) + ); + + $product->set_downloads( $downloads ); + + if ( $save ) { + $product->save(); + return \wc_get_product( $product->get_id() ); + } else { + return $product; + } + } + + /** + * Create external product. + * + * @since 3.0.0 + * @return \WC_Product_External + */ + public static function create_external_product() { + $product = new \WC_Product_External(); + $product->set_props( + array( + 'name' => 'Dummy External Product', + 'regular_price' => 10, + 'sku' => 'DUMMY EXTERNAL SKU', + 'product_url' => 'https://woocommerce.com', + 'button_text' => 'Buy external product', + ) + ); + $product->save(); + + return wc_get_product( $product->get_id() ); + } + + /** + * Create grouped product. + * + * @since 3.0.0 + * @return WC_Product_Grouped + */ + public static function create_grouped_product() { + $simple_product_1 = self::create_simple_product(); + $simple_product_2 = self::create_simple_product(); + $product = new \WC_Product_Grouped(); + $product->set_props( + array( + 'name' => 'Dummy Grouped Product', + 'sku' => 'DUMMY GROUPED SKU', + ) + ); + $product->set_children( array( $simple_product_1->get_id(), $simple_product_2->get_id() ) ); + $product->save(); + + return wc_get_product( $product->get_id() ); + } + + /** + * Create a dummy variation product or configure an existing product object with dummy data. + * + * + * @since 2.3 + * @param \WC_Product_Variable|null $product Product object to configure, or null to create a new one. + * @return \WC_Product_Variable + */ + public static function create_variation_product( $product = null ) { + $is_new_product = is_null( $product ); + if ( $is_new_product ) { + $product = new \WC_Product_Variable(); + } + + $product->set_props( + array( + 'name' => 'Dummy Variable Product', + 'sku' => 'DUMMY VARIABLE SKU', + ) + ); + + $attributes = array(); + + $attributes[] = self::create_product_attribute_object( 'size', array( 'small', 'large', 'huge' ) ); + $attributes[] = self::create_product_attribute_object( 'colour', array( 'red', 'blue' ) ); + $attributes[] = self::create_product_attribute_object( 'number', array( '0', '1', '2' ) ); + + $product->set_attributes( $attributes ); + $product->save(); + + $variations = array(); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE SMALL', + 10, + array( 'pa_size' => 'small' ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE LARGE', + 15, + array( 'pa_size' => 'large' ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE RED 0', + 16, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'red', + 'pa_number' => '0', + ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE RED 2', + 17, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'red', + 'pa_number' => '2', + ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE BLUE 2', + 18, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'blue', + 'pa_number' => '2', + ) + ); + + $variations[] = self::create_product_variation_object( + $product->get_id(), + 'DUMMY SKU VARIABLE HUGE BLUE ANY NUMBER', + 19, + array( + 'pa_size' => 'huge', + 'pa_colour' => 'blue', + 'pa_number' => '', + ) + ); + + if ( $is_new_product ) { + return wc_get_product( $product->get_id() ); + } + + $variation_ids = array_map( + function( $variation ) { + return $variation->get_id(); + }, + $variations + ); + $product->set_children( $variation_ids ); + return $product; + } + + /** + * Creates an instance of WC_Product_Variation with the supplied parameters, optionally persisting it to the database. + * + * @param string $parent_id Parent product id. + * @param string $sku SKU for the variation. + * @param int $price Price of the variation. + * @param array $attributes Attributes that define the variation, e.g. ['pa_color'=>'red']. + * @param bool $save If true, the object will be saved to the database after being created and configured. + * + * @return \WC_Product_Variation The created object. + */ + public static function create_product_variation_object( $parent_id, $sku, $price, $attributes, $save = true ) { + $variation = new \WC_Product_Variation(); + $variation->set_props( + array( + 'parent_id' => $parent_id, + 'sku' => $sku, + 'regular_price' => $price, + ) + ); + $variation->set_attributes( $attributes ); + if ( $save ) { + $variation->save(); + } + return $variation; + } + + /** + * Creates an instance of WC_Product_Attribute with the supplied parameters. + * + * @param string $raw_name Attribute raw name (without 'pa_' prefix). + * @param array $terms Possible values for the attribute. + * + * @return \WC_Product_Attribute The created attribute object. + */ + public static function create_product_attribute_object( $raw_name = 'size', $terms = array( 'small' ) ) { + $attribute = new \WC_Product_Attribute(); + $attribute_data = self::create_attribute( $raw_name, $terms ); + $attribute->set_id( $attribute_data['attribute_id'] ); + $attribute->set_name( $attribute_data['attribute_taxonomy'] ); + $attribute->set_options( $attribute_data['term_ids'] ); + $attribute->set_position( 1 ); + $attribute->set_visible( true ); + $attribute->set_variation( true ); + return $attribute; + } + + /** + * Create a dummy attribute. + * + * @since 2.3 + * + * @param string $raw_name Name of attribute to create. + * @param array(string) $terms Terms to create for the attribute. + * @return array + */ + public static function create_attribute( $raw_name = 'size', $terms = array( 'small' ) ) { + global $wpdb, $wc_product_attributes; + + // Make sure caches are clean. + delete_transient( 'wc_attribute_taxonomies' ); + \WC_Cache_Helper::invalidate_cache_group( 'woocommerce-attributes' ); + + // These are exported as labels, so convert the label to a name if possible first. + $attribute_labels = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_label', 'attribute_name' ); + $attribute_name = array_search( $raw_name, $attribute_labels, true ); + + if ( ! $attribute_name ) { + $attribute_name = wc_sanitize_taxonomy_name( $raw_name ); + } + + $attribute_id = wc_attribute_taxonomy_id_by_name( $attribute_name ); + + if ( ! $attribute_id ) { + $taxonomy_name = wc_attribute_taxonomy_name( $attribute_name ); + + // Degister taxonomy which other tests may have created... + unregister_taxonomy( $taxonomy_name ); + + $attribute_id = wc_create_attribute( + array( + 'name' => $raw_name, + 'slug' => $attribute_name, + 'type' => 'select', + 'order_by' => 'menu_order', + 'has_archives' => 0, + ) + ); + + // Register as taxonomy. + register_taxonomy( + $taxonomy_name, + apply_filters( 'woocommerce_taxonomy_objects_' . $taxonomy_name, array( 'product' ) ), + apply_filters( + 'woocommerce_taxonomy_args_' . $taxonomy_name, + array( + 'labels' => array( + 'name' => $raw_name, + ), + 'hierarchical' => false, + 'show_ui' => false, + 'query_var' => true, + 'rewrite' => false, + ) + ) + ); + + // Set product attributes global. + $wc_product_attributes = array(); + + foreach ( wc_get_attribute_taxonomies() as $taxonomy ) { + $wc_product_attributes[ wc_attribute_taxonomy_name( $taxonomy->attribute_name ) ] = $taxonomy; + } + } + + $attribute = wc_get_attribute( $attribute_id ); + $return = array( + 'attribute_name' => $attribute->name, + 'attribute_taxonomy' => $attribute->slug, + 'attribute_id' => $attribute_id, + 'term_ids' => array(), + ); + + foreach ( $terms as $term ) { + $result = term_exists( $term, $attribute->slug ); + + if ( ! $result ) { + $result = wp_insert_term( $term, $attribute->slug ); + $return['term_ids'][] = $result['term_id']; + } else { + $return['term_ids'][] = $result['term_id']; + } + } + + return $return; + } + + /** + * Delete an attribute. + * + * @param int $attribute_id ID to delete. + * + * @since 2.3 + */ + public static function delete_attribute( $attribute_id ) { + global $wpdb; + + $attribute_id = absint( $attribute_id ); + + $wpdb->query( + $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d", $attribute_id ) + ); + } + + /** + * Creates a new product review on a specific product. + * + * @since 3.0 + * @param int $product_id integer Product ID that the review is for. + * @param string $review_content string Content to use for the product review. + * @return integer Product Review ID. + */ + public static function create_product_review( $product_id, $review_content = 'Review content here' ) { + $data = array( + 'comment_post_ID' => $product_id, + 'comment_author' => 'admin', + 'comment_author_email' => 'woo@woo.local', + 'comment_author_url' => '', + 'comment_date' => '2016-01-01T11:11:11', + 'comment_content' => $review_content, + 'comment_approved' => 1, + 'comment_type' => 'review', + ); + return wp_insert_comment( $data ); + } + + /** + * A helper function for hooking into save_post during the test_product_meta_save_post test. + * @since 3.0.1 + * + * @param int $id ID to update. + */ + public static function save_post_test_update_meta_data_direct( $id ) { + update_post_meta( $id, '_test2', 'world' ); + } +} \ No newline at end of file From 02083c521fb60d61ebec6395c9447c8fd8416109 Mon Sep 17 00:00:00 2001 From: sgillot Date: Tue, 1 Oct 2024 15:08:57 +0200 Subject: [PATCH 06/11] issue/74034 : fix psalm --- src/Feed/Generator.php | 20 +++++++++---------- src/Products/Product.php | 32 ++++++++++++++++++++---------- tests/wpunit/WC_Helper_Product.php | 8 ++++++-- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/Feed/Generator.php b/src/Feed/Generator.php index 49dcb60..cbe5db2 100644 --- a/src/Feed/Generator.php +++ b/src/Feed/Generator.php @@ -123,7 +123,7 @@ function ( if ( ! empty( $sf_product->get_link() ) ) { $product->setLink( $sf_product->get_link() ); } - if ( ! empty( $sf_product->get_discount() ) ) { + if ( $sf_product->is_on_sale() && ! empty( $sf_product->get_discount() ) ) { $product->addDiscount( $sf_product->get_discount() ); } if ( ! empty( $sf_product->get_image_main() ) ) { @@ -143,15 +143,15 @@ function ( } if ( ! empty( $sf_product->get_length() ) ) { - $product->setAttribute( 'length', (float) $sf_product->get_length() ); + $product->setAttribute( 'length', (string) $sf_product->get_length() ); } if ( ! empty( $sf_product->get_width() ) ) { - $product->setAttribute('width', (float) $sf_product->get_width() ); + $product->setAttribute( 'width', (string) $sf_product->get_width() ); } if ( ! empty( $sf_product->get_height() ) ) { - $product->setAttribute('height', (float) $sf_product->get_height() ); + $product->setAttribute( 'height', (string) $sf_product->get_height() ); } if ( ! empty( $sf_product->get_category_name() ) ) { @@ -225,14 +225,14 @@ function ( if ( ! empty( $variation_images ) ) { $variation->setAdditionalImages( $variation_images ); } - if ( ! empty ( $sf_product_variation['width'] ) ){ - $variation->setAttribute( 'width', (float) $sf_product_variation['width'] ); + if ( ! empty ( $sf_product_variation['width'] ) ) { + $variation->setAttribute( 'width', (string) $sf_product_variation['width'] ); } - if ( ! empty ( $sf_product_variation['length'] ) ){ - $variation->setAttribute( 'length', (float) $sf_product_variation['length'] ); + if ( ! empty ( $sf_product_variation['length'] ) ) { + $variation->setAttribute( 'length', (string) $sf_product_variation['length'] ); } - if ( ! empty ( $sf_product_variation['height'] ) ){ - $variation->setAttribute( 'height', (float) $sf_product_variation['height'] ); + if ( ! empty ( $sf_product_variation['height'] ) ) { + $variation->setAttribute( 'height', (string) $sf_product_variation['height'] ); } } } diff --git a/src/Products/Product.php b/src/Products/Product.php index 5773ff2..4c0c10d 100644 --- a/src/Products/Product.php +++ b/src/Products/Product.php @@ -33,16 +33,21 @@ class Product { /** * @var string */ - private $weight; /** - * @var string - */ - - private $length; /** - * @var string - */ - private $width; /** - * @var string - */ + private $weight; + + /** + * @var string + */ + private $length; + + /** + * @var string + */ + private $width; + + /** + * @var string + */ private $height; /** @@ -177,6 +182,13 @@ public function get_discount() { return (float) $this->product->get_sale_price(); } + /** + * @return bool + */ + public function is_on_sale() { + return (bool) $this->product->is_on_sale(); + } + /** * @return string */ diff --git a/tests/wpunit/WC_Helper_Product.php b/tests/wpunit/WC_Helper_Product.php index 9d4d032..76d9d81 100644 --- a/tests/wpunit/WC_Helper_Product.php +++ b/tests/wpunit/WC_Helper_Product.php @@ -155,8 +155,9 @@ public static function create_grouped_product() { * * @since 2.3 * @param \WC_Product_Variable|null $product Product object to configure, or null to create a new one. - * @return \WC_Product_Variable + * @return \WC_Product_Variable|null */ + /** @psalm-suppress PossiblyNullReference */ public static function create_variation_product( $product = null ) { $is_new_product = is_null( $product ); if ( $is_new_product ) { @@ -249,7 +250,10 @@ function( $variation ) { }, $variations ); - $product->set_children( $variation_ids ); + + if ( ! empty( $variation_ids ) ) { + $product->set_children( $variation_ids ); + } return $product; } From a6525b1eaef3a478e170959c6f5b40501d3425ed Mon Sep 17 00:00:00 2001 From: sgillot Date: Tue, 1 Oct 2024 17:52:58 +0200 Subject: [PATCH 07/11] fix cs issues --- src/Feed/Generator.php | 6 +++--- src/Products/Product.php | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Feed/Generator.php b/src/Feed/Generator.php index cbe5db2..a11ff9e 100644 --- a/src/Feed/Generator.php +++ b/src/Feed/Generator.php @@ -225,13 +225,13 @@ function ( if ( ! empty( $variation_images ) ) { $variation->setAdditionalImages( $variation_images ); } - if ( ! empty ( $sf_product_variation['width'] ) ) { + if ( ! empty( $sf_product_variation['width'] ) ) { $variation->setAttribute( 'width', (string) $sf_product_variation['width'] ); } - if ( ! empty ( $sf_product_variation['length'] ) ) { + if ( ! empty( $sf_product_variation['length'] ) ) { $variation->setAttribute( 'length', (string) $sf_product_variation['length'] ); } - if ( ! empty ( $sf_product_variation['height'] ) ) { + if ( ! empty( $sf_product_variation['height'] ) ) { $variation->setAttribute( 'height', (string) $sf_product_variation['height'] ); } } diff --git a/src/Products/Product.php b/src/Products/Product.php index 4c0c10d..818a82c 100644 --- a/src/Products/Product.php +++ b/src/Products/Product.php @@ -470,12 +470,10 @@ public function get_variations( $for_feed = false ) { $variation_data['height'] = $variation->get_height(); $variation_data['length'] = $variation->get_length(); - if ( ! empty( get_the_post_thumbnail_url( $variation->get_id(), 'full' ) ) ) { $variation_data['image_main'] = get_the_post_thumbnail_url( $variation->get_id(), 'full' ); } - $variation_data['attributes'] = $this->get_variation_attributes( $variation ); $variations[] = $variation_data; } @@ -517,7 +515,6 @@ public function get_variation_attributes( $variation ) { } } - return apply_filters( 'shopping_feed_extra_variation_attributes', $attribute_names, $variation ); } From 05690cafebfa973db431ec3d8ebe640f14814542 Mon Sep 17 00:00:00 2001 From: sgillot Date: Tue, 8 Oct 2024 14:11:56 +0200 Subject: [PATCH 08/11] issue/74026 fix cs issue --- src/Products/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/Product.php b/src/Products/Product.php index 4f47739..8f77e85 100644 --- a/src/Products/Product.php +++ b/src/Products/Product.php @@ -327,7 +327,7 @@ public function get_attributes() { $wc_attributes = $this->product->get_attributes(); - if ( 'variable' === $this->product->get_type() && is_array( $wc_attributes ) && !empty( $wc_attributes ) ) { + if ( 'variable' === $this->product->get_type() && is_array( $wc_attributes ) && ! empty( $wc_attributes ) ) { foreach ( $wc_attributes as $key => $attribute ) { if ( $attribute->get_variation() ) { unset( $wc_attributes[ $key ] ); From 88c7b07553442759f08d26c22778b56f08fda1e5 Mon Sep 17 00:00:00 2001 From: sgillot Date: Tue, 26 Nov 2024 09:21:32 +0100 Subject: [PATCH 09/11] issue/74026 : rm comment --- src/Feed/Generator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Feed/Generator.php b/src/Feed/Generator.php index 8180117..e4387f5 100644 --- a/src/Feed/Generator.php +++ b/src/Feed/Generator.php @@ -146,7 +146,6 @@ function ( $product->setCategory( $sf_product->get_category_name(), $sf_product->get_category_link() ); } - // For variable products, don't include attributes. They will be available in the variations. if ( ! empty( $sf_product->get_attributes() ) ) { $product->setAttributes( $sf_product->get_attributes() ); } From c8910d5d760fb1b26391540a018e410086e9af84 Mon Sep 17 00:00:00 2001 From: Clement Boirie Date: Tue, 17 Oct 2023 17:17:13 +0200 Subject: [PATCH 10/11] fix incorrect timestamp value when scheduling action --- src/Orders/Operations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Orders/Operations.php b/src/Orders/Operations.php index fe0c7ad..7de1452 100644 --- a/src/Orders/Operations.php +++ b/src/Orders/Operations.php @@ -205,7 +205,7 @@ public static function acknowledge_order( $order_id, $message = '' ) { if ( false === $ok ) { //if we cant acknowledge order => add action after 15 min as_schedule_single_action( - MINUTE_IN_SECONDS * 15, + time() + ( 15 * MINUTE_IN_SECONDS ), 'sf_acknowledge_remain_order', array( $order_id, From 49a940ec2d94162f4b859e52a28c024a61d6ebbb Mon Sep 17 00:00:00 2001 From: Clement Boirie Date: Thu, 19 Dec 2024 18:48:27 +0100 Subject: [PATCH 11/11] chore: release 6.9.0 --- .plugin-data | 2 +- readme.md | 12 ++++++++---- readme.txt | 14 +++++++++----- shoppingfeed.php | 4 ++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.plugin-data b/.plugin-data index bdb2af6..86df32b 100644 --- a/.plugin-data +++ b/.plugin-data @@ -1,4 +1,4 @@ { - "version": "6.8.0", + "version": "6.9.0", "slug": "shopping-feed" } diff --git a/readme.md b/readme.md index 73841e9..d12ba45 100644 --- a/readme.md +++ b/readme.md @@ -2,19 +2,23 @@ * Contributors: ShoppingFeed, BeAPI * Tags: shoppingfeed, marketplace, woocommerce, woocommerce shoppingfeed, create woocommerce products shoppingfeed, products feed, generate shoppingfeed, amazon, Jet, Walmart, many marketplace, import orders -* Stable tag: 6.8.0 -* Version: 6.8.0 +* Stable tag: 6.9.0 +* Version: 6.9.0 * Requires PHP: 7.3 * Requires at least: 5.7 -* Tested up to: 6.5 +* Tested up to: 6.7 * WC requires at least: 5.1.0 -* WC tested up to: 8.8 +* WC tested up to: 9.4.3 ## Upgrade Notice > Version 6.0.0 is a major version, there are several changes and improvements which affect the architecture of the plugin. You will have to re-configure the plugin, all the previous settings will be lost ## Changelog +* 6.9.0 + * Feed : Fix attributes not use in variations missing in the feed. + * Feed : Dimension data are correctly included in the feed. + * Orders : Fix invalid timestamp when scheduling async task to acknowledge orders. * 6.8.0 * Feed : Fix the promotion date * 6.7.0 diff --git a/readme.txt b/readme.txt index 1133ff3..c218254 100644 --- a/readme.txt +++ b/readme.txt @@ -1,20 +1,24 @@ ## ShoppingFeed Contributors: ShoppingFeed, BeAPI Tags: shoppingfeed, marketplace, woocommerce, woocommerce shoppingfeed, create woocommerce products shoppingfeed, products feed, generate shoppingfeed, amazon, Jet, Walmart, many marketplace, import orders -Stable tag: 6.8.0 -Version: 6.8.0 +Stable tag: 6.9.0 +Version: 6.9.0 Requires PHP: 7.3 Requires at least: 5.7 -Tested up to: 6.5 +Tested up to: 6.7 WC requires at least: 5.1.0 -WC tested up to: 8.8 +WC tested up to: 9.4.3 == Upgrade Notice == Version 6.0.0 is a major version, there are several changes and improvements which affect the architecture of the plugin. You will have to re-configure the plugin, all the previous settings will be lost == Changelog == +* 6.9.0 + * Feed : Fix attributes not use in variations missing in the feed. + * Feed : Dimension data are correctly included in the feed. + * Orders : Fix invalid timestamp when scheduling async task to acknowledge orders. * 6.8.0 - * Feed : Fix the promotion date + * Feed : Fix the promotion date. * 6.7.0 * Orders : The 'buyer_identification_number' field is imported in an order custom field if it exists. * Orders : Product updates (price and stock) via the SF API are made asynchronously via a scheduled task. diff --git a/shoppingfeed.php b/shoppingfeed.php index 5947c88..d0dc9a7 100644 --- a/shoppingfeed.php +++ b/shoppingfeed.php @@ -7,7 +7,7 @@ * Author URI: https://www.shopping-feed.com/ * Text Domain: shopping-feed * Domain Path: /languages - * Version: 6.8.0 + * Version: 6.9.0 * Requires at least: 5.7 * Requires PHP: 7.3 * WC requires at least: 5.1.0 @@ -26,7 +26,7 @@ require_once plugin_dir_path( __FILE__ ) . '/vendor/autoload.php'; } -define( 'SF_VERSION', '6.8.0' ); +define( 'SF_VERSION', '6.9.0' ); define( 'SF_DB_VERSION_SLUG', 'SF_DB_VERSION' ); define( 'SF_DB_VERSION', '1.0.0' ); define( 'SF_UPGRADE_RUNNING', 'SF_UPGRADE_RUNNING' );