I'm using the "WooCommerce Customer / Order / Coupon Export" plugin to export my orders as a CSV file.
I'm exporting the orders in a one row per item format. This means that every order has multiple lines. One line per order item.
Now I want to add some extra data to every line item. For example the author (in our case vendor) of the item.
I found a good way to add exta data to the export. It could be found in the documentation of the plugin: https://docs.woocommerce.com/document/ordercustomer-csv-export-developer-documentation/#section-14
It works but it's not exactly what I need. I could add some data to the whole order.
So every line item gets the same data.
So I tried to change the code and go through every line item. Unfortunatly with the same result.
Every line item gets the same data. In my case the data from the last item of the order.
Here's how I've changed the step 2 of the example from the official docs:
function sv_wc_csv_export_modify_row_data_example( $order_data, $order, $csv_generator ) {
// Example showing how to extract order metadata into it's own column
//$meta_key_example = is_callable( array( $order, 'get_meta' ) ) ? $order->get_meta( 'meta_key_example' ) : $order->meta_key_example;
//$meta_key_example = is_callable( array( $order, 'get_meta' ) ) ? $order->get_meta( 'meta_key_example' ) : $order->meta_key_example;
// Loop through order line items
$allItems = $order->get_items();
foreach( $allItems as $item_line ){
$item_vendor_id = get_post_field( 'post_author', $item_line->get_product_id() );
$custom_data = array(
// User/Vendor
'item_vendor_id' => $item_vendor_id,
'vendor_username' => get_the_author_meta( 'username', $item_vendor_id ),
'vendor_user_email' => get_the_author_meta( 'user_email', $item_vendor_id ),
// Address
'vendor_company' => get_the_author_meta( '_wcv_custom_settings_company_name', $item_vendor_id ),
'vendor_street' => get_the_author_meta( '_wcv_store_address1', $item_vendor_id ),
'vendor_housenumber' => get_the_author_meta( '_wcv_store_address2', $item_vendor_id ),
'vendor_zip' => get_the_author_meta( '_wcv_store_postcode', $item_vendor_id ),
'vendor_city' => get_the_author_meta( '_wcv_store_city', $item_vendor_id ),
'vendor_country' => get_the_author_meta( '_wcv_store_country', $item_vendor_id ),
// Bank
'vendor_bank_name' => get_the_author_meta( 'wcv_bank_name', $item_vendor_id ),
'vendor_bank_account_name' => get_the_author_meta( 'wcv_bank_account_name', $item_vendor_id ),
'vendor_iban' => get_the_author_meta( 'wcv_bank_iban', $item_vendor_id ),
'vendor_bic' => get_the_author_meta( 'wcv_bank_bic_swift', $item_vendor_id ),
);
}
return sv_wc_csv_export_add_custom_order_data( $order_data, $custom_data, $csv_generator );
}
add_filter( 'wc_customer_order_export_csv_order_row', 'sv_wc_csv_export_modify_row_data_example', 10, 3 );
I guess that I'm adding the data at the wrong position?!
But I couldn't figure out where the problem is.
EDIT: After the comment from #CBroe I tried to use wc_customer_order_csv_export_order_line_item.
I found an example here but that's destroying my CSV file:
/**
* Add weight as line item meta in CSV export Default format
*/
function sv_add_weight_to_csv_export_line_item( $line_item, $item, $product, $order ) {
$line_item['weight'] = $product->get_weight();
return $line_item;
}
add_filter( 'wc_customer_order_csv_export_order_line_item', 'sv_add_weight_to_csv_export_line_item', 10, 4 );
/**
* Add weight as line item meta in CSV export CSV Import format
*/
function sv_add_weight_to_csv_export_import_format( $order_data, $order ) {
$count = 1;
// add line items
foreach ( $order->get_items() as $item ) {
$product = $order->get_product_from_item( $item );
if ( ! is_object( $product ) ) {
$product = new WC_Product( 0 );
}
if ( $weight = $product->get_weight() ) {
$order_data[ "order_item_{$count}" ] .= '|weight: ' . $weight;
}
$count++;
}
return $order_data;
}
add_filter( 'wc_customer_order_csv_export_order_row', 'sv_add_weight_to_csv_export_import_format', 20, 2 );
But I'm still trying...
I found an answer. It was an example hidden in the official docs: https://github.com/skyverge/wc-plugins-snippets/blob/master/woocommerce-customer-order-coupon-export/csv/add-item-meta-to-order-export.php
/**
* Add line item meta to the Order CSV Export in Default format
* Example: add weight to the item meta data
*/
/**
* Step 1. Add weight to line item data
*
* #param array $line_item the original line item data
* #param array $item the item's order data
* #param object $product the \WC_Product object for the line
* #param object $order the \WC_Order object being exported
* #return array the updated line item data
*/
function sv_wc_csv_export_add_weight_to_line_item( $line_item, $item, $product, $order ) {
$new_item_data = array();
foreach ( $line_item as $key => $data ) {
$new_item_data[ $key ] = $data;
if ( 'sku' === $key && $product ) {
$new_item_data['weight'] = wc_format_decimal( $product->get_weight(), 2 );
}
}
return $new_item_data;
}
add_filter( 'wc_customer_order_export_csv_order_line_item', 'sv_wc_csv_export_add_weight_to_line_item', 10, 4 );
/**
* (optional) Step 2. Add a separate `item_weight` column to the default and custom formats
*
* #param array $column_headers the original column headers
* #param \CSV_Export_Generator $csv_generator the generator instance
* #return array - the updated column headers
*/
function sv_wc_csv_export_modify_column_headers_item_price( $column_headers, $csv_generator ) {
$new_headers = array();
if ( sv_wc_csv_export_is_one_row( $csv_generator ) ) {
foreach( $column_headers as $key => $column ) {
$new_headers[ $key ] = $column;
// add the item_price after the SKU column
if ( 'item_sku' === $key ) {
$new_headers['item_weight'] = 'item_weight';
}
}
} else {
return $column_headers;
}
return $new_headers;
}
add_filter( 'wc_customer_order_export_csv_order_headers', 'sv_wc_csv_export_modify_column_headers_item_price', 10, 2 );
/**
* (optional) Step 3. Add the item weight data (step 1) to the new item_weight column (step 2)
* for the Default - One Row per Item format
*
* #param array $order_data the original order data
* #param array $item the item for this row
* #return array - the updated order data
*/
function sv_wc_csv_export_order_row_one_row_per_item_weight( $order_data, $item ) {
$order_data['item_weight'] = $item['weight'];
return $order_data;
}
add_filter( 'wc_customer_order_export_csv_order_row_one_row_per_item', 'sv_wc_csv_export_order_row_one_row_per_item_weight', 10, 2 );
if ( ! function_exists( 'sv_wc_csv_export_is_one_row' ) ) :
/**
* Helper function to check the export format
*
* #param \WC_Customer_Order_CSV_Export_Generator $csv_generator the generator instance
* #return bool - true if this is a one row per item format
*/
function sv_wc_csv_export_is_one_row( $csv_generator ) {
$one_row_per_item = false;
if ( version_compare( wc_customer_order_csv_export()->get_version(), '4.0.0', '<' ) ) {
// pre 4.0 compatibility
$one_row_per_item = ( 'default_one_row_per_item' === $csv_generator->order_format || 'legacy_one_row_per_item' === $csv_generator->order_format );
} elseif ( isset( $csv_generator->format_definition ) ) {
// post 4.0 (requires 4.0.3+)
$one_row_per_item = 'item' === $csv_generator->format_definition['row_type'];
}
return $one_row_per_item;
}
endif;
Related
first of all I'm very new to edit or write php, so maybe my question is just dumb.
I have an invoice plugin, but I want to change to another one, so I have to hide all "show-invoice" buttons for invoices with higher ordernumber then 527420.
/**
* Display download link on My Account page.
*
* #param array $actions my account order table actions.
* #param WC_Order $order WooCommerce order object.
*
* #return mixed
*/
public function add_my_account_pdf( $actions, $order ) {
if ( false === (bool) WPI()->get_option( 'general', 'download_invoice_account' ) ) {
return $actions;
}
// By default order status should be Processing or Completed.
$order = wc_get_order( $order );
$invoice = new BEWPI_Invoice( $order->get_id() );
if ( '' === $invoice->get_full_path() ) {
return $actions;
}
if ( false === apply_filters( 'wpi_show_my_account_pdf', $order->is_paid(), $invoice ) ) {
return $actions;
}
$args = array(
'bewpi_action' => 'view',
'post' => $order->get_id(),
'nonce' => wp_create_nonce( 'view' ),
);
$url = add_query_arg( $args );
$actions['invoice'] = array(
'url' => $url,
'name' => apply_filters( 'bewpi_my_account_pdf_name', __( 'Invoice', 'woocommerce-pdf-invoices' ), $invoice ),
);
return $actions;
}
This was the original script.
I tried with entering:
if ($order_id < 527420) {
and I also tried it with:
if (get_order_number() < '527420') {
Always added after
public function add_my_account_pdf( $actions, $order ) {
Wordpress mentions on the plugin-editor if a script is working or not, on my first try with $oder_id, the button disappered as I want, but all button dissapered even the old ones who should stay :)
I really don't know why it is wrong. :(
Anybody got a advise for me?
What I thought I can do is create an if rule, so the rest of the script is just working if the ordernumber is 527420 or lower.
I've used the awesome snippet of https://jeroensormani.com/custom-stock-quantity-reduction/ to add an additional setting to variations that reduces the main inventory stock by the set amount in the variation.
The problem I'm facing now is that it doesn't check if those variations are out of stock (for example main inventory is 10, and the bundle setting is set to 12 bottles).
The code I've used to add the the multiplier for the total stock reduction is:
// For implementation instructions see: https://aceplugins.com/how-to-add-a-code-snippet/
/**
* Simple product setting.
*/
function ace_add_stock_inventory_multiplier_setting() {
?><div class='options_group'><?php
woocommerce_wp_text_input( array(
'id' => '_stock_multiplier',
'label' => __( 'Inventory reduction per quantity sold', 'woocommerce' ),
'desc_tip' => 'true',
'description' => __( 'Enter the quantity multiplier used for reducing stock levels when purchased.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'min' => '1',
'step' => '1',
),
) );
?></div><?php
}
add_action( 'woocommerce_product_options_inventory_product_data', 'ace_add_stock_inventory_multiplier_setting' );
/**
* Add variable setting.
*
* #param $loop
* #param $variation_data
* #param $variation
*/
function ace_add_variation_stock_inventory_multiplier_setting( $loop, $variation_data, $variation ) {
$variation = wc_get_product( $variation );
woocommerce_wp_text_input( array(
'id' => "stock_multiplier{$loop}",
'name' => "stock_multiplier[{$loop}]",
'value' => $variation->get_meta( '_stock_multiplier' ),
'label' => __( 'Inventory reduction per quantity sold', 'woocommerce' ),
'desc_tip' => 'true',
'description' => __( 'Enter the quantity multiplier used for reducing stock levels when purchased.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'min' => '1',
'step' => '1',
),
) );
}
add_action( 'woocommerce_variation_options_pricing', 'ace_add_variation_stock_inventory_multiplier_setting', 50, 3 );
/**
* Save the custom fields.
*
* #param WC_Product $product
*/
function ace_save_custom_stock_reduction_setting( $product ) {
if ( ! empty( $_POST['_stock_multiplier'] ) ) {
$product->update_meta_data( '_stock_multiplier', absint( $_POST['_stock_multiplier'] ) );
}
}
add_action( 'woocommerce_admin_process_product_object', 'ace_save_custom_stock_reduction_setting' );
/**
* Save custom variable fields.
*
* #param int $variation_id
* #param $i
*/
function ace_save_variable_custom_stock_reduction_setting( $variation_id, $i ) {
$variation = wc_get_product( $variation_id );
if ( ! empty( $_POST['stock_multiplier'] ) && ! empty( $_POST['stock_multiplier'][ $i ] ) ) {
$variation->update_meta_data( '_stock_multiplier', absint( $_POST['stock_multiplier'][ $i ] ) );
$variation->save();
}
}
add_action( 'woocommerce_save_product_variation', 'ace_save_variable_custom_stock_reduction_setting', 10, 2 );
The code that reduces the quantity then is the following:
// For implementation instructions see: https://aceplugins.com/how-to-add-a-code-snippet/
/**
* Reduce with custom stock quantity based on the settings.
*
* #param $quantity
* #param $order
* #param $item
* #return mixed
*/
function ace_custom_stock_reduction( $quantity, $order, $item ) {
/** #var WC_Order_Item_Product $product */
$multiplier = $item->get_product()->get_meta( '_stock_multiplier' );
if ( empty( $multiplier ) && $item->get_product()->is_type( 'variation' ) ) {
$product = wc_get_product( $item->get_product()->get_parent_id() );
$multiplier = $product->get_meta( '_stock_multiplier' );
}
if ( ! empty( $multiplier ) ) {
$quantity = $multiplier * $quantity;
}
return $quantity;
}
add_filter( 'woocommerce_order_item_quantity', 'ace_custom_stock_reduction', 10, 3 );
What I've tried to do is add an "If" Snippet to check the quantity
add_filter( ‘woocommerce_variation_is_active’, ‘my_jazzy_function’, 10, 2 );
function my_jazzy_function( $active, $variation ) {
// Get Multiplier
$multiplier = $item->get_product()->get_meta( '_stock_multiplier' );
$var_stock_count = $variation->get_stock_quantity();
// if there are 5 or less, disable the variant, could always just set to 0.
if( $var_stock_count <= $multiplier ) {
return false;
}
else {
return true;
}
}
But this doesn't work, I think it only checks the variations quantity (if you set the variation to its own quantity instead of global).
How can I compare the total stock count to the newly added setting $multiplier?
Any help would be great.
Compare the total stock quantity to the newly added setting $multiplier
Comment with explanation added to the code
function filter_woocommerce_variation_is_active( $active, $variation ) {
// Get multiplier
$multiplier = get_post_meta( $variation->get_variation_id(), '_stock_multiplier', true );
// NOT empty
if ( ! empty( $multiplier ) ) {
// Get stock quantity
$var_stock_count = $variation->get_stock_quantity();
// Stock quantity < multiplier
if( $var_stock_count < $multiplier ) {
$active = false;
}
}
return $active;
}
add_filter( 'woocommerce_variation_is_active', 'filter_woocommerce_variation_is_active', 10, 2 );
It doesn't work because:
$item variable is not defined in your code.
your custom field is defined in the parent variable product.
So you need to replace:
$multiplier = $item->get_product()->get_meta( '_stock_multiplier' );
by the folling (getting the data from the parent variable product):
$multiplier = get_post_meta( $variation->get_parent_id(), '_stock_multiplier', true );
So in your code:
add_filter( 'woocommerce_variation_is_active', 'my_jazzy_function', 10, 2 );
function my_jazzy_function( $active, $variation ) {
// Get multiplier
if( $multiplier = get_post_meta( $variation->get_parent_id(), '_stock_multiplier', true ) {
// Get stock quantity
$var_stock_count = (int) $variation->get_stock_quantity();
// if there are 5 or less, disable the variant, could always just set to 0
return $var_stock_count <= $multiplier ? false : $active;
}
return $active;
}
It should work now.
I needed a request for quote system on my Woocommerce site and couldn't find any that are compatible with the Composite Products plugin. So I'm going to have customers check out normally using a "no shipping / quote" shipping option and a "request for quote payment gateway. That way I can see the quotes in the backend, approve them, then the customer can order (technically reorder) their quote from their my account section.
I got the buttons for Completed orders and Quote Approved to show up using this:
/**
* Add order again button in my orders completed actions.
*
* #param array $actions
* #param WC_Order $order
* #return array
*/
function cs_add_order_again_to_my_orders_actions( $actions, $order ) {
if ( $order->has_status( 'completed' ) ) {
$actions['order-again'] = array(
'url' => wp_nonce_url( add_query_arg( 'order_again', $order->id ) , 'woocommerce-order_again' ),
'name' => __( 'Order Again', 'woocommerce' )
);
}
return $actions;
}
add_filter( 'woocommerce_my_account_my_orders_actions', 'cs_add_order_again_to_my_orders_actions', 50, 2 );
/**
* Add Place order button in my orders quote-approved actions.
*
* #param array $actions
* #param WC_Order $order
* #return array
*/
function cs_add_place_order_to_my_orders_actions( $actions, $order ) {
if ( $order->has_status( 'quote-approved' ) ) {
$actions['place-order'] = array(
'url' => wp_nonce_url( add_query_arg( 'order_again', $order->id ) , 'woocommerce-place_order' ),
'name' => __( 'place order', 'woocommerce' )
);
}
return $actions;
}
add_filter( 'woocommerce_my_account_my_orders_actions', 'cs_add_place_order_to_my_orders_actions', 50, 2 );
But my second button doesn't function, I believe because of this:
if ( ! function_exists( 'woocommerce_order_again_button' ) ) {
/**
* Display an 'order again' button on the view order page.
*
* #param object $order Order.
*/
function woocommerce_order_again_button( $order ) {
if ( ! $order || ! $order->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_order_again', array( 'completed' ) ) ) || ! is_user_logged_in() ) {
return;
}
wc_get_template( 'order/order-again.php', array(
'order' => $order,
) );
}
}
in woocommerce/includes/wc-template-functions.php
So I think I just need to add 'quote-approved' to
woocommerce_valid_order_statuses_for_order_again
array
which I tried using this:
//Make order again work for Place order , see below
add_filter('woocommerce_valid_order_statuses_for_order_again', function( $statuses ){
$statuses = wc_get_order_statuses('completed', 'quote-approved');
return $statuses;
}, 10, 2);
Which I found here: Woocommerce - Allowing Order Again for different statuses
But I can't get it to work. Anyone know what I'm doing wrong? Any help would be greatly appreciated. Thanks!
The problem comes from wc_get_order_statuses() function that has no arguments and just gives an indexed array of all available orders status.
Instead, you just need to add your custom order status slug this way:
add_filter( 'woocommerce_valid_order_statuses_for_order_again', 'add_custom_status_for_order_again', 20, 1 );
function add_custom_status_for_order_again( $statuses ){
$statuses[] = 'quote-approved';
return $statuses;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I Would like to set a custom price when product is added to cart, based on an input hidden field located in the single product page.
I have tried to use this code to change the product price in cart:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = $_POST['_custom_price'];
foreach ( $cart_object->cart_contents as $key => $value ) {
// for WooCommerce version 3+ use:
$value['data']->set_price($custom_price);
}
}
But I get a zero price.
Here is my test page. Any help is appreciated.
Thanks for help but it dosent work...it give an error
this is my function.php
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
if(isset($_POST['_custom_price'] )){
$custom_price = $_POST['_custom_price'];
foreach ( $cart_object->cart_contents as $key => $value ) {
// for WooCommerce version 3+ use:
$value['data']->set_price($custom_price);
$_SESSION['custom_price']=$custom_price;
}
}else{
}
}
// STACK HELP CODE
/*
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_price', 20, 2 );
function add_custom_price( $cart_item_data, $product_id ){
// Only when option is passed through the URL
if( ! isset($_POST['_custom_price']) && empty($_POST['_custom_price']) )
return $cart_item_data;
$cart_item_data['custom_price'] = sanitize_text_field( $_POST['_custom_price'] );
return $cart_item_data;
}
add_action( 'woocommerce_before_calculate_totals', 'set_custom_price' );
function set_custom_price( $cart ) {
foreach ( $cart->get_cart() as $cart_item ) {
if( isset($cart_item['custom_price']) ){
$value['data']->set_price($cart_item['custom_price']);
}
}
}
*/
?>
<?php
/*
* Display input on single product page
* #return html
*/
function kia_custom_option(){
$value = isset( $_POST['_custom_option'] ) ? sanitize_text_field( $_POST['_custom_option'] ) : '';
printf( '<label>%s</label><input id="hotel_chambre_selected" name="_custom_option" value="%s" />', __( '', 'kia-plugin-textdomain' ), esc_attr( $value ) );
}
add_action( 'woocommerce_before_add_to_cart_button', 'kia_custom_option', 9 );
/*
* Validate when adding to cart
* #param bool $passed
* #param int $product_id
* #param int $quantity
* #return bool
*/
function kia_add_to_cart_validation($passed, $product_id, $qty){
if( isset( $_POST['_custom_option'] ) && sanitize_text_field( $_POST['_custom_option'] ) == '' ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter some custom text.', 'kia-plugin-textdomain' ), $product->get_title() ), 'error' );
return false;
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'kia_add_to_cart_validation', 10, 3 );
/*
* Add custom data to the cart item
* #param array $cart_item
* #param int $product_id
* #return array
*/
function kia_add_cart_item_data( $cart_item, $product_id ){
if( isset( $_POST['_custom_option'] ) ) {
$cart_item['custom_option'] = sanitize_text_field( $_POST['_custom_option'] );
}
return $cart_item;
}
add_filter( 'woocommerce_add_cart_item_data', 'kia_add_cart_item_data', 10, 2 );
/*
* Load cart data from session
* #param array $cart_item
* #param array $other_data
* #return array
*/
function kia_get_cart_item_from_session( $cart_item, $values ) {
if ( isset( $values['custom_option'] ) ){
$cart_item['custom_option'] = $values['custom_option'];
}
return $cart_item;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'kia_get_cart_item_from_session', 20, 2 );
/*
* Add meta to order item
* #param int $item_id
* #param array $values
* #return void
*/
function kia_add_order_item_meta( $item_id, $values ) {
if ( ! empty( $values['custom_option'] ) ) {
woocommerce_add_order_item_meta( $item_id, 'custom_option', $values['custom_option'] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'kia_add_order_item_meta', 10, 2 );
/*
* Get item data to display in cart
* #param array $other_data
* #param array $cart_item
* #return array
*/
function kia_get_item_data( $other_data, $cart_item ) {
if ( isset( $cart_item['custom_option'] ) ){
$other_data[] = array(
'name' => __( 'Votre chambre ', 'kia-plugin-textdomain' ),
'value' => sanitize_text_field( $cart_item['custom_option'] )
);
}
return $other_data;
}
add_filter( 'woocommerce_get_item_data', 'kia_get_item_data', 10, 2 );
/*
* Show custom field in order overview
* #param array $cart_item
* #param array $order_item
* #return array
*/
function kia_order_item_product( $cart_item, $order_item ){
if( isset( $order_item['custom_option'] ) ){
$cart_item_meta['custom_option'] = $order_item['custom_option'];
}
return $cart_item;
}
add_filter( 'woocommerce_order_item_product', 'kia_order_item_product', 10, 2 );
/*
* Add the field to order emails
* #param array $keys
* #return array
*/
function kia_email_order_meta_fields( $fields ) {
$fields['custom_field'] = __( 'Votre chambre ', 'kia-plugin-textdomain' );
return $fields;
}
add_filter('woocommerce_email_order_meta_fields', 'kia_email_order_meta_fields');
/*
* Order Again
* #param array $cart_item
* #param array $order_item
* #param obj $order
* #return array
*/
function kia_order_again_cart_item_data( $cart_item, $order_item, $order ){
if( isset( $order_item['custom_option'] ) ){
$cart_item_meta['custom_option'] = $order_item['custom_option'];
}
return $cart_item;
}
add_filter( 'woocommerce_order_again_cart_item_data', 'kia_order_again_cart_item_data', 10, 3 );
this is the result of print_r($_POST) in my function
I've built my first shipping method plugin based on flat rate with a few extra fields.
I have done the following:
1. Installed and activated the plugin
2. Added 2 instances of the shipping method to the UK zone
I can see in the top sub menu in the shipping section there appears to be some kind of "default" instance of the shipping plugin in a menu labelled "UK Flat Rate"
I was wondering if there's a way to remove this and ONLY have the plugin work in the shipping zones section.
The reason I ask is that then in checkout if I enter a UK address I see the 2 UK methods defined and then underneath them both there is also a radio button for UK Flat Rate which I'm trying to get rid of. It shows the default values based on the values entered in the sub-section.
if (!defined('ABSPATH')) {
exit;
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_action( 'woocommerce_shipping_init', 'uk_shipping_method');
function uk_shipping_method() {
if (!class_exists('UK_WC_Shipping_Flat_Rate')) {
class UK_WC_Shipping_Flat_Rate extends WC_Shipping_Method {
/** #var string cost passed to [fee] shortcode */
protected $fee_cost = '';
/**
* Constructor.
*
* #param int $instance_id
*/
public function __construct($instance_id = 1) {
$this->id = 'uk_flat_rate';
$this->instance_id = absint($instance_id);
$this->enabled = "yes"; // This can be added as an setting but for this example its forced enabled
$this->method_title = __('UK Flat Rate');
$this->title = 'UK Flat Rate';
$this->method_description = __('Lets you charge a fixed rate for shipping but flags for UK Status Update.');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
add_action('woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options'));
}
/**
* init user set variables.
*/
public function init() {
$this->instance_form_fields = include('includes/settings-flat-rate.php');
$this->title = $this->get_option( 'title' );
$this->tax_status = $this->get_option( 'tax_status' );
$this->cost = $this->get_option( 'cost' );
$this->type = $this->get_option( 'type', 'class' );
}
/**
* Evaluate a cost from a sum/string.
* #param string $sum
* #param array $args
* #return string
*/
protected function evaluate_cost( $sum, $args = array() ) {
include_once( WC()->plugin_path() . '/includes/libraries/class-wc-eval-math.php' );
// Allow 3rd parties to process shipping cost arguments
$args = apply_filters( 'woocommerce_evaluate_shipping_cost_args', $args, $sum, $this );
$locale = localeconv();
$decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'], ',' );
$this->fee_cost = $args['cost'];
// Expand shortcodes
add_shortcode( 'fee', array( $this, 'fee' ) );
$sum = do_shortcode( str_replace(
array(
'[qty]',
'[cost]',
),
array(
$args['qty'],
$args['cost'],
),
$sum
) );
remove_shortcode( 'fee', array( $this, 'fee' ) );
// Remove whitespace from string
$sum = preg_replace( '/\s+/', '', $sum );
// Remove locale from string
$sum = str_replace( $decimals, '.', $sum );
// Trim invalid start/end characters
$sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
// Do the math
return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
}
/**
* Work out fee (shortcode).
* #param array $atts
* #return string
*/
public function fee( $atts ) {
$atts = shortcode_atts( array(
'percent' => '',
'min_fee' => '',
'max_fee' => '',
), $atts, 'fee' );
$calculated_fee = 0;
if ( $atts['percent'] ) {
$calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
}
if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
$calculated_fee = $atts['min_fee'];
}
if ( $atts['max_fee'] && $calculated_fee > $atts['max_fee'] ) {
$calculated_fee = $atts['max_fee'];
}
return $calculated_fee;
}
/**
* calculate_shipping function.
*
* #param array $package (default: array())
*/
public function calculate_shipping( $package = array() ) {
$rate = array(
'id' => $this->get_rate_id(),
'label' => $this->title,
'cost' => 0,
'package' => $package,
);
// Calculate the costs
$has_costs = false; // True when a cost is set. False if all costs are blank strings.
$cost = $this->get_option('cost');
if ( '' !== $cost ) {
$has_costs = true;
$rate['cost'] = $this->evaluate_cost( $cost, array(
'qty' => $this->get_package_item_qty( $package ),
'cost' => $package['contents_cost'],
) );
}
// Add shipping class costs.
$shipping_classes = WC()->shipping->get_shipping_classes();
if ( ! empty( $shipping_classes ) ) {
$found_shipping_classes = $this->find_shipping_classes( $package );
$highest_class_cost = 0;
foreach ( $found_shipping_classes as $shipping_class => $products ) {
// Also handles BW compatibility when slugs were used instead of ids
$shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
$class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' );
if ( '' === $class_cost_string ) {
continue;
}
$has_costs = true;
$class_cost = $this->evaluate_cost( $class_cost_string, array(
'qty' => array_sum( wp_list_pluck( $products, 'quantity' ) ),
'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) ),
) );
if ( 'class' === $this->type ) {
$rate['cost'] += $class_cost;
} else {
$highest_class_cost = $class_cost > $highest_class_cost ? $class_cost : $highest_class_cost;
}
}
if ( 'order' === $this->type && $highest_class_cost ) {
$rate['cost'] += $highest_class_cost;
}
}
// Add the rate
if ( $has_costs ) {
$this->add_rate( $rate );
}
/**
* Developers can add additional flat rates based on this one via this action since #version 2.4.
*
* Previously there were (overly complex) options to add additional rates however this was not user.
* friendly and goes against what Flat Rate Shipping was originally intended for.
*
* This example shows how you can add an extra rate based on this flat rate via custom function:
*
* add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_another_custom_flat_rate', 10, 2 );
*
* function add_another_custom_flat_rate( $method, $rate ) {
* $new_rate = $rate;
* $new_rate['id'] .= ':' . 'custom_rate_name'; // Append a custom ID.
* $new_rate['label'] = 'Rushed Shipping'; // Rename to 'Rushed Shipping'.
* $new_rate['cost'] += 2; // Add $2 to the cost.
*
* // Add it to WC.
* $method->add_rate( $new_rate );
* }.
*/
do_action( 'woocommerce_' . $this->id . '_shipping_add_rate', $this, $rate );
}
/**
* Get items in package.
* #param array $package
* #return int
*/
public function get_package_item_qty( $package ) {
$total_quantity = 0;
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['quantity'] > 0 && $values['data']->needs_shipping() ) {
$total_quantity += $values['quantity'];
}
}
return $total_quantity;
}
/**
* Finds and returns shipping classes and the products with said class.
* #param mixed $package
* #return array
*/
public function find_shipping_classes( $package ) {
$found_shipping_classes = array();
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['data']->needs_shipping() ) {
$found_class = $values['data']->get_shipping_class();
if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
$found_shipping_classes[ $found_class ] = array();
}
$found_shipping_classes[ $found_class ][ $item_id ] = $values;
}
}
return $found_shipping_classes;
}
}
}
function add_uk_shipping_method( $methods ) {
$methods['uk_flat_rate'] = 'UK_WC_Shipping_Flat_Rate';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_uk_shipping_method' );
}
}
Is there a setting in the plugin I'm missing to enforce the method is only zones based?
Turns out I needed to add filters to remove the section and the first instance of the method from shipping like so
woocommerce_shipping_option_remove( $section ) {
unset($section['uk_flat_rate']);
return $section;
}
add_filter( 'woocommerce_get_sections_shipping', 'woocommerce_shipping_option_remove' ,1 );
function woocommerce_shipping_remove_method( $rates )
{
unset($rates['uk_flat_rate:1']);
return $rates;
}
add_filter('woocommerce_package_rates','woocommerce_shipping_remove_method', 100 );
}
}