merge changes

This commit is contained in:
Bryce 2014-11-22 00:09:11 +07:00
commit e93c32565c
42 changed files with 1273 additions and 1162 deletions

File diff suppressed because one or more lines are too long

View File

@ -305,27 +305,34 @@ table.wc_status_table {
}
.inline-edit-group {
label,
div.alignright {
label {
clear: none;
width: 49%;
margin: .2em 0;
}
&.dimensions {
label {
width: 75%;
max-width: 75%;
}
}
}
.regular_price,
.sale_price,
.weight,
.stock {
.stock,
.length {
@include box-sizing(border-box);
width: 100%;
margin-left: 4.4em;
}
.length,
.width,
.height {
@include box-sizing(border-box);
width: 32%;
width: 25%;
}
}

File diff suppressed because one or more lines are too long

View File

@ -417,3 +417,47 @@
}
}
}
/**
* Twenty Fifteen specific styles
*/
.twentyfifteen {
.t15wc {
padding-left: 7.6923%;
padding-right: 7.6923%;
padding-top: 7.6923%;
background: #fff;
box-shadow: 0 0 1px rgba(0, 0, 0, 0.15);
.page-title {
margin-left: 0;
}
}
}
@media screen and (min-width: 59.6875em) {
.twentyfifteen {
.t15wc {
margin-left: 8.3333%;
margin-right: 8.3333%;
}
}
.single-product {
.twentyfifteen {
.entry-summary {
padding: 0 !important;
}
}
}
}
@media screen and (min-width: 38.75em) {
.twentyfifteen {
.t15wc {
margin-right: 7.6923%;
margin-left: 7.6923%;
margin-top: 8.3333%;
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -912,6 +912,7 @@ p.demo_store {
margin-right: 1em;
text-indent: -9999px;
position: relative;
border-bottom: 0 !important;
&:last-child {
border-right: 0;

View File

@ -101,15 +101,15 @@ jQuery(function( $ ) {
$( '#wpbody' ).on( 'click', '#doaction, #doaction2', function() {
$( 'input.text', '.inline-edit-row' ).val( '' );
$( '#woocommerce-fields select' ).prop( 'selectedIndex', 0 );
$( '#woocommerce-fields-bulk .inline-edit-group .alignright' ).hide();
$( '#woocommerce-fields-bulk .inline-edit-group .change-input' ).hide();
});
$( '#wpbody' ).on( 'change', '#woocommerce-fields-bulk .inline-edit-group .change_to', function() {
if ( 0 < $( this ).val() ) {
$( this ).closest( 'div' ).find( '.alignright' ).show();
$( this ).closest( 'div' ).find( '.change-input' ).show();
} else {
$( this ).closest( 'div' ).find( '.alignright' ).hide();
$( this ).closest( 'div' ).find( '.change-input' ).hide();
}
});

View File

@ -1 +1 @@
jQuery(function(a){a("#the-list").on("click",".editinline",function(){inlineEditPost.revert();var b=a(this).closest("tr").attr("id");b=b.replace("post-","");var c=a("#woocommerce_inline_"+b),d=c.find(".sku").text(),e=c.find(".regular_price").text(),f=c.find(".sale_price ").text(),g=c.find(".weight").text(),h=c.find(".length").text(),i=c.find(".width").text(),j=c.find(".height").text(),k=c.find(".shipping_class").text(),l=c.find(".visibility").text(),m=c.find(".stock_status").text(),n=c.find(".stock").text(),o=c.find(".featured").text(),p=c.find(".manage_stock").text(),q=c.find(".menu_order").text(),r=c.find(".tax_status").text(),s=c.find(".tax_class").text(),t=c.find(".backorders").text();a('input[name="_sku"]',".inline-edit-row").val(d),a('input[name="_regular_price"]',".inline-edit-row").val(e),a('input[name="_sale_price"]',".inline-edit-row").val(f),a('input[name="_weight"]',".inline-edit-row").val(g),a('input[name="_length"]',".inline-edit-row").val(h),a('input[name="_width"]',".inline-edit-row").val(i),a('input[name="_height"]',".inline-edit-row").val(j),a('select[name="_shipping_class"] option:selected',".inline-edit-row").attr("selected",!1).change(),a('select[name="_shipping_class"] option[value="'+k+'"]').attr("selected","selected").change(),a('input[name="_stock"]',".inline-edit-row").val(n),a('input[name="menu_order"]',".inline-edit-row").val(q),a('select[name="_tax_status"] option[value="'+r+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_tax_class"] option[value="'+s+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_visibility"] option, select[name="_stock_status"] option, select[name="_backorders"] option').removeAttr("selected"),a('select[name="_visibility"] option[value="'+l+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_stock_status"] option[value="'+m+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_backorders"] option[value="'+t+'"]',".inline-edit-row").attr("selected","selected"),"yes"===o?a('input[name="_featured"]',".inline-edit-row").attr("checked","checked"):a('input[name="_featured"]',".inline-edit-row").removeAttr("checked"),"yes"===p?(a(".stock_qty_field",".inline-edit-row").show().removeAttr("style"),a('input[name="_manage_stock"]',".inline-edit-row").attr("checked","checked")):(a(".stock_qty_field",".inline-edit-row").hide(),a('input[name="_manage_stock"]',".inline-edit-row").removeAttr("checked"));var u=c.find(".product_type").text(),v=c.find(".product_is_virtual").text();"simple"===u||"external"===u?a(".price_fields",".inline-edit-row").show().removeAttr("style"):a(".price_fields",".inline-edit-row").hide(),"yes"===v?a(".dimension_fields",".inline-edit-row").hide():a(".dimension_fields",".inline-edit-row").show().removeAttr("style"),"grouped"===u?a(".stock_fields",".inline-edit-row").hide():a(".stock_fields",".inline-edit-row").show().removeAttr("style")}),a("#the-list").on("change",'.inline-edit-row input[name="_manage_stock"]',function(){a(this).is(":checked")?a(".stock_qty_field",".inline-edit-row").show().removeAttr("style"):a(".stock_qty_field",".inline-edit-row").hide()}),a("#wpbody").on("click","#doaction, #doaction2",function(){a("input.text",".inline-edit-row").val(""),a("#woocommerce-fields select").prop("selectedIndex",0),a("#woocommerce-fields-bulk .inline-edit-group .alignright").hide()}),a("#wpbody").on("change","#woocommerce-fields-bulk .inline-edit-group .change_to",function(){0<a(this).val()?a(this).closest("div").find(".alignright").show():a(this).closest("div").find(".alignright").hide()})});
jQuery(function(a){a("#the-list").on("click",".editinline",function(){inlineEditPost.revert();var b=a(this).closest("tr").attr("id");b=b.replace("post-","");var c=a("#woocommerce_inline_"+b),d=c.find(".sku").text(),e=c.find(".regular_price").text(),f=c.find(".sale_price ").text(),g=c.find(".weight").text(),h=c.find(".length").text(),i=c.find(".width").text(),j=c.find(".height").text(),k=c.find(".shipping_class").text(),l=c.find(".visibility").text(),m=c.find(".stock_status").text(),n=c.find(".stock").text(),o=c.find(".featured").text(),p=c.find(".manage_stock").text(),q=c.find(".menu_order").text(),r=c.find(".tax_status").text(),s=c.find(".tax_class").text(),t=c.find(".backorders").text();a('input[name="_sku"]',".inline-edit-row").val(d),a('input[name="_regular_price"]',".inline-edit-row").val(e),a('input[name="_sale_price"]',".inline-edit-row").val(f),a('input[name="_weight"]',".inline-edit-row").val(g),a('input[name="_length"]',".inline-edit-row").val(h),a('input[name="_width"]',".inline-edit-row").val(i),a('input[name="_height"]',".inline-edit-row").val(j),a('select[name="_shipping_class"] option:selected',".inline-edit-row").attr("selected",!1).change(),a('select[name="_shipping_class"] option[value="'+k+'"]').attr("selected","selected").change(),a('input[name="_stock"]',".inline-edit-row").val(n),a('input[name="menu_order"]',".inline-edit-row").val(q),a('select[name="_tax_status"] option[value="'+r+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_tax_class"] option[value="'+s+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_visibility"] option, select[name="_stock_status"] option, select[name="_backorders"] option').removeAttr("selected"),a('select[name="_visibility"] option[value="'+l+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_stock_status"] option[value="'+m+'"]',".inline-edit-row").attr("selected","selected"),a('select[name="_backorders"] option[value="'+t+'"]',".inline-edit-row").attr("selected","selected"),"yes"===o?a('input[name="_featured"]',".inline-edit-row").attr("checked","checked"):a('input[name="_featured"]',".inline-edit-row").removeAttr("checked"),"yes"===p?(a(".stock_qty_field",".inline-edit-row").show().removeAttr("style"),a('input[name="_manage_stock"]',".inline-edit-row").attr("checked","checked")):(a(".stock_qty_field",".inline-edit-row").hide(),a('input[name="_manage_stock"]',".inline-edit-row").removeAttr("checked"));var u=c.find(".product_type").text(),v=c.find(".product_is_virtual").text();"simple"===u||"external"===u?a(".price_fields",".inline-edit-row").show().removeAttr("style"):a(".price_fields",".inline-edit-row").hide(),"yes"===v?a(".dimension_fields",".inline-edit-row").hide():a(".dimension_fields",".inline-edit-row").show().removeAttr("style"),"grouped"===u?a(".stock_fields",".inline-edit-row").hide():a(".stock_fields",".inline-edit-row").show().removeAttr("style")}),a("#the-list").on("change",'.inline-edit-row input[name="_manage_stock"]',function(){a(this).is(":checked")?a(".stock_qty_field",".inline-edit-row").show().removeAttr("style"):a(".stock_qty_field",".inline-edit-row").hide()}),a("#wpbody").on("click","#doaction, #doaction2",function(){a("input.text",".inline-edit-row").val(""),a("#woocommerce-fields select").prop("selectedIndex",0),a("#woocommerce-fields-bulk .inline-edit-group .change-input").hide()}),a("#wpbody").on("change","#woocommerce-fields-bulk .inline-edit-group .change_to",function(){0<a(this).val()?a(this).closest("div").find(".change-input").show():a(this).closest("div").find(".change-input").hide()})});

View File

@ -79,10 +79,10 @@ abstract class WC_Abstract_Order {
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 itemmeta USING {$wpdb->prefix}woocommerce_order_itemmeta itemmeta INNER JOIN {$wpdb->prefix}woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id AND items.order_id = %d AND items.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 itemmeta USING {$wpdb->prefix}woocommerce_order_itemmeta itemmeta INNER JOIN {$wpdb->prefix}woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id and items.order_id = %d )", $this->id ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->id ) );
}
}

View File

@ -1,358 +1,282 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WP_Importer' ) ) {
return;
}
/**
* Tax Rates importer - import tax rates and local tax rates into WooCommerce.
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin/Importers
* @version 2.0.0
* @version 2.3.0
*/
class WC_Tax_Rate_Importer extends WP_Importer {
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
public $id;
public $file_url;
public $import_page;
public $delimiter;
if ( class_exists( 'WP_Importer' ) ) {
class WC_Tax_Rate_Importer extends WP_Importer {
/**
* Constructor
*/
public function __construct() {
$this->import_page = 'woocommerce_tax_rate_csv';
$this->delimiter = empty( $_POST['delimiter'] ) ? ',' : wc_clean( $_POST['delimiter'] );
}
var $id;
var $file_url;
var $import_page;
var $delimiter;
var $posts = array();
var $imported;
var $skipped;
/**
* Registered callback function for the WordPress Importer
*
* Manages the three separate stages of the CSV import process
*/
public function dispatch() {
/**
* __construct function.
*/
public function __construct() {
$this->import_page = 'woocommerce_tax_rate_csv';
}
$this->header();
/**
* Registered callback function for the WordPress Importer
*
* Manages the three separate stages of the CSV import process
*/
public function dispatch() {
$step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step'];
$this->header();
switch ( $step ) {
if ( ! empty( $_POST['delimiter'] ) )
$this->delimiter = stripslashes( trim( $_POST['delimiter'] ) );
case 0:
$this->greet();
break;
if ( ! $this->delimiter )
$this->delimiter = ',';
case 1:
check_admin_referer( 'import-upload' );
$step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step'];
if ( $this->handle_upload() ) {
switch ( $step ) {
case 0:
$this->greet();
break;
case 1:
check_admin_referer( 'import-upload' );
if ( $this->handle_upload() ) {
if ( $this->id )
$file = get_attached_file( $this->id );
else
$file = ABSPATH . $this->file_url;
add_filter( 'http_request_timeout', array( $this, 'bump_request_timeout' ) );
if ( function_exists( 'gc_enable' ) )
gc_enable();
@set_time_limit(0);
@ob_flush();
@flush();
$this->import( $file );
}
break;
}
$this->footer();
}
/**
* format_data_from_csv function.
*
* @param mixed $data
* @param string $enc
* @return string
*/
public function format_data_from_csv( $data, $enc ) {
return ( $enc == 'UTF-8' ) ? $data : utf8_encode( $data );
}
/**
* import function.
*
* @param mixed $file
*/
function import( $file ) {
global $wpdb;
$this->imported = $this->skipped = 0;
if ( ! is_file($file) ) {
echo '<p><strong>' . __( 'Sorry, there has been an error.', 'woocommerce' ) . '</strong><br />';
echo __( 'The file does not exist, please try again.', 'woocommerce' ) . '</p>';
$this->footer();
die();
}
$new_rates = array();
ini_set( 'auto_detect_line_endings', '1' );
if ( ( $handle = fopen( $file, "r" ) ) !== FALSE ) {
$header = fgetcsv( $handle, 0, $this->delimiter );
if ( sizeof( $header ) == 10 ) {
$loop = 0;
while ( ( $row = fgetcsv( $handle, 0, $this->delimiter ) ) !== FALSE ) {
list( $country, $state, $postcode, $city, $rate, $name, $priority, $compound, $shipping, $class ) = $row;
$country = trim( strtoupper( $country ) );
$state = trim( strtoupper( $state ) );
if ( $country == '*' )
$country = '';
if ( $state == '*' )
$state = '';
if ( $class == 'standard' )
$class = '';
$wpdb->insert(
$wpdb->prefix . "woocommerce_tax_rates",
array(
'tax_rate_country' => $country,
'tax_rate_state' => $state,
'tax_rate' => wc_format_decimal( $rate, 4 ),
'tax_rate_name' => trim( $name ),
'tax_rate_priority' => absint( $priority ),
'tax_rate_compound' => $compound ? 1 : 0,
'tax_rate_shipping' => $shipping ? 1 : 0,
'tax_rate_order' => $loop,
'tax_rate_class' => sanitize_title( $class )
)
);
$tax_rate_id = $wpdb->insert_id;
$postcode = wc_clean( $postcode );
$postcodes = explode( ';', $postcode );
$postcodes = array_map( 'strtoupper', array_map( 'wc_clean', $postcodes ) );
$postcode_query = array();
foreach( $postcodes as $postcode ) {
if ( ! empty( $postcode ) && $postcode != '*' ) {
if ( strstr( $postcode, '-' ) ) {
$postcode_parts = explode( '-', $postcode );
if ( is_numeric( $postcode_parts[0] ) && is_numeric( $postcode_parts[1] ) && $postcode_parts[1] > $postcode_parts[0] ) {
for ( $i = $postcode_parts[0]; $i <= $postcode_parts[1]; $i ++ ) {
if ( ! $i ) {
continue;
}
if ( strlen( $i ) < strlen( $postcode_parts[0] ) ) {
$i = str_pad( $i, strlen( $postcode_parts[0] ), "0", STR_PAD_LEFT );
}
$postcode_query[] = "( '" . esc_sql( $i ) . "', $tax_rate_id, 'postcode' )";
}
}
} else {
$postcode_query[] = "( '" . esc_sql( $postcode ) . "', $tax_rate_id, 'postcode' )";
}
}
}
if ( sizeof( $postcode_query ) > 0 ) {
$wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $postcode_query ) );
}
$city = wc_clean( $city );
$cities = explode( ';', $city );
$cities = array_map( 'strtoupper', array_map( 'wc_clean', $cities ) );
foreach( $cities as $city ) {
if ( ! empty( $city ) && $city != '*' ) {
$wpdb->insert(
$wpdb->prefix . "woocommerce_tax_rate_locations",
array(
'location_code' => $city,
'tax_rate_id' => $tax_rate_id,
'location_type' => 'city',
)
);
}
}
$loop ++;
$this->imported++;
if ( $this->id ) {
$file = get_attached_file( $this->id );
} else {
$file = ABSPATH . $this->file_url;
}
} else {
echo '<p><strong>' . __( 'Sorry, there has been an error.', 'woocommerce' ) . '</strong><br />';
echo __( 'The CSV is invalid.', 'woocommerce' ) . '</p>';
$this->footer();
die();
add_filter( 'http_request_timeout', array( $this, 'bump_request_timeout' ) );
$this->import( $file );
}
fclose( $handle );
}
// Show Result
echo '<div class="updated settings-error below-h2"><p>
'.sprintf( __( 'Import complete - imported <strong>%s</strong> tax rates and skipped <strong>%s</strong>.', 'woocommerce' ), $this->imported, $this->skipped ).'
</p></div>';
$this->import_end();
break;
}
/**
* Performs post-import cleanup of files and the cache
*/
public function import_end() {
echo '<p>' . __( 'All done!', 'woocommerce' ) . ' <a href="' . admin_url('admin.php?page=wc-settings&tab=tax') . '">' . __( 'View Tax Rates', 'woocommerce' ) . '</a>' . '</p>';
$this->footer();
}
do_action( 'import_end' );
/**
* Import is starting
*/
private function import_start() {
if ( function_exists( 'gc_enable' ) ) {
gc_enable();
}
@set_time_limit(0);
@ob_flush();
@flush();
@ini_set( 'auto_detect_line_endings', '1' );
}
/**
* format_data_from_csv function.
*
* @param mixed $data
* @param string $enc
* @return string
*/
public function format_data_from_csv( $data, $enc ) {
return ( $enc == 'UTF-8' ) ? $data : utf8_encode( $data );
}
/**
* import function.
*
* @param mixed $file
*/
public function import( $file ) {
if ( ! is_file( $file ) ) {
$this->import_error( __( 'The file does not exist, please try again.', 'woocommerce' ) );
}
/**
* Handles the CSV upload and initial parsing of the file to prepare for
* displaying author import options
*
* @return bool False if error uploading or invalid file, true otherwise
*/
public function handle_upload() {
$this->import_start();
if ( empty( $_POST['file_url'] ) ) {
if ( ( $handle = fopen( $file, "r" ) ) !== FALSE ) {
$file = wp_import_handle_upload();
$header = fgetcsv( $handle, 0, $this->delimiter );
if ( isset( $file['error'] ) ) {
echo '<p><strong>' . __( 'Sorry, there has been an error.', 'woocommerce' ) . '</strong><br />';
echo esc_html( $file['error'] ) . '</p>';
return false;
if ( 10 === sizeof( $header ) ) {
$loop = 0;
while ( ( $row = fgetcsv( $handle, 0, $this->delimiter ) ) !== FALSE ) {
list( $country, $state, $postcode, $city, $rate, $name, $priority, $compound, $shipping, $class ) = $row;
$tax_rate = array(
'tax_rate_country' => $country,
'tax_rate_state' => $state,
'tax_rate' => $rate,
'tax_rate_name' => $name,
'tax_rate_priority' => $priority,
'tax_rate_compound' => $compound ? 1 : 0,
'tax_rate_shipping' => $shipping ? 1 : 0,
'tax_rate_order' => $loop ++,
'tax_rate_class' => $class
);
$tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, wc_clean( $postcode ) );
WC_Tax::_update_tax_rate_cities( $tax_rate_id, wc_clean( $city ) );
}
$this->id = (int) $file['id'];
} else {
if ( file_exists( ABSPATH . $_POST['file_url'] ) ) {
$this->file_url = esc_attr( $_POST['file_url'] );
} else {
echo '<p><strong>' . __( 'Sorry, there has been an error.', 'woocommerce' ) . '</strong></p>';
return false;
}
$this->import_error( __( 'The CSV is invalid.', 'woocommerce' ) );
}
return true;
fclose( $handle );
}
/**
* header function.
*/
public function header() {
echo '<div class="wrap"><div class="icon32 icon32-woocommerce-importer" id="icon-woocommerce"><br></div>';
echo '<h2>' . __( 'Import Tax Rates', 'woocommerce' ) . '</h2>';
// Show Result
echo '<div class="updated settings-error below-h2"><p>
'.sprintf( __( 'Import complete - imported <strong>%s</strong> tax rates.', 'woocommerce' ), $loop ).'
</p></div>';
$this->import_end();
}
/**
* Performs post-import cleanup of files and the cache
*/
public function import_end() {
echo '<p>' . __( 'All done!', 'woocommerce' ) . ' <a href="' . admin_url('admin.php?page=wc-settings&tab=tax') . '">' . __( 'View Tax Rates', 'woocommerce' ) . '</a>' . '</p>';
do_action( 'import_end' );
}
/**
* Handles the CSV upload and initial parsing of the file to prepare for
* displaying author import options
*
* @return bool False if error uploading or invalid file, true otherwise
*/
public function handle_upload() {
if ( empty( $_POST['file_url'] ) ) {
$file = wp_import_handle_upload();
if ( isset( $file['error'] ) ) {
$this->import_error( $file['error'] );
}
$this->id = absint( $file['id'] );
} elseif ( file_exists( ABSPATH . $_POST['file_url'] ) ) {
$this->file_url = esc_attr( $_POST['file_url'] );
} else {
$this->import_error();
}
/**
* footer function.
*/
public function footer() {
echo '</div>';
return true;
}
/**
* header function.
*/
public function header() {
echo '<div class="wrap"><div class="icon32 icon32-woocommerce-importer" id="icon-woocommerce"><br></div>';
echo '<h2>' . __( 'Import Tax Rates', 'woocommerce' ) . '</h2>';
}
/**
* footer function.
*/
public function footer() {
echo '</div>';
}
/**
* greet function.
*/
public function greet() {
echo '<div class="narrow">';
echo '<p>' . __( 'Hi there! Upload a CSV file containing tax rates to import the contents into your shop. Choose a .csv file to upload, then click "Upload file and import".', 'woocommerce' ).'</p>';
echo '<p>' . sprintf( __( 'Tax rates need to be defined with columns in a specific order (10 columns). <a href="%s">Click here to download a sample</a>.', 'woocommerce' ), WC()->plugin_url() . '/dummy-data/sample_tax_rates.csv' ) . '</p>';
$action = 'admin.php?import=woocommerce_tax_rate_csv&step=1';
$bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
$size = size_format( $bytes );
$upload_dir = wp_upload_dir();
if ( ! empty( $upload_dir['error'] ) ) :
?><div class="error"><p><?php _e( 'Before you can upload your import file, you will need to fix the following error:', 'woocommerce' ); ?></p>
<p><strong><?php echo $upload_dir['error']; ?></strong></p></div><?php
else :
?>
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="<?php echo esc_attr(wp_nonce_url($action, 'import-upload')); ?>">
<table class="form-table">
<tbody>
<tr>
<th>
<label for="upload"><?php _e( 'Choose a file from your computer:', 'woocommerce' ); ?></label>
</th>
<td>
<input type="file" id="upload" name="import" size="25" />
<input type="hidden" name="action" value="save" />
<input type="hidden" name="max_file_size" value="<?php echo $bytes; ?>" />
<small><?php printf( __('Maximum size: %s', 'woocommerce' ), $size ); ?></small>
</td>
</tr>
<tr>
<th>
<label for="file_url"><?php _e( 'OR enter path to file:', 'woocommerce' ); ?></label>
</th>
<td>
<?php echo ' ' . ABSPATH . ' '; ?><input type="text" id="file_url" name="file_url" size="25" />
</td>
</tr>
<tr>
<th><label><?php _e( 'Delimiter', 'woocommerce' ); ?></label><br/></th>
<td><input type="text" name="delimiter" placeholder="," size="2" /></td>
</tr>
</tbody>
</table>
<p class="submit">
<input type="submit" class="button" value="<?php esc_attr_e( 'Upload file and import', 'woocommerce' ); ?>" />
</p>
</form>
<?php
endif;
echo '</div>';
}
/**
* Show import error and quit
* @param string $message
*/
private function import_error( $message = '' ) {
echo '<p><strong>' . __( 'Sorry, there has been an error.', 'woocommerce' ) . '</strong><br />';
if ( $message ) {
echo esc_html( $message );
}
echo '</p>';
$this->footer();
die();
}
/**
* greet function.
*/
public function greet() {
echo '<div class="narrow">';
echo '<p>' . __( 'Hi there! Upload a CSV file containing tax rates to import the contents into your shop. Choose a .csv file to upload, then click "Upload file and import".', 'woocommerce' ).'</p>';
echo '<p>' . sprintf( __( 'Tax rates need to be defined with columns in a specific order (10 columns). <a href="%s">Click here to download a sample</a>.', 'woocommerce' ), WC()->plugin_url() . '/dummy-data/sample_tax_rates.csv' ) . '</p>';
$action = 'admin.php?import=woocommerce_tax_rate_csv&step=1';
$bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
$size = size_format( $bytes );
$upload_dir = wp_upload_dir();
if ( ! empty( $upload_dir['error'] ) ) :
?><div class="error"><p><?php _e( 'Before you can upload your import file, you will need to fix the following error:', 'woocommerce' ); ?></p>
<p><strong><?php echo $upload_dir['error']; ?></strong></p></div><?php
else :
?>
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="<?php echo esc_attr(wp_nonce_url($action, 'import-upload')); ?>">
<table class="form-table">
<tbody>
<tr>
<th>
<label for="upload"><?php _e( 'Choose a file from your computer:', 'woocommerce' ); ?></label>
</th>
<td>
<input type="file" id="upload" name="import" size="25" />
<input type="hidden" name="action" value="save" />
<input type="hidden" name="max_file_size" value="<?php echo $bytes; ?>" />
<small><?php printf( __('Maximum size: %s', 'woocommerce' ), $size ); ?></small>
</td>
</tr>
<tr>
<th>
<label for="file_url"><?php _e( 'OR enter path to file:', 'woocommerce' ); ?></label>
</th>
<td>
<?php echo ' ' . ABSPATH . ' '; ?><input type="text" id="file_url" name="file_url" size="25" />
</td>
</tr>
<tr>
<th><label><?php _e( 'Delimiter', 'woocommerce' ); ?></label><br/></th>
<td><input type="text" name="delimiter" placeholder="," size="2" /></td>
</tr>
</tbody>
</table>
<p class="submit">
<input type="submit" class="button" value="<?php esc_attr_e( 'Upload file and import', 'woocommerce' ); ?>" />
</p>
</form>
<?php
endif;
echo '</div>';
}
/**
* Added to http_request_timeout filter to force timeout at 60 seconds during import
*
* @param int $val
* @return int 60
*/
public function bump_request_timeout( $val ) {
return 60;
}
/**
* Added to http_request_timeout filter to force timeout at 60 seconds during import
*
* @param int $val
* @return int 60
*/
public function bump_request_timeout( $val ) {
return 60;
}
}

View File

@ -257,8 +257,8 @@ class WC_Meta_Box_Product_Data {
'none' => _x( 'None', 'Tax status', 'woocommerce' )
) ) );
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
$classes_options = array();
$tax_classes = WC_Tax::get_tax_classes();
$classes_options = array();
$classes_options[''] = __( 'Standard', 'woocommerce' );
if ( $tax_classes ) {
@ -778,7 +778,7 @@ class WC_Meta_Box_Product_Data {
}
// Get tax classes
$tax_classes = array_filter( array_map('trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
$tax_class_options = array();
$tax_class_options[''] = __( 'Standard', 'woocommerce' );

View File

@ -16,7 +16,7 @@ $line_items_shipping = $order->get_items( 'shipping' );
if ( wc_tax_enabled() ) {
$order_taxes = $order->get_taxes();
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
$classes_options = array();
$classes_options[''] = __( 'Standard', 'woocommerce' );

View File

@ -17,11 +17,21 @@ if ( ! class_exists( 'WC_Settings_Page' ) ) :
/**
* WC_Settings_Page
*/
class WC_Settings_Page {
abstract class WC_Settings_Page {
protected $id = '';
protected $label = '';
/**
* Constructor
*/
public function __construct() {
add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) );
add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) );
add_action( 'woocommerce_settings_save_' . $this->id, array( $this, 'save' ) );
}
/**
* Add this page to settings
*/

View File

@ -9,7 +9,7 @@
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
if ( ! class_exists( 'WC_Settings_Tax' ) ) :
@ -19,17 +19,14 @@ if ( ! class_exists( 'WC_Settings_Tax' ) ) :
*/
class WC_Settings_Tax extends WC_Settings_Page {
protected $id = 'tax';
/**
* Constructor.
*/
public function __construct() {
$this->id = 'tax';
$this->label = __( 'Tax', 'woocommerce' );
add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) );
add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) );
add_action( 'woocommerce_settings_save_' . $this->id, array( $this, 'save' ) );
parent::__construct();
}
/**
@ -44,12 +41,10 @@ class WC_Settings_Tax extends WC_Settings_Page {
);
// Get tax classes and display as links
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
if ( $tax_classes ) {
foreach ( $tax_classes as $class ) {
$sections[ sanitize_title( $class ) ] = sprintf( __( '%s Rates', 'woocommerce' ), $class );
}
foreach ( $tax_classes as $class ) {
$sections[ sanitize_title( $class ) ] = sprintf( __( '%s Rates', 'woocommerce' ), $class );
}
return apply_filters( 'woocommerce_get_sections_' . $this->id, $sections );
@ -61,139 +56,14 @@ class WC_Settings_Tax extends WC_Settings_Page {
* @return array
*/
public function get_settings() {
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
$classes_options = array();
if ( $tax_classes ) {
foreach ( $tax_classes as $class ) {
$classes_options[ sanitize_title( $class ) ] = esc_html( $class );
}
foreach ( $tax_classes as $class ) {
$classes_options[ sanitize_title( $class ) ] = esc_html( $class );
}
$settings = apply_filters('woocommerce_tax_settings', array(
array( 'title' => __( 'Tax Options', 'woocommerce' ), 'type' => 'title','desc' => '', 'id' => 'tax_options' ),
array(
'title' => __( 'Enable Taxes', 'woocommerce' ),
'desc' => __( 'Enable taxes and tax calculations', 'woocommerce' ),
'id' => 'woocommerce_calc_taxes',
'default' => 'no',
'type' => 'checkbox'
),
array(
'title' => __( 'Prices Entered With Tax', 'woocommerce' ),
'id' => 'woocommerce_prices_include_tax',
'default' => 'no',
'type' => 'radio',
'desc_tip' => __( 'This option is important as it will affect how you input prices. Changing it will not update existing products.', 'woocommerce' ),
'options' => array(
'yes' => __( 'Yes, I will enter prices inclusive of tax', 'woocommerce' ),
'no' => __( 'No, I will enter prices exclusive of tax', 'woocommerce' )
),
),
array(
'title' => __( 'Calculate Tax Based On:', 'woocommerce' ),
'id' => 'woocommerce_tax_based_on',
'desc_tip' => __( 'This option determines which address is used to calculate tax.', 'woocommerce' ),
'default' => 'shipping',
'type' => 'select',
'options' => array(
'shipping' => __( 'Customer shipping address', 'woocommerce' ),
'billing' => __( 'Customer billing address', 'woocommerce' ),
'base' => __( 'Shop base address', 'woocommerce' )
),
),
array(
'title' => __( 'Default Customer Address:', 'woocommerce' ),
'id' => 'woocommerce_default_customer_address',
'desc_tip' => __( 'This option determines the customers default address (before they input their own).', 'woocommerce' ),
'default' => 'base',
'type' => 'select',
'options' => array(
'' => __( 'No address', 'woocommerce' ),
'base' => __( 'Shop base address', 'woocommerce' ),
),
),
array(
'title' => __( 'Shipping Tax Class:', 'woocommerce' ),
'desc' => __( 'Optionally control which tax class shipping gets, or leave it so shipping tax is based on the cart items themselves.', 'woocommerce' ),
'id' => 'woocommerce_shipping_tax_class',
'css' => 'min-width:150px;',
'default' => 'title',
'type' => 'select',
'options' => array( '' => __( 'Shipping tax class based on cart items', 'woocommerce' ), 'standard' => __( 'Standard', 'woocommerce' ) ) + $classes_options,
'desc_tip' => true,
),
array(
'title' => __( 'Rounding', 'woocommerce' ),
'desc' => __( 'Round tax at subtotal level, instead of rounding per line', 'woocommerce' ),
'id' => 'woocommerce_tax_round_at_subtotal',
'default' => 'no',
'type' => 'checkbox',
),
array(
'title' => __( 'Additional Tax Classes', 'woocommerce' ),
'desc' => __( 'List additional tax classes below (1 per line). This is in addition to the default <code>Standard Rate</code>. Tax classes can be assigned to products.', 'woocommerce' ),
'id' => 'woocommerce_tax_classes',
'css' => 'width:100%; height: 65px;',
'type' => 'textarea',
'default' => sprintf( __( 'Reduced Rate%sZero Rate', 'woocommerce' ), PHP_EOL )
),
array(
'title' => __( 'Display prices in the shop:', 'woocommerce' ),
'id' => 'woocommerce_tax_display_shop',
'default' => 'excl',
'type' => 'select',
'options' => array(
'incl' => __( 'Including tax', 'woocommerce' ),
'excl' => __( 'Excluding tax', 'woocommerce' ),
)
),
array(
'title' => __( 'Price display suffix:', 'woocommerce' ),
'id' => 'woocommerce_price_display_suffix',
'default' => '',
'type' => 'text',
'desc' => __( 'Define text to show after your product prices. This could be, for example, "inc. Vat" to explain your pricing. You can also have prices substituted here using one of the following: <code>{price_including_tax}, {price_excluding_tax}</code>.', 'woocommerce' ),
),
array(
'title' => __( 'Display prices during cart/checkout:', 'woocommerce' ),
'id' => 'woocommerce_tax_display_cart',
'default' => 'excl',
'type' => 'select',
'options' => array(
'incl' => __( 'Including tax', 'woocommerce' ),
'excl' => __( 'Excluding tax', 'woocommerce' ),
),
'autoload' => false
),
array(
'title' => __( 'Display tax totals:', 'woocommerce' ),
'id' => 'woocommerce_tax_total_display',
'default' => 'itemized',
'type' => 'select',
'options' => array(
'single' => __( 'As a single total', 'woocommerce' ),
'itemized' => __( 'Itemized', 'woocommerce' ),
),
'autoload' => false
),
array( 'type' => 'sectionend', 'id' => 'tax_options' ),
) );
return apply_filters( 'woocommerce_get_settings_' . $this->id, $settings );
return apply_filters( 'woocommerce_get_settings_' . $this->id, include( 'settings-tax.php' ) );
}
/**
@ -202,7 +72,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
public function output() {
global $current_section;
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
if ( $current_section == 'standard' || in_array( $current_section, array_map( 'sanitize_title', $tax_classes ) ) ) {
$this->output_tax_rates();
@ -220,14 +90,11 @@ class WC_Settings_Tax extends WC_Settings_Page {
global $current_section, $wpdb;
if ( ! $current_section ) {
$settings = $this->get_settings();
WC_Admin_Settings::save_fields( $settings );
} else {
} elseif ( ! empty( $_POST['tax_rate_country'] ) ) {
$this->save_tax_rates();
}
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_wc_tax_rates_%') OR `option_name` LIKE ('_transient_timeout_wc_tax_rates_%')" );
@ -242,265 +109,8 @@ class WC_Settings_Tax extends WC_Settings_Page {
$page = ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1;
$limit = 100;
$current_class = $this->get_current_tax_class();
?>
<h3><?php printf( __( 'Tax Rates for the "%s" Class', 'woocommerce' ), $current_class ? esc_html( $current_class ) : __( 'Standard', 'woocommerce' ) ); ?></h3>
<p><?php printf( __( 'Define tax rates for countries and states below. <a href="%s">See here</a> for available alpha-2 country codes.', 'woocommerce' ), 'http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes' ); ?></p>
<table class="wc_tax_rates wc_input_table sortable widefat">
<thead>
<tr>
<th class="sort">&nbsp;</th>
<th width="8%"><?php _e( 'Country&nbsp;Code', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('A 2 digit country code, e.g. US. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'State&nbsp;Code', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('A 2 digit state code, e.g. AL. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th><?php _e( 'ZIP/Postcode', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Postcode for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all areas. Wildcards (*) can be used. Ranges for numeric postcodes (e.g. 12345-12350) will be expanded into individual postcodes.', 'woocommerce'); ?>">[?]</span></th>
<th><?php _e( 'City', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Cities for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all cities.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Rate&nbsp;%', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e( 'Enter a tax rate (percentage) to 4 decimal places.', 'woocommerce' ); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Tax&nbsp;Name', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Enter a name for this tax rate.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Priority', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Choose a priority for this tax rate. Only 1 matching rate per priority will be used. To define multiple tax rates for a single area you need to specify a different priority per rate.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Compound', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Choose whether or not this is a compound rate. Compound tax rates are applied on top of other tax rates.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Shipping', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Choose whether or not this tax rate also gets applied to shipping.', 'woocommerce'); ?>">[?]</span></th>
</tr>
</thead>
<tbody id="rates">
<?php
$rates = $wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE tax_rate_class = %s
ORDER BY tax_rate_order
LIMIT %d, %d
" ,
sanitize_title( $current_class ),
( $page - 1 ) * $limit,
$limit
) );
foreach ( $rates as $rate ) {
?>
<tr class="tips" data-tip="<?php echo __( 'Tax rate ID', 'woocommerce' ) . ': ' . $rate->tax_rate_id; ?>">
<td class="sort"><input type="hidden" class="remove_tax_rate" name="remove_tax_rate[<?php echo $rate->tax_rate_id ?>]" value="0" /></td>
<td class="country" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_country ) ?>" placeholder="*" name="tax_rate_country[<?php echo $rate->tax_rate_id ?>]" class="wc_input_country_iso" />
</td>
<td class="state" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_state ) ?>" placeholder="*" name="tax_rate_state[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="postcode">
<input type="text" value="<?php
$locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='postcode' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
echo esc_attr( implode( '; ', $locations ) );
?>" placeholder="*" data-name="tax_rate_postcode[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="city">
<input type="text" value="<?php
$locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='city' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
echo esc_attr( implode( '; ', $locations ) );
?>" placeholder="*" data-name="tax_rate_city[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="rate" width="8%">
<input type="number" step="any" min="0" value="<?php echo esc_attr( $rate->tax_rate ) ?>" placeholder="0" name="tax_rate[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="name" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_name ) ?>" name="tax_rate_name[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="priority" width="8%">
<input type="number" step="1" min="1" value="<?php echo esc_attr( $rate->tax_rate_priority ) ?>" name="tax_rate_priority[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="compound" width="8%">
<input type="checkbox" class="checkbox" name="tax_rate_compound[<?php echo $rate->tax_rate_id ?>]" <?php checked( $rate->tax_rate_compound, '1' ); ?> />
</td>
<td class="apply_to_shipping" width="8%">
<input type="checkbox" class="checkbox" name="tax_rate_shipping[<?php echo $rate->tax_rate_id ?>]" <?php checked($rate->tax_rate_shipping, '1' ); ?> />
</td>
</tr>
<?php
}
?>
</tbody>
<tfoot>
<tr>
<th colspan="10">
<a href="#" class="button plus insert"><?php _e( 'Insert row', 'woocommerce' ); ?></a>
<a href="#" class="button minus remove_tax_rates"><?php _e( 'Remove selected row(s)', 'woocommerce' ); ?></a>
<div class="pagination">
<?php
echo str_replace( 'page-numbers', 'page-numbers button', paginate_links( array(
'base' => add_query_arg( 'p', '%#%' ),
'type' => 'plain',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'total' => ceil( absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(tax_rate_id) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s;", sanitize_title( $current_class ) ) ) ) / $limit ),
'current' => $page
) ) );
?>
</div>
<a href="#" download="tax_rates.csv" class="button export"><?php _e( 'Export CSV', 'woocommerce' ); ?></a>
<a href="<?php echo admin_url( 'admin.php?import=woocommerce_tax_rate_csv' ); ?>" class="button import"><?php _e( 'Import CSV', 'woocommerce' ); ?></a>
</th>
</tr>
</tfoot>
</table>
<script type="text/javascript">
jQuery( function() {
jQuery('.wc_tax_rates .remove_tax_rates').click(function() {
var $tbody = jQuery('.wc_tax_rates').find('tbody');
if ( $tbody.find('tr.current').size() > 0 ) {
$current = $tbody.find('tr.current');
$current.find('input').val('');
$current.find('input.remove_tax_rate').val('1');
$current.each(function(){
if ( jQuery(this).is('.new') )
jQuery(this).remove();
else
jQuery(this).hide();
});
} else {
alert('<?php echo esc_js( __( 'No row(s) selected', 'woocommerce' ) ); ?>');
}
return false;
});
jQuery('.wc_tax_rates .export').click(function() {
var csv_data = "data:application/csv;charset=utf-8,<?php _e( 'Country Code', 'woocommerce' ); ?>,<?php _e( 'State Code', 'woocommerce' ); ?>,<?php _e( 'ZIP/Postcode', 'woocommerce' ); ?>,<?php _e( 'City', 'woocommerce' ); ?>,<?php _e( 'Rate %', 'woocommerce' ); ?>,<?php _e( 'Tax Name', 'woocommerce' ); ?>,<?php _e( 'Priority', 'woocommerce' ); ?>,<?php _e( 'Compound', 'woocommerce' ); ?>,<?php _e( 'Shipping', 'woocommerce' ); ?>,<?php _e( 'Tax Class', 'woocommerce' ); ?>\n";
jQuery('#rates tr:visible').each(function() {
var row = '';
jQuery(this).find('td:not(.sort) input').each(function() {
if ( jQuery(this).is('.checkbox') ) {
if ( jQuery(this).is(':checked') ) {
val = 1;
} else {
val = 0;
}
} else {
var val = jQuery(this).val();
if ( ! val )
val = jQuery(this).attr('placeholder');
}
row = row + val + ',';
});
row = row + '<?php echo $current_class; ?>';
//row.substring( 0, row.length - 1 );
csv_data = csv_data + row + "\n";
});
jQuery(this).attr( 'href', encodeURI( csv_data ) );
return true;
});
jQuery('.wc_tax_rates .insert').click(function() {
var $tbody = jQuery('.wc_tax_rates').find('tbody');
var size = $tbody.find('tr').size();
var code = '<tr class="new">\
<td class="sort">&nbsp;</td>\
<td class="country" width="8%">\
<input type="text" placeholder="*" name="tax_rate_country[new-' + size + ']" class="wc_input_country_iso" />\
</td>\
<td class="state" width="8%">\
<input type="text" placeholder="*" name="tax_rate_state[new-' + size + ']" />\
</td>\
<td class="postcode">\
<input type="text" placeholder="*" name="tax_rate_postcode[new-' + size + ']" />\
</td>\
<td class="city">\
<input type="text" placeholder="*" name="tax_rate_city[new-' + size + ']" />\
</td>\
<td class="rate" width="8%">\
<input type="number" step="any" min="0" placeholder="0" name="tax_rate[new-' + size + ']" />\
</td>\
<td class="name" width="8%">\
<input type="text" name="tax_rate_name[new-' + size + ']" />\
</td>\
<td class="priority" width="8%">\
<input type="number" step="1" min="1" value="1" name="tax_rate_priority[new-' + size + ']" />\
</td>\
<td class="compound" width="8%">\
<input type="checkbox" class="checkbox" name="tax_rate_compound[new-' + size + ']" />\
</td>\
<td class="apply_to_shipping" width="8%">\
<input type="checkbox" class="checkbox" name="tax_rate_shipping[new-' + size + ']" checked="checked" />\
</td>\
</tr>';
if ( $tbody.find('tr.current').size() > 0 ) {
$tbody.find('tr.current').after( code );
} else {
$tbody.append( code );
}
jQuery( "td.country input" ).autocomplete({
source: availableCountries,
minLength: 3
});
jQuery( "td.state input" ).autocomplete({
source: availableStates,
minLength: 3
});
return false;
});
jQuery('.wc_tax_rates td.postcode, .wc_tax_rates td.city').find('input').change(function() {
jQuery(this).attr( 'name', jQuery(this).attr( 'data-name' ) );
});
var availableCountries = [<?php
$countries = array();
foreach ( WC()->countries->get_allowed_countries() as $value => $label )
$countries[] = '{ label: "' . esc_attr( $label ) . '", value: "' . $value . '" }';
echo implode( ', ', $countries );
?>];
var availableStates = [<?php
$countries = array();
foreach ( WC()->countries->get_allowed_country_states() as $value => $label )
foreach ( $label as $code => $state )
$countries[] = '{ label: "' . esc_attr( $state ) . '", value: "' . $code . '" }';
echo implode( ', ', $countries );
?>];
jQuery( "td.country input" ).autocomplete({
source: availableCountries,
minLength: 3
});
jQuery( "td.state input" ).autocomplete({
source: availableStates,
minLength: 3
});
});
</script>
<?php
include( 'html-settings-tax.php' );
}
/**
@ -510,7 +120,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
private function get_current_tax_class() {
global $current_section;
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
$current_class = '';
foreach( $tax_classes as $class ) {
@ -523,154 +133,14 @@ class WC_Settings_Tax extends WC_Settings_Page {
}
/**
* Delete a tax rate from the database
* @param int $tax_rate_id
* Get a posted tax rate
* @param string $key Key of tax rate in the post data array
* @param int $order Position/order of rate
* @param string $class Tax class for rate
* @return array
*/
private function delete_tax_rate( $tax_rate_id ) {
global $wpdb;
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d;", $tax_rate_id ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $tax_rate_id ) );
do_action( 'woocommerce_tax_rate_deleted', $tax_rate_id );
}
/**
* format the state
* @param string $state
* @return string
*/
private function tax_rate_state( $state ) {
$state = strtoupper( $state );
return $state === '*' ? '' : $state;
}
/**
* format the country
* @param string $state
* @return string
*/
private function tax_rate_country( $country ) {
$country = strtoupper( $country );
return $country === '*' ? '' : $country;
}
/**
* format the tax rate name
* @param string $state
* @return string
*/
private function tax_rate_name( $name ) {
return $name ? $name : __( 'Tax', 'woocommerce' );
}
/**
* format the rate
* @param string $state
* @return float
*/
private function format_tax_rate( $rate ) {
return number_format( (double) $rate, 4, '.', '' );
}
/**
* format the city
* @param string $state
* @return string
*/
private function format_tax_rate_city( $city ) {
return strtoupper( $city );
}
/**
* format the postcodes
* @param string $state
* @return string
*/
private function format_tax_rate_postcode( $postcode ) {
return strtoupper( $postcode );
}
/**
* Update postcodes for a tax rate in the DB
* @param int $tax_rate_id
* @param string $postcodes
* @return string
*/
private function update_tax_rate_postcodes( $tax_rate_id, $postcodes ) {
global $wpdb;
// Delete old
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = 'postcode';", $tax_rate_id ) );
// Add changed
$postcodes = array_filter( explode( ';', $postcodes ) );
$postcode_query = array();
foreach( $postcodes as $postcode ) {
if ( strstr( $postcode, '-' ) ) {
$postcode_parts = explode( '-', $postcode );
if ( is_numeric( $postcode_parts[0] ) && is_numeric( $postcode_parts[1] ) && $postcode_parts[1] > $postcode_parts[0] ) {
for ( $i = $postcode_parts[0]; $i <= $postcode_parts[1]; $i ++ ) {
if ( ! $i ) {
continue;
}
if ( strlen( $i ) < strlen( $postcode_parts[0] ) ) {
$i = str_pad( $i, strlen( $postcode_parts[0] ), "0", STR_PAD_LEFT );
}
$postcode_query[] = "( '" . esc_sql( $i ) . "', $tax_rate_id, 'postcode' )";
}
}
} elseif ( $postcode ) {
$postcode_query[] = "( '" . esc_sql( $postcode ) . "', $tax_rate_id, 'postcode' )";
}
}
if ( ! empty( $postcode_query ) ) {
$wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $postcode_query ) );
}
}
/**
* Update cities for a tax rate in the DB
* @param int $tax_rate_id
* @param string $cities
* @return string
*/
private function update_tax_rate_cities( $tax_rate_id, $cities ) {
global $wpdb;
// Delete old
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = 'city';", $tax_rate_id ) );
// Add changed
$cities = array_filter( explode( ';', $cities ) );
$city_query = array();
foreach( $cities as $city ) {
$city_query[] = "( '" . esc_sql( $city ) . "', $tax_rate_id, 'city' )";
}
if ( ! empty( $city_query ) ) {
$wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES " . implode( ',', $city_query ) );
}
}
/**
* Save tax rates
*/
public function save_tax_rates() {
global $wpdb;
if ( empty( $_POST['tax_rate_country'] ) ) {
return;
}
$current_class = sanitize_title( $this->get_current_tax_class() );
$i = 0;
private function get_posted_tax_rate( $key, $order, $class ) {
$tax_rate = array();
$tax_rate_keys = array(
'tax_rate_country',
'tax_rate_state',
@ -679,57 +149,47 @@ class WC_Settings_Tax extends WC_Settings_Page {
'tax_rate_priority'
);
foreach ( $tax_rate_keys as $tax_rate_key ) {
if ( isset( $_POST[ $tax_rate_key ] ) && isset( $_POST[ $tax_rate_key ][ $key ] ) ) {
$tax_rate[ $tax_rate_key ] = wc_clean( $_POST[ $tax_rate_key ][ $key ] );
}
}
$tax_rate['tax_rate_compound'] = isset( $_POST['tax_rate_compound'][ $key ] ) ? 1 : 0;
$tax_rate['tax_rate_shipping'] = isset( $_POST['tax_rate_shipping'][ $key ] ) ? 1 : 0;
$tax_rate['tax_rate_order'] = $order;
$tax_rate['tax_rate_class'] = $class;
return $tax_rate;
}
/**
* Save tax rates
*/
public function save_tax_rates() {
$current_class = sanitize_title( $this->get_current_tax_class() );
$index = 0;
// Loop posted fields
foreach ( $_POST['tax_rate_country'] as $key => $value ) {
$mode = 0 === strpos( $key, 'new-' ) ? 'insert' : 'update';
$_tax_rate = array();
foreach ( $tax_rate_keys as $tax_rate_key ) {
if ( isset( $_POST[ $tax_rate_key ] ) && isset( $_POST[ $tax_rate_key ][ $key ] ) ) {
$_tax_rate[ $tax_rate_key ] = wc_clean( $_POST[ $tax_rate_key ][ $key ] );
if ( method_exists( $this, 'format_' . $tax_rate_key ) ) {
$_tax_rate[ $tax_rate_key ] = call_user_func( array( $this, 'format_' . $tax_rate_key ), $_tax_rate[ $tax_rate_key ] );
}
}
}
$_tax_rate['tax_rate_compound'] = isset( $_POST['tax_rate_compound'][ $key ] ) ? 1 : 0;
$_tax_rate['tax_rate_shipping'] = isset( $_POST['tax_rate_shipping'][ $key ] ) ? 1 : 0;
$_tax_rate['tax_rate_order'] = $i ++;
$_tax_rate['tax_rate_class'] = $current_class;
$mode = 0 === strpos( $key, 'new-' ) ? 'insert' : 'update';
$tax_rate = $this->get_posted_tax_rate( $key, $index ++, $current_class );
if ( 'insert' === $mode ) {
$wpdb->insert( $wpdb->prefix . 'woocommerce_tax_rates', $_tax_rate );
$tax_rate_id = $wpdb->insert_id;
do_action( 'woocommerce_tax_rate_added', $tax_rate_id, $_tax_rate );
$tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
} elseif ( 1 == $_POST['remove_tax_rate'][ $key ] ) {
WC_Tax::_delete_tax_rate( $key );
continue;
} else {
$tax_rate_id = absint( $key );
if ( 1 == $_POST['remove_tax_rate'][ $key ] ) {
$this->delete_tax_rate( $tax_rate_id );
continue;
}
$wpdb->update(
$wpdb->prefix . "woocommerce_tax_rates",
$_tax_rate,
array(
'tax_rate_id' => $tax_rate_id
)
);
do_action( 'woocommerce_tax_rate_updated', $tax_rate_id, $_tax_rate );
$tax_rate_id = $key;
WC_Tax::_update_tax_rate( $tax_rate_id, $tax_rate );
}
if ( isset( $_POST['tax_rate_postcode'][ $key ] ) ) {
$this->update_tax_rate_postcodes( $tax_rate_id, $this->format_tax_rate_postcode( $_POST['tax_rate_postcode'][ $key ] ) );
WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, wc_clean( $_POST['tax_rate_postcode'][ $key ] ) );
}
if ( isset( $_POST['tax_rate_city'][ $key ] ) ) {
$this->update_tax_rate_cities( $tax_rate_id, $this->format_tax_rate_city( $_POST['tax_rate_city'][ $key ] ) );
WC_Tax::_update_tax_rate_cities( $tax_rate_id, wc_clean( $_POST['tax_rate_city'][ $key ] ) );
}
}
}

View File

@ -0,0 +1,262 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<h3><?php printf( __( 'Tax Rates for the "%s" Class', 'woocommerce' ), $current_class ? esc_html( $current_class ) : __( 'Standard', 'woocommerce' ) ); ?></h3>
<p><?php printf( __( 'Define tax rates for countries and states below. <a href="%s">See here</a> for available alpha-2 country codes.', 'woocommerce' ), 'http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes' ); ?></p>
<table class="wc_tax_rates wc_input_table sortable widefat">
<thead>
<tr>
<th class="sort">&nbsp;</th>
<th width="8%"><?php _e( 'Country&nbsp;Code', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('A 2 digit country code, e.g. US. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'State&nbsp;Code', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('A 2 digit state code, e.g. AL. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th><?php _e( 'ZIP/Postcode', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Postcode for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all areas. Wildcards (*) can be used. Ranges for numeric postcodes (e.g. 12345-12350) will be expanded into individual postcodes.', 'woocommerce'); ?>">[?]</span></th>
<th><?php _e( 'City', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Cities for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all cities.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Rate&nbsp;%', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e( 'Enter a tax rate (percentage) to 4 decimal places.', 'woocommerce' ); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Tax&nbsp;Name', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Enter a name for this tax rate.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Priority', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Choose a priority for this tax rate. Only 1 matching rate per priority will be used. To define multiple tax rates for a single area you need to specify a different priority per rate.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Compound', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Choose whether or not this is a compound rate. Compound tax rates are applied on top of other tax rates.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Shipping', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php _e('Choose whether or not this tax rate also gets applied to shipping.', 'woocommerce'); ?>">[?]</span></th>
</tr>
</thead>
<tbody id="rates">
<?php
$rates = $wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE tax_rate_class = %s
ORDER BY tax_rate_order
LIMIT %d, %d
" ,
sanitize_title( $current_class ),
( $page - 1 ) * $limit,
$limit
) );
foreach ( $rates as $rate ) {
?>
<tr class="tips" data-tip="<?php echo __( 'Tax rate ID', 'woocommerce' ) . ': ' . $rate->tax_rate_id; ?>">
<td class="sort"><input type="hidden" class="remove_tax_rate" name="remove_tax_rate[<?php echo $rate->tax_rate_id ?>]" value="0" /></td>
<td class="country" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_country ) ?>" placeholder="*" name="tax_rate_country[<?php echo $rate->tax_rate_id ?>]" class="wc_input_country_iso" />
</td>
<td class="state" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_state ) ?>" placeholder="*" name="tax_rate_state[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="postcode">
<input type="text" value="<?php
$locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='postcode' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
echo esc_attr( implode( '; ', $locations ) );
?>" placeholder="*" data-name="tax_rate_postcode[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="city">
<input type="text" value="<?php
$locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='city' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
echo esc_attr( implode( '; ', $locations ) );
?>" placeholder="*" data-name="tax_rate_city[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="rate" width="8%">
<input type="number" step="any" min="0" value="<?php echo esc_attr( $rate->tax_rate ) ?>" placeholder="0" name="tax_rate[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="name" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_name ) ?>" name="tax_rate_name[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="priority" width="8%">
<input type="number" step="1" min="1" value="<?php echo esc_attr( $rate->tax_rate_priority ) ?>" name="tax_rate_priority[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="compound" width="8%">
<input type="checkbox" class="checkbox" name="tax_rate_compound[<?php echo $rate->tax_rate_id ?>]" <?php checked( $rate->tax_rate_compound, '1' ); ?> />
</td>
<td class="apply_to_shipping" width="8%">
<input type="checkbox" class="checkbox" name="tax_rate_shipping[<?php echo $rate->tax_rate_id ?>]" <?php checked($rate->tax_rate_shipping, '1' ); ?> />
</td>
</tr>
<?php
}
?>
</tbody>
<tfoot>
<tr>
<th colspan="10">
<a href="#" class="button plus insert"><?php _e( 'Insert row', 'woocommerce' ); ?></a>
<a href="#" class="button minus remove_tax_rates"><?php _e( 'Remove selected row(s)', 'woocommerce' ); ?></a>
<div class="pagination">
<?php
echo str_replace( 'page-numbers', 'page-numbers button', paginate_links( array(
'base' => add_query_arg( 'p', '%#%' ),
'type' => 'plain',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'total' => ceil( absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(tax_rate_id) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s;", sanitize_title( $current_class ) ) ) ) / $limit ),
'current' => $page
) ) );
?>
</div>
<a href="#" download="tax_rates.csv" class="button export"><?php _e( 'Export CSV', 'woocommerce' ); ?></a>
<a href="<?php echo admin_url( 'admin.php?import=woocommerce_tax_rate_csv' ); ?>" class="button import"><?php _e( 'Import CSV', 'woocommerce' ); ?></a>
</th>
</tr>
</tfoot>
</table>
<script type="text/javascript">
jQuery( function() {
jQuery('.wc_tax_rates .remove_tax_rates').click(function() {
var $tbody = jQuery('.wc_tax_rates').find('tbody');
if ( $tbody.find('tr.current').size() > 0 ) {
$current = $tbody.find('tr.current');
$current.find('input').val('');
$current.find('input.remove_tax_rate').val('1');
$current.each(function(){
if ( jQuery(this).is('.new') )
jQuery(this).remove();
else
jQuery(this).hide();
});
} else {
alert('<?php echo esc_js( __( 'No row(s) selected', 'woocommerce' ) ); ?>');
}
return false;
});
jQuery('.wc_tax_rates .export').click(function() {
var csv_data = "data:application/csv;charset=utf-8,<?php _e( 'Country Code', 'woocommerce' ); ?>,<?php _e( 'State Code', 'woocommerce' ); ?>,<?php _e( 'ZIP/Postcode', 'woocommerce' ); ?>,<?php _e( 'City', 'woocommerce' ); ?>,<?php _e( 'Rate %', 'woocommerce' ); ?>,<?php _e( 'Tax Name', 'woocommerce' ); ?>,<?php _e( 'Priority', 'woocommerce' ); ?>,<?php _e( 'Compound', 'woocommerce' ); ?>,<?php _e( 'Shipping', 'woocommerce' ); ?>,<?php _e( 'Tax Class', 'woocommerce' ); ?>\n";
jQuery('#rates tr:visible').each(function() {
var row = '';
jQuery(this).find('td:not(.sort) input').each(function() {
if ( jQuery(this).is('.checkbox') ) {
if ( jQuery(this).is(':checked') ) {
val = 1;
} else {
val = 0;
}
} else {
var val = jQuery(this).val();
if ( ! val )
val = jQuery(this).attr('placeholder');
}
row = row + val + ',';
});
row = row + '<?php echo $current_class; ?>';
//row.substring( 0, row.length - 1 );
csv_data = csv_data + row + "\n";
});
jQuery(this).attr( 'href', encodeURI( csv_data ) );
return true;
});
jQuery('.wc_tax_rates .insert').click(function() {
var $tbody = jQuery('.wc_tax_rates').find('tbody');
var size = $tbody.find('tr').size();
var code = '<tr class="new">\
<td class="sort">&nbsp;</td>\
<td class="country" width="8%">\
<input type="text" placeholder="*" name="tax_rate_country[new-' + size + ']" class="wc_input_country_iso" />\
</td>\
<td class="state" width="8%">\
<input type="text" placeholder="*" name="tax_rate_state[new-' + size + ']" />\
</td>\
<td class="postcode">\
<input type="text" placeholder="*" name="tax_rate_postcode[new-' + size + ']" />\
</td>\
<td class="city">\
<input type="text" placeholder="*" name="tax_rate_city[new-' + size + ']" />\
</td>\
<td class="rate" width="8%">\
<input type="number" step="any" min="0" placeholder="0" name="tax_rate[new-' + size + ']" />\
</td>\
<td class="name" width="8%">\
<input type="text" name="tax_rate_name[new-' + size + ']" />\
</td>\
<td class="priority" width="8%">\
<input type="number" step="1" min="1" value="1" name="tax_rate_priority[new-' + size + ']" />\
</td>\
<td class="compound" width="8%">\
<input type="checkbox" class="checkbox" name="tax_rate_compound[new-' + size + ']" />\
</td>\
<td class="apply_to_shipping" width="8%">\
<input type="checkbox" class="checkbox" name="tax_rate_shipping[new-' + size + ']" checked="checked" />\
</td>\
</tr>';
if ( $tbody.find('tr.current').size() > 0 ) {
$tbody.find('tr.current').after( code );
} else {
$tbody.append( code );
}
jQuery( "td.country input" ).autocomplete({
source: availableCountries,
minLength: 3
});
jQuery( "td.state input" ).autocomplete({
source: availableStates,
minLength: 3
});
return false;
});
jQuery('.wc_tax_rates td.postcode, .wc_tax_rates td.city').find('input').change(function() {
jQuery(this).attr( 'name', jQuery(this).attr( 'data-name' ) );
});
var availableCountries = [<?php
$countries = array();
foreach ( WC()->countries->get_allowed_countries() as $value => $label )
$countries[] = '{ label: "' . esc_attr( $label ) . '", value: "' . $value . '" }';
echo implode( ', ', $countries );
?>];
var availableStates = [<?php
$countries = array();
foreach ( WC()->countries->get_allowed_country_states() as $value => $label )
foreach ( $label as $code => $state )
$countries[] = '{ label: "' . esc_attr( $state ) . '", value: "' . $code . '" }';
echo implode( ', ', $countries );
?>];
jQuery( "td.country input" ).autocomplete({
source: availableCountries,
minLength: 3
});
jQuery( "td.state input" ).autocomplete({
source: availableStates,
minLength: 3
});
});
</script>

View File

@ -0,0 +1,129 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
return apply_filters( 'woocommerce_tax_settings', array(
array( 'title' => __( 'Tax Options', 'woocommerce' ), 'type' => 'title','desc' => '', 'id' => 'tax_options' ),
array(
'title' => __( 'Enable Taxes', 'woocommerce' ),
'desc' => __( 'Enable taxes and tax calculations', 'woocommerce' ),
'id' => 'woocommerce_calc_taxes',
'default' => 'no',
'type' => 'checkbox'
),
array(
'title' => __( 'Prices Entered With Tax', 'woocommerce' ),
'id' => 'woocommerce_prices_include_tax',
'default' => 'no',
'type' => 'radio',
'desc_tip' => __( 'This option is important as it will affect how you input prices. Changing it will not update existing products.', 'woocommerce' ),
'options' => array(
'yes' => __( 'Yes, I will enter prices inclusive of tax', 'woocommerce' ),
'no' => __( 'No, I will enter prices exclusive of tax', 'woocommerce' )
),
),
array(
'title' => __( 'Calculate Tax Based On:', 'woocommerce' ),
'id' => 'woocommerce_tax_based_on',
'desc_tip' => __( 'This option determines which address is used to calculate tax.', 'woocommerce' ),
'default' => 'shipping',
'type' => 'select',
'options' => array(
'shipping' => __( 'Customer shipping address', 'woocommerce' ),
'billing' => __( 'Customer billing address', 'woocommerce' ),
'base' => __( 'Shop base address', 'woocommerce' )
),
),
array(
'title' => __( 'Default Customer Address:', 'woocommerce' ),
'id' => 'woocommerce_default_customer_address',
'desc_tip' => __( 'This option determines the customers default address (before they input their own).', 'woocommerce' ),
'default' => 'base',
'type' => 'select',
'options' => array(
'' => __( 'No address', 'woocommerce' ),
'base' => __( 'Shop base address', 'woocommerce' ),
),
),
array(
'title' => __( 'Shipping Tax Class:', 'woocommerce' ),
'desc' => __( 'Optionally control which tax class shipping gets, or leave it so shipping tax is based on the cart items themselves.', 'woocommerce' ),
'id' => 'woocommerce_shipping_tax_class',
'css' => 'min-width:150px;',
'default' => 'title',
'type' => 'select',
'options' => array( '' => __( 'Shipping tax class based on cart items', 'woocommerce' ), 'standard' => __( 'Standard', 'woocommerce' ) ) + $classes_options,
'desc_tip' => true,
),
array(
'title' => __( 'Rounding', 'woocommerce' ),
'desc' => __( 'Round tax at subtotal level, instead of rounding per line', 'woocommerce' ),
'id' => 'woocommerce_tax_round_at_subtotal',
'default' => 'no',
'type' => 'checkbox',
),
array(
'title' => __( 'Additional Tax Classes', 'woocommerce' ),
'desc' => __( 'List additional tax classes below (1 per line). This is in addition to the default <code>Standard Rate</code>. Tax classes can be assigned to products.', 'woocommerce' ),
'id' => 'woocommerce_tax_classes',
'css' => 'width:100%; height: 65px;',
'type' => 'textarea',
'default' => sprintf( __( 'Reduced Rate%sZero Rate', 'woocommerce' ), PHP_EOL )
),
array(
'title' => __( 'Display prices in the shop:', 'woocommerce' ),
'id' => 'woocommerce_tax_display_shop',
'default' => 'excl',
'type' => 'select',
'options' => array(
'incl' => __( 'Including tax', 'woocommerce' ),
'excl' => __( 'Excluding tax', 'woocommerce' ),
)
),
array(
'title' => __( 'Price display suffix:', 'woocommerce' ),
'id' => 'woocommerce_price_display_suffix',
'default' => '',
'type' => 'text',
'desc' => __( 'Define text to show after your product prices. This could be, for example, "inc. Vat" to explain your pricing. You can also have prices substituted here using one of the following: <code>{price_including_tax}, {price_excluding_tax}</code>.', 'woocommerce' ),
),
array(
'title' => __( 'Display prices during cart/checkout:', 'woocommerce' ),
'id' => 'woocommerce_tax_display_cart',
'default' => 'excl',
'type' => 'select',
'options' => array(
'incl' => __( 'Including tax', 'woocommerce' ),
'excl' => __( 'Excluding tax', 'woocommerce' ),
),
'autoload' => false
),
array(
'title' => __( 'Display tax totals:', 'woocommerce' ),
'id' => 'woocommerce_tax_total_display',
'default' => 'itemized',
'type' => 'select',
'options' => array(
'single' => __( 'As a single total', 'woocommerce' ),
'itemized' => __( 'Itemized', 'woocommerce' ),
),
'autoload' => false
),
array( 'type' => 'sectionend', 'id' => 'tax_options' ),
) );

View File

@ -35,8 +35,8 @@ if ( ! defined( 'ABSPATH' ) ) {
</select>
</span>
</label>
<label class="alignright">
<input type="text" name="_regular_price" class="text regular_price" placeholder="<?php _e( 'Enter price', 'woocommerce' ); ?>" value="" />
<label class="change-input">
<input type="text" name="_regular_price" class="text regular_price" placeholder="<?php echo sprintf( __( 'Enter price (%s)', 'woocommerce' ), get_woocommerce_currency_symbol() ); ?>" value="" />
</label>
</div>
@ -60,8 +60,8 @@ if ( ! defined( 'ABSPATH' ) ) {
</select>
</span>
</label>
<label class="alignright">
<input type="text" name="_sale_price" class="text sale_price" placeholder="<?php _e( 'Enter price', 'woocommerce' ); ?>" value="" />
<label class="change-input">
<input type="text" name="_sale_price" class="text sale_price" placeholder="<?php echo sprintf( __( 'Enter sale price (%s)', 'woocommerce' ), get_woocommerce_currency_symbol() ); ?>" value="" />
</label>
</div>
@ -94,7 +94,7 @@ if ( ! defined( 'ABSPATH' ) ) {
'standard' => __( 'Standard', 'woocommerce' )
);
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
if ( $tax_classes )
foreach ( $tax_classes as $class ) {
@ -127,8 +127,8 @@ if ( ! defined( 'ABSPATH' ) ) {
</select>
</span>
</label>
<label class="alignright">
<input type="text" name="_weight" class="text weight" placeholder="0.00" value="">
<label class="change-input">
<input type="text" name="_weight" class="text weight" placeholder="<?php echo sprintf( __( '0.00 (%s)', 'woocommerce' ), get_option( 'woocommerce_weight_unit' ) ); ?>" value="">
</label>
</div>
<?php endif; ?>
@ -151,11 +151,11 @@ if ( ! defined( 'ABSPATH' ) ) {
</select>
</span>
</label>
<div class="alignright">
<input type="text" name="_length" class="text length" placeholder="<?php _e( 'Length', 'woocommerce' ); ?>" value="">
<input type="text" name="_width" class="text width" placeholder="<?php _e( 'Width', 'woocommerce' ); ?>" value="">
<input type="text" name="_height" class="text height" placeholder="<?php _e( 'Height', 'woocommerce' ); ?>" value="">
</div>
<label class="change-input">
<input type="text" name="_length" class="text length" placeholder="<?php echo sprintf( __( 'Length (%s)', 'woocommerce' ), get_option( 'woocommerce_dimension_unit' ) ); ?>" value="">
<input type="text" name="_width" class="text width" placeholder="<?php echo sprintf( __( 'Width (%s)', 'woocommerce' ), get_option( 'woocommerce_dimension_unit' ) ); ?>" value="">
<input type="text" name="_height" class="text height" placeholder="<?php echo sprintf( __( 'Height (%s)', 'woocommerce' ), get_option( 'woocommerce_dimension_unit' ) ); ?>" value="">
</label>
</div>
<?php endif; ?>
@ -265,8 +265,8 @@ if ( ! defined( 'ABSPATH' ) ) {
</select>
</span>
</label>
<label class="alignright">
<input type="number" name="_stock" class="text stock" placeholder="<?php _e( 'Stock Qty', 'woocommerce' ); ?>" step="any" value="">
<label class="change-input">
<input type="text" name="_stock" class="text stock" placeholder="<?php _e( 'Stock Qty', 'woocommerce' ); ?>" step="any" value="">
</label>
</div>

View File

@ -72,7 +72,7 @@ if ( ! defined( 'ABSPATH' ) ) {
'' => __( 'Standard', 'woocommerce' )
);
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
$tax_classes = WC_Tax::get_tax_classes();
if ( $tax_classes )
foreach ( $tax_classes as $class ) {

View File

@ -585,7 +585,7 @@ class WC_AJAX {
$attributes = (array) maybe_unserialize( get_post_meta( $post_id, '_product_attributes', true ) );
// Get tax classes
$tax_classes = array_filter(array_map('trim', explode("\n", get_option('woocommerce_tax_classes'))));
$tax_classes = WC_Tax::get_tax_classes();
$tax_class_options = array();
$tax_class_options['parent'] =__( 'Same as parent', 'woocommerce' );
$tax_class_options[''] = __( 'Standard', 'woocommerce' );

View File

@ -768,6 +768,20 @@ class WC_Cart {
return apply_filters( 'woocommerce_cart_tax_totals', $tax_totals, $this );
}
/**
* Get all tax classes for items in the cart
* @return array
*/
public function get_cart_item_tax_classes() {
$found_tax_classes = array();
foreach ( WC()->cart->get_cart() as $item ) {
$found_tax_classes[] = $item['data']->get_tax_class();
}
return array_unique( $found_tax_classes );
}
/*-----------------------------------------------------------------------------------*/
/* Add to cart handling */
/*-----------------------------------------------------------------------------------*/

View File

@ -219,7 +219,7 @@ class WC_Product_Variation extends WC_Product {
$visible = false;
}
return apply_filters( 'woocommerce_variation_is_visible', $visible, $this->variation_id, $this->id );
return apply_filters( 'woocommerce_variation_is_visible', $visible, $this->variation_id, $this->id, $this );
}
/**
@ -230,7 +230,7 @@ class WC_Product_Variation extends WC_Product {
* @return bool
*/
public function variation_is_active() {
return apply_filters( 'woocommerce_variation_is_active', true, $this->variation_id, $this->id );
return apply_filters( 'woocommerce_variation_is_active', true, $this );
}
/**
@ -284,7 +284,7 @@ class WC_Product_Variation extends WC_Product {
public function has_all_attributes_set() {
$set = true;
// undefined attributes have null strings as array values
foreach( $this->get_variation_attributes() as $att ){
if( ! $att ){
@ -296,7 +296,7 @@ class WC_Product_Variation extends WC_Product {
return $set;
}
/**
* Get variation price HTML. Prices are not inherited from parents.
*

View File

@ -197,22 +197,17 @@ class WC_Tax {
/**
* Searches for all matching country/state/postcode tax rates.
*
* @access public
* @param array $args
* @return array
*/
public static function find_rates( $args = array() ) {
global $wpdb;
$defaults = array(
'country' => '',
'state' => '',
'city' => '',
'postcode' => '',
'tax_class' => ''
);
$args = wp_parse_args( $args, $defaults );
$args = wp_parse_args( $args, array(
'country' => '',
'state' => '',
'city' => '',
'postcode' => '',
'tax_class' => ''
) );
extract( $args, EXTR_SKIP );
@ -220,28 +215,53 @@ class WC_Tax {
return array();
}
// Handle postcodes
$valid_postcodes = array( '*', strtoupper( wc_clean( $postcode ) ) );
// Work out possible valid wildcard postcodes
$postcode_length = strlen( $postcode );
$wildcard_postcode = strtoupper( wc_clean( $postcode ) );
for ( $i = 0; $i < $postcode_length; $i ++ ) {
$wildcard_postcode = substr( $wildcard_postcode, 0, -1 );
$valid_postcodes[] = $wildcard_postcode . '*';
}
// Build transient key and try to retrieve them from cache
$valid_postcodes = self::_get_wildcard_postcodes( wc_clean( $postcode ) );
$rates_transient_key = 'wc_tax_rates_' . md5( sprintf( '%s+%s+%s+%s+%s', $country, $state, $city, implode( ',', $valid_postcodes), $tax_class ) );
$matched_tax_rates = get_transient( $rates_transient_key );
$matched_tax_rates = get_transient( $rates_transient_key );
if ( false === $matched_tax_rates ) {
$matched_tax_rates = self::get_matched_tax_rates( $country, $state, $postcode, $city, $tax_class, $valid_postcodes );
set_transient( $rates_transient_key, $matched_tax_rates, WEEK_IN_SECONDS );
}
// Run the query
$found_rates = $wpdb->get_results( $wpdb->prepare( "
return $matched_tax_rates;
}
/**
* Searches for all matching country/state/postcode tax rates.
*
* @param array $args
* @return array
*/
public static function find_shipping_rates( $args = array() ) {
$rates = self::find_rates( $args );
$shipping_rates = array();
foreach ( $rates as $key => $rate ) {
if ( 'yes' === $rate['shipping'] ) {
$shipping_rates[ $key ] = $rate;
}
}
return $shipping_rates;
}
/**
* Loop through a set of tax rates and get the matching rates (1 per priority)
*
* @param string $country
* @param string $state
* @param string $postcode
* @param string $city
* @param string $tax_class
* @param array $valid_postcodes
* @return array
*/
private static function get_matched_tax_rates( $country, $state, $postcode, $city, $tax_class, $valid_postcodes ) {
global $wpdb;
$found_rates = $wpdb->get_results(
$wpdb->prepare( "
SELECT tax_rates.*
FROM {$wpdb->prefix}woocommerce_tax_rates as tax_rates
LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations ON tax_rates.tax_rate_id = locations.tax_rate_id
@ -281,32 +301,47 @@ class WC_Tax {
sanitize_title( $tax_class ),
strtoupper( $city ),
strtoupper( $city )
) );
)
);
$matched_tax_rates = array();
$found_priority = array();
$matched_tax_rates = array();
$found_priority = array();
foreach ( $found_rates as $found_rate ) {
if ( in_array( $found_rate->tax_rate_priority, $found_priority ) ) {
continue;
}
$matched_tax_rates[ $found_rate->tax_rate_id ] = array(
'rate' => $found_rate->tax_rate,
'label' => $found_rate->tax_rate_name,
'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no',
'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no'
);
$found_priority[] = $found_rate->tax_rate_priority;
foreach ( $found_rates as $found_rate ) {
if ( in_array( $found_rate->tax_rate_priority, $found_priority ) ) {
continue;
}
$matched_tax_rates = apply_filters( 'woocommerce_matched_tax_rates', $matched_tax_rates, $country, $state, $postcode, $city, $tax_class );
$matched_tax_rates[ $found_rate->tax_rate_id ] = array(
'rate' => $found_rate->tax_rate,
'label' => $found_rate->tax_rate_name,
'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no',
'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no'
);
set_transient( $rates_transient_key, $matched_tax_rates, DAY_IN_SECONDS );
$found_priority[] = $found_rate->tax_rate_priority;
}
return $matched_tax_rates;
return apply_filters( 'woocommerce_matched_tax_rates', $matched_tax_rates, $country, $state, $postcode, $city, $tax_class );
}
/**
* Get the customer tax location based on their status and the current page
* @return array
*/
public static function get_customer_location() {
if ( defined( 'WOOCOMMERCE_CHECKOUT' ) || ( ! empty( WC()->customer ) && WC()->customer->has_calculated_shipping() ) ) {
return WC()->customer->get_taxable_address();
} elseif ( wc_prices_include_tax() || get_option( 'woocommerce_default_customer_address' ) == 'base' ) {
return array(
WC()->countries->get_base_country(),
WC()->countries->get_base_state(),
WC()->countries->get_base_postcode(),
WC()->countries->get_base_city()
);
} else {
return array();
}
}
/**
@ -315,13 +350,12 @@ class WC_Tax {
* @return array
*/
public static function get_rates( $tax_class = '' ) {
$tax_class = sanitize_title( $tax_class );
$location = self::get_customer_location();
$matched_tax_rates = array();
$tax_class = sanitize_title( $tax_class );
/* Checkout uses customer location for the tax rates. Also, if shipping has been calculated, use the customers address. */
if ( ( defined('WOOCOMMERCE_CHECKOUT') && WOOCOMMERCE_CHECKOUT ) || ( ! empty( WC()->customer ) && WC()->customer->has_calculated_shipping() ) ) {
list( $country, $state, $postcode, $city ) = WC()->customer->get_taxable_address();
if ( sizeof( $location ) === 4 ) {
list( $country, $state, $postcode, $city ) = $location;
$matched_tax_rates = self::find_rates( array(
'country' => $country,
@ -330,16 +364,6 @@ class WC_Tax {
'city' => $city,
'tax_class' => $tax_class
) );
} else {
// Prices which include tax should always use the base rate if we don't know where the user is located
// 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 = wc_prices_include_tax() || get_option( 'woocommerce_default_customer_address' ) == 'base'
? self::get_base_tax_rates( $tax_class )
: array();
}
return apply_filters( 'woocommerce_matched_rates', $matched_tax_rates, $tax_class );
@ -379,97 +403,39 @@ class WC_Tax {
* @return mixed
*/
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' ) ) {
$tax_class = $shipping_tax_class == 'standard' ? '' : $shipping_tax_class;
$tax_class = 'standard' === $shipping_tax_class ? '' : $shipping_tax_class;
}
if ( ( defined('WOOCOMMERCE_CHECKOUT') && WOOCOMMERCE_CHECKOUT ) || ( ! empty( WC()->customer ) && WC()->customer->has_calculated_shipping() ) ) {
$location = self::get_customer_location();
$matched_tax_rates = array();
list( $country, $state, $postcode, $city ) = WC()->customer->get_taxable_address();
if ( sizeof( $location ) === 4 ) {
list( $country, $state, $postcode, $city ) = $location;
} else {
// Prices which include tax should always use the base rate if we don't know where the user is located
// Prices excluding tax however should just not add any taxes, as they will be added during checkout
if ( wc_prices_include_tax() || get_option( 'woocommerce_default_customer_address' ) == 'base' ) {
$country = WC()->countries->get_base_country();
$state = WC()->countries->get_base_state();
$postcode = '';
$city = '';
} else {
return array();
}
}
// If we are here then shipping is taxable - work it out
if ( ! is_null( $tax_class ) ) {
$matched_tax_rates = array();
// This will be per item shipping
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class
) );
if ( $rates )
foreach ( $rates as $key => $rate )
if ( isset( $rate['shipping'] ) && $rate['shipping'] == 'yes' )
$matched_tax_rates[ $key ] = $rate;
if ( sizeof( $matched_tax_rates ) == 0 ) {
// Get standard rate
$rates = self::find_rates( array(
if ( ! is_null( $tax_class ) ) {
// This will be per item shipping
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'city' => $city,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $tax_class
) );
if ( $rates )
foreach ( $rates as $key => $rate )
if ( isset( $rate['shipping'] ) && $rate['shipping'] == 'yes' )
$matched_tax_rates[ $key ] = $rate;
}
} else {
return $matched_tax_rates;
// This will be per order shipping - loop through the order and find the highest tax class rate
$cart_tax_classes = WC()->cart->get_cart_item_tax_classes();
} else {
// This will be per order shipping - loop through the order and find the highest tax class rate
$found_tax_classes = array();
$matched_tax_rates = array();
$rates = false;
// Loop cart and find the highest tax band
if ( sizeof( WC()->cart->get_cart() ) > 0 )
foreach ( WC()->cart->get_cart() as $item )
$found_tax_classes[] = $item['data']->get_tax_class();
$found_tax_classes = array_unique( $found_tax_classes );
// If multiple classes are found, use highest
if ( sizeof( $found_tax_classes ) > 1 ) {
if ( in_array( '', $found_tax_classes ) ) {
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'city' => $city,
'postcode' => $postcode,
) );
} else {
$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
// If multiple classes are found, use highest. Don't bother with standard rate, we can get that later.
if ( sizeof( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes ) ) {
$tax_classes = self::get_tax_classes();
foreach ( $tax_classes as $tax_class ) {
if ( in_array( $tax_class, $found_tax_classes ) ) {
$rates = self::find_rates( array(
if ( in_array( $tax_class, $cart_tax_classes ) ) {
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
@ -479,39 +445,31 @@ class WC_Tax {
break;
}
}
// If a single tax class is found, use it
} elseif ( sizeof( $cart_tax_classes ) == 1 ) {
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $cart_tax_classes[0]
) );
}
// If a single tax class is found, use it
} elseif ( sizeof( $found_tax_classes ) == 1 ) {
$rates = self::find_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'tax_class' => $found_tax_classes[0]
) );
}
// If no class rate are found, use standard rates
if ( ! $rates )
$rates = self::find_rates( array(
// Get standard rate if no taxes were found
if ( ! sizeof( $matched_tax_rates ) ) {
$matched_tax_rates = self::find_shipping_rates( array(
'country' => $country,
'state' => $state,
'postcode' => $postcode,
'city' => $city,
'city' => $city
) );
if ( $rates )
foreach ( $rates as $key => $rate )
if ( isset( $rate['shipping'] ) && $rate['shipping'] == 'yes' )
$matched_tax_rates[ $key ] = $rate;
return $matched_tax_rates;
}
}
return array(); // return false
return $matched_tax_rates;
}
/**
@ -610,5 +568,290 @@ class WC_Tax {
public static function get_tax_total( $taxes ) {
return array_sum( array_map( array( __CLASS__, 'round' ), $taxes ) );
}
/**
* Get store tax classes
* @return array
*/
public static function get_tax_classes() {
return array_filter( array_map( 'trim', explode( "\n", get_option('woocommerce_tax_classes' ) ) ) );
}
/**
* format the postcodes
* @param string $state
* @return string
*/
private static function format_tax_rate_postcode( $postcode ) {
return strtoupper( trim( $postcode ) );
}
/**
* format the city
* @param string $state
* @return string
*/
private static function format_tax_rate_city( $city ) {
return strtoupper( trim( $city ) );
}
/**
* format the state
* @param string $state
* @return string
*/
private static function format_tax_rate_state( $state ) {
$state = strtoupper( $state );
return $state === '*' ? '' : $state;
}
/**
* format the country
* @param string $state
* @return string
*/
private static function format_tax_rate_country( $country ) {
$country = strtoupper( $country );
return $country === '*' ? '' : $country;
}
/**
* format the tax rate name
* @param string $state
* @return string
*/
private static function format_tax_rate_name( $name ) {
return $name ? $name : __( 'Tax', 'woocommerce' );
}
/**
* format the rate
* @param string $state
* @return float
*/
private static function format_tax_rate( $rate ) {
return number_format( (double) $rate, 4, '.', '' );
}
/**
* format the priority
* @param string $priority
* @return int
*/
private static function format_tax_rate_priority( $priority ) {
return absint( $priority );
}
/**
* format the class
* @param string $class
* @return string
*/
private static function format_tax_rate_class( $class ) {
$class = sanitize_title( $class );
return $class === 'standard' ? '' : $class;
}
/**
* Prepare and format tax rate for DB insursion
* @param array $tax_rate
* @return array
*/
private static function prepare_tax_rate( $tax_rate ) {
foreach ( $tax_rate as $key => $value ) {
if ( method_exists( __CLASS__, 'format_' . $key ) ) {
$tax_rate[ $key ] = call_user_func( array( __CLASS__, 'format_' . $key ), $value );
}
}
return $tax_rate;
}
/**
* Insert a new tax rate
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param array $tax_rate
* @return int tax rate id
*/
public static function _insert_tax_rate( $tax_rate ) {
global $wpdb;
$wpdb->insert( $wpdb->prefix . 'woocommerce_tax_rates', self::prepare_tax_rate( $tax_rate ) );
do_action( 'woocommerce_tax_rate_added', $wpdb->insert_id, $tax_rate );
return $wpdb->insert_id;
}
/**
* Update a tax rate
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param array $tax_rate
*/
public static function _update_tax_rate( $tax_rate_id, $tax_rate ) {
global $wpdb;
$tax_rate_id = absint( $tax_rate_id );
$wpdb->update(
$wpdb->prefix . "woocommerce_tax_rates",
self::prepare_tax_rate( $tax_rate ),
array(
'tax_rate_id' => $tax_rate_id
)
);
do_action( 'woocommerce_tax_rate_updated', $tax_rate_id, $tax_rate );
}
/**
* Delete a tax rate from the database
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
*/
public static function _delete_tax_rate( $tax_rate_id ) {
global $wpdb;
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d;", $tax_rate_id ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $tax_rate_id ) );
do_action( 'woocommerce_tax_rate_deleted', $tax_rate_id );
}
/**
* Update postcodes for a tax rate in the DB
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param string $postcodes String of postcodes separated by ; characters
* @return string
*/
public static function _update_tax_rate_postcodes( $tax_rate_id, $postcodes ) {
$postcodes = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_postcode' ), explode( ';', $postcodes ) ), array( '*' ) ) );
$postcodes = self::_get_expanded_numeric_ranges_from_array( $postcodes );
self::_update_tax_rate_locations( $tax_rate_id, $postcodes, 'postcode' );
}
/**
* Update cities for a tax rate in the DB
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param string $cities
* @return string
*/
public static function _update_tax_rate_cities( $tax_rate_id, $cities ) {
$cities = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_city' ), explode( ';', $cities ) ), array( '*' ) ) );
self::_update_tax_rate_locations( $tax_rate_id, $cities, 'city' );
}
/**
* Updates locations (postcode and city)
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param string $cities
* @return string
*/
private static function _update_tax_rate_locations( $tax_rate_id, $values, $type ) {
global $wpdb;
$wpdb->query(
$wpdb->prepare( "
DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s;
", $tax_rate_id, $type
)
);
if ( sizeof( $values ) > 0 ) {
$sql = "( '" . implode( "', $tax_rate_id, '" . esc_sql( $type ) . "' ),( '", array_map( 'esc_sql', $values ) ) . "', $tax_rate_id, '" . esc_sql( $type ) . "' )";
$wpdb->query( "
INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql;
" );
}
}
/**
* Expands ranges in an array (used for zipcodes). e.g. 101-105 would expand to 101, 102, 103, 104, 105
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param array $values array of values
* @return array expanded values
*/
private static function _get_expanded_numeric_ranges_from_array( $values = array() ) {
$expanded = array();
foreach ( $values as $value ) {
if ( strstr( $value, '-' ) ) {
$parts = array_map( 'absint', array_map( 'trim', explode( '-', $value ) ) );
for ( $expanded_value = $parts[0]; $expanded_value <= $parts[1]; $expanded_value ++ ) {
if ( strlen( $expanded_value ) < strlen( $parts[0] ) ) {
$expanded_value = str_pad( $expanded_value, strlen( $parts[0] ), "0", STR_PAD_LEFT );
}
$expanded[] = $expanded_value;
}
} else {
$expanded[] = trim( $value );
}
}
return array_filter( $expanded );
}
/**
* Get postcode wildcards in array format
*
* Internal use only.
*
* @since 2.3.0
* @access private
*
* @param string $postcode array of values
* @return array Array of postcodes with wildcards
*/
private static function _get_wildcard_postcodes( $postcode ) {
$postcodes = array( '*', strtoupper( $postcode ) );
$postcode_length = strlen( $postcode );
$wildcard_postcode = strtoupper( $postcode );
for ( $i = 0; $i < $postcode_length; $i ++ ) {
$wildcard_postcode = substr( $wildcard_postcode, 0, -1 );
$postcodes[] = $wildcard_postcode . '*';
}
return $postcodes;
}
}
WC_Tax::init();

View File

@ -254,12 +254,15 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$order = wc_get_order( $order_id );
if ( ! $this->can_refund_order( $order ) ) {
$this->log( 'Refund Failed: No transaction ID' );
return false;
}
WC_Gateway_Paypal_Refund::$api_username = $this->api_username;
WC_Gateway_Paypal_Refund::$api_password = $this->api_password;
WC_Gateway_Paypal_Refund::$api_signature = $this->api_signature;
include_once( 'includes/class-wc-gateway-paypal-refund.php' );
WC_Gateway_Paypal_Refund::$api_username = $this->get_option( 'api_username' );
WC_Gateway_Paypal_Refund::$api_password = $this->get_option( 'api_password' );
WC_Gateway_Paypal_Refund::$api_signature = $this->get_option( 'api_signature' );
$result = WC_Gateway_Paypal_Refund::refund_order( $order, $amount, $reason, $this->testmode );
@ -268,6 +271,8 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
return false;
}
$this->log( 'Refund Result: ' . print_r( $result, true ) );
switch ( strtolower( $result['ACK'] ) ) {
case 'success':
case 'successwithwarning':

View File

@ -53,10 +53,6 @@ class WC_Gateway_Paypal_Refund {
* @return array|wp_error The parsed response from paypal, or a WP_Error object
*/
public static function refund_order( $order, $amount = null, $reason = '', $sandbox = false ) {
if ( empty( self::$api_signature ) || empty( self::$api_username ) || empty( self::$api_password ) ) {
return new WP_Error( 'paypal-refunds', 'Missing API credentials' );
}
$response = wp_remote_post(
$sandbox ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp',
array(

View File

@ -11,7 +11,7 @@
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
// Include core functions (available in both admin and frontend)

View File

@ -1022,7 +1022,7 @@ if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) {
// Reviews tab - shows comments
if ( comments_open() ) {
$tabs['reviews'] = array(
'title' => sprintf( __( 'Reviews (%d)', 'woocommerce' ), get_comments_number( $post->ID ) ),
'title' => sprintf( __( 'Reviews (%d)', 'woocommerce' ), $product->get_rating_count() ),
'priority' => 30,
'callback' => 'comments_template'
);

View File

@ -0,0 +1,49 @@
<?php
/**
* WooCommerce Widget Functions
*
* Widget related functions and widget registration
*
* @author WooThemes
* @category Core
* @package WooCommerce/Functions
* @version 2.3.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Include widget classes
include_once( 'abstracts/abstract-wc-widget.php' );
include_once( 'widgets/class-wc-widget-cart.php' );
include_once( 'widgets/class-wc-widget-layered-nav-filters.php' );
include_once( 'widgets/class-wc-widget-layered-nav.php' );
include_once( 'widgets/class-wc-widget-price-filter.php' );
include_once( 'widgets/class-wc-widget-product-categories.php' );
include_once( 'widgets/class-wc-widget-product-search.php' );
include_once( 'widgets/class-wc-widget-product-tag-cloud.php' );
include_once( 'widgets/class-wc-widget-products.php' );
include_once( 'widgets/class-wc-widget-recent-reviews.php' );
include_once( 'widgets/class-wc-widget-recently-viewed.php' );
include_once( 'widgets/class-wc-widget-top-rated-products.php' );
/**
* Register Widgets
*
* @since 2.3.0
*/
function wc_register_widgets() {
register_widget( 'WC_Widget_Cart' );
register_widget( 'WC_Widget_Layered_Nav_Filters' );
register_widget( 'WC_Widget_Layered_Nav' );
register_widget( 'WC_Widget_Price_Filter' );
register_widget( 'WC_Widget_Product_Categories' );
register_widget( 'WC_Widget_Product_Search' );
register_widget( 'WC_Widget_Product_Tag_Cloud' );
register_widget( 'WC_Widget_Products' );
register_widget( 'WC_Widget_Recent_Reviews' );
register_widget( 'WC_Widget_Recently_Viewed' );
register_widget( 'WC_Widget_Top_Rated_Products' );
}
add_action( 'widgets_init', 'wc_register_widgets' );

View File

@ -74,5 +74,3 @@ class WC_Widget_Cart extends WC_Widget {
$this->widget_end( $args );
}
}
register_widget( 'WC_Widget_Cart' );

View File

@ -99,5 +99,3 @@ class WC_Widget_Layered_Nav_Filters extends WC_Widget {
}
}
}
register_widget( 'WC_Widget_Layered_Nav_Filters' );

View File

@ -414,5 +414,3 @@ class WC_Widget_Layered_Nav extends WC_Widget {
}
}
}
register_widget( 'WC_Widget_Layered_Nav' );

View File

@ -180,5 +180,3 @@ class WC_Widget_Price_Filter extends WC_Widget {
$this->widget_end( $args );
}
}
register_widget( 'WC_Widget_Price_Filter' );

View File

@ -236,5 +236,3 @@ class WC_Widget_Product_Categories extends WC_Widget {
$this->widget_end( $args );
}
}
register_widget( 'WC_Widget_Product_Categories' );

View File

@ -52,5 +52,3 @@ class WC_Widget_Product_Search extends WC_Widget {
$this->widget_end( $args );
}
}
register_widget( 'WC_Widget_Product_Search' );

View File

@ -73,5 +73,3 @@ class WC_Widget_Product_Tag_Cloud extends WC_Widget {
return 'product_tag';
}
}
register_widget( 'WC_Widget_Product_Tag_Cloud' );

View File

@ -189,5 +189,3 @@ class WC_Widget_Products extends WC_Widget {
echo $this->cache_widget( $args, ob_get_clean() );
}
}
register_widget( 'WC_Widget_Products' );

View File

@ -102,5 +102,3 @@ class WC_Widget_Recent_Reviews extends WC_Widget {
$this->cache_widget( $args, $content );
}
}
register_widget( 'WC_Widget_Recent_Reviews' );

View File

@ -96,5 +96,3 @@ class WC_Widget_Recently_Viewed extends WC_Widget {
echo $content;
}
}
register_widget( 'WC_Widget_Recently_Viewed' );

View File

@ -99,5 +99,3 @@ class WC_Widget_Top_Rated_Products extends WC_Widget {
$this->cache_widget( $args, $content );
}
}
register_widget( 'WC_Widget_Top_Rated_Products' );

View File

@ -137,6 +137,7 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
* Feature - Bulk edit sales schedule on variations.
* Feature - Fresh new frontend design.
* Feature - Undo link in message when removing products from the cart.
* Feature - Compatibility with Twenty Fifteen default theme.
* Refactor - Removed deprecated methods from WC_Frontend_Scripts and rewrote script registration and localization to run once.
* Refactor - Routing all email functionality through one send() method.
* Refactor - Replaced existing email css inliner with Emogrifier.

View File

@ -27,6 +27,9 @@ switch( $template ) {
echo '</div></div></div>';
get_sidebar( 'content' );
break;
case 'twentyfifteen' :
echo '</div></div>';
break;
default :
echo '</div></div>';
break;

View File

@ -26,6 +26,9 @@ switch( $template ) {
case 'twentyfourteen' :
echo '<div id="primary" class="content-area"><div id="content" role="main" class="site-content twentyfourteen"><div class="tfwc">';
break;
case 'twentyfifteen' :
echo '<div id="primary" role="main" class="content-area twentyfifteen"><div id="main" class="site-main t15wc">';
break;
default :
echo '<div id="container"><div id="content" role="main">';
break;

View File

@ -133,7 +133,6 @@ final class WooCommerce {
add_action( 'after_setup_theme', array( $this, 'include_template_functions' ), 11 );
add_action( 'init', array( $this, 'init' ), 0 );
add_action( 'init', array( 'WC_Shortcodes', 'init' ) );
add_action( 'widgets_init', array( $this, 'widget_includes' ) );
// Loaded action
do_action( 'woocommerce_loaded' );
@ -196,6 +195,7 @@ final class WooCommerce {
private function includes() {
include_once( 'includes/class-wc-autoloader.php' );
include_once( 'includes/wc-core-functions.php' );
include_once( 'includes/wc-widget-functions.php' );
include_once( 'includes/class-wc-install.php' );
include_once( 'includes/class-wc-download-handler.php' );
include_once( 'includes/class-wc-comments.php' );
@ -275,24 +275,6 @@ final class WooCommerce {
}
}
/**
* Include core widgets
*/
public function widget_includes() {
include_once( 'includes/abstracts/abstract-wc-widget.php' );
include_once( 'includes/widgets/class-wc-widget-cart.php' );
include_once( 'includes/widgets/class-wc-widget-layered-nav-filters.php' );
include_once( 'includes/widgets/class-wc-widget-layered-nav.php' );
include_once( 'includes/widgets/class-wc-widget-price-filter.php' );
include_once( 'includes/widgets/class-wc-widget-product-categories.php' );
include_once( 'includes/widgets/class-wc-widget-product-search.php' );
include_once( 'includes/widgets/class-wc-widget-product-tag-cloud.php' );
include_once( 'includes/widgets/class-wc-widget-products.php' );
include_once( 'includes/widgets/class-wc-widget-recent-reviews.php' );
include_once( 'includes/widgets/class-wc-widget-recently-viewed.php' );
include_once( 'includes/widgets/class-wc-widget-top-rated-products.php' );
}
/**
* Init WooCommerce when WordPress Initialises.
*/