diff --git a/includes/admin/wc-admin-functions.php b/includes/admin/wc-admin-functions.php index d56a72829c8..724ed230f65 100644 --- a/includes/admin/wc-admin-functions.php +++ b/includes/admin/wc-admin-functions.php @@ -2,14 +2,12 @@ /** * WooCommerce Admin Functions * - * @author WooThemes - * @category Core * @package WooCommerce/Admin/Functions * @version 2.4.0 */ if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly + exit; } /** @@ -46,7 +44,9 @@ function wc_get_screen_ids() { $screen_ids[] = 'edit-' . $type; } - if ( $attributes = wc_get_attribute_taxonomies() ) { + $attributes = wc_get_attribute_taxonomies(); + + if ( $attributes ) { foreach ( $attributes as $attribute ) { $screen_ids[] = 'edit-' . wc_attribute_taxonomy_name( $attribute->attribute_name ); } @@ -58,30 +58,32 @@ function wc_get_screen_ids() { /** * Create a page and store the ID in an option. * - * @param mixed $slug Slug for the new page - * @param string $option Option name to store the page's ID - * @param string $page_title (default: '') Title for the new page - * @param string $page_content (default: '') Content for the new page - * @param int $post_parent (default: 0) Parent for the new page - * @return int page ID + * @param mixed $slug Slug for the new page. + * @param string $option Option name to store the page's ID. + * @param string $page_title (default: '') Title for the new page. + * @param string $page_content (default: '') Content for the new page. + * @param int $post_parent (default: 0) Parent for the new page. + * @return int page ID. */ function wc_create_page( $slug, $option = '', $page_title = '', $page_content = '', $post_parent = 0 ) { global $wpdb; $option_value = get_option( $option ); - if ( $option_value > 0 && ( $page_object = get_post( $option_value ) ) ) { - if ( 'page' === $page_object->post_type && ! in_array( $page_object->post_status, array( 'pending', 'trash', 'future', 'auto-draft' ) ) ) { - // Valid page is already in place + if ( $option_value > 0 ) { + $page_object = get_post( $option_value ); + + if ( $page_object && 'page' === $page_object->post_type && ! in_array( $page_object->post_status, array( 'pending', 'trash', 'future', 'auto-draft' ), true ) ) { + // Valid page is already in place. return $page_object->ID; } } if ( strlen( $page_content ) > 0 ) { - // Search for an existing page with the specified page content (typically a shortcode) + // Search for an existing page with the specified page content (typically a shortcode). $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) ); } else { - // Search for an existing page with the specified page slug + // Search for an existing page with the specified page slug. $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) ); } @@ -94,12 +96,12 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content = return $valid_page_found; } - // Search for a matching valid trashed page + // Search for a matching valid trashed page. if ( strlen( $page_content ) > 0 ) { - // Search for an existing page with the specified page content (typically a shortcode) + // Search for an existing page with the specified page content (typically a shortcode). $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) ); } else { - // Search for an existing page with the specified page slug + // Search for an existing page with the specified page slug. $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) ); } @@ -136,7 +138,7 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content = * * Loops though the woocommerce options array and outputs each field. * - * @param array $options Opens array to output + * @param array $options Opens array to output. */ function woocommerce_admin_fields( $options ) { @@ -150,8 +152,8 @@ function woocommerce_admin_fields( $options ) { /** * Update all settings which are passed. * - * @param array $options - * @param array $data + * @param array $options Option fields to save. + * @param array $data Passed data. */ function woocommerce_update_options( $options, $data = null ) { @@ -165,8 +167,8 @@ function woocommerce_update_options( $options, $data = null ) { /** * Get a setting from the settings API. * - * @param mixed $option_name - * @param mixed $default + * @param mixed $option_name Option name to save. + * @param mixed $default Default value to save. * @return string */ function woocommerce_settings_get_option( $option_name, $default = '' ) { @@ -178,17 +180,61 @@ function woocommerce_settings_get_option( $option_name, $default = '' ) { return WC_Admin_Settings::get_option( $option_name, $default ); } +/** + * Sees if line item stock has already reduced stock, and whether those values need adjusting e.g. after changing item qty. + * + * @since 3.6.0 + * @param WC_Order_Item $item Item object. + * @param integer $item_quantity Optional quantity to check against. Read from object if not passed. + * @return boolean|array|WP_Error Array of changes or error object when stock is updated (@see wc_update_product_stock). False if nothing changes. + */ +function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) { + if ( 'line_item' !== $item->get_type() ) { + return; + } + + $product = $item->get_product(); + $item_quantity = wc_stock_amount( $item_quantity >= 0 ? $item_quantity : $item->get_quantity() ); + $already_reduced_stock = wc_stock_amount( $item->get_meta( '_reduced_stock', true ) ); + + if ( ! $product || ! $product->managing_stock() || ! $already_reduced_stock || $item_quantity === $already_reduced_stock ) { + return; + } + + $diff = $item_quantity - $already_reduced_stock; + + if ( $diff < 0 ) { + $new_stock = wc_update_product_stock( $product, $diff * -1, 'increase' ); + } else { + $new_stock = wc_update_product_stock( $product, $diff, 'decrease' ); + } + + if ( is_wp_error( $new_stock ) ) { + return $new_stock; + } + + $item->update_meta_data( '_reduced_stock', $item_quantity ); + $item->save(); + + return array( + 'from' => $new_stock + $diff, + 'to' => $new_stock, + ); +} + /** * Save order items. Uses the CRUD. * * @since 2.2 - * @param int $order_id Order ID - * @param array $items Order items to save + * @param int $order_id Order ID. + * @param array $items Order items to save. */ function wc_save_order_items( $order_id, $items ) { // Allow other plugins to check change in order items before they are saved. do_action( 'woocommerce_before_save_order_items', $order_id, $items ); + $qty_change_order_notes = array(); + // Line items and fees. if ( isset( $items['order_item_id'] ) ) { $data_keys = array( @@ -201,7 +247,9 @@ function wc_save_order_items( $order_id, $items ) { 'line_subtotal' => null, ); foreach ( $items['order_item_id'] as $item_id ) { - if ( ! $item = WC_Order_Factory::get_order_item( absint( $item_id ) ) ) { + $item = WC_Order_Factory::get_order_item( absint( $item_id ) ); + + if ( ! $item ) { continue; } @@ -212,6 +260,10 @@ function wc_save_order_items( $order_id, $items ) { } if ( '0' === $item_data['order_item_qty'] ) { + $changed_stock = wc_maybe_adjust_line_item_product_stock( $item, 0 ); + if ( $changed_stock && ! is_wp_error( $changed_stock ) ) { + $qty_change_order_notes[] = $item->get_name() . ' – ' . $changed_stock['from'] . '→' . $changed_stock['to']; + } $item->delete(); continue; } @@ -255,10 +307,15 @@ function wc_save_order_items( $order_id, $items ) { do_action( 'woocommerce_before_save_order_item', $item ); $item->save(); + + $changed_stock = wc_maybe_adjust_line_item_product_stock( $item ); + if ( $changed_stock && ! is_wp_error( $changed_stock ) ) { + $qty_change_order_notes[] = $item->get_name() . ' (' . $changed_stock['from'] . '→' . $changed_stock['to'] . ')'; + } } } - // Shipping Rows + // Shipping Rows. if ( isset( $items['shipping_method_id'] ) ) { $data_keys = array( 'shipping_method' => null, @@ -268,7 +325,9 @@ function wc_save_order_items( $order_id, $items ) { ); foreach ( $items['shipping_method_id'] as $item_id ) { - if ( ! $item = WC_Order_Factory::get_order_item( absint( $item_id ) ) ) { + $item = WC_Order_Factory::get_order_item( absint( $item_id ) ); + + if ( ! $item ) { continue; } @@ -310,10 +369,16 @@ function wc_save_order_items( $order_id, $items ) { } $order = wc_get_order( $order_id ); + + if ( ! empty( $qty_change_order_notes ) ) { + /* translators: %s item name. */ + $order->add_order_note( sprintf( __( 'Adjusted stock: %s', 'woocommerce' ), implode( ', ', $qty_change_order_notes ) ), false, true ); + } + $order->update_taxes(); $order->calculate_totals( false ); - // Inform other plugins that the items have been saved + // Inform other plugins that the items have been saved. do_action( 'woocommerce_saved_order_items', $order_id, $items ); }