Merge pull request #26174 from woocommerce/fix/25843

Fix stock status saving for variable products on bulk and quick edit
This commit is contained in:
Claudio Sanches 2020-07-24 17:05:16 -03:00 committed by GitHub
commit aafb06929a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 581 additions and 335 deletions

View File

@ -7268,3 +7268,8 @@ table.bar_chart {
} }
} }
} }
.wc-quick-edit-warning {
color: darkred;
font-weight: bold;
}

View File

@ -1,6 +1,10 @@
/*global inlineEditPost, woocommerce_admin, woocommerce_quick_edit */ /*global inlineEditPost, woocommerce_admin, woocommerce_quick_edit */
jQuery(function( $ ) { jQuery(
$( '#the-list' ).on( 'click', '.editinline', function() { function( $ ) {
$( '#the-list' ).on(
'click',
'.editinline',
function() {
inlineEditPost.revert(); inlineEditPost.revert();
@ -12,7 +16,7 @@ jQuery(function( $ ) {
var sku = $wc_inline_data.find( '.sku' ).text(), var sku = $wc_inline_data.find( '.sku' ).text(),
regular_price = $wc_inline_data.find( '.regular_price' ).text(), regular_price = $wc_inline_data.find( '.regular_price' ).text(),
sale_price = $wc_inline_data.find( '.sale_price ').text(), sale_price = $wc_inline_data.find( '.sale_price ' ).text(),
weight = $wc_inline_data.find( '.weight' ).text(), weight = $wc_inline_data.find( '.weight' ).text(),
length = $wc_inline_data.find( '.length' ).text(), length = $wc_inline_data.find( '.length' ).text(),
width = $wc_inline_data.find( '.width' ).text(), width = $wc_inline_data.find( '.width' ).text(),
@ -26,10 +30,11 @@ jQuery(function( $ ) {
menu_order = $wc_inline_data.find( '.menu_order' ).text(), menu_order = $wc_inline_data.find( '.menu_order' ).text(),
tax_status = $wc_inline_data.find( '.tax_status' ).text(), tax_status = $wc_inline_data.find( '.tax_status' ).text(),
tax_class = $wc_inline_data.find( '.tax_class' ).text(), tax_class = $wc_inline_data.find( '.tax_class' ).text(),
backorders = $wc_inline_data.find( '.backorders' ).text(); backorders = $wc_inline_data.find( '.backorders' ).text(),
product_type = $wc_inline_data.find( '.product_type' ).text();
var formatted_regular_price = regular_price.replace('.', woocommerce_admin.mon_decimal_point ), var formatted_regular_price = regular_price.replace( '.', woocommerce_admin.mon_decimal_point ),
formatted_sale_price = sale_price.replace('.', woocommerce_admin.mon_decimal_point ); formatted_sale_price = sale_price.replace( '.', woocommerce_admin.mon_decimal_point );
$( 'input[name="_sku"]', '.inline-edit-row' ).val( sku ); $( 'input[name="_sku"]', '.inline-edit-row' ).val( sku );
$( 'input[name="_regular_price"]', '.inline-edit-row' ).val( formatted_regular_price ); $( 'input[name="_regular_price"]', '.inline-edit-row' ).val( formatted_regular_price );
@ -45,13 +50,22 @@ jQuery(function( $ ) {
$( 'input[name="_stock"]', '.inline-edit-row' ).val( stock ); $( 'input[name="_stock"]', '.inline-edit-row' ).val( stock );
$( 'input[name="menu_order"]', '.inline-edit-row' ).val( menu_order ); $( 'input[name="menu_order"]', '.inline-edit-row' ).val( menu_order );
// eslint-disable-next-line max-len $(
$( 'select[name="_tax_status"] option, select[name="_tax_class"] option, select[name="_visibility"] option, select[name="_stock_status"] option, select[name="_backorders"] option' ).removeAttr( 'selected' ); 'select[name="_tax_status"] option, ' +
'select[name="_tax_class"] option, ' +
'select[name="_visibility"] option, ' +
'select[name="_stock_status"] option, ' +
'select[name="_backorders"] option'
).removeAttr( 'selected' );
var is_variable_product = 'variable' === product_type;
$( 'select[name="_stock_status"] ~ .wc-quick-edit-warning', '.inline-edit-row' ).toggle( is_variable_product );
$( 'select[name="_stock_status"] option[value="' + (is_variable_product ? '' : stock_status) + '"]', '.inline-edit-row' )
.attr( 'selected', 'selected' );
$( 'select[name="_tax_status"] option[value="' + tax_status + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' ); $( 'select[name="_tax_status"] option[value="' + tax_status + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_tax_class"] option[value="' + tax_class + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' ); $( 'select[name="_tax_class"] option[value="' + tax_class + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_visibility"] option[value="' + visibility + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' ); $( 'select[name="_visibility"] option[value="' + visibility + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_stock_status"] option[value="' + stock_status + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_backorders"] option[value="' + backorders + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' ); $( 'select[name="_backorders"] option[value="' + backorders + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
if ( 'yes' === featured ) { if ( 'yes' === featured ) {
@ -60,9 +74,8 @@ jQuery(function( $ ) {
$( 'input[name="_featured"]', '.inline-edit-row' ).removeAttr( 'checked' ); $( 'input[name="_featured"]', '.inline-edit-row' ).removeAttr( 'checked' );
} }
// Conditional display // Conditional display.
var product_type = $wc_inline_data.find( '.product_type' ).text(), var product_is_virtual = $wc_inline_data.find( '.product_is_virtual' ).text();
product_is_virtual = $wc_inline_data.find( '.product_is_virtual' ).text();
var product_supports_stock_status = 'external' !== product_type; var product_supports_stock_status = 'external' !== product_type;
var product_supports_stock_fields = 'external' !== product_type && 'grouped' !== product_type; var product_supports_stock_fields = 'external' !== product_type && 'grouped' !== product_type;
@ -97,11 +110,15 @@ jQuery(function( $ ) {
$( '.dimension_fields', '.inline-edit-row' ).show().removeAttr( 'style' ); $( '.dimension_fields', '.inline-edit-row' ).show().removeAttr( 'style' );
} }
// Rename core strings // Rename core strings.
$( 'input[name="comment_status"]' ).parent().find( '.checkbox-title' ).text( woocommerce_quick_edit.strings.allow_reviews ); $( 'input[name="comment_status"]' ).parent().find( '.checkbox-title' ).text( woocommerce_quick_edit.strings.allow_reviews );
}); }
);
$( '#the-list' ).on( 'change', '.inline-edit-row input[name="_manage_stock"]', function() { $( '#the-list' ).on(
'change',
'.inline-edit-row input[name="_manage_stock"]',
function() {
if ( $( this ).is( ':checked' ) ) { if ( $( this ).is( ':checked' ) ) {
$( '.stock_qty_field, .backorder_field', '.inline-edit-row' ).show().removeAttr( 'style' ); $( '.stock_qty_field, .backorder_field', '.inline-edit-row' ).show().removeAttr( 'style' );
@ -111,15 +128,23 @@ jQuery(function( $ ) {
$( '.stock_status_field' ).show().removeAttr( 'style' ); $( '.stock_status_field' ).show().removeAttr( 'style' );
} }
}); }
);
$( '#wpbody' ).on( 'click', '#doaction, #doaction2', function() { $( '#wpbody' ).on(
'click',
'#doaction, #doaction2',
function() {
$( 'input.text', '.inline-edit-row' ).val( '' ); $( 'input.text', '.inline-edit-row' ).val( '' );
$( '#woocommerce-fields' ).find( 'select' ).prop( 'selectedIndex', 0 ); $( '#woocommerce-fields' ).find( 'select' ).prop( 'selectedIndex', 0 );
$( '#woocommerce-fields-bulk' ).find( '.inline-edit-group .change-input' ).hide(); $( '#woocommerce-fields-bulk' ).find( '.inline-edit-group .change-input' ).hide();
}); }
);
$( '#wpbody' ).on( 'change', '#woocommerce-fields-bulk .inline-edit-group .change_to', function() { $( '#wpbody' ).on(
'change',
'#woocommerce-fields-bulk .inline-edit-group .change_to',
function() {
if ( 0 < $( this ).val() ) { if ( 0 < $( this ).val() ) {
$( this ).closest( 'div' ).find( '.change-input' ).show(); $( this ).closest( 'div' ).find( '.change-input' ).show();
@ -127,9 +152,15 @@ jQuery(function( $ ) {
$( this ).closest( 'div' ).find( '.change-input' ).hide(); $( this ).closest( 'div' ).find( '.change-input' ).hide();
} }
}); }
);
$( '#wpbody' ).on( 'click', '.trash-product', function() { $( '#wpbody' ).on(
'click',
'.trash-product',
function() {
return window.confirm( woocommerce_admin.i18n_delete_product_notice ); return window.confirm( woocommerce_admin.i18n_delete_product_notice );
}); }
}); );
}
);

View File

@ -79,6 +79,8 @@ class WC_Admin_Post_Types {
public function setup_screen() { public function setup_screen() {
global $wc_list_table; global $wc_list_table;
$request_data = $this->request_data();
$screen_id = false; $screen_id = false;
if ( function_exists( 'get_current_screen' ) ) { if ( function_exists( 'get_current_screen' ) ) {
@ -86,8 +88,8 @@ class WC_Admin_Post_Types {
$screen_id = isset( $screen, $screen->id ) ? $screen->id : ''; $screen_id = isset( $screen, $screen->id ) ? $screen->id : '';
} }
if ( ! empty( $_REQUEST['screen'] ) ) { // WPCS: input var ok. if ( ! empty( $request_data['screen'] ) ) {
$screen_id = wc_clean( wp_unslash( $_REQUEST['screen'] ) ); // WPCS: input var ok, sanitization ok. $screen_id = wc_clean( wp_unslash( $request_data['screen'] ) );
} }
switch ( $screen_id ) { switch ( $screen_id ) {
@ -296,6 +298,8 @@ class WC_Admin_Post_Types {
* @return int * @return int
*/ */
public function bulk_and_quick_edit_save_post( $post_id, $post ) { public function bulk_and_quick_edit_save_post( $post_id, $post ) {
$request_data = $this->request_data();
// If this is an autosave, our form has not been submitted, so we don't want to do anything. // If this is an autosave, our form has not been submitted, so we don't want to do anything.
if ( Constants::is_true( 'DOING_AUTOSAVE' ) ) { if ( Constants::is_true( 'DOING_AUTOSAVE' ) ) {
return $post_id; return $post_id;
@ -307,14 +311,15 @@ class WC_Admin_Post_Types {
} }
// Check nonce. // Check nonce.
if ( ! isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) || ! wp_verify_nonce( $_REQUEST['woocommerce_quick_edit_nonce'], 'woocommerce_quick_edit_nonce' ) ) { // WPCS: input var ok, sanitization ok. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
if ( ! isset( $request_data['woocommerce_quick_edit_nonce'] ) || ! wp_verify_nonce( $request_data['woocommerce_quick_edit_nonce'], 'woocommerce_quick_edit_nonce' ) ) {
return $post_id; return $post_id;
} }
// Get the product and save. // Get the product and save.
$product = wc_get_product( $post ); $product = wc_get_product( $post );
if ( ! empty( $_REQUEST['woocommerce_quick_edit'] ) ) { // WPCS: input var ok. if ( ! empty( $request_data['woocommerce_quick_edit'] ) ) { // WPCS: input var ok.
$this->quick_edit_save( $post_id, $product ); $this->quick_edit_save( $post_id, $product );
} else { } else {
$this->bulk_edit_save( $post_id, $product ); $this->bulk_edit_save( $post_id, $product );
@ -330,6 +335,8 @@ class WC_Admin_Post_Types {
* @param WC_Product $product Product object. * @param WC_Product $product Product object.
*/ */
private function quick_edit_save( $post_id, $product ) { private function quick_edit_save( $post_id, $product ) {
$request_data = $this->request_data();
$data_store = $product->get_data_store(); $data_store = $product->get_data_store();
$old_regular_price = $product->get_regular_price(); $old_regular_price = $product->get_regular_price();
$old_sale_price = $product->get_sale_price(); $old_sale_price = $product->get_sale_price();
@ -344,14 +351,15 @@ class WC_Admin_Post_Types {
); );
foreach ( $input_to_props as $input_var => $prop ) { foreach ( $input_to_props as $input_var => $prop ) {
if ( isset( $_REQUEST[ $input_var ] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data[ $input_var ] ) ) {
$product->{"set_{$prop}"}( wc_clean( wp_unslash( $_REQUEST[ $input_var ] ) ) ); // WPCS: input var ok, sanitization ok. $product->{"set_{$prop}"}( wc_clean( wp_unslash( $request_data[ $input_var ] ) ) );
} }
} }
if ( isset( $_REQUEST['_sku'] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data['_sku'] ) ) {
$sku = $product->get_sku(); $sku = $product->get_sku();
$new_sku = (string) wc_clean( $_REQUEST['_sku'] ); // WPCS: input var ok, sanitization ok. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$new_sku = (string) wc_clean( $request_data['_sku'] );
if ( $new_sku !== $sku ) { if ( $new_sku !== $sku ) {
if ( ! empty( $new_sku ) ) { if ( ! empty( $new_sku ) ) {
@ -365,27 +373,30 @@ class WC_Admin_Post_Types {
} }
} }
if ( ! empty( $_REQUEST['_shipping_class'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_shipping_class'] ) ) {
if ( '_no_shipping_class' === $_REQUEST['_shipping_class'] ) { // WPCS: input var ok, sanitization ok. if ( '_no_shipping_class' === $request_data['_shipping_class'] ) {
$product->set_shipping_class_id( 0 ); $product->set_shipping_class_id( 0 );
} else { } else {
$shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $_REQUEST['_shipping_class'] ) ); // WPCS: input var ok, sanitization ok. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $request_data['_shipping_class'] ) );
$product->set_shipping_class_id( $shipping_class_id ); $product->set_shipping_class_id( $shipping_class_id );
} }
} }
$product->set_featured( isset( $_REQUEST['_featured'] ) ); // WPCS: input var ok, sanitization ok. $product->set_featured( isset( $request_data['_featured'] ) );
if ( $product->is_type( 'simple' ) || $product->is_type( 'external' ) ) { if ( $product->is_type( 'simple' ) || $product->is_type( 'external' ) ) {
if ( isset( $_REQUEST['_regular_price'] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data['_regular_price'] ) ) {
$new_regular_price = ( '' === $_REQUEST['_regular_price'] ) ? '' : wc_format_decimal( $_REQUEST['_regular_price'] ); // WPCS: input var ok, sanitization ok. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$new_regular_price = ( '' === $request_data['_regular_price'] ) ? '' : wc_format_decimal( $request_data['_regular_price'] );
$product->set_regular_price( $new_regular_price ); $product->set_regular_price( $new_regular_price );
} else { } else {
$new_regular_price = null; $new_regular_price = null;
} }
if ( isset( $_REQUEST['_sale_price'] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data['_sale_price'] ) ) {
$new_sale_price = ( '' === $_REQUEST['_sale_price'] ) ? '' : wc_format_decimal( $_REQUEST['_sale_price'] ); // WPCS: input var ok, sanitization ok. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$new_sale_price = ( '' === $request_data['_sale_price'] ) ? '' : wc_format_decimal( $request_data['_sale_price'] );
$product->set_sale_price( $new_sale_price ); $product->set_sale_price( $new_sale_price );
} else { } else {
$new_sale_price = null; $new_sale_price = null;
@ -407,35 +418,25 @@ class WC_Admin_Post_Types {
} }
// Handle Stock Data. // Handle Stock Data.
$manage_stock = ! empty( $_REQUEST['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no'; // WPCS: input var ok, sanitization ok. // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$backorders = ! empty( $_REQUEST['_backorders'] ) ? wc_clean( $_REQUEST['_backorders'] ) : 'no'; // WPCS: input var ok, sanitization ok. $manage_stock = ! empty( $request_data['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no';
$stock_status = ! empty( $_REQUEST['_stock_status'] ) ? wc_clean( $_REQUEST['_stock_status'] ) : 'instock'; // WPCS: input var ok, sanitization ok. $backorders = ! empty( $request_data['_backorders'] ) ? wc_clean( $request_data['_backorders'] ) : 'no';
$stock_amount = 'yes' === $manage_stock && isset( $_REQUEST['_stock'] ) && is_numeric( wp_unslash( $_REQUEST['_stock'] ) ) ? wc_stock_amount( wp_unslash( $_REQUEST['_stock'] ) ) : ''; // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_stock_status'] ) ) {
$stock_status = wc_clean( $request_data['_stock_status'] );
} else {
$stock_status = $product->is_type( 'variable' ) ? null : 'instock';
}
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$product->set_manage_stock( $manage_stock ); $product->set_manage_stock( $manage_stock );
$product->set_backorders( $backorders ); $product->set_backorders( $backorders );
if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
$stock_amount = 'yes' === $manage_stock && isset( $request_data['_stock'] ) && is_numeric( wp_unslash( $request_data['_stock'] ) ) ? wc_stock_amount( wp_unslash( $request_data['_stock'] ) ) : '';
$product->set_stock_quantity( $stock_amount ); $product->set_stock_quantity( $stock_amount );
} }
// Apply product type constraints to stock status. $product = $this->maybe_update_stock_status( $product, $stock_status );
if ( $product->is_type( 'external' ) ) {
// External products are always in stock.
$product->set_stock_status( 'instock' );
} elseif ( $product->is_type( 'variable' ) && ! $product->get_manage_stock() ) {
// Stock status is determined by children.
foreach ( $product->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
if ( ! $product->get_manage_stock() ) {
$child->set_stock_status( $stock_status );
$child->save();
}
}
$product = WC_Product_Variable::sync( $product, false );
} else {
$product->set_stock_status( $stock_status );
}
$product->save(); $product->save();
@ -449,58 +450,61 @@ class WC_Admin_Post_Types {
* @param WC_Product $product Product object. * @param WC_Product $product Product object.
*/ */
public function bulk_edit_save( $post_id, $product ) { public function bulk_edit_save( $post_id, $product ) {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$request_data = $this->request_data();
$data_store = $product->get_data_store(); $data_store = $product->get_data_store();
$old_regular_price = $product->get_regular_price();
$old_sale_price = $product->get_sale_price();
$data = wp_unslash( $_REQUEST ); // WPCS: input var ok, CSRF ok.
if ( ! empty( $_REQUEST['change_weight'] ) && isset( $_REQUEST['_weight'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['change_weight'] ) && isset( $request_data['_weight'] ) ) {
$product->set_weight( wc_clean( wp_unslash( $_REQUEST['_weight'] ) ) ); // WPCS: input var ok, sanitization ok. $product->set_weight( wc_clean( wp_unslash( $request_data['_weight'] ) ) );
} }
if ( ! empty( $_REQUEST['change_dimensions'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['change_dimensions'] ) ) {
if ( isset( $_REQUEST['_length'] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data['_length'] ) ) {
$product->set_length( wc_clean( wp_unslash( $_REQUEST['_length'] ) ) ); // WPCS: input var ok, sanitization ok. $product->set_length( wc_clean( wp_unslash( $request_data['_length'] ) ) );
} }
if ( isset( $_REQUEST['_width'] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data['_width'] ) ) {
$product->set_width( wc_clean( wp_unslash( $_REQUEST['_width'] ) ) ); // WPCS: input var ok, sanitization ok. $product->set_width( wc_clean( wp_unslash( $request_data['_width'] ) ) );
} }
if ( isset( $_REQUEST['_height'] ) ) { // WPCS: input var ok, sanitization ok. if ( isset( $request_data['_height'] ) ) {
$product->set_height( wc_clean( wp_unslash( $_REQUEST['_height'] ) ) ); // WPCS: input var ok, sanitization ok. $product->set_height( wc_clean( wp_unslash( $request_data['_height'] ) ) );
} }
} }
if ( ! empty( $_REQUEST['_tax_status'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_tax_status'] ) ) {
$product->set_tax_status( wc_clean( $_REQUEST['_tax_status'] ) ); // WPCS: input var ok, sanitization ok. $product->set_tax_status( wc_clean( $request_data['_tax_status'] ) );
} }
if ( ! empty( $_REQUEST['_tax_class'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_tax_class'] ) ) {
$tax_class = wc_clean( wp_unslash( $_REQUEST['_tax_class'] ) ); // WPCS: input var ok, sanitization ok. $tax_class = wc_clean( wp_unslash( $request_data['_tax_class'] ) );
if ( 'standard' === $tax_class ) { if ( 'standard' === $tax_class ) {
$tax_class = ''; $tax_class = '';
} }
$product->set_tax_class( $tax_class ); $product->set_tax_class( $tax_class );
} }
if ( ! empty( $_REQUEST['_shipping_class'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_shipping_class'] ) ) {
if ( '_no_shipping_class' === $_REQUEST['_shipping_class'] ) { // WPCS: input var ok, sanitization ok. if ( '_no_shipping_class' === $request_data['_shipping_class'] ) {
$product->set_shipping_class_id( 0 ); $product->set_shipping_class_id( 0 );
} else { } else {
$shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $_REQUEST['_shipping_class'] ) ); // WPCS: input var ok, sanitization ok. $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $request_data['_shipping_class'] ) );
$product->set_shipping_class_id( $shipping_class_id ); $product->set_shipping_class_id( $shipping_class_id );
} }
} }
if ( ! empty( $_REQUEST['_visibility'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_visibility'] ) ) {
$product->set_catalog_visibility( wc_clean( $_REQUEST['_visibility'] ) ); // WPCS: input var ok, sanitization ok. $product->set_catalog_visibility( wc_clean( $request_data['_visibility'] ) );
} }
if ( ! empty( $_REQUEST['_featured'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_featured'] ) ) {
$product->set_featured( wp_unslash( $_REQUEST['_featured'] ) ); // WPCS: input var ok, sanitization ok. // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$product->set_featured( wp_unslash( $request_data['_featured'] ) );
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} }
if ( ! empty( $_REQUEST['_sold_individually'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_sold_individually'] ) ) {
if ( 'yes' === $_REQUEST['_sold_individually'] ) { // WPCS: input var ok, sanitization ok. if ( 'yes' === $request_data['_sold_individually'] ) {
$product->set_sold_individually( 'yes' ); $product->set_sold_individually( 'yes' );
} else { } else {
$product->set_sold_individually( '' ); $product->set_sold_individually( '' );
@ -518,93 +522,10 @@ class WC_Admin_Post_Types {
} }
if ( $can_product_type_change_price ) { if ( $can_product_type_change_price ) {
$price_changed = false; $regular_price_changed = $this->set_new_price( $product, 'regular' );
$sale_price_changed = $this->set_new_price( $product, 'sale' );
if ( ! empty( $_REQUEST['change_regular_price'] ) && isset( $_REQUEST['_regular_price'] ) ) { // WPCS: input var ok, sanitization ok. if ( $regular_price_changed || $sale_price_changed ) {
$change_regular_price = absint( $_REQUEST['change_regular_price'] ); // WPCS: input var ok, sanitization ok.
$raw_regular_price = wc_clean( wp_unslash( $_REQUEST['_regular_price'] ) ); // WPCS: input var ok, sanitization ok.
$is_percentage = (bool) strstr( $raw_regular_price, '%' );
$regular_price = wc_format_decimal( $raw_regular_price );
switch ( $change_regular_price ) {
case 1:
$new_price = $regular_price;
break;
case 2:
if ( $is_percentage ) {
$percent = $regular_price / 100;
$new_price = $old_regular_price + ( round( $old_regular_price * $percent, wc_get_price_decimals() ) );
} else {
$new_price = $old_regular_price + $regular_price;
}
break;
case 3:
if ( $is_percentage ) {
$percent = $regular_price / 100;
$new_price = max( 0, $old_regular_price - ( round( $old_regular_price * $percent, wc_get_price_decimals() ) ) );
} else {
$new_price = max( 0, $old_regular_price - $regular_price );
}
break;
default:
break;
}
if ( isset( $new_price ) && $new_price !== $old_regular_price ) {
$price_changed = true;
$new_price = round( $new_price, wc_get_price_decimals() );
$product->set_regular_price( $new_price );
}
}
if ( ! empty( $_REQUEST['change_sale_price'] ) && isset( $_REQUEST['_sale_price'] ) ) { // WPCS: input var ok, sanitization ok.
$change_sale_price = absint( $_REQUEST['change_sale_price'] ); // WPCS: input var ok, sanitization ok.
$raw_sale_price = wc_clean( wp_unslash( $_REQUEST['_sale_price'] ) ); // WPCS: input var ok, sanitization ok.
$is_percentage = (bool) strstr( $raw_sale_price, '%' );
$sale_price = wc_format_decimal( $raw_sale_price );
switch ( $change_sale_price ) {
case 1:
$new_price = $sale_price;
break;
case 2:
if ( $is_percentage ) {
$percent = $sale_price / 100;
$new_price = $old_sale_price + ( $old_sale_price * $percent );
} else {
$new_price = $old_sale_price + $sale_price;
}
break;
case 3:
if ( $is_percentage ) {
$percent = $sale_price / 100;
$new_price = max( 0, $old_sale_price - ( $old_sale_price * $percent ) );
} else {
$new_price = max( 0, $old_sale_price - $sale_price );
}
break;
case 4:
if ( $is_percentage ) {
$percent = $sale_price / 100;
$new_price = max( 0, $product->regular_price - ( $product->regular_price * $percent ) );
} else {
$new_price = max( 0, $product->regular_price - $sale_price );
}
break;
default:
break;
}
if ( isset( $new_price ) && $new_price !== $old_sale_price ) {
$price_changed = true;
$new_price = ! empty( $new_price ) || '0' === $new_price ? round( $new_price, wc_get_price_decimals() ) : '';
$product->set_sale_price( $new_price );
}
}
if ( $price_changed ) {
$product->set_date_on_sale_to( '' ); $product->set_date_on_sale_to( '' );
$product->set_date_on_sale_from( '' ); $product->set_date_on_sale_from( '' );
@ -616,24 +537,22 @@ class WC_Admin_Post_Types {
// Handle Stock Data. // Handle Stock Data.
$was_managing_stock = $product->get_manage_stock() ? 'yes' : 'no'; $was_managing_stock = $product->get_manage_stock() ? 'yes' : 'no';
$stock_status = $product->get_stock_status();
$backorders = $product->get_backorders(); $backorders = $product->get_backorders();
$backorders = ! empty( $_REQUEST['_backorders'] ) ? wc_clean( $_REQUEST['_backorders'] ) : $backorders; // WPCS: input var ok, sanitization ok. $backorders = ! empty( $request_data['_backorders'] ) ? wc_clean( $request_data['_backorders'] ) : $backorders;
$stock_status = ! empty( $_REQUEST['_stock_status'] ) ? wc_clean( $_REQUEST['_stock_status'] ) : $stock_status; // WPCS: input var ok, sanitization ok.
if ( ! empty( $_REQUEST['_manage_stock'] ) ) { // WPCS: input var ok, sanitization ok. if ( ! empty( $request_data['_manage_stock'] ) ) {
$manage_stock = 'yes' === wc_clean( $_REQUEST['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no'; // WPCS: input var ok, sanitization ok. $manage_stock = 'yes' === wc_clean( $request_data['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no';
} else { } else {
$manage_stock = $was_managing_stock; $manage_stock = $was_managing_stock;
} }
$stock_amount = 'yes' === $manage_stock && ! empty( $_REQUEST['change_stock'] ) && isset( $_REQUEST['_stock'] ) ? wc_stock_amount( $_REQUEST['_stock'] ) : $product->get_stock_quantity(); // WPCS: input var ok, sanitization ok. $stock_amount = 'yes' === $manage_stock && ! empty( $request_data['change_stock'] ) && isset( $request_data['_stock'] ) ? wc_stock_amount( $request_data['_stock'] ) : $product->get_stock_quantity();
$product->set_manage_stock( $manage_stock ); $product->set_manage_stock( $manage_stock );
$product->set_backorders( $backorders ); $product->set_backorders( $backorders );
if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
$change_stock = absint( $_REQUEST['change_stock'] ); $change_stock = absint( $request_data['change_stock'] );
switch ( $change_stock ) { switch ( $change_stock ) {
case 2: case 2:
wc_update_product_stock( $product, $stock_amount, 'increase', true ); wc_update_product_stock( $product, $stock_amount, 'increase', true );
@ -651,27 +570,14 @@ class WC_Admin_Post_Types {
$product->set_manage_stock( 'no' ); $product->set_manage_stock( 'no' );
} }
// Apply product type constraints to stock status. $stock_status = empty( $request_data['_stock_status'] ) ? null : wc_clean( $request_data['_stock_status'] );
if ( $product->is_type( 'external' ) ) { $product = $this->maybe_update_stock_status( $product, $stock_status );
// External products are always in stock.
$product->set_stock_status( 'instock' );
} elseif ( $product->is_type( 'variable' ) && ! $product->get_manage_stock() ) {
// Stock status is determined by children.
foreach ( $product->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
if ( ! $product->get_manage_stock() ) {
$child->set_stock_status( $stock_status );
$child->save();
}
}
$product = WC_Product_Variable::sync( $product, false );
} else {
$product->set_stock_status( $stock_status );
}
$product->save(); $product->save();
do_action( 'woocommerce_product_bulk_edit_save', $product ); do_action( 'woocommerce_product_bulk_edit_save', $product );
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
} }
/** /**
@ -719,11 +625,13 @@ class WC_Admin_Post_Types {
* @param WP_Post $post Current post object. * @param WP_Post $post Current post object.
*/ */
public function edit_form_after_title( $post ) { public function edit_form_after_title( $post ) {
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
if ( 'shop_coupon' === $post->post_type ) { if ( 'shop_coupon' === $post->post_type ) {
?> ?>
<textarea id="woocommerce-coupon-description" name="excerpt" cols="5" rows="2" placeholder="<?php esc_attr_e( 'Description (optional)', 'woocommerce' ); ?>"><?php echo $post->post_excerpt; // WPCS: XSS ok. ?></textarea> <textarea id="woocommerce-coupon-description" name="excerpt" cols="5" rows="2" placeholder="<?php esc_attr_e( 'Description (optional)', 'woocommerce' ); ?>"><?php echo $post->post_excerpt; ?></textarea>
<?php <?php
} }
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
} }
/** /**
@ -802,7 +710,8 @@ class WC_Admin_Post_Types {
* @return array * @return array
*/ */
public function upload_dir( $pathdata ) { public function upload_dir( $pathdata ) {
if ( isset( $_POST['type'] ) && 'downloadable_product' === $_POST['type'] ) { // WPCS: CSRF ok, input var ok. // phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['type'] ) && 'downloadable_product' === $_POST['type'] ) {
if ( empty( $pathdata['subdir'] ) ) { if ( empty( $pathdata['subdir'] ) ) {
$pathdata['path'] = $pathdata['path'] . '/woocommerce_uploads'; $pathdata['path'] = $pathdata['path'] . '/woocommerce_uploads';
@ -817,6 +726,7 @@ class WC_Admin_Post_Types {
} }
} }
return $pathdata; return $pathdata;
// phpcs:enable WordPress.Security.NonceVerification.Missing
} }
/** /**
@ -830,7 +740,8 @@ class WC_Admin_Post_Types {
* @since 4.0 * @since 4.0
*/ */
public function update_filename( $full_filename, $ext, $dir ) { public function update_filename( $full_filename, $ext, $dir ) {
if ( ! isset( $_POST['type'] ) || ! 'downloadable_product' === $_POST['type'] ) { // WPCS: CSRF ok, input var ok. // phpcs:disable WordPress.Security.NonceVerification.Missing
if ( ! isset( $_POST['type'] ) || ! 'downloadable_product' === $_POST['type'] ) {
return $full_filename; return $full_filename;
} }
@ -843,6 +754,7 @@ class WC_Admin_Post_Types {
} }
return $this->unique_filename( $full_filename, $ext ); return $this->unique_filename( $full_filename, $ext );
// phpcs:enable WordPress.Security.NonceVerification.Missing
} }
/** /**
@ -961,6 +873,118 @@ class WC_Admin_Post_Types {
return $post_states; return $post_states;
} }
/**
* Apply product type constraints to stock status.
*
* @param WC_Product $product The product whose stock status will be adjusted.
* @param string|null $stock_status The stock status to use for adjustment, or null if no new stock status has been supplied in the request.
* @return WC_Product The supplied product, or the synced product if it was a variable product.
*/
private function maybe_update_stock_status( $product, $stock_status ) {
if ( $product->is_type( 'external' ) ) {
// External products are always in stock.
$product->set_stock_status( 'instock' );
} elseif ( isset( $stock_status ) ) {
if ( $product->is_type( 'variable' ) && ! $product->get_manage_stock() ) {
// Stock status is determined by children.
foreach ( $product->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
if ( ! $product->get_manage_stock() ) {
$child->set_stock_status( $stock_status );
$child->save();
}
}
$product = WC_Product_Variable::sync( $product, false );
} else {
$product->set_stock_status( $stock_status );
}
}
return $product;
}
/**
* Set the new regular or sale price if requested.
*
* @param WC_Product $product The product to set the new price for.
* @param string $price_type 'regular' or 'sale'.
* @return bool true if a new price has been set, false otherwise.
*/
private function set_new_price( $product, $price_type ) {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$request_data = $this->request_data();
if ( empty( $request_data[ "change_{$price_type}_price" ] ) || ! isset( $request_data[ "_{$price_type}_price" ] ) ) {
return false;
}
$old_price = $product->{"get_{$price_type}_price"}();
$price_changed = false;
$change_price = absint( $request_data[ "change_{$price_type}_price" ] );
$raw_price = wc_clean( wp_unslash( $request_data[ "_{$price_type}_price" ] ) );
$is_percentage = (bool) strstr( $raw_price, '%' );
$price = wc_format_decimal( $raw_price );
switch ( $change_price ) {
case 1:
$new_price = $price;
break;
case 2:
if ( $is_percentage ) {
$percent = $price / 100;
$new_price = $old_price + ( $old_price * $percent );
} else {
$new_price = $old_price + $price;
}
break;
case 3:
if ( $is_percentage ) {
$percent = $price / 100;
$new_price = max( 0, $old_price - ( $old_price * $percent ) );
} else {
$new_price = max( 0, $old_price - $price );
}
break;
case 4:
if ( 'sale' !== $price_type ) {
break;
}
$regular_price = $product->get_regular_price();
if ( $is_percentage ) {
$percent = $price / 100;
$new_price = max( 0, $regular_price - ( round( $regular_price * $percent, wc_get_price_decimals() ) ) );
} else {
$new_price = max( 0, $regular_price - $price );
}
break;
default:
break;
}
if ( isset( $new_price ) && $new_price !== $old_price ) {
$price_changed = true;
$new_price = round( $new_price, wc_get_price_decimals() );
$product->{"set_{$price_type}_price"}( $new_price );
}
return $price_changed;
// phpcs:disable WordPress.Security.NonceVerification.Recommended
}
/**
* Get the current request data ($_REQUEST superglobal).
* This method is added to ease unit testing.
*
* @return array The $_REQUEST superglobal.
*/
protected function request_data() {
return $_REQUEST;
}
} }
new WC_Admin_Post_Types(); new WC_Admin_Post_Types();

View File

@ -140,7 +140,8 @@ defined( 'ABSPATH' ) || exit;
<select class="visibility" name="_visibility"> <select class="visibility" name="_visibility">
<?php <?php
$options = apply_filters( $options = apply_filters(
'woocommerce_product_visibility_options', array( 'woocommerce_product_visibility_options',
array(
'visible' => __( 'Catalog &amp; search', 'woocommerce' ), 'visible' => __( 'Catalog &amp; search', 'woocommerce' ),
'catalog' => __( 'Catalog', 'woocommerce' ), 'catalog' => __( 'Catalog', 'woocommerce' ),
'search' => __( 'Search', 'woocommerce' ), 'search' => __( 'Search', 'woocommerce' ),
@ -174,11 +175,15 @@ defined( 'ABSPATH' ) || exit;
<span class="input-text-wrap"> <span class="input-text-wrap">
<select class="stock_status" name="_stock_status"> <select class="stock_status" name="_stock_status">
<?php <?php
echo '<option value="" id="stock_status_no_change">' . esc_html__( '— No Change —', 'woocommerce' ) . '</option>';
foreach ( wc_get_product_stock_status_options() as $key => $value ) { foreach ( wc_get_product_stock_status_options() as $key => $value ) {
echo '<option value="' . esc_attr( $key ) . '">' . esc_html( $value ) . '</option>'; echo '<option value="' . esc_attr( $key ) . '">' . esc_html( $value ) . '</option>';
} }
?> ?>
</select> </select>
<div class="wc-quick-edit-warning" style="display:none">
<?php echo esc_html__( 'This will change the stock status of all variations.', 'woocommerce' ); ?></p>
</div>
</span> </span>
</label> </label>

View File

@ -1,4 +1,10 @@
<?php <?php
/**
* Base class for REST API test classes.
*
* @package WooCommerce\Tests\Framework
*/
/** /**
* WC API Unit Test Case * WC API Unit Test Case
* *
@ -19,18 +25,16 @@ class WC_API_Unit_Test_Case extends WC_Unit_Test_Case {
parent::setUp(); parent::setUp();
// load API classes // load API classes.
WC()->api->includes(); WC()->api->includes();
// set user // set user.
$this->user_id = $this->factory->user->create( array( 'role' => 'shop_manager' ) ); $this->user_id = $this->login_as_role( 'shop_manager' );
wp_set_current_user( $this->user_id );
// this isn't used, but it causes a warning unless set // this isn't used, but it causes a warning unless set.
$_SERVER['REQUEST_METHOD'] = null; $_SERVER['REQUEST_METHOD'] = null;
// mock the API server to prevent headers from being sent // mock the API server to prevent headers from being sent.
// $this->mock_server = $this->getMock( 'WC_API_Server', array( 'header' ), array( '/' ) );
$this->mock_server = $this->getMockBuilder( 'WC_API_Server' )->setMethods( array( 'header' ) )->disableOriginalConstructor()->getMock(); $this->mock_server = $this->getMockBuilder( 'WC_API_Server' )->setMethods( array( 'header' ) )->disableOriginalConstructor()->getMock();
WC()->api->register_resources( $this->mock_server ); WC()->api->register_resources( $this->mock_server );
@ -40,19 +44,19 @@ class WC_API_Unit_Test_Case extends WC_Unit_Test_Case {
* Assert the given response is an API error with a specific code and status. * Assert the given response is an API error with a specific code and status.
* *
* @since 2.2 * @since 2.2
* @param string $code error code, e.g. `woocommerce_api_user_cannot_read_orders_count` * @param string $code error code, e.g. `woocommerce_api_user_cannot_read_orders_count`.
* @param int|null $status HTTP status code associated with error, e.g. 400 * @param int|null $status HTTP status code associated with error, e.g. 400.
* @param WP_Error $response * @param WP_Error $response Response to assert.
* @param string $message optional message to render when assertion fails * @param string $message optional message to render when assertion fails.
*/ */
public function assertHasAPIError( $code, $status = null, $response, $message = '' ) { public function assertHasAPIError( $code, $status = null, $response, $message = '' ) {
$this->assertWPError( $response, $message ); $this->assertWPError( $response, $message );
// code // code.
$this->assertEquals( $code, $response->get_error_code(), $message ); $this->assertEquals( $code, $response->get_error_code(), $message );
// status // status.
$data = $response->get_error_data(); $data = $response->get_error_data();
$this->assertArrayHasKey( 'status', $data, $message ); $this->assertArrayHasKey( 'status', $data, $message );
@ -65,14 +69,14 @@ class WC_API_Unit_Test_Case extends WC_Unit_Test_Case {
* permission checking. * permission checking.
* *
* @since 2.2 * @since 2.2
* @param string $capability, e.g. `read_private_shop_orders` * @param string $capability e.g. `read_private_shop_orders`.
*/ */
protected function disable_capability( $capability ) { protected function disable_capability( $capability ) {
$user = wp_get_current_user(); $user = wp_get_current_user();
$user->add_cap( $capability, false ); $user->add_cap( $capability, false );
// flush capabilities, see https://core.trac.wordpress.org/ticket/28374 // flush capabilities, see https://core.trac.wordpress.org/ticket/28374.
$user->get_role_caps(); $user->get_role_caps();
$user->update_user_level_from_caps(); $user->update_user_level_from_caps();
} }

View File

@ -134,7 +134,6 @@ class WC_Unit_Test_Case extends WP_HTTP_TestCase {
throw new Exception( $message, $code ); throw new Exception( $message, $code );
} }
/** /**
* Copies a file, temporarily disabling the code hacker. * Copies a file, temporarily disabling the code hacker.
* Use this instead of "copy" in tests for compatibility with the code hacker. * Use this instead of "copy" in tests for compatibility with the code hacker.
@ -149,9 +148,31 @@ class WC_Unit_Test_Case extends WP_HTTP_TestCase {
self::disable_code_hacker(); self::disable_code_hacker();
$result = copy( $source, $dest ); $result = copy( $source, $dest );
self::reenable_code_hacker(); self::reenable_code_hacker();
return $result; return $result;
} }
/**
* Create a new user in a given role and set it as the current user.
*
* @param string $role The role for the user to be created.
* @return int The id of the user created.
*/
public function login_as_role( $role ) {
$user_id = $this->factory->user->create( array( 'role' => $role ) );
wp_set_current_user( $user_id );
return $user_id;
}
/**
* Create a new administrator user and set it as the current user.
*
* @return int The id of the user created.
*/
public function login_as_administrator() {
return $this->login_as_role( 'administrator' );
}
/** /**
* Get an instance of a class that has been registered in the dependency injection container. * Get an instance of a class that has been registered in the dependency injection container.
* To get an instance of a legacy class (such as the ones in the 'íncludes' directory) use * To get an instance of a legacy class (such as the ones in the 'íncludes' directory) use

View File

@ -29,11 +29,12 @@ class WC_Helper_Product {
* *
* @since 2.3 * @since 2.3
* @param bool $save Save or return object. * @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 * @return WC_Product_Simple
*/ */
public static function create_simple_product( $save = true ) { public static function create_simple_product( $save = true, $props = array() ) {
$product = new WC_Product_Simple(); $product = new WC_Product_Simple();
$product->set_props( $default_props =
array( array(
'name' => 'Dummy Product', 'name' => 'Dummy Product',
'regular_price' => 10, 'regular_price' => 10,
@ -45,9 +46,10 @@ class WC_Helper_Product {
'virtual' => false, 'virtual' => false,
'stock_status' => 'instock', 'stock_status' => 'instock',
'weight' => '1.1', 'weight' => '1.1',
)
); );
$product->set_props( array_merge( $default_props, $props ) );
if ( $save ) { if ( $save ) {
$product->save(); $product->save();
return wc_get_product( $product->get_id() ); return wc_get_product( $product->get_id() );

View File

@ -355,12 +355,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case {
*/ */
public function test_request_url() { public function test_request_url() {
// User set up. // User set up.
$this->user = $this->factory->user->create( $this->login_as_administrator();
array(
'role' => 'administrator',
)
);
wp_set_current_user( $this->user );
// wc_tax_enabled(), wc_prices_include_tax() and WC_Gateway_Paypal_Request::prepare_line_items() determine if // wc_tax_enabled(), wc_prices_include_tax() and WC_Gateway_Paypal_Request::prepare_line_items() determine if
// shipping tax should be included, these are the correct options. // shipping tax should be included, these are the correct options.
@ -371,6 +366,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case {
// woocommerce_calc_taxes, woocommerce_prices_include_tax, $shipping_tax_included values. // woocommerce_calc_taxes, woocommerce_prices_include_tax, $shipping_tax_included values.
array( 'no', 'no', false ), array( 'no', 'no', false ),
array( 'yes', 'no', false ), array( 'yes', 'no', false ),
// phpcs:ignore Squiz.PHP.CommentedOutCode.Found
// array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax(). // array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax().
array( 'yes', 'yes', true ), array( 'yes', 'yes', true ),
); );
@ -399,7 +395,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case {
// Many items in order -> forced to use one line item -> shipping tax included. // Many items in order -> forced to use one line item -> shipping tax included.
$this->check_large_order( true, $testmode ); $this->check_large_order( true, $testmode );
// Test removing tags from line item name // Test removing tags from line item name.
$this->check_product_title_containing_html( $testmode ); $this->check_product_title_containing_html( $testmode );
// Test amount < 0. // Test amount < 0.

View File

@ -1,22 +1,30 @@
<?php <?php
/**
* Payment_Tokens class file.
*
* @package WooCommerce\Tests\Payment_Tokens
*/
/** /**
* Class Payment_Tokens * Class Payment_Tokens
* @package WooCommerce\Tests\Payment_Tokens
*/ */
class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case { class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
/**
* Setup the test case.
*
* @see WC_Unit_Test_Case::setUp()
*/
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->user_id = $this->factory->user->create( array( 'role' => 'shop_manager' ) ); $this->user_id = $this->login_as_role( 'shop_manager' );
wp_set_current_user( $this->user_id );
} }
/** /**
* Test getting tokens associated with an order. * Test getting tokens associated with an order.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_get_order_tokens() { public function test_wc_payment_tokens_get_order_tokens() {
$order = WC_Helper_Order::create_order(); $order = WC_Helper_Order::create_order();
$this->assertEmpty( WC_Payment_Tokens::get_order_tokens( $order->get_id() ) ); $this->assertEmpty( WC_Payment_Tokens::get_order_tokens( $order->get_id() ) );
@ -31,7 +39,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test getting tokens associated with a user and no gateway ID. * Test getting tokens associated with a user and no gateway ID.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_get_customer_tokens_no_gateway() { public function test_wc_payment_tokens_get_customer_tokens_no_gateway() {
$this->assertEmpty( WC_Payment_Tokens::get_customer_tokens( $this->user_id ) ); $this->assertEmpty( WC_Payment_Tokens::get_customer_tokens( $this->user_id ) );
$token = WC_Helper_Payment_Token::create_cc_token(); $token = WC_Helper_Payment_Token::create_cc_token();
@ -49,7 +57,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test getting tokens associated with a user and for a specific gateway. * Test getting tokens associated with a user and for a specific gateway.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_get_customer_tokens_with_gateway() { public function test_wc_payment_tokens_get_customer_tokens_with_gateway() {
$this->assertEmpty( WC_Payment_Tokens::get_customer_tokens( $this->user_id ) ); $this->assertEmpty( WC_Payment_Tokens::get_customer_tokens( $this->user_id ) );
$token = WC_Helper_Payment_Token::create_cc_token(); $token = WC_Helper_Payment_Token::create_cc_token();
@ -74,7 +82,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test getting a customers default token. * Test getting a customers default token.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_get_customer_default_token() { public function test_wc_get_customer_default_token() {
$token = WC_Helper_Payment_Token::create_cc_token(); $token = WC_Helper_Payment_Token::create_cc_token();
$token->set_user_id( $this->user_id ); $token->set_user_id( $this->user_id );
$token->set_gateway_id( 'bacs' ); $token->set_gateway_id( 'bacs' );
@ -99,7 +107,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* @group failing * @group failing
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_get_customer_default_token_returns_first_created_when_no_default_token_set() { public function test_wc_get_customer_default_token_returns_first_created_when_no_default_token_set() {
$token = WC_Helper_Payment_Token::create_cc_token( $this->user_id ); $token = WC_Helper_Payment_Token::create_cc_token( $this->user_id );
$token->set_gateway_id( 'bacs' ); $token->set_gateway_id( 'bacs' );
$token->save(); $token->save();
@ -118,7 +126,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test getting a token by ID. * Test getting a token by ID.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_get() { public function test_wc_payment_tokens_get() {
$token = WC_Helper_Payment_Token::create_cc_token(); $token = WC_Helper_Payment_Token::create_cc_token();
$token_id = $token->get_id(); $token_id = $token->get_id();
$get_token = WC_Payment_Tokens::get( $token_id ); $get_token = WC_Payment_Tokens::get( $token_id );
@ -129,7 +137,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test deleting a token by ID. * Test deleting a token by ID.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_delete() { public function test_wc_payment_tokens_delete() {
$token = WC_Helper_Payment_Token::create_cc_token(); $token = WC_Helper_Payment_Token::create_cc_token();
$token_id = $token->get_id(); $token_id = $token->get_id();
@ -143,7 +151,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test getting a token's type by ID. * Test getting a token's type by ID.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_get_type_by_id() { public function test_wc_payment_tokens_get_type_by_id() {
$token = WC_Helper_Payment_Token::create_cc_token(); $token = WC_Helper_Payment_Token::create_cc_token();
$token_id = $token->get_id(); $token_id = $token->get_id();
$this->assertEquals( 'CC', WC_Payment_Tokens::get_token_type_by_id( $token_id ) ); $this->assertEquals( 'CC', WC_Payment_Tokens::get_token_type_by_id( $token_id ) );
@ -153,7 +161,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
* Test setting a users default token. * Test setting a users default token.
* @since 2.6.0 * @since 2.6.0
*/ */
function test_wc_payment_tokens_set_users_default() { public function test_wc_payment_tokens_set_users_default() {
$token = WC_Helper_Payment_Token::create_cc_token( $this->user_id ); $token = WC_Helper_Payment_Token::create_cc_token( $this->user_id );
$token_id = $token->get_id(); $token_id = $token->get_id();
$token->save(); $token->save();
@ -162,7 +170,7 @@ class WC_Tests_Payment_Tokens extends WC_Unit_Test_Case {
$token_id_2 = $token2->get_id(); $token_id_2 = $token2->get_id();
$token2->save(); $token2->save();
$this->assertTrue( $token->is_default() ); // first created is default $this->assertTrue( $token->is_default() ); // first created is default.
$this->assertFalse( $token2->is_default() ); $this->assertFalse( $token2->is_default() );
WC_Payment_Tokens::set_users_default( $this->user_id, $token_id_2 ); WC_Payment_Tokens::set_users_default( $this->user_id, $token_id_2 );

View File

@ -19,8 +19,7 @@ class WC_Tests_Setup_Functions extends WC_Unit_Test_Case {
$setup_wizard = new WC_Admin_Setup_Wizard(); $setup_wizard = new WC_Admin_Setup_Wizard();
// non-admin user. // non-admin user.
$this->user_id = $this->factory->user->create( array( 'role' => 'shop_manager' ) ); $this->user_id = $this->login_as_role( 'shop_manager' );
wp_set_current_user( $this->user_id );
$this->assertEquals( $this->assertEquals(
array( array(
'paypal' => false, 'paypal' => false,
@ -29,8 +28,7 @@ class WC_Tests_Setup_Functions extends WC_Unit_Test_Case {
); );
// set admin user. // set admin user.
$this->user_id = $this->factory->user->create( array( 'role' => 'administrator' ) ); $this->user_id = $this->login_as_administrator();
wp_set_current_user( $this->user_id );
update_option( 'woocommerce_default_country', 'US' ); update_option( 'woocommerce_default_country', 'US' );
$this->assertEquals( $this->assertEquals(

View File

@ -0,0 +1,152 @@
<?php
/**
* Unit tests for the post types admin class.
*
* @package WooCommerce\Tests\Admin
*/
/**
* WC_Admin_Post_Types tests.
*
* @package WooCommerce\Tests\Admin
*/
class WC_Tests_Admin_Post_Types extends WC_Unit_Test_Case {
/**
* Get a new SUT (System Under Test) instance and configure the specified fake request data.
*
* @param array $request_data Fake request data to configure.
* @return WC_Admin_Post_Types
*/
private function get_sut_with_request_data( $request_data ) {
$sut = $this
->getMockBuilder( WC_Admin_Post_Types::class )
->setMethods( array( 'request_data' ) )
->getMock();
$sut->method( 'request_data' )->willReturn( $request_data );
return $sut;
}
/**
* Data for bulk_and_quick_edit_stock_status_for_variable_product test.
*
* @return array
*/
public function data_provider_bulk_and_quick_edit_stock_status_for_variable_product() {
return array(
// phpcs:ignore Squiz.PHP.CommentedOutCode.Found
// $edit_type, $change_stock_request_value, $expected_new_stock_status
array( 'quick_edit', '', 'outofstock' ),
array( 'quick_edit', 'instock', 'instock' ),
array( 'bulk_edit', '', 'outofstock' ),
array( 'bulk_edit', 'instock', 'instock' ),
);
}
/**
* @test
* @testdox When quick or bulk editing a variable product, stock status for the variations should change only if a new stock status is supplied.
* @dataProvider data_provider_bulk_and_quick_edit_stock_status_for_variable_product
*
* @param string $edit_type 'quick_edit' or 'bulk_edit'.
* @param string $change_stock_request_value The value of '_stock_status' from the request.
* @param string $expected_new_stock_status Expected value of the stock status for the variations after the save operation.
*/
public function bulk_and_quick_edit_stock_status_for_variable_product( $edit_type, $change_stock_request_value, $expected_new_stock_status ) {
$product = WC_Helper_Product::create_variation_product();
foreach ( $product->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
$product->set_manage_stock( false );
$child->set_stock_status( 'outofstock' );
$child->save();
}
$this->login_as_administrator();
$request_data = array(
"woocommerce_{$edit_type}" => '1',
'_stock_status' => $change_stock_request_value,
'woocommerce_quick_edit_nonce' => wp_create_nonce( 'woocommerce_quick_edit_nonce' ),
);
$sut = $this->get_sut_with_request_data( $request_data );
$sut->bulk_and_quick_edit_save_post( $product->get_id(), get_post( $product->get_id() ) );
foreach ( $product->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
$this->assertEquals( $expected_new_stock_status, $child->get_stock_status() );
}
}
/**
* Data for bulk_change_price test.
*
* @return array
*/
public function data_provider_bulk_change_price() {
$dataset = array();
// phpcs:ignore Squiz.PHP.CommentedOutCode.Found
// $type_of_price, $initial_price, $type_of_change, $change_amount, $expected_new_price
foreach ( array( 'regular', 'sale' ) as $type ) {
array_push( $dataset, array( $type, '10.33', '1', '5.339', 5.34 ) );
array_push( $dataset, array( $type, '10.33', '2', '5.3333', 15.66 ) );
array_push( $dataset, array( $type, '10.33', '2', '15.555%', 11.94 ) );
array_push( $dataset, array( $type, '10.33', '3', '5.339', 4.99 ) );
array_push( $dataset, array( $type, '10.33', '3', '15.555%', 8.72 ) );
}
array_push( $dataset, array( 'regular', '10.33', '4', '5.339', 10.33 ) );
array_push( $dataset, array( 'regular', '10.33', '4', '15.555%', 10.33 ) );
array_push( $dataset, array( 'sale', '10.33', '4', '5.339', 97.96 ) );
array_push( $dataset, array( 'sale', '10.33', '4', '15.555%', 87.23 ) );
return $dataset;
}
/**
* @test
* @testdox Prices should change appropriately when a price change is requested via bulk edit.
* @dataProvider data_provider_bulk_change_price
*
* @param string $type_of_price 'regular' or 'sale'.
* @param string $initial_price Initial value for the price.
* @param string $type_of_change 1=absolute, 2=increase, 3=decrease, 4=regular minus (for sale price only).
* @param string $change_amount The amount to change, ending with '%' if it's a percent.
* @param string $expected_new_price Expected value of the product price after the save operation.
*/
public function bulk_change_price( $type_of_price, $initial_price, $type_of_change, $change_amount, $expected_new_price ) {
if ( 'regular' === $type_of_price ) {
$props = array( 'regular_price' => $initial_price );
} else {
$props = array(
'regular_price' => $initial_price * 10,
'sale_price' => $initial_price,
);
}
$product = WC_Helper_Product::create_simple_product( true, $props );
$this->login_as_administrator();
$request_data = array(
'woocommerce_bulk_edit' => '1',
'woocommerce_quick_edit_nonce' => wp_create_nonce( 'woocommerce_quick_edit_nonce' ),
"change_{$type_of_price}_price" => $type_of_change,
"_{$type_of_price}_price" => $change_amount,
);
$sut = $this->get_sut_with_request_data( $request_data );
$sut->bulk_and_quick_edit_save_post( $product->get_id(), get_post( $product->get_id() ) );
$product = wc_get_product( $product->get_id() );
$actual = $product->{"get_{$type_of_price}_price"}();
$this->assertEquals( $expected_new_price, $actual );
}
}