/** * Bundled product availability that takes quantity into account. * * @param WC_Product $product the product * @param int $quantity the quantity * @return array availability data */ function get_bundled_product_availability($product, $quantity) { $availability = $class = ''; if ($product->managing_stock()) { if ($product->is_in_stock() && $product->get_total_stock() > get_option('woocommerce_notify_no_stock_amount') && $product->get_total_stock() >= $quantity) { switch (get_option('woocommerce_stock_format')) { case 'no_amount': $availability = __('In stock', 'woocommerce'); break; case 'low_amount': if ($product->get_total_stock() <= get_option('woocommerce_notify_low_stock_amount')) { $availability = sprintf(__('Only %s left in stock', 'woocommerce'), $product->get_total_stock()); if ($product->backorders_allowed() && $product->backorders_require_notification()) { $availability .= ' ' . __('(can be backordered)', 'woocommerce'); } } else { $availability = __('In stock', 'woocommerce'); } break; default: $availability = sprintf(__('%s in stock', 'woocommerce'), $product->get_total_stock()); if ($product->backorders_allowed() && $product->backorders_require_notification()) { $availability .= ' ' . __('(can be backordered)', 'woocommerce'); } break; } $class = 'in-stock'; } elseif ($product->backorders_allowed() && $product->backorders_require_notification()) { if ($product->get_total_stock() >= $quantity || get_option('woocommerce_stock_format') == 'no_amount') { $availability = __('Available on backorder', 'woocommerce'); } else { $availability = __('Available on backorder', 'woocommerce') . ' ' . sprintf(__('(only %s left in stock)', 'woocommerce-product-bundles'), $product->get_total_stock()); } $class = 'available-on-backorder'; } elseif ($product->backorders_allowed()) { $availability = __('In stock', 'woocommerce'); $class = 'in-stock'; } else { if ($product->is_in_stock() && $product->get_total_stock() > get_option('woocommerce_notify_no_stock_amount')) { if (get_option('woocommerce_stock_format') == 'no_amount') { $availability = __('Insufficient stock', 'woocommerce-product-bundles'); } else { $availability = __('Insufficient stock', 'woocommerce-product-bundles') . ' ' . sprintf(__('(only %s left in stock)', 'woocommerce-product-bundles'), $product->get_total_stock()); } $class = 'out-of-stock'; } else { $availability = __('Out of stock', 'woocommerce'); $class = 'out-of-stock'; } } } elseif (!$product->is_in_stock()) { $availability = __('Out of stock', 'woocommerce'); $class = 'out-of-stock'; } _deprecated_function('get_bundled_product_availability', '4.8.8', 'WC_Bundled_Item::get_availability()'); return apply_filters('woocommerce_get_bundled_product_availability', array('availability' => $availability, 'class' => $class), $product); }
/** * Add a product line item to the order * * @since 2.2 * @param \WC_Product $product * @param int $qty Line item quantity * @param array $args * @return int|bool Item ID or false */ public function add_product($product, $qty = 1, $args = array()) { $default_args = array('variation' => array(), 'totals' => array()); $args = wp_parse_args($args, $default_args); $item_id = wc_add_order_item($this->id, array('order_item_name' => $product->get_title(), 'order_item_type' => 'line_item')); if (!$item_id) { return false; } wc_add_order_item_meta($item_id, '_qty', wc_stock_amount($qty)); wc_add_order_item_meta($item_id, '_tax_class', $product->get_tax_class()); wc_add_order_item_meta($item_id, '_product_id', $product->id); wc_add_order_item_meta($item_id, '_variation_id', isset($product->variation_id) ? $product->variation_id : 0); // Set line item totals, either passed in or from the product wc_add_order_item_meta($item_id, '_line_subtotal', wc_format_decimal(isset($args['totals']['subtotal']) ? $args['totals']['subtotal'] : $product->get_price_excluding_tax($qty))); wc_add_order_item_meta($item_id, '_line_total', wc_format_decimal(isset($args['totals']['total']) ? $args['totals']['total'] : $product->get_price_excluding_tax($qty))); wc_add_order_item_meta($item_id, '_line_subtotal_tax', wc_format_decimal(isset($args['totals']['subtotal_tax']) ? $args['totals']['subtotal_tax'] : 0)); wc_add_order_item_meta($item_id, '_line_tax', wc_format_decimal(isset($args['totals']['tax']) ? $args['totals']['tax'] : 0)); // Save tax data - Since 2.2 if (isset($args['totals']['tax_data'])) { $tax_data = array(); $tax_data['total'] = array_map('wc_format_decimal', $args['totals']['tax_data']['total']); $tax_data['subtotal'] = array_map('wc_format_decimal', $args['totals']['tax_data']['subtotal']); wc_add_order_item_meta($item_id, '_line_tax_data', $tax_data); } else { wc_add_order_item_meta($item_id, '_line_tax_data', array('total' => array(), 'subtotal' => array())); } // Add variation meta if (!empty($args['variation'])) { foreach ($args['variation'] as $key => $value) { wc_add_order_item_meta($item_id, str_replace('attribute_', '', $key), $value); } } // Backorders if ($product->backorders_require_notification() && $product->is_on_backorder($qty)) { wc_add_order_item_meta($item_id, apply_filters('woocommerce_backordered_item_meta_name', __('Backordered', 'woocommerce')), $qty - max(0, $product->get_total_stock())); } do_action('woocommerce_order_add_product', $this->id, $item_id, $product, $qty, $args); return $item_id; }
/** * Low stock notification email. * * @param WC_Product $product */ public function low_stock($product) { $subject = sprintf('[%s] %s', $this->get_blogname(), __('Product low in stock', 'woocommerce')); $message = sprintf(__('%s is low in stock.', 'woocommerce'), html_entity_decode(strip_tags($product->get_formatted_name()), ENT_QUOTES, get_bloginfo('charset'))) . ' ' . sprintf(__('There are %d left', 'woocommerce'), html_entity_decode(strip_tags($product->get_total_stock()))); wp_mail(apply_filters('woocommerce_email_recipient_low_stock', get_option('woocommerce_stock_email_recipient'), $product), apply_filters('woocommerce_email_subject_low_stock', $subject, $product), apply_filters('woocommerce_email_content_low_stock', $message, $product), apply_filters('woocommerce_email_headers', '', 'low_stock', $product), apply_filters('woocommerce_email_attachments', array(), 'low_stock', $product)); }
function woocommerce_custom_product_columns($column) { global $post, $woocommerce; $product = new WC_Product($post->ID); switch ($column) { case "thumb": $product->get_image(); break; case "name": $edit_link = get_edit_post_link($post->ID); $title = _draft_or_post_title(); $post_type_object = get_post_type_object($post->post_type); $can_edit_post = current_user_can($post_type_object->cap->edit_post, $post->ID); echo '<strong><a class="row-title" href="' . $edit_link . '">' . $title . '</a>'; _post_states($post); echo '</strong>'; if ($post->post_parent > 0) { echo ' ← <a href="' . get_edit_post_link($post->post_parent) . '">' . get_the_title($post->post_parent) . '</a>'; } // Excerpt view if (isset($_GET['mode']) && $_GET['mode'] == 'excerpt') { echo apply_filters('the_excerpt', $post->post_excerpt); } // Get actions $actions = array(); $actions['id'] = 'ID: ' . $post->ID; if ($can_edit_post && 'trash' != $post->post_status) { $actions['inline hide-if-no-js'] = '<a href="#" class="editinline" title="' . esc_attr(__('Edit this item inline', 'woocommerce')) . '">' . __('Quick Edit', 'woocommerce') . '</a>'; } if (current_user_can($post_type_object->cap->delete_post, $post->ID)) { if ('trash' == $post->post_status) { $actions['untrash'] = "<a title='" . esc_attr(__('Restore this item from the Trash', 'woocommerce')) . "' href='" . wp_nonce_url(admin_url(sprintf($post_type_object->_edit_link . '&action=untrash', $post->ID)), 'untrash-' . $post->post_type . '_' . $post->ID) . "'>" . __('Restore', 'woocommerce') . "</a>"; } elseif (EMPTY_TRASH_DAYS) { $actions['trash'] = "<a class='submitdelete' title='" . esc_attr(__('Move this item to the Trash', 'woocommerce')) . "' href='" . get_delete_post_link($post->ID) . "'>" . __('Trash', 'woocommerce') . "</a>"; } if ('trash' == $post->post_status || !EMPTY_TRASH_DAYS) { $actions['delete'] = "<a class='submitdelete' title='" . esc_attr(__('Delete this item permanently', 'woocommerce')) . "' href='" . get_delete_post_link($post->ID, '', true) . "'>" . __('Delete Permanently', 'woocommerce') . "</a>"; } } if ($post_type_object->public) { if (in_array($post->post_status, array('pending', 'draft'))) { if ($can_edit_post) { $actions['view'] = '<a href="' . esc_url(add_query_arg('preview', 'true', get_permalink($post->ID))) . '" title="' . esc_attr(sprintf(__('Preview “%s”', 'woocommerce'), $title)) . '" rel="permalink">' . __('Preview', 'woocommerce') . '</a>'; } } elseif ('trash' != $post->post_status) { $actions['view'] = '<a href="' . get_permalink($post->ID) . '" title="' . esc_attr(sprintf(__('View “%s”', 'woocommerce'), $title)) . '" rel="permalink">' . __('View', 'woocommerce') . '</a>'; } } $actions = apply_filters('post_row_actions', $actions, $post); echo '<div class="row-actions">'; $i = 0; $action_count = sizeof($actions); foreach ($actions as $action => $link) { ++$i; $i == $action_count ? $sep = '' : ($sep = ' | '); echo "<span class='{$action}'>{$link}{$sep}</span>"; } echo '</div>'; get_inline_data($post); /* Custom inline data for woocommerce */ echo ' <div class="hidden" id="woocommerce_inline_' . $post->ID . '"> <div class="sku">' . $product->sku . '</div> <div class="regular_price">' . $product->regular_price . '</div> <div class="sale_price">' . $product->sale_price . '</div> <div class="weight">' . $product->weight . '</div> <div class="length">' . $product->length . '</div> <div class="width">' . $product->width . '</div> <div class="height">' . $product->height . '</div> <div class="visibility">' . $product->visibility . '</div> <div class="stock_status">' . $product->stock_status . '</div> <div class="stock">' . $product->stock . '</div> <div class="manage_stock">' . $product->manage_stock . '</div> <div class="featured">' . $product->featured . '</div> <div class="product_type">' . $product->product_type . '</div> <div class="product_is_virtual">' . $product->virtual . '</div> </div> '; break; case "sku": if ($product->get_sku()) { echo $product->get_sku(); } else { echo '<span class="na">–</span>'; } break; case "product_type": if ($product->product_type == 'grouped') { echo '<span class="product-type tips ' . $product->product_type . '" tip="' . __('Grouped', 'woocommerce') . '"></span>'; } elseif ($product->product_type == 'external') { echo '<span class="product-type tips ' . $product->product_type . '" tip="' . __('External/Affiliate', 'woocommerce') . '"></span>'; } elseif ($product->product_type == 'simple') { if ($product->is_virtual()) { echo '<span class="product-type tips virtual" tip="' . __('Virtual', 'woocommerce') . '"></span>'; } elseif ($product->is_downloadable()) { echo '<span class="product-type tips downloadable" tip="' . __('Downloadable', 'woocommerce') . '"></span>'; } else { echo '<span class="product-type tips ' . $product->product_type . '" tip="' . __('Simple', 'woocommerce') . '"></span>'; } } elseif ($product->product_type == 'variable') { echo '<span class="product-type tips ' . $product->product_type . '" tip="' . __('Variable', 'woocommerce') . '"></span>'; } else { // Assuming that we have other types in future echo '<span class="product-type tips ' . $product->product_type . '" tip="' . ucwords($product->product_type) . '"></span>'; } break; case "price": if ($product->get_price_html()) { echo $product->get_price_html(); } else { echo '<span class="na">–</span>'; } break; case "product_cat": if (!($terms = get_the_term_list($post->ID, 'product_cat', '', ', ', ''))) { echo '<span class="na">–</span>'; } else { echo $terms; } break; case "product_tags": if (!($terms = get_the_term_list($post->ID, 'product_tag', '', ', ', ''))) { echo '<span class="na">–</span>'; } else { echo $terms; } break; case "featured": $url = wp_nonce_url(admin_url('admin-ajax.php?action=woocommerce-feature-product&product_id=' . $post->ID), 'woocommerce-feature-product'); echo '<a href="' . $url . '" title="' . __('Change', 'woocommerce') . '">'; if ($product->is_featured()) { echo '<a href="' . $url . '"><img src="' . $woocommerce->plugin_url() . '/assets/images/featured.png" alt="yes" />'; } else { echo '<img src="' . $woocommerce->plugin_url() . '/assets/images/featured-off.png" alt="no" />'; } echo '</a>'; break; case "is_in_stock": if ($product->is_in_stock()) { echo '<mark class="instock">' . __('In stock', 'woocommerce') . '</mark>'; } else { echo '<mark class="outofstock">' . __('Out of stock', 'woocommerce') . '</mark>'; } if ($product->managing_stock()) { echo ' × ' . $product->get_total_stock(); } break; } }
/** * Reduce stock level of the product * * @param int $by Amount to reduce by */ function reduce_stock($by = 1) { global $woocommerce; if ($this->variation_has_stock) { if ($this->managing_stock()) { $this->stock = $this->stock - $by; $this->total_stock = $this->total_stock - $by; update_post_meta($this->variation_id, '_stock', $this->stock); $woocommerce->clear_product_transients($this->id); // Clear transient // Check parents out of stock attribute if (!$this->is_in_stock()) { // Check parent $parent_product = new WC_Product($this->id); // Only continue if the parent has backorders off if (!$parent_product->backorders_allowed() && $parent_product->get_total_stock() <= 0) { update_post_meta($this->id, '_stock_status', 'outofstock'); } } return $this->stock; } } else { return parent::reduce_stock($by); } }
function process_qty() { global $woocommerce; // Save quantities if (!empty($_POST['stock_quantity']) && !empty($_POST['save_stock'])) { check_admin_referer('save', 'wc-stock-management'); $quantities = $_POST['stock_quantity']; $current_quantities = $_POST['current_stock_quantity']; foreach ($quantities as $id => $qty) { if ($qty == '') { continue; } if (isset($current_quantities[$id])) { // Check the qty has not changed since showing the form $current_stock = (int) get_post_meta($id, '_stock', true); if ($current_stock == $current_quantities[$id]) { $post = get_post($id); // Format $qty $qty = (int) $qty; // Update stock amount update_post_meta($id, '_stock', $qty); // Update stock status if ($post->post_type == 'product') { // Update manage stock variable for products update_post_meta($id, '_manage_stock', 'yes'); if (function_exists('get_product')) { $product = get_product($post->ID); } else { $product = new WC_Product($post->ID); } if ($product->managing_stock() && !$product->backorders_allowed() && $product->get_total_stock() <= 0) { update_post_meta($post->ID, '_stock_status', 'outofstock'); } elseif ($product->managing_stock() && ($product->backorders_allowed() || $product->get_total_stock() > 0)) { update_post_meta($post->ID, '_stock_status', 'instock'); } $woocommerce->clear_product_transients($post->ID); // Clear transient } else { if (function_exists('get_product')) { $product = get_product($post->post_parent); } else { $product = new WC_Product($post->post_parent); } if ($product->managing_stock() && !$product->backorders_allowed() && $product->get_total_stock() <= 0) { update_post_meta($post->post_parent, '_stock_status', 'outofstock'); } elseif ($product->managing_stock() && ($product->backorders_allowed() || $product->get_total_stock() > 0)) { update_post_meta($post->post_parent, '_stock_status', 'instock'); } $woocommerce->clear_product_transients($post->post_parent); // Clear transient } } else { $this->messages[] = sprintf(__('Product # %s was not updated - the stock amount has changed since posting.', 'wc_stock_management'), $id); } } } $this->messages[] = __('Stock quantities saved.', 'wc_stock_management'); } }
/** * Update a line item for the order. * * Note this does not update order totals. * * @param object|int $item order item ID or item object. * @param WC_Product $product * @param array $args data to update. * @return int updated order item ID */ public function update_product($item, $product, $args) { _deprecated_function('WC_Order::update_product', '2.7', 'Interact with WC_Order_Item_Product class'); if (is_numeric($item)) { $item = $this->get_item($item); } if (!is_object($item) || !$item->is_type('line_item')) { return false; } if (!$this->get_id()) { $this->save(); // Order must exist } // BW compatibility with old args if (isset($args['totals'])) { foreach ($args['totals'] as $key => $value) { if ('tax' === $key) { $args['total_tax'] = $value; } elseif ('tax_data' === $key) { $args['taxes'] = $value; } else { $args[$key] = $value; } } } // Handly qty if set if (isset($args['qty'])) { if ($product->backorders_require_notification() && $product->is_on_backorder($args['qty'])) { $item->add_meta_data(apply_filters('woocommerce_backordered_item_meta_name', __('Backordered', 'woocommerce')), $args['qty'] - max(0, $product->get_total_stock()), true); } $args['subtotal'] = $args['subtotal'] ? $args['subtotal'] : $product->get_price_excluding_tax($args['qty']); $args['total'] = $args['total'] ? $args['total'] : $product->get_price_excluding_tax($args['qty']); } $item->set_order_id($this->get_id()); $item->set_all($args); $item->save(); do_action('woocommerce_order_edit_product', $this->get_id(), $item->get_id(), $args, $product); return $item->get_id(); }