I wish to display the full price that the customer paid for the item, rather than 'price without tax' and 'tax'.
I followed this: Woocommerce - Admin Order Detail Page and it almost gets me what I want, however it is not perfect to my case and I am failing to modify it. I am not very experienced in PHP and worried about breaking anything as it is a live webshop.
I managed to change the currency sign easily enough and put it at the end of the price rather than the start, but there's a couple of other things I wish to change and I'm not sure how/where.
We use commas instead of periods as decimal points. This is reflected without a problem everywhere else, but not with this custom snippet. How do I change this?
In the case of free item/shipping, this is empty. I tried to change "if $val>0" to just "if $val" but it made no difference.
I'd like this entire column to be on the right (as the last column) rather than on the left. So it reads Quantity, Total, Tax, Total incl. Tax.
Here is my current, slightly modified code:
function action_woocommerce_admin_order_item_values( $null, $item, $absint ) {
$val = ($item['type'] == 'line_item' || $item['type'] == 'shipping') ? $item['total'] + $item['total_tax'] : ' ';
$valdecimal = wc_format_decimal( $val, $dp='', $trim_zeros );
?>
<td class="item_fcost" data-sort-value="<?php echo $val; ?>">
<div class="view" style="font-weight: bold; text-align: right; padding-right: 10px;">
<?php if ($val) echo $valdecimal; echo ' DKK'; ?>
</div>
</td>
<?php
};
add_action( 'woocommerce_admin_order_item_values', 'action_woocommerce_admin_order_item_values', 10, 3 );
function action_woocommerce_admin_order_item_headers( $order ) {
echo '<th class="item_fcost sortable" data-sort="float" style="text-align: right;">Pris inkl. moms</th>';
};
add_action( 'woocommerce_admin_order_item_headers', 'action_woocommerce_admin_order_item_headers', 10, 3 );
Edit: I found out that this code also breaks/throws a fatal error with refunded orders. I've had to deactivate the code entirely.
I'm not experienced enough to know how to modify it and am still looking for help.
There was a little problem in your code and that's that you were accesing the $item as it was an array while it's actually an object. One of the objects extending the WC_Order_Item abstract classes. Also, the total and total_tax properties of $item are strings, not numbers so when you are adding them together, you first need to convert them to floats.
Lastly, I reasearched how the total is displayed by Woocommerce and they include the refunded total too. So I added the refunded total including VAT/tax too.
You can find updated code below.
function action_woocommerce_admin_order_item_values( $null, WC_Order_Item $item) {
$val = floatval($item->get_total()) + floatval($item->get_total_tax());
?>
<td class="item_full_cost" data-sort-value="<?= $val ?>">
<div class="view" style="font-weight: bold; text-align: right; padding-right: 10px;">
<?= wc_price($val, ['currency' => $item->get_order()->get_currency()]) ?>
</div>
<?php
$refunded = $item->get_order()->get_total_refunded_for_item( $item->get_id() );
if ( $refunded ) {
echo '<small class="refunded">-' . wc_price( $refunded, array( 'currency' => $item->get_order()->get_currency() ) ) . '</small>';
}
?>
</td>
<?php
};
add_action( 'woocommerce_admin_order_item_values', 'action_woocommerce_admin_order_item_values', 10, 2 );
function action_woocommerce_admin_order_item_headers( $order ) {
echo '<th class="line_full_cost sortable" data-sort="float" style="font-weight:bold;text-align: right;">' . __('Price incl. VAT') .'</th>';
};
add_action( 'woocommerce_admin_order_item_headers', 'action_woocommerce_admin_order_item_headers', 10, 3 );
Related
I use this code below in the my functions.php file.
//Add price inc VAT column on admin order page
function action_woocommerce_admin_order_item_values( $null, $item, $absint ) {
$val = ($item['type'] == 'line_item' || $item['type'] == 'shipping') ? $item['total'] + $item['total_tax'] : ' ';
$valdecimal = wc_format_decimal( $val, $dp='', $trim_zeros );
?>
<td class="item_fcost" data-sort-value="<?php echo $val; ?>">
<div class="view" style="font-weight: bold; text-align: right; padding-right: 10px;">
<?php if ($val>0) echo '$'; echo $valdecimal;?>
</div>
</td>
<?php
};
add_action( 'woocommerce_admin_order_item_values', 'action_woocommerce_admin_order_item_values', 10, 3 );
function action_woocommerce_admin_order_item_headers( $order ) {
echo '<th class="item_fcost sortable" data-sort="float" style="text-align: right;">Price inc VAT</th>';
};
add_action( 'woocommerce_admin_order_item_headers', 'action_woocommerce_admin_order_item_headers', 10, 3 );
This give me the opportunity to show show VAT $ separate.
You can learn more about this thread here
The issue is that when I refund a product, I get the new woocommerce error (since 5.2)
The site is experiencing technical difficulties. Please check your site admin email inbox for instructions.
I just look at the error and it seems that wocommerce display it because it can't find that custom field in the refund process...
#3 /nas/content/staging/.../wp-content/plugins/woocommerce/includes/admin/meta-boxes/views/html-order-refund.php(48): do_action('woocommerce_adm...', NULL, Object(WC_Order_Refund), 68711)
How can I remove this custom functions from the refund process? Is there a way to just hide this error?
I hit this issue as well. The problem is that woocommerce_admin_order_item_values is now getting called with both WC_Order_Refund as well as WC_Order_item. We now have to discern which type of object we're getting when we receive it and handle accordingly. You can discern with this code.
if ($item->get_type() == 'shop_order_refund') {
$item = new WC_Order_Refund($item_id);
}
else {
$item = new WC_Order_Item_Product($item_id);
}
In your case I believe you need to change that first condition statement at the beginning of your code to something similar to.
switch ($item->get_type()) {
case 'shop_order_refund':
$val = $item->get_amount();
break;
case 'line_item':
case 'shipping':
$val = $item['total'] + $item['total_tax'];
break;
default:
$val = ' ';
break;
}
Is there a way to change the WooCommerce add-to-cart form through functions.php?
The goal is to add a checkbox for an additional product. When the checkbox is checked this product will also be added to the cart after a click on the add to cart button.
I am looking for a solution which doesn't rely on javascript.
A better title would be "WooCommerce up-sells as checkboxes".
A lot of research and several strategies to tackle this problem lead me to a solution which I thought was not even possible in the beginning.
The solution is now exactly what I wanted. A non-JavaScript, no-template-override, but a simple and pure addition to functions.php. It works for simple and variable products (and probably with grouped and external products too).
It misses some nice features still. It won't work yet if an up-sell is a variable product. Quantity selection and limiting up-sells per item or order would be nice additions too. Based on the code below adding those features should not be a big deal anymore.
// create the checkbox form fields and add them before the cart button
add_action( 'woocommerce_before_add_to_cart_button', 'action_woocommerce_before_add_to_cart_form', 10, 0 );
function action_woocommerce_before_add_to_cart_form(){
global $woocommerce, $product;
// get the product up-sells
$upsells = $product->get_upsells();
// store the number of up-sells and pass it on to the add-to-cart hook
?>
<input type="hidden" name="upsells_size" value="<?php echo(sizeof($upsells)); ?>">
<div id="wb-upsell-div">
<?php
// iterate through all upsells and add an input field for each
$i = 1;
foreach( $upsells as $value ){
$product_id = $value;
?>
<input id="wb-upsell-checkboxes" type="checkbox" name="upsell_<?php echo($i) ?>" value="<?php echo($product_id); ?>"><?php echo( '' . get_the_title( $product_id ) . "". " ($" . get_post_meta( $product_id, '_regular_price', true) . ")"); ?><br>
<?php
$i++;
}
?>
</div>
<?php
}
// function to add all up-sells, where the checkbox have been checked, to the cart
add_action('woocommerce_add_to_cart', 'custom_add_to_cart', 10, 3);
function custom_add_to_cart() {
global $woocommerce;
// get the number of up-sells to iterate through
$upsell_size = $_POST['upsells_size'];
// iterate through up-sell fields
for ($i=1; $i<=$upsell_size; $i++){
// get the product id of the up-sell
$product_id = $_POST['upsell_' . $i];
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}
And here is the CSS for formatting the <div>and the checkboxes. It goes into the style.css file:
#wb-upsell-div {
margin-top: 10px;
margin-bottom: 20px;
}
#wb-upsell-checkboxes{
}
So there's an actual answer to this question, you can add whatever you want inside the add to cart <form> using hooks. For example:
add_action( 'woocommerce_before_add_to_cart_button', 'so_34115452_add_input' );
function so_34115452_add_input(){
echo '<input type="checkbox" name="something"/>' . __( 'Some Checkbox', 'text-domain' );
}
I have a WordPress site that has a ton of posts on it, all categorized. I set up a new theme, with pagination (15 posts per page), so the user can cycle through each page. Some of the categories paginate fine. Others are missing the final page.
So, if a category has 66 posts ... the first 4 pages show 15 different posts. However, when I click to view page 5, the page says "no posts found". Where did the last 6 posts go? They still show up in my administration (as published and visible). However, other category pages do not have this issue - for example, I have a category with 42 post, and it has 3 page ... the last page of which has 12 of the final post.
So, the pagination seems to be working fine (since it clearly shows the correct number of pages, for the number of posts). Please take a look below at the code I have... this is code from my templated index.php page (I didnt set up a category.php page, because it lists very similarly to the homepage).
<?php
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1; // Page
$args = "posts_per_page=15&paged=".$paged; // Base query
$category = get_queried_object(); // Get Cat info
$thisCat = $category->term_id; // Get Cat ID (if exists)
$tagID = get_queried_object()->term_id; // Get Tag ID (if exists)
echo '<!-- paged: '.$paged.'-->';
echo '<!-- catID: '.$thisCat.'-->';
echo '<!-- tagID: '.$tagID.'-->';
if (is_home() || is_front_page()) { // HOMEPAGE
query_posts($args.'&orderby=rand');
echo '<!-- args: '.$args.'&orderby=rand'.'-->';
} elseif ( is_search() ) { // SEARCH RESULTS ?>
<?php
$search_query = get_search_query();
query_posts($args.'&s='.$search_query);
echo "<!-- args: ".$args.'&s='.$search_query."-->"; ?>
<h1>Search</h1>
<div class="content_labels">
<div class="content_label">SEARCH RESULTS FOR: <?php echo $s; ?></div>
</div>
<div class="clear" style="margin:0 0 10px 0;"></div>
<div class="previouspage">
<< Previous Page
</div><?php
} elseif( is_category() ) { // CATEGORY
query_posts($args.'&cat='.$thisCat);
echo '<!-- args: '.$args.'&cat='.$thisCat.'-->'; ?>
<div class="content_labels">
<div class="content_label">Category:</div>
</div>
<h1><?php single_cat_title( '', true ); ?></h1>
<div class="clear" style="margin:0 0 10px 0;"></div>
<div class="previouspage">
<< Previous Page
</div><?php
} elseif( is_tag()) { // TAGS
echo '<!-- args: '.$args.'&tag_id='.$tagID.'-->';
query_posts($args.'&tag_id='.$tagID); ?>
<div class="content_labels">
<div class="content_label">Tag:</div>
</div>
<h1><?php single_tag_title(); ?> </h1>
<div class="clear" style="margin:0 0 10px 0;"></div>
<div class="previouspage">
Previous Page
</div><?php
}
if ( have_posts() ) :
$i=1;
while ( have_posts() ) : the_post(); ?>
// PAGE CODE GOES HERE
endwhile; ?>
<?php base_pagination(); // PAGINATION CODE ?>
<?php endif; ?>
Here is the pagination code, from my functions.php ... I don't think this is the issue...
function base_pagination() {
global $wp_query;
$big = 999999999; // This needs to be an unlikely integer
// For more options and info view the docs for paginate_links()
// http://codex.wordpress.org/Function_Reference/paginate_links
$paginate_links = paginate_links( array(
'base' => str_replace( $big, '%#%', get_pagenum_link($big) ),
'current' => max( 1, get_query_var('paged') ),
'total' => $wp_query->max_num_pages,
'mid_size' => 5
) );
// Display the pagination if more than one page is found
if ( $paginate_links ) {
echo '<div class="pagination">';
echo $paginate_links;
echo '</div><!--// end .pagination -->';
}
}
Can anyone see what is going wrong? I've been playing with this for several hours, and I can't seem to find a solution...
So, I did some digging, and tried some things... I couldn't find anything wrong with the code above. The query was correct (showing 15 per page, for each category archive page, etc.), and the pagination was working ... In the end, the default POSTS PER PAGE was conflicting with my own posts_per_page=15 query. Not sure WHERE this conflict was occurring (that is beyond my skills) - but I did learn how to stop it.
Under SETTINGS - READING ...
I just changed the "Blog pages show at most" to "15"
This wasn't an ideal fix (since I don't know where this problem started, and I can't adjust custom "posts_per_page" if it differs from 15)... but my site now works how I want.
I've been trying to add a single text input field to every item in the cart and submit that user input to product's meta info. It's been 2 days and I haven't succeeded yet.
My objective is to:
Take input from user for every item added to the cart.
Display that input in the order's meta info.
Display that input in confirmation email sent to the customer.
So far, I have copied the template file to my theme and added an input field inside a cell. I'm having trouble with the hooks, learned about hooks I will need from WooCommerce Product Gift Wrap plugin as indicated in this woocommerce issue.
Code I added to the cart.php template copied in my theme directory :
$input_url_data = '<div class="input-url"><input type="text" name="cart-url" value="" title="" class="input-text cart-url text" /></div>';
echo apply_filters( 'woocommerce_add_cart_item_data', $input_url_data, $cart_item_key );
Code I added to my theme's functions.php :
add_filter( 'woocommerce_add_cart_item_data','add_cart_item_data', 10, 2 );
add_filter( 'woocommerce_get_cart_item_from_session','get_cart_item_from_session', 10, 2 );
add_filter( 'woocommerce_get_item_data','get_item_data', 10, 2 );
add_filter( 'woocommerce_add_cart_item','add_cart_item', 10, 1 );
add_action( 'woocommerce_add_order_item_meta','add_order_item_meta', 10, 2 );
function add_cart_item_data( $cart_item_meta, $product_id ) {
$input_url_key = "";
$input_url_data['inputurl'] = $input_url_key;
return $input_url_data;
}
function get_cart_item_from_session( $cart_item, $values ) {
if ( ! empty( $values['inputurl'] ) ) {
$cart_item['inputurl'] = true;
}
return $cart_item;
}
function get_item_data( $item_data, $cart_item ) {
if ( ! empty( $cart_item['inputurl'] ) )
$item_data[] = array(
);
return $item_data;
}
function add_cart_item( $cart_item ) {
if ( ! empty( $cart_item['inputurl'] ) ) {
}
return $cart_item;
}
function add_order_item_meta( $item_id, $cart_item ) {
if ( ! empty( $cart_item['inputurl'] ) )
woocommerce_add_order_item_meta( $item_id, __( 'URL by buyer', 'custom_input_url' ), __( 'Yes', 'custom_input_url' ) );
}
Documentation about hook woocommerce_add_cart_item_data isn't very helpful and I'm stuck at this. How do I proceed?
There is a wordpress plugin called WC Fields Factory for the exact purpose.
You can also achieve this by using the following woocommerce hooks woocommerce_before_add_to_cart_button, woocommerce_add_to_cart, woocommerce_cart_item_name,and 'woocommerce_add_order_item_meta'
like for adding text field to product page
function add_name_on_tshirt_field() {
echo '<table class="variations" cellspacing="0">
<tbody>
<tr>
<td class="label"><label for="color">Name On T-Shirt</label></td>
<td class="value">
<input type="text" name="name-on-tshirt" value="" />
</td>
</tr>
</tbody>
</table>';
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_name_on_tshirt_field' );
For displaying custom field on cart item table use the below
function render_meta_on_cart_item( $title = null, $cart_item = null, $cart_item_key = null ) {
if( $cart_item_key && is_cart() ) {
echo $title. '<dl class="">
<dt class="">Name On T-Shirt : </dt>
<dd class=""><p>'. WC()->session->get( $cart_item_key.'_name_on_tshirt') .'</p></dd>
</dl>';
}else {
echo $title;
}
}
add_filter( 'woocommerce_cart_item_name', 'render_meta_on_cart_item', 1, 3 );
to make your custom meta data on you order details, do some thing like this
function tshirt_order_meta_handler( $item_id, $values, $cart_item_key ) {
wc_add_order_item_meta( $item_id, "name_on_tshirt", WC()->session->get( $cart_item_key.'_name_on_tshirt') );
}
add_action( 'woocommerce_add_order_item_meta', 'tshirt_order_meta_handler', 1, 3 );
for detailed implementation, i have an article about how to do this without using any plugins. http://sarkware.com/how-to-pass-custom-data-to-cart-line-item-in-woocommerce-without-using-plugins/
It's easy. Try searching and reading code of Woocommerce.
This much of code got me to point where I can add Url # Cart. And I can see it in Order review as customer and as admin.
I can't test email because I'm lazy. I'm sorry.
Something like this goes in templates\cart\cart.php (there is need for some more code as it's seperate column)
<td class="product-url">
<?php
$html = sprintf( '<div class="url"><input type="text" name="cart[%s][url]" value="%s" size="4" title="Url" class="input-text url text" /></div>', $cart_item_key, esc_attr( $values['url'] ) );
echo $html;
?>
</td>
Functions.php
// get from session your URL variable and add it to item
add_filter('woocommerce_get_cart_item_from_session', 'cart_item_from_session', 99, 3);
function cart_item_from_session( $data, $values, $key ) {
$data['url'] = isset( $values['url'] ) ? $values['url'] : '';
return $data;
}
// this one does the same as woocommerce_update_cart_action() in plugins\woocommerce\woocommerce-functions.php
// but with your URL variable
// this might not be the best way but it works
add_action( 'init', 'update_cart_action', 9);
function update_cart_action() {
global $woocommerce;
if ( ( ! empty( $_POST['update_cart'] ) || ! empty( $_POST['proceed'] ) ) && $woocommerce->verify_nonce('cart')) {
$cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : '';
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
if ( isset( $cart_totals[ $cart_item_key ]['url'] ) ) {
$woocommerce->cart->cart_contents[ $cart_item_key ]['url'] = $cart_totals[ $cart_item_key ]['url'];
}
}
}
}
}
// this is in Order summary. It show Url variable under product name. Same place where Variations are shown.
add_filter( 'woocommerce_get_item_data', 'item_data', 10, 2 );
function item_data( $data, $cart_item ) {
if ( isset( $cart_item['url'] ) ) {
$data['url'] = array('name' => 'Url', 'value' => $cart_item['url']);
}
return $data;
}
// this adds Url as meta in Order for item
add_action ('woocommerce_add_order_item_meta', 'add_item_meta', 10, 2);
function add_item_meta( $item_id, $values ) {
woocommerce_add_order_item_meta( $item_id, 'Url', $values['url'] );
}
400$ is nice price.
You can do this fairly easily with the Woocommerce Product Add-ons plugin
From the WooThemes website:
Allow your customers to customise your products by adding new options
such as input boxes, dropdowns or checkboxes. With the Product Add-ons
extension, gift messages, donations, laser engraving and any other
product which may require user input in some way is now an option for
your customers!
Product add-ons supports required fields, textareas, checkboxes,
radios, select boxes, custom price inputs and file upload boxes.
I've used it before to add an additional donation field to a product purchase and display that on the thank you page/receipt email.
It's about $50 US and will get you up and running in no time to add the text input and display the field on thank you page/email like you want. $50 is definitely worth the amount of time you'd save drying to develop this feature yourself.
Here's the flow from the end-users perspective:
End user enters in field data and adds product to cart
When User views cart the data they entered into the custom field displays along with the product
After purchase, end user lands on thank you page and receives receipt email with field data included with the product item.
On the backend:
Create custom field for each product. The option is located on the Add-Ons menu tab.
Create a new Addon Group
Enter a group name (for easier organization)
Add a new option and enter the label (this is what the end user sees and is tied to the product)
Update/Publish product.
For completed orders, this is what you will see in the Order Details admin:
Hope this helps and saves you a ton of time in development!
Im trying to change the user profile "Display name publicly as" from a select box to a textbox. Any ideas on how this can be done? Couldnt find anything all all.
Two way to solve it. You edit the wordpress core file, which is not a good way. Or you can add an extra field on profile box using wordpress hook.
I am giving you the first way:
Go to wp-admin/user-edit.php
Open the file. Then find
<td>
<select name="display_name" id="display_name">
<?php
$public_display = array();
$public_display['display_nickname'] = $profileuser->nickname;
$public_display['display_username'] = $profileuser->user_login;
if ( !empty($profileuser->first_name) )
$public_display['display_firstname'] = $profileuser->first_name;
if ( !empty($profileuser->last_name) )
$public_display['display_lastname'] = $profileuser->last_name;
if ( !empty($profileuser->first_name) && !empty($profileuser->last_name) ) {
$public_display['display_firstlast'] = $profileuser->first_name . ' ' . $profileuser->last_name;
$public_display['display_lastfirst'] = $profileuser->last_name . ' ' . $profileuser->first_name;
}
if ( !in_array( $profileuser->display_name, $public_display ) ) // Only add this if it isn't duplicated elsewhere
$public_display = array( 'display_displayname' => $profileuser->display_name ) + $public_display;
$public_display = array_map( 'trim', $public_display );
$public_display = array_unique( $public_display );
foreach ( $public_display as $id => $item ) {
?>
<option id="<?php echo $id; ?>"<?php selected( $profileuser->display_name, $item ); ?>><?php echo $item; ?></option>
<?php
}
?>
</select>
</td>
Change it with
<th><label for="display_name"><?php _e('Display name publicly as'); ?> </label></th>
<td><input type="text" name="display_name" id="display_name" value="<?php echo esc_attr( $profileuser->display_name ); ?>" class="regular-text" /></td>
But still this is not a good solution.
I came across this same issue and used a bit of javascript to solve it. Wordpress allows you to set the display_name field to anything you want, so you can use the following script (in your theme's functions.php file) to convert the select to a text field.
function change_display_name_to_textfield() {
echo "><div>"; // don't remove '>'
?>
<script>
jQuery(function($) {
// replace display_name select with input
$('select#display_name').after( '<input type="text" name="display_name" id="display_name" value="' + $('#display_name').val() + '" class="regular-text">' ).remove();
})
</script>
<?php
echo "</div"; // don't add '>'
}
// hook into new user and edit user pages
add_action( "user_new_form_tag", "change_display_name_to_textfield" );
add_action( "user_edit_form_tag", "change_display_name_to_textfield" );
Note the weird use of opening/closing tags. This is because those particular hooks execute just before the end of the opening form tag. So you need to close the form tag, print your script, then print a final tag without closing it.
For admin user edit pages, I found one of the easiest way:
add_action( 'personal_options', 'rm_personal_options' );
function rm_personal_options($profileuser){
if( !is_admin() ) return; ?>
<script type="text/javascript">
jQuery(document).ready(function($){
//$("input#nickname").parent().parent().hide();
$("select#display_name").replaceWith(function(){
return '<input type="text" name="display_name" id="display_name" value="'+$(this).val()+'" class="regular-text" style="width:25em" />';
});
});
</script>
<?php
}
Simply paste it into the functions.php of the theme file :)