Related
I have an Ajax checkout on my woocommerce installation and I am trying to capture the custom fields on the checkout. I am using below code to try to capture the field called add_gift_box, the field appears fine on my checkout page.
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ($_POST['add_gift_box']) {
update_post_meta( $order_id, '_add_gift_box', esc_attr($_POST['add_gift_box']));
}
}
When I press "place order" button it creates a new order but the value of this field does not get saved in the database. I don't think even the hook gets fired. As I said everything is being handled by Ajax. The checkout page is one page checkout.
I need help sorting this out. Thanks in advance.
UPDATE
I am setting the field as follows:
add_action( 'woocommerce_after_checkout_billing_form', 'add_box_option_to_checkout' );
function add_box_option_to_checkout( $checkout ) {
woocommerce_form_field( 'add_gift_box', array(
'type' => 'radio',
'class' => array('add_gift_box form-row-wide'),
'label_class' => array('checkbox'),
'input_class' => array('input-checkbox'),
'required' => true,
'options' => array(
'option_1' => 'option1</br>' ,
'option_2' =>'option2</br>',
'option_3' =>'option3</br>',
'option_4' =>'option4</br>',
'option_5' =>'option5</br>',
'option_6' =>'option6</br>',
'option_7' =>'option7</br>',
),
'label' => __('Select Option'),
'placeholder' => __(''),
), $checkout->get_value( 'add_gift_box' ));
}
here is a plugin to add custom field on checkout page woocomerce https://wordpress.org/plugins/woo-custom-checkout-field/
or if you want to add by code you can follow codding from here
https://www.cloudways.com/blog/custom-field-woocommerce-checkout-page/
add_action('woocommerce_after_order_notes', 'customise_checkout_field');
function customise_checkout_field($checkout)
{
echo '<div id="customise_checkout_field"><h2>' . __('Heading') . '</h2>';
woocommerce_form_field('customised_field_name', array(
'type' => 'text',
'class' => array(
'my-field-class form-row-wide'
) ,
'label' => __('Customise Additional Field') ,
'placeholder' => __('Guidence') ,
'required' => true,
) , $checkout->get_value('customised_field_name'));
echo '</div>';
}
Do you have a plugin activated which creates the order by hooking into woocommerce_create_order filter? I don't see any other reason why add_gift_box isn't saved or why woocommerce_checkout_update_order_meta isn't firing.
The original code from includes/class-wc-checkout.php is
/**
* Action hook to adjust order before save.
* #since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order', $order, $data );
// Save the order.
$order_id = $order->save();
do_action( 'woocommerce_checkout_update_order_meta', $order_id, $data );
So, you could create log files in your themes directory to see which hooks are firing like this:
add_action('woocommerce_checkout_update_order_meta', function ($order_id) {
file_put_contents(__DIR__ . '/woocommerce_checkout_update_order_meta.log', 'woocommerce_checkout_update_order_meta fired for order id ' . $order_id);
});
add_action('woocommerce_checkout_create_order', function () {
file_put_contents(__DIR__ . '/woocommerce_checkout_create_order.log', 'woocommerce_checkout_create_order fired');
});
I've created new post status. Here's code from my custom plugin:
add_action('init', 'new_post_status_add');
function new_post_status_add () {
register_post_status('refused', array(
'label' => _x('Refused', 'post'),
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop('Refused <span class="count">(%s)</span>', 'Refused <span class="count">(%s)</span>'),
));
}
But it's not working - not visible in edit form and quick edit form:
What should I do to make status available?
Sorry for delayed reply. I hope my comment would help others.
Custom Post Status is an unsolved issue that has been there for such a long time, or at least since 8yrs ago on Wordpress website here.
Wordpress noted on codex document for function register_post_status():
This function does NOT add the registered post status to the admin panel. This functionality is pending future development. Please refer
to Trac Ticket #12706. Consider the action hook
post_submitbox_misc_actions for adding this parameter.
There is a work around that I could use for one of my projects. I mixed solutions that described on this url and this url.
The summary is that Wordpress doesn't show custom post status automatically. It needs to be manipulated by using your theme's function.php.
First step
Create custom post status, same as you mentioned. just, don't forget that "This function should not be called before the 'init' action."
function j_custom_post_status() {
$args = array(
'label' => _x( 'Refused', 'post'),
'label_count' => _n_noop( 'Refused <span class="count">(%s)</span>', 'Refused (%s)', 'post' ),
'public' => false,
'internal' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'exclude_from_search' => false,
);
register_post_status( 'j_refused', $args );
add_action( 'init', 'j_custom_post_status', 0 );
I use WP Generator to get the code snippet above.
Second step
Tweak Admin Panel to show the custom status in Quick Edit Menu (QE) on posts list page edit.php. We need JQuery's help to do so and we need to add JQuery code to Admin Panel footer.
function j_append_status_list(){
//array of all custom status that you created
$arr_customstatus = array(
'refused',
'other_custom_status',
);
echo '<script>';
//flush
$tmp = '';
//generate string
foreach ($arr_customstatus as $pkey ) {
$tmp.= '<option value="'. $pkey .'">'. $pkey .'</option>';
}
echo "jQuery(document).ready( function() { jQuery( 'select[name=\"_status\"]' ).append( ". $tmp ." );
});";
echo '</script>';
}
add_action('admin_footer-edit.php','j_append_status_list');
Note: The function above doesn't automatically select the status in QE in case the post's status has been modified before. To add the selected="true" to the option, you need to use the global $post and check the current status, same as the the method used in the Third step below.
Third step
Now, we need to add custom post status meta to post edit page too.
function j_custom_status_metabox(){
global $post;
$custom = get_post_custom( $post->ID );
$status = $custom["_status"][0];
// Array of custom status messages
$arr_customstatus = array(
'refused',
'other_custom_status',
);
//Flush
$tmp = '';
foreach( $jstats as $pkey ) {
if( $status == $pkey ){
$tmp.= '<option value="'.$pkey.'" selected="true">'. $pkey .'</option>';
} else {
$tmp.= '<option value="'. $pkey .'">'. $pkey .'</option>';
}
}
echo '<script>';
echo "jQuery(document).ready( function() { jQuery( 'select[name=\"_status\"]' ).append( ". $tmp ." );
});";
echo '</script>';
}
add_action('admin_footer-post.php','j_custom_status_metabox');
add_action('admin_footer-post-new.php','j_custom_status_metabox');
Step four
In case you like to show a message in posts lists based on custom post status, you can add this function too. However, this is optional.
function j_display_status_label( $statuses ) {
global $post;
//Display label on all posts list but not on the custom status list
if( get_query_var( 'post_status' ) != 'refused' ){
if( $post->post_status == 'refused' ){
return array('Refused');
}
}
return $statuses;
}
add_filter( 'display_post_states', 'j_display_status_label' );
Note: The function above doesn't include all future custom status. You need to add foreach loop in case you need.
Try to use this code,
// Register Custom Status
function custom_post_status() {
$args = array(
'label' => _x( 'Refused', 'Status General Name', 'text_domain' ),
'label_count' => _n_noop( 'Refused (%s)', ' (%s)', 'text_domain' ),
'public' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'exclude_from_search' => false,
);
register_post_status( 'refused', $args );
}
add_action( 'init', 'custom_post_status', 0 );
I hope this will work for you.
Try adding PHP_INT_MAX to your j_custom_post_status action
add_action( 'init', 'j_custom_post_status', PHP_INT_MAX );
I am using woocommerce with Wordpress and have added some custom fields to the checkout:
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
$extra_fields = array('job_title', 'company', 'telephone', 'occupation');
foreach($extra_fields as $key => $value) {
woocommerce_form_field($value, array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __($label),
'value' => '',
), $checkout->get_value( $value ));
}
}
Now currently, these appear in the checkout fine, not sure if using woocommerce_after_order_notes is right in this case. I have also added some custom fields to the user meta that correspond to the fields added to the checkout - which all display in the user profile page:
function add_contact_methods( $contactmethods ) {
$contactmethods['job_title'] = 'Job Title';
$contactmethods['company'] = 'Company Name';
$contactmethods['telephone'] = 'Telephone';
$contactmethods['occupation'] = 'Occupation';
$contactmethods['refer'] = 'How you heard about us?';
return $contactmethods;
}
add_filter('user_contactmethods','add_contact_methods',10,1);
As you can imagine, if I update any of these field in any profile page, it works fine but what I cant seem to do is update the user meta when a new user makes a purchase, it does not update the user meta for these fields in the database.
I understand alot of how this works, and understand that I must hook into a Woocommerce process to add the fields into the process. So I have added this code into my functions too:
add_action('woocommerce_checkout_update_user_meta', 'my_custom_checkout_field_update_user_meta');
function my_custom_checkout_field_update_user_meta( $user_id ) {
global $extra_fields;
foreach($extra_fields as $key => $value) {
if ($_POST[$value]) update_user_meta( $user_id, $value, esc_attr($_POST[$value]));
}
}
Now the twist is, this works if a user who is already signed in as a member, makes a repurchase and goes through the checkout - the reason this works is because $user_id already exists, but when a new user is checking out, they do not yet exist as a user, hence the function cannot update the user meta of NIL where $user_id does not exist.
My question is, how do I hook into the checkout process, presumably AFTER the user has been created, so I that I can get the $user_id returned, and execute this function to update the user meta.
class-wc-checkout.php line 639 creates the new user with $this->customer_id = wp_insert_user( apply_filters( 'woocommerce_new_customer_data', $new_customer_data ) ); The new customer data is an array listed just above that line.
Following that, you can access the user id with line 649's action do_action( 'woocommerce_created_customer', $this->customer_id );
It is unlikey, in your case, you will need to use the filter, but simply add the action 'woocommerce_created_customer', pull in the id, and add the meta.
When customer is not logged in checkout page should be acceptable field customer want to create a new account.Below sample code change in checkout page when customer order a new item and update user meta data.
function user_extra_meta_fields(){
return array(
'job_title' => __( 'Job Title', 'yourtext_domain'),
'company' => __( 'Company Name', 'yourtext_domain'),
'telephone' => __( 'Telephone', 'yourtext_domain'),
'occupation' => __( 'Occupation', 'yourtext_domain'),
'refer' => __( 'How you heard about us?', 'yourtext_domain'),
);
}
function add_contact_methods( $contactmethods ) {
$contactmethods = array_merge( $contactmethods, user_extra_meta_fields());
return $contactmethods;
}
add_filter('user_contactmethods','add_contact_methods',10,1);
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
foreach( user_extra_meta_fields() as $name => $label) {
$value = '';
if( is_user_logged_in() )
$value = get_user_meta( get_current_user_id(), $name, true );
woocommerce_form_field( $name, array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => $label,
), $value );
}
}
add_action( 'woocommerce_checkout_process', 'user_fields_woocommerce_checkout_process' );
function user_fields_woocommerce_checkout_process(){
if( is_user_logged_in() )
add_action('woocommerce_checkout_update_user_meta', 'my_custom_checkout_field_update_user_meta' );
else
add_action( 'woocommerce_created_customer', 'my_custom_checkout_field_update_user_meta' );
}
function my_custom_checkout_field_update_user_meta( $user_id ) {
foreach( array_keys( user_extra_meta_fields() ) as $meta_name ){
if( isset( $_POST[$meta_name] ) ){
$meta_value = $_POST[$meta_name] ? esc_attr($_POST[$meta_name]) : '';
update_user_meta( $user_id, $meta_name, $meta_value );
}
}
}
// if want to validate field
add_action( 'woocommerce_after_checkout_validation', 'user_fields_woocommerce_after_checkout_validation' );
function user_fields_woocommerce_after_checkout_validation( $posted ){
$validate = true;
if( ! is_user_logged_in() && empty( $posted['createaccount'] ) )
$validate = false;
if( $validate == false )
return;
$meta_data = user_extra_meta_fields();
foreach( array_keys( $meta_data ) as $meta_name ){
if( empty($_POST[$meta_name]) )
wc_add_notice( sprintf( __(' <strong>%s</strong> is required.', 'yourtext_domain'), $meta_data[$meta_name] ), 'error' );
}
}
I have a few custom fields within my WooCommerce setup, however I am trying to get them to display on the order view.
This is my code (one custom field for the sake of it):
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
echo '<div id="my_custom_checkout_field"><h3>'.__('Extra Information').'</h3>';
woocommerce_form_field( 'date_of_birth', array(
'type' => 'select',
'class' => array('my-field-class form-row-wide'),
'label' => __('Date of Birth'),
'placeholder' => __('Enter something'),
'required' => true,
'options' => array(
'1995' => __('1995', 'woocommerce' ),
'1994' => __('1994', 'woocommerce' )
)
),
$checkout->get_value( 'date_of_birth' ));
I thought this below would do the job on adding it to the order view, however it does not.. or I am completely wrong and the custom field is not saving?
add_action('woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta');
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ($_POST['date_of_birth']) update_post_meta( $order_id, 'Extra Information', esc_attr($_POST['date_of_birth']));
I then looked through stackoverflow and found some more information and tried this:
function my_custom_checkout_field1($order){
echo "<p><strong>Date of Birth:</strong> " . $order->order_custom_fields['_date_of_birth'][0] . "</p>";
}
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field1', 10, 1 );
That does add a "Date of Birth:" to the order view, but unfortunately it is empty afterwards. I tried both _date_of_birth and date_of_birth, apparently it needed the extra _ infront, I got that answer from here: Show custom field on order in woocommerce
I have tried to look inside the database, but can not find the extra details on an order displayed anywhere (no regular details either except order id's).
Can anyone tell me what I am doing wrong and what I can do to make the field show up on the order view?
Thanks
I don´t know, but this:
'Extra Information'
here:
update_post_meta($post_id, $meta_key, $meta_value, $prev_value);
Are you sure 'Extra Information' is the $meta_key? Is not "date_of_birth"?
I am trying to add a form to my checkout page so when a user clicks the 'Tax Exempt' checkbox, a textbox will popup and ask the user what the Tax Exempt Id number is.
I got all of that working great, and I even added the update_totals_on_change class to my form field so it will update the totals.
My next step was to add an action/filter on a method so when the update_totals_on_change executes, I can set the tax to 0 and then it will finish calculating the total.
Does anyone know which functions I can hook on to?
Looking at the checkout.js file in WooCommerce, they set the action to woocommerce_update_order_review for the ajax operation.
I tried following that but soon got lost.
I was thinking I could add some post data by hooking in to woocommerce_checkout_update_order_review
and then hooking in to woocommerce_before_calculate_totals to modify the tax stuff, but I have no idea what I need to modify.
Am I even on the right path?
Alright, I finally figured it out in case anyone is interested.
In my plugin, I made a form after the order notes by hooking in to this function: 'woocommerce_before_order_notes'
add_action('woocommerce_before_order_notes', array(&$this, 'taxexempt_before_order_notes') );
my 'taxexempt_before_order_notes' function contained:
function taxexempt_before_order_notes( $checkout ) {
echo '<div style="clear: both"></div>
<h3>Tax Exempt Details</h3>';
woocommerce_form_field( 'tax_exempt_checkbox', array(
'type' => 'checkbox',
'class' => array('tiri taxexempt'),array( 'form-row-wide', 'address-field' ),
'label' => __('Tax Exempt'),
), $checkout->get_value( 'tax_exempt_checkbox' ));
woocommerce_form_field( 'tax_exempt_name', array(
'type' => 'text',
'class' => array('form-row-first', 'tiri', 'taxexempt', 'textbox', 'hidden'),
'label' => __('Tax Exempt Name'),
), $checkout->get_value( 'tax_exempt_name' ));
woocommerce_form_field( 'tax_exempt_id', array(
'type' => 'text',
'class' => array('form-row-last', 'tiri', 'taxexempt', 'textbox', 'hidden', 'update_totals_on_change'),
'label' => __('Tax Exempt Id'),
), $checkout->get_value( 'tax_exempt_id' ));
}
Then the most important woocommerce function to hook was: 'woocommerce_checkout_update_order_review'
add_action( 'woocommerce_checkout_update_order_review', array(&$this, 'taxexempt_checkout_update_order_review' ));
function taxexempt_checkout_update_order_review( $post_data ) {
global $woocommerce;
$woocommerce->customer->set_is_vat_exempt(FALSE);
parse_str($post_data);
if ( isset($tax_exempt_checkbox) && isset($tax_exempt_id) && $tax_exempt_checkbox == '1' && !empty($tax_exempt_id))
$woocommerce->customer->set_is_vat_exempt(true);
}
I simply parsed out the $post_data that is the serialized form data from the checkout.js file in woocommerce and checked if my part of the form was filled out correctly.
If it was, then I would set the tax exempt for the user.
The accepted solution didn't work for me, but I modified it to use the following:
//=============================================================================
// ADD TAX EXEMPT CHECKMARK
// =============================================================================
add_action( 'woocommerce_after_order_notes', 'qd_tax_exempt');
function qd_tax_exempt( $checkout ) {
echo '<div id="qd-tax-exempt"><h3>'.__('Tax Exempt').'</h3>';
woocommerce_form_field( 'shipping_method_tax_exempt', array(
'type' => 'checkbox',
'class' => array(),
'label' => __('My organization is tax exempt.'),
'required' => false,
), $checkout->get_value( 'shipping_method_tax_exempt' ));
echo '</div>';
}
add_action( 'woocommerce_checkout_update_order_review', 'taxexempt_checkout_update_order_review');
function taxexempt_checkout_update_order_review( $post_data ) {
global $woocommerce;
$woocommerce->customer->set_is_vat_exempt(FALSE);
parse_str($post_data);
if ( isset($shipping_method_tax_exempt) && $shipping_method_tax_exempt == '1')
$woocommerce->customer->set_is_vat_exempt(true);
}
The key here is understanding that any field with a name that starts with shipping_method is going to inherit this updating order functionality (which was the part that didn't work for me). I found this answer at http://www.affectivia.com/blog/have-a-checkbox-on-the-checkout-page-which-updates-the-order-totals/
After a long search I found that there is a method for the cart object called remove_taxes() .
So, after setting a user meta for the tax exempt users, this cancels the tax totals.
function remove_tax_for_exempt( $cart ) {
global $current_user;
$ok_taxexp = get_the_author_meta( 'granted_taxexempt', $current_user->ID );
if ($ok_taxexp){ // now 0 the tax if user is tax exempt
$cart->remove_taxes();
}
return $cart;
}
add_action( 'woocommerce_calculate_totals', 'remove_tax_for_exempt' );
Because $cart->remove_taxes(); is deprecated. This is what I used instead.
I didn't have a form on the frontend, but have a user roll that is tax exempt. This was my solution.
Also worth noting that set_is_vat_exempt(true) also works in the US to set as tax exempt.
/**
* Set customer as tax exempt if user is a wholesale customer
*/
function remove_tax_for_exempt( $cart ) {
global $woocommerce;
if ( is_user_logged_in() && current_user_can( 'wholesale_customer' ) ) {
$woocommerce->customer->set_is_vat_exempt(true);
}
return $cart;
}
add_action( 'woocommerce_calculate_totals', 'remove_tax_for_exempt' );
Since this answer still pops up on google, I thought I'd share that setting the customer as tax exempt only works during checkout, if you need to edit the order on the back-end after it is placed and use the "recalculate" button, the taxes will still appear. Fortunately there is a hook for this as well:
function remove_tax_for_exempt($exempt, $order){
return $exempt || user_can($order->get_user_id(), 'wholesale_customer');
}
add_filter('woocommerce_order_is_vat_exempt', 'remove_tax_for_exempt', 10, 2);