Merge branch 'feature-wc-create-order'

This commit is contained in:
Mike Jolley 2014-06-18 16:07:29 +01:00
commit cf3cca4c8e
9 changed files with 722 additions and 365 deletions

View File

@ -451,7 +451,6 @@ jQuery( function($){
}; };
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) { $.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) {
if ( response ) { if ( response ) {
$items.each( function() { $items.each( function() {
var $row = $(this); var $row = $(this);

View File

@ -786,8 +786,6 @@ class WC_Product {
* @return string * @return string
*/ */
public function get_price_including_tax( $qty = 1, $price = '' ) { public function get_price_including_tax( $qty = 1, $price = '' ) {
$_tax = new WC_Tax();
if ( ! $price ) { if ( ! $price ) {
$price = $this->get_price(); $price = $this->get_price();
} }
@ -796,26 +794,26 @@ class WC_Product {
if ( get_option('woocommerce_prices_include_tax') === 'no' ) { if ( get_option('woocommerce_prices_include_tax') === 'no' ) {
$tax_rates = $_tax->get_rates( $this->get_tax_class() ); $tax_rates = WC_Tax::get_rates( $this->get_tax_class() );
$taxes = $_tax->calc_tax( $price * $qty, $tax_rates, false ); $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, false );
$tax_amount = $_tax->get_tax_total( $taxes ); $tax_amount = WC_Tax::get_tax_total( $taxes );
$price = round( $price * $qty + $tax_amount, absint( get_option( 'woocommerce_price_num_decimals' ) ) ); $price = round( $price * $qty + $tax_amount, absint( get_option( 'woocommerce_price_num_decimals' ) ) );
} else { } else {
$tax_rates = $_tax->get_rates( $this->get_tax_class() ); $tax_rates = WC_Tax::get_rates( $this->get_tax_class() );
$base_tax_rates = $_tax->get_shop_base_rate( $this->tax_class ); $base_tax_rates = WC_Tax::get_shop_base_rate( $this->tax_class );
if ( ! empty( WC()->customer ) && WC()->customer->is_vat_exempt() ) { if ( ! empty( WC()->customer ) && WC()->customer->is_vat_exempt() ) {
$base_taxes = $_tax->calc_tax( $price * $qty, $base_tax_rates, true ); $base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true );
$base_tax_amount = array_sum( $base_taxes ); $base_tax_amount = array_sum( $base_taxes );
$price = round( $price * $qty - $base_tax_amount, absint( get_option( 'woocommerce_price_num_decimals' ) ) ); $price = round( $price * $qty - $base_tax_amount, absint( get_option( 'woocommerce_price_num_decimals' ) ) );
} elseif ( $tax_rates !== $base_tax_rates ) { } elseif ( $tax_rates !== $base_tax_rates ) {
$base_taxes = $_tax->calc_tax( $price * $qty, $base_tax_rates, true ); $base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true );
$modded_taxes = $_tax->calc_tax( ( $price * $qty ) - array_sum( $base_taxes ), $tax_rates, false ); $modded_taxes = WC_Tax::calc_tax( ( $price * $qty ) - array_sum( $base_taxes ), $tax_rates, false );
$price = round( ( $price * $qty ) - array_sum( $base_taxes ) + array_sum( $modded_taxes ), absint( get_option( 'woocommerce_price_num_decimals' ) ) ); $price = round( ( $price * $qty ) - array_sum( $base_taxes ) + array_sum( $modded_taxes ), absint( get_option( 'woocommerce_price_num_decimals' ) ) );
} else { } else {
@ -848,12 +846,9 @@ class WC_Product {
} }
if ( $this->is_taxable() && get_option('woocommerce_prices_include_tax') === 'yes' ) { if ( $this->is_taxable() && get_option('woocommerce_prices_include_tax') === 'yes' ) {
$tax_rates = WC_Tax::get_shop_base_rate( $this->tax_class );
$_tax = new WC_Tax(); $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, true );
$tax_rates = $_tax->get_shop_base_rate( $this->tax_class ); $price = WC_Tax::round( $price * $qty - array_sum( $taxes ) );
$taxes = $_tax->calc_tax( $price * $qty, $tax_rates, true );
$price = $_tax->round( $price * $qty - array_sum( $taxes ) );
} else { } else {
$price = $price * $qty; $price = $price * $qty;
} }

View File

@ -95,7 +95,6 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
// This saves shipping methods having to do complex tax calculations // This saves shipping methods having to do complex tax calculations
if ( ! is_array( $taxes ) && $taxes !== false && $total_cost > 0 && $this->is_taxable() ) { if ( ! is_array( $taxes ) && $taxes !== false && $total_cost > 0 && $this->is_taxable() ) {
$_tax = new WC_Tax();
$taxes = array(); $taxes = array();
switch ( $calc_tax ) { switch ( $calc_tax ) {
@ -114,8 +113,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
$_product = $cart[ $cost_key ]['data']; $_product = $cart[ $cost_key ]['data'];
$rates = $_tax->get_shipping_tax_rates( $_product->get_tax_class() ); $rates = WC_Tax::get_shipping_tax_rates( $_product->get_tax_class() );
$item_taxes = $_tax->calc_shipping_tax( $amount, $rates ); $item_taxes = WC_Tax::calc_shipping_tax( $amount, $rates );
// Sum the item taxes // Sum the item taxes
foreach ( array_keys( $taxes + $item_taxes ) as $key ) foreach ( array_keys( $taxes + $item_taxes ) as $key )
@ -126,8 +125,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
// Add any cost for the order - order costs are in the key 'order' // Add any cost for the order - order costs are in the key 'order'
if ( isset( $cost['order'] ) ) { if ( isset( $cost['order'] ) ) {
$rates = $_tax->get_shipping_tax_rates(); $rates = WC_Tax::get_shipping_tax_rates();
$item_taxes = $_tax->calc_shipping_tax( $cost['order'], $rates ); $item_taxes = WC_Tax::calc_shipping_tax( $cost['order'], $rates );
// Sum the item taxes // Sum the item taxes
foreach ( array_keys( $taxes + $item_taxes ) as $key ) foreach ( array_keys( $taxes + $item_taxes ) as $key )
@ -140,8 +139,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
default : default :
$rates = $_tax->get_shipping_tax_rates(); $rates = WC_Tax::get_shipping_tax_rates();
$taxes = $_tax->calc_shipping_tax( $total_cost, $rates ); $taxes = WC_Tax::calc_shipping_tax( $total_cost, $rates );
break; break;

View File

@ -1185,7 +1185,6 @@ class WC_AJAX {
self::json_headers(); self::json_headers();
$tax = new WC_Tax();
$taxes = $tax_rows = $item_taxes = $shipping_taxes = array(); $taxes = $tax_rows = $item_taxes = $shipping_taxes = array();
$order_id = absint( $_POST['order_id'] ); $order_id = absint( $_POST['order_id'] );
$order = new WC_Order( $order_id ); $order = new WC_Order( $order_id );
@ -1200,17 +1199,12 @@ class WC_AJAX {
// Calculate sales tax first // Calculate sales tax first
if ( sizeof( $items ) > 0 ) { if ( sizeof( $items ) > 0 ) {
foreach( $items as $item_id => $item ) { foreach( $items as $item_id => $item ) {
$item_id = absint( $item_id ); $item_id = absint( $item_id );
$line_subtotal = isset( $item['line_subtotal'] ) ? wc_format_decimal( $item['line_subtotal'] ) : 0; $line_subtotal = isset( $item['line_subtotal'] ) ? wc_format_decimal( $item['line_subtotal'] ) : 0;
$line_total = wc_format_decimal( $item['line_total'] ); $line_total = wc_format_decimal( $item['line_total'] );
$tax_class = sanitize_text_field( $item['tax_class'] ); $tax_class = sanitize_text_field( $item['tax_class'] );
$product_id = $order->get_item_meta( $item_id, '_product_id', true ); $product_id = $order->get_item_meta( $item_id, '_product_id', true );
if ( ! $item_id || '0' == $tax_class ) {
continue;
}
// Get product details // Get product details
if ( get_post_type( $product_id ) == 'product' ) { if ( get_post_type( $product_id ) == 'product' ) {
$_product = get_product( $product_id ); $_product = get_product( $product_id );
@ -1219,10 +1213,8 @@ class WC_AJAX {
$item_tax_status = 'taxable'; $item_tax_status = 'taxable';
} }
// Only calc if taxable if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
if ( 'taxable' == $item_tax_status ) { $tax_rates = WC_Tax::find_rates( array(
$tax_rates = $tax->find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -1230,18 +1222,10 @@ class WC_AJAX {
'tax_class' => $tax_class 'tax_class' => $tax_class
) ); ) );
$line_subtotal_taxes = $tax->calc_tax( $line_subtotal, $tax_rates, false ); $line_subtotal_taxes = WC_Tax::calc_tax( $line_subtotal, $tax_rates, false );
$line_taxes = $tax->calc_tax( $line_total, $tax_rates, false ); $line_taxes = WC_Tax::calc_tax( $line_total, $tax_rates, false );
$line_subtotal_tax = array_sum( $line_subtotal_taxes ); $line_subtotal_tax = max( 0, array_sum( $line_subtotal_taxes ) );
$line_tax = array_sum( $line_taxes ); $line_tax = max( 0, array_sum( $line_taxes ) );
if ( $line_subtotal_tax < 0 ) {
$line_subtotal_tax = 0;
}
if ( $line_tax < 0 ) {
$line_tax = 0;
}
$item_taxes[ $item_id ] = array( $item_taxes[ $item_id ] = array(
'line_subtotal_tax' => wc_format_localized_price( $line_subtotal_tax ), 'line_subtotal_tax' => wc_format_localized_price( $line_subtotal_tax ),
@ -1255,14 +1239,13 @@ class WC_AJAX {
$taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 ); $taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
} }
} }
} }
} }
// Now calculate shipping tax // Now calculate shipping tax
$matched_tax_rates = array(); $matched_tax_rates = array();
$tax_rates = $tax->find_rates( array( $tax_rates = WC_Tax::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -1278,62 +1261,20 @@ class WC_AJAX {
} }
} }
$shipping_taxes = $tax->calc_shipping_tax( $shipping, $matched_tax_rates ); $shipping_taxes = WC_Tax::calc_shipping_tax( $shipping, $matched_tax_rates );
$shipping_tax = $tax->round( array_sum( $shipping_taxes ) ); $shipping_tax = WC_Tax::round( array_sum( $shipping_taxes ) );
// Remove old tax rows // Remove old tax rows
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'tax' )", $order_id ) ); $order->remove_order_items( 'tax' );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'tax'", $order_id ) ); // Add tax rows
foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) {
// Get tax rates $order->add_tax( $tax_rate_id, isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 );
$rates = $wpdb->get_results( "SELECT tax_rate_id, tax_rate_country, tax_rate_state, tax_rate_name, tax_rate_priority FROM {$wpdb->prefix}woocommerce_tax_rates ORDER BY tax_rate_name" );
$tax_codes = array();
foreach( $rates as $rate ) {
$code = array();
$code[] = $rate->tax_rate_country;
$code[] = $rate->tax_rate_state;
$code[] = $rate->tax_rate_name ? sanitize_title( $rate->tax_rate_name ) : 'TAX';
$code[] = absint( $rate->tax_rate_priority );
$tax_codes[ $rate->tax_rate_id ] = strtoupper( implode( '-', array_filter( $code ) ) );
} }
// Now merge to keep tax rows
ob_start(); ob_start();
foreach ( array_keys( $taxes + $shipping_taxes ) as $key ) { foreach ( $order->get_taxes() as $item_id => $item ) {
$item = array();
$item['rate_id'] = $key;
$item['name'] = $tax_codes[ $key ];
$item['label'] = $tax->get_rate_label( $key );
$item['compound'] = $tax->is_compound( $key ) ? 1 : 0;
$item['tax_amount'] = wc_format_decimal( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
$item['shipping_tax_amount'] = wc_format_decimal( isset( $shipping_taxes[ $key ] ) ? $shipping_taxes[ $key ] : 0 );
if ( ! $item['label'] ) {
$item['label'] = WC()->countries->tax_or_vat();
}
// Add line item
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $item['name'],
'order_item_type' => 'tax'
) );
// Add line item meta
if ( $item_id ) {
wc_add_order_item_meta( $item_id, 'rate_id', $item['rate_id'] );
wc_add_order_item_meta( $item_id, 'label', $item['label'] );
wc_add_order_item_meta( $item_id, 'compound', $item['compound'] );
wc_add_order_item_meta( $item_id, 'tax_amount', $item['tax_amount'] );
wc_add_order_item_meta( $item_id, 'shipping_tax_amount', $item['shipping_tax_amount'] );
}
include( 'admin/meta-boxes/views/html-order-tax.php' ); include( 'admin/meta-boxes/views/html-order-tax.php' );
} }

View File

@ -1371,7 +1371,7 @@ class WC_Cart {
$needs_shipping_address = false; $needs_shipping_address = false;
if ( WC()->cart->needs_shipping() === true && ! WC()->cart->ship_to_billing_address_only() ) { if ( $this->needs_shipping() === true && ! $this->ship_to_billing_address_only() ) {
$needs_shipping_address = true; $needs_shipping_address = true;
} }
@ -1661,6 +1661,15 @@ class WC_Cart {
return $this->applied_coupons; return $this->applied_coupons;
} }
/**
* Get the discount amount for a used coupon
* @param string $code coupon code
* @return float discount amount
*/
public function get_coupon_discount_amount( $code ) {
return isset( $this->coupon_discount_amounts[ $code ] ) ? $this->coupon_discount_amounts[ $code ] : 0;
}
/** /**
* Remove coupons from the cart of a defined type. Type 1 is before tax, type 2 is after tax. * Remove coupons from the cart of a defined type. Type 1 is before tax, type 2 is after tax.
* *
@ -2075,6 +2084,24 @@ class WC_Cart {
return apply_filters( 'woocommerce_get_cart_tax', $cart_total_tax ? wc_price( $cart_total_tax ) : '' ); return apply_filters( 'woocommerce_get_cart_tax', $cart_total_tax ? wc_price( $cart_total_tax ) : '' );
} }
/**
* Get a tax amount
* @param string $tax_rate_id
* @return float amount
*/
public function get_tax_amount( $tax_rate_id ) {
return isset( $this->taxes[ $tax_rate_id ] ) ? $this->taxes[ $tax_rate_id ] : 0;
}
/**
* Get a tax amount
* @param string $tax_rate_id
* @return float amount
*/
public function get_shipping_tax_amount( $tax_rate_id ) {
return isset( $this->shipping_taxes[ $tax_rate_id ] ) ? $this->shipping_taxes[ $tax_rate_id ] : 0;
}
/** /**
* Get tax row amounts with or without compound taxes includes. * Get tax row amounts with or without compound taxes includes.
* *

View File

@ -123,7 +123,6 @@ class WC_Checkout {
do_action( 'woocommerce_checkout_init', $this ); do_action( 'woocommerce_checkout_init', $this );
} }
/** /**
* Checkout process * Checkout process
*/ */
@ -132,7 +131,6 @@ class WC_Checkout {
do_action('woocommerce_check_cart_items'); do_action('woocommerce_check_cart_items');
} }
/** /**
* Output the billing information form * Output the billing information form
* *
@ -143,7 +141,6 @@ class WC_Checkout {
wc_get_template( 'checkout/form-billing.php', array( 'checkout' => $this ) ); wc_get_template( 'checkout/form-billing.php', array( 'checkout' => $this ) );
} }
/** /**
* Output the shipping information form * Output the shipping information form
* *
@ -154,242 +151,184 @@ class WC_Checkout {
wc_get_template( 'checkout/form-shipping.php', array( 'checkout' => $this ) ); wc_get_template( 'checkout/form-shipping.php', array( 'checkout' => $this ) );
} }
/** /**
* create_order function. * create_order function.
* @access public * @access public
* @throws Exception * @throws Exception
* @return int * @return int|WP_ERROR
*/ */
public function create_order() { public function create_order() {
global $wpdb; global $wpdb;
// Give plugins the opportunity to create an order themselves // Give plugins the opportunity to create an order themselves
$order_id = apply_filters( 'woocommerce_create_order', null, $this ); if ( $order_id = apply_filters( 'woocommerce_create_order', null, $this ) ) {
if ( is_numeric( $order_id ) )
return $order_id; return $order_id;
}
// Create Order (send cart variable so we can record items and reduce inventory). Only create if this is a new order, not if the payment was rejected. try {
$order_data = apply_filters( 'woocommerce_new_order_data', array( // Start transaction if available
'post_type' => 'shop_order', $wpdb->query( 'START TRANSACTION' );
'post_title' => sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) ),
'post_status' => 'wc-' . apply_filters( 'woocommerce_default_order_status', 'pending' ),
'ping_status' => 'closed',
'post_excerpt' => isset( $this->posted['order_comments'] ) ? $this->posted['order_comments'] : '',
'post_author' => 1,
'post_password' => uniqid( 'order_' ) // Protects the post just in case
) );
// Insert or update the post data $order_data = array(
$create_new_order = true; 'status' => apply_filters( 'woocommerce_default_order_status', 'pending' ),
'customer_id' => $this->customer_id,
if ( WC()->session->order_awaiting_payment > 0 ) { 'customer_note' => isset( $this->posted['order_comments'] ) ? $this->posted['order_comments'] : ''
);
// Insert or update the post data
$order_id = absint( WC()->session->order_awaiting_payment ); $order_id = absint( WC()->session->order_awaiting_payment );
// Resume the unpaid order if its pending // Resume the unpaid order if its pending
if ( ( $existing_order = get_post( $order_id ) ) && $existing_order->has_status( array( 'pending', 'failed' ) ) ) { if ( $order_id > 0 && ( $order = new WC_Order( $order_id ) ) && $order->has_status( array( 'pending', 'failed' ) ) ) {
// Update the existing order as we are resuming it $order_data['order_id'] = $order_id;
$create_new_order = false; $order = wc_update_order( $order_data );
$order_data['ID'] = $order_id;
wp_update_post( $order_data );
// Clear the old line items - we'll add these again in case they changed if ( is_wp_error( $order ) ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d )", $order_id ) ); throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $order_id ) );
// Trigger an action for the resumed order
do_action( 'woocommerce_resume_order', $order_id );
}
}
if ( $create_new_order ) {
$order_id = wp_insert_post( $order_data, true );
if ( is_wp_error( $order_id ) )
throw new Exception( 'Error: Unable to create order. Please try again.' );
else
do_action( 'woocommerce_new_order', $order_id );
}
// Store user data
if ( $this->checkout_fields['billing'] )
foreach ( $this->checkout_fields['billing'] as $key => $field ) {
update_post_meta( $order_id, '_' . $key, $this->posted[ $key ] );
if ( $this->customer_id && apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) )
update_user_meta( $this->customer_id, $key, $this->posted[ $key ] );
}
if ( $this->checkout_fields['shipping'] && WC()->cart->needs_shipping() ) {
foreach ( $this->checkout_fields['shipping'] as $key => $field ) {
$postvalue = false;
if ( $this->posted['ship_to_different_address'] == false ) {
if ( isset( $this->posted[ str_replace( 'shipping_', 'billing_', $key ) ] ) ) {
$postvalue = $this->posted[ str_replace( 'shipping_', 'billing_', $key ) ];
update_post_meta( $order_id, '_' . $key, $postvalue );
}
} else { } else {
$postvalue = $this->posted[ $key ]; $order->remove_order_items();
update_post_meta( $order_id, '_' . $key, $postvalue ); do_action( 'woocommerce_resume_order', $order_id );
} }
// User } else {
if ( $postvalue && $this->customer_id && apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) )
update_user_meta( $this->customer_id, $key, $postvalue ); $order = wc_create_order( $order_data );
if ( is_wp_error( $order ) ) {
throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
} else {
$order_id = $order->id;
do_action( 'woocommerce_new_order', $order_id );
}
} }
}
// Save any other user meta // Store the line items to the new/resumed order
if ( $this->customer_id ) foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
do_action( 'woocommerce_checkout_update_user_meta', $this->customer_id, $this->posted ); $item_id = $order->add_product(
$values['data'],
$values['quantity'],
array(
'variation' => $values['variation'],
'totals' => array(
'subtotal' => $values['line_subtotal'],
'subtotal_tax' => $values['line_subtotal_tax'],
'total' => $values['line_total'],
'tax' => $values['line_tax']
)
)
);
// Store the line items to the new/resumed order if ( ! $item_id ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) { throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
}
$_product = $values['data']; // Allow plugins to add order item meta
do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
}
// Add line item // Store fees
$item_id = wc_add_order_item( $order_id, array( foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
'order_item_name' => $_product->get_title(), $item_id = $order->add_fee( $fee );
'order_item_type' => 'line_item'
) );
// Add line item meta if ( ! $item_id ) {
if ( $item_id ) { throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
wc_add_order_item_meta( $item_id, '_qty', apply_filters( 'woocommerce_stock_amount', $values['quantity'] ) ); }
wc_add_order_item_meta( $item_id, '_tax_class', $_product->get_tax_class() );
wc_add_order_item_meta( $item_id, '_product_id', $values['product_id'] ); // Allow plugins to add order item meta to fees
wc_add_order_item_meta( $item_id, '_variation_id', $values['variation_id'] ); do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key );
wc_add_order_item_meta( $item_id, '_line_subtotal', wc_format_decimal( $values['line_subtotal'] ) ); }
wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( $values['line_total'] ) );
wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $values['line_tax'] ) );
wc_add_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $values['line_subtotal_tax'] ) );
// Store variation data in meta so admin can view it // Store shipping for all packages
if ( $values['variation'] && is_array( $values['variation'] ) ) { foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
foreach ( $values['variation'] as $key => $value ) { if ( isset( $package['rates'][ $this->shipping_methods[ $package_key ] ] ) ) {
$key = str_replace( 'attribute_', '', $key ); $item_id = $order->add_shipping( $package['rates'][ $this->shipping_methods[ $package_key ] ] );
wc_add_order_item_meta( $item_id, $key, $value );
if ( ! $item_id ) {
throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
}
// Allows plugins to add order item meta to shipping
do_action( 'woocommerce_add_shipping_order_item', $order_id, $item_id, $package_key );
}
}
// Store tax rows
foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $tax_rate_id ) {
if ( ! $order->add_tax( $tax_rate_id, WC()->cart->get_tax_amount( $tax_rate_id ), WC()->cart->get_shipping_tax_amount( $tax_rate_id ) ) ) {
throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
}
}
// Store coupons
foreach ( WC()->cart->get_coupons() as $code => $coupon ) {
if ( ! $order->add_coupon( $code, WC()->cart->get_coupon_discount_amount( $code ) ) ) {
throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
}
}
// Billing address
$billing_address = array(
'first_name' => $this->get_posted_address_data( 'first_name' ),
'last_name' => $this->get_posted_address_data( 'last_name' ),
'company' => $this->get_posted_address_data( 'company' ),
'email' => $this->get_posted_address_data( 'email' ),
'phone' => $this->get_posted_address_data( 'phone' ),
'address_1' => $this->get_posted_address_data( 'address_1' ),
'address_2' => $this->get_posted_address_data( 'address_2' ),
'city' => $this->get_posted_address_data( 'city' ),
'state' => $this->get_posted_address_data( 'state' ),
'postcode' => $this->get_posted_address_data( 'postcode' ),
'country' => $this->get_posted_address_data( 'country' )
);
$shipping_address = array(
'first_name' => $this->get_posted_address_data( 'first_name', 'shipping' ),
'last_name' => $this->get_posted_address_data( 'last_name', 'shipping' ),
'company' => $this->get_posted_address_data( 'company', 'shipping' ),
'address_1' => $this->get_posted_address_data( 'address_1', 'shipping' ),
'address_2' => $this->get_posted_address_data( 'address_2', 'shipping' ),
'city' => $this->get_posted_address_data( 'city', 'shipping' ),
'state' => $this->get_posted_address_data( 'state', 'shipping' ),
'postcode' => $this->get_posted_address_data( 'postcode', 'shipping' ),
'country' => $this->get_posted_address_data( 'country', 'shipping' ),
);
$order->set_address( $billing_address, 'billing' );
$order->set_address( $shipping_address, 'shipping' );
$order->set_payment_method( $this->payment_method );
$order->set_total( WC()->cart->shipping_total, 'shipping' );
$order->set_total( WC()->cart->get_order_discount_total(), 'order_discount' );
$order->set_total( WC()->cart->get_cart_discount_total(), 'cart_discount' );
$order->set_total( WC()->cart->tax_total, 'tax' );
$order->set_total( WC()->cart->shipping_tax_total, 'shipping_tax' );
$order->set_total( WC()->cart->total );
// Update user meta
if ( $this->customer_id ) {
if ( apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) ) {
foreach( $billing_address as $key => $value ) {
update_user_meta( $this->customer_id, 'billing_' . $key, $value );
}
foreach( $shipping_address as $key => $value ) {
update_user_meta( $this->customer_id, 'shipping_' . $key, $value );
} }
} }
do_action( 'woocommerce_checkout_update_user_meta', $this->customer_id, $this->posted );
// Add line item meta for backorder status
if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $values['quantity'] ) ) {
wc_add_order_item_meta( $item_id, apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ), $cart_item_key, $order_id ), $values['quantity'] - max( 0, $_product->get_total_stock() ) );
}
// Allow plugins to add order item meta
do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
}
}
// Store fees
foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $fee->name,
'order_item_type' => 'fee'
) );
if ( $fee->taxable )
wc_add_order_item_meta( $item_id, '_tax_class', $fee->tax_class );
else
wc_add_order_item_meta( $item_id, '_tax_class', '0' );
wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( $fee->amount ) );
wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $fee->tax ) );
// Allow plugins to add order item meta to fees
do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key );
}
// Store shipping for all packages
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
if ( isset( $package['rates'][ $this->shipping_methods[ $i ] ] ) ) {
$method = $package['rates'][ $this->shipping_methods[ $i ] ];
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $method->label,
'order_item_type' => 'shipping'
) );
if ( $item_id ) {
wc_add_order_item_meta( $item_id, 'method_id', $method->id );
wc_add_order_item_meta( $item_id, 'cost', wc_format_decimal( $method->cost ) );
do_action( 'woocommerce_add_shipping_order_item', $order_id, $item_id, $i );
}
} }
// Let plugins add meta
do_action( 'woocommerce_checkout_update_order_meta', $order_id, $this->posted );
// If we got here, the order was created without problems!
$wpdb->query( 'COMMIT' );
} catch ( Exception $e ) {
// There was an error adding order data!
$wpdb->query( 'ROLLBACK' );
return new WP_Error( 'checkout-error', $e->getMessage() );
} }
// Store tax rows
foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $key ) {
$code = WC()->cart->tax->get_rate_code( $key );
if ( $code ) {
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $code,
'order_item_type' => 'tax'
) );
// Add line item meta
if ( $item_id ) {
wc_add_order_item_meta( $item_id, 'rate_id', $key );
wc_add_order_item_meta( $item_id, 'label', WC()->cart->tax->get_rate_label( $key ) );
wc_add_order_item_meta( $item_id, 'compound', absint( WC()->cart->tax->is_compound( $key ) ? 1 : 0 ) );
wc_add_order_item_meta( $item_id, 'tax_amount', wc_format_decimal( isset( WC()->cart->taxes[ $key ] ) ? WC()->cart->taxes[ $key ] : 0 ) );
wc_add_order_item_meta( $item_id, 'shipping_tax_amount', wc_format_decimal( isset( WC()->cart->shipping_taxes[ $key ] ) ? WC()->cart->shipping_taxes[ $key ] : 0 ) );
}
}
}
// Store coupons
if ( $applied_coupons = WC()->cart->get_coupons() ) {
foreach ( $applied_coupons as $code => $coupon ) {
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $code,
'order_item_type' => 'coupon'
) );
// Add line item meta
if ( $item_id ) {
wc_add_order_item_meta( $item_id, 'discount_amount', isset( WC()->cart->coupon_discount_amounts[ $code ] ) ? WC()->cart->coupon_discount_amounts[ $code ] : 0 );
}
}
}
if ( $this->payment_method ) {
update_post_meta( $order_id, '_payment_method', $this->payment_method->id );
update_post_meta( $order_id, '_payment_method_title', $this->payment_method->get_title() );
}
if ( empty( $this->posted['billing_email'] ) && is_user_logged_in() ) {
$current_user = wp_get_current_user();
update_post_meta( $order_id, '_billing_email', $current_user->user_email );
}
update_post_meta( $order_id, '_order_shipping', wc_format_decimal( WC()->cart->shipping_total ) );
update_post_meta( $order_id, '_order_discount', wc_format_decimal( WC()->cart->get_order_discount_total() ) );
update_post_meta( $order_id, '_cart_discount', wc_format_decimal( WC()->cart->get_cart_discount_total() ) );
update_post_meta( $order_id, '_order_tax', wc_format_decimal( WC()->cart->tax_total ) );
update_post_meta( $order_id, '_order_shipping_tax', wc_format_decimal( WC()->cart->shipping_tax_total ) );
update_post_meta( $order_id, '_order_total', wc_format_decimal( WC()->cart->total, get_option( 'woocommerce_price_num_decimals' ) ) );
update_post_meta( $order_id, '_order_key', 'wc_' . apply_filters('woocommerce_generate_order_key', uniqid('order_') ) );
update_post_meta( $order_id, '_customer_user', absint( $this->customer_id ) );
update_post_meta( $order_id, '_order_currency', get_woocommerce_currency() );
update_post_meta( $order_id, '_prices_include_tax', get_option( 'woocommerce_prices_include_tax' ) );
update_post_meta( $order_id, '_customer_ip_address', isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'] );
update_post_meta( $order_id, '_customer_user_agent', isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '' );
// Let plugins add meta
do_action( 'woocommerce_checkout_update_order_meta', $order_id, $this->posted );
return $order_id; return $order_id;
} }
@ -658,6 +597,10 @@ class WC_Checkout {
$order_id = $this->create_order(); $order_id = $this->create_order();
if ( is_wp_error( $order_id ) ) {
throw new Exception( $order_id->get_error_message() );
}
do_action( 'woocommerce_checkout_order_processed', $order_id, $this->posted ); do_action( 'woocommerce_checkout_order_processed', $order_id, $this->posted );
// Process payment // Process payment
@ -717,10 +660,9 @@ class WC_Checkout {
} }
} catch ( Exception $e ) { } catch ( Exception $e ) {
if ( ! empty( $e ) ) {
if ( ! empty( $e ) )
wc_add_notice( $e->getMessage(), 'error' ); wc_add_notice( $e->getMessage(), 'error' );
}
} }
} // endif } // endif
@ -746,6 +688,26 @@ class WC_Checkout {
} }
} }
/**
* Get a posted address field after sanitization and validation.
* @param string $key
* @param string $type billing for shipping
* @return string
*/
public function get_posted_address_data( $key, $type = 'billing' ) {
if ( 'billing' === $type || false === $this->posted['ship_to_different_address'] ) {
$return = isset( $this->posted[ 'billing_' . $key ] ) ? $this->posted[ 'billing_' . $key ] : '';
} else {
$return = isset( $this->posted[ 'shipping_' . $key ] ) ? $this->posted[ 'shipping_' . $key ] : '';
}
// Use logged in user's billing email if neccessary
if ( 'email' === $key && empty( $return ) && is_user_logged_in() ) {
$current_user = wp_get_current_user();
$return = $current_user->user_email;
}
return $return;
}
/** /**
* Gets the value either from the posted data, or from the users meta data * Gets the value either from the posted data, or from the users meta data

View File

@ -5,7 +5,7 @@
* The WooCommerce order class handles order data. * The WooCommerce order class handles order data.
* *
* @class WC_Order * @class WC_Order
* @version 2.1.0 * @version 2.2.0
* @package WooCommerce/Classes * @package WooCommerce/Classes
* @category Class * @category Class
* @author WooThemes * @author WooThemes
@ -34,6 +34,360 @@ class WC_Order {
} }
} }
/**
* Remove all line items (products, coupons, shipping, taxes) from the order.
*/
public function remove_order_items( $type = null ) {
global $wpdb;
if ( $type ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s )", $this->id, $type ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->id, $type ) );
} else {
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d )", $this->id ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->id ) );
}
}
/**
* Set the payment method for the order
* @param WC_Payment_Gateway
*/
public function set_payment_method( $payment_method ) {
if ( is_object( $payment_method ) ) {
update_post_meta( $this->id, '_payment_method', $payment_method->id );
update_post_meta( $this->id, '_payment_method_title', $payment_method->get_title() );
}
}
/**
* Set the customer address
* @param array $address Address data
* @param string $type billing or shipping
*/
public function set_address( $address, $type = 'billing' ) {
foreach( $address as $key => $value ) {
update_post_meta( $this->id, "_{$type}_" . $key, $value );
}
}
/**
* Add a product line item to the order
* @param WC_Product $item
* @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', apply_filters( 'woocommerce_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['total']['subtotal_tax'] : $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 ) );
// Add variation meta
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;
}
/**
* Add coupon code to the order
* @param string $code
* @param float $discount_amount
* @return int|bool Item ID or false
*/
public function add_coupon( $code, $discount_amount = 0 ) {
$item_id = wc_add_order_item( $this->id, array(
'order_item_name' => $code,
'order_item_type' => 'coupon'
) );
if ( ! $item_id ) {
return false;
}
wc_add_order_item_meta( $item_id, 'discount_amount', $discount_amount );
do_action( 'woocommerce_order_add_coupon', $this->id, $item_id, $code, $discount_amount );
return $item_id;
}
/**
* Add a tax row to the order
* @param int tax_rate_id
* @return int|bool Item ID or false
*/
public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) {
$code = WC_Tax::get_rate_code( $tax_rate_id );
if ( ! $code ) {
return false;
}
$item_id = wc_add_order_item( $this->id, array(
'order_item_name' => $code,
'order_item_type' => 'tax'
) );
if ( ! $item_id ) {
return false;
}
wc_add_order_item_meta( $item_id, 'rate_id', $tax_rate_id );
wc_add_order_item_meta( $item_id, 'label', WC_Tax::get_rate_label( $tax_rate_id ) );
wc_add_order_item_meta( $item_id, 'compound', WC_Tax::is_compound( $tax_rate_id ) ? 1 : 0 );
wc_add_order_item_meta( $item_id, 'tax_amount', wc_format_decimal( $tax_amount ) );
wc_add_order_item_meta( $item_id, 'shipping_tax_amount', wc_format_decimal( $shipping_tax_amount ) );
do_action( 'woocommerce_order_add_tax', $this->id, $item_id, $tax_rate_id, $tax_amount, $shipping_tax_amount );
return $item_id;
}
/**
* Add a shipping row to the order
* @param WC_Shipping_Rate shipping_rate
* @return int|bool Item ID or false
*/
public function add_shipping( $shipping_rate ) {
$item_id = wc_add_order_item( $this->id, array(
'order_item_name' => $shipping_rate->label,
'order_item_type' => 'shipping'
) );
if ( ! $item_id ) {
return false;
}
wc_add_order_item_meta( $item_id, 'method_id', $shipping_rate->id );
wc_add_order_item_meta( $item_id, 'cost', wc_format_decimal( $shipping_rate->cost ) );
do_action( 'woocommerce_order_add_shipping', $this->id, $item_id, $shipping_rate );
// Update total
$this->set_total( $this->order_shipping + wc_format_decimal( $shipping_rate->cost ), 'shipping' );
return $item_id;
}
/**
* Add a fee to the order
* @param object $fee
* @return int|bool Item ID or false
*/
public function add_fee( $fee ) {
$item_id = wc_add_order_item( $this->id, array(
'order_item_name' => $fee->name,
'order_item_type' => 'fee'
) );
if ( ! $item_id ) {
return false;
}
if ( $fee->taxable ) {
wc_add_order_item_meta( $item_id, '_tax_class', $fee->tax_class );
} else {
wc_add_order_item_meta( $item_id, '_tax_class', '0' );
}
wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( $fee->amount ) );
wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $fee->tax ) );
do_action( 'woocommerce_order_add_fee', $this->id, $item_id, $fee );
return $item_id;
}
/**
* Set an order total
* @param float $amount
* @param string $total_type
*/
public function set_total( $amount, $total_type = 'total' ) {
if ( ! in_array( $total_type, array( 'shipping', 'order_discount', 'tax', 'shipping_tax', 'total', 'cart_discount' ) ) ) {
return false;
}
switch ( $total_type ) {
case 'total' :
$key = '_order_total';
$amount = wc_format_decimal( $amount, get_option( 'woocommerce_price_num_decimals' ) );
break;
case 'order_discount' :
case 'cart_discount' :
$key = '_' . $total_type;
$amount = wc_format_decimal( $amount );
break;
default :
$key = '_order_' . $total_type;
$amount = wc_format_decimal( $amount );
break;
}
update_post_meta( $this->id, $key, $amount );
}
/**
* Calculate taxes for all line items and shipping, and store the totals and tax rows.
*
* Will use the base country unless customer addresses are set.
*
* @return bool success or fail
*/
public function calculate_taxes() {
$shipping_tax_total = 0;
$tax_total = 0;
$taxes = array();
$tax_based_on = get_option( 'woocommerce_tax_based_on' );
if ( 'base' === $tax_based_on ) {
$default = get_option( 'woocommerce_default_country' );
$postcode = '';
$city = '';
if ( strstr( $default, ':' ) ) {
list( $country, $state ) = explode( ':', $default );
} else {
$country = $default;
$state = '';
}
} elseif ( 'billing' === $tax_based_on ) {
$country = $this->billing_country;
$state = $this->billing_state;
$postcode = $this->billing_postcode;
$city = $this->billing_city;
} else {
$country = $this->shipping_country;
$state = $this->shipping_state;
$postcode = $this->shipping_postcode;
$city = $this->shipping_city;
}
// Get items
foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
$product = $this->get_product_from_item( $item );
$line_total = isset( $item['line_total'] ) ? $item['line_total'] : 0;
$line_subtotal = isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
$tax_class = $item['tax_class'];
$item_tax_status = $product ? $product->get_tax_status() : 'taxable';
if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
$tax_rates = WC_Tax::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class
) );
$line_subtotal_taxes = WC_Tax::calc_tax( $line_subtotal, $tax_rates, false );
$line_taxes = WC_Tax::calc_tax( $line_total, $tax_rates, false );
$line_subtotal_tax = max( 0, array_sum( $line_subtotal_taxes ) );
$line_tax = max( 0, array_sum( $line_taxes ) );
$tax_total += $line_tax;
wc_update_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $line_subtotal_tax ) );
wc_update_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $line_tax ) );
// Sum the item taxes
foreach ( array_keys( $taxes + $line_taxes ) as $key ) {
$taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
}
}
}
// Now calculate shipping tax
$matched_tax_rates = array();
$tax_rates = WC_Tax::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => ''
) );
if ( $tax_rates ) {
foreach ( $tax_rates as $key => $rate ) {
if ( isset( $rate['shipping'] ) && 'yes' === $rate['shipping'] ) {
$matched_tax_rates[ $key ] = $rate;
}
}
}
$shipping_taxes = WC_Tax::calc_shipping_tax( $this->order_shipping, $matched_tax_rates );
$shipping_tax_total = WC_Tax::round( array_sum( $shipping_taxes ) );
// Save tax totals
$this->set_total( $shipping_tax_total, 'shipping_tax' );
$this->set_total( $tax_total, 'tax' );
// Tax rows
$this->remove_order_items( 'tax' );
// Now merge to keep tax rows
foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) {
$this->add_tax( $tax_rate_id, isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 );
}
return true;
}
/**
* Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
*
* @return $total calculated grand total
*/
public function calculate_totals() {
$cart_subtotal = 0;
$cart_total = 0;
$fee_total = 0;
$this->calculate_taxes();
foreach ( $this->get_items() as $item ) {
$cart_subtotal += wc_format_decimal( isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0 );
$cart_total += wc_format_decimal( isset( $item['line_total'] ) ? $item['line_total'] : 0 );
}
foreach ( $this->get_fees() as $item ) {
$fee_total += $item['line_total'];
}
$grand_total = round( $cart_total + $fee_total + $this->get_total_shipping() - $this->get_order_discount() + $this->get_cart_tax() + $this->get_shipping_tax(), absint( get_option( 'woocommerce_price_num_decimals' ) ) );
$this->set_total( $cart_subtotal - $cart_total, 'cart_discount' );
$this->set_total( $grand_total, 'total' );
return $grand_total;
}
/** /**
* Gets an order from the database. * Gets an order from the database.
@ -53,7 +407,6 @@ class WC_Order {
return false; return false;
} }
/** /**
* Populates an order from the loaded post data. * Populates an order from the loaded post data.
* *
@ -160,7 +513,6 @@ class WC_Order {
if ( $key == $this->order_key ) { if ( $key == $this->order_key ) {
return true; return true;
} }
return false; return false;
} }
@ -260,7 +612,6 @@ class WC_Order {
return $this->formatted_shipping_address; return $this->formatted_shipping_address;
} }
/** /**
* Get the shipping address in an array. * Get the shipping address in an array.
* *
@ -930,7 +1281,7 @@ class WC_Order {
* @return WC_Product * @return WC_Product
*/ */
public function get_product_from_item( $item ) { public function get_product_from_item( $item ) {
$_product = get_product( $item['variation_id'] ? $item['variation_id'] : $item['product_id'] ); $_product = get_product( ! empty( $item['variation_id'] ) ? $item['variation_id'] : $item['product_id'] );
return apply_filters( 'woocommerce_get_product_from_item', $_product, $item, $this ); return apply_filters( 'woocommerce_get_product_from_item', $_product, $item, $this );
} }
@ -1352,6 +1703,9 @@ class WC_Order {
// Record the completed date of the order // Record the completed date of the order
update_post_meta( $this->id, '_completed_date', current_time('mysql') ); update_post_meta( $this->id, '_completed_date', current_time('mysql') );
// Update reports
wc_delete_shop_order_transients( $this->id );
break; break;
case 'processing' : case 'processing' :
case 'on-hold' : case 'on-hold' :
@ -1360,15 +1714,19 @@ class WC_Order {
// Increase coupon usage counts // Increase coupon usage counts
$this->increase_coupon_usage_counts(); $this->increase_coupon_usage_counts();
// Update reports
wc_delete_shop_order_transients( $this->id );
break; break;
case 'cancelled' : case 'cancelled' :
// If the order is cancelled, restore used coupons // If the order is cancelled, restore used coupons
$this->decrease_coupon_usage_counts(); $this->decrease_coupon_usage_counts();
// Update reports
wc_delete_shop_order_transients( $this->id );
break; break;
} }
} }
wc_delete_shop_order_transients( $this->id );
} }
@ -1610,10 +1968,8 @@ class WC_Order {
$this->add_order_note( __( 'Order item stock reduced successfully.', 'woocommerce' ) ); $this->add_order_note( __( 'Order item stock reduced successfully.', 'woocommerce' ) );
} }
} }
/** /**
* send_stock_notifications function. * send_stock_notifications function.
* *

View File

@ -3,28 +3,24 @@
* Performs tax calculations and loads tax rates. * Performs tax calculations and loads tax rates.
* *
* @class WC_Tax * @class WC_Tax
* @version 2.0.0 * @version 2.2.0
* @package WooCommerce/Classes * @package WooCommerce/Classes
* @category Class * @category Class
* @author WooThemes * @author WooThemes
*/ */
class WC_Tax { class WC_Tax {
/** @var array */ public static $precision;
public $matched_rates; public static $round_at_subtotal;
var $log = array();
/** /**
* __construct function. * Load options
* *
* @access public * @access public
*/ */
public function __construct() { public static function init() {
$this->precision = WC_ROUNDING_PRECISION; self::$precision = WC_ROUNDING_PRECISION;
$this->dp = (int) get_option( 'woocommerce_price_num_decimals' ); self::$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
$this->round_at_subtotal = get_option('woocommerce_tax_round_at_subtotal') == 'yes';
} }
/** /**
@ -35,23 +31,23 @@ class WC_Tax {
* @param boolean $suppress_rounding Whether to suppress any rounding from taking place * @param boolean $suppress_rounding Whether to suppress any rounding from taking place
* @return array Array of rates + prices after tax * @return array Array of rates + prices after tax
*/ */
public function calc_tax( $price, $rates, $price_includes_tax = false, $suppress_rounding = false ) { public static function calc_tax( $price, $rates, $price_includes_tax = false, $suppress_rounding = false ) {
// Work in pence to X precision // Work in pence to X precision
$price = $this->precision( $price ); $price = self::precision( $price );
if ( $price_includes_tax ) if ( $price_includes_tax )
$taxes = $this->calc_inclusive_tax( $price, $rates ); $taxes = self::calc_inclusive_tax( $price, $rates );
else else
$taxes = $this->calc_exclusive_tax( $price, $rates ); $taxes = self::calc_exclusive_tax( $price, $rates );
// Round to precision // Round to precision
if ( ! $this->round_at_subtotal && ! $suppress_rounding ) { if ( ! self::$round_at_subtotal && ! $suppress_rounding ) {
$taxes = array_map( 'round', $taxes ); // Round to precision $taxes = array_map( 'round', $taxes ); // Round to precision
} }
// Remove precision // Remove precision
$price = $this->remove_precision( $price ); $price = self::remove_precision( $price );
$taxes = array_map( array( $this, 'remove_precision' ), $taxes ); $taxes = array_map( array( __CLASS__, 'remove_precision' ), $taxes );
return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $suppress_rounding ); return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $suppress_rounding );
} }
@ -63,8 +59,8 @@ class WC_Tax {
* @param array Taxation Rate * @param array Taxation Rate
* @return array * @return array
*/ */
public function calc_shipping_tax( $price, $rates ) { public static function calc_shipping_tax( $price, $rates ) {
return $this->calc_exclusive_tax( $price, $rates ); return self::calc_exclusive_tax( $price, $rates );
} }
/** /**
@ -72,8 +68,8 @@ class WC_Tax {
* @param float $price * @param float $price
* @return float * @return float
*/ */
private function precision( $price ) { private static function precision( $price ) {
return $price * ( pow( 10, $this->precision ) ); return $price * ( pow( 10, self::$precision ) );
} }
/** /**
@ -81,8 +77,8 @@ class WC_Tax {
* @param float $price * @param float $price
* @return float * @return float
*/ */
private function remove_precision( $price ) { private static function remove_precision( $price ) {
return $price / ( pow( 10, $this->precision ) ); return $price / ( pow( 10, self::$precision ) );
} }
/** /**
@ -95,8 +91,8 @@ class WC_Tax {
* } * }
* add_filter( 'woocommerce_tax_round', 'euro_5cent_rounding' ); * add_filter( 'woocommerce_tax_round', 'euro_5cent_rounding' );
*/ */
public function round( $in ) { public static function round( $in ) {
return apply_filters( 'woocommerce_tax_round', round( $in, $this->precision ), $in ); return apply_filters( 'woocommerce_tax_round', round( $in, self::$precision ), $in );
} }
/** /**
@ -106,7 +102,7 @@ class WC_Tax {
* @param array $rates * @param array $rates
* @return array * @return array
*/ */
private function calc_inclusive_tax( $price, $rates ) { private static function calc_inclusive_tax( $price, $rates ) {
$taxes = array(); $taxes = array();
$regular_tax_rates = $compound_tax_rates = 0; $regular_tax_rates = $compound_tax_rates = 0;
@ -150,7 +146,7 @@ class WC_Tax {
* @param array $rates * @param array $rates
* @return array * @return array
*/ */
private function calc_exclusive_tax( $price, $rates ) { private static function calc_exclusive_tax( $price, $rates ) {
$taxes = array(); $taxes = array();
// Multiple taxes // Multiple taxes
@ -205,7 +201,7 @@ class WC_Tax {
* @param array $args * @param array $args
* @return array * @return array
*/ */
public function find_rates( $args = array() ) { public static function find_rates( $args = array() ) {
global $wpdb; global $wpdb;
$defaults = array( $defaults = array(
@ -318,7 +314,7 @@ class WC_Tax {
* @param string $tax_class * @param string $tax_class
* @return array * @return array
*/ */
public function get_rates( $tax_class = '' ) { public static function get_rates( $tax_class = '' ) {
$tax_class = sanitize_title( $tax_class ); $tax_class = sanitize_title( $tax_class );
@ -327,7 +323,7 @@ class WC_Tax {
list( $country, $state, $postcode, $city ) = WC()->customer->get_taxable_address(); list( $country, $state, $postcode, $city ) = WC()->customer->get_taxable_address();
$matched_tax_rates = $this->find_rates( array( $matched_tax_rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -341,7 +337,7 @@ class WC_Tax {
// Prices excluding tax however should just not add any taxes, as they will be added during checkout. // Prices excluding tax however should just not add any taxes, as they will be added during checkout.
// The woocommerce_default_customer_address option (when set to base) is also used here. // The woocommerce_default_customer_address option (when set to base) is also used here.
$matched_tax_rates = get_option( 'woocommerce_prices_include_tax' ) == 'yes' || get_option( 'woocommerce_default_customer_address' ) == 'base' $matched_tax_rates = get_option( 'woocommerce_prices_include_tax' ) == 'yes' || get_option( 'woocommerce_default_customer_address' ) == 'base'
? $this->get_shop_base_rate( $tax_class ) ? self::get_shop_base_rate( $tax_class )
: array(); : array();
} }
@ -355,8 +351,8 @@ class WC_Tax {
* @param string Tax Class * @param string Tax Class
* @return array * @return array
*/ */
public function get_shop_base_rate( $tax_class = '' ) { public static function get_shop_base_rate( $tax_class = '' ) {
return $this->find_rates( array( return self::find_rates( array(
'country' => WC()->countries->get_base_country(), 'country' => WC()->countries->get_base_country(),
'state' => WC()->countries->get_base_state(), 'state' => WC()->countries->get_base_state(),
'postcode' => WC()->countries->get_base_postcode(), 'postcode' => WC()->countries->get_base_postcode(),
@ -371,7 +367,7 @@ class WC_Tax {
* @param string Tax Class * @param string Tax Class
* @return mixed * @return mixed
*/ */
public function get_shipping_tax_rates( $tax_class = null ) { public static function get_shipping_tax_rates( $tax_class = null ) {
// See if we have an explicitly set shipping tax class // See if we have an explicitly set shipping tax class
if ( $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ) ) { if ( $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ) ) {
@ -403,7 +399,7 @@ class WC_Tax {
$matched_tax_rates = array(); $matched_tax_rates = array();
// This will be per item shipping // This will be per item shipping
$rates = $this->find_rates( array( $rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -418,7 +414,7 @@ class WC_Tax {
if ( sizeof( $matched_tax_rates ) == 0 ) { if ( sizeof( $matched_tax_rates ) == 0 ) {
// Get standard rate // Get standard rate
$rates = $this->find_rates( array( $rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'city' => $city, 'city' => $city,
@ -451,7 +447,7 @@ class WC_Tax {
if ( sizeof( $found_tax_classes ) > 1 ) { if ( sizeof( $found_tax_classes ) > 1 ) {
if ( in_array( '', $found_tax_classes ) ) { if ( in_array( '', $found_tax_classes ) ) {
$rates = $this->find_rates( array( $rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'city' => $city, 'city' => $city,
@ -462,7 +458,7 @@ class WC_Tax {
foreach ( $tax_classes as $tax_class ) { foreach ( $tax_classes as $tax_class ) {
if ( in_array( $tax_class, $found_tax_classes ) ) { if ( in_array( $tax_class, $found_tax_classes ) ) {
$rates = $this->find_rates( array( $rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -477,7 +473,7 @@ class WC_Tax {
// If a single tax class is found, use it // If a single tax class is found, use it
} elseif ( sizeof( $found_tax_classes ) == 1 ) { } elseif ( sizeof( $found_tax_classes ) == 1 ) {
$rates = $this->find_rates( array( $rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -489,7 +485,7 @@ class WC_Tax {
// If no class rate are found, use standard rates // If no class rate are found, use standard rates
if ( ! $rates ) if ( ! $rates )
$rates = $this->find_rates( array( $rates = self::find_rates( array(
'country' => $country, 'country' => $country,
'state' => $state, 'state' => $state,
'postcode' => $postcode, 'postcode' => $postcode,
@ -513,7 +509,7 @@ class WC_Tax {
* @param int key * @param int key
* @return bool * @return bool
*/ */
public function is_compound( $key ) { public static function is_compound( $key ) {
global $wpdb; global $wpdb;
return $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ) ? true : false; return $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ) ? true : false;
} }
@ -524,7 +520,7 @@ class WC_Tax {
* @param int key * @param int key
* @return string * @return string
*/ */
public function get_rate_label( $key ) { public static function get_rate_label( $key ) {
global $wpdb; global $wpdb;
$rate_name = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_name FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ); $rate_name = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_name FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) );
@ -532,7 +528,7 @@ class WC_Tax {
if ( ! $rate_name ) if ( ! $rate_name )
$rate_name = WC()->countries->tax_or_vat(); $rate_name = WC()->countries->tax_or_vat();
return apply_filters( 'woocommerce_rate_label', $rate_name, $key, $this ); return apply_filters( 'woocommerce_rate_label', $rate_name, $key );
} }
/** /**
@ -542,7 +538,7 @@ class WC_Tax {
* @param mixed $key * @param mixed $key
* @return string * @return string
*/ */
public function get_rate_code( $key ) { public static function get_rate_code( $key ) {
global $wpdb; global $wpdb;
$rate = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ); $rate = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) );
@ -558,7 +554,7 @@ class WC_Tax {
$code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX'; $code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX';
$code[] = absint( $rate->tax_rate_priority ); $code[] = absint( $rate->tax_rate_priority );
return apply_filters( 'woocommerce_rate_code', strtoupper( implode( '-', array_filter( $code ) ) ), $key, $this ); return apply_filters( 'woocommerce_rate_code', strtoupper( implode( '-', array_filter( $code ) ) ), $key );
} }
/** /**
@ -567,7 +563,8 @@ class WC_Tax {
* @param array * @param array
* @return float * @return float
*/ */
public function get_tax_total( $taxes ) { public static function get_tax_total( $taxes ) {
return array_sum( array_map( array( $this, 'round' ), $taxes ) ); return array_sum( array_map( array( __CLASS__, 'round' ), $taxes ) );
} }
} }
WC_Tax::init();

View File

@ -42,6 +42,87 @@ add_filter( 'woocommerce_short_description', 'shortcode_unautop' );
add_filter( 'woocommerce_short_description', 'prepend_attachment' ); add_filter( 'woocommerce_short_description', 'prepend_attachment' );
add_filter( 'woocommerce_short_description', 'do_shortcode', 11 ); // AFTER wpautop() add_filter( 'woocommerce_short_description', 'do_shortcode', 11 ); // AFTER wpautop()
/**
* Create a new order programmatically
*
* Returns a new order object on success which can then be used to add additonal data.
*
* @return WC_Order on success, WP_Error on failure
*/
function wc_create_order( $args = array() ) {
$default_args = array(
'status' => '',
'customer_id' => null,
'customer_note' => null,
'order_id' => 0
);
$args = wp_parse_args( $args, $default_args );
$order_data = array();
if ( $args['order_id'] > 0 ) {
$updating = true;
$order_data['ID'] = $args['order_id'];
} else {
$updating = false;
$order_data['post_type'] = 'shop_order';
$order_data['post_status'] = 'wc-' . apply_filters( 'woocommerce_default_order_status', 'pending' );
$order_data['ping_status'] = 'closed';
$order_data['post_author'] = 1;
$order_data['post_password'] = uniqid( 'order_' );
$order_data['post_title'] = sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) );
}
if ( $args['status'] ) {
if ( ! in_array( 'wc-' . $args['status'], array_keys( wc_get_order_statuses() ) ) ) {
return new WP_Error( __( 'Invalid order status', 'woocommerce' ) );
}
$order_data['post_status'] = 'wc-' . $args['status'];
}
if ( ! is_null( $args['customer_note'] ) ) {
$order_data['post_excerpt'] = $args['customer_note'];
}
if ( $updating ) {
$order_id = wp_update_post( $order_data );
} else {
$order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', $order_data ), true );
}
if ( is_wp_error( $order_id ) ) {
return $order_id;
}
// Default order meta data.
if ( ! $updating ) {
update_post_meta( $order_id, '_order_key', 'wc_' . apply_filters( 'woocommerce_generate_order_key', uniqid( 'order_' ) ) );
update_post_meta( $order_id, '_order_currency', get_woocommerce_currency() );
update_post_meta( $order_id, '_prices_include_tax', get_option( 'woocommerce_prices_include_tax' ) );
update_post_meta( $order_id, '_customer_ip_address', isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'] );
update_post_meta( $order_id, '_customer_user_agent', isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '' );
update_post_meta( $order_id, '_customer_user', 0 );
}
if ( is_numeric( $args['customer_id'] ) ) {
update_post_meta( $order_id, '_customer_user', $args['customer_id'] );
}
return new WC_Order( $order_id );
}
/**
* Update an order. Uses wc_create_order.
* @param array $args
* @return WC_Error | WC_Order
*/
function wc_update_order( $args ) {
if ( ! $args['order_id'] ) {
return new WP_Error( __( 'Invalid order ID', 'woocommerce' ) );
}
return wc_create_order( $args );
}
/** /**
* Get template part (for templates like the shop-loop). * Get template part (for templates like the shop-loop).
* *