I know this is not a woocommerce related websites, I tried the official forum, but no reply in 3 days. I spent a day or more with this, so I hope you can help.
I created a custom wp_query for the products. These products has a custom field (custom_price). I would like to override the prices in the query with the values of the custom fields.
I saw questions here about this, but I'm very new in php/wordpress. I really appreciate your help.
My query:
<?php woocommerce_product_loop_start(); ?>
<?php
if(is_front_page()){
$args = array(
'post_type' => 'product',
'posts_per_page' => 6,
'meta_key' => '_featured',
'meta_value' => 'yes'
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
wc_get_template_part( 'content', 'product' );
}
}
wp_reset_query();
}
?>
<?php woocommerce_product_loop_end(); ?>
If you just want to change the price that is displayed, then you can implement the filters woocommerce_price_html, woocommerce_sale_price_html, woocommerce_cart_item_price_html. This will result in the displayed price being whatever you would like, however the actual price in the cart used for calculating tax, shipping, totals, etc will be based on the "real" price.
// the HTML that is displayed on the product pages
function my_price_html( $html, $_product ) {
// if this is a variation we want the variation ID probably?
$id = isset($_product->variation_id) ? $_product->variation_id : $_product->id;
$custom_price = get_post_meta( $id, 'custom_price', true );
$custom_price_html = "<b>$custom_price</b>"; // just an example of HTML
return $custom_price_html;
}
add_filter( 'woocommerce_price_html', 'my_price_html', 10, 2 );
add_filter( 'woocommerce_sale_price_html', 'my_price_html', 10, 2 );
// the HTML that is displayed on the cart
function my_cart_item_price_html( $html, $cart_item, $cart_item_key ) {
$id = $cart_item['data']->id;
$custom_price = get_post_meta( $id, 'custom_price', true );
$custom_price_html = "<b>$custom_price</b>"; // just an example of HTML
return $custom_price_html;
}
add_filter( 'woocommerce_cart_item_price_html', 'my_cart_item_price_html', 10, 3 );
You will need to implement other filters/actions if you want to actually affect the item price, the above just affects the display. A better option might be to implement an action for save_post or update_post_meta that updates the WooCommerce product price when the custom field value changes (assuming you are using the codex to update this value).
Related
In WooCommerce I would like to hide Out of Stock products from Related products in single product pages. Is it possible?
Any track is appreciated.
None of the answers given here worked for me (I believe the woocommerce_output_related_products_args filter mentioned does not accept meta_queries), and I wanted a solution that didn't use an SQL query, so I put together the solution below:
add_filter( 'woocommerce_related_products', 'mysite_filter_related_products', 10, 1 );
function mysite_filter_related_products( $related_product_ids ) {
foreach( $related_product_ids as $key => $value ) {
$relatedProduct = wc_get_product( $value );
if( ! $relatedProduct->is_in_stock() ) {
unset( $related_product_ids["$key"] );
}
}
return $related_product_ids;
}
Hope that helps someone looking for a similar solution.
UPDATE 2021
You can use the following:
add_filter( 'woocommerce_product_related_posts_query', 'alter_product_related_posts_query', 10, 3 );
function alter_product_related_posts_query( $query, $product_id, $args ){
global $wpdb;
$query['join'] .= " INNER JOIN {$wpdb->postmeta} as pm ON p.ID = pm.post_id ";
$query['where'] .= " AND pm.meta_key = '_stock_status' AND meta_value = 'instock' ";
return $query;
}
Code goes in functions.php file of your active child theme (or active theme).
Now we need to remove "related products" cached data deleting the related transients to flush this cache (thanks to #Cody Rees).
There is 2 ways to do it:
1). The easiest way:
Go to admin Woocommerce > Status > Tools > WooCommerce transients and press on "Clear transcients".
2). The other way targeting specific related transients to be deleted:
Add the following code and save:
add_action('init', 'delete_related_products_cached_data');
function delete_related_products_cached_data() {
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->prefix}options WHERE `option_name` LIKE '_transient_wc_related_%'");
}
Code goes in functions.php file of your active child theme (or active theme).
Run it only once by browsing any page of your web site and remove it.
Yes it's possible to hide out of stock products from related products.
Add the below to functions.php – this will hide out of stock products from related products.
add_filter( 'woocommerce_output_related_products_args', function( $args )
{
$args = wp_parse_args( array(
'posts_per_page' => 4,
'meta_query' => array (
'key' => '_stock_status',
'value' => 'instock'
)
), $args );
return $args;
});
The posts per page line can be removed, but its useful as a quick of visualising that this has worked on your related products block.
For those who didn't find solution:
Tested on Woocommerce +6
add_filter( 'woocommerce_related_products', 'vahids_related_products', 10, 3 );
function vahids_related_products( $related_posts, $product_id, $args ){
$in_stock_product_ids = (array) wc_get_products( array(
'status' => 'publish',
'limit' => -1,
'stock_status' => 'instock',
'return' => 'ids',
));
return $in_stock_product_ids;
}
This is working code from here. Add this code to your functions.php and you will see that our of stocks products will not be seen in related product block. Code is from here : https://stackoverflow.com/a/60978253/15213069
add_filter( 'woocommerce_related_products', 'mysite_filter_related_products', 10, 1 );
function mysite_filter_related_products( $related_product_ids ) {
foreach( $related_product_ids as $key => $value ) {
$relatedProduct = wc_get_product( $value );
if( ! $relatedProduct->is_in_stock() ) {
unset( $related_product_ids["$key"] );
}
}
return $related_product_ids;
}
create a function and hook it to related products hook of woocommerce like:
function dont_show_outofstock( $is_visible, $id ) {
$product = new wC_Product( $id );
if ( ! $product->is_in_stock() && ! $product->backorders_allowed() ) {
$is_visible = false;
}
return $is_visible;
}
add_filter( 'woocommerce_output_related_products_args', 'dont_show_outofstock', 10, 2 );
I'm helping my mother create her website for her store i Denmark.
It has gone okay, but now i'm stuck with a problem.
I need to be able to make a custom field in the order complete email, that displays a track & trace number, i've inserted on the admin order page. Everything i've done up until now, haven't worked, so therefore i seek your help.
I've added a custom field already called Track & Trace Pakkenr. (see screenshot 1)
But the problem is getting this in the order complete email, under shipping (forsendelse in danish, see screenshot 2)
I am also a complete and utter beginner in coding so if any of you can give any help or advice, please make it almost foolproof to follow.
Here are the screenshot 1 and screenshot 2.
To get this order custom field value displayed in order totals table on email notifications, use the following:
add_filter( 'woocommerce_get_order_item_totals', 'insert_custom_line_order_item_totals', 10, 3 );
function insert_custom_line_order_item_totals( $total_rows, $order, $tax_display ){
// Only on emails notifications
if( is_wc_endpoint_url() ) return $total_rows; // Exit
$tracking_label = 'Track & Trace Pakkenr.'; // The tracking label name
$tracking_value = $order->get_meta( $tracking_label ); // Get the tracking value (custom field).
if( empty($tracking_value) ) return $total_rows; // Exit
$new_total_rows = array(); // Initializing
// Loop through total rows
foreach( $total_rows as $key => $value ){
if( 'payment_method' == $key && ! empty($tracking_value) ) {
$new_total_rows['tracking_parcel'] = array(
'label' => $tracking_label,
'value' => $tracking_value,
);
}
$new_total_rows[$key] = $total_rows[$key];
}
return sizeof($new_total_rows) > 0 ? $new_total_rows : $total_rows;
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
You will get something like:
From this:
You can add custom fields to the order email, you need to use the field key where the code has meta_key.
/**
* Add a custom field (in an order) to the emails
*/
add_filter( 'woocommerce_email_order_meta_fields', 'custom_woocommerce_email_order_meta_fields', 10, 3 );
function custom_woocommerce_email_order_meta_fields( $fields, $sent_to_admin, $order ) {
$fields['meta_key'] = array(
'label' => __( 'Label' ),
'value' => get_post_meta( $order->id, 'meta_key', true ),
);
return $fields;
}
Ref: https://docs.woocommerce.com/document/add-a-custom-field-in-an-order-to-the-emails/
Below is a simple yet functional example of adding custom meta fields to order email
add_filter( 'woocommerce_email_order_meta_fields', 'woocommerce_email_order_meta_fields_func', 10, 3 );
function woocommerce_email_order_meta_fields_func( $fields, $sent_to_admin, $order ) {
$fields['Track_Field'] = array(
'label' => __( 'Track', 'woocommerce' ),
'value' => wptexturize( get_post_meta( $order->id, 'Track_Field', true ) )
);
//... more meta fields goes here
return $fields;
}
In email after order email table:
add_action( 'woocommerce_email_after_order_table', 'woocommerce_email_after_order_table_func' );
function woocommerce_email_after_order_table_func( $order ) {
?>
<h3>Track</h3>
<table>
<tr>
<td>Track Fields </td>
<td><?php echo wptexturize( get_post_meta( $order->id, 'Track_Field', true ) ); ?></td>
</tr>
<!--additional custom meta and HTML code goes here-->
</table>
<?php
}
For more help see this link : Click here
My Goal: I am using WooCommerce with WCVendor plugin. All vendors have a taxonomy of location. Similarly all products publish by them have taxonomy called 'Product Location'. When vendors add new product, they have to select their location(s) everytime. Now, I would like to run a program that will update their product's location(s) according to their location.
I have managed to get a snippet by searching online -
add_action( 'added_post_meta', 'mp_sync_on_product_save', 10, 4 );
add_action( 'updated_post_meta', 'mp_sync_on_product_save', 10, 4 );
function mp_sync_on_product_save( $meta_id, $post_id, $meta_key, $meta_value ) {
if ( $meta_key == '_edit_lock' ) { // we've been editing the post
if ( get_post_type( $post_id ) == 'product' ) { // we've been editing a product
$product = wc_get_product( $post_id );
// Find the locations of author and save it as product's location
}
}
}
Can anyone please help?
Thank you.
I made a new post type named "sub_products" containing the meta tag "unit_price".
After assigning a new field for every Woocommerce product containing a list of all the "sub_products" posts, the goal was to update every product price based on the selected "sub_products" meta "unit_price".
function kulcskron_edit_post( $p1, $p2 )
{
if ( !is_admin() )
return;
if ( get_post_type() != 'product' )
return;
$sub_product_ids = $p2->get_meta( 'sub_products' );
if ( empty($sub_product_ids) )
return;
$product_regular_price = 0;
foreach ( $sub_product_ids as $id )
$product_regular_price += get_post_meta( $id, 'unit_price', true );
if ( $p1 == $product_regular_price )
return;
$p2->set_regular_price( $product_regular_price );
$p2->save();
}
add_action( 'woocommerce_product_get_price', 'kulcskron_edit_post', 10, 2 );
I tried every possible hook to make this work:
add_action( 'the_post', 'kulcskron_edit_post', 9, 1 );
add_action( 'edit_post', 'kulcskron_edit_post', 10, 2 );
add_action( 'pre_get_posts', 'kulcskron_edit_post' );
add_action( 'save_post', 'kulcskron_edit_post' );
This code updates the price but in a strange way:
Admin single product edit view: The price is not updated right away, just after I revisit the edit screen.
Admin product listing view: Every price is 0.
Front-end product page view: The displayed price is 0.
How do I update the product price based on the assigned "sub_products" "unit_price" post meta when a single product is saved in admin view?
You may use the hook "woocommefrce_get_price_html" it should work
I managed to solve the price update problem.
I used the save_post action:
save_post is an action triggered whenever a post or page is created or updated, which could be from an import, post/page edit form, xmlrpc, or post by email.
The final code:
function kulcskron_update_product( $product_obj )
{
if ( !is_admin() )
return;
if ( get_post_type() != 'product' )
return;
$product = wc_get_product( $product_obj );
if ( !$product->meta_exists( 'sub_products' ) )
return;
$sub_product_ids = $product->get_meta( 'sub_products' );
if ( empty($sub_product_ids) )
return;
_update_product_price( $product_obj, $sub_product_ids );
}
add_action( 'save_post', 'kulcskron_update_product' );
And for the sake of completeness here is the rest of code:
function _update_product_price( $product_obj, $sub_product_ids )
{
$product = wc_get_product( $product_obj );
$product_regular_price = 0;
foreach ( $sub_product_ids as $id )
$product_regular_price += get_post_meta( $id, 'kulcskron_unit_price', true );
$product->set_regular_price( $product_regular_price );
$product->set_price( $product_regular_price );
$product->save();
}
This way basically everything can be edited and updated. Example:
function _update_product_stock( $product_obj, $sub_product_ids )
{
$product = wc_get_product( $product_obj );
$sub_product_stocks = array();
foreach ( $sub_product_ids as $id )
$sub_product_stocks[] = get_post_meta( $id, 'kulcskron_free_stock_quantity', true );
$product->set_manage_stock( true );
$product->set_stock_quantity( min($sub_product_stocks) );
$product->save();
}
I would love to post a link to all the methods to edit a product but I have no rep for that.
But sadly this is not all...
In order to fully update the price, we need to filter the price HTML to return the newly updated price.
function kulcskron_price_html( $priceHtml, $product )
{
$symbol = get_woocommerce_currency_symbol();
$price = $product->get_regular_price();
$html = '<span class="woocommerce-Price-amount amount">'. $price .' <span class="woocommerce-Price-currencySymbol">'. $symbol .'</span></span>';
return $html;
};
add_filter( 'woocommerce_get_price_html', 'kulcskron_price_html', 10, 2 );
A little backstory. We need to sync all the products from an external database through an XML file. But there is a catch, the XML file only contains product parts and they are not displayed, searched, filtered and certainly can't be ordered individually. None of the Woocommerce build in functionality meet these requirements.
In order to solve this, I registered a new post type and imported all the product parts into this newly created post type. After that, I registered a new field for every WC Product with the Advanced Custom Fields plugin.
It looks like this:
Newly registered custom post type with the custom fields
The code above makes easy to update the WC Products (e.g. Prices) based on the selected Sub products.
I have issue with updating of product price from external database. I need to check price of product in every access to this position. For that I use the_post hook. For example I got '1718' price value for single product.
function chd_the_post_action( $post ) {
if ( $post && $post->post_type == 'product' ) {
$product = wc_get_product( $post->ID );
if ( $product ) {
$price = '1718';
$product->set_price( "$price" );
$product->set_regular_price( "$price" );
$product->set_sale_price( "$price" );
$product->save();
}
}
}
This code update product price in database, but it's not change view of price on page in the same moment, but only after page reload because post and product variables was setup by setup_postdata().
Therefore I use woocommerce hook for display updated price:
function chd_get_price_filter( $price, $item ) {
return '1718';
}
add_filter( 'woocommerce_product_get_price', 'chd_get_price_filter', 100, 2 );
add_filter( 'woocommerce_product_get_regular_price', 'chd_get_price_filter', 100, 2 );
add_filter( 'woocommerce_product_get_sale_price', 'chd_get_price_filter', 100, 2 );
Is there any hook in which I can do this action in better way?
Update product price using update_post_meta function like this
update_post_meta( $product->id, '_sale_price', '1718' );
update_post_meta( $product->id, '_regular_price', '1718 );
add_action( 'the_post', 'chd_the_post_action', 9, 1);