Limit shipping to specific State's only - wordpress

I want to limit shipping to my customer to specific states only. Actually i want to show specific states on my shipping address state field. I have try this
function patricks_only_ship_to_continental_us( $available_methods ) {
global $woocommerce;
$excluded_states = array( 'AK','HI','GU','PR' );
if( !in_array( $woocommerce->customer->get_shipping_state(), $excluded_states ) ) {
// Empty the $available_methods array
$available_methods = array();
}
return $available_methods;
}
add_filter( 'woocommerce_package_rates', 'patricks_only_ship_to_continental_us', 10 );
but this seems to be not working as a expected, Its throws error message while placing the order. I don't want my customer select invalid shipping method.
I have also tried these filter but they change both shipping and billing address state field
woocommerce_states
woocommerce_countries_allowed_country_states

Here is example of enable shipping to limited state of india only.
place below code into your function.php
add_filter( 'woocommerce_states', 'custom_woocommerce_states' );
function custom_woocommerce_states( $states ) {
$states['IN'] = array(
'GA' => __( 'Goa', 'woocommerce' ),
'JK' => __( 'Jammu and Kashmir', 'woocommerce' ),
'JH' => __( 'Jharkhand', 'woocommerce' ),
'GJ' => __( 'Gujarat', 'woocommerce' )
);
return $states;
}
Modify array list by listing of state where you want to enable shipping.
You can get list of state code from woo commerce default state
plugins\woocommerce\i18n\states

I cannot comment so I'll post as an answer, There are a lot of plugins that can do this by adding something like a search bar where you're customer can enter there zip code and check if you are delivering in that place. It can also restrict them to add anything in a cart if they haven't checked their zip code yet.
Here's the list of plugin's I know so far. Everything's paid though
http://gremlin.io/shop/woocommerce-plugins/restrict-check-cod-payment-method-zip-pin-codes-woocommerce
http://codecanyon.net/item/zip-code-checker/8740060
http://codecanyon.net/item/wp-triggers-add-instant-interactivity-to-wp/3516401?sso%3FWT.ac=search_item&ref=cmoreira&WT.seg_1=search_item&WT.z_author=deptweb730&ref=cmoreira&clickthrough_id=410306377&redirect_back=true

Related

WooCommerce: Adding custom data to processing order

I need add a custom data on Processing order email, but the data always update after the email is sent, like this:
Order status change ==> Send email ==> Insert data on custom table (plugin)
What I need instead is:
Order status change ==> Insert data on custom table (plugin) ==> Send email.
I have checked and this is done with the following hooked function:
add_action('woocommerce_order_status_changed', 'fun_order_status_changed', 10, 4);
function fun_order_status_changed($order_id, $from_status, $to_status, $order){
// Some code
// Then insert to database
}
How could I do or what files can I need to modify so that first the insert is saved in the database and then the e-mail is sent?
EDIT 1
I put deliberately a var_dump and first execute the mail templeate
You can try to use woocommerce_order_status_pending_to_processing_notification action hook with a lower priority, for example 5. So that it will get processed before the mail is sent.
If you want to add a custom field to the order meta data, send the value of this field with the order confirmation mail, and additionally display it in the order detail and edit screen in the backend, you can use the follwing code. There are multiple steps to be done.
Create a new field to show up in WooCommerce Checkout. Set it as required if you want to make sure, that there is a value entered. For this we are using 'woocommerce_after_checkout_billing_form'. (just a sidenote: If you have other purposes, you can also i.e. use a hidden field and a given value)
Save the value inside order meta data using 'woocommerce_checkout_update_order_meta'
Add the value to the email being send after completing order using 'woocommerce_email_order_meta_keys'
Show the value in the order detail screen in the backend using 'woocommerce_order_details_after_order_table' and for the order edit screen 'woocommerce_admin_order_data_after_billing_address' This will place it below the billing address. Notice: The value will not show up (but still be saved in database), if the order is made within the backend, only works for orders placed in the frontend (Would go beyond the scope now).
In my code example, I did this steps to add a VAT ID field which is important in europe for business to business transactions. The VAT ID is also added to the emails and backend screens.
You can adjust the names (vat_number, or the "mrank" prefixes) to your needs, but remember to keep it consistent.
/**
* VAT Number in WooCommerce Checkout
*/
function mrank_vat_field( $checkout ) {
echo '<div id="mrank_vat_field">';
woocommerce_form_field( 'vat_number', array(
'type' => 'text',
'class' => array( 'vat-number-field form-row-wide') ,
'label' => __( 'VAT-ID' ),
'placeholder' => __( 'Enter number' ),
'description' => __( 'Please enter your VAT-ID' ),
'required' => true,
), $checkout->get_value( 'vat_number' ));
echo '</div>';
}
add_action( 'woocommerce_after_checkout_billing_form', 'mrank_vat_field' );
/**
* Save VAT Number in the order meta
*/
function mrank_checkout_vat_number_update_order_meta( $order_id ) {
if ( ! empty( $_POST['vat_number'] ) ) {
update_post_meta( $order_id, '_vat_number', sanitize_text_field( $_POST['vat_number'] ) );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'mrank_checkout_vat_number_update_order_meta' );
/**
* Display VAT Number in order details screen
*/
function mrank_vat_number_display_order_details($order){
echo '<p><strong>'.__('VAT-ID').':</strong> ' . get_post_meta( $order->get_id(), '_vat_number', true ) . '</p>';
}
add_action( 'woocommerce_order_details_after_order_table', 'mrank_vat_number_display_order_details', 10, 1 );
/**
* Display VAT Number in order edit screen
*/
function mrank_vat_number_display_admin_order_meta( $order ) {
echo '<p><strong>' . __( 'VAT-ID', 'woocommerce' ) . ':</strong> ' . get_post_meta( $order->get_id(), '_vat_number', true ) . '</p>';
}
add_action( 'woocommerce_admin_order_data_after_billing_address', 'mrank_vat_number_display_admin_order_meta', 10, 1 );
/**
* VAT Number in emails
*/
function mrank_vat_number_display_email( $keys ) {
$keys['VAT-ID'] = '_vat_number';
return $keys;
}
add_filter( 'woocommerce_email_order_meta_keys', 'mrank_vat_number_display_email' );

Woocommerce Set Billing Address Optional

I redesigned my Woo checkout page to only show the billing address fields when the Stripe gateway is selected, otherwise if the cheque gateway is selected I do not want to collect their billing address. I have the template finished and the jQuery calls work like a charm. However, I'm having problems making the billing address fields optional and the shipping address fields required.
Here's what I have so far...I added the following filter to make both address fields not-required:
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields');
function custom_override_default_address_fields( $fields ) {
// add or remove billing fields you do not want
$keys = array(
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'city',
'postcode',
'state',
'country'
);
foreach( $keys as $key ) {
$fields[$key]['required'] = false;
}
return $fields;}
I remove this filter when the Stripe gateway is selected using jQuery so that the billing address fields show as required (and re-apply if cheque is selected), this behavior works for the billing address fields.
I now need to make the shipping address fields to show as required, so I tried adding the following filter:
add_filter( 'woocommerce_checkout_fields', 'set_shipping_required_fields',9999 );
function set_shipping_required_fields( $fields ) {
// add or remove billing fields you do not want
$shipping_keys = array(
'first_name',
'last_name',
'address_1',
'city',
'postcode',
'state',
);
foreach( $shipping_keys as $key ) {
$fields['shipping']['shipping_'.$key]['required'] = true;
}
return $fields;
}
This successfully will display the shipping_first_name and shipping_last_name as required, however, the rest of the shipping fields initially render as required, but a split second later will revert back to being optional. Does anyone have any tips what I'm doing wrong to make the billing address fields optional and shipping address fields required? Is there a better way?
ou should need to use woocommerce_default_address_fields filter hook instead as explained here.
/ Billing and shipping addresses fields
add_filter( 'woocommerce_default_address_fields' , 'filter_default_address_fields', 20, 1 );
function filter_default_address_fields( $address_fields ) {
// Only on checkout page
if( ! is_checkout() ) return $address_fields;
// All field keys in this array
$key_fields = array('country','first_name','last_name','company','address_1','address_2','city','state','postcode');
// Loop through each address fields (billing and shipping)
foreach( $key_fields as $key_field )
$address_fields[$key_field]['required'] = false;
return $address_fields;
}
As billing email and phone are already required by default, if you want them to be not required, you should need this additional code:
// For billing email and phone - Make them not required
add_filter( 'woocommerce_billing_fields', 'filter_billing_fields', 20, 1 );
function filter_billing_fields( $billing_fields ) {
// Only on checkout page
if( ! is_checkout() ) return $billing_fields;
$billing_fields['billing_phone']['required'] = false;
$billing_fields['billing_email']['required'] = false;
return $billing_fields;
}
All code goes in function.php file of your active child theme (or active theme). Tested and works.

New billing fields not showing on User page (Poor Guys Swiss Knife plugin for WooCommerce)

Using Poor Guys Swiss Knife plugin for WooCommerce, I created 3 new Billing fields: birthday (date), newsletter (select: yes/no) and terms & conditions (select: yes/no). I successfully registered as a new customer on the site and filled these new fields.
However when checking the account thus created in the dashboard, I see all the regular fields except the 3 ones I created with the Poor Guys Swiss Knife plugin. Why is that ?
Clearly plugin is not sending fields data to user account.
You can achieve same results using woocommerce custom fields in your functions.php file.
// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
$fields['shipping']['shipping_phone'] = array(
'label' => __('Phone', 'woocommerce'),
'placeholder' => _x('Phone', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
To make it required use this code:
/**
* Process the checkout
*/
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');
function my_custom_checkout_field_process() {
// Check if set, if its not set add an error.
if ( ! $_POST['my_field_name'] )
wc_add_notice( __( 'Please enter something into this new shiny field.' ), 'error' );
}
you can find more on this website:
http://docs.woothemes.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/
Here is a solution that I came up with and it has been working fine for me, hope it helps you as well.
I created a few new fields for the billing form using that plugin but let's just use one as an example:
-Resale License # ( billing_resale_license_ )
Now, the plugin ( or the code from the first answer ) will add the field to the form for you, in my case, I end up with:
<input type="text" class="input-text " name="billing_resale_license_" id="billing_resale_license_" placeholder="" value="" display="text">
Now, we need to save the value of that field to the user_meta table, like so:
add_action( 'woocommerce_checkout_process', 'ws_billing_fields_save', 10, 1 );
function ws_billing_fields_save( $user_id ){
if ( isset( $_POST['billing_resale_license_'] ) ) {
update_user_meta($user_id, 'billing_resale_license_', $_POST['billing_resale_license_']);
}
}
Now that data is stored in user_meta, we need to hook in to the profile area to display it and allow it to be edited by the user or admins.
add_action( 'show_user_profile', 'ws_update_user_profile' );
add_action( 'edit_user_profile', 'ws_update_user_profile' );
function ws_update_user_profile( $user ){ ?>
<h3>Additional Fields</h3>
<table class="form-table">
<tr>
<th><label for="billing_resale_license_">Resale #</label></th>
<td><input type="text" name="billing_resale_license_" value="<?php echo esc_attr(get_the_author_meta( 'billing_resale_license_', $user->ID )); ?>" class="regular-text" /></td>
</tr>
</table>
add_action( 'personal_options_update', 'save_extra_fields' );
add_action( 'edit_user_profile_update', 'save_extra_fields' );
function save_extra_fields( $user_id ){
update_user_meta( $user_id,'billing_resale_license_', sanitize_text_field( $_POST['billing_resale_license_'] ) );
}
I chose to break my additional fields into their own table just above the default Woocommerce fields as I have many of them and they are more specific to the user rather than the order processing itself, so I keep them organized in their own table titled "Additional Fields".
I think the billing fields are visible on the order only, not the user. User fields are available through the register_form and user_register hooks.

Woocommerce - Product price depending on country

I have 2 questions regarding Woocommerce for Wordpress.
I'm working on a site that sells speakers to the Danish market.
Question one:
Can I detect the IP of a visitor and detect which country the person is from? I guess this can be done with some ClientLocation api.
Can I then disable all shopping relatet pages and buttons if a person is not from Denmark. Fx: hiding the add to cart, cart and checkout.
I still want the persons to be able to see the prices, they should just not have the option to buy them.
Question 2:
Lets say that question one was sucessfull made. Then I would like to show different prices for other contries than Denmark. So if you are visiting the site from one country, the price is XXX and from another country the price is XXXX.
Let's say:
In USA the price is = $500
And in UK the price = £400
(This has nothing to do with currency. The market price is just different in different countries.)
I've looked at this plugin: http://wordpress.org/plugins/woocomerce-price-by-country/
It allowed me to write different prices for each product, but when I testet it with http://geopeeker.com/ I hadn't worked at all.
Can you give me some pointets or some links to some plugins that you know of?
UPDATE
I managed to solve question 1. I store the visitors country in a cookie with IP location XML API And then I could just create an if statement, saying that if the country was not equal to Denmark, then the add to cart, cart etc. should be removed.
So yeah, I would really appreciate it if anyknow could give me an idea on how I can solve question 2.
I'm able to detect country, but not able to specify a price of each product to the given country.
2'nd update:
Just to let any interested readers know, I ended up buying this plugin. which is working perfectly!
For the 2nd part of your question: If you are only using simple product types (without variations) then you can add custom price fields to the product data page and filter the price using woocommerce_get_price_html.
add_filter('woocommerce_get_price_html','so24863612_custom_price');
function so24863612_custom_price(){
global $post;
$_postID = $post->ID;
$product = get_product( $_postID );
$UK_price = get_post_meta($_postID, '_UK_price', true); //loads custom meta data
$return_price = $product->get_regular_price(); //default to regular price
if (!empty($UK_price)) {
$return_price = $UK_price;
}
return $return_price;
}
You can create and save custom fields on the product page like this:
//Display custom fields on product data page in admin
add_action( 'woocommerce_product_options_general_product_data', 'so24963039_display_custom_general_tab_fields' );
function so24963039_display_custom_general_tab_fields() {
global $woocommerce, $post;
$UK_price = get_post_meta( $post->ID, '_UK_price', true );
woocommerce_wp_text_input(
array(
'id' => '_UK_price',
'label' => __( 'UK Price (£)', 'woocommerce' ),
'value' => $UK_price,
'desc_tip' => 'false'
)
);
}
//Save custom fields to access via get_post_meta
add_action( 'woocommerce_process_product_meta', 'so24963039_save_custom_general_tab_fields' );
function so24963039_save_custom_general_tab_fields ($post_id) {
$woocommerce_UK_price = $_POST['_UK_price'];
if( !empty( $woocommerce_UK_price ) )
update_post_meta( $post_id, '_UK_price', esc_attr( $woocommerce_UK_price ) );
}
-----------------For products with Variations----------------------------
WARNING: Variable products are much more complicated and I'm not nearly as confident in this answer as I am with the simple products part above, but here's my current understanding either way. I had some mini-cart display issues that I had to hack around when using this method (which I will explain at the end), but the totals are calculated correctly in both the mini-cart and the regular cart.
First we want to add new fields to each variant on the variation tab of existing products:
add_action( 'woocommerce_product_after_variable_attributes', 'so24963039_variable_fields', 10, 2 ); //Display Fields
function so24963039_variable_fields( $loop, $variation_data ) {
echo '<tr><td>';
woocommerce_wp_text_input(
array(
'id' => '_variant_UK_price['.$loop.']',
'label' => __( 'UK Price (£)', 'woocommerce' ),
'desc_tip' => 'false',
'value' => $variation_data['_variant_UK_price'][0]
)
);
echo '</td></tr>';
}
We also need to add them dynamically whenever the user adds new variants on the edit product page:
add_action( 'woocommerce_product_after_variable_attributes_js', 'so24963039_variable_fields_js' ); //JS to add fields for dynamically added new variations
function so24963039_variable_fields_js(){ //add fields to new variations that get added
echo '<tr><td>';
woocommerce_wp_text_input(
array(
'id' => '_variant_UK_price[ + loop + ]',
'label' => __( 'UK Price (£)', 'woocommerce' ),
'desc_tip' => 'false',
'value' => $variation_data['_variant_UK_price'][0]
)
);
echo '</td></tr>';
}
Then we save changes to the custom fields in the variation meta data:
add_action( 'woocommerce_process_product_meta_variable', 'so24963039_save_variable_fields', 10, 1 ); //Save variation fields
function so24963039_save_variable_fields( $post_id ) {
if (isset( $_POST['variable_sku'] ) ) {
$variable_sku = $_POST['variable_sku'];
$variable_post_id = $_POST['variable_post_id'];
// Variant Tier 1 Price
$_variant_UK_price = $_POST['_variant_UK_price'];
for ( $i = 0; $i < sizeof( $variable_sku ); $i++) {
$variation_id = (int) $variable_post_id[$i];
if ( isset( $_variant_UK_price[$i] ) ) {
update_post_meta( $variation_id, '_variant_UK_price', stripslashes($_variant_UK_price[$i] ) );
}
}
}
}
Now that we have our custom variation meta data, we can access it in the custom price module like so:
add_filter('woocommerce_get_price_html','so24863612_custom_price');
function so24863612_custom_price(){
global $post;
$_postID = $post->ID;
$product = get_product( $_postID );
$product_type = $product->product_type;
$UK_price = get_post_meta($_postID, '_UK_price', true); //covers simple products
if($product_type == 'variation'){ //override with variant prices
$UK_price = get_post_meta($_postID, '_variant_$UK_price', true);
}
$return_price = $product->get_regular_price(); //default to regular price
if (!empty($UK_price)) {
$return_price = $UK_price;
}
return $return_price;
}
Now, I believe that part should have everything working except for the mini-cart display. For some reason it seems like I just couldn't figure out how to get access to the variation meta data to force it to display properly in the mini cart - like I found where the mini-cart display was being generated but I was having trouble getting the right context path to access the custom variable so I ended up having to do that in the template-tags.php and pass an array of custom values to an optional parameter in my custom price function. This feels very 'wrong' in terms of how we should do things, but it gets the job done. I'm very open to hearing the 'correct' solution to this part of the problem.
In template-tags.php:
<div class="small-7 large-7 columns"><?php
$product_title = $_product->get_title();
echo '<a class="cart_list_product_title" href="'.get_permalink($cart_item['product_id']).'">' . apply_filters('woocommerce_cart_widget_product_title', $product_title, $_product) . '</a>';
echo '<div class="cart_list_product_price">';
//original line: echo woocommerce_price($_product->get_price());
/*Custom Price Override Block*/
$_productID = $_product->id;
$product_type = $_product->product_type;
if($product_type == 'variation') {
$custom_field_data = $_product->product_custom_fields;
$regular_price = $custom_field_data['_regular_price'];
$custom_UK_price = $custom_field_data['_variant_UK_price'];
$custom_variant_prices = [$regular_price[0], $custom_UK_price[0]];
echo so24863612_get_custom_price($_productID, $custom_variant_prices );
} else {
echo so24863612_get_custom_price($_productID );
}
/*End Custom Price Override Block*/
echo ' /</div>';
echo '<div class="cart_list_product_quantity">'.__('Quantity', 'woocommerce').': '.$cart_item['quantity'].'</div>';
?></div>
I see your update that you did manage to get the visitor's country and that you can use this to create the if statement to remove the cart. (which is freaking cool, by the way)
Doesn't that answer your question 2, about changing the prices for each visitor? All you have to do is make sure that both prices are stored somewhere, and then just have it echo the Denmark or the UK one.
Prices are specific - custom fields
You mentioned this is NOT currency conversion - so you need to store both values. Add a custom field to the product entry that you edit with that new price, and name it "denmarkprice" or something
I'm not 100% familiar enough with WooCommerce to say what custom field plugin might work, but you could use http://www.advancedcustomfields.com/ if you don't want to just create the custom field yourself and call the variable with the_meta() when you want to display it inside your if else statement.
http://codex.wordpress.org/Custom_Fields

Get WooCommerce products on sale with WC_Query

I know there are plenty of solutions on the internet about how to get WooCommerce products on sale, by doing a WP_Query. However, WooCommerce doesn't seem to work fully if it's WC_Query object is not populated. For example: filter or sorting
Both these templates call:
woocommerce_products_will_display()
Which check's to see if the page is a taxonomy page (obvious false if you're using your own custom template):
if ( ! is_product_taxonomy() ) return false;
This is an example of a simple solution if you just want the products: WooCommerce: Display ONLY on-sale products in Shop
So, I there seems to be a couple of issues I need to solve here:
1) How to tell WC that my "Sale" page is a taxonomy page? Is there some sort of trick I need to do to force it into a taxonomy?
2) How do I get get WC_Query filled with the sales query (rather than just the WP_Query)
I have plugins that depend on:
$woocommerce->query->layered_nav_product_ids
being populated.
Any help is appreciated!
Thanks!!!
Well woocommerce_products_will_display() is pluggable, meaning you can define it in your own functions.php (or site-specific plugin) and alter it, having it return true for your specific template/page.
I think it could stand for some tweaking and a filter.
EDIT
I played around with this a bit more. Typically changing the posts that you want to retrieve is done in the pre_get_posts hook. I took a look at what WooCommerce is doing. They are adding something to the pre_get_posts hook and calling their special query stuff from there.
But their special query stuff dies if you aren't on a WooCommerce page. So, it made me thing that maybe we could just call it ourselves from our own function. I put this together and coupled with a special page template for a page called "on-sale" (basically just a copy of the shop template), seems to show just the for sale items with proper sorting and pagination.
Your mileage may vary, but I hope it helps.
function kia_pre_get_posts( $q ){
// We only want to affect the main query
if ( ! $q->is_main_query() ) {
return;
}
// Fix for verbose page rules
if ( is_page('on-sale') ) {
$q->set( 'post_type', 'product' );
$q->set( 'page_id', '' );
$q->set( 'page', '' );
$q->set( 'pagename', '' );
$meta_query = array( array(
'key' => '_sale_price',
'value' => 0,
'compare' => '>'
) );
$q->set( 'meta_query', $meta_query );
if ( isset( $q->query['paged'] ) ) {
$q->set( 'paged', $q->query['paged'] );
}
// Fix conditional Functions
$q->is_archive = true;
$q->is_post_type_archive = true;
$q->is_singular = false;
$q->is_page = false;
}
$wc_query = WC()->query;
$wc_query->product_query( $q );
if ( is_search() ) {
add_filter( 'posts_where', array( $wc_query, 'search_post_excerpt' ) );
add_filter( 'wp', array( $wc_query, 'remove_posts_where' ) );
}
add_filter( 'posts_where', array( $wc_query, 'exclude_protected_products' ) );
// We're on a shop page so queue the woocommerce_get_products_in_view function
add_action( 'wp', array( $wc_query, 'get_products_in_view' ), 2);
// And remove the pre_get_posts hook
$wc_query->remove_product_query();
}
add_action( 'pre_get_posts', 'kia_pre_get_posts' );

Resources