Before this gets flagged as a duplicate of Save custom post fields value before Woocommerce email send I will point out that the answer given to that question is a) not accepted and b) refers to a hook that is no longer in the documentation.
I'm helping out a friend with their WordPress WooCommerce site, and they want to be able to add a tracking number to the email that gets automatically sent out to a customer once the order status is set to "Completed". I've managed to get this all working using the Advanced Custom Fields plugin and some code in functions.php. The problem I have is the user friendliness isn't quite there when it comes to processing the order, it being a double-barrelled operation:
Fill in the tracking number custom field on the Edit Order screen and then click the Update button
Set the status to Completed and the click the Update button again
Ideally, it would be nice to fill in the tracking number text field and change the status dropdown value to Completed, and then hit update. If that is done though, the email doesn't get the custom field populated, presumably because the custom fields get saved after the email gets triggered.
Is there a hook in WooCommerce that allows, on update of the order, saving of custom fields to occur before triggering the email?
For reference, this is my current code in functions.php, where (despite changing the priority of the operation to 1) it doesn't populate the tracking number on the email:
add_action( 'woocommerce_email_before_order_table', 'add_content_specific_email', 1, 4 );
function add_content_specific_email( $order, $sent_to_admin, $plain_text, $email ) {
if ( $email->id == 'customer_completed_order' ) {
$ausPostTrackingNumber = get_post_meta( $order->get_id(),'australia_post_tracking_number',true);
if($ausPostTrackingNumber != '') {
echo '<p class="email-auspost-text">Your Australia Post tracking number is ' . $ausPostTrackingNumber . '</p>';
}
}
}
Related
I am trying to customise the Woocommerce myaccount page, in particular the edit address page.
I want to display both the shipping + billing address forms on a single page. Ideally, in a single form with a one save button. I also need to remove a lot of the fields, so that it's a much simpler form of just an address (no name, company, etc).
I have implemented the code found on This Answer. It works nicely in that it shows both forms. However, I cannot remove the fields from the forms. If I try code like this:
add_filter( 'woocommerce_billing_fields' , 'custom_override_billing_fields' );
add_filter( 'woocommerce_shipping_fields' , 'custom_override_shipping_fields' );
function custom_override_billing_fields( $fields ) {
unset($fields['billing_country']);
unset($fields['billing_company']);
unset($fields['billing_first_name']);
unset($fields['billing_last_name']);
unset($fields['billing_phone']);
unset($fields['billing_email']);
return $fields;
}
function custom_override_shipping_fields( $fields ) {
unset($fields['shipping_country']);
unset($fields['shipping_company']);
unset($fields['shipping_first_name']);
unset($fields['shipping_last_name']);
return $fields;
}
It doesn't work, the fields are no longer shown but the form does not save on click... it just redirects to /my-account/edit-address/billing/ - and doesn't save. (the same form shown on this page doesn't save either).
I've also tried:
foreach ( $billing_fields as $key => $field ) :
if($key != 'billing_first_name' && $key != 'billing_last_name') :
woocommerce_form_field( $key, $field, $userMeta[$key][0] );
endif;
endforeach;
This removes the field from displaying, BUT the validation still exists - and any filter code I add to functions using
woocommerce_checkout_fields to remove the validation doesn't seem to affect this form at all.
Is there a way to either:
Remove fields from this form generated by woocommerce_form_field including the validation?
Create a custom form that allows me to set the input fields manually in the code, and update any fields that are there, ignoring the validation from Woocommerce completely?
This should work 100%. You need to state whether the fields you are removing is from billing or shipping and this is done by adding the ['billing'] or ['shipping'], whichever it is.
After this, adding the function directly to woocommerce_checkout_fields will apply both for billing and shipping.
For phone and company fields you can disable it in admin panel itself, do it.
Edit: And yes, all validation that was involved with the fields in the past will be removed. You can then apply any validation you need.
add_filter( 'woocommerce_checkout_fields' , 'brandimagemarketer_remove_billing_fields_checkout' );
function brandimagemarketer_remove_billing_fields_checkout( $fields ) {
unset($fields['billing']['billing_country']);
unset($fields['billing']['billing_first_name']);
unset($fields['billing']['billing_last_name']);
unset($fields['billing']['billing_email']);
unset($fields['shipping']['shipping_country']);
unset($fields['shipping']['shipping_first_name']);
unset($fields['shipping']['shipping_last_name']);
unset($fields['shipping']['shipping_email']);
return $fields;
}
I've added a couple new fields to the date-picker.php template in WooCommerce Bookings and need to trigger the AJAX recalculation of the booking cost on the product page when these fields are updated.
Currently this recalculation is triggered when the standard fields in date-picker.php are updated. However, I will be using the additional fields to recalculate the duration and booking cost so I need this AJAX recalculation triggered when updates are made to the new fields as well.
How can I trigger the AJAX recalculation? I have tried simply triggering a change event on one of the standard fields, but that doesn't seem to work.
I had the same issue. If it helps someone in the future, heres my work around :
Go to
/wp-content/plugins/woocommerce-bookings/templates/single-product/add-to-cart/booking.php
This is the template for the single product booking form.
You can then move one of the actions such as <?php do_action( 'woocommerce_before_add_to_cart_button' ); ?> (or create your own action) within the <div id="wc-bookings-booking-form"/>.
Now add a custom field or code to this action (I used wc-fields-factory). When you change a field value within the booking form the AJAX recalculation is triggered.
You can hook into this trigger in your functions.php file (within your theme) to alter the cost based on the custom fields you have added like so :
add_filter( 'booking_form_calculated_booking_cost', 'my_booking_cost', 10, 3 );
function my_booking_cost( $booking_cost, $booking, $posted ) {
/* use fields here to show a new booking cost*/
$my_time = $posted[ <name of custom field> ]; //access field within booking form
$booking_cost = 1; //whatever your calc is
return $booking_cost;
}
I am using the code below to "re-direct" back to the product page because if not, the page stays at the "bottom" where the review form is.
My question is this:
how do I print / display a message on the product page after the review has been submitted by the customer?
Here's my code:
add_filter('comment_post_redirect', 'redirect_after_comment');
function redirect_after_comment($location) {
$location = wp_get_referer();
wc_add_notice( __( 'Thank you for writing a review. Use this coupon code <code>FIVEOFF</code> on the checkout and get $5 OFF!', 'woocommerce' ), 'success' );
return $location;
}
Problem is, the message is not being displayed and I tried adding in a do_action for wc_notice which did not help either.
You should put wc_add_notice() before return $location;. Because return stops the execution of the function and sends the value back, so the statements after it won't be executed.
I have a field "file_url" defined by code for every order item. But i must have to insert also this meta field editable in the backend order page. I can have also another input field into each order item line, i just need an input field to save this data for each order item line.
Woocommerce checks if order is editable or not and it does not allow to edit orders that are processing or completed. But there is a hook to change this. You can simply add this code to functions.php in your theme folder:
function make_orders_editable( $is_editable, $order ) {
// Allow only for admin and moderators
if ( current_user_can( "manage_options" ) ) {
return true;
}
}
add_filter( 'wc_order_is_editable', 'make_orders_editable', 10, 2 );
But be careful - this will allow to edit ALL data in orders for admin user and moderators.
I am using the save_post action to inspect a metadata field in a custom post and take some action on that value. This is the essential guts of how I am doing it:
add_action('save_post', 'my_save_post');
function my_save_post($post_id)
{
// Check if not autosaving, processing correct post type etc.
// ...
// Get the custom field value.
$my_field_value = get_post_meta($post_id, 'my_field', true);
// Do some action
// ...
}
This works fine when updating the post through the admin page. However, when first creating the post, the my_field_value is always empty. The field does get saved correctly, but this action trigger does not seem to be able to see it, nor any other custom field values.
I would like the action to be performed on all posts of this type created, and I will be importing many through the CSV Imported plugin. Even then, the custom fields do get imported correctly, and the action trigger does get fired for each row imported, but the save_post action still cannot see the custom field value.
So far as I can see from documentation, the post has already been created by the time this action fires, so I should always be able to see that custom metafield.
The answer, it seems, is in the order in which things happen. When creating a post from a form, the custom fields are all collected by the appropriate actions and added to the post before my save_post action fires. This means my trigger is able to see those custom field values.
When importing from CSV, the basic post is created first, and then the custom metafields are added. The save_post trigger fires on the first creation, before the metafields are added, and so the custom field data is not visible to the save_post action.
My solution was to catch the updates of the metadata using the updated_post_meta and added_post_meta actions as well as the save_post action:
add_action('updated_post_meta', 'my_updated_post_meta', 10, 4);
add_action('added_post_meta', 'my_updated_post_meta', 10, 4);
function my_updated_post_meta($meta_id, $post_id, $meta_key, $meta_value)
{
// Make sure we are handling just the meta field we are interested in.
if ($meta_key != 'my_custom_field') return;
if (wp_is_post_revision($post_id)) return;
if (get_post_type($post_id) != 'my_post_type') return;
if (trim($meta_value) == '') return;
// Do my custom task (linking this post to a parent post in a different
// post type). This is the same task performed by the save_post action.
my_link_product_track($post_id, trim($meta_value));
}
That is essentially what I do, and it seems to work well. I do encapsulate all the above into a custom class in the theme, and don't recommend using global scope variables as shown here, but this is just to show the method.
You should look at using $post->ID instead of $post_id -
$my_field_value = get_post_meta($post->ID, 'my_field', true);
get_post_meta in the Codex
EDIT:
Could you do something like this?
if($post->ID == ''){
$pid = $post_id;
} else {
$pid = $post->ID;
}
//$pid = $post->ID or $post_id, whichever contains a value
$my_field_value = get_post_meta($pid, 'my_field', true);
something that looks for a value in $post->ID and $post_id, and uses whichever one isn't blank?