/**
 * Filter the product retruned by `WC_Order::woocommerce_get_product_from_item()`
 * to re-calculate a Measurement Price Calculator product's weight based on the
 * selected measurements. This function ensures that the "Weight" calculator
 * type is handled appropriately as well.
 *
 * @param \WC_Product $product The product.
 * @param array $item The order item.
 * @param \WC_Order $order The order.
 * @return \WC_Product The filtered product
 */
function sv_wc_mpc_shipstation_get_product_from_item_weight($product, $item, $order)
{
    if (WC_Price_Calculator_Product::pricing_calculated_weight_enabled($product)) {
        $settings = new WC_Price_Calculator_Settings($product);
        if ('weight' == $settings->get_calculator_type()) {
            // Now, the weight calculator products have to be handled specially
            // since the customer is actually supplying the weight, but it will
            // be in pricing units which may not be the same as the globally
            // configured WooCommerce Weight Unit expected by other plugins and code
            if (isset($item['item_meta']['_measurement_data'][0])) {
                $measurement_data = maybe_unserialize($item['item_meta']['_measurement_data'][0]);
                if (isset($measurement_data['_measurement_needed_unit']) && isset($measurement_data['_measurement_needed'])) {
                    $supplied_weight = new WC_Price_Calculator_Measurement($measurement_data['_measurement_needed_unit'], $measurement_data['_measurement_needed']);
                    // set the product weight as supplied by the customer, in WC Weight Units
                    $product->weight = $supplied_weight->get_value(get_option('woocommerce_weight_unit'));
                }
            }
        } elseif ($product->get_weight()) {
            if (isset($item['item_meta']['_measurement_data'][0])) {
                $measurement_data = maybe_unserialize($item['item_meta']['_measurement_data'][0]);
                // record the configured weight per unit for future reference
                if (!isset($measurement_data['_weight'])) {
                    $measurement_data['_weight'] = $product->get_weight();
                }
                // calculate the product weight = unit weight * total measurement (both will be in the same pricing units so we have say lbs/sq. ft. * sq. ft. = lbs)
                $product->weight = $measurement_data['_weight'] * $measurement_data['_measurement_needed'];
            }
        }
    }
    return $product;
}
 /**
  * Add a measurement product to the cart.  This allows for the programmatic
  * addition of measurement pricing calculator products to the cart.
  *
  * This method expects the single total measurement needed, given by
  * $measurement_needed, this would be the dimension, area, volume or weight
  * depending on the type of calculator.
  *
  * This method also expects the full set of product measurements, given by
  * $measurements.  For calculators with a single measurement like the dimension
  * calculator or simple area, this will contain the same value as
  * $measurement_needed.  For more complex calculators, like Area (w x l)
  * this is how the width and length measurements are specified.  For
  * convenience use <code>WC_Price_Calculator_Product::get_product_measurements( $product )</code>
  * to get the set of dimensions for your product, along with the correct
  * units, and set whatever values you need.
  *
  * @since 3.0
  * @param string $product_id contains the id of the product to add to the cart
  * @param WC_Price_Calculator_Measurement $measurement_needed the total
  *        measurement desired, ie 1 m, 3 sq. ft., etc
  * @param array $measurements array of WC_Price_Calculator_Measurement product
  *        measurements, ie 1 m or 1.5 ft, 1.5 ft.  Defaults to $measurement_needed
  *        for convenience for calculators with a single measurement like dimension,
  *        simple area, etc.
  * @param string $quantity contains the quantity of the item to add
  * @param int $variation_id optional variation id
  * @param array $variation optional attribute values
  * @param array $cart_item_data optional extra cart item data we want to pass into the item
  * @return bool true on success
  */
 public function add_to_cart($product_id, $measurement_needed, $measurements = array(), $quantity = 1, $variation_id = '', $variation = '', $cart_item_data = array())
 {
     // if measurements is empty just use the provided $measurement_needed (this is a shortcut for calculators with only one measurement, ie 'length', 'area', etc)
     if (empty($measurements)) {
         $measurements[] = $measurement_needed;
     }
     // build up the cart item data with the required values that would normally come in over the add to cart post request
     $cart_item_data['pricing_item_meta_data']['_measurement_needed_internal'] = $measurement_needed->get_value();
     $cart_item_data['pricing_item_meta_data']['_measurement_needed_unit_internal'] = $measurement_needed->get_unit();
     $cart_item_data['pricing_item_meta_data']['_quantity'] = $quantity;
     $product = wc_get_product($product_id);
     $settings = new WC_Price_Calculator_Settings($product);
     if (WC_Price_Calculator_Product::pricing_calculator_inventory_enabled($product)) {
         // pricing calculator product with inventory enabled, means we need to take the item quantity (ie 2) and determine the unit quantity (ie 2 * 3 ft = 6)
         $quantity *= $measurement_needed->get_value($settings->get_pricing_unit());
     }
     foreach ($measurements as $measurement) {
         $cart_item_data['pricing_item_meta_data'][$measurement->get_name()] = $measurement->get_value();
     }
     // initialize the cart_contents member if needed to avoid a warning from cart::find_product_in_cart()
     if (is_null(WC()->cart->cart_contents)) {
         WC()->cart->cart_contents = array();
     }
     return WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variation, $cart_item_data);
 }
 /**
  * Gets the price for the given $measurement, if there is a matching pricing
  * rule, or null
  *
  * @since 3.0
  * @param WC_Price_Calculator_Measurement $measurement the product total measurement
  * @return float the price for the given $measurement (regular or sale)
  */
 public function get_pricing_rules_price($measurement)
 {
     // get the value in pricing units for comparison
     $measurement_value = $measurement->get_value($this->get_pricing_unit());
     foreach ($this->get_pricing_rules() as $rule) {
         // if we find a matching rule, return the price
         if ($measurement_value >= $rule['range_start'] && ('' === $rule['range_end'] || $measurement_value <= $rule['range_end'])) {
             return $rule['price'];
         }
     }
     return null;
 }
 /**
  * Manage the order stock (whether restore or reduce) from the order admin
  * returning the true product stock change if this is for a pricing calculator
  * product/item with inventory enabled.  Ie 2 pieces of cloth at 3 ft each
  * we'd want to return 6
  *
  * @since 3.0
  * @param numeric $quantity the new quantity
  * @param string $item_id the order item identifier
  * @return numeric $quantity the measurement quantity
  */
 public function admin_manage_order_stock($quantity, $item_id)
 {
     $order_id = absint($_POST['order_id']);
     $order = wc_get_order($order_id);
     $order_items = $order->get_items();
     $product = wc_get_product($order_items[$item_id]['product_id']);
     if (WC_Price_Calculator_Product::pricing_calculator_inventory_enabled($product) && isset($order_items[$item_id]['measurement_data'])) {
         $settings = new WC_Price_Calculator_Settings($product);
         $measurement_data = maybe_unserialize($order_items[$item_id]['measurement_data']);
         $total_amount = new WC_Price_Calculator_Measurement($measurement_data['_measurement_needed_unit'], $measurement_data['_measurement_needed']);
         // this is a pricing calculator product so we want to return the
         //  quantity in terms of units, ie 2 pieces of cloth at 3 ft each = 6
         $quantity *= $total_amount->get_value($settings->get_pricing_unit());
     }
     return $quantity;
 }