I have tax enabled for my WooCommerce installation. It calculates tax for all customer's who live in my state (based on their shipping address). This is the default setup for WooCommerce.
Some customers are tax exempt and shouldn't be charged tax. I created a custom field in the user profile where I can check a box to exempt customers from being charged tax. This works correctly.
I tried to find a hook where I can use that selection to disable the tax but the code I have causes the checkout page to be blank for all users. No error message is displayed.
My functions.php code is as follows:
///////////////////////////////////////
/* Tax exempt customers */
///////////////////////////////////////
// Add tax exempt custom user field in admin
add_action( 'show_user_profile', 'add_customer_tax_exempt_checkbox', 10 );
add_action( 'edit_user_profile', 'add_customer_tax_exempt_checkbox', 10 );
function add_customer_tax_exempt_checkbox( $user )
{
?>
<h3><?php _e("Tax status"); ?></h3>
<table class="form-table">
<tr>
<th><?php _e("Tax exempt"); ?></th>
<td>
<?php
woocommerce_form_field( 'tax_exempt', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('Allowed'),
), get_user_meta( $user->id, 'tax_exempt', true ) );
?>
</td>
</tr>
</table>
<?php
}
// Save allowed custom user field in admin
add_action( 'personal_options_update', 'save_customer_tax_exempt_checkbox' );
add_action( 'edit_user_profile_update', 'save_customer_tax_exempt_checkbox' );
function save_customer_tax_exempt_checkbox( $user_id )
{
if ( current_user_can( 'edit_user', $user_id ) ) {
update_user_meta( $user_id, 'tax_exempt', isset($_POST['tax_exempt']) ? '1' : '0' );
}
}
// Enabling or disabling tax calculation at checkout
add_filter( 'woocommerce_product_tax_class', 'disable_tax_calculation' );
function disable_tax_calculation( $tax_class, $product ) {
if ( get_user_meta( get_current_user_id(), 'tax_exempt', true ) ) {
$tax_class = 'Zero Rate';
}
return $tax_class;
}
///////////////////////////////////////
/* END: Tax exempt customers */
///////////////////////////////////////
Since Woocommerce 3 woocommerce_product_tax_class hook is deprecated and has been replaced. I have updated your 3rd function below:
// Add tax exempt custom user field in admin
add_action( 'show_user_profile', 'add_customer_tax_exempt_checkbox', 10 );
add_action( 'edit_user_profile', 'add_customer_tax_exempt_checkbox', 10 );
function add_customer_tax_exempt_checkbox( $user )
{
?>
<h3><?php _e("Tax status"); ?></h3>
<table class="form-table">
<tr>
<th><?php _e("Tax exempt"); ?></th>
<td>
<?php
woocommerce_form_field( 'tax_exempt', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('Allowed'),
), get_user_meta( $user->id, 'tax_exempt', true ) );
?>
</td>
</tr>
</table>
<?php
}
// Save allowed custom user field in admin
add_action( 'personal_options_update', 'save_customer_tax_exempt_checkbox' );
add_action( 'edit_user_profile_update', 'save_customer_tax_exempt_checkbox' );
function save_customer_tax_exempt_checkbox( $user_id )
{
if ( current_user_can( 'edit_user', $user_id ) ) {
update_user_meta( $user_id, 'tax_exempt', isset($_POST['tax_exempt']) ? '1' : '0' );
}
}
// Enabling or disabling tax calculation at checkout
add_filter( 'woocommerce_product_get_tax_class', 'disable_tax_calculation', 10, 2 );
add_filter( 'woocommerce_product_variation_get_tax_class', 'disable_tax_calculation', 10, 2 );
function disable_tax_calculation( $tax_class, $product ) {
if ( get_user_meta( get_current_user_id(), 'tax_exempt', true ) ) {
$tax_class = 'Zero Rate';
}
return $tax_class;
}
Code goes in functions.php file of your active child theme (or active theme). It should better work.
Related: Disable tax programmatically for a specific user role
Related
I have added to admin users a custom meta field using the following code:``
function wporg_usermeta_form_field_birthday( $user )
{
?>
<table class="form-table" id="table-form-dob" >
<tr>
<th><h3 style="margin: 0">Extra Meta Fields</h3></th>
</tr>
<tr>
<th>
<label for="user_dob">Birthday</label>
</th>
<td>
<input type="date"
class="regular-text ltr"
id="user_dob"
name="user_dob"
value="<?= esc_attr( get_user_meta( $user->ID, 'user_dob', true ) ) ?>"
title="Please use YYYY-MM-DD as the date format."
pattern="(19[0-9][0-9]|20[0-9][0-9])-(1[0-2]|0[1-9])-(3[01]|[21][0-9]|0[1-9])"
required>
</td>
</tr>
</table>
<script>
jQuery(function($){
jQuery('#table-form-dob tr').insertAfter(jQuery('#display_name').parentsUntil('tr').parent());
});
</script>
<?php
}
function wporg_usermeta_form_field_birthday_update( $user_id )
{
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
return update_user_meta(
$user_id,
'user_dob',
$_POST['user_dob']
);
}
add_action(
'show_user_profile',
'wporg_usermeta_form_field_birthday'
);
add_action(
'edit_user_profile',
'wporg_usermeta_form_field_birthday'
);
add_action(
'personal_options_update',
'wporg_usermeta_form_field_birthday_update'
);
add_action(
'edit_user_profile_update',
'wporg_usermeta_form_field_birthday_update'
);
register_meta('user', 'user_dob', array(
"type" => "string",
"show_in_rest" => true // this is the key part
));
I want to add this same field In woocommerce checkout page, so when the user get registered in woocommerce checkout page, we should be able to see this "Birthday" field (user_dob) in admin user profile/ edit section.
also, I am accessing user meta in rest API currently its showing meta in rest API after user saver values in check it should value in wp rest API.
How can I add this?
You can use the following that will add user_dob custom field to checkout account registration fields:
add_filter( 'woocommerce_checkout_fields', 'add_checkout_account_birthday_field' );
function add_checkout_account_birthday_field( $fields ){
$fields['account']['user_dob'] = array(
'type' => 'date',
'label' => __("Birthday", "woocommerce"),
'placeholder' => __("Please use YYYY-MM-DD as the date format.", "woocommerce"),
'class' => array('form-row-wide regular-text ltr'),
'required' => true,
'custom_attributes' => ['pattern' => '(19[0-9][0-9]|20[0-9][0-9])-(1[0-2]|0[1-9])-(3[01]|[21][0-9]|0[1-9])'],
);
return $fields;
}
add_action( 'woocommerce_checkout_update_customer', 'save_checkout_account_birthday_field', 10, 2 );
function save_checkout_account_birthday_field( $customer, $data ){
if ( isset($_POST['user_dob']) && ! empty($_POST['user_dob']) ) {
$customer->update_meta_data( 'user_dob', sanitize_text_field($_POST['user_dob']) );
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Once order is placed the birth date is saved to user data and displayed in admin user "Birthday" field.
I have added to admin users a custom meta field using the following code:``
function wporg_usermeta_form_field_birthday( $user )
{
?>
<table class="form-table" id="table-form-dob" >
<tr>
<th><h3 style="margin: 0">Extra Meta Fields</h3></th>
</tr>
<tr>
<th>
<label for="user_dob">Birthday</label>
</th>
<td>
<input type="date"
class="regular-text ltr"
id="user_dob"
name="user_dob"
value="<?= esc_attr( get_user_meta( $user->ID, 'user_dob', true ) ) ?>"
title="Please use YYYY-MM-DD as the date format."
pattern="(19[0-9][0-9]|20[0-9][0-9])-(1[0-2]|0[1-9])-(3[01]|[21][0-9]|0[1-9])"
required>
</td>
</tr>
</table>
<script>
jQuery(function($){
jQuery('#table-form-dob tr').insertAfter(jQuery('#display_name').parentsUntil('tr').parent());
});
</script>
<?php
}
function wporg_usermeta_form_field_birthday_update( $user_id )
{
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
return update_user_meta(
$user_id,
'user_dob',
$_POST['user_dob']
);
}
add_action(
'show_user_profile',
'wporg_usermeta_form_field_birthday'
);
add_action(
'edit_user_profile',
'wporg_usermeta_form_field_birthday'
);
add_action(
'personal_options_update',
'wporg_usermeta_form_field_birthday_update'
);
add_action(
'edit_user_profile_update',
'wporg_usermeta_form_field_birthday_update'
);
register_meta('user', 'user_dob', array(
"type" => "string",
"show_in_rest" => true // this is the key part
));
I want to add this same field In woocommerce checkout page, so when the user get registered in woocommerce checkout page, we should be able to see this "Birthday" field (user_dob) in admin user profile/ edit section.
also, I am accessing user meta in rest API currently its showing meta in rest API after user saver values in check it should value in wp rest API.
How can I add this?
You can use the following that will add user_dob custom field to checkout account registration fields:
add_filter( 'woocommerce_checkout_fields', 'add_checkout_account_birthday_field' );
function add_checkout_account_birthday_field( $fields ){
$fields['account']['user_dob'] = array(
'type' => 'date',
'label' => __("Birthday", "woocommerce"),
'placeholder' => __("Please use YYYY-MM-DD as the date format.", "woocommerce"),
'class' => array('form-row-wide regular-text ltr'),
'required' => true,
'custom_attributes' => ['pattern' => '(19[0-9][0-9]|20[0-9][0-9])-(1[0-2]|0[1-9])-(3[01]|[21][0-9]|0[1-9])'],
);
return $fields;
}
add_action( 'woocommerce_checkout_update_customer', 'save_checkout_account_birthday_field', 10, 2 );
function save_checkout_account_birthday_field( $customer, $data ){
if ( isset($_POST['user_dob']) && ! empty($_POST['user_dob']) ) {
$customer->update_meta_data( 'user_dob', sanitize_text_field($_POST['user_dob']) );
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Once order is placed the birth date is saved to user data and displayed in admin user "Birthday" field.
I created a some custom fields for Woocommerce in the check-out page. I was able to display them in WP admin area, email and invoices but not in the Front-End for User's Dashboard.
This is the code I used to create the field "CODICE SNEP" in my CHILD-THEM on function.php file:
// Create hook - CODICE SNEP
add_filter( 'woocommerce_checkout_fields' , 'codice_snep' );
function codice_snep ( $fields ) {
$fields['billing']['codice_snep'] = array(
'label' => __('Codice Snep', 'woocommerce'),
'placeholder' => _x('Codice Snep', 'placeholder', 'woocommerce'),
'required' => true,
'clear' => true
);
return $fields;
}
add_action( 'woocommerce_checkout_update_order_meta', 'codice_snep_order_meta' );
function codice_snep_order_meta( $order_id ) {
if ( ! empty( $_POST['codice_snep'] ) ) {
update_post_meta( $order_id, 'Codice Snep', sanitize_text_field( $_POST['codice_snep'] ) );
}
}
// Save the custom field 'codice_snep'
add_action( 'woocommerce_save_account_details', 'save_codice_snep_account_details', 12, 1 );
function save_codice_snep_account_details( $user_id ) {
// For Codice Snep
if( isset( $_POST['codice_snep'] ) )
update_user_meta( $user_id, 'codice_snep', sanitize_text_field( $_POST['codice_snep'] ) );
}
After that I show an error if you don't put the field:
// Show error if you don't insert CODICE SNEP
add_action('woocommerce_checkout_process', 'required_codice_snep_checkout_field_process');
function required_codice_snep_checkout_field_process() {
// Check if set, if its not set add an error.
if ( ! $_POST['codice_snep'] )
wc_add_notice( __( 'Compila il campo Codice SNEP .' ), 'error' );
}
Than I show the code in the Admin Area of WC, emails and invoice:
// Show field codice snep in the Wc pdf invoices plugin
add_action( 'wpo_wcpdf_after_order_data', 'wpo_wcpdf_codice_snep', 10, 2 );
function wpo_wcpdf_codice_snep ($template_type, $order) {
$document = wcpdf_get_document( $template_type, $order );
if ($template_type == 'invoice') {
?>
<tr>
<th>Codice Snep:</th>
<td><?php $document->custom_field('Codice Snep'); ?></td>
</tr>
<?php
}
}
// Show CODICE SNEP in Back End Wp
add_action( 'woocommerce_admin_order_data_after_billing_address', 'codice_snep_order_meta_admin', 10, 1 );
function codice_snep_order_meta_admin($order){
echo '<p><strong>'.__('Codice Snep').':</strong> ' . get_post_meta( $order->id, 'Codice Snep', true ) . '</p>';
}
// Show codoce snep in the order email
add_filter('woocommerce_email_order_meta_keys', 'my_custom_codice_snep_order_meta_keys');
function my_custom_codice_snep_order_meta_keys( $keys ) {
$keys[] = 'Codice Snep';
return $keys;
}
Everything seems to work until here.
The problem now is to just show (must be not editable) this field (codice_snep) in the Dashboard front-end.
Right now I'm trying with this but I can only see the label without the value of the field.
// Show codice snep in the Dashboard of the user in front-end.
add_action( 'woocommerce_account_dashboard', 'codice_snep_order_dashboard', 12, 1 );
function codice_snep_order_dashboard ($order){
echo '<p><strong>'.__('Codice Snep').':</strong> ' . get_post_meta( $order->id, 'Codice Snep', true ) . '</p>';
}
This is what happens: Field not showing on dashboard
Can you help me to fix this problem in order to show the value that the user insert in the check-out?
The code you are using is a bit outdated and/or some hooks are deprecated (even if they come from Woocommerce documentation).
The code below uses different hooks. It allows to display this field and the related data (when it exist), in checkout and in My account > Adresses > Billing fields, without any need of additional validation code.
Also when order is placed, the data is saved with one hook in the order and in the user meta data at the same time. So the code is more compact and uses the right hooks with the new CRUD methods introduced in Woocommerce 3.
Your revisited code:
// Display a custom field on checkout and on My account > edit billing address
add_filter( 'woocommerce_billing_fields' , 'adding_billing_codice_snep', 20, 1 );
function adding_billing_codice_snep ( $fields ) {
$fields['billing_codice_snep'] = array(
'label' => __('Codice Snep', 'woocommerce'),
'placeholder' => _x('Codice Snep', 'placeholder', 'woocommerce'),
'class' => array('form-row-wide'),
'required' => true,
'clear' => true,
);
return $fields;
}
// Save the custom field data to the order meta data and to user meta data
add_action( 'woocommerce_checkout_create_order', 'codice_snep_order_meta', 20, 2 );
function codice_snep_order_meta( $order, $data ) {
if ( isset( $_POST['billing_codice_snep'] ) && ! empty( $_POST['billing_codice_snep'] ) ) {
$order->update_meta_data('_billing_codice_snep', sanitize_text_field( $_POST['billing_codice_snep'] ) );
update_user_meta( $order->get_customer_id(), 'billing_codice_snep', sanitize_text_field( $_POST['billing_codice_snep'] ) );
}
}
// Order pages (frontend and admin): Display custom field "codice snep"
add_filter( 'woocommerce_order_details_after_order_table' , 'display_admin_order_meta_codice_snep', 20, 1 ); // Front
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_admin_order_meta_codice_snep', 20, 1 ); // Admin
function display_admin_order_meta_codice_snep( $order ){
$codice_snep = $order->get_meta('_billing_codice_snep', true );
if( ! empty( $codice_snep ) ){
$label = __('Codice Snep');
if( is_admin() ){ // Admin
echo '<p><strong>' . $label . ':</strong> ' . $codice_snep . '</p>';
}
else { // Front end: order view and Order received (thankyou)
echo '<table class="woocommerce-table"><tbody><tr>
<th>' . $label . ':</th><td>' . $codice_snep . '</td>
</tr></tbody></table>';
}
}
}
// Email notifications: Display custom field "codice snep"
add_filter( 'woocommerce_email_order_meta_fields' , 'display_email_codice_snep', 20, 3 );
function display_email_codice_snep ( $fields, $sent_to_admin, $order ) {
$codice_snep = $order->get_meta('_billing_codice_snep', true );
if( ! empty( $codice_snep ) )
$fields['codice_snep'] = array(
'label' => __("Codice Snep"),
'value' => $codice_snep,
);
return $fields;
}
// PDF Invoices: Display custom field "codice snep"
add_action( 'wpo_wcpdf_after_order_data', 'display_pdf_invoice_codice_snep', 20, 2 );
function display_pdf_invoice_codice_snep ($template_type, $order) {
$document = wcpdf_get_document( $template_type, $order );
if ($template_type == 'invoice') {
echo '<tr>
<th>' . __("Codice Snep") . ':</th>
<td>' . $order->get_meta('_billing_codice_snep', true ) . '</td>
</tr>';
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Order view:
My account > adresses > Billing
In woocommerce, I have successfully managed to add a custom field to a Woocommerce simple product and display that value on the additional info tab on a product page.
Now I am totally lost with the final piece of my jigsaw trying to insert a total row in cart and checkout pages with for the total calculated volume.
The custom field is to store the volume in m3 of a furniture item. When a client adds an item to the basket I would like to calculate the total m3 of the shipment by adding the values of all the custom fields together and displaying the total to the customer on the page. Can anyone point me in the right direction of how to add these custom fields and display them please.
So far my code looks like this
// Display Fields using WooCommerce Action Hook
add_action( 'woocommerce_product_options_general_product_data', 'woocom_general_product_data_custom_field' );
function woocom_general_product_data_custom_field() {
// Create a custom text field
// Number Field
woocommerce_wp_text_input(
array(
'id' => '_item_volume',
'label' => __( 'Item Shipping Volume', 'woocommerce' ),
'placeholder' => '',
'description' => __( 'Enter the volume her in m3.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => 'any',
'min' => '0.001'
)
)
);
}
// Hook to save the data value from the custom fields
add_action( 'woocommerce_process_product_meta', 'woocom_save_general_proddata_custom_field' );
/** Hook callback function to save custom fields information */
function woocom_save_general_proddata_custom_field( $post_id ) {
// Save Number Field
$number_field = $_POST['_item_volume'];
if( ! empty( $number_field ) ) {
update_post_meta( $post_id, '_item_volume', esc_attr( $number_field ) );
}
}
add_action( 'woocommerce_product_additional_information', 'custom_data_in_product_add_info_tab', 20, 1 );
function custom_data_in_product_add_info_tab( $product ) {
//Product ID - WooCommerce compatibility
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
// Get your custom fields data
$custom_field1 = get_post_meta( $product_id, '_item_volume', true );
// Set your custom fields labels (or names)
$label1 = __( 'Shipping Volume m3', 'woocommerce');
// The Output
echo '<h3>'. __('Item Shipping Volume', 'woocommerce') .'</h3>
<table class="custom-fields-data">
<tbody>
<tr class="custom-field1">
<th>'. $label1 .'</th>
<td>'. $custom_field1 .'</td>
</tr>
</tbody>
</table>';
}
The following will display the total cart volume in cart and checkout page:
add_action( 'woocommerce_cart_totals_before_shipping', 'display_cart_volume_total', 20 );
add_action( 'woocommerce_review_order_before_shipping', 'display_cart_volume_total', 20 );
function display_cart_volume_total() {
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'], '_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
if( $total_volume > 0 ){
// The Output
echo ' <tr class="cart-total-volume">
<th>' . __( "Total Shipping Volume", "woocommerce" ) . '</th>
<td data-title="total-volume">' . number_format($total_volume, 2) . ' m3</td>
</tr>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I set up a simple checkbox field in the user account admin interface. Here is how I am displaying/saving it:
function show_free_ground_field( $user ) {
?>
<h3>Free Ground Shipping</h3>
<table class="form-table">
<tr>
<th>Free ground for order > $1000</th>
<td>
<?php
woocommerce_form_field( 'freeGround', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('Yes'),
), '' );
?>
</td>
</tr>
</table>
<?php
}
add_action( 'show_user_profile', 'show_free_ground_field' );
add_action( 'edit_user_profile', 'show_free_ground_field' );
function save_free_ground_field( $user_id ) {
if ( !current_user_can( 'edit_user', $user_id ) ){
return false;
}
if ( ! empty( $_POST['freeGround'] ) ){
update_usermeta( $user_id, 'freeGround', $_POST['freeGround'] );
}
}
add_action( 'personal_options_update', 'save_free_ground_field' );
add_action( 'edit_user_profile_update', 'save_free_ground_field' );
It displays fine, but if I check it off and re-visit the same user after saving the checkbox is unchecked. How do I fix that?
You should need to get the saved value for this checkbox field in the first function:
add_action( 'show_user_profile', 'show_free_ground_field' );
add_action( 'edit_user_profile', 'show_free_ground_field' );
function show_free_ground_field( $user ) {
?>
<h3>Free Ground Shipping</h3>
<table class="form-table">
<tr>
<th>Free ground for order > $1000</th>
<td>
<?php
$freeGround = get_user_meta( $user->id, 'freeGround', true );
if ( empty( $freeGround ) ) $freeGround = '';
woocommerce_form_field( 'freeGround', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('Yes'),
), $freeGround );
?>
</td>
</tr>
</table>
<?php
}
This should work now