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 ) {
if ( response ) {
$items.each( function() {
var $row = $(this);

View File

@ -786,8 +786,6 @@ class WC_Product {
* @return string
*/
public function get_price_including_tax( $qty = 1, $price = '' ) {
$_tax = new WC_Tax();
if ( ! $price ) {
$price = $this->get_price();
}
@ -796,26 +794,26 @@ class WC_Product {
if ( get_option('woocommerce_prices_include_tax') === 'no' ) {
$tax_rates = $_tax->get_rates( $this->get_tax_class() );
$taxes = $_tax->calc_tax( $price * $qty, $tax_rates, false );
$tax_amount = $_tax->get_tax_total( $taxes );
$tax_rates = WC_Tax::get_rates( $this->get_tax_class() );
$taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, false );
$tax_amount = WC_Tax::get_tax_total( $taxes );
$price = round( $price * $qty + $tax_amount, absint( get_option( 'woocommerce_price_num_decimals' ) ) );
} else {
$tax_rates = $_tax->get_rates( $this->get_tax_class() );
$base_tax_rates = $_tax->get_shop_base_rate( $this->tax_class );
$tax_rates = WC_Tax::get_rates( $this->get_tax_class() );
$base_tax_rates = WC_Tax::get_shop_base_rate( $this->tax_class );
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 );
$price = round( $price * $qty - $base_tax_amount, absint( get_option( 'woocommerce_price_num_decimals' ) ) );
} elseif ( $tax_rates !== $base_tax_rates ) {
$base_taxes = $_tax->calc_tax( $price * $qty, $base_tax_rates, true );
$modded_taxes = $_tax->calc_tax( ( $price * $qty ) - array_sum( $base_taxes ), $tax_rates, false );
$base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true );
$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' ) ) );
} else {
@ -848,12 +846,9 @@ class WC_Product {
}
if ( $this->is_taxable() && get_option('woocommerce_prices_include_tax') === 'yes' ) {
$_tax = new WC_Tax();
$tax_rates = $_tax->get_shop_base_rate( $this->tax_class );
$taxes = $_tax->calc_tax( $price * $qty, $tax_rates, true );
$price = $_tax->round( $price * $qty - array_sum( $taxes ) );
$tax_rates = WC_Tax::get_shop_base_rate( $this->tax_class );
$taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, true );
$price = WC_Tax::round( $price * $qty - array_sum( $taxes ) );
} else {
$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
if ( ! is_array( $taxes ) && $taxes !== false && $total_cost > 0 && $this->is_taxable() ) {
$_tax = new WC_Tax();
$taxes = array();
switch ( $calc_tax ) {
@ -114,8 +113,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
$_product = $cart[ $cost_key ]['data'];
$rates = $_tax->get_shipping_tax_rates( $_product->get_tax_class() );
$item_taxes = $_tax->calc_shipping_tax( $amount, $rates );
$rates = WC_Tax::get_shipping_tax_rates( $_product->get_tax_class() );
$item_taxes = WC_Tax::calc_shipping_tax( $amount, $rates );
// Sum the item taxes
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'
if ( isset( $cost['order'] ) ) {
$rates = $_tax->get_shipping_tax_rates();
$item_taxes = $_tax->calc_shipping_tax( $cost['order'], $rates );
$rates = WC_Tax::get_shipping_tax_rates();
$item_taxes = WC_Tax::calc_shipping_tax( $cost['order'], $rates );
// Sum the item taxes
foreach ( array_keys( $taxes + $item_taxes ) as $key )
@ -140,8 +139,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
default :
$rates = $_tax->get_shipping_tax_rates();
$taxes = $_tax->calc_shipping_tax( $total_cost, $rates );
$rates = WC_Tax::get_shipping_tax_rates();
$taxes = WC_Tax::calc_shipping_tax( $total_cost, $rates );
break;

View File

@ -1185,7 +1185,6 @@ class WC_AJAX {
self::json_headers();
$tax = new WC_Tax();
$taxes = $tax_rows = $item_taxes = $shipping_taxes = array();
$order_id = absint( $_POST['order_id'] );
$order = new WC_Order( $order_id );
@ -1200,17 +1199,12 @@ class WC_AJAX {
// Calculate sales tax first
if ( sizeof( $items ) > 0 ) {
foreach( $items as $item_id => $item ) {
$item_id = absint( $item_id );
$line_subtotal = isset( $item['line_subtotal'] ) ? wc_format_decimal( $item['line_subtotal'] ) : 0;
$line_total = wc_format_decimal( $item['line_total'] );
$tax_class = sanitize_text_field( $item['tax_class'] );
$product_id = $order->get_item_meta( $item_id, '_product_id', true );
if ( ! $item_id || '0' == $tax_class ) {
continue;
}
// Get product details
if ( get_post_type( $product_id ) == 'product' ) {
$_product = get_product( $product_id );
@ -1219,10 +1213,8 @@ class WC_AJAX {
$item_tax_status = 'taxable';
}
// Only calc if taxable
if ( 'taxable' == $item_tax_status ) {
$tax_rates = $tax->find_rates( array(
if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
$tax_rates = WC_Tax::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -1230,18 +1222,10 @@ class WC_AJAX {
'tax_class' => $tax_class
) );
$line_subtotal_taxes = $tax->calc_tax( $line_subtotal, $tax_rates, false );
$line_taxes = $tax->calc_tax( $line_total, $tax_rates, false );
$line_subtotal_tax = array_sum( $line_subtotal_taxes );
$line_tax = array_sum( $line_taxes );
if ( $line_subtotal_tax < 0 ) {
$line_subtotal_tax = 0;
}
if ( $line_tax < 0 ) {
$line_tax = 0;
}
$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 ) );
$item_taxes[ $item_id ] = array(
'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 );
}
}
}
}
// Now calculate shipping tax
$matched_tax_rates = array();
$tax_rates = $tax->find_rates( array(
$tax_rates = WC_Tax::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -1278,62 +1261,20 @@ class WC_AJAX {
}
}
$shipping_taxes = $tax->calc_shipping_tax( $shipping, $matched_tax_rates );
$shipping_tax = $tax->round( array_sum( $shipping_taxes ) );
$shipping_taxes = WC_Tax::calc_shipping_tax( $shipping, $matched_tax_rates );
$shipping_tax = WC_Tax::round( array_sum( $shipping_taxes ) );
// 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 ) );
// Get tax rates
$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 ) ) );
// Add tax rows
foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) {
$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 );
}
// Now merge to keep tax rows
ob_start();
foreach ( array_keys( $taxes + $shipping_taxes ) as $key ) {
$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'] );
}
foreach ( $order->get_taxes() as $item_id => $item ) {
include( 'admin/meta-boxes/views/html-order-tax.php' );
}

View File

@ -1371,7 +1371,7 @@ class WC_Cart {
$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;
}
@ -1661,6 +1661,15 @@ class WC_Cart {
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.
*
@ -2075,6 +2084,24 @@ class WC_Cart {
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.
*

View File

@ -123,7 +123,6 @@ class WC_Checkout {
do_action( 'woocommerce_checkout_init', $this );
}
/**
* Checkout process
*/
@ -132,7 +131,6 @@ class WC_Checkout {
do_action('woocommerce_check_cart_items');
}
/**
* Output the billing information form
*
@ -143,7 +141,6 @@ class WC_Checkout {
wc_get_template( 'checkout/form-billing.php', array( 'checkout' => $this ) );
}
/**
* Output the shipping information form
*
@ -154,242 +151,184 @@ class WC_Checkout {
wc_get_template( 'checkout/form-shipping.php', array( 'checkout' => $this ) );
}
/**
* create_order function.
* @access public
* @throws Exception
* @return int
* @return int|WP_ERROR
*/
public function create_order() {
global $wpdb;
// Give plugins the opportunity to create an order themselves
$order_id = apply_filters( 'woocommerce_create_order', null, $this );
if ( is_numeric( $order_id ) )
if ( $order_id = apply_filters( 'woocommerce_create_order', null, $this ) ) {
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.
$order_data = apply_filters( 'woocommerce_new_order_data', array(
'post_type' => 'shop_order',
'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
) );
try {
// Start transaction if available
$wpdb->query( 'START TRANSACTION' );
// Insert or update the post data
$create_new_order = true;
if ( WC()->session->order_awaiting_payment > 0 ) {
$order_data = array(
'status' => apply_filters( 'woocommerce_default_order_status', 'pending' ),
'customer_id' => $this->customer_id,
'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 );
// 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
$create_new_order = false;
$order_data['ID'] = $order_id;
wp_update_post( $order_data );
$order_data['order_id'] = $order_id;
$order = wc_update_order( $order_data );
// Clear the old line items - we'll add these again in case they changed
$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 ) );
$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 );
}
if ( is_wp_error( $order ) ) {
throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
} else {
$postvalue = $this->posted[ $key ];
update_post_meta( $order_id, '_' . $key, $postvalue );
$order->remove_order_items();
do_action( 'woocommerce_resume_order', $order_id );
}
// User
if ( $postvalue && $this->customer_id && apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) )
update_user_meta( $this->customer_id, $key, $postvalue );
} else {
$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
if ( $this->customer_id )
do_action( 'woocommerce_checkout_update_user_meta', $this->customer_id, $this->posted );
// Store the line items to the new/resumed order
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$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
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
if ( ! $item_id ) {
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
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $_product->get_title(),
'order_item_type' => 'line_item'
) );
// Store fees
foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
$item_id = $order->add_fee( $fee );
// Add line item meta
if ( $item_id ) {
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'] );
wc_add_order_item_meta( $item_id, '_variation_id', $values['variation_id'] );
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'] ) );
if ( ! $item_id ) {
throw new Exception( __( 'Error: Unable to create order. Please try again.', 'woocommerce' ) );
}
// Allow plugins to add order item meta to fees
do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key );
}
// Store variation data in meta so admin can view it
if ( $values['variation'] && is_array( $values['variation'] ) ) {
foreach ( $values['variation'] as $key => $value ) {
$key = str_replace( 'attribute_', '', $key );
wc_add_order_item_meta( $item_id, $key, $value );
// Store shipping for all packages
foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
if ( isset( $package['rates'][ $this->shipping_methods[ $package_key ] ] ) ) {
$item_id = $order->add_shipping( $package['rates'][ $this->shipping_methods[ $package_key ] ] );
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 );
}
}
// 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 );
}
do_action( 'woocommerce_checkout_update_user_meta', $this->customer_id, $this->posted );
}
// 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;
}
@ -658,6 +597,10 @@ class WC_Checkout {
$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 );
// Process payment
@ -717,10 +660,9 @@ class WC_Checkout {
}
} catch ( Exception $e ) {
if ( ! empty( $e ) )
if ( ! empty( $e ) ) {
wc_add_notice( $e->getMessage(), 'error' );
}
}
} // 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

View File

@ -5,7 +5,7 @@
* The WooCommerce order class handles order data.
*
* @class WC_Order
* @version 2.1.0
* @version 2.2.0
* @package WooCommerce/Classes
* @category Class
* @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.
@ -53,7 +407,6 @@ class WC_Order {
return false;
}
/**
* Populates an order from the loaded post data.
*
@ -160,7 +513,6 @@ class WC_Order {
if ( $key == $this->order_key ) {
return true;
}
return false;
}
@ -260,7 +612,6 @@ class WC_Order {
return $this->formatted_shipping_address;
}
/**
* Get the shipping address in an array.
*
@ -930,7 +1281,7 @@ class WC_Order {
* @return WC_Product
*/
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 );
}
@ -1352,6 +1703,9 @@ class WC_Order {
// Record the completed date of the order
update_post_meta( $this->id, '_completed_date', current_time('mysql') );
// Update reports
wc_delete_shop_order_transients( $this->id );
break;
case 'processing' :
case 'on-hold' :
@ -1360,15 +1714,19 @@ class WC_Order {
// Increase coupon usage counts
$this->increase_coupon_usage_counts();
// Update reports
wc_delete_shop_order_transients( $this->id );
break;
case 'cancelled' :
// If the order is cancelled, restore used coupons
$this->decrease_coupon_usage_counts();
// Update reports
wc_delete_shop_order_transients( $this->id );
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' ) );
}
}
/**
* send_stock_notifications function.
*

View File

@ -3,28 +3,24 @@
* Performs tax calculations and loads tax rates.
*
* @class WC_Tax
* @version 2.0.0
* @version 2.2.0
* @package WooCommerce/Classes
* @category Class
* @author WooThemes
*/
class WC_Tax {
/** @var array */
public $matched_rates;
var $log = array();
public static $precision;
public static $round_at_subtotal;
/**
* __construct function.
* Load options
*
* @access public
*/
public function __construct() {
$this->precision = WC_ROUNDING_PRECISION;
$this->dp = (int) get_option( 'woocommerce_price_num_decimals' );
$this->round_at_subtotal = get_option('woocommerce_tax_round_at_subtotal') == 'yes';
public static function init() {
self::$precision = WC_ROUNDING_PRECISION;
self::$round_at_subtotal = 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' );
}
/**
@ -35,23 +31,23 @@ class WC_Tax {
* @param boolean $suppress_rounding Whether to suppress any rounding from taking place
* @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
$price = $this->precision( $price );
$price = self::precision( $price );
if ( $price_includes_tax )
$taxes = $this->calc_inclusive_tax( $price, $rates );
$taxes = self::calc_inclusive_tax( $price, $rates );
else
$taxes = $this->calc_exclusive_tax( $price, $rates );
$taxes = self::calc_exclusive_tax( $price, $rates );
// 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
}
// Remove precision
$price = $this->remove_precision( $price );
$taxes = array_map( array( $this, 'remove_precision' ), $taxes );
$price = self::remove_precision( $price );
$taxes = array_map( array( __CLASS__, 'remove_precision' ), $taxes );
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
* @return array
*/
public function calc_shipping_tax( $price, $rates ) {
return $this->calc_exclusive_tax( $price, $rates );
public static function calc_shipping_tax( $price, $rates ) {
return self::calc_exclusive_tax( $price, $rates );
}
/**
@ -72,8 +68,8 @@ class WC_Tax {
* @param float $price
* @return float
*/
private function precision( $price ) {
return $price * ( pow( 10, $this->precision ) );
private static function precision( $price ) {
return $price * ( pow( 10, self::$precision ) );
}
/**
@ -81,8 +77,8 @@ class WC_Tax {
* @param float $price
* @return float
*/
private function remove_precision( $price ) {
return $price / ( pow( 10, $this->precision ) );
private static function remove_precision( $price ) {
return $price / ( pow( 10, self::$precision ) );
}
/**
@ -95,8 +91,8 @@ class WC_Tax {
* }
* add_filter( 'woocommerce_tax_round', 'euro_5cent_rounding' );
*/
public function round( $in ) {
return apply_filters( 'woocommerce_tax_round', round( $in, $this->precision ), $in );
public static function round( $in ) {
return apply_filters( 'woocommerce_tax_round', round( $in, self::$precision ), $in );
}
/**
@ -106,7 +102,7 @@ class WC_Tax {
* @param array $rates
* @return array
*/
private function calc_inclusive_tax( $price, $rates ) {
private static function calc_inclusive_tax( $price, $rates ) {
$taxes = array();
$regular_tax_rates = $compound_tax_rates = 0;
@ -150,7 +146,7 @@ class WC_Tax {
* @param array $rates
* @return array
*/
private function calc_exclusive_tax( $price, $rates ) {
private static function calc_exclusive_tax( $price, $rates ) {
$taxes = array();
// Multiple taxes
@ -205,7 +201,7 @@ class WC_Tax {
* @param array $args
* @return array
*/
public function find_rates( $args = array() ) {
public static function find_rates( $args = array() ) {
global $wpdb;
$defaults = array(
@ -318,7 +314,7 @@ class WC_Tax {
* @param string $tax_class
* @return array
*/
public function get_rates( $tax_class = '' ) {
public static function get_rates( $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();
$matched_tax_rates = $this->find_rates( array(
$matched_tax_rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'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.
// 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'
? $this->get_shop_base_rate( $tax_class )
? self::get_shop_base_rate( $tax_class )
: array();
}
@ -355,8 +351,8 @@ class WC_Tax {
* @param string Tax Class
* @return array
*/
public function get_shop_base_rate( $tax_class = '' ) {
return $this->find_rates( array(
public static function get_shop_base_rate( $tax_class = '' ) {
return self::find_rates( array(
'country' => WC()->countries->get_base_country(),
'state' => WC()->countries->get_base_state(),
'postcode' => WC()->countries->get_base_postcode(),
@ -371,7 +367,7 @@ class WC_Tax {
* @param string Tax Class
* @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
if ( $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ) ) {
@ -403,7 +399,7 @@ class WC_Tax {
$matched_tax_rates = array();
// This will be per item shipping
$rates = $this->find_rates( array(
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -418,7 +414,7 @@ class WC_Tax {
if ( sizeof( $matched_tax_rates ) == 0 ) {
// Get standard rate
$rates = $this->find_rates( array(
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'city' => $city,
@ -451,7 +447,7 @@ class WC_Tax {
if ( sizeof( $found_tax_classes ) > 1 ) {
if ( in_array( '', $found_tax_classes ) ) {
$rates = $this->find_rates( array(
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'city' => $city,
@ -462,7 +458,7 @@ class WC_Tax {
foreach ( $tax_classes as $tax_class ) {
if ( in_array( $tax_class, $found_tax_classes ) ) {
$rates = $this->find_rates( array(
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -477,7 +473,7 @@ class WC_Tax {
// If a single tax class is found, use it
} elseif ( sizeof( $found_tax_classes ) == 1 ) {
$rates = $this->find_rates( array(
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -489,7 +485,7 @@ class WC_Tax {
// If no class rate are found, use standard rates
if ( ! $rates )
$rates = $this->find_rates( array(
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -513,7 +509,7 @@ class WC_Tax {
* @param int key
* @return bool
*/
public function is_compound( $key ) {
public static function is_compound( $key ) {
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;
}
@ -524,7 +520,7 @@ class WC_Tax {
* @param int key
* @return string
*/
public function get_rate_label( $key ) {
public static function get_rate_label( $key ) {
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 ) );
@ -532,7 +528,7 @@ class WC_Tax {
if ( ! $rate_name )
$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
* @return string
*/
public function get_rate_code( $key ) {
public static function get_rate_code( $key ) {
global $wpdb;
$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[] = 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
* @return float
*/
public function get_tax_total( $taxes ) {
return array_sum( array_map( array( $this, 'round' ), $taxes ) );
public static function get_tax_total( $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', '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).
*