/** * 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; }
/** * Returns whether or not the product needs to notify the customer on backorder. * * @return bool */ public function backorders_require_notification() { if (true === $this->managing_stock()) { return parent::backorders_require_notification(); } else { return $this->parent->backorders_require_notification(); } }
/** * Modifies the results of get_available_variations() to implement variation filtering and bundle discounts for variable products. * Also calculates variation prices incl. or excl. tax. * * @param array $variation_data unmodified variation data * @param WC_Product $bundled_product the bundled product * @param WC_Product_Variation $bundled_variation the variation in question * @return array modified variation data */ function bundled_item_available_variation($variation_data, $bundled_product, $bundled_variation) { global $woocommerce_bundles; $bundled_item_id = $this->item_id; // Disable if certain conditions are met if (!empty($this->allowed_variations)) { if (!is_array($this->allowed_variations)) { return array(); } if (!in_array($bundled_variation->variation_id, $this->allowed_variations)) { return array(); } } if ($bundled_variation->price === '') { return array(); } // Modify product id for JS (deprecated) $variation_data['product_id'] = $bundled_item_id; // Add price data $variation_data['regular_price'] = $woocommerce_bundles->helpers->get_product_price_incl_or_excl_tax($bundled_variation, $bundled_variation->get_regular_price()); $variation_data['price'] = $woocommerce_bundles->helpers->get_product_price_incl_or_excl_tax($bundled_variation, $bundled_variation->get_price()); $variation_data['price_html'] = $this->is_priced_per_product() ? $variation_data['price_html'] === '' ? '<p class="price">' . $bundled_variation->get_price_html() . '</p>' : $variation_data['price_html'] : ''; // Modify availability data $quantity = $this->get_quantity(); $quantity_max = $this->get_quantity('max'); $availability = $this->get_availability($bundled_variation); if (!$bundled_variation->is_in_stock() || !$bundled_variation->has_enough_stock($quantity)) { $variation_data['is_in_stock'] = false; } if ($bundled_variation->is_on_backorder() && $bundled_product->backorders_require_notification()) { $variation_data['is_on_backorder'] = 'available-on-backorder'; } $availability_html = !empty($availability['availability']) ? apply_filters('woocommerce_stock_html', '<p class="stock ' . $availability['class'] . '">' . $availability['availability'] . '</p>', $availability['availability']) : ''; $variation_data['availability_html'] = $availability_html; $variation_data['min_qty'] = $quantity; $variation_data['max_qty'] = $quantity_max; return $variation_data; }
/** * A bundle on backorder requires notification if the container is defined like this, or a bundled item is on backorder and requires notification. * * @return boolean true if backorders require notification or if has items on backorder */ public function backorders_require_notification() { if (!$this->is_synced()) { $this->sync_bundle(); } return parent::backorders_require_notification() || $this->has_items_on_backorder; }
/** * 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(); }
/** * Modifies the results of get_available_variations() to implement variation filtering and bundle discounts for variable products. * Also calculates variation prices incl. or excl. tax. * * @param array $variation_data unmodified variation data * @param WC_Product $bundled_product the bundled product * @param WC_Product_Variation $bundled_variation the variation in question * @return array modified variation data */ public function bundled_item_available_variation($variation_data, $bundled_product, $bundled_variation) { $bundled_item_id = $this->item_id; // Disable if certain conditions are met... if (!empty($this->allowed_variations)) { if (!is_array($this->allowed_variations)) { return array(); } if (!in_array($bundled_variation->variation_id, $this->allowed_variations)) { return array(); } } if ($bundled_variation->price === '') { return array(); } // Modify product id for JS (deprecated). $variation_data['product_id'] = $bundled_item_id; // Add price data with WC 2.2 missing display_regular_price/display_price compatibility. $variation_data['regular_price'] = isset($variation_data['display_regular_price']) ? $variation_data['display_regular_price'] : WC_PB_Helpers::get_product_display_price($bundled_variation, $bundled_variation->get_regular_price()); $variation_data['price'] = isset($variation_data['display_price']) ? $variation_data['display_price'] : WC_PB_Helpers::get_product_display_price($bundled_variation, $bundled_variation->get_price()); $variation_data['regular_recurring_price'] = ''; $variation_data['recurring_price'] = ''; $variation_data['recurring_html'] = ''; $variation_data['recurring_key'] = ''; if ($bundled_product->product_type === 'variable-subscription') { $variation_data['regular_recurring_price'] = $variation_data['regular_price']; $variation_data['recurring_price'] = $variation_data['price']; $signup_fee = WC_PB_Helpers::get_product_display_price($bundled_variation, $bundled_variation->get_sign_up_fee()); $variation_data['regular_price'] = $this->get_prorated_price_for_subscription($variation_data['regular_price'], $signup_fee, $bundled_variation); $variation_data['price'] = $this->get_prorated_price_for_subscription($variation_data['price'], $signup_fee, $bundled_variation); $variation_data['recurring_html'] = WC_PB_Helpers::get_recurring_price_html_component($bundled_variation); $variation_data['recurring_key'] = str_replace('_synced', '', WC_Subscriptions_Cart::get_recurring_cart_key(array('data' => $bundled_variation), ' ')); } $variation_price_html = ''; if ($this->is_priced_per_product()) { $variation_price_html = $variation_data['price_html'] === '' ? '<p class="price">' . $bundled_variation->get_price_html() . '</p>' : $variation_data['price_html']; } $variation_data['price_html'] = $variation_price_html; // Modify availability data. $quantity = $this->get_quantity(); $quantity_max = $this->get_quantity('max', true, $bundled_variation); $availability = $this->get_availability($bundled_variation); if (!$this->is_in_stock() || !$bundled_variation->is_in_stock() || !$bundled_variation->has_enough_stock($quantity)) { $variation_data['is_in_stock'] = false; } if ($bundled_variation->is_on_backorder() && $bundled_product->backorders_require_notification()) { $variation_data['is_on_backorder'] = 'available-on-backorder'; } $availability_html = empty($availability['availability']) ? '' : '<p class="stock ' . esc_attr($availability['class']) . '">' . wp_kses_post($availability['availability']) . '</p>'; $variation_data['availability_html'] = apply_filters('woocommerce_stock_html', $availability_html, $availability['availability'], $bundled_variation); $variation_data['min_qty'] = $quantity; $variation_data['max_qty'] = $quantity_max; if ($variation_data['min_qty'] !== $variation_data['max_qty']) { $variation_data['is_sold_individually'] = false; } return $variation_data; }